Finally implemented the stack control, and used it for town comments in the town details dialog

This commit is contained in:
2014-12-22 13:22:06 -05:00
parent 5e762147bd
commit dcd28b363b
20 changed files with 469 additions and 17 deletions

View File

@@ -145,6 +145,9 @@
915E090C1A317E2E008BDF00 /* map_parse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 915E09081A316D89008BDF00 /* map_parse.cpp */; }; 915E090C1A317E2E008BDF00 /* map_parse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 915E09081A316D89008BDF00 /* map_parse.cpp */; };
9179A4601A42988500FEF872 /* sounds.exa in Copy Sounds and Graphics */ = {isa = PBXBuildFile; fileRef = 9179A45F1A42988200FEF872 /* sounds.exa */; }; 9179A4601A42988500FEF872 /* sounds.exa in Copy Sounds and Graphics */ = {isa = PBXBuildFile; fileRef = 9179A45F1A42988200FEF872 /* sounds.exa */; };
9179A4611A42988800FEF872 /* graphics.exd in Copy Sounds and Graphics */ = {isa = PBXBuildFile; fileRef = 9179A45E1A42986200FEF872 /* graphics.exd */; }; 9179A4611A42988800FEF872 /* graphics.exd in Copy Sounds and Graphics */ = {isa = PBXBuildFile; fileRef = 9179A45E1A42986200FEF872 /* graphics.exd */; };
9179A4651A48683100FEF872 /* stack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9179A4641A48681800FEF872 /* stack.cpp */; };
9179A4661A48683100FEF872 /* stack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9179A4641A48681800FEF872 /* stack.cpp */; };
9179A4671A48683100FEF872 /* stack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9179A4641A48681800FEF872 /* stack.cpp */; };
91870F81190C8C1C0081C150 /* winutil.mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 919145FF18E63B70005CF3A4 /* winutil.mac.mm */; }; 91870F81190C8C1C0081C150 /* winutil.mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 919145FF18E63B70005CF3A4 /* winutil.mac.mm */; };
91870F83190C8C1F0081C150 /* tarball.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BFA3D81902AD78001686E4 /* tarball.cpp */; }; 91870F83190C8C1F0081C150 /* tarball.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BFA3D81902AD78001686E4 /* tarball.cpp */; };
91870F84190C90980081C150 /* scen.menu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 914CA49F190C4E9200B6ADD1 /* scen.menu.xib */; }; 91870F84190C90980081C150 /* scen.menu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 914CA49F190C4E9200B6ADD1 /* scen.menu.xib */; };
@@ -669,6 +672,8 @@
9179A45E1A42986200FEF872 /* graphics.exd */ = {isa = PBXFileReference; lastKnownFileType = folder; path = graphics.exd; sourceTree = "<group>"; }; 9179A45E1A42986200FEF872 /* graphics.exd */ = {isa = PBXFileReference; lastKnownFileType = folder; path = graphics.exd; sourceTree = "<group>"; };
9179A45F1A42988200FEF872 /* sounds.exa */ = {isa = PBXFileReference; lastKnownFileType = folder; path = sounds.exa; sourceTree = "<group>"; }; 9179A45F1A42988200FEF872 /* sounds.exa */ = {isa = PBXFileReference; lastKnownFileType = folder; path = sounds.exa; sourceTree = "<group>"; };
9179A4621A47D4E200FEF872 /* vector2d.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = vector2d.hpp; sourceTree = "<group>"; }; 9179A4621A47D4E200FEF872 /* vector2d.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = vector2d.hpp; sourceTree = "<group>"; };
9179A4631A4867E200FEF872 /* stack.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = stack.hpp; sourceTree = "<group>"; };
9179A4641A48681800FEF872 /* stack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stack.cpp; sourceTree = "<group>"; };
917B573F100B956C0096C978 /* undo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = undo.h; sourceTree = "<group>"; }; 917B573F100B956C0096C978 /* undo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = undo.h; sourceTree = "<group>"; };
918D59A718EA513900735B66 /* dialog.keys.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = dialog.keys.hpp; sourceTree = "<group>"; }; 918D59A718EA513900735B66 /* dialog.keys.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = dialog.keys.hpp; sourceTree = "<group>"; };
919145FB18E3A32F005CF3A4 /* boe.appleevents.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = boe.appleevents.mm; sourceTree = "<group>"; }; 919145FB18E3A32F005CF3A4 /* boe.appleevents.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = boe.appleevents.mm; sourceTree = "<group>"; };
@@ -872,6 +877,7 @@
910BBAB40FB91A26001E34EA /* field.hpp */, 910BBAB40FB91A26001E34EA /* field.hpp */,
910BBAB80FB91ADB001E34EA /* message.hpp */, 910BBAB80FB91ADB001E34EA /* message.hpp */,
910BBAA80FB8F733001E34EA /* pict.hpp */, 910BBAA80FB8F733001E34EA /* pict.hpp */,
9179A4631A4867E200FEF872 /* stack.hpp */,
919145FD18E3C750005CF3A4 /* scrollbar.hpp */, 919145FD18E3C750005CF3A4 /* scrollbar.hpp */,
); );
name = headers; name = headers;
@@ -887,6 +893,7 @@
910BBAB50FB91A26001E34EA /* field.cpp */, 910BBAB50FB91A26001E34EA /* field.cpp */,
910BBAB90FB91ADB001E34EA /* message.cpp */, 910BBAB90FB91ADB001E34EA /* message.cpp */,
910BBAA90FB8F733001E34EA /* pict.cpp */, 910BBAA90FB8F733001E34EA /* pict.cpp */,
9179A4641A48681800FEF872 /* stack.cpp */,
9191460018E63D8E005CF3A4 /* scrollbar.cpp */, 9191460018E63D8E005CF3A4 /* scrollbar.cpp */,
); );
name = src; name = src;
@@ -1598,6 +1605,7 @@
9153253B1A2E5F37000A9A1C /* specials_parse.cpp in Sources */, 9153253B1A2E5F37000A9A1C /* specials_parse.cpp in Sources */,
915E090A1A316EE3008BDF00 /* map_parse.cpp in Sources */, 915E090A1A316EE3008BDF00 /* map_parse.cpp in Sources */,
91597A6F1A3C021400BE7BF9 /* spell.cpp in Sources */, 91597A6F1A3C021400BE7BF9 /* spell.cpp in Sources */,
9179A4671A48683100FEF872 /* stack.cpp in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -1652,6 +1660,7 @@
91BFA3DB1902B13F001686E4 /* tarball.cpp in Sources */, 91BFA3DB1902B13F001686E4 /* tarball.cpp in Sources */,
91BFA3EA19033E01001686E4 /* gzstream.cpp in Sources */, 91BFA3EA19033E01001686E4 /* gzstream.cpp in Sources */,
915E090C1A317E2E008BDF00 /* map_parse.cpp in Sources */, 915E090C1A317E2E008BDF00 /* map_parse.cpp in Sources */,
9179A4661A48683100FEF872 /* stack.cpp in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -1711,6 +1720,7 @@
915325391A2E5F36000A9A1C /* specials_parse.cpp in Sources */, 915325391A2E5F36000A9A1C /* specials_parse.cpp in Sources */,
915E090B1A316EE4008BDF00 /* map_parse.cpp in Sources */, 915E090B1A316EE4008BDF00 /* map_parse.cpp in Sources */,
91597A701A3C021600BE7BF9 /* spell.cpp in Sources */, 91597A701A3C021600BE7BF9 /* spell.cpp in Sources */,
9179A4651A48683100FEF872 /* stack.cpp in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@@ -255,6 +255,18 @@ void cLed::draw(){
} }
} }
cControl::storage_t cLed::store() {
storage_t storage = cButton::store();
storage["led-state"] = getState();
return storage;
}
void cLed::restore(storage_t to) {
cButton::restore(to);
if(to.find("led-state") != to.end())
setState(boost::any_cast<eLedState>(to["led-state"]));
}
cLedGroup::cLedGroup(cDialog* parent) : cLedGroup::cLedGroup(cDialog* parent) :
cControl(CTRL_GROUP,*parent), cControl(CTRL_GROUP,*parent),
fromList("none") {} fromList("none") {}
@@ -307,6 +319,8 @@ eLedState cLed::getState(){
void cLedGroup::addChoice(cLed* ctrl, std::string key) { void cLedGroup::addChoice(cLed* ctrl, std::string key) {
choices[key] = ctrl; choices[key] = ctrl;
if(ctrl->getState() != led_off)
setSelected(key);
} }
bool cLedGroup::handleClick(location where) { bool cLedGroup::handleClick(location where) {
@@ -476,3 +490,15 @@ void cButton::setBtnType(eBtnType newType){
eBtnType cButton::getBtnType(){ eBtnType cButton::getBtnType(){
return type; return type;
} }
cControl::storage_t cLedGroup::store() {
storage_t storage = cControl::store();
storage["led-select"] = getSelected();
return storage;
}
void cLedGroup::restore(storage_t to) {
cControl::restore(to);
if(to.find("led-select") != to.end())
setSelected(boost::any_cast<std::string>(to["led-select"]));
}

View File

@@ -111,6 +111,8 @@ public:
bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus); bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus);
void setFormat(eFormat prop, short val) throw(xUnsupportedProp); void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp); short getFormat(eFormat prop) throw(xUnsupportedProp);
storage_t store();
void restore(storage_t to);
/// Create a new LED button. /// Create a new LED button.
/// @param parent The parent dialog. /// @param parent The parent dialog.
explicit cLed(cDialog* parent); explicit cLed(cDialog* parent);
@@ -175,6 +177,8 @@ public:
void attachFocusHandler(focus_callback_t f) throw(); void attachFocusHandler(focus_callback_t f) throw();
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods); bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus); bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus);
storage_t store();
void restore(storage_t to);
/// Add a new LED to this group. /// Add a new LED to this group.
/// @param ctrl A pointer to the LED, which should already be constructed. /// @param ctrl A pointer to the LED, which should already be constructed.
/// @param key A key to be used to look up the LED later. /// @param key A key to be used to look up the LED later.

View File

@@ -315,3 +315,14 @@ bool cControl::hasKey(){
if(key.spec) return true; if(key.spec) return true;
return key.c != 0; return key.c != 0;
} }
cControl::storage_t cControl::store() {
storage_t storage;
storage["text"] = lbl;
return storage;
}
void cControl::restore(storage_t to) {
if(to.find("text") != to.end())
lbl = boost::any_cast<std::string>(to["text"]);
}

View File

@@ -17,6 +17,7 @@
#include <string> #include <string>
#include <exception> #include <exception>
#include <functional> #include <functional>
#include <boost/any.hpp>
#include "dialog.hpp" #include "dialog.hpp"
#include "location.h" #include "location.h"
@@ -44,7 +45,7 @@ enum eControlType {
CTRL_FIELD, ///< An edit text field CTRL_FIELD, ///< An edit text field
CTRL_TEXT, ///< A static text object CTRL_TEXT, ///< A static text object
CTRL_GROUP, ///< A LED radiobutton-like group CTRL_GROUP, ///< A LED radiobutton-like group
CTRL_STACK, ///< A group of controls that display pages (not implemented yet) CTRL_STACK, ///< A group of controls that represents one element in an array
CTRL_SCROLL,///< A scrollbar CTRL_SCROLL,///< A scrollbar
}; };
@@ -87,6 +88,7 @@ public:
/// a keyboard event is received that should trigger the control. /// a keyboard event is received that should trigger the control.
class cControl { class cControl {
public: public:
using storage_t = std::map<std::string, boost::any>;
/// Attach a keyboard shortcut to a control. Pressing the keyboard shortcut is equivalent to clicking the control. /// Attach a keyboard shortcut to a control. Pressing the keyboard shortcut is equivalent to clicking the control.
/// @param key The desired keyboard shortcut. /// @param key The desired keyboard shortcut.
void attachKey(cKey key); void attachKey(cKey key);
@@ -204,6 +206,12 @@ public:
/// The practical effect of this is that hiding or showing this control automatically hides or shows the label as well. /// The practical effect of this is that hiding or showing this control automatically hides or shows the label as well.
/// @param label A pointer to the control that acts as a label. /// @param label A pointer to the control that acts as a label.
void setLabelCtrl(cControl* label); void setLabelCtrl(cControl* label);
/// Get a view of the control's current state.
/// @return A map of string keys to boost::any values, representing the control's state.
virtual storage_t store();
/// Restore the control to a previous state.
/// @param to A state previously returned from store()
virtual void restore(storage_t to);
/// Create a new control attached to an arbitrary window, rather than a dialog. /// Create a new control attached to an arbitrary window, rather than a dialog.
/// @param t The type of the control. /// @param t The type of the control.
/// @param p The parent window. /// @param p The parent window.

View File

@@ -20,6 +20,7 @@ using namespace ticpp;
#include "field.hpp" #include "field.hpp"
#include "message.hpp" #include "message.hpp"
#include "scrollbar.hpp" #include "scrollbar.hpp"
#include "stack.hpp"
#include "winutil.h" #include "winutil.h"
#include "mathutil.h" #include "mathutil.h"
#include "cursors.h" #include "cursors.h"
@@ -754,6 +755,68 @@ template<> pair<string,cTextField*> cDialog::parse(Element& who /*field*/){
return p; return p;
} }
// Note: This specialization must come last because it requires the other specializations
template<> pair<string,cStack*> cDialog::parse(Element& who /*stack*/) {
pair<string, cStack*> p;
Iterator<Attribute> attr;
Iterator<Element> node;
string name;
p.second = new cStack(*this);
for(attr = attr.begin(&who); attr != attr.end(); attr++) {
attr->GetName(&name);
if(name == "name")
attr->GetValue(&p.first);
else if(name == "pages") {
size_t val;
attr->GetValue(&val);
p.second->setPageCount(val);
} else throw xBadAttr("stack",name,attr->Row(),attr->Column(),fname);
}
vector<string> stack;
for(node = node.begin(&who); node != node.end(); node++) {
string val;
int type = node->Type();
node->GetValue(&val);
if(type == TiXmlNode::ELEMENT) {
if(val == "field") {
auto field = parse<cTextField>(*node);
controls.insert(field);
stack.push_back(field.first);
tabOrder.push_back(field);
} else if(val == "text") {
auto text = parse<cTextMsg>(*node);
controls.insert(text);
stack.push_back(text.first);
} else if(val == "pict") {
auto pict = parse<cPict>(*node);
controls.insert(pict);
stack.push_back(pict.first);
} else if(val == "button") {
auto button = parse<cButton>(*node);
controls.insert(button);
stack.push_back(button.first);
} else if(val == "led") {
auto led = parse<cLed>(*node);
controls.insert(led);
stack.push_back(led.first);
} else if(val == "group") {
auto group = parse<cLedGroup>(*node);
controls.insert(group);
stack.push_back(group.first);
} else throw xBadNode(val,node->Row(),node->Column(),fname);
} else if(type != TiXmlNode::COMMENT)
throw xBadVal("stack",xBadVal::CONTENT,val,node->Row(),node->Column(),fname);
}
p.second->controls = stack;
p.second->recalcRect();
if(p.first == ""){
do{
p.first = generateRandomString();
}while(controls.find(p.first) != controls.end());
}
return p;
}
cDialog::cDialog(cDialog* p) : parent(p) {} cDialog::cDialog(cDialog* p) : parent(p) {}
cDialog::cDialog(std::string path, cDialog* p) : parent(p) { cDialog::cDialog(std::string path, cDialog* p) : parent(p) {
@@ -826,7 +889,12 @@ void cDialog::loadFromFile(std::string path){
controls.insert(parse<cLed>(*node)); controls.insert(parse<cLed>(*node));
else if(type == "group") else if(type == "group")
controls.insert(parse<cLedGroup>(*node)); controls.insert(parse<cLedGroup>(*node));
else throw xBadNode(type,node->Row(),node->Column(),fname); else if(type == "stack") {
auto parsed = parse<cStack>(*node);
controls.insert(parsed);
// Now, if it contains any fields, their tab order must be accounted for
parsed.second->fillTabOrder(specificTabs, reverseTabs);
} else throw xBadNode(type,node->Row(),node->Column(),fname);
} }
// Sort by tab order // Sort by tab order
// First, fill any gaps that might have been left, using ones that had no specific tab order // First, fill any gaps that might have been left, using ones that had no specific tab order
@@ -1192,6 +1260,10 @@ bool cDialog::setFocus(cTextField* newFocus, bool force) {
if(!force) { if(!force) {
if(!this->getControl(currentFocus).triggerFocusHandler(*this, currentFocus, true)) return false; if(!this->getControl(currentFocus).triggerFocusHandler(*this, currentFocus, true)) return false;
} }
if(newFocus == nullptr) {
currentFocus = "";
return true;
}
auto iter = find_if(controls.begin(), controls.end(), [newFocus](std::pair<const std::string, cControl*> p){ auto iter = find_if(controls.begin(), controls.end(), [newFocus](std::pair<const std::string, cControl*> p){
return p.second == newFocus; return p.second == newFocus;
}); });
@@ -1201,6 +1273,12 @@ bool cDialog::setFocus(cTextField* newFocus, bool force) {
return true; return true;
} }
cTextField* cDialog::getFocus() {
auto iter = controls.find(currentFocus);
if(iter == controls.end()) return nullptr;
return dynamic_cast<cTextField*>(iter->second);
}
void cDialog::attachClickHandlers(click_callback_t handler, std::vector<std::string> controls) { void cDialog::attachClickHandlers(click_callback_t handler, std::vector<std::string> controls) {
cDialog& me = *this; cDialog& me = *this;
for(std::string control : controls) { for(std::string control : controls) {
@@ -1437,11 +1515,6 @@ cControl& cDialog::getControl(std::string id) {
cLedGroup* tmp = (cLedGroup*) (iter->second); cLedGroup* tmp = (cLedGroup*) (iter->second);
return tmp->operator[](id); return tmp->operator[](id);
}catch(std::invalid_argument) {} }catch(std::invalid_argument) {}
}else if(iter->second->getType() == CTRL_STACK){ // TODO: Implement stacks
// try{
// cStack* tmp = (cStack*) (iter->second);
// return tmp->operator[](id);
// }catch(std::invalid_argument) {}
} }
iter++; iter++;
} }

