- All Carbon code is gone - Many dialogs converted; some are still left unimplemented since they still need to be converted - Menus converted to a xib file - The giant arrays specifying the configuration of the special node dialog for each special node type have been replaced with maps and sets. Changes to dialogs: - pict choice dialog can now show picts of differing types; this was required for picking a monster graphic, as monsters of all sizes need to be shown in the same dialog - string choice dialog can set the title, and properly shows the currently selected string - LEDs now accept font format - Fixed LED group's calculation of its rect - Fixed LED group crashing if it has no selection - Tabbing between text fields now works - Fix display of larger monster graphics in dialogs - Fix the script element content showing in the browser preview
482 lines
12 KiB
C++
482 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 f __attribute__((unused))) 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, location where){
|
|
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;
|
|
int textMode = 1;
|
|
if(type == BTN_TINY) {
|
|
textMode = 2;
|
|
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->bg]);
|
|
}
|
|
}
|
|
|
|
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 throw xUnsupportedProp(prop);
|
|
}
|
|
|
|
void cButton::setColour(sf::Color clr) throw(xUnsupportedProp) {
|
|
// TODO: Colour is not supported
|
|
}
|
|
|
|
sf::Color cButton::getColour() throw(xUnsupportedProp) {
|
|
// TODO: Colour is not supported
|
|
return sf::Color();
|
|
}
|
|
|
|
void cButton::setBtnType(eBtnType newType) {
|
|
type = newType;
|
|
}
|
|
|
|
eBtnType cButton::getBtnType() {
|
|
return type;
|
|
}
|
|
|
|
// 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->defTextClr) {
|
|
setBtnType(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, location where){
|
|
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->defTextClr;
|
|
to_rect.right = frame.right;
|
|
to_rect.left = frame.left + 18; // Possibly could be 20
|
|
win_draw_string(*inWindow,to_rect,lbl,2,style);
|
|
}else{
|
|
tileImage(*inWindow,frame,bg_gworld,bg[parent->bg]);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/** A click handler is called whenever a click is received, even on the currently selected element. */
|
|
void cLedGroup::attachClickHandler(click_callback_t f) throw() {
|
|
onClick = f;
|
|
}
|
|
|
|
/** A focus handler is called when the currently selected element changes. */
|
|
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, location where){
|
|
std::string which_clicked = clicking;
|
|
clicking = "";
|
|
|
|
if(choices[which_clicked]->triggerClickHandler(me,which_clicked,mods,where)){
|
|
eLedState a, b;
|
|
if(curSelect.empty()) a = led_off;
|
|
else {
|
|
a = choices[curSelect]->getState();
|
|
choices[curSelect]->setState(led_off);
|
|
if(!choices[curSelect]->triggerFocusHandler(me,curSelect,true)){
|
|
choices[curSelect]->setState(a);
|
|
choices[which_clicked]->setState(b);
|
|
return false;
|
|
}
|
|
}
|
|
b = choices[which_clicked]->getState();
|
|
choices[which_clicked]->setState(led_red);
|
|
if(!choices[which_clicked]->triggerFocusHandler(me,which_clicked,false)){
|
|
if(!curSelect.empty())
|
|
choices[curSelect]->setState(a);
|
|
choices[which_clicked]->setState(b);
|
|
return false;
|
|
}
|
|
curSelect = which_clicked;
|
|
}else return false;
|
|
|
|
return triggerFocusHandler(me,id,false);
|
|
}
|
|
|
|
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 __attribute__((unused)), short val __attribute__((unused))) throw(xUnsupportedProp) {
|
|
throw xUnsupportedProp(prop);
|
|
}
|
|
|
|
short cLedGroup::getFormat(eFormat prop __attribute__((unused))) throw(xUnsupportedProp) {
|
|
throw xUnsupportedProp(prop);
|
|
}
|
|
|
|
void cLedGroup::setColour(sf::Color clr) 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::setType(eBtnType newType){
|
|
if(type == BTN_LED) return; // can't change type
|
|
type = newType;
|
|
}
|
|
|
|
eBtnType cButton::getType(){
|
|
return type;
|
|
}
|