Refactor dialog parsing to reduce code duplication

- Refactor dialog format parameter management to use a single protected control function
- Format parameters "frame" and "frame style" merged
- Colour is now a first-class format parameter (though it still has a separate setter)
This commit is contained in:
2020-02-20 23:41:40 -05:00
parent 3c748ca071
commit 12bde373b1
20 changed files with 805 additions and 980 deletions

View File

@@ -226,17 +226,17 @@ private:
friend class cControl; friend class cControl;
}; };
/// Thrown when an invalid element is found while parsing an XML dialog definition. /// Thrown when an invalid node (element or text/cdata) is found while parsing an XML dialog definition.
class xBadNode : public std::exception { class xBadNode : public std::exception {
std::string type, dlg; std::string type, dlg;
int row, col; int row, col;
mutable const char* msg; mutable const char* msg;
public: public:
/// Construct a new exception. /// Construct a new exception.
/// @param t The tag name of the invalid element. /// @param t The tag name of the invalid element, or empty if the invalid node was a text node.
/// @param r The line number of the element in the source. /// @param r The line number of the node in the source.
/// @param c The column number of the element in the source. /// @param c The column number of the node in the source.
/// @param dlg The name of the file in which the element occurred. /// @param dlg The name of the file in which the node occurred.
xBadNode(std::string t, int r, int c, std::string dlg) throw(); xBadNode(std::string t, int r, int c, std::string dlg) throw();
~xBadNode() throw(); ~xBadNode() throw();
/// @return The error message. /// @return The error message.

View File

@@ -90,161 +90,89 @@ void cButton::draw(){
} }
} }
void cButton::setFormat(eFormat prop, short val){ bool cButton::manageFormat(eFormat prop, bool set, boost::any* val) {
if(prop == TXT_WRAP) wrapLabel = val; switch(prop) {
else if(prop == TXT_FRAMESTYLE) frameStyle = eFrameStyle(val); case TXT_FRAME:
else throw xUnsupportedProp(prop); if(val) {
} if(set) frameStyle = boost::any_cast<eFrameStyle>(*val);
else *val = frameStyle;
short cButton::getFormat(eFormat prop){
if(prop == TXT_WRAP) return wrapLabel;
else if(prop == TXT_FRAMESTYLE) return frameStyle;
else throw xUnsupportedProp(prop);
}
void cButton::setColour(sf::Color clr) {
textClr = clr;
}
sf::Color cButton::getColour() {
return textClr;
}
std::string cButton::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp;
Iterator<Attribute> attr;
Iterator<Node> node;
std::string name, id;
int width = 0, height = 0;
bool foundType = false, foundTop = false, foundLeft = false; // required attributes
rectangle frame;
for(attr = attr.begin(&who); attr != attr.end(); attr++){
attr->GetName(&name);
if(name == "name")
attr->GetValue(&id);
else if(name == "wrap"){
std::string val;
attr->GetValue(&val);
if(val == "true") setFormat(TXT_WRAP, true);
}else if(name == "type"){
std::string val;
foundType = true;
attr->GetValue(&val);
if(val == "small")
setBtnType(BTN_SM);
else if(val == "regular")
setBtnType(BTN_REG);
else if(val == "large")
setBtnType(BTN_LG);
else if(val == "help")
setBtnType(BTN_HELP);
else if(val == "left")
setBtnType(BTN_LEFT);
else if(val == "right")
setBtnType(BTN_RIGHT);
else if(val == "up")
setBtnType(BTN_UP);
else if(val == "down")
setBtnType(BTN_DOWN);
else if(val == "tiny")
setBtnType(BTN_TINY);
else if(val == "done")
setBtnType(BTN_DONE);
else if(val == "tall")
setBtnType(BTN_TALL);
else if(val == "trait")
setBtnType(BTN_TRAIT);
else if(val == "push")
setBtnType(BTN_PUSH);
}else if(name == "color" || name == "colour"){
std::string val;
attr->GetValue(&val);
sf::Color clr;
try{
clr = parseColor(val);
}catch(int){
throw xBadVal("button",name,val,attr->Row(),attr->Column(),fname);
} }
setColour(clr); break;
}else if(name == "def-key"){ case TXT_WRAP:
std::string val; if(val) {
attr->GetValue(&val); if(set) wrapLabel = boost::any_cast<bool>(*val);
try{ else *val = wrapLabel;
}
break;
case TXT_COLOUR:
if(val) {
if(set) textClr = boost::any_cast<sf::Color>(*val);
else *val = textClr;
}
break;
default: return false;
}
return true;
}
bool cButton::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
std::string name = attr.Name();
if(name == "type") {
std::string val = attr.Value();
if(val == "small") setBtnType(BTN_SM);
else if(val == "regular") setBtnType(BTN_REG);
else if(val == "large") setBtnType(BTN_LG);
else if(val == "help") setBtnType(BTN_HELP);
else if(val == "left") setBtnType(BTN_LEFT);
else if(val == "right") setBtnType(BTN_RIGHT);
else if(val == "up") setBtnType(BTN_UP);
else if(val == "down") setBtnType(BTN_DOWN);
else if(val == "tiny") setBtnType(BTN_DONE);
else if(val == "done") setBtnType(BTN_DONE);
else if(val == "tall") setBtnType(BTN_TALL);
else if(val == "trait") setBtnType(BTN_TRAIT);
else if(val == "push") setBtnType(BTN_PUSH);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
} else if(name == "def-key") {
std::string val = attr.Value();
try {
attachKey(parseKey(val)); attachKey(parseKey(val));
}catch(int){ } catch(int) {
throw xBadVal("button",name,val,attr->Row(),attr->Column(),fname); throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
} }
// }else if(name == "fromlist"){ return true;
// attr->GetValue(&fromList);
}else if(name == "top"){
attr->GetValue(&frame.top), foundTop = true;
}else if(name == "left"){
attr->GetValue(&frame.left), foundLeft = true;
}else if(name == "width"){
attr->GetValue(&width);
}else if(name == "height"){
attr->GetValue(&height);
}else throw xBadAttr("button",name,attr->Row(),attr->Column(),fname);
} }
if(parent->getBg() == cDialog::BG_DARK && (getBtnType() == BTN_TINY || getBtnType() == BTN_PUSH)) return cControl::parseAttribute(attr, tagName, fname);
setColour(sf::Color::White); }
if(!foundType) throw xMissingAttr("button","type",who.Row(),who.Column(),fname);
if(!foundTop) throw xMissingAttr("button","top",who.Row(),who.Column(),fname); bool cButton::parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) {
if(!foundLeft) throw xMissingAttr("button","left",who.Row(),who.Column(),fname); if(content.Type() == TiXmlNode::TEXT) {
switch(getBtnType()){ text += dlogStringFilter(content.Value());
case BTN_SM: return true;
frame.right = frame.left + 23; } else if(content.Value() == "key") {
frame.bottom = frame.top + 23;
break;
case BTN_LG:
frame.right = frame.left + 102;
frame.bottom = frame.top + 23;
break;
case BTN_HELP:
frame.right = frame.left + 16;
frame.bottom = frame.top + 13;
break;
case BTN_TINY:
case BTN_LED: // this should never happen
frame.right = frame.left + 14;
frame.bottom = frame.top + 10;
break;
case BTN_TALL:
case BTN_TRAIT:
frame.right = frame.left + 63;
frame.bottom = frame.top + 40;
break;
case BTN_PUSH:
frame.right = frame.left + 30;
frame.bottom = frame.top + 30;
break;
default:
frame.right = frame.left + 63;
frame.bottom = frame.top + 23;
}
if(width > 0)
frame.right = frame.left + width;
if(height > 0)
frame.bottom = frame.top + height;
setBounds(frame);
std::string content;
for(node = node.begin(&who); node != node.end(); node++){
std::string val;
int type = node->Type();
node->GetValue(&val);
if(type == TiXmlNode::ELEMENT && val == "key"){
// TODO: There's surely a better way to do this // TODO: There's surely a better way to do this
if(content.length() > 0) throw xBadVal("button",xBadVal::CONTENT,content + val,node->Row(),node->Column(),fname); if(text.length() > 0) throw xBadVal("button", xBadVal::CONTENT, text + "<key/>", content.Row(), content.Column(), fname);
// labelWithKey = true; labelWithKey = true;
}else if(type == TiXmlNode::TEXT) return true;
content += dlogStringFilter(val);
else if(type != TiXmlNode::COMMENT) {
val = '<' + val + '>';
throw xBadVal("button",xBadVal::CONTENT,val,node->Row(),node->Column(),fname);
} }
return cControl::parseContent(content, n, tagName, fname, text);
}
void cButton::validatePostParse(ticpp::Element& elem, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& elems) {
cControl::validatePostParse(elem, fname, attrs, elems);
if(getType() == CTRL_BTN && !attrs.count("type")) throw xMissingAttr(elem.Value(), "type", elem.Row(), elem.Column(), fname);
static const std::set<eBtnType> labelledButtons{BTN_TINY, BTN_LED, BTN_PUSH};
if(labelledButtons.count(type)) {
if(!attrs.count("color") && !attrs.count("colour") && parent->getBg() == cDialog::BG_DARK)
setColour(sf::Color::White);
if(!attrs.count("width"))
throw xMissingAttr(elem.Value(), "width", elem.Row(), elem.Column(), fname);
} }
setText(content); }
return id;
location cButton::getPreferredSize() {
return {btnRects[type][0].width(), btnRects[type][0].height()};
} }
// Indices within the buttons array. // Indices within the buttons array.
@@ -348,18 +276,35 @@ void cLed::callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, std::str
} }
} }
void cLed::setFormat(eFormat prop, short val){ bool cLed::manageFormat(eFormat prop, bool set, boost::any* val) {
if(prop == TXT_FONT) textFont = (eFont) val; switch(prop) {
else if(prop == TXT_SIZE) textSize = val; case TXT_FONT:
else if(prop == TXT_WRAP) wrapLabel = val; if(val) {
else throw xUnsupportedProp(prop); if(set) textFont = boost::any_cast<eFont>(*val);
} else *val = textFont;
}
short cLed::getFormat(eFormat prop){ break;
if(prop == TXT_FONT) return textFont; case TXT_SIZE:
else if(prop == TXT_SIZE) return textSize; if(val) {
else if(prop == TXT_WRAP) return wrapLabel; if(set) textSize = boost::any_cast<short>(*val);
else throw xUnsupportedProp(prop); else *val = textSize;
}
break;
case TXT_WRAP:
if(val) {
if(set) wrapLabel = boost::any_cast<bool>(*val);
else *val = wrapLabel;
}
break;
case TXT_COLOUR:
if(val) {
if(set) textClr = boost::any_cast<sf::Color>(*val);
else *val = textClr;
}
break;
default: return false;
}
return true;
} }
void cLed::draw(){ void cLed::draw(){
@@ -439,104 +384,22 @@ eLedState cLed::getState(){
return state; return state;
} }
std::string cLed::parse(ticpp::Element& who, std::string fname) { bool cLed::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
using namespace ticpp; if(attr.Name() == "state") {
Iterator<Attribute> attr; std::string val = attr.Value();
Iterator<Node> node;
std::string name, id;
int width = 0, height = 0;
bool foundTop = false, foundLeft = false; // requireds
rectangle frame;
if(parent->getBg() == cDialog::BG_DARK)
setColour(sf::Color::White);
for(attr = attr.begin(&who); attr != attr.end(); attr++){
attr->GetName(&name);
if(name == "name")
attr->GetValue(&id);
else if(name == "state"){
std::string val;
attr->GetValue(&val);
if(val == "red") setState(led_red); if(val == "red") setState(led_red);
else if(val == "green") setState(led_green); else if(val == "green") setState(led_green);
else if(val == "off") setState(led_off); else if(val == "off") setState(led_off);
else throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname); else throw xBadVal(tagName, attr.Name(), val, attr.Row(), attr.Column(), fname);
// }else if(name == "fromlist"){ return true;
// attr->GetValue(&fromList);
}else if(name == "font"){
std::string val;
attr->GetValue(&val);
if(val == "dungeon")
setFormat(TXT_FONT, FONT_DUNGEON);
else if(val == "plain")
setFormat(TXT_FONT, FONT_PLAIN);
else if(val == "bold")
setFormat(TXT_FONT, FONT_BOLD);
else if(val == "maidenword")
setFormat(TXT_FONT, FONT_MAIDWORD);
else throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname);
}else if(name == "size"){
std::string val;
attr->GetValue(&val);
if(val == "large")
setFormat(TXT_SIZE, 12);
else if(val == "small")
setFormat(TXT_SIZE, 10);
else if(val == "title")
setFormat(TXT_SIZE, 18);
else throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname);
}else if(name == "color" || name == "colour"){
std::string val;
attr->GetValue(&val);
sf::Color clr;
try{
clr = parseColor(val);
}catch(int){
throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname);
} }
setColour(clr); return cButton::parseAttribute(attr, tagName, fname);
} else if(name == "wrap") { }
std::string val;
attr->GetValue(&val); bool cLed::parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) {
if(val == "true") if(content.Type() == TiXmlNode::ELEMENT && content.Value() == "key")
setFormat(TXT_WRAP, true); return false;
else setFormat(TXT_WRAP, false); return cButton::parseContent(content, n, tagName, fname, text);
}else if(name == "top"){
attr->GetValue(&frame.top), foundTop = true;
}else if(name == "left"){
attr->GetValue(&frame.left), foundLeft = true;
}else if(name == "width"){
attr->GetValue(&width);
}else if(name == "height"){
attr->GetValue(&height);
}else throw xBadAttr("led",name,attr->Row(),attr->Column(),fname);
}
if(!foundTop) throw xMissingAttr("led","top",who.Row(),who.Column(),fname);
if(!foundLeft) throw xMissingAttr("led","left",who.Row(),who.Column(),fname);
if(width > 0) {
frame.right = frame.left + width;
}else{
frame.right = frame.left + 14;
}
if(height > 0) {
frame.bottom = frame.top + height;
}else{
frame.bottom = frame.top + 10;
}
setBounds(frame);
std::string content;
for(node = node.begin(&who); node != node.end(); node++){
std::string val;
int type = node->Type();
node->GetValue(&val);
if(type == TiXmlNode::TEXT)
content += dlogStringFilter(val);
else if(type != TiXmlNode::COMMENT) {
val = '<' + val + '>';
throw xBadVal("led",xBadVal::CONTENT,content + val,node->Row(),node->Column(),fname);
}
}
setText(content);
return id;
} }
void cLedGroup::addChoice(cLed* ctrl, std::string key) { void cLedGroup::addChoice(cLed* ctrl, std::string key) {
@@ -627,27 +490,6 @@ void cLedGroup::hide(std::string id){
choices[id]->hide(); choices[id]->hide();
} }
void cLedGroup::setFormat(eFormat prop, short val) {
if(prop == TXT_FRAME) drawFramed = val;
else if(prop == TXT_FRAMESTYLE) frameStyle = eFrameStyle(val);
else throw xUnsupportedProp(prop);
}
short cLedGroup::getFormat(eFormat prop) {
if(prop == TXT_FRAME) return drawFramed;
else if(prop == TXT_FRAMESTYLE) return frameStyle;
throw xUnsupportedProp(prop);
}
void cLedGroup::setColour(sf::Color) {
// TODO: Colour is not supported
}
sf::Color cLedGroup::getColour() {
// TODO: Colour is not supported
return sf::Color();
}
bool cLedGroup::isClickable(){ bool cLedGroup::isClickable(){
return true; return true;
} }
@@ -725,7 +567,7 @@ void cLedGroup::draw(){
iter->second->draw(); iter->second->draw();
iter++; iter++;
} }
if(drawFramed) drawFrame(2, frameStyle); drawFrame(2, frameStyle);
} }
void cButton::setBtnType(eBtnType newType){ void cButton::setBtnType(eBtnType newType){
@@ -756,8 +598,8 @@ void cButton::setBtnType(eBtnType newType){
frame.height() = 40; frame.height() = 40;
break; break;
case BTN_PUSH: case BTN_PUSH:
frame.width() = 30; frame.width() = std::min<int>(frame.width(), 30);
frame.height() = 30; frame.height() = std::min<int>(frame.height(), 30);
break; break;
case BTN_TINY: case BTN_TINY:
case BTN_LED: case BTN_LED:
@@ -789,42 +631,22 @@ void cLedGroup::forEach(std::function<void(std::string,cControl&)> callback) {
callback(ctrl.first, *ctrl.second); callback(ctrl.first, *ctrl.second);
} }
std::string cLedGroup::parse(ticpp::Element& who, std::string fname) { bool cLedGroup::parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) {
using namespace ticpp; std::string val = content.Value();
Iterator<Attribute> attr; int type = content.Type();
Iterator<Element> node;
std::string name, id;
for(attr = attr.begin(&who); attr != attr.end(); attr++){
attr->GetName(&name);
if(name == "name")
attr->GetValue(&id);
// else if(name == "fromlist")
// attr->GetValue(&fromList);
else if(name == "framed"){
std::string val;
attr->GetValue(&val);
if(val == "true") setFormat(TXT_FRAME, true);
}else if(name == "outline") {
std::string val;
attr->GetValue(&val);
if(val == "solid") setFormat(TXT_FRAMESTYLE, FRM_SOLID);
else if(val == "inset") setFormat(TXT_FRAMESTYLE, FRM_INSET);
else if(val == "outset") setFormat(TXT_FRAMESTYLE, FRM_OUTSET);
else if(val == "double") setFormat(TXT_FRAMESTYLE, FRM_DOUBLE);
}else throw xBadAttr("group",name,attr->Row(),attr->Column(),fname);
}
for(node = node.begin(&who); node != node.end(); node++){
std::string val;
int type = node->Type();
node->GetValue(&val);
if(type == TiXmlNode::ELEMENT && val == "led"){ if(type == TiXmlNode::ELEMENT && val == "led"){
auto led = parent->parse<cLed>(*node); auto led = parent->parse<cLed>(dynamic_cast<ticpp::Element&>(content));
addChoice(led.second, led.first); addChoice(led.second, led.first);
}else if(type != TiXmlNode::COMMENT) { return true;
val = '<' + val + '>'; } else if(type == TiXmlNode::TEXT) {
throw xBadVal("group",xBadVal::CONTENT,val,node->Row(),node->Column(),fname); return false;
} }
} return cContainer::parseContent(content, n, tagName, fname, text);
recalcRect(); }
return id;
void cLedGroup::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
// Don't defer to super-class; groups are an abstract container that doesn't require a position.
//cControl::validatePostParse(who, fname, attrs, nodes);
recalcRect();
frameStyle = FRM_NONE;
} }