View File

@@ -131,7 +131,12 @@ public:
/// @return true if the focus changed; if it returns false, it could mean either that the control did not exist in the dialog /// @return true if the focus changed; if it returns false, it could mean either that the control did not exist in the dialog
/// or that one of the focus handlers prevented the focus change. /// or that one of the focus handlers prevented the focus change.
/// @note This function is intended for internal use, which is why it takes a control pointer instead of a unique key. /// @note This function is intended for internal use, which is why it takes a control pointer instead of a unique key.
/// @note If a null pointer is passed, the focus will be cleared. This means that tabbing between fields will no longer work,
/// though clicking a field to focus it should still work.
bool setFocus(cTextField* newFocus, bool force = false); bool setFocus(cTextField* newFocus, bool force = false);
/// Get the currently focused text field.
/// @return A pointer to the currently focused field.
cTextField* getFocus();
/// Close the dialog. /// Close the dialog.
/// @param triggerFocus true to allow the focus handler of the currently focused text field to prevent the dialog from closing /// @param triggerFocus true to allow the focus handler of the currently focused text field to prevent the dialog from closing
/// @return true unless the currently focused field prevented the dialog from closing /// @return true unless the currently focused field prevented the dialog from closing

View File

@@ -413,3 +413,18 @@ void cTextField::handleInput(cKey key) {
} }
setText(contents); setText(contents);
} }
cControl::storage_t cTextField::store() {
storage_t storage = cControl::store();
storage["fld-ip"] = insertionPoint;
storage["fld-sp"] = selectionPoint;
return storage;
}
void cTextField::restore(storage_t to) {
cControl::restore(to);
if(to.find("fld-ip") != to.end())
insertionPoint = boost::any_cast<int>(to["fld-ip"]);
if(to.find("fld-sp") != to.end())
selectionPoint = boost::any_cast<int>(to["fld-sp"]);
}

