Add page definition facility to the DialogXML <stack> widget
- New <page> element in <stack> - Visibility of elements is now remembered when switching pages - Unstoring a <pict> on a stack page no longer resets default values - Documentation updated for new features
This commit is contained in:
@@ -251,6 +251,22 @@
|
||||
<xs:element name="led" type="led"/>
|
||||
<xs:element name="group" type="ledGroup"/>
|
||||
<xs:element name="slider" type="slider"/>
|
||||
<xs:element name="page">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="field" type="field"/>
|
||||
<xs:element name="text" type="message"/>
|
||||
<xs:element name="pict" type="pict"/>
|
||||
<xs:element name="button" type="button"/>
|
||||
<xs:element name="led" type="led"/>
|
||||
<xs:element name="group" type="ledGroup"/>
|
||||
<xs:element name="slider" type="slider"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="span" type="xs:integer"/>
|
||||
<xs:attribute name="default" type="bool"/>
|
||||
<xs:attribute name="template" type="xs:token"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
<xs:attribute name="name" type="xs:token"/>
|
||||
<xs:attributeGroup ref="frame"/>
|
||||
@@ -282,7 +298,16 @@
|
||||
<xs:element name="led" type="led"/>
|
||||
<xs:element name="group" type="ledGroup"/>
|
||||
<xs:element name="slider" type="slider"/>
|
||||
<xs:element name="stack" type="stack"/>
|
||||
<xs:element name="stack" type="stack">
|
||||
<xs:unique name="pageName">
|
||||
<xs:selector xpath="page"/>
|
||||
<xs:field xpath="@template"/>
|
||||
</xs:unique>
|
||||
<xs:unique name="onlyOneDefault">
|
||||
<xs:selector xpath="page"/>
|
||||
<xs:field xpath="@default"/>
|
||||
</xs:unique>
|
||||
</xs:element>
|
||||
<xs:element name="pane" type="scrollPane"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="skin" default="dark">
|
||||
|
@@ -663,6 +663,7 @@ bool cControl::hasKey(){
|
||||
cControl::storage_t cControl::store() {
|
||||
storage_t storage;
|
||||
storage["text"] = lbl;
|
||||
storage["visible"] = visible;
|
||||
return storage;
|
||||
}
|
||||
|
||||
@@ -670,4 +671,6 @@ void cControl::restore(storage_t to) {
|
||||
if(to.find("text") != to.end())
|
||||
lbl = boost::any_cast<std::string>(to["text"]);
|
||||
else lbl = "";
|
||||
if(to.find("visible") != to.end())
|
||||
boost::any_cast<bool>(to["visible"]) ? show() : hide();
|
||||
}
|
||||
|
@@ -1293,8 +1293,6 @@ void cPict::restore(storage_t to) {
|
||||
cControl::restore(to);
|
||||
if(to.find("pic-num") != to.end())
|
||||
picNum = boost::any_cast<pic_num_t>(to["pic-num"]);
|
||||
else picNum = -1;
|
||||
if(to.find("pic-type") != to.end())
|
||||
picType = boost::any_cast<ePicType>(to["pic-type"]);
|
||||
else picNum = PIC_DLOG;
|
||||
}
|
||||
|
@@ -99,9 +99,18 @@ size_t cStack::getPage() {
|
||||
void cStack::setPageCount(size_t n) {
|
||||
if(curPage >= n && n > 0)
|
||||
setPage(nPages - 1);
|
||||
auto added = n - nPages;
|
||||
if(n == 0) curPage = 0;
|
||||
nPages = n;
|
||||
storage.resize(nPages);
|
||||
if(added > 0 && defaultTemplate < storage.size()) {
|
||||
auto saveCur = curPage;
|
||||
for(int i = n - added; i < nPages; i++) {
|
||||
setPage(i);
|
||||
applyTemplate(defaultTemplate);
|
||||
}
|
||||
setPage(saveCur);
|
||||
}
|
||||
}
|
||||
|
||||
void cStack::addPage() {
|
||||
@@ -167,15 +176,74 @@ bool cStack::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::st
|
||||
}
|
||||
|
||||
bool cStack::parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) {
|
||||
using namespace ticpp;
|
||||
if(content.Type() == TiXmlNode::ELEMENT) {
|
||||
auto& elem = dynamic_cast<Element&>(content);
|
||||
std::string id;
|
||||
return parseChildControl(dynamic_cast<ticpp::Element&>(content), controls, id);
|
||||
if(elem.Value() == "page") {
|
||||
templates.emplace_back();
|
||||
Iterator<Element> iter;
|
||||
for(iter = iter.begin(&elem); iter != iter.end(); iter++) {
|
||||
if(iter->Type() == TiXmlNode::ELEMENT) {
|
||||
if(!parseChildControl(*iter, controls, id)) return false;
|
||||
templates.back().push_back(id);
|
||||
}
|
||||
}
|
||||
Iterator<Attribute> attr;
|
||||
for(attr = attr.begin(&elem); attr != attr.end(); attr++) {
|
||||
std::string name = attr->Name();
|
||||
if(name == "default") {
|
||||
std::string val = attr->Value();
|
||||
if(val == "true") {
|
||||
if(defaultTemplate != std::numeric_limits<size_t>::max())
|
||||
throw xBadAttr("page", "multiple default", attr->Row(), attr->Column(), fname);
|
||||
defaultTemplate = templates.size() - 1;
|
||||
} else if(val != "false") throw xBadVal("page", name, val, attr->Row(), attr->Column(), fname);
|
||||
} else if(name == "span") {
|
||||
int span;
|
||||
try {
|
||||
attr->GetValue(&span);
|
||||
} catch(Exception&) {
|
||||
throw xBadVal("page", name, attr->Value(), attr->Row(), attr->Column(), fname);
|
||||
}
|
||||
while(0 <-- span) {
|
||||
templates.push_back(templates.back());
|
||||
}
|
||||
} else if(name == "template") {
|
||||
templateNames[attr->Value()] = templates.size() - 1;
|
||||
} else throw xBadAttr("page", name, attr->Row(), attr->Column(), fname);
|
||||
}
|
||||
return true;
|
||||
} else return parseChildControl(elem, controls, id);
|
||||
}
|
||||
return cContainer::parseContent(content, n, tagName, fname, text);
|
||||
}
|
||||
|
||||
void cStack::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
|
||||
void cStack::validatePostParse(ticpp::Element&, std::string, const std::set<std::string>&, const std::multiset<std::string>&) {
|
||||
// Don't defer to the superclass, since this is an abstract widget with no position
|
||||
//cControl::validatePostParse(who, fname, attrs, nodes);
|
||||
// Actual number of pages is the larger of pages= and count of <page>
|
||||
setPageCount(std::max(nPages, templates.size()));
|
||||
for(size_t i = 0; i < nPages; i++) {
|
||||
setPage(i);
|
||||
applyTemplate(i);
|
||||
}
|
||||
setPage(0);
|
||||
}
|
||||
|
||||
void cStack::applyTemplate(size_t n) {
|
||||
if(n >= templates.size()) return;
|
||||
for(size_t i = 0; i < templates.size(); i++) {
|
||||
for(size_t j = 0; j < templates[i].size(); j++) {
|
||||
auto ctrl = controls[templates[i][j]];
|
||||
if(n == i) ctrl->show(); else ctrl->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cStack::applyTemplate(const std::string& name) {
|
||||
auto iter = templateNames.find(name);
|
||||
if(iter != templateNames.end()) applyTemplate(iter->second);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -38,6 +38,10 @@ class cStack : public cContainer {
|
||||
std::map<std::string,cControl*> controls;
|
||||
bool drawFramed = false;
|
||||
bool manageFormat(eFormat prop, bool set, boost::any* val) override;
|
||||
std::vector<std::vector<std::string>> templates;
|
||||
std::map<std::string, size_t> templateNames;
|
||||
size_t defaultTemplate = std::numeric_limits<size_t>::max();
|
||||
void applyTemplate(size_t n);
|
||||
public:
|
||||
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;
|
||||
@@ -64,6 +68,9 @@ public:
|
||||
/// Add a new page to the end of the stack.
|
||||
/// This is equivalent to calling setPageCount(getPageCount() + 1).
|
||||
void addPage();
|
||||
/// Apply a named template to the current page.
|
||||
/// @param name The name of the template to apply
|
||||
void applyTemplate(const std::string& name);
|
||||
// Get the number of pages in the stack.
|
||||
/// @return The number of pages
|
||||
size_t getPageCount();
|
||||
|
@@ -262,6 +262,23 @@ The `<stack>` tag accepts the following attributes:
|
||||
* `pages` - The initial number of pages in the stack. This can also be
|
||||
set programmatically.
|
||||
|
||||
In addition to nested widgets, the `<stack>` tag can contain `<page>` elements.
|
||||
Each `<page>` element contains a group of widgets that will be visible on a
|
||||
particular page. Unless changed programmatically, any widgets in a `<page>`
|
||||
element will be visible if and if only the corresponding page is active.
|
||||
Widgets not assigned to a `<page>` will always be visible.
|
||||
A `<page>` can contain any elements except for nested `<stack>` or `<pane>` elements.
|
||||
|
||||
The `<page>` tag accepts the following attributes:
|
||||
|
||||
* `span` - If set to an integer greater than 1, the same page definition will be
|
||||
used for a span of this many pages, as if the entire element had been duplicated
|
||||
that many times.
|
||||
* `default` - If set to true, this page will be used as the default definition for
|
||||
any new pages added programmatically. Only one page may be the default.
|
||||
* `template` - If set to a string, this can be used to apply the page definition to
|
||||
a page programmatically.
|
||||
|
||||
The `<pane>` tag
|
||||
----------------
|
||||
|
||||
|
Reference in New Issue
Block a user