View File

@@ -50,11 +50,10 @@ class cButton : public cControl {
public: public:
/// @copydoc cDialog::init() /// @copydoc cDialog::init()
static void init(); static void init();
std::string parse(ticpp::Element& who, std::string fname); bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
void setFormat(eFormat prop, short val); bool parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) override;
short getFormat(eFormat prop); void validatePostParse(ticpp::Element& elem, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& elems) override;
void setColour(sf::Color clr); location getPreferredSize() override;
sf::Color getColour();
/// Set the type of this button. /// Set the type of this button.
/// @param newType The desired button type. /// @param newType The desired button type.
void setBtnType(eBtnType newType); void setBtnType(eBtnType newType);
@@ -88,6 +87,7 @@ protected:
/// @param t The type of control. Should be either CTRL_LED or CTRL_BTN. /// @param t The type of control. Should be either CTRL_LED or CTRL_BTN.
cButton(cDialog& parent,eControlType t); cButton(cDialog& parent,eControlType t);
private: private:
bool manageFormat(eFormat prop, bool set, boost::any* val) override;
bool labelWithKey; bool labelWithKey;
std::string fromList; std::string fromList;
static rectangle btnRects[13][2]; static rectangle btnRects[13][2];
@@ -114,9 +114,8 @@ public:
/// default toggle-selected action of an LED. /// default toggle-selected action of an LED.
/// @return true to indicate the event should continue. /// @return true to indicate the event should continue.
static bool noAction(cDialog&,std::string,eKeyMod) {return true;} static bool noAction(cDialog&,std::string,eKeyMod) {return true;}
std::string parse(ticpp::Element& who, std::string fname) override; bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
void setFormat(eFormat prop, short val) override; bool parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) override;
short getFormat(eFormat prop) override;
storage_t store() override; storage_t store() override;
void restore(storage_t to) override; void restore(storage_t to) override;
/// Create a new LED button. /// Create a new LED button.
@@ -141,6 +140,7 @@ public:
private: private:
void defaultClickHandler(cDialog&, std::string, eKeyMod); void defaultClickHandler(cDialog&, std::string, eKeyMod);
void callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, std::string id, eKeyMod mods) override; void callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, std::string id, eKeyMod mods) override;
bool manageFormat(eFormat prop, bool set, boost::any* val) override;
eLedState state; eLedState state;
eFont textFont; eFont textFont;
short textSize; short textSize;
@@ -166,7 +166,6 @@ private:
/// However, when the focus handler of the LED group is called, the selection _has_ been updated., /// However, when the focus handler of the LED group is called, the selection _has_ been updated.,
/// so getSelected() will return the new selection. (This is the reason for the getPreviousSelection() method.) /// so getSelected() will return the new selection. (This is the reason for the getPreviousSelection() method.)
class cLedGroup : public cContainer { class cLedGroup : public cContainer {
bool drawFramed = false;
std::map<std::string,cLed*> choices; std::map<std::string,cLed*> choices;
std::string fromList; std::string fromList;
std::string curSelect, prevSelect; std::string curSelect, prevSelect;
@@ -177,7 +176,8 @@ class cLedGroup : public cContainer {
public: public:
/// @deprecated in favour of @ref attachEventHandler /// @deprecated in favour of @ref attachEventHandler
void attachFocusHandler(std::function<bool(cDialog&,std::string,bool)> f) override; void attachFocusHandler(std::function<bool(cDialog&,std::string,bool)> f) override;
std::string parse(ticpp::Element& who, std::string fname) override; bool parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) override;
void validatePostParse(ticpp::Element& elem, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& elems) override;
/// @copydoc cControl::attachClickHandler() /// @copydoc cControl::attachClickHandler()
/// ///
/// The click handler is called whenever an LED in the group is clicked, even if it's the currently selected LED. /// The click handler is called whenever an LED in the group is clicked, even if it's the currently selected LED.
@@ -210,10 +210,6 @@ public:
/// Show one of the choices in this group. /// Show one of the choices in this group.
/// @param id The unique key of the choice. /// @param id The unique key of the choice.
void show(std::string id); void show(std::string id);
void setFormat(eFormat prop, short val) override;
short getFormat(eFormat prop) override;
void setColour(sf::Color clr) override;
sf::Color getColour() override;
/// Create a new LED group. /// Create a new LED group.
/// @param parent The parent dialog. /// @param parent The parent dialog.
explicit cLedGroup(cDialog& parent); explicit cLedGroup(cDialog& parent);

View File

@@ -67,9 +67,6 @@ const char* xUnsupportedProp::what() const throw(){
case TXT_FRAME: case TXT_FRAME:
s = "TXT_FRAME"; s = "TXT_FRAME";
break; break;
case TXT_FRAMESTYLE:
s = "TXT_FRAMESTYLE";
break;
case TXT_FONT: case TXT_FONT:
s = "TXT_FONT"; s = "TXT_FONT";
break; break;
@@ -79,8 +76,10 @@ const char* xUnsupportedProp::what() const throw(){
case TXT_WRAP: case TXT_WRAP:
s = "TXT_WRAP"; s = "TXT_WRAP";
break; break;
case TXT_COLOUR:
s = "TXT_COLOUR";
break;
} }
// TODO: Support colour, which doesn't use the setFormat function
sprintf(msg,"Format property %s not valid for this control.\n",s.c_str()); sprintf(msg,"Format property %s not valid for this control.\n",s.c_str());
} }
return msg; return msg;
@@ -188,6 +187,69 @@ void cControl::setActive(bool active) {
depressed = active; depressed = active;
} }
void cControl::setFormat(eFormat prop, short val) {
boost::any newVal;
switch(prop) {
case TXT_WRAP:
newVal = bool(val);
break;
case TXT_FONT:
newVal = eFont(val);
break;
case TXT_SIZE:
newVal = val;
break;
case TXT_FRAME:
newVal = eFrameStyle(val);
break;
case TXT_COLOUR: // Interpret as a shade of grey
newVal = sf::Color{val, val, val};
break;
}
if(!manageFormat(prop, true, &newVal))
throw xUnsupportedProp(prop);
}
short cControl::getFormat(eFormat prop) {
boost::any curVal;
if(!manageFormat(prop, false, &curVal))
throw xUnsupportedProp(prop);
switch(prop) {
case TXT_WRAP:
return boost::any_cast<bool>(curVal);
case TXT_FONT:
return boost::any_cast<eFont>(curVal);
case TXT_SIZE:
return boost::any_cast<short>(curVal);
case TXT_FRAME:
return boost::any_cast<eFrameStyle>(curVal);
case TXT_COLOUR: // Interpret as a shade of grey
return boost::any_cast<sf::Color>(curVal).toInteger();
}
return 0;
}
bool cControl::canFormat(eFormat prop) {
return manageFormat(prop, false, nullptr);
}
void cControl::setColour(sf::Color clr) {
boost::any newVal = clr;
if(!manageFormat(TXT_COLOUR, true, &newVal))
throw xUnsupportedProp(TXT_COLOUR);
}
sf::Color cControl::getColour() {
boost::any curVal;
if(!manageFormat(TXT_COLOUR, false, &curVal))
throw xUnsupportedProp(TXT_COLOUR);
return boost::any_cast<sf::Color>(curVal);
}
bool cControl::manageFormat(eFormat, bool, boost::any*) {
return false;
}
void cControl::redraw() { void cControl::redraw() {
// If there's no parent dialog, we're not responsible for redrawing // If there's no parent dialog, we're not responsible for redrawing
if(parent) parent->draw(); if(parent) parent->draw();
@@ -312,6 +374,7 @@ bool cControl::triggerFocusHandler(cDialog& dlg, std::string id, bool losing){
} }
void cControl::drawFrame(short amt, eFrameStyle frameStyle){ void cControl::drawFrame(short amt, eFrameStyle frameStyle){
if(frameStyle == FRM_NONE) return;
// dk_gray had a 0..65535 component of 12287, and med_gray had a 0..65535 component of 24574 // dk_gray had a 0..65535 component of 12287, and med_gray had a 0..65535 component of 24574
static sf::Color lt_gray = {224,224,224},dk_gray = {48,48,48}; static sf::Color lt_gray = {224,224,224},dk_gray = {48,48,48};
rectangle rect = frame, ul_rect; rectangle rect = frame, ul_rect;
@@ -340,6 +403,122 @@ void cControl::drawFrame(short amt, eFrameStyle frameStyle){
} }
} }
std::string cControl::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp;
std::string tagName, id;
who.GetValue(&tagName);
Iterator<Attribute> attr;
Iterator<Node> node;
std::set<std::string> foundAttrs;
std::multiset<std::string> foundNodes;
int width = 0, height = 0;
rectangle frame;
for(attr = attr.begin(&who); attr != attr.end(); attr++){
std::string attrName = attr->Name();
foundAttrs.insert(attrName);
if(attrName == "name") attr->GetValue(&id);
else if(attrName == "top") attr->GetValue(&frame.top);
else if(attrName == "left") attr->GetValue(&frame.left);
else if(attrName == "width") attr->GetValue(&width);
else if(attrName == "height") attr->GetValue(&height);
else if(!parseAttribute(*attr, tagName, fname))
throw xBadAttr(tagName, attrName, attr->Row(), attr->Column(), fname);
}
std::string text;
for(node = node.begin(&who); node != node.end(); node++){
int type = node->Type();
std::string nodeTag;
if(type == TiXmlNode::ELEMENT)
nodeTag = node->Value();
if(type == TiXmlNode::COMMENT) continue;
else if(!parseContent(*node, foundNodes.count(nodeTag), tagName, fname, text)) {
std::string val = nodeTag.empty() ? nodeTag : xBadVal::CONTENT;
throw xBadVal(tagName, xBadVal::CONTENT, val, node->Row(), node->Column(), fname);
}
foundNodes.insert(nodeTag);
}
setText(text);
location bestSz = getPreferredSize();
frame.width() = width > 0 ? width : bestSz.x;
frame.height() = height > 0 ? height : bestSz.y;
setBounds(frame);
validatePostParse(who, fname, foundAttrs, foundNodes);
return id;
}
bool cControl::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
std::string name;
attr.GetName(&name);
// Colour and formatting, if supported
if(name == "framed" && canFormat(TXT_FRAME)) {
std::string val;
attr.GetValue(&val);
if(val == "true") setFormat(TXT_FRAME, FRM_SOLID);
else if(val == "false") setFormat(TXT_FRAME, FRM_NONE);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
if(name == "outline" && canFormat(TXT_FRAME)) {
std::string val;
attr.GetValue(&val);
if(val == "none") setFormat(TXT_FRAME, FRM_NONE);
else if(val == "solid") setFormat(TXT_FRAME, FRM_SOLID);
else if(val == "inset") setFormat(TXT_FRAME, FRM_INSET);
else if(val == "outset") setFormat(TXT_FRAME, FRM_OUTSET);
else if(val == "double") setFormat(TXT_FRAME, FRM_DOUBLE);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
if(name == "font" && canFormat(TXT_FONT)) {
std::string val;
attr.GetValue(&val);
if(val == "plain") setFormat(TXT_FONT, FONT_PLAIN);
else if(val == "bold") setFormat(TXT_FONT, FONT_BOLD);
else if(val == "dungeon") setFormat(TXT_FONT, FONT_DUNGEON);
else if(val == "maidenword") setFormat(TXT_FONT, FONT_MAIDWORD);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
if(name == "size" && canFormat(TXT_SIZE)) {
std::string val;
attr.GetValue(&val);
if(val == "small") setFormat(TXT_SIZE, 10);
else if(val == "large") setFormat(TXT_SIZE, 12);
else if(val == "title") setFormat(TXT_SIZE, 18);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
if(name == "wrap" && canFormat(TXT_WRAP)) {
std::string val;
attr.GetValue(&val);
if(val == "true") setFormat(TXT_WRAP, true);
else if(val == "false") setFormat(TXT_WRAP, false);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
if((name == "color" || name == "colour") && canFormat(TXT_COLOUR)) {
std::string val;
try{
sf::Color clr = parseColor(val);
setColour(clr);
} catch(int) {
throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
}
return true;
}
return false;
}
bool cControl::parseContent(ticpp::Node&, int, std::string, std::string, std::string&) {
return false;
}
void cControl::validatePostParse(ticpp::Element& elem, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>&) {
if(!attrs.count("left")) throw xMissingAttr(elem.Value(), "left", elem.Row(), elem.Column(), fname);
if(!attrs.count("top")) throw xMissingAttr(elem.Value(), "top", elem.Row(), elem.Column(), fname);
}
cControl::~cControl() {} cControl::~cControl() {}
eControlType cControl::getType(){ eControlType cControl::getType(){

View File

@@ -25,15 +25,16 @@
/// Formatting properties /// Formatting properties
enum eFormat { enum eFormat {
TXT_FRAME, ///< Whether to draw a frame around the control. Should be a boolean (true or false). TXT_FRAME, ///< The control's frame style. Should be an enum from @ref eFrameStyle.
TXT_FONT, ///< The control's text font. Should be one of the constants FONT_PLAIN, FONT_BOLD, FONT_DUNGEON, FONT_MAIDEN. TXT_FONT, ///< The control's text font. Should be one of the constants FONT_PLAIN, FONT_BOLD, FONT_DUNGEON, FONT_MAIDEN.
TXT_SIZE, ///< The control's text size. Should be an integer indicating point size. TXT_SIZE, ///< The control's text size. Should be an integer indicating point size.
TXT_WRAP, ///< Whether the control should wrap. Should be a boolean (true or false). TXT_WRAP, ///< Whether the control should wrap. Should be a boolean (true or false).
TXT_FRAMESTYLE, ///< The control's frame style. Should be an enum from @ref eFrameStyle. TXT_COLOUR, ///< The control's text colour. Should be an sf::Color. Use the separate set/getColour functions to set this.
}; };
/// Frame styles /// Frame styles
enum eFrameStyle { enum eFrameStyle {
FRM_NONE, ///< No frame.
FRM_INSET, ///< An outline style that makes it look like the interior is slightly depressed. FRM_INSET, ///< An outline style that makes it look like the interior is slightly depressed.
FRM_OUTSET, ///< An outline style that makes it look like the interior is slightly raise. FRM_OUTSET, ///< An outline style that makes it look like the interior is slightly raise.
FRM_SOLID, ///< A solid outline. FRM_SOLID, ///< A solid outline.
@@ -91,8 +92,46 @@ public:
class cControl { class cControl {
public: public:
using storage_t = std::map<std::string, boost::any>; using storage_t = std::map<std::string, boost::any>;
protected:
/// Parses the control from an XML element /// Parses the control from an XML element
virtual std::string parse(ticpp::Element& who, std::string fname) = 0; /// Most controls probably don't need to override this. Override parseAttribute() and parseContent() instead.
/// @param who A reference to the XML element being parsed.
/// @param fname The file being parsed, for error messages.
/// @return The unique ID of the widget.
/// @throw xBadNode if an unsupported sub-element is detected, or if text is found in an element that does not support it
virtual std::string parse(ticpp::Element& who, std::string fname);
/// Parses an attribute on the XML element representing this control.
/// All controls should defer to cControl::parseAttribute if they don't recognize the attribute.
/// @param attr A reference to the XML attribute being parsed.
/// @param tagName The name of the XML element, for error messages.
/// @param fname The file being parsed, for error messages.
/// @return true if the attribute is valid and was successfully parsed, false if it is unrecognized.
/// @throw xBadVal if the attribute is allowed but its content is invalid.
virtual bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname);
/// Parses content of the XML element representing this control, either text or a nested element.
/// Comments will not be passed to this function.
/// @param content A reference to the XML node being parsed; it will be either an element node or a text node.
/// @param tagName The name of the XML parent element, for error messages.
/// @param counter The number of previously-parsed nodes of the same tag name (or previously-parsed text nodes), excluding the current one.
/// @param[out] text If non-empty, this becomes the widget's text content.
/// @return true if the node is recognized and allowed in this location, false otherwise.
/// @throw xBadAttr if the node is an element and one of its attributes is not allowed.
/// @throw xMissingAttr if the node is an element and is missing a required attribute.
/// @throw xMissingElem if the node is an element and is missing a mandatory sub-element.
/// @throw xBadVal if the value of the node or (if the node is an element) an attribute is invalid.
/// The third argument can be used to accumulate the control's text.
virtual bool parseContent(ticpp::Node& content, int counter, std::string tagName, std::string fname, std::string& text);
/// Performs final validation of a parsed widget.
/// This can be used to throw errors for missing required attributes or elements,
/// or set defaults for optional values that were not found while parsing.
/// @param who A reference to the XML element being validated.
/// @param fname The file being parsed, for error messages
/// @param attrs A set containing all the attributes parsed from the widget's root element.
/// @param nodes A multiset containing all the sub-elements parsed from the widget's root element. Text nodes are also included in this set under the key "".
/// @throw xMissingAttr if a required attribute is missing
/// @throw xMissingElem if a required attribute is either missing or present in insufficient quantity
virtual void validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes);
public:
/// 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);
@@ -203,6 +242,11 @@ public:
/// Set the bounding rect of this control. /// Set the bounding rect of this control.
/// @param newBounds The new bounding rect. /// @param newBounds The new bounding rect.
void setBounds(rectangle newBounds); void setBounds(rectangle newBounds);
/// Calculate a preferred size for this control.
/// This can specify an exact preferred size or just a preferred width or height.
/// The preferred size is only used if the size is not specified in the XML.
/// @return The preferred size, or (0,0) if there is no preferred size.
virtual location getPreferredSize() {return {0,0};}
/// Set the position of this control. /// Set the position of this control.
/// @param to The new position. /// @param to The new position.
void relocate(location to); void relocate(location to);
@@ -216,20 +260,27 @@ public:
/// @param prop The parameter to set. /// @param prop The parameter to set.
/// @param val The desired value of the parameter. /// @param val The desired value of the parameter.
/// @throw xUnsupportedProp if this control doesn't support the given parameter. /// @throw xUnsupportedProp if this control doesn't support the given parameter.
virtual void setFormat(eFormat prop, short val) = 0; void setFormat(eFormat prop, short val);
/// Get one of the control's formatting parameters. /// Get one of the control's formatting parameters.
/// @param prop The parameter to retrieve. /// @param prop The parameter to retrieve.
/// @return The value of the parameter. /// @return The value of the parameter.
/// @throw xUnsupportedProp if this control doesn't support the given parameter. /// @throw xUnsupportedProp if this control doesn't support the given parameter.
virtual short getFormat(eFormat prop) = 0; short getFormat(eFormat prop);
/// Test if the control supports a given formatting parameter
/// @param prop The parameter to check.
/// @return true if supported, false if not.
bool canFormat(eFormat prop);
/// Set the control's colour (usually text colour). /// Set the control's colour (usually text colour).
/// @param clr The desired colour. /// @param clr The desired colour.
/// @throw xUnsupportedProp if this control does not support colour. /// @throw xUnsupportedProp if this control does not support colour.
virtual void setColour(sf::Color clr) = 0; void setColour(sf::Color clr);
/// Get the control's colour. /// Get the control's colour.
/// @return The current colour. /// @return The current colour.
/// @throw xUnsupportedProp if this control does not support colour. /// @throw xUnsupportedProp if this control does not support colour.
virtual sf::Color getColour() = 0; sf::Color getColour();
/// Test if the control supports colour
/// @return true if supported, false if not.
bool canColour();
/// Check if the control is clickable. /// Check if the control is clickable.
/// @return true if it's clickable. /// @return true if it's clickable.
/// @note This does not indicate whether the control supports click handlers. /// @note This does not indicate whether the control supports click handlers.
@@ -326,6 +377,14 @@ protected:
if(handler) return handler(dlg, id, newVal); if(handler) return handler(dlg, id, newVal);
return true; return true;
} }
/// Called to manage a format setting on this control.
/// No action is taken if the parameter is unsupported.
/// @param prop The parameter to manage.
/// @param set Whether to set the value.
/// @param val If @a set is true, set to this value, otherwise store the value here. Ignored if null.
/// The real type of this value depends on @a prop.
/// @return true if the parameter is supported, false otherwise.
virtual bool manageFormat(eFormat prop, bool set, boost::any* val);
/// Parses an HTML colour code. /// Parses an HTML colour code.
/// Recognizes three-digit hex, six-digit hex, and HTML colour names. /// Recognizes three-digit hex, six-digit hex, and HTML colour names.
/// @param code The colour code to parse. /// @param code The colour code to parse.

View File

@@ -165,20 +165,17 @@ bool cTextField::handleClick(location clickLoc) {
return true; return true;
} }
void cTextField::setFormat(eFormat prop, short){ bool cTextField::manageFormat(eFormat prop, bool set, boost::any* val) {
throw xUnsupportedProp(prop); switch(prop) {
} case TXT_COLOUR:
if(val) {
short cTextField::getFormat(eFormat prop){ if(set) color = boost::any_cast<sf::Color>(*val);
throw xUnsupportedProp(prop); else *val = color;
} }
break;
void cTextField::setColour(sf::Color clr) { default: return false;
color = clr; }
} return true;
sf::Color cTextField::getColour() {
return color;
} }
eFldType cTextField::getInputType() { eFldType cTextField::getInputType() {
@@ -638,21 +635,10 @@ void cTextField::handleInput(cKey key) {
selectionPoint = sp; selectionPoint = sp;
} }
std::string cTextField::parse(ticpp::Element& who, std::string fname) { bool cTextField::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
using namespace ticpp; std::string name = attr.Name();
Iterator<Attribute> attr; if(name == "type"){
Iterator<Node> node; std::string val = attr.Value();
std::string name, id;
int width = 0, height = 0;
bool foundTop = false, foundLeft = false; // requireds
rectangle frame;
for(attr = attr.begin(&who); attr != attr.end(); attr++){
attr->GetName(&name);
if(name == "name")
attr->GetValue(&id);
else if(name == "type"){
std::string val;
attr->GetValue(&val);
if(val == "int") if(val == "int")
setInputType(FLD_INT); setInputType(FLD_INT);
else if(val == "uint") else if(val == "uint")
@@ -661,38 +647,21 @@ std::string cTextField::parse(ticpp::Element& who, std::string fname) {
setInputType(FLD_REAL); setInputType(FLD_REAL);
else if(val == "text") else if(val == "text")
setInputType(FLD_TEXT); setInputType(FLD_TEXT);
else throw xBadVal("field",name,val,attr->Row(),attr->Column(),fname); else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
}else if(name == "top"){ return true;
attr->GetValue(&frame.top), foundTop = true; } else if(name == "tab-order"){
}else if(name == "left"){ attr.GetValue(&tabOrder);
attr->GetValue(&frame.left), foundLeft = true; return true;
}else if(name == "width"){
attr->GetValue(&width);
}else if(name == "height"){
attr->GetValue(&height);
}else if(name == "tab-order"){
attr->GetValue(&tabOrder);
}else throw xBadAttr("field",name,attr->Row(),attr->Column(),fname);
} }
if(!foundTop) throw xMissingAttr("field","top",attr->Row(),attr->Column(),fname); return cControl::parseAttribute(attr, tagName, fname);
if(!foundLeft) throw xMissingAttr("field","left",attr->Row(),attr->Column(),fname); }
frame.right = frame.left + width;
frame.bottom = frame.top + height; bool cTextField::parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) {
setBounds(frame); if(content.Type() == TiXmlNode::TEXT) {
std::string content; text += dlogStringFilter(content.Value());
for(node = node.begin(&who); node != node.end(); node++){ return true;
std::string val;
int type = node->Type();
node->GetValue(&val);
if(type == TiXmlNode::TEXT)
content += dlogStringFilter(val);
else if(type != TiXmlNode::COMMENT) {
val = '<' + val + '>';
throw xBadVal("field",xBadVal::CONTENT,val,node->Row(),node->Column(),fname);
} }
} return cControl::parseContent(content, n, tagName, fname, text);
setText(content);
return id;
} }
aTextInsert::aTextInsert(cTextField& in, int at, std::string text) : cAction("insert text"), in(in), at(at), text(text) {} aTextInsert::aTextInsert(cTextField& in, int at, std::string text) : cAction("insert text"), in(in), at(at), text(text) {}

View File

@@ -33,7 +33,8 @@ enum eFldType {
/// There is no Unicode support. /// There is no Unicode support.
class cTextField : public cControl { class cTextField : public cControl {
public: public:
std::string parse(ticpp::Element& who, std::string fname) override; bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
bool parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) override;
/// For text fields, this is triggered when it loses or gains the input focus. /// For text fields, this is triggered when it loses or gains the input focus.
/// @copydoc cControl::getSupportedHandlers /// @copydoc cControl::getSupportedHandlers
/// ///
@@ -42,9 +43,6 @@ public:
return {EVT_FOCUS, EVT_DEFOCUS}; return {EVT_FOCUS, EVT_DEFOCUS};
} }
bool handleClick(location where) override; bool handleClick(location where) override;
void setFormat(eFormat prop, short val) override;
short getFormat(eFormat prop) override;
void setColour(sf::Color clr) override;
void setText(std::string to) override; void setText(std::string to) override;
storage_t store() override; storage_t store() override;
void restore(storage_t to) override; void restore(storage_t to) override;
@@ -54,7 +52,6 @@ public:
/// Set the input type of the field. /// Set the input type of the field.
/// @param newType The new input type. /// @param newType The new input type.
void setInputType(eFldType newType); void setInputType(eFldType newType);
sf::Color getColour() override;
/// Create a new editable text field. /// Create a new editable text field.
/// @param parent The parent dialog. /// @param parent The parent dialog.
explicit cTextField(cDialog& parent); explicit cTextField(cDialog& parent);
@@ -76,6 +73,7 @@ public:
private: private:
void callHandler(event_fcn<EVT_FOCUS>::type onFocus, cDialog& me, std::string id) override; void callHandler(event_fcn<EVT_FOCUS>::type onFocus, cDialog& me, std::string id) override;
bool callHandler(event_fcn<EVT_DEFOCUS>::type onFocus, cDialog& me, std::string id) override; bool callHandler(event_fcn<EVT_DEFOCUS>::type onFocus, cDialog& me, std::string id) override;
bool manageFormat(eFormat prop, bool set, boost::any* val) override;
void set_ip(location clickLoc, int cTextField::* insertionPoint); void set_ip(location clickLoc, int cTextField::* insertionPoint);
cUndoList history; cUndoList history;
action_ptr current_action; action_ptr current_action;

View File

@@ -12,167 +12,85 @@
extern sf::Texture bg_gworld; extern sf::Texture bg_gworld;
void cTextMsg::setColour(sf::Color clr) { bool cTextMsg::manageFormat(eFormat prop, bool set, boost::any* val) {
color = clr; switch(prop) {
}
void cTextMsg::setFormat(eFormat prop, short val){
switch(prop){
case TXT_FRAME: case TXT_FRAME:
drawFramed = val; if(val) {
break; if(set) frameStyle = boost::any_cast<eFrameStyle>(*val);
case TXT_FRAMESTYLE: else *val = frameStyle;
frameStyle = eFrameStyle(val); }
break; break;
case TXT_SIZE: case TXT_SIZE:
textSize = val; if(val) {
if(set) textSize = boost::any_cast<short>(*val);
else *val = textSize;
}
break; break;
case TXT_FONT: case TXT_FONT:
if(val == FONT_DUNGEON) textFont = FONT_DUNGEON; if(val) {
else if(val == FONT_PLAIN) textFont = FONT_PLAIN; if(set) textFont = boost::any_cast<eFont>(*val);
else if(val == FONT_MAIDWORD) textFont = FONT_MAIDWORD; else *val = textFont;
else textFont = FONT_BOLD; // Defaults to bold if an invalid value is provided. }
break; break;
case TXT_WRAP: case TXT_COLOUR:
throw xUnsupportedProp(prop); if(val) {
if(set) color = boost::any_cast<sf::Color>(*val);
else *val = color;
} }
break;
default: return false;
}
return true;
} }
sf::Color cTextMsg::getColour() { bool cTextMsg::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
return color; if(attr.Name() == "def-key"){
} std::string val = attr.Value();
try {
short cTextMsg::getFormat(eFormat prop){
switch(prop){
case TXT_FRAME:
return drawFramed;
case TXT_FRAMESTYLE:
return frameStyle;
case TXT_SIZE:
return textSize;
case TXT_FONT:
return textFont;
case TXT_WRAP:
throw xUnsupportedProp(prop);
}
return 0;
}
std::string cTextMsg::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp;
Iterator<Attribute> attr;
Iterator<Node> node;
std::string name, id;
int width = 0, height = 0;
bool foundTop = false, foundLeft = false; // top and left are required attributes
rectangle frame;
if(parent->getBg() == cDialog::BG_DARK)
setColour(sf::Color::White);
for(attr = attr.begin(&who); attr != attr.end(); attr++){
attr->GetName(&name);
if(name == "name")
attr->GetValue(&id);
else if(name == "framed"){
std::string val;
attr->GetValue(&val);
if(val == "true") setFormat(TXT_FRAME, true);
}else if(name == "outline") {
std::string val;
attr->GetValue(&val);
if(val == "solid") setFormat(TXT_FRAMESTYLE, FRM_SOLID);
else if(val == "inset") setFormat(TXT_FRAMESTYLE, FRM_INSET);
else if(val == "outset") setFormat(TXT_FRAMESTYLE, FRM_OUTSET);
else if(val == "double") setFormat(TXT_FRAMESTYLE, FRM_DOUBLE);
}else if(name == "font"){
std::string val;
attr->GetValue(&val);
if(val == "dungeon")
setFormat(TXT_FONT, FONT_DUNGEON);
else if(val == "plain")
setFormat(TXT_FONT, FONT_PLAIN);
else if(val == "bold")
setFormat(TXT_FONT, FONT_BOLD);
else if(val == "maidenword")
setFormat(TXT_FONT, FONT_MAIDWORD);
else throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname);
}else if(name == "size"){
std::string val;
attr->GetValue(&val);
if(val == "large")
setFormat(TXT_SIZE, 12);
else if(val == "small")
setFormat(TXT_SIZE, 10);
else if(val == "title")
setFormat(TXT_SIZE, 18);
else throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname);
}else if(name == "color" || name == "colour"){
std::string val;
attr->GetValue(&val);
sf::Color clr;
try{
clr = parseColor(val);
}catch(int){
throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname);
}
setColour(clr);
}else if(name == "def-key"){
std::string val;
attr->GetValue(&val);
try{
attachKey(parseKey(val)); attachKey(parseKey(val));
}catch(int){ } catch(int){
throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname); throw xBadVal(tagName, attr.Name(), val, attr.Row(), attr.Column(), fname);
} }
}else if(name == "top"){ return true;
attr->GetValue(&frame.top), foundTop = true;
}else if(name == "left"){
attr->GetValue(&frame.left), foundLeft = true;
}else if(name == "width"){
attr->GetValue(&width);
}else if(name == "height"){
attr->GetValue(&height);
// }else if(name == "fromlist"){
// attr->GetValue(&p.second->fromList);
}else throw xBadAttr("text",name,attr->Row(),attr->Column(),fname);
} }
if(!foundTop) throw xMissingAttr("text","top",who.Row(),who.Column(),fname); return cControl::parseAttribute(attr, tagName, fname);
if(!foundLeft) throw xMissingAttr("text","left",who.Row(),who.Column(),fname); }
frame.right = frame.left + width;
frame.bottom = frame.top + height; bool cTextMsg::parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) {
setBounds(frame); if(content.Type() == TiXmlNode::TEXT) {
std::string content; text += dlogStringFilter(content.Value());
for(node = node.begin(&who); node != node.end(); node++){ return true;
std::string val; } else if(content.Value() == "br") {
int type = node->Type();
node->GetValue(&val);
// TODO: De-magic the | character // TODO: De-magic the | character
if(type == TiXmlNode::ELEMENT && val == "br") content += '|'; // because vertical bar is replaced by a newline when drawing strings text += '|'; // because vertical bar is replaced by a newline when drawing strings
else if(type == TiXmlNode::TEXT) return true;
content += dlogStringFilter(val);
else if(type != TiXmlNode::COMMENT) {
val = '<' + val + '>';
throw xBadVal("text",xBadVal::CONTENT,content + val,node->Row(),node->Column(),fname);
} }
} return cControl::parseContent(content, n, tagName, fname, text);
setText(content); }
return id;
void cTextMsg::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
cControl::validatePostParse(who, fname, attrs, nodes);
if(!attrs.count("color") && !attrs.count("colour") && parent->getBg() == cDialog::BG_DARK)
setColour(sf::Color::White);
} }
cTextMsg::cTextMsg(cDialog& parent) : cTextMsg::cTextMsg(cDialog& parent) :
cControl(CTRL_TEXT,parent), cControl(CTRL_TEXT,parent),
drawFramed(false),
textFont(FONT_BOLD), textFont(FONT_BOLD),
textSize(10), textSize(10),
color(parent.getDefTextClr()), color(parent.getDefTextClr()),
fromList("none") {} fromList("none") {
setFormat(TXT_FRAME, FRM_NONE);
}
cTextMsg::cTextMsg(sf::RenderWindow& parent) : cTextMsg::cTextMsg(sf::RenderWindow& parent) :
cControl(CTRL_TEXT,parent), cControl(CTRL_TEXT,parent),
drawFramed(false),
textFont(FONT_BOLD), textFont(FONT_BOLD),
textSize(10), textSize(10),
color(cDialog::defaultBackground == cDialog::BG_DARK ? sf::Color::White : sf::Color::Black), color(cDialog::defaultBackground == cDialog::BG_DARK ? sf::Color::White : sf::Color::Black),
fromList("none") {} fromList("none") {
setFormat(TXT_FRAME, FRM_NONE);
}
bool cTextMsg::isClickable(){ bool cTextMsg::isClickable(){
return haveHandler(EVT_CLICK); return haveHandler(EVT_CLICK);
@@ -195,7 +113,7 @@ void cTextMsg::draw(){
TextStyle style; TextStyle style;
style.font = textFont; style.font = textFont;
style.pointSize = textSize; style.pointSize = textSize;
if(drawFramed) drawFrame(2,frameStyle); drawFrame(2, frameStyle);
sf::Color draw_color = color; sf::Color draw_color = color;
if(depressed){ if(depressed){
draw_color.r = 256 - draw_color.r; draw_color.r = 256 - draw_color.r;

View File

@@ -22,11 +22,10 @@
/// This class can also create a frame for grouping controls or a clickable area. /// This class can also create a frame for grouping controls or a clickable area.
class cTextMsg : public cControl { class cTextMsg : public cControl {
public: public:
std::string parse(ticpp::Element& who, std::string fname); bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
void setFormat(eFormat prop, short val); bool parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) override;
short getFormat(eFormat prop); void validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) override;
void setColour(sf::Color clr); bool manageFormat(eFormat prop, bool set, boost::any* val);
sf::Color getColour();
/// Create a new text message. /// Create a new text message.
/// @param parent The parent dialog. /// @param parent The parent dialog.
explicit cTextMsg(cDialog& parent); explicit cTextMsg(cDialog& parent);
@@ -47,7 +46,6 @@ public:
cTextMsg& operator=(cTextMsg& other) = delete; cTextMsg& operator=(cTextMsg& other) = delete;
cTextMsg(cTextMsg& other) = delete; cTextMsg(cTextMsg& other) = delete;
private: private:
bool drawFramed;
short textSize; short textSize;
eFont textFont; eFont textFont;
sf::Color color; sf::Color color;

View File

@@ -75,27 +75,24 @@ std::map<ePicType,void(cPict::*)(short,rectangle)>& cPict::drawPict(){
return f; return f;
} }
void cPict::setFormat(eFormat prop, short val){ bool cPict::manageFormat(eFormat prop, bool set, boost::any* val) {
if(prop == TXT_FRAME) drawFramed = val; switch(prop) {
else if(prop == TXT_FRAMESTYLE) frameStyle = eFrameStyle(val); case TXT_FRAME:
else if(prop == TXT_WRAP) drawScaled = !val; if(val) {
else throw xUnsupportedProp(prop); if(set) frameStyle = boost::any_cast<eFrameStyle>(*val);
} else *val = frameStyle;
}
short cPict::getFormat(eFormat prop){ break;
if(prop == TXT_FRAME) return drawFramed; case TXT_WRAP:
else if(prop == TXT_FRAMESTYLE) return frameStyle; if(val) {
else if(prop == TXT_WRAP) return !drawScaled; if(set) drawScaled = !boost::any_cast<bool>(*val);
else throw xUnsupportedProp(prop); else *val = !drawScaled;
} }
break;
void cPict::setColour(sf::Color) { // TODO: Color is not supported
// TODO: Colour is not supported default: return false;
} }
return true;
sf::Color cPict::getColour() {
// TODO: Colour is not supported
return sf::Color();
} }
void cPict::setPict(pic_num_t num, ePicType type){ void cPict::setPict(pic_num_t num, ePicType type){
@@ -133,12 +130,14 @@ ePicType cPict::getPicType(){
} }
cPict::cPict(cDialog& parent) : cPict::cPict(cDialog& parent) :
cControl(CTRL_PICT,parent), cControl(CTRL_PICT,parent) {
drawFramed(true) {} setFormat(TXT_FRAME, FRM_SOLID);
}
cPict::cPict(sf::RenderWindow& parent) : cPict::cPict(sf::RenderWindow& parent) :
cControl(CTRL_PICT, parent), cControl(CTRL_PICT, parent) {
drawFramed(true) {} setFormat(TXT_FRAME, FRM_SOLID);
}
bool cPict::isClickable(){ bool cPict::isClickable(){
return haveHandler(EVT_CLICK); return haveHandler(EVT_CLICK);
@@ -435,141 +434,95 @@ void cPict::advanceAnim() {
if(animFrame >= 256) animFrame = 0; if(animFrame >= 256) animFrame = 0;
} }
std::string cPict::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp; bool cPict::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
Iterator<Attribute> attr; std::string name = attr.Name();
std::string name, id; if(name == "type") {
ePicType type; std::string val = attr.Value();
bool wide = false, tall = false, custom = false, tiny = false; if(val == "blank")
bool foundTop = false, foundLeft = false, foundType = false, foundNum = false; // required attributes blank = true;
rectangle frame; else if(val == "ter")
int width = 0, height = 0; picType = PIC_TER;
for(attr = attr.begin(&who); attr != attr.end(); attr++){
attr->GetName(&name);
if(name == "name")
attr->GetValue(&id);
else if(name == "type"){
std::string val;
foundType = true;
attr->GetValue(&val);
if(val == "blank"){
setPict(cPict::BLANK, PIC_TER);
foundNum = true;
continue;
}else if(val == "ter")
type = PIC_TER;
else if(val == "teranim") else if(val == "teranim")
type = PIC_TER_ANIM; picType = PIC_TER_ANIM;
else if(val == "monst") else if(val == "monst")
type = PIC_MONST; picType = PIC_MONST;
else if(val == "dlog") else if(val == "dlog")
type = PIC_DLOG; picType = PIC_DLOG;
else if(val == "talk") else if(val == "talk")
type = PIC_TALK; picType = PIC_TALK;
else if(val == "scen") else if(val == "scen")
type = PIC_SCEN; picType = PIC_SCEN;
else if(val == "item") else if(val == "item")
type = PIC_ITEM; picType = PIC_ITEM;
else if(val == "pc") else if(val == "pc")
type = PIC_PC; picType = PIC_PC;
else if(val == "field") else if(val == "field")
type = PIC_FIELD; picType = PIC_FIELD;
else if(val == "boom") else if(val == "boom")
type = PIC_BOOM; picType = PIC_BOOM;
else if(val == "missile") else if(val == "missile")
type = PIC_MISSILE; picType = PIC_MISSILE;
else if(val == "full") else if(val == "full")
type = PIC_FULL; picType = PIC_FULL;
else if(val == "map") else if(val == "map")
type = PIC_TER_MAP; picType = PIC_TER_MAP;
else if(val == "status") else if(val == "status")
type = PIC_STATUS; picType = PIC_STATUS;
else throw xBadVal("pict",name,val,attr->Row(),attr->Column(),fname); else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
if(foundNum) { return true;
pic_num_t wasPic = getPicNum(); } else if(name == "num") {
setPict(wasPic, type); try {
attr.GetValue(&picNum);
} catch(ticpp::Exception&) {
throw xBadVal(tagName, name, attr.Value(), attr.Row(), attr.Column(), fname);
} }
}else if(name == "custom"){ return true;
std::string val; } else if(name == "custom") {
attr->GetValue(&val); custom = true;
if(val == "true") custom = true; return true;
}else if(name == "scaled"){ } else if(name == "size") {
std::string val; std::string val = attr.Value();
attr->GetValue(&val);
if(val == "true") setFormat(TXT_WRAP, false);
}else if(name == "size"){
std::string val;
attr->GetValue(&val);
if(val == "wide") wide = true; if(val == "wide") wide = true;
else if(val == "tall") tall = true; else if(val == "tall") tall = true;
else if(val == "large") wide = tall = true; else if(val == "large") wide = tall = true;
else if(val == "small") tiny = true; else if(val == "small") tiny = true;
else throw xBadVal("pict",name,val,attr->Row(),attr->Column(),fname); else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
}else if(name == "def-key"){ return true;
std::string val; } else if(name == "scaled") {
attr->GetValue(&val); std::string val = attr.Value();
try{ if(val == "true") setFormat(TXT_WRAP, false);
attachKey(parseKey(val)); else if(val == "false") setFormat(TXT_WRAP, true);
}catch(int){ else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
throw xBadVal("pict",name,val,attr->Row(),attr->Column(),fname); return true;
} else if(name == "def-key") {
try {
attachKey(parseKey(attr.Value()));
} catch(int) {
throw xBadVal(tagName, name, attr.Value(), attr.Row(), attr.Column(), fname);
} }
}else if(name == "num"){ return true;
pic_num_t newPic; } else if(name == "wrap") {
attr->GetValue(&newPic), foundNum = true; return false;
if(foundType) setPict(newPic, type);
else setPict(newPic);
}else if(name == "top"){
attr->GetValue(&frame.top), foundTop = true;
}else if(name == "left"){
attr->GetValue(&frame.left), foundLeft = true;
}else if(name == "width"){
attr->GetValue(&width);
}else if(name == "height"){
attr->GetValue(&height);
}else if(name == "framed"){
std::string val;
attr->GetValue(&val);
if(val == "true") setFormat(TXT_FRAME, true);
else setFormat(TXT_FRAME, false);
}else if(name == "outline") {
std::string val;
attr->GetValue(&val);
if(val == "solid") setFormat(TXT_FRAMESTYLE, FRM_SOLID);
else if(val == "inset") setFormat(TXT_FRAMESTYLE, FRM_INSET);
else if(val == "outset") setFormat(TXT_FRAMESTYLE, FRM_OUTSET);
else if(val == "double") setFormat(TXT_FRAMESTYLE, FRM_DOUBLE);
}else throw xBadAttr("pict",name,attr->Row(),attr->Column(),fname);
} }
if(!foundType) throw xMissingAttr("pict","type",who.Row(),who.Column(),fname); return cControl::parseAttribute(attr, tagName, fname);
if(!foundNum) throw xMissingAttr("pict","num",who.Row(),who.Column(),fname); }
if(!foundTop) throw xMissingAttr("pict","top",who.Row(),who.Column(),fname);
if(!foundLeft) throw xMissingAttr("pict","left",who.Row(),who.Column(),fname); void cPict::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
if(wide || tall) { if(!attrs.count("type")) throw xMissingAttr(who.Value(), "type", who.Row(), who.Column(), fname);
pic_num_t wasPic = getPicNum(); if(blank && attrs.count("num")) throw xBadAttr(who.Value(), "num", who.Row(), who.Column(), fname);
if(wide && !tall && getPicType() == PIC_MONST) setPict(wasPic, PIC_MONST_WIDE); else if(!blank && !attrs.count("num")) throw xMissingAttr(who.Value(), "num", who.Row(), who.Column(), fname);
else if(!wide && tall && getPicType() == PIC_MONST) setPict(wasPic, PIC_MONST_TALL);
else if(wide && tall){ if(blank) picType = PIC_MONST, picNum = BLANK;
if(getPicType() == PIC_MONST) setPict(wasPic, PIC_MONST_LG); else if(tiny && picType == PIC_ITEM) picType = PIC_TINY_ITEM;
else if(getPicType() == PIC_SCEN) setPict(wasPic, PIC_SCEN_LG); else if(custom) picType += PIC_CUSTOM;
else if(getPicType() == PIC_DLOG) setPict(wasPic, PIC_DLOG_LG);
} if(wide && tall) picType += PIC_LARGE;
} else if(tiny && type == PIC_ITEM) { else if(wide) picType += PIC_WIDE;
pic_num_t wasPic = getPicNum(); else if(tall) picType += PIC_TALL;
setPict(wasPic, PIC_TINY_ITEM);
} setPict(picNum, picType);
frame.right = frame.left; return cControl::validatePostParse(who, fname, attrs, nodes);
frame.bottom = frame.top;
if(width > 0 || height > 0 || getPicType() == PIC_FULL){
frame.right = frame.left + width;
frame.bottom = frame.top + height;
}
setBounds(frame);
pic_num_t wasPic = getPicNum();
if(custom) {
setPict(wasPic, getPicType() + PIC_CUSTOM);
} else setPict(wasPic, getPicType());
// The above line also sets the graphic's bounding rect, if necessary
return id;
} }
void cPict::recalcRect() { void cPict::recalcRect() {
@@ -733,7 +686,7 @@ void cPict::draw(){
fill_rect(*inWindow, rect, sf::Color::Black); fill_rect(*inWindow, rect, sf::Color::Black);
else (this->*drawPict()[picType])(picNum,rect); else (this->*drawPict()[picType])(picNum,rect);
if(drawFramed) drawFrame(2,frameStyle); drawFrame(2, frameStyle);
} }
void cPict::drawPresetTer(short num, rectangle to_rect){ void cPict::drawPresetTer(short num, rectangle to_rect){
@@ -1325,7 +1278,7 @@ void cPict::drawAt(sf::RenderWindow& win, rectangle dest, pic_num_t which_g, ePi
cPict pic(win); cPict pic(win);
pic.frame = dest; pic.frame = dest;
pic.setPict(which_g, type_g); pic.setPict(which_g, type_g);
pic.setFormat(TXT_FRAME, framed); pic.setFormat(TXT_FRAME, framed ? FRM_SOLID : FRM_NONE);
pic.draw(); pic.draw();
} }

View File

@@ -25,11 +25,9 @@ class cPict : public cControl {
public: public:
/// @copydoc cDialog::init() /// @copydoc cDialog::init()
static void init(); static void init();
std::string parse(ticpp::Element& who, std::string fname); bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
void setFormat(eFormat prop, short val); void validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) override;
short getFormat(eFormat prop); bool manageFormat(eFormat prop, bool set, boost::any* val);
void setColour(sf::Color clr);
sf::Color getColour();
storage_t store(); storage_t store();
void restore(storage_t to); void restore(storage_t to);
/// @copydoc setPict(pic_num_t) /// @copydoc setPict(pic_num_t)
@@ -93,7 +91,9 @@ private:
static short animFrame; static short animFrame;
pic_num_t picNum; pic_num_t picNum;
ePicType picType; ePicType picType;
bool drawFramed, drawScaled; bool drawScaled;
// Transient parse flags
bool wide = false, tall = false, tiny = false, custom = false, blank = false;
void drawPresetTer(short num, rectangle to_rect); void drawPresetTer(short num, rectangle to_rect);
void drawPresetTerAnim(short num, rectangle to_rect); void drawPresetTerAnim(short num, rectangle to_rect);
void drawPresetMonstSm(short num, rectangle to_rect); void drawPresetMonstSm(short num, rectangle to_rect);

View File

@@ -519,54 +519,46 @@ void cScrollbar::draw() {
else draw_horizontal(); else draw_horizontal();
} }
std::string cScrollbar::parse(ticpp::Element& who, std::string fname) { bool cScrollbar::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
using namespace ticpp; std::string name = attr.Name();
Iterator<Attribute> attr; if(name == "vertical"){
std::string name, id;
bool foundTop = false, foundLeft = false, foundMax = false; // required attributes
bool foundVertical = false;
rectangle frame;
int width = 0, height = 0;
style = SCROLL_LED; // Dialog scrollbars have a different default.
for(attr = attr.begin(&who); attr != attr.end(); attr++){
attr->GetName(&name);
if(name == "name")
attr->GetValue(&id);
else if(name == "vertical"){
std::string val; std::string val;
attr->GetValue(&val); attr.GetValue(&val);
if(val == "true") vert = true; if(val == "true") vert = true;
else vert = false; else vert = false;
foundVertical = true;
}else if(name == "style"){ }else if(name == "style"){
std::string val; std::string val;
attr->GetValue(&val); attr.GetValue(&val);
if(val == "white") style = SCROLL_WHITE; if(val == "white") style = SCROLL_WHITE;
else if(val == "led") style = SCROLL_LED; else if(val == "led") style = SCROLL_LED;
else throw xBadVal("slider", name, val, attr->Row(), attr->Column(), fname); else throw xBadVal("slider", name, val, attr.Row(), attr.Column(), fname);
}else if(name == "link"){ }else if(name == "link"){
attr->GetValue(&link); attr.GetValue(&link);
}else if(name == "initial"){ }else if(name == "initial"){
attr->GetValue(&pos); attr.GetValue(&pos);
}else if(name == "max"){ }else if(name == "max"){
attr->GetValue(&max); attr.GetValue(&max);
foundMax = true;
}else if(name == "page-size"){ }else if(name == "page-size"){
attr->GetValue(&pgsz); attr.GetValue(&pgsz);
}else if(name == "top"){
attr->GetValue(&frame.top), foundTop = true;
}else if(name == "left"){
attr->GetValue(&frame.left), foundLeft = true;
}else if(name == "width"){
attr->GetValue(&width);
}else if(name == "height"){
attr->GetValue(&height);
}else throw xBadAttr("slider",name,attr->Row(),attr->Column(),fname);
} }
if(!foundMax) throw xMissingAttr("slider","num",who.Row(),who.Column(),fname); return cControl::parseAttribute(attr, tagName, fname);
if(!foundTop) throw xMissingAttr("slider","top",who.Row(),who.Column(),fname); }
if(!foundLeft) throw xMissingAttr("slider","left",who.Row(),who.Column(),fname);
if(pos > max) throw xBadAttr("slider","initial",who.Row(),who.Column(),fname);
void cScrollbar::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
cControl::validatePostParse(who, fname, attrs, nodes);
if(!attrs.count("max")) throw xMissingAttr(who.Value(), "max", who.Row(), who.Column(), fname);
if(pos > max) throw xBadVal(who.Value(), "initial", std::to_string(pos), who.Row(), who.Column(), fname);
if(!attrs.count("vertical")) {
// Try to guess whether it's horizontal or vertical based on its size
int width, height;
who.GetAttributeOrDefault("width", &width, 0);
who.GetAttributeOrDefault("height", &height, 0);
if(height > width) vert = true;
else if(width > height) vert = false;
else throw xMissingAttr(who.Value(), "vertical", who.Row(), who.Column(), fname);
}
/*
int thickness = vert ? up_rect[style][VERT].width() : up_rect[style][HORZ].height(); int thickness = vert ? up_rect[style][VERT].width() : up_rect[style][HORZ].height();
if(width > 0) { if(width > 0) {
frame.right = frame.left + width; frame.right = frame.left + width;
@@ -585,10 +577,14 @@ std::string cScrollbar::parse(ticpp::Element& who, std::string fname) {
if(vert) frame.height() = 25; if(vert) frame.height() = 25;
else frame.height() = thickness; else frame.height() = thickness;
} }
setBounds(frame); */
if(parent->hasControl(link)) if(parent->hasControl(link))
parent->getControl(link).setTextToNum(pos); parent->getControl(link).setTextToNum(pos);
return id; }
location cScrollbar::getPreferredSize() {
if(vert) return {up_rect[style][VERT].width(), 0};
else return {0, up_rect[style][HORZ].height()};
} }
cControl::storage_t cScrollbar::store() { cControl::storage_t cScrollbar::store() {

View File

@@ -64,7 +64,9 @@ class cScrollbar : public cControl, public iEventListener, public iDrawable {
public: public:
/// @copydoc cDialog::init() /// @copydoc cDialog::init()
static void init(); static void init();
std::string parse(ticpp::Element& who, std::string fname); bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
void validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) override;
location getPreferredSize() override;
/// Create a new scrollbar without a parent dialog. /// Create a new scrollbar without a parent dialog.
/// @param parent The parent window. /// @param parent The parent window.
explicit cScrollbar(sf::RenderWindow& parent); explicit cScrollbar(sf::RenderWindow& parent);

View File

@@ -70,25 +70,18 @@ void cScrollPane::recalcRect() {
frame.right += scrollFrame.width(); frame.right += scrollFrame.width();
} }
void cScrollPane::setFormat(eFormat prop, short val) { bool cScrollPane::manageFormat(eFormat prop, bool set, boost::any* val) {
if(prop == TXT_FRAME) framed = val; switch(prop) {
else if(prop == TXT_FRAMESTYLE) frameStyle = eFrameStyle(val); case TXT_FRAME:
else throw xUnsupportedProp(prop); if(val) {
} if(set) frameStyle = boost::any_cast<eFrameStyle>(*val);
else *val = frameStyle;
short cScrollPane::getFormat(eFormat prop) { }
if(prop == TXT_FRAME) return framed; break;
else if(prop == TXT_FRAMESTYLE) return frameStyle;
else throw xUnsupportedProp(prop);
}
void cScrollPane::setColour(sf::Color) {
// TODO: Colour is not supported // TODO: Colour is not supported
} default: return false;
}
sf::Color cScrollPane::getColour() { return true;
// TODO: Colour is not supported
return sf::Color();
} }
cControl::storage_t cScrollPane::store() { cControl::storage_t cScrollPane::store() {
@@ -179,92 +172,52 @@ void cScrollPane::forEach(std::function<void(std::string,cControl&)> callback) {
callback(ctrl.first, *ctrl.second); callback(ctrl.first, *ctrl.second);
} }
std::string cScrollPane::parse(ticpp::Element& who, std::string fname) { bool cScrollPane::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
using namespace ticpp; if(attr.Name() == "style"){
Iterator<Attribute> attr; std::string val = attr.Value();
Iterator<Element> node;
std::string name, id;
rectangle frame;
int width = 0, height = 0;
bool foundTop = false, foundLeft = false;
scroll.setStyle(SCROLL_LED);
for(attr = attr.begin(&who); attr != attr.end(); attr++) {
attr->GetName(&name);
if(name == "name")
attr->GetValue(&id);
else if(name == "top")
attr->GetValue(&frame.top), foundTop = true;
else if(name == "left")
attr->GetValue(&frame.left), foundLeft = true;
else if(name == "width")
attr->GetValue(&width);
else if(name == "height")
attr->GetValue(&height);
else if(name == "framed") {
std::string val;
attr->GetValue(&val);
if(val == "true") setFormat(TXT_FRAME, true);
else setFormat(TXT_FRAME, false);
}else if(name == "outline") {
std::string val;
attr->GetValue(&val);
if(val == "solid") setFormat(TXT_FRAMESTYLE, FRM_SOLID);
else if(val == "inset") setFormat(TXT_FRAMESTYLE, FRM_INSET);
else if(val == "outset") setFormat(TXT_FRAMESTYLE, FRM_OUTSET);
else if(val == "double") setFormat(TXT_FRAMESTYLE, FRM_DOUBLE);
}else if(name == "style"){
std::string val;
attr->GetValue(&val);
if(val == "white") setStyle(SCROLL_WHITE); if(val == "white") setStyle(SCROLL_WHITE);
else if(val == "led") setStyle(SCROLL_LED); else if(val == "led") setStyle(SCROLL_LED);
else throw xBadVal("slider", name, val, attr->Row(), attr->Column(), fname); else throw xBadVal(tagName, attr.Name(), val, attr.Row(), attr.Column(), fname);
} else throw xBadAttr("pane",name,attr->Row(),attr->Column(),fname); return true;
} }
if(!foundTop) throw xMissingAttr("pane","top",who.Row(),who.Column(),fname); return cContainer::parseAttribute(attr, tagName, fname);
if(!foundLeft) throw xMissingAttr("pane","left",who.Row(),who.Column(),fname); }
if(width > 0) {
frame.width() = width; bool cScrollPane::parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) {
}else{ if(content.Type() == TiXmlNode::ELEMENT) {
throw xMissingAttr("pane","width",who.Row(),who.Column(),fname); std::string tag = content.Value();
} auto& elem = dynamic_cast<ticpp::Element&>(content);
if(height > 0) { if(tag == "field") {
frame.height() = height; auto field = parent->parse<cTextField>(elem);
}else{
frame.height() = 10;
}
setBounds(frame);
for(node = node.begin(&who); node != node.end(); node++) {
std::string val;
int type = node->Type();
node->GetValue(&val);
if(type == TiXmlNode::ELEMENT) {
if(val == "field") {
auto field = parent->parse<cTextField>(*node);
contents.insert(field); contents.insert(field);
// TODO: Add field to tab order // TODO: Add field to tab order
//tabOrder.push_back(field); //tabOrder.push_back(field);
} else if(val == "text") { } else if(tag == "text") {
auto text = parent->parse<cTextMsg>(*node); auto text = parent->parse<cTextMsg>(elem);
contents.insert(text); contents.insert(text);
} else if(val == "pict") { } else if(tag == "pict") {
auto pict = parent->parse<cPict>(*node); auto pict = parent->parse<cPict>(elem);
contents.insert(pict); contents.insert(pict);
} else if(val == "slider") { } else if(tag == "slider") {
auto slide = parent->parse<cScrollbar>(*node); auto slide = parent->parse<cScrollbar>(elem);
contents.insert(slide); contents.insert(slide);
} else if(val == "button") { } else if(tag == "button") {
auto button = parent->parse<cButton>(*node); auto button = parent->parse<cButton>(elem);
contents.insert(button); contents.insert(button);
} else if(val == "led") { } else if(tag == "led") {
auto led = parent->parse<cLed>(*node); auto led = parent->parse<cLed>(elem);
contents.insert(led); contents.insert(led);
} else if(val == "group") { } else if(tag == "group") {
auto group = parent->parse<cLedGroup>(*node); auto group = parent->parse<cLedGroup>(elem);
contents.insert(group); contents.insert(group);
} else throw xBadNode(val,node->Row(),node->Column(),fname); } else throw xBadNode(tag, content.Row(), content.Column(), fname);
} else if(type != TiXmlNode::COMMENT) return true;
throw xBadVal("pane",xBadVal::CONTENT,val,node->Row(),node->Column(),fname);
} }
recalcRect(); return cContainer::parseContent(content, n, tagName, fname, text);
return id; }
void cScrollPane::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
cContainer::validatePostParse(who, fname, attrs, nodes);
if(!attrs.count("style")) setStyle(SCROLL_LED);
recalcRect();
} }

View File

@@ -20,15 +20,14 @@ class cScrollPane : public cContainer {
std::map<std::string, cControl*> contents; std::map<std::string, cControl*> contents;
rectangle globalFrame; rectangle globalFrame;
bool framed = false; bool framed = false;
bool manageFormat(eFormat prop, bool set, boost::any* val) override;
public: public:
/// Create a new scroll pane /// Create a new scroll pane
explicit cScrollPane(cDialog& parent); explicit cScrollPane(cDialog& parent);
std::string parse(ticpp::Element& who, std::string fname) override; bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
bool parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) override;
void validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) override;
bool handleClick(location where) override; bool handleClick(location where) override;
void setFormat(eFormat prop, short val) override;
short getFormat(eFormat prop) override;
void setColour(sf::Color clr) override;
sf::Color getColour() override;
bool hasChild(std::string id) override; bool hasChild(std::string id) override;
cControl& getChild(std::string id) override; cControl& getChild(std::string id) override;
storage_t store() override; storage_t store() override;