View File

@@ -40,6 +40,8 @@ public:
void setFormat(eFormat prop, short val) throw(xUnsupportedProp); void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp); short getFormat(eFormat prop) throw(xUnsupportedProp);
void setColour(sf::Color clr) throw(xUnsupportedProp); void setColour(sf::Color clr) throw(xUnsupportedProp);
storage_t store();
void restore(storage_t to);
/// Get the current input type of the field. /// Get the current input type of the field.
/// @return The input type. /// @return The input type.
eFldType getInputType(); eFldType getInputType();

View File

@@ -1119,3 +1119,18 @@ void cPict::drawAt(sf::RenderWindow& win, rectangle dest, pic_num_t which_g, ePi
pic.setFormat(TXT_FRAME, framed); pic.setFormat(TXT_FRAME, framed);
pic.draw(); pic.draw();
} }
cControl::storage_t cPict::store() {
storage_t storage = cControl::store();
storage["pic-num"] = picNum;
storage["pic-type"] = picType;
return storage;
}
void cPict::restore(storage_t to) {
cControl::restore(to);
if(to.find("pic-num") != to.end())
picNum = boost::any_cast<pic_num_t>(to["pic-num"]);
if(to.find("pic-type") != to.end())
picType = boost::any_cast<ePicType>(to["pic-type"]);
}

