Finally implemented the stack control, and used it for town comments in the town details dialog
This commit is contained in:
@@ -145,6 +145,9 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
91870F83190C8C1F0081C150 /* tarball.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BFA3D81902AD78001686E4 /* tarball.cpp */; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -872,6 +877,7 @@
|
||||
910BBAB40FB91A26001E34EA /* field.hpp */,
|
||||
910BBAB80FB91ADB001E34EA /* message.hpp */,
|
||||
910BBAA80FB8F733001E34EA /* pict.hpp */,
|
||||
9179A4631A4867E200FEF872 /* stack.hpp */,
|
||||
919145FD18E3C750005CF3A4 /* scrollbar.hpp */,
|
||||
);
|
||||
name = headers;
|
||||
@@ -887,6 +893,7 @@
|
||||
910BBAB50FB91A26001E34EA /* field.cpp */,
|
||||
910BBAB90FB91ADB001E34EA /* message.cpp */,
|
||||
910BBAA90FB8F733001E34EA /* pict.cpp */,
|
||||
9179A4641A48681800FEF872 /* stack.cpp */,
|
||||
9191460018E63D8E005CF3A4 /* scrollbar.cpp */,
|
||||
);
|
||||
name = src;
|
||||
@@ -1598,6 +1605,7 @@
|
||||
9153253B1A2E5F37000A9A1C /* specials_parse.cpp in Sources */,
|
||||
915E090A1A316EE3008BDF00 /* map_parse.cpp in Sources */,
|
||||
91597A6F1A3C021400BE7BF9 /* spell.cpp in Sources */,
|
||||
9179A4671A48683100FEF872 /* stack.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1652,6 +1660,7 @@
|
||||
91BFA3DB1902B13F001686E4 /* tarball.cpp in Sources */,
|
||||
91BFA3EA19033E01001686E4 /* gzstream.cpp in Sources */,
|
||||
915E090C1A317E2E008BDF00 /* map_parse.cpp in Sources */,
|
||||
9179A4661A48683100FEF872 /* stack.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1711,6 +1720,7 @@
|
||||
915325391A2E5F36000A9A1C /* specials_parse.cpp in Sources */,
|
||||
915E090B1A316EE4008BDF00 /* map_parse.cpp in Sources */,
|
||||
91597A701A3C021600BE7BF9 /* spell.cpp in Sources */,
|
||||
9179A4651A48683100FEF872 /* stack.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -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) :
|
||||
cControl(CTRL_GROUP,*parent),
|
||||
fromList("none") {}
|
||||
@@ -307,6 +319,8 @@ eLedState cLed::getState(){
|
||||
|
||||
void cLedGroup::addChoice(cLed* ctrl, std::string key) {
|
||||
choices[key] = ctrl;
|
||||
if(ctrl->getState() != led_off)
|
||||
setSelected(key);
|
||||
}
|
||||
|
||||
bool cLedGroup::handleClick(location where) {
|
||||
@@ -476,3 +490,15 @@ void cButton::setBtnType(eBtnType newType){
|
||||
eBtnType cButton::getBtnType(){
|
||||
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"]));
|
||||
}
|
||||
|
||||
@@ -111,6 +111,8 @@ public:
|
||||
bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus);
|
||||
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
|
||||
short getFormat(eFormat prop) throw(xUnsupportedProp);
|
||||
storage_t store();
|
||||
void restore(storage_t to);
|
||||
/// Create a new LED button.
|
||||
/// @param parent The parent dialog.
|
||||
explicit cLed(cDialog* parent);
|
||||
@@ -175,6 +177,8 @@ public:
|
||||
void attachFocusHandler(focus_callback_t f) throw();
|
||||
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
|
||||
bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus);
|
||||
storage_t store();
|
||||
void restore(storage_t to);
|
||||
/// Add a new LED to this group.
|
||||
/// @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.
|
||||
|
||||
@@ -315,3 +315,14 @@ bool cControl::hasKey(){
|
||||
if(key.spec) return true;
|
||||
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"]);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <boost/any.hpp>
|
||||
#include "dialog.hpp"
|
||||
|
||||
#include "location.h"
|
||||
@@ -44,7 +45,7 @@ enum eControlType {
|
||||
CTRL_FIELD, ///< An edit text field
|
||||
CTRL_TEXT, ///< A static text object
|
||||
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
|
||||
};
|
||||
|
||||
@@ -87,6 +88,7 @@ public:
|
||||
/// a keyboard event is received that should trigger the control.
|
||||
class cControl {
|
||||
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.
|
||||
/// @param key The desired keyboard shortcut.
|
||||
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.
|
||||
/// @param label A pointer to the control that acts as a 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.
|
||||
/// @param t The type of the control.
|
||||
/// @param p The parent window.
|
||||
|
||||
@@ -20,6 +20,7 @@ using namespace ticpp;
|
||||
#include "field.hpp"
|
||||
#include "message.hpp"
|
||||
#include "scrollbar.hpp"
|
||||
#include "stack.hpp"
|
||||
#include "winutil.h"
|
||||
#include "mathutil.h"
|
||||
#include "cursors.h"
|
||||
@@ -754,6 +755,68 @@ template<> pair<string,cTextField*> cDialog::parse(Element& who /*field*/){
|
||||
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(std::string path, cDialog* p) : parent(p) {
|
||||
@@ -826,7 +889,12 @@ void cDialog::loadFromFile(std::string path){
|
||||
controls.insert(parse<cLed>(*node));
|
||||
else if(type == "group")
|
||||
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
|
||||
// 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(!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){
|
||||
return p.second == newFocus;
|
||||
});
|
||||
@@ -1201,6 +1273,12 @@ bool cDialog::setFocus(cTextField* newFocus, bool force) {
|
||||
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) {
|
||||
cDialog& me = *this;
|
||||
for(std::string control : controls) {
|
||||
@@ -1437,11 +1515,6 @@ cControl& cDialog::getControl(std::string id) {
|
||||
cLedGroup* tmp = (cLedGroup*) (iter->second);
|
||||
return tmp->operator[](id);
|
||||
}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++;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// 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 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);
|
||||
/// Get the currently focused text field.
|
||||
/// @return A pointer to the currently focused field.
|
||||
cTextField* getFocus();
|
||||
/// Close the dialog.
|
||||
/// @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
|
||||
|
||||
@@ -413,3 +413,18 @@ void cTextField::handleInput(cKey key) {
|
||||
}
|
||||
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"]);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
|
||||
short getFormat(eFormat prop) 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.
|
||||
/// @return The input type.
|
||||
eFldType getInputType();
|
||||
|
||||
@@ -1119,3 +1119,18 @@ void cPict::drawAt(sf::RenderWindow& win, rectangle dest, pic_num_t which_g, ePi
|
||||
pic.setFormat(TXT_FRAME, framed);
|
||||
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"]);
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@ public:
|
||||
short getFormat(eFormat prop) throw(xUnsupportedProp);
|
||||
void setColour(sf::Color clr) throw(xUnsupportedProp);
|
||||
sf::Color getColour() throw(xUnsupportedProp);
|
||||
storage_t store();
|
||||
void restore(storage_t to);
|
||||
/// @copydoc setPict(pic_num_t)
|
||||
/// @param type The type of the new icon
|
||||
/// @note Calling this function automatically adjusts the bounding rect so that the picture fits perfectly.
|
||||
|
||||
@@ -187,3 +187,15 @@ void cScrollbar::draw() {
|
||||
from_rect.offset(0,16);
|
||||
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"]);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ public:
|
||||
short getFormat(eFormat prop) throw(xUnsupportedProp);
|
||||
void setColour(sf::Color clr) throw(xUnsupportedProp);
|
||||
sf::Color getColour() throw(xUnsupportedProp);
|
||||
storage_t store();
|
||||
void restore(storage_t to);
|
||||
bool isClickable();
|
||||
/// Get the scrollbar thumb's current position.
|
||||
/// @return The current position.
|
||||
|
||||
146
osx/dialogxml/stack.cpp
Normal file
146
osx/dialogxml/stack.cpp
Normal 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
85
osx/dialogxml/stack.hpp
Normal 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
|
||||
@@ -209,8 +209,13 @@ integer that is unique in the dialog.
|
||||
The `<stack>` tag
|
||||
-----------------
|
||||
|
||||
The `<stack>` tag is currently unimplemented. Trying to load a dialog
|
||||
that contains it will crash the game.
|
||||
The `<stack>` tag groups elements that represent a single entry in an array.
|
||||
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
|
||||
------------------
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "button.hpp"
|
||||
#include "dlogutil.hpp"
|
||||
#include "winutil.h"
|
||||
#include "stack.hpp"
|
||||
|
||||
extern short cen_x, cen_y, overall_mode;//,user_given_password;
|
||||
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 == "drains") town->lighting_type = LIGHT_DRAINS;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -591,6 +597,11 @@ static void put_town_details_in_dlog(cDialog& me) {
|
||||
case LIGHT_DRAINS: lighting.setSelected("drains"); 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() {
|
||||
@@ -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["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["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);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<xsl:attribute name='class'>dark</xsl:attribute>
|
||||
</xsl:if>
|
||||
<div class='dialog'>
|
||||
<xsl:for-each select='dialog/pict'>
|
||||
<xsl:for-each select='dialog/pict | dialog/stack/pict'>
|
||||
<div>
|
||||
<xsl:attribute name='class'>
|
||||
pict
|
||||
@@ -66,7 +66,7 @@
|
||||
</div>
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:for-each select='dialog/button'>
|
||||
<xsl:for-each select='dialog/button | dialog/stack/button'>
|
||||
<div>
|
||||
<xsl:attribute name='class'>
|
||||
button <xsl:value-of select='./@type'/>
|
||||
@@ -88,7 +88,7 @@
|
||||
</div>
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:for-each select='dialog/led'>
|
||||
<xsl:for-each select='dialog/led | dialog/stack/led'>
|
||||
<div>
|
||||
<xsl:attribute name='class'>
|
||||
led
|
||||
@@ -117,7 +117,7 @@
|
||||
</div>
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:for-each select='dialog/group/led'>
|
||||
<xsl:for-each select='dialog/group/led | dialog/stack/group/led'>
|
||||
<div>
|
||||
<xsl:attribute name='class'>
|
||||
led
|
||||
@@ -147,7 +147,7 @@
|
||||
</div>
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:for-each select='dialog/text'>
|
||||
<xsl:for-each select='dialog/text | dialog/stack/text'>
|
||||
<div>
|
||||
<xsl:attribute name='class'>
|
||||
text
|
||||
@@ -180,7 +180,7 @@
|
||||
</div>
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:for-each select='dialog/field'>
|
||||
<xsl:for-each select='dialog/field | dialog/stack/field'>
|
||||
<div class='tfield'>
|
||||
<xsl:attribute name='style'>
|
||||
left: <xsl:value-of select='./@left'/>px; top: <xsl:value-of select='./@top'/>px;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<field name='key' top='101' left='327' width='43' height='16'/>
|
||||
<field name='population' top='219' 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'/>
|
||||
<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>
|
||||
@@ -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='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='160' left='122' width='164' height='14'>Dark</text>
|
||||
@@ -36,4 +36,17 @@
|
||||
Town difficulty (0-10):
|
||||
(Determines how fast wandering monsters appear, how nasty traps are, and how hard it is to unlock doors.)
|
||||
</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>
|
||||
@@ -266,6 +266,7 @@
|
||||
<xs:element ref="group"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="name" type="xs:ID"/>
|
||||
<xs:attribute name="pages" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="dialog">
|
||||
|
||||
Reference in New Issue
Block a user