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

@@ -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);