View File

@@ -115,6 +115,8 @@ public:
short getFormat(eFormat prop) throw(xUnsupportedProp); short getFormat(eFormat prop) throw(xUnsupportedProp);
void setColour(sf::Color clr) throw(xUnsupportedProp); void setColour(sf::Color clr) throw(xUnsupportedProp);
sf::Color getColour() throw(xUnsupportedProp); sf::Color getColour() throw(xUnsupportedProp);
storage_t store();
void restore(storage_t to);
/// @copydoc setPict(pic_num_t) /// @copydoc setPict(pic_num_t)
/// @param type The type of the new icon /// @param type The type of the new icon
/// @note Calling this function automatically adjusts the bounding rect so that the picture fits perfectly. /// @note Calling this function automatically adjusts the bounding rect so that the picture fits perfectly.

View File

@@ -187,3 +187,15 @@ void cScrollbar::draw() {
from_rect.offset(0,16); from_rect.offset(0,16);
rect_draw_some_item(scroll_gw, from_rect, *inWindow, draw_rect); rect_draw_some_item(scroll_gw, from_rect, *inWindow, draw_rect);
} }
cControl::storage_t cScrollbar::store() {
storage_t storage = cControl::store();
storage["scroll-pos"] = pos;
return storage;
}
void cScrollbar::restore(storage_t to) {
cControl::restore(to);
if(to.find("scroll-pos") != to.end())
pos = boost::any_cast<int>(to["scroll-pos"]);
}

