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 { .led {
background-image: url('img/button/led-off.png'); background-image: url('img/button/led-off.png');
background-position: left top;
}
.group .led {
border-color: red;
} }
.button.small { .button.small {

View File

@@ -175,7 +175,6 @@
<xsl:if test='./@state'> <xsl:if test='./@state'>
background-image: url('img/button/led-<xsl:value-of select='./@state'/>.png'); background-image: url('img/button/led-<xsl:value-of select='./@state'/>.png');
</xsl:if> </xsl:if>
background-position: left top;
<xsl:call-template name='set-bounds'/> <xsl:call-template name='set-bounds'/>
</xsl:attribute> </xsl:attribute>
<xsl:value-of select='.'/> <xsl:value-of select='.'/>
@@ -183,33 +182,16 @@
</div> </div>
</xsl:template> </xsl:template>
<xsl:template match='group/led'> <xsl:template match='group'>
<div> <div>
<xsl:attribute name='class'> <xsl:attribute name='class'>
led group
<xsl:if test='/dialog/@debug = "true"'>debug</xsl:if> <xsl:if test='/dialog/@debug = "true"'>debug</xsl:if>
<xsl:if test='@framed = "true"'>framed </xsl:if>
</xsl:attribute> </xsl:attribute>
<xsl:if test='/dialog/@debug = "true" and @name'>
<xsl:attribute name='title'> <xsl:apply-templates select='led'/>
<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='.'/>
</div> </div>
</xsl:template> </xsl:template>
@@ -269,7 +251,12 @@
</xsl:template> </xsl:template>
<xsl:template match='stack'> <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'> <xsl:attribute name='style'>
color: <xsl:value-of select='./@color'/> <xsl:value-of select='/dialog/@fore'/>; 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="width" type="xs:integer"/>
<xs:attribute name="height" type="xs:integer"/> <xs:attribute name="height" type="xs:integer"/>
</xs:attributeGroup> </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:attribute name="def-key" type="key"/>
<xs:attributeGroup name="font"> <xs:attributeGroup name="font">
<xs:attribute name="font" default="bold"> <xs:attribute name="font" default="bold">
@@ -130,8 +134,7 @@
</xs:element> </xs:element>
</xs:sequence> </xs:sequence>
<xs:attribute name="name" type="xs:token"/> <xs:attribute name="name" type="xs:token"/>
<xs:attribute name="framed" default="false" type="bool"/> <xs:attributeGroup ref="frame"/>
<xs:attribute name="outline" default="inset" type="frameStyle"/>
<xs:attributeGroup ref="font"/> <xs:attributeGroup ref="font"/>
<xs:attribute ref="def-key"/> <xs:attribute ref="def-key"/>
<xs:attribute name="clickable" default="false" type="bool"/> <xs:attribute name="clickable" default="false" type="bool"/>
@@ -217,6 +220,7 @@
<xs:element maxOccurs="unbounded" ref="led"/> <xs:element maxOccurs="unbounded" ref="led"/>
</xs:sequence> </xs:sequence>
<xs:attribute name="name" type="xs:token"/> <xs:attribute name="name" type="xs:token"/>
<xs:attributeGroup ref="frame"/>
<xs:attribute name="fromlist" default="none" type="xs:string"/> <xs:attribute name="fromlist" default="none" type="xs:string"/>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
@@ -232,6 +236,7 @@
<xs:element ref="slider"/> <xs:element ref="slider"/>
</xs:choice> </xs:choice>
<xs:attribute name="name" type="xs:token"/> <xs:attribute name="name" type="xs:token"/>
<xs:attributeGroup ref="frame"/>
<xs:attribute name="pages" type="xs:integer"/> <xs:attribute name="pages" type="xs:integer"/>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
@@ -247,8 +252,7 @@
<xs:element ref="slider"/> <xs:element ref="slider"/>
</xs:choice> </xs:choice>
<xs:attribute name="name" type="xs:token"/> <xs:attribute name="name" type="xs:token"/>
<xs:attribute name="framed" default="true" type="bool"/> <xs:attributeGroup ref="frame"/>
<xs:attribute name="outline" default="inset" type="frameStyle"/>
<xs:attribute name="style" type="scrollStyle" default="led"/> <xs:attribute name="style" type="scrollStyle" default="led"/>
<xs:attributeGroup ref="rect-size"/> <xs:attributeGroup ref="rect-size"/>
</xs:complexType> </xs:complexType>

View File

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

View File

@@ -163,6 +163,7 @@ private:
class cLedGroup : public cContainer { class cLedGroup : public cContainer {
click_callback_t onClick; click_callback_t onClick;
focus_callback_t onFocus; focus_callback_t onFocus;
bool drawFramed;
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;

View File

@@ -43,7 +43,7 @@ const char* xHandlerNotSupported::clickMsg = "This control cannot handle click e
xHandlerNotSupported::xHandlerNotSupported(bool isFocus){ xHandlerNotSupported::xHandlerNotSupported(bool isFocus){
this->isFocus = isFocus; this->isFocus = isFocus;
} }
const char* xHandlerNotSupported::what(){ const char* xHandlerNotSupported::what() const throw() {
if(isFocus) return focusMsg; if(isFocus) return focusMsg;
else return clickMsg; else return clickMsg;
} }
@@ -55,7 +55,7 @@ xUnsupportedProp::xUnsupportedProp(eFormat prop) throw(){
xUnsupportedProp::~xUnsupportedProp() throw(){ xUnsupportedProp::~xUnsupportedProp() throw(){
if(msg != nullptr) delete msg; if(msg != nullptr) delete msg;
} }
const char* xUnsupportedProp::what() throw(){ const char* xUnsupportedProp::what() const throw(){
if(msg == nullptr){ if(msg == nullptr){
msg = new char[62]; msg = new char[62];
std::string s; 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; 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. /// 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* focusMsg;
static const char* clickMsg; static const char* clickMsg;
bool isFocus; bool isFocus;
@@ -73,20 +73,20 @@ public:
/// @param isFocus true to indicate a focus event, false for a click event. /// @param isFocus true to indicate a focus event, false for a click event.
xHandlerNotSupported(bool isFocus); xHandlerNotSupported(bool isFocus);
/// @return The error message. /// @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. /// 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; eFormat whichProp;
char* msg; mutable char* msg;
public: public:
/// Construct a new exception. /// Construct a new exception.
/// @param prop The unsupported format property. /// @param prop The unsupported format property.
xUnsupportedProp(eFormat prop) throw(); xUnsupportedProp(eFormat prop) throw();
~xUnsupportedProp() throw(); ~xUnsupportedProp() throw();
/// @return The error message. /// @return The error message.
const char* what() throw(); const char* what() const throw();
}; };
/// The superclass of all dialog controls. /// The superclass of all dialog controls.

View File

@@ -13,6 +13,15 @@
#include "pict.hpp" #include "pict.hpp"
#include "scrollbar.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) { void cStack::attachClickHandler(click_callback_t f) throw(xHandlerNotSupported) {
onClick = f; onClick = f;
} }
@@ -21,23 +30,21 @@ void cStack::attachFocusHandler(focus_callback_t) throw(xHandlerNotSupported) {
throw xHandlerNotSupported(true); 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) { bool cStack::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods) {
std::string which_clicked = clicking; std::string which_clicked = clicking;
clicking = ""; clicking = "";
if(onClick) onClick(me, id, mods); 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) { bool cStack::handleClick(location where) {
std::string which_clicked; std::string which_clicked;
auto iter = controls.begin(); auto iter = controls.begin();
while(iter != controls.end()){ while(iter != controls.end()){
if(parent->getControl(*iter).isVisible() && where.in(parent->getControl(*iter).getBounds())){ if(iter->second->isVisible() && where.in(iter->second->getBounds())){
if(parent->getControl(*iter).handleClick(where)) { if(iter->second->handleClick(where)) {
which_clicked = *iter; which_clicked = iter->first;
break; break;
} }
} }
@@ -50,11 +57,15 @@ bool cStack::handleClick(location where) {
return true; return true;
} }
void cStack::setFormat(eFormat prop, short) throw(xUnsupportedProp) { void cStack::setFormat(eFormat prop, short val) throw(xUnsupportedProp) {
throw xUnsupportedProp(prop); 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) { short cStack::getFormat(eFormat prop) throw(xUnsupportedProp) {
if(prop == TXT_FRAME) return drawFramed;
else if(prop == TXT_FRAMESTYLE) return frameStyle;
throw xUnsupportedProp(prop); throw xUnsupportedProp(prop);
} }
@@ -71,15 +82,22 @@ bool cStack::isClickable() {
return true; 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) { bool cStack::setPage(size_t n) {
if(n >= nPages) return false; if(n >= nPages) return false;
if(n == curPage) return true; if(n == curPage) return true;
cTextField* focus = parent->getFocus(); cTextField* focus = parent->getFocus();
bool failed = false; bool failed = false;
for(auto id : controls) { for(auto p : controls) {
cControl& ctrl = parent->getControl(id); const std::string& id = p.first;
cControl& ctrl = *p.second;
storage[curPage][id] = ctrl.store(); storage[curPage][id] = ctrl.store();
if(!ctrl.triggerFocusHandler(*parent, id, true)) if(!ctrl.triggerFocusHandler(*parent, id, true))
failed = true; failed = true;
@@ -119,7 +137,7 @@ void cStack::recalcRect() {
auto iter = controls.begin(); auto iter = controls.begin();
frame = {INT_MAX, INT_MAX, 0, 0}; frame = {INT_MAX, INT_MAX, 0, 0};
while(iter != controls.end()){ while(iter != controls.end()){
cControl& ctrl = parent->getControl(*iter); cControl& ctrl = *iter->second;
rectangle otherFrame = ctrl.getBounds(); rectangle otherFrame = ctrl.getBounds();
if(otherFrame.right > frame.right) if(otherFrame.right > frame.right)
frame.right = otherFrame.right; frame.right = otherFrame.right;
@@ -134,15 +152,9 @@ void cStack::recalcRect() {
frame.inset(-6,-6); 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) { void cStack::fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reverseTabs) {
for(auto id : controls) { for(auto p : controls) {
cControl& ctrl = parent->getControl(id); cControl& ctrl = *p.second;
if(ctrl.getType() == CTRL_FIELD) { if(ctrl.getType() == CTRL_FIELD) {
cTextField& field = dynamic_cast<cTextField&>(ctrl); cTextField& field = dynamic_cast<cTextField&>(ctrl);
if(field.tabOrder > 0) 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) { std::string cStack::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp; using namespace ticpp;
@@ -168,9 +180,19 @@ std::string cStack::parse(ticpp::Element& who, std::string fname) {
size_t val; size_t val;
attr->GetValue(&val); attr->GetValue(&val);
setPageCount(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); } else throw xBadAttr("stack",name,attr->Row(),attr->Column(),fname);
} }
std::vector<std::string> stack;
for(node = node.begin(&who); node != node.end(); node++) { for(node = node.begin(&who); node != node.end(); node++) {
std::string val; std::string val;
int type = node->Type(); int type = node->Type();
@@ -178,39 +200,31 @@ std::string cStack::parse(ticpp::Element& who, std::string fname) {
if(type == TiXmlNode::ELEMENT) { if(type == TiXmlNode::ELEMENT) {
if(val == "field") { if(val == "field") {
auto field = parent->parse<cTextField>(*node); auto field = parent->parse<cTextField>(*node);
parent->add(field.second, field.second->getBounds(), field.first); controls.insert(field);
stack.push_back(field.first);
// 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(val == "text") {
auto text = parent->parse<cTextMsg>(*node); auto text = parent->parse<cTextMsg>(*node);
parent->add(text.second, text.second->getBounds(), text.first); controls.insert(text);
stack.push_back(text.first);
} else if(val == "pict") { } else if(val == "pict") {
auto pict = parent->parse<cPict>(*node); auto pict = parent->parse<cPict>(*node);
parent->add(pict.second, pict.second->getBounds(), pict.first); controls.insert(pict);
stack.push_back(pict.first);
} else if(val == "slider") { } else if(val == "slider") {
auto slide = parent->parse<cScrollbar>(*node); auto slide = parent->parse<cScrollbar>(*node);
parent->add(slide.second, slide.second->getBounds(), slide.first); controls.insert(slide);
stack.push_back(slide.first);
} else if(val == "button") { } else if(val == "button") {
auto button = parent->parse<cButton>(*node); auto button = parent->parse<cButton>(*node);
parent->add(button.second, button.second->getBounds(), button.first); controls.insert(button);
stack.push_back(button.first);
} else if(val == "led") { } else if(val == "led") {
auto led = parent->parse<cLed>(*node); auto led = parent->parse<cLed>(*node);
parent->add(led.second, led.second->getBounds(), led.first); controls.insert(led);
stack.push_back(led.first);
} else if(val == "group") { } else if(val == "group") {
auto group = parent->parse<cLedGroup>(*node); auto group = parent->parse<cLedGroup>(*node);
parent->add(group.second, group.second->getBounds(), group.first); controls.insert(group);
stack.push_back(group.first);
} else throw xBadNode(val,node->Row(),node->Column(),fname); } else throw xBadNode(val,node->Row(),node->Column(),fname);
} else if(type != TiXmlNode::COMMENT) } else if(type != TiXmlNode::COMMENT)
throw xBadVal("stack",xBadVal::CONTENT,val,node->Row(),node->Column(),fname); throw xBadVal("stack",xBadVal::CONTENT,val,node->Row(),node->Column(),fname);
} }
controls = stack;
recalcRect(); recalcRect();
return id; return id;
} }

View File

@@ -20,13 +20,9 @@
/// and stores the hidden portion of the array within itself. /// and stores the hidden portion of the array within itself.
/// @note The stack itself provides no mechanism for switching pages. You will need /// @note The stack itself provides no mechanism for switching pages. You will need
/// other controls, not within the stack, to trigger the switch. /// 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 /// 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 /// clicked control.
/// any layering concept in the engine.
/// ///
/// When the stack's page is changed, the focus handlers for any edit text fields in /// 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 /// 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 /// 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 /// focus handler is called with the third parameter set to false, to indicate that
/// it is gaining focus. /// it is gaining focus.
class cStack : public cControl { class cStack : public cContainer {
friend class cDialog; // So it can insert controls friend class cDialog; // So it can insert controls
size_t nPages, curPage = 0; size_t nPages, curPage = 0;
std::string clicking; std::string clicking;
std::vector<std::map<std::string,storage_t>> storage; std::vector<std::map<std::string,storage_t>> storage;
std::vector<std::string> controls; std::map<std::string,cControl*> controls;
click_callback_t onClick; click_callback_t onClick;
bool drawFramed;
public: public:
std::string parse(ticpp::Element& who, std::string fname); std::string parse(ticpp::Element& who, std::string fname);
void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported); void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported);
@@ -53,6 +50,8 @@ public:
sf::Color getColour() throw(xUnsupportedProp); sf::Color getColour() throw(xUnsupportedProp);
bool isClickable(); bool isClickable();
void draw(); void draw();
bool hasChild(std::string id);
cControl& getChild(std::string id);
/// Switch the stack to a particular page. /// Switch the stack to a particular page.
/// You need to do this before retrieving data from that page. /// You need to do this before retrieving data from that page.
/// @param The new page number /// @param The new page number
@@ -74,9 +73,6 @@ public:
size_t getPageCount(); size_t getPageCount();
/// Recalculate the stack's bounding rect based on its contained controls. /// Recalculate the stack's bounding rect based on its contained controls.
void recalcRect(); 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. /// Adds any fields in this stack to the tab order building arrays.
/// Meant for internal use. /// Meant for internal use.
void fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reverseTabs); void fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reverseTabs);