View File

@@ -32,25 +32,18 @@ void cContainer::callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, st
getChild(which_clicked).triggerClickHandler(me, which_clicked, mods); getChild(which_clicked).triggerClickHandler(me, which_clicked, mods);
} }
void cStack::setFormat(eFormat prop, short val) { bool cStack::manageFormat(eFormat prop, bool set, boost::any* val) {
if(prop == TXT_FRAME) drawFramed = val; switch(prop) {
else if(prop == TXT_FRAMESTYLE) frameStyle = eFrameStyle(val); case TXT_FRAME:
else throw xUnsupportedProp(prop); if(val) {
} if(set) frameStyle = boost::any_cast<eFrameStyle>(*val);
else *val = frameStyle;
short cStack::getFormat(eFormat prop) { }
if(prop == TXT_FRAME) return drawFramed; break;
else if(prop == TXT_FRAMESTYLE) return frameStyle;
throw xUnsupportedProp(prop);
}
void cStack::setColour(sf::Color) {
// TODO: Colour is not supported // TODO: Colour is not supported
} default: return false;
}
sf::Color cStack::getColour() { return true;
// TODO: Colour is not supported
return sf::Color();
} }
bool cStack::isClickable() { bool cStack::isClickable() {
@@ -158,65 +151,56 @@ void cStack::forEach(std::function<void(std::string,cControl&)> callback) {
callback(ctrl.first, *ctrl.second); callback(ctrl.first, *ctrl.second);
} }
std::string cStack::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp; bool cStack::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
Iterator<Attribute> attr; if(attr.Name() == "pages") {
Iterator<Element> node; try {
std::string name, id; size_t n;
for(attr = attr.begin(&who); attr != attr.end(); attr++) { attr.GetValue(&n);
attr->GetName(&name); setPageCount(n);
if(name == "name") } catch(ticpp::Exception&) {
attr->GetValue(&id); throw xBadVal(tagName, attr.Name(), attr.Value(), attr.Row(), attr.Column(), fname);
else if(name == "pages") {
size_t val;
attr->GetValue(&val);
setPageCount(val);
}else if(name == "framed"){
std::string val;
attr->GetValue(&val);
if(val == "true") setFormat(TXT_FRAME, true);
}else if(name == "outline") {
std::string val;
attr->GetValue(&val);
if(val == "solid") setFormat(TXT_FRAMESTYLE, FRM_SOLID);
else if(val == "inset") setFormat(TXT_FRAMESTYLE, FRM_INSET);
else if(val == "outset") setFormat(TXT_FRAMESTYLE, FRM_OUTSET);
else if(val == "double") setFormat(TXT_FRAMESTYLE, FRM_DOUBLE);
} else throw xBadAttr("stack",name,attr->Row(),attr->Column(),fname);
} }
for(node = node.begin(&who); node != node.end(); node++) { return true;
std::string val; }
int type = node->Type(); return cContainer::parseAttribute(attr, tagName, fname);
node->GetValue(&val); }
if(type == TiXmlNode::ELEMENT) {
if(val == "field") { bool cStack::parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) {
auto field = parent->parse<cTextField>(*node); if(content.Type() == TiXmlNode::ELEMENT) {
std::string tag = content.Value();
auto& elem = dynamic_cast<ticpp::Element&>(content);
if(tag == "field") {
auto field = parent->parse<cTextField>(elem);
controls.insert(field); controls.insert(field);
// TODO: Add field to tab order // TODO: Add field to tab order
//tabOrder.push_back(field); //tabOrder.push_back(field);
} else if(val == "text") { } else if(tag == "text") {
auto text = parent->parse<cTextMsg>(*node); auto text = parent->parse<cTextMsg>(elem);
controls.insert(text); controls.insert(text);
} else if(val == "pict") { } else if(tag == "pict") {
auto pict = parent->parse<cPict>(*node); auto pict = parent->parse<cPict>(elem);
controls.insert(pict); controls.insert(pict);
} else if(val == "slider") { } else if(tag == "slider") {
auto slide = parent->parse<cScrollbar>(*node); auto slide = parent->parse<cScrollbar>(elem);
controls.insert(slide); controls.insert(slide);
} else if(val == "button") { } else if(tag == "button") {
auto button = parent->parse<cButton>(*node); auto button = parent->parse<cButton>(elem);
controls.insert(button); controls.insert(button);
} else if(val == "led") { } else if(tag == "led") {
auto led = parent->parse<cLed>(*node); auto led = parent->parse<cLed>(elem);
controls.insert(led); controls.insert(led);
} else if(val == "group") { } else if(tag == "group") {
auto group = parent->parse<cLedGroup>(*node); auto group = parent->parse<cLedGroup>(elem);
controls.insert(group); controls.insert(group);
} else throw xBadNode(val,node->Row(),node->Column(),fname); } else throw xBadNode(tag, content.Row(), content.Column(), fname);
} else if(type != TiXmlNode::COMMENT) return true;
throw xBadVal("stack",xBadVal::CONTENT,val,node->Row(),node->Column(),fname);
} }
recalcRect(); return cContainer::parseContent(content, n, tagName, fname, text);
return id; }
void cStack::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
validatePostParse(who, fname, attrs, nodes);
recalcRect();
} }