View File

@@ -47,6 +47,8 @@ public:
short getFormat(eFormat prop) throw(xUnsupportedProp); short getFormat(eFormat prop) throw(xUnsupportedProp);
void setColour(sf::Color clr) throw(xUnsupportedProp); void setColour(sf::Color clr) throw(xUnsupportedProp);
sf::Color getColour() throw(xUnsupportedProp); sf::Color getColour() throw(xUnsupportedProp);
storage_t store();
void restore(storage_t to);
bool isClickable(); bool isClickable();
/// Get the scrollbar thumb's current position. /// Get the scrollbar thumb's current position.
/// @return The current position. /// @return The current position.

146
osx/dialogxml/stack.cpp Normal file
View File

@@ -0,0 +1,146 @@
//
// stack.cpp
// BoE
//
// Created by Celtic Minstrel on 14-12-22.
//
//
#include "stack.hpp"
#include "field.hpp"
void cStack::attachClickHandler(click_callback_t f) throw(xHandlerNotSupported) {
onClick = f;
}
void cStack::attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported) {
throw xHandlerNotSupported(true);
}
// TODO: The only reason the handleClick and delegation here is needed is because the engine currently has no concept of layering.
// This means a stack hides any of its controls that happen to end up underneath it.
bool cStack::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods) {
std::string which_clicked = clicking;
clicking = "";
if(onClick) onClick(me, id, mods);
return parent->getControl(which_clicked).triggerClickHandler(me, id, mods);
}
bool cStack::handleClick(location where) {
std::string which_clicked;
auto iter = controls.begin();
while(iter != controls.end()){
if(parent->getControl(*iter).isVisible() && where.in(parent->getControl(*iter).getBounds())){
if(parent->getControl(*iter).handleClick(where)) {
which_clicked = *iter;
break;
}
}
iter++;
}
if(which_clicked == "") return false;
clicking = which_clicked;
return true;
}
void cStack::setFormat(eFormat prop, short) throw(xUnsupportedProp) {
throw xUnsupportedProp(prop);
}
short cStack::getFormat(eFormat prop) throw(xUnsupportedProp) {
throw xUnsupportedProp(prop);
}
void cStack::setColour(sf::Color) throw(xUnsupportedProp) {
// TODO: Colour is not supported
}
sf::Color cStack::getColour() throw(xUnsupportedProp) {
// TODO: Colour is not supported
return sf::Color();
}
bool cStack::isClickable() {
return true;
}
void cStack::draw() {}
bool cStack::setPage(size_t n) {
if(n >= nPages) return false;
if(n == curPage) return true;
cTextField* focus = parent->getFocus();
bool failed = false;
for(auto id : controls) {
cControl& ctrl = parent->getControl(id);
storage[curPage][id] = ctrl.store();
if(!ctrl.triggerFocusHandler(*parent, id, true))
failed = true;
if(!failed) {
ctrl.restore(storage[n][id]);
if(focus == &ctrl && !ctrl.triggerFocusHandler(*parent, id, false)) {
failed = true;
ctrl.restore(storage[curPage][id]);
}
}
}
if(!failed) curPage = n;
return !failed;
}
size_t cStack::getPage() {
return curPage;
}
void cStack::setPageCount(size_t n) {
nPages = n;
storage.resize(nPages);
}
size_t cStack::getPageCount() {
return nPages;
}
void cStack::recalcRect() {
auto iter = controls.begin();
frame = {INT_MAX, INT_MAX, 0, 0};
while(iter != controls.end()){
cControl& ctrl = parent->getControl(*iter);
rectangle otherFrame = ctrl.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);
}
cControl& cStack::operator[](std::string id) {
auto iter = std::find(controls.begin(), controls.end(), id);
if(iter == controls.end()) throw std::invalid_argument(id + " does not exist in the stack.");
return parent->getControl(id);
}
void cStack::fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reverseTabs) {
for(auto id : controls) {
cControl& ctrl = parent->getControl(id);
if(ctrl.getType() == CTRL_FIELD) {
cTextField& field = dynamic_cast<cTextField&>(ctrl);
if(field.tabOrder > 0)
specificTabs.push_back(field.tabOrder);
else if(field.tabOrder < 0)
reverseTabs.push_back(field.tabOrder);
}
}
}
cStack::cStack(cDialog& parent) : cControl(CTRL_STACK, parent) {}

