Make stack control a real container instead of a controller

- Also add framed/outlined attributes to it and to LED groups
This commit is contained in:
2015-10-03 12:30:26 -04:00
parent 06ad776236
commit 8b90fa614b
9 changed files with 108 additions and 85 deletions

View File

@@ -107,6 +107,11 @@ title {
.led {
background-image: url('img/button/led-off.png');
background-position: left top;
}
.group .led {
border-color: red;
}
.button.small {

View File

@@ -175,7 +175,6 @@
<xsl:if test='./@state'>
background-image: url('img/button/led-<xsl:value-of select='./@state'/>.png');
</xsl:if>
background-position: left top;
<xsl:call-template name='set-bounds'/>
</xsl:attribute>
<xsl:value-of select='.'/>
@@ -183,33 +182,16 @@
</div>
</xsl:template>
<xsl:template match='group/led'>
<xsl:template match='group'>
<div>
<xsl:attribute name='class'>
led
group
<xsl:if test='/dialog/@debug = "true"'>debug</xsl:if>
<xsl:if test='@framed = "true"'>framed </xsl:if>
</xsl:attribute>
<xsl:if test='/dialog/@debug = "true" and @name'>
<xsl:attribute name='title'>
<xsl:value-of select='./@name'/>
</xsl:attribute>
</xsl:if>
<xsl:attribute name='style'>
color: <xsl:value-of select='./@color'/> <xsl:value-of select='/dialog/@fore'/>;
font-family:
<xsl:choose>
<xsl:when test='@font = "plain"'>'DejaVu Sans', Geneva</xsl:when>
<xsl:when test='@font = "bold"'>Capriola, Silom</xsl:when>
<xsl:otherwise><xsl:value-of select='./@font'/></xsl:otherwise>
</xsl:choose>;
border-color: red;
<xsl:if test='./@state'>
background-image: url('img/button/led-<xsl:value-of select='./@state'/>.png');
</xsl:if>
<xsl:call-template name='set-bounds'/>
</xsl:attribute>
<xsl:value-of select='.'/>
<xsl:apply-templates select='led'/>
</div>
</xsl:template>
@@ -269,7 +251,12 @@
</xsl:template>
<xsl:template match='stack'>
<div class='stack'>
<div>
<xsl:attribute name='class'>
stack
<xsl:if test='@framed = "true"'>framed </xsl:if>
</xsl:attribute>
<xsl:attribute name='style'>
color: <xsl:value-of select='./@color'/> <xsl:value-of select='/dialog/@fore'/>;

View File

@@ -77,6 +77,10 @@
<xs:attribute name="width" type="xs:integer"/>
<xs:attribute name="height" type="xs:integer"/>
</xs:attributeGroup>
<xs:attributeGroup name="frame">
<xs:attribute name="framed" default="false" type="bool"/>
<xs:attribute name="outline" default="inset" type="frameStyle"/>
</xs:attributeGroup>
<xs:attribute name="def-key" type="key"/>
<xs:attributeGroup name="font">
<xs:attribute name="font" default="bold">
@@ -130,8 +134,7 @@
</xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:token"/>
<xs:attribute name="framed" default="false" type="bool"/>
<xs:attribute name="outline" default="inset" type="frameStyle"/>
<xs:attributeGroup ref="frame"/>
<xs:attributeGroup ref="font"/>
<xs:attribute ref="def-key"/>
<xs:attribute name="clickable" default="false" type="bool"/>
@@ -217,6 +220,7 @@
<xs:element maxOccurs="unbounded" ref="led"/>
</xs:sequence>
<xs:attribute name="name" type="xs:token"/>
<xs:attributeGroup ref="frame"/>
<xs:attribute name="fromlist" default="none" type="xs:string"/>
</xs:complexType>
</xs:element>
@@ -232,6 +236,7 @@
<xs:element ref="slider"/>
</xs:choice>
<xs:attribute name="name" type="xs:token"/>
<xs:attributeGroup ref="frame"/>
<xs:attribute name="pages" type="xs:integer"/>
</xs:complexType>
</xs:element>
@@ -247,8 +252,7 @@
<xs:element ref="slider"/>
</xs:choice>
<xs:attribute name="name" type="xs:token"/>
<xs:attribute name="framed" default="true" type="bool"/>
<xs:attribute name="outline" default="inset" type="frameStyle"/>
<xs:attributeGroup ref="frame"/>
<xs:attribute name="style" type="scrollStyle" default="led"/>
<xs:attributeGroup ref="rect-size"/>
</xs:complexType>

View File

@@ -651,11 +651,15 @@ void cLedGroup::hide(std::string id){
choices[id]->hide();
}
void cLedGroup::setFormat(eFormat prop, short) throw(xUnsupportedProp) {
throw xUnsupportedProp(prop);
void cLedGroup::setFormat(eFormat prop, short val) throw(xUnsupportedProp) {
if(prop == TXT_FRAME) drawFramed = val;
else if(prop == TXT_FRAMESTYLE) frameStyle = eFrameStyle(val);
else throw xUnsupportedProp(prop);
}
short cLedGroup::getFormat(eFormat prop) throw(xUnsupportedProp) {
if(prop == TXT_FRAME) return drawFramed;
else if(prop == TXT_FRAMESTYLE) return frameStyle;
throw xUnsupportedProp(prop);
}
@@ -737,6 +741,7 @@ void cLedGroup::draw(){
iter->second->draw();
iter++;
}
if(drawFramed) drawFrame(2, frameStyle);
}
void cButton::setBtnType(eBtnType newType){
@@ -806,7 +811,18 @@ std::string cLedGroup::parse(ticpp::Element& who, std::string fname) {
attr->GetValue(&id);
// else if(name == "fromlist")
// attr->GetValue(&fromList);
else throw xBadAttr("group",name,attr->Row(),attr->Column(),fname);
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;

View File

@@ -163,6 +163,7 @@ private:
class cLedGroup : public cContainer {
click_callback_t onClick;
focus_callback_t onFocus;
bool drawFramed;
std::map<std::string,cLed*> choices;
std::string fromList;
std::string curSelect, prevSelect;

View File

@@ -43,7 +43,7 @@ const char* xHandlerNotSupported::clickMsg = "This control cannot handle click e
xHandlerNotSupported::xHandlerNotSupported(bool isFocus){
this->isFocus = isFocus;
}
const char* xHandlerNotSupported::what(){
const char* xHandlerNotSupported::what() const throw() {
if(isFocus) return focusMsg;
else return clickMsg;
}
@@ -55,7 +55,7 @@ xUnsupportedProp::xUnsupportedProp(eFormat prop) throw(){
xUnsupportedProp::~xUnsupportedProp() throw(){
if(msg != nullptr) delete msg;
}
const char* xUnsupportedProp::what() throw(){
const char* xUnsupportedProp::what() const throw(){
if(msg == nullptr){
msg = new char[62];
std::string s;

View File

@@ -64,7 +64,7 @@ typedef std::function<bool(cDialog&,std::string,eKeyMod)> click_callback_t;
typedef std::function<bool(cDialog&,std::string,bool)> focus_callback_t;
/// Thrown when you try to set a handler that the control does not support.
class xHandlerNotSupported : std::exception {
class xHandlerNotSupported : public std::exception {
static const char* focusMsg;
static const char* clickMsg;
bool isFocus;
@@ -73,20 +73,20 @@ public:
/// @param isFocus true to indicate a focus event, false for a click event.
xHandlerNotSupported(bool isFocus);
/// @return The error message.
const char* what();
const char* what() const throw();
};
/// Thrown when you try to set or retrieve a formatting property that the control does not support.
class xUnsupportedProp : std::exception {
class xUnsupportedProp : public std::exception {
eFormat whichProp;
char* msg;
mutable char* msg;
public:
/// Construct a new exception.
/// @param prop The unsupported format property.
xUnsupportedProp(eFormat prop) throw();
~xUnsupportedProp() throw();
/// @return The error message.
const char* what() throw();
const char* what() const throw();
};
/// The superclass of all dialog controls.

View File

@@ -13,6 +13,15 @@
#include "pict.hpp"
#include "scrollbar.hpp"
bool cStack::hasChild(std::string id) {
return controls.find(id) != controls.end();
}
cControl& cStack::getChild(std::string id) {
if(!hasChild(id)) throw std::invalid_argument(id + " was not found in the stack");
return *controls[id];
}
void cStack::attachClickHandler(click_callback_t f) throw(xHandlerNotSupported) {
onClick = f;
}
@@ -21,23 +30,21 @@ void cStack::attachFocusHandler(focus_callback_t) 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);
return controls[which_clicked]->triggerClickHandler(me, which_clicked, 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;
if(iter->second->isVisible() && where.in(iter->second->getBounds())){
if(iter->second->handleClick(where)) {
which_clicked = iter->first;
break;
}
}
@@ -50,11 +57,15 @@ bool cStack::handleClick(location where) {
return true;
}
void cStack::setFormat(eFormat prop, short) throw(xUnsupportedProp) {
throw xUnsupportedProp(prop);
void cStack::setFormat(eFormat prop, short val) throw(xUnsupportedProp) {
if(prop == TXT_FRAME) drawFramed = val;
else if(prop == TXT_FRAMESTYLE) frameStyle = eFrameStyle(val);
else throw xUnsupportedProp(prop);
}
short cStack::getFormat(eFormat prop) throw(xUnsupportedProp) {
if(prop == TXT_FRAME) return drawFramed;
else if(prop == TXT_FRAMESTYLE) return frameStyle;
throw xUnsupportedProp(prop);
}
@@ -71,15 +82,22 @@ bool cStack::isClickable() {
return true;
}
void cStack::draw() {}
void cStack::draw() {
if(!isVisible()) return;
for(auto& p : controls) {
p.second->draw();
}
if(drawFramed) drawFrame(2, frameStyle);
}
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);
for(auto p : controls) {
const std::string& id = p.first;
cControl& ctrl = *p.second;
storage[curPage][id] = ctrl.store();
if(!ctrl.triggerFocusHandler(*parent, id, true))
failed = true;
@@ -119,7 +137,7 @@ void cStack::recalcRect() {
auto iter = controls.begin();
frame = {INT_MAX, INT_MAX, 0, 0};
while(iter != controls.end()){
cControl& ctrl = parent->getControl(*iter);
cControl& ctrl = *iter->second;
rectangle otherFrame = ctrl.getBounds();
if(otherFrame.right > frame.right)
frame.right = otherFrame.right;
@@ -134,15 +152,9 @@ void cStack::recalcRect() {
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);
for(auto p : controls) {
cControl& ctrl = *p.second;
if(ctrl.getType() == CTRL_FIELD) {
cTextField& field = dynamic_cast<cTextField&>(ctrl);
if(field.tabOrder > 0)
@@ -153,7 +165,7 @@ void cStack::fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reve
}
}
cStack::cStack(cDialog& parent) : cControl(CTRL_STACK, parent), curPage(0), nPages(0) {}
cStack::cStack(cDialog& parent) : cContainer(CTRL_STACK, parent), curPage(0), nPages(0) {}
std::string cStack::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp;
@@ -168,9 +180,19 @@ std::string cStack::parse(ticpp::Element& who, std::string fname) {
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);
}
std::vector<std::string> stack;
for(node = node.begin(&who); node != node.end(); node++) {
std::string val;
int type = node->Type();
@@ -178,39 +200,31 @@ std::string cStack::parse(ticpp::Element& who, std::string fname) {
if(type == TiXmlNode::ELEMENT) {
if(val == "field") {
auto field = parent->parse<cTextField>(*node);
parent->add(field.second, field.second->getBounds(), field.first);
stack.push_back(field.first);
controls.insert(field);
// TODO: Add field to tab order
//tabOrder.push_back(field);
} else if(val == "text") {
auto text = parent->parse<cTextMsg>(*node);
parent->add(text.second, text.second->getBounds(), text.first);
stack.push_back(text.first);
controls.insert(text);
} else if(val == "pict") {
auto pict = parent->parse<cPict>(*node);
parent->add(pict.second, pict.second->getBounds(), pict.first);
stack.push_back(pict.first);
controls.insert(pict);
} else if(val == "slider") {
auto slide = parent->parse<cScrollbar>(*node);
parent->add(slide.second, slide.second->getBounds(), slide.first);
stack.push_back(slide.first);
controls.insert(slide);
} else if(val == "button") {
auto button = parent->parse<cButton>(*node);
parent->add(button.second, button.second->getBounds(), button.first);
stack.push_back(button.first);
controls.insert(button);
} else if(val == "led") {
auto led = parent->parse<cLed>(*node);
parent->add(led.second, led.second->getBounds(), led.first);
stack.push_back(led.first);
controls.insert(led);
} else if(val == "group") {
auto group = parent->parse<cLedGroup>(*node);
parent->add(group.second, group.second->getBounds(), group.first);
stack.push_back(group.first);
controls.insert(group);
} 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);
}
controls = stack;
recalcRect();
return id;
}

View File

@@ -20,13 +20,9 @@
/// 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.
/// clicked control.
///
/// 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
@@ -34,13 +30,14 @@
/// 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 {
class cStack : public cContainer {
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;
std::map<std::string,cControl*> controls;
click_callback_t onClick;
bool drawFramed;
public:
std::string parse(ticpp::Element& who, std::string fname);
void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported);
@@ -53,6 +50,8 @@ public:
sf::Color getColour() throw(xUnsupportedProp);
bool isClickable();
void draw();
bool hasChild(std::string id);
cControl& getChild(std::string id);
/// Switch the stack to a particular page.
/// You need to do this before retrieving data from that page.
/// @param The new page number
@@ -74,9 +73,6 @@ public:
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);