Files
oboe/osx/dialogxml/button.cpp
Celtic Minstrel 88cb60fb8d Embark on an epic journey to document the dialog engine in as much detail as possible.
... and the previous commits (from 56f73cb on) were by and large a result of things noticed during this process.
2014-12-06 13:37:43 -05:00

479 lines
12 KiB
C++

/*
* button.cpp
* BoE
*
* Created by Celtic Minstrel on 11/05/09.
*
*/
#include "button.h"
#include <vector>
#include <map>
#include <stdexcept>
#include "dialog.h"
#include "graphtool.h"
#include <cmath>
#include <climits>
#include "restypes.hpp"
extern sf::Texture bg_gworld;
void cButton::attachFocusHandler(focus_callback_t) throw(xHandlerNotSupported){
throw xHandlerNotSupported(true);
}
void cButton::attachClickHandler(click_callback_t f) throw(){
onClick = f;
}
bool cButton::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
if(onClick) return onClick(me,id,mods);
return false;
}
cButton::cButton(cDialog* parent) :
cControl(CTRL_BTN,*parent),
wrapLabel(false),
type(BTN_REG),
fromList("none") {}
cButton::cButton(cDialog* parent,eControlType t) :
cControl(t,*parent),
fromList("none"),
wrapLabel("true") {/* This constructor is only called for LEDs. TODO: Should wrapLabel be true for LEDs? */}
bool cButton::isClickable(){
return true;
}
void cButton::draw(){
RECT from_rect, to_rect;
inWindow->setActive();
if(visible){
TextStyle style;
if(type == BTN_TINY) style.pointSize = 9;
else if(type == BTN_PUSH) style.pointSize = 10;
else style.pointSize = 12;
from_rect = btnRects[type][depressed];
to_rect = frame;
rect_draw_some_item(buttons[btnGW[type]],from_rect,*inWindow,to_rect,sf::BlendAlpha);
style.colour = sf::Color::Black;
style.lineHeight = 8;
eTextMode textMode = eTextMode::CENTRE;
if(type == BTN_TINY) {
textMode = eTextMode::LEFT_TOP;
to_rect.left += 18;
} else if(type == BTN_PUSH) {
to_rect.top += 34;
}
win_draw_string(*inWindow,to_rect,lbl,textMode,style);
// TODO: Adjust string location as appropriate
// Tiny button string location should be shifted right 20 pixels (or possibly 18)
// Push button string should be centred below the button
// Others may need adjustments too, not sure
// TODO: How is it supposed to know it's a default button when this fact is stored in the dialog, not the button?
if(key.spec && key.k == key_enter) drawFrame(2,frameStyle); // frame default button, to provide a visual cue that it's the default
}else{
tileImage(*inWindow,frame,bg_gworld,bg[parent->getBg()]);
}
}
void cButton::setFormat(eFormat prop, short val) throw(xUnsupportedProp){
if(prop == TXT_WRAP) wrapLabel = val;
else if(prop == TXT_FRAMESTYLE) frameStyle = val;
else throw xUnsupportedProp(prop);
}
short cButton::getFormat(eFormat prop) throw(xUnsupportedProp){
if(prop == TXT_WRAP) return wrapLabel;
else if(prop == TXT_FRAMESTYLE) return frameStyle;
else throw xUnsupportedProp(prop);
}
void cButton::setColour(sf::Color) throw(xUnsupportedProp) {
// TODO: Colour is not supported
}
sf::Color cButton::getColour() throw(xUnsupportedProp) {
// TODO: Colour is not supported
return sf::Color();
}
// Indices within the buttons array.
size_t cButton::btnGW[14] = {
0, // BTN_SM
1, // BTN_REG
2, // BTN_LG
4, // BTN_HELP
1, // BTN_LEFT
1, // BTN_RIGHT
1, // BTN_UP
1, // BTN_DOWN
5, // BTN_TINY
1, // BTN_DONE
3, // BTN_TALL
3, // BTN_TRAIT
6, // BTN_PUSH
5, // BTN_LED
};
sf::Texture cButton::buttons[7];
RECT cButton::btnRects[13][2];
void cButton::init(){
static const char*const buttonFiles[7] = {
"dlogbtnsm",
"dlogbtnmed",
"dlogbtnlg",
"dlogbtntall",
"dlogbtnhelp",
"dlogbtnled",
"dlgbtnred"
};
for(int i = 0; i < 7; i++)
buttons[i].loadFromImage(*ResMgr::get<ImageRsrc>(buttonFiles[i]));
btnRects[BTN_SM][0] = {0,0,23,23};
btnRects[BTN_REG][0] = {0,0,23,63};
btnRects[BTN_LEFT][0] = {23,0,46,63};
btnRects[BTN_RIGHT][0] = {46,0,69,63};
btnRects[BTN_UP][0] = {69,0,92,63};
btnRects[BTN_DOWN][0] = {92,0,115,63};
btnRects[BTN_DONE][0] = {115,0,138,63};
btnRects[BTN_LG][0] = {0,0,23,102};
btnRects[BTN_HELP][0] = {0,0,13,16};
btnRects[BTN_TINY][0] = {0,42,10,56};
btnRects[BTN_TALL][0] = {0,0,40,63};
btnRects[BTN_TRAIT][0] = {40,0,80,63};
btnRects[BTN_PUSH][0] = {0,0,30,30};
for(int j = 0; j < 12; j++)
btnRects[j][1] = btnRects[j][0];
btnRects[BTN_SM][1].offset(23,0);
btnRects[BTN_REG][1].offset(63,0);
btnRects[BTN_LEFT][1].offset(63,0);
btnRects[BTN_RIGHT][1].offset(63,0);
btnRects[BTN_UP][1].offset(63,0);
btnRects[BTN_DOWN][1].offset(63,0);
btnRects[BTN_DONE][1].offset(63,0);
btnRects[BTN_LG][1].offset(102,0);
btnRects[BTN_HELP][1].offset(16,0);
btnRects[BTN_TINY][1].offset(0,10);
btnRects[BTN_TALL][1].offset(63,0);
btnRects[BTN_TRAIT][1].offset(63,0);
btnRects[BTN_PUSH][1].offset(30,0);
}
RECT cLed::ledRects[3][2];
void cLed::init(){
RECT baseLed = {0,0,10,14};
for(int i = 0; i < 3; i++)
for(int j = 0; j < 2; j++){
ledRects[i][j] = baseLed;
ledRects[i][j].offset(i * 14, j * 10);
}
}
cLed::cLed(cDialog* parent) :
cButton(parent,CTRL_LED),
state(led_off),
textFont(FONT_BOLD),
textSize(10),
color(parent->getDefTextClr()) {
type = BTN_LED;
}
void cLed::attachClickHandler(click_callback_t f) throw(){
onClick = f;
}
void cLed::attachFocusHandler(focus_callback_t f) throw(){
onFocus = f;
}
bool cLed::triggerFocusHandler(cDialog& me, std::string id, bool losing){
if(onFocus != NULL) return onFocus(me,id,losing);
return true;
}
bool cLed::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
bool result;
eLedState oldState = state;
if(onClick != NULL) result = onClick(me,id,mods);
else{ // simple state toggle
switch(state){
case led_red:
case led_green:
state = led_off;
break;
case led_off:
state = led_red;
}
result = true;
}
if(!triggerFocusHandler(me,id, oldState != led_off)){
result = false;
state = oldState;
}
return result;
}
void cLed::setFormat(eFormat prop, short val) throw(xUnsupportedProp){
if(prop == TXT_FONT) textFont = (eFont) val;
else throw xUnsupportedProp(prop);
}
short cLed::getFormat(eFormat prop) throw(xUnsupportedProp){
if(prop == TXT_FONT) return textFont;
else throw xUnsupportedProp(prop);
}
void cLed::draw(){
RECT from_rect, to_rect;
inWindow->setActive();
if(visible){
TextStyle style;
style.pointSize = 9;
style.lineHeight = 8;
from_rect = ledRects[state][depressed];
to_rect = frame;
to_rect.right = to_rect.left + 14;
rect_draw_some_item(buttons[btnGW[BTN_LED]],from_rect,*inWindow,to_rect);
style.colour = parent->getDefTextClr();
to_rect.right = frame.right;
to_rect.left = frame.left + 18; // Possibly could be 20
win_draw_string(*inWindow,to_rect,lbl,eTextMode::LEFT_TOP,style);
}else{
tileImage(*inWindow,frame,bg_gworld,bg[parent->getBg()]);
}
}
cLedGroup::cLedGroup(cDialog* parent) :
cControl(CTRL_GROUP,*parent),
fromList("none") {}
cButton::~cButton() {}
cLed::~cLed() {}
cLedGroup::~cLedGroup(){
ledIter iter = choices.begin();
while(iter != choices.end()){
delete iter->second;
iter++;
}
}
void cLedGroup::recalcRect(){
ledIter iter = choices.begin();
frame = {INT_MAX, INT_MAX, 0, 0};
while(iter != choices.end()){
RECT otherFrame = iter->second->getBounds();
if(otherFrame.right > frame.right)
frame.right = otherFrame.right;
if(otherFrame.bottom > frame.bottom)
frame.bottom = otherFrame.bottom;
if(otherFrame.left < frame.left)
frame.left = otherFrame.left;
if(otherFrame.top < frame.top)
frame.top = otherFrame.top;
iter++;
}
frame.inset(-6,-6);
}
void cLedGroup::attachClickHandler(click_callback_t f) throw() {
onClick = f;
}
void cLedGroup::attachFocusHandler(focus_callback_t f) throw() {
onFocus = f;
}
void cLed::setState(eLedState to){
state = to;
}
eLedState cLed::getState(){
return state;
}
void cLedGroup::addChoice(cLed* ctrl, std::string key) {
choices[key] = ctrl;
}
bool cLedGroup::handleClick(location where) {
std::string which_clicked;
ledIter iter = choices.begin();
while(iter != choices.end()){
if(iter->second->isVisible() && where.in(iter->second->getBounds())){
if(iter->second->handleClick(where)) {
which_clicked = iter->first;
break;
}
}
iter++;
}
if(which_clicked == "") return false;
clicking = which_clicked;
return true;
}
bool cLedGroup::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
std::string which_clicked = clicking;
clicking = "";
if(choices[which_clicked]->triggerClickHandler(me,which_clicked,mods)){
if(onClick && !onClick(me,id,mods)) return false;
if(!curSelect.empty()) {
choices[curSelect]->setState(led_off);
if(!choices[curSelect]->triggerFocusHandler(me,curSelect,true)){
choices[curSelect]->setState(led_red);
return false;
}
}
choices[which_clicked]->setState(led_red);
if(!choices[which_clicked]->triggerFocusHandler(me,which_clicked,false)){
if(!curSelect.empty())
choices[curSelect]->setState(led_red);
choices[which_clicked]->setState(led_off);
return false;
}
}else return false;
std::string savePrevSelect = prevSelect;
prevSelect = curSelect;
curSelect = which_clicked;
if(!triggerFocusHandler(me,id,false)) {
if(!curSelect.empty())
choices[curSelect]->setState(led_red);
choices[which_clicked]->setState(led_off);
curSelect = prevSelect;
prevSelect = savePrevSelect;
return false;
}
return true;
}
bool cLedGroup::triggerFocusHandler(cDialog& me, std::string id, bool losingFocus){
if(onFocus != NULL) return onFocus(me,id,losingFocus);
return true;
}
void cLedGroup::disable(std::string /*id*/) {
// TODO: Implement this
}
void cLedGroup::enable(std::string /*id*/) {
// TODO: Implement this
}
void cLedGroup::show(std::string id){
choices[id]->show();
}
void cLedGroup::hide(std::string id){
choices[id]->hide();
}
void cLedGroup::setFormat(eFormat prop, short) throw(xUnsupportedProp) {
throw xUnsupportedProp(prop);
}
short cLedGroup::getFormat(eFormat prop) throw(xUnsupportedProp) {
throw xUnsupportedProp(prop);
}
void cLedGroup::setColour(sf::Color) throw(xUnsupportedProp) {
// TODO: Colour is not supported
}
sf::Color cLedGroup::getColour() throw(xUnsupportedProp) {
// TODO: Colour is not supported
return sf::Color();
}
bool cLedGroup::isClickable(){
return true;
}
cLed& cLedGroup::operator[](std::string id){
ledIter iter = choices.find(id);
if(iter == choices.end()) throw std::invalid_argument(id + " does not exist in the ledgroup.");
return *(iter->second);
}
void cLedGroup::setSelected(std::string id){
if(id == "") { // deselect all
if(curSelect == "") return;
eLedState was = choices[curSelect]->getState();
choices[curSelect]->setState(led_off);
if(choices[curSelect]->triggerFocusHandler(*parent,curSelect,true))
curSelect = "";
else
choices[curSelect]->setState(was);
return;
}
ledIter iter = choices.find(id);
if(iter == choices.end()) throw std::invalid_argument(id + " does not exist in the ledgroup.");
if(curSelect == ""){
if(iter->second->triggerFocusHandler(*parent,curSelect,false)){
iter->second->setState(led_red);
curSelect = iter->first;
}
}else{
eLedState a, b;
a = choices[curSelect]->getState();
b = iter->second->getState();
choices[curSelect]->setState(led_off);
iter->second->setState(led_red);
if(!choices[curSelect]->triggerFocusHandler(*parent,curSelect,true)){
choices[curSelect]->setState(a);
iter->second->setState(b);
return;
}
if(!iter->second->triggerFocusHandler(*parent,curSelect,false)){
choices[curSelect]->setState(a);
iter->second->setState(b);
return;
}
curSelect = iter->first;
}
}
std::string cLedGroup::getSelected(){
return curSelect;
}
std::string cLedGroup::getPrevSelection(){
return prevSelect;
}
void cLedGroup::draw(){
ledIter iter = choices.begin();
while(iter != choices.end()){
iter->second->draw();
iter++;
}
}
void cButton::setBtnType(eBtnType newType){
if(type == BTN_LED || newType == BTN_LED) return; // can't change type
type = newType;
}
eBtnType cButton::getBtnType(){
return type;
}