85
osx/dialogxml/stack.hpp Normal file
View File

@@ -0,0 +1,85 @@
//
// stack.hpp
// BoE
//
// Created by Celtic Minstrel on 14-12-22.
//
//
#ifndef BoE_DIALOG_STACK_HPP
#define BoE_DIALOG_STACK_HPP
#include <vector>
#include <map>
#include <string>
#include "control.hpp"
/// A stack groups several controls that represent an array of data.
/// Generally, each control would represent some aspect of a single element of the data.
/// The stack handles updating those controls to displaying different elements of the data,
/// and stores the hidden portion of the array within itself.
/// @note The stack itself provides no mechanism for switching pages. You will need
/// other controls, not within the stack, to trigger the switch.
/// @note Unlike an LED group, a stack does not have ownership of its contained controls.
/// It merely keeps track of a reference to the controls, which are in the parent dialog's
/// dictionary. Thus, a stack requires a parent dialog.
///
/// A stack supports a click handler, which is triggered prior to passing it on to the
/// clicked control, though at present this should not be relied on due to the lack of
/// any layering concept in the engine.
///
/// When the stack's page is changed, the focus handlers for any edit text fields in
/// the stack are triggered with the third parameter set to true to indicate they are
/// losing focus. If any of them return false, the page change is cancelled.
/// In addition, if one of the fields in the stack previously held the focus, its
/// focus handler is called with the third parameter set to false, to indicate that
/// it is gaining focus.
class cStack : public cControl {
friend class cDialog; // So it can insert controls
size_t nPages, curPage = 0;
std::string clicking;
std::vector<std::map<std::string,storage_t>> storage;
std::vector<std::string> controls;
click_callback_t onClick;
public:
void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported);
void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported);
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
bool handleClick(location where);
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp);
void setColour(sf::Color clr) throw(xUnsupportedProp);
sf::Color getColour() throw(xUnsupportedProp);
bool isClickable();
void draw();
/// Switch the stack to a particular page.
/// You need to do this before retrieving data from that page.
/// @param The new page number
/// @return false if the page could not be changed, usually due to a focus handler
bool setPage(size_t n);
/// Get the current page the stack is displaying.
/// @return The current page number
size_t getPage();
/// Set the number of pages in the stack.
/// @param n The new number of pages
/// @note If you reduce the number of pages, some data will be destroyed.
void setPageCount(size_t n);
// Get the number of pages in the stack.
/// @return The number of pages
size_t getPageCount();
/// Recalculate the stack's bounding rect based on its contained controls.
void recalcRect();
/// Retrieve a control reference from the stack.
/// @param id The control's unique ID
cControl& operator[](std::string id);
/// Adds any fields in this stack to the tab order building arrays.
/// Meant for internal use.
void fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reverseTabs);
/// Create a new stack
/// @param parent The parent dialog.
cStack(cDialog& parent);
cStack& operator=(cStack& other) = delete;
cStack(cStack& other) = delete;
};
#endif