View File

@@ -37,12 +37,11 @@ class cStack : public cContainer {
std::vector<std::map<std::string,storage_t>> storage; std::vector<std::map<std::string,storage_t>> storage;
std::map<std::string,cControl*> controls; std::map<std::string,cControl*> controls;
bool drawFramed = false; bool drawFramed = false;
bool manageFormat(eFormat prop, bool set, boost::any* val) override;
public: public:
std::string parse(ticpp::Element& who, std::string fname) override; bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
void setFormat(eFormat prop, short val) override; bool parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) override;
short getFormat(eFormat prop) override; void validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) override;
void setColour(sf::Color clr) override;
sf::Color getColour() override;
bool isClickable() override; bool isClickable() override;
bool isFocusable() override; bool isFocusable() override;
bool isScrollable() override; bool isScrollable() override;

View File

@@ -520,7 +520,7 @@ static void readQuestFromXml(ticpp::Element& data, cQuest& quest) {
elem->GetText(&quest.bank1); elem->GetText(&quest.bank1);
else if(banks_found == 1) else if(banks_found == 1)
elem->GetText(&quest.bank2); elem->GetText(&quest.bank2);
else throw xBadNode(name, elem->Row(), elem->Column(), fname); else throw xBadNode(type, elem->Row(), elem->Column(), fname);
banks_found++; banks_found++;
} else if(type == "name") { } else if(type == "name") {
elem->GetText(&quest.name, false); elem->GetText(&quest.name, false);

View File

@@ -1620,7 +1620,7 @@ static void put_target_status_graphics(cDialog& me, short for_pc) {
for(auto next : univ.party[for_pc].status) { for(auto next : univ.party[for_pc].status) {
std::string id2 = id + "-stat" + std::to_string(slot + 1); std::string id2 = id + "-stat" + std::to_string(slot + 1);
cPict& pic = dynamic_cast<cPict&>(me[id2]); cPict& pic = dynamic_cast<cPict&>(me[id2]);
pic.setFormat(TXT_FRAME, false); pic.setFormat(TXT_FRAME, FRM_NONE);
if(isAlive) { if(isAlive) {
short placedIcon = -1; short placedIcon = -1;
if(next.first == eStatus::POISON && next.second > 4) placedIcon = 1; if(next.first == eStatus::POISON && next.second > 4) placedIcon = 1;

View File

@@ -1486,7 +1486,7 @@ void draw_map(bool need_refresh) {
cPict theGraphic(mini_map); cPict theGraphic(mini_map);
theGraphic.setBounds(dlogpicrect); theGraphic.setBounds(dlogpicrect);
theGraphic.setPict(21, PIC_DLOG); theGraphic.setPict(21, PIC_DLOG);
theGraphic.setFormat(TXT_FRAME, false); theGraphic.setFormat(TXT_FRAME, FRM_NONE);
theGraphic.draw(); theGraphic.draw();
style.colour = sf::Color::White; style.colour = sf::Color::White;
style.lineHeight = 12; style.lineHeight = 12;