View File

@@ -209,8 +209,13 @@ integer that is unique in the dialog.
The `<stack>` tag The `<stack>` tag
----------------- -----------------
The `<stack>` tag is currently unimplemented. Trying to load a dialog The `<stack>` tag groups elements that represent a single entry in an array.
that contains it will crash the game. It can contain any elements except for nested `<stack>` elements.
The `<stack>` tag accepts the following attributes:
* `pages` - The initial number of pages in the stack. This can also be
set programmatically.
Keyboard Shortcuts Keyboard Shortcuts
------------------ ------------------

View File

@@ -13,6 +13,7 @@
#include "button.hpp" #include "button.hpp"
#include "dlogutil.hpp" #include "dlogutil.hpp"
#include "winutil.h" #include "winutil.h"
#include "stack.hpp"
extern short cen_x, cen_y, overall_mode;//,user_given_password; extern short cen_x, cen_y, overall_mode;//,user_given_password;
extern bool mouse_button_held,editing_town; extern bool mouse_button_held,editing_town;
@@ -575,6 +576,11 @@ static bool save_town_details(cDialog& me, std::string, eKeyMod) {
else if(lighting == "dark") town->lighting_type = LIGHT_DARK; else if(lighting == "dark") town->lighting_type = LIGHT_DARK;
else if(lighting == "drains") town->lighting_type = LIGHT_DRAINS; else if(lighting == "drains") town->lighting_type = LIGHT_DRAINS;
else if(lighting == "no-light") town->lighting_type = LIGHT_NONE; else if(lighting == "no-light") town->lighting_type = LIGHT_NONE;
cStack& comments = dynamic_cast<cStack&>(me["cmt"]);
for(int i = 0; i < 3; i++) {
comments.setPage(i);
town->comment[i] = comments["comment"].getText();
}
return true; return true;
} }
@@ -591,6 +597,11 @@ static void put_town_details_in_dlog(cDialog& me) {
case LIGHT_DRAINS: lighting.setSelected("drains"); break; case LIGHT_DRAINS: lighting.setSelected("drains"); break;
case LIGHT_NONE: lighting.setSelected("no-light"); break; case LIGHT_NONE: lighting.setSelected("no-light"); break;
} }
cStack& comments = dynamic_cast<cStack&>(me["cmt"]);
for(int i = 2; i >= 0; i--) {
comments.setPage(i);
comments["comment"].setText(town->comment[i]);
}
} }
void edit_town_details() { void edit_town_details() {
@@ -600,6 +611,12 @@ void edit_town_details() {
town_dlg["chop"].attachFocusHandler(std::bind(check_range_msg, _1, _2, _3, -1, 10000, "The day the town becomes abandoned", "-1 if it doesn't")); town_dlg["chop"].attachFocusHandler(std::bind(check_range_msg, _1, _2, _3, -1, 10000, "The day the town becomes abandoned", "-1 if it doesn't"));
town_dlg["key"].attachFocusHandler(std::bind(check_range_msg, _1, _2, _3, -1, 10, "The event which prevents the town from becoming abandoned", "-1 or 0 for none")); town_dlg["key"].attachFocusHandler(std::bind(check_range_msg, _1, _2, _3, -1, 10, "The event which prevents the town from becoming abandoned", "-1 or 0 for none"));
town_dlg["difficulty"].attachFocusHandler(std::bind(check_range_msg, _1, _2, _3, 0, 10, "The town difficulty", "0 - easiest, 10 - hardest")); town_dlg["difficulty"].attachFocusHandler(std::bind(check_range_msg, _1, _2, _3, 0, 10, "The town difficulty", "0 - easiest, 10 - hardest"));
town_dlg["pick-cmt"].attachFocusHandler([](cDialog& me, std::string id, bool losing) -> bool {
if(losing) return true;
int i = dynamic_cast<cLedGroup&>(me[id]).getSelected()[3] - '0' - 1;
dynamic_cast<cStack&>(me["cmt"]).setPage(i);
return true;
});
put_town_details_in_dlog(town_dlg); put_town_details_in_dlog(town_dlg);

View File

@@ -23,7 +23,7 @@
<xsl:attribute name='class'>dark</xsl:attribute> <xsl:attribute name='class'>dark</xsl:attribute>
</xsl:if> </xsl:if>
<div class='dialog'> <div class='dialog'>
<xsl:for-each select='dialog/pict'> <xsl:for-each select='dialog/pict | dialog/stack/pict'>
<div> <div>
<xsl:attribute name='class'> <xsl:attribute name='class'>
pict pict
@@ -66,7 +66,7 @@
</div> </div>
</xsl:for-each> </xsl:for-each>
<xsl:for-each select='dialog/button'> <xsl:for-each select='dialog/button | dialog/stack/button'>
<div> <div>
<xsl:attribute name='class'> <xsl:attribute name='class'>
button <xsl:value-of select='./@type'/> button <xsl:value-of select='./@type'/>
@@ -88,7 +88,7 @@
</div> </div>
</xsl:for-each> </xsl:for-each>
<xsl:for-each select='dialog/led'> <xsl:for-each select='dialog/led | dialog/stack/led'>
<div> <div>
<xsl:attribute name='class'> <xsl:attribute name='class'>
led led
@@ -117,7 +117,7 @@
</div> </div>
</xsl:for-each> </xsl:for-each>
<xsl:for-each select='dialog/group/led'> <xsl:for-each select='dialog/group/led | dialog/stack/group/led'>
<div> <div>
<xsl:attribute name='class'> <xsl:attribute name='class'>
led led
@@ -147,7 +147,7 @@
</div> </div>
</xsl:for-each> </xsl:for-each>
<xsl:for-each select='dialog/text'> <xsl:for-each select='dialog/text | dialog/stack/text'>
<div> <div>
<xsl:attribute name='class'> <xsl:attribute name='class'>
text text
@@ -180,7 +180,7 @@
</div> </div>
</xsl:for-each> </xsl:for-each>
<xsl:for-each select='dialog/field'> <xsl:for-each select='dialog/field | dialog/stack/field'>
<div class='tfield'> <div class='tfield'>
<xsl:attribute name='style'> <xsl:attribute name='style'>
left: <xsl:value-of select='./@left'/>px; top: <xsl:value-of select='./@top'/>px; left: <xsl:value-of select='./@left'/>px; top: <xsl:value-of select='./@top'/>px;

View File

@@ -7,7 +7,7 @@
<field name='key' top='101' left='327' width='43' height='16'/> <field name='key' top='101' left='327' width='43' height='16'/>
<field name='population' top='219' left='294' width='54' height='16'/> <field name='population' top='219' left='294' width='54' height='16'/>
<field name='difficulty' top='265' left='294' width='54' height='16'/> <field name='difficulty' top='265' left='294' width='54' height='16'/>
<button name='okay' type='regular' top='317' left='309'>OK</button> <button name='okay' type='regular' top='410' left='349'>OK</button>
<pict type='dlog' num='16' top='8' left='8'/> <pict type='dlog' num='16' top='8' left='8'/>
<text size='large' top='6' left='50' width='256' height='17'>Town Details</text> <text size='large' top='6' left='50' width='256' height='17'>Town Details</text>
<text top='30' left='50' width='130' height='14'>Town name:</text> <text top='30' left='50' width='130' height='14'>Town name:</text>
@@ -16,7 +16,7 @@
<text top='98' left='59' width='261' height='41'>Number of event which prevents town death (if -1 or 0, none) - see chapter in documentation on time for more details.</text> <text top='98' left='59' width='261' height='41'>Number of event which prevents town death (if -1 or 0, none) - see chapter in documentation on time for more details.</text>
<text top='143' left='50' width='63' height='14'>Lighting:</text> <text top='143' left='50' width='63' height='14'>Lighting:</text>
<!-- <!--
TODO: Put these LED labels in the LED elementes TODO: Put these LED labels in the LED elements
--> -->
<text top='143' left='122' width='164' height='14'>Fully Lit</text> <text top='143' left='122' width='164' height='14'>Fully Lit</text>
<text top='160' left='122' width='164' height='14'>Dark</text> <text top='160' left='122' width='164' height='14'>Dark</text>
@@ -36,4 +36,17 @@
Town difficulty (0-10): Town difficulty (0-10):
(Determines how fast wandering monsters appear, how nasty traps are, and how hard it is to unlock doors.) (Determines how fast wandering monsters appear, how nasty traps are, and how hard it is to unlock doors.)
</text> </text>
<text top='317' left='50' width='101' height='110'>Comments:<br/>
You can add up to three comments for yourself.
These are not used by the game.
Use the LEDs to the left to switch between them.
</text>
<group name='pick-cmt'>
<led name='cmt1' top='327' left='30' state='red'/>
<led name='cmt2' top='347' left='30'/>
<led name='cmt3' top='367' left='30'/>
</group>
<stack name='cmt' pages='3'>
<field name='comment' top='317' left='162' width='251' height='80'/>
</stack>
</dialog> </dialog>

View File

@@ -266,6 +266,7 @@
<xs:element ref="group"/> <xs:element ref="group"/>
</xs:choice> </xs:choice>
<xs:attribute name="name" type="xs:ID"/> <xs:attribute name="name" type="xs:ID"/>
<xs:attribute name="pages" type="xs:integer"/>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="dialog"> <xs:element name="dialog">