refactor & optimize xml parser

This commit is contained in:
MihailRis 2024-12-05 20:12:02 +03:00
parent 04b6b6b546
commit 84b3b82dba
6 changed files with 384 additions and 376 deletions

View File

@ -11,15 +11,17 @@
using namespace json; using namespace json;
class Parser : BasicParser { namespace {
dv::value parseList(); class Parser : BasicParser {
dv::value parseObject(); dv::value parseList();
dv::value parseValue(); dv::value parseObject();
public: dv::value parseValue();
Parser(std::string_view filename, std::string_view source); public:
Parser(std::string_view filename, std::string_view source);
dv::value parse(); dv::value parse();
}; };
}
inline void newline( inline void newline(
std::stringstream& ss, bool nice, uint indent, const std::string& indentstr std::stringstream& ss, bool nice, uint indent, const std::string& indentstr

View File

@ -107,8 +107,8 @@ glm::vec4 Attribute::asColor() const {
Node::Node(std::string tag) : tag(std::move(tag)) { Node::Node(std::string tag) : tag(std::move(tag)) {
} }
void Node::add(const xmlelement& element) { void Node::add(std::unique_ptr<Node> element) {
elements.push_back(element); elements.push_back(std::move(element));
} }
void Node::set(const std::string& name, const std::string& text) { void Node::set(const std::string& name, const std::string& text) {
@ -119,7 +119,7 @@ const std::string& Node::getTag() const {
return tag; return tag;
} }
const xmlattribute& Node::attr(const std::string& name) const { const Attribute& Node::attr(const std::string& name) const {
auto found = attrs.find(name); auto found = attrs.find(name);
if (found == attrs.end()) { if (found == attrs.end()) {
throw std::runtime_error( throw std::runtime_error(
@ -129,7 +129,7 @@ const xmlattribute& Node::attr(const std::string& name) const {
return found->second; return found->second;
} }
xmlattribute Node::attr(const std::string& name, const std::string& def) const { Attribute Node::attr(const std::string& name, const std::string& def) const {
auto found = attrs.find(name); auto found = attrs.find(name);
if (found == attrs.end()) { if (found == attrs.end()) {
return Attribute(name, def); return Attribute(name, def);
@ -142,19 +142,23 @@ bool Node::has(const std::string& name) const {
return found != attrs.end(); return found != attrs.end();
} }
xmlelement Node::sub(size_t index) { Node& Node::sub(size_t index) {
return elements.at(index); return *elements.at(index);
}
const Node& Node::sub(size_t index) const {
return *elements.at(index);
} }
size_t Node::size() const { size_t Node::size() const {
return elements.size(); return elements.size();
} }
const std::vector<xmlelement>& Node::getElements() const { const std::vector<std::unique_ptr<Node>>& Node::getElements() const {
return elements; return elements;
} }
const xmlelements_map& Node::getAttributes() const { const std::unordered_map<std::string, Attribute>& Node::getAttributes() const {
return attrs; return attrs;
} }
@ -162,12 +166,12 @@ Document::Document(std::string version, std::string encoding)
: version(std::move(version)), encoding(std::move(encoding)) { : version(std::move(version)), encoding(std::move(encoding)) {
} }
void Document::setRoot(const xmlelement& element) { void Document::setRoot(std::unique_ptr<Node> element) {
this->root = element; root = std::move(element);
} }
xmlelement Document::getRoot() const { const Node* Document::getRoot() const {
return root; return root.get();
} }
const std::string& Document::getVersion() const { const std::string& Document::getVersion() const {
@ -178,82 +182,6 @@ const std::string& Document::getEncoding() const {
return encoding; return encoding;
} }
Parser::Parser(std::string_view filename, std::string_view source)
: BasicParser(filename, source) {
}
xmlelement Parser::parseOpenTag() {
std::string tag = parseXMLName();
auto node = std::make_shared<Node>(tag);
char c;
while (true) {
skipWhitespace();
c = peek();
if (c == '/' || c == '>' || c == '?') break;
std::string attrname = parseXMLName();
std::string attrtext = "";
skipWhitespace();
if (peek() == '=') {
nextChar();
skipWhitespace();
char quote = peek();
if (quote != '\'' && quote != '"') {
throw error("string literal expected");
}
skip(1);
attrtext = parseString(quote);
}
node->set(attrname, attrtext);
}
return node;
}
void Parser::parseDeclaration() {
std::string version = "1.0";
std::string encoding = "UTF-8";
expect('<');
if (peek() == '?') {
nextChar();
xmlelement node = parseOpenTag();
expect("?>");
if (node->getTag() != "xml") {
throw error("invalid declaration");
}
version = node->attr("version", version).getText();
encoding = node->attr("encoding", encoding).getText();
if (encoding != "utf-8" && encoding != "UTF-8") {
throw error("UTF-8 encoding is only supported");
}
} else {
goBack();
}
document = std::make_shared<Document>(version, encoding);
}
void Parser::parseComment() {
expect("!--");
if (skipTo("-->")) {
skip(3);
} else {
throw error("comment close missing");
}
}
std::string Parser::parseText() {
size_t start = pos;
while (hasNext()) {
char c = peek();
if (c == '<') {
break;
}
nextChar();
}
return Parser("[string]", std::string(source.substr(start, pos - start)))
.parseString('\0', false);
}
inline bool is_xml_identifier_start(char c) { inline bool is_xml_identifier_start(char c) {
return is_identifier_start(c) || c == ':'; return is_identifier_start(c) || c == ':';
} }
@ -262,82 +190,166 @@ inline bool is_xml_identifier_part(char c) {
return is_identifier_part(c) || c == '-' || c == '.' || c == ':'; return is_identifier_part(c) || c == '-' || c == '.' || c == ':';
} }
std::string Parser::parseXMLName() { namespace {
char c = peek(); class Parser : BasicParser {
if (!is_xml_identifier_start(c)) { std::unique_ptr<Document> document;
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_xml_identifier_part(source[pos])) {
pos++;
}
return std::string(source.substr(start, pos - start));
}
xmlelement Parser::parseElement() { std::unique_ptr<Node> parseOpenTag() {
// text element std::string tag = parseXMLName();
if (peek() != '<') { auto node = std::make_unique<Node>(tag);
auto element = std::make_shared<Node>("#");
auto text = parseText(); char c;
util::replaceAll(text, "&quot;", "\""); while (true) {
util::replaceAll(text, "&apos;", "'"); skipWhitespace();
util::replaceAll(text, "&lt;", "<"); c = peek();
util::replaceAll(text, "&gt;", ">"); if (c == '/' || c == '>' || c == '?') break;
util::replaceAll(text, "&amp;", "&"); std::string attrname = parseXMLName();
element->set("#", text); std::string attrtext = "";
skipWhitespace();
if (peek() == '=') {
nextChar();
skipWhitespace();
char quote = peek();
if (quote != '\'' && quote != '"') {
throw error("string literal expected");
}
skip(1);
attrtext = parseString(quote);
}
node->set(attrname, attrtext);
}
return node;
}
std::unique_ptr<Node> parseElement() {
// text element
if (peek() != '<') {
auto element = std::make_unique<Node>("#");
auto text = parseText();
util::replaceAll(text, "&quot;", "\"");
util::replaceAll(text, "&apos;", "'");
util::replaceAll(text, "&lt;", "<");
util::replaceAll(text, "&gt;", ">");
util::replaceAll(text, "&amp;", "&");
element->set("#", text);
return element;
}
nextChar();
// <!--element-->
if (peek() == '!') {
if (isNext("!DOCTYPE ")) {
throw error("XML DTD is not supported yet");
}
parseComment();
return nullptr;
}
auto element = parseOpenTag();
char c = nextChar();
// <element/>
if (c == '/') {
expect('>');
}
// <element>...</element>
else if (c == '>') {
skipWhitespace();
while (!isNext("</")) {
auto sub = parseElement();
if (sub) {
element->add(std::move(sub));
}
skipWhitespace();
}
skip(2);
expect(element->getTag());
expect('>');
}
// <element?>
else {
throw error("invalid syntax");
}
return element; return element;
} }
nextChar();
// <!--element--> void parseDeclaration() {
if (peek() == '!') { std::string version = "1.0";
if (isNext("!DOCTYPE ")) { std::string encoding = "UTF-8";
throw error("XML DTD is not supported yet"); expect('<');
} if (peek() == '?') {
parseComment(); nextChar();
return nullptr; auto node = parseOpenTag();
} expect("?>");
if (node->getTag() != "xml") {
auto element = parseOpenTag(); throw error("invalid declaration");
char c = nextChar();
// <element/>
if (c == '/') {
expect('>');
}
// <element>...</element>
else if (c == '>') {
skipWhitespace();
while (!isNext("</")) {
auto sub = parseElement();
if (sub) {
element->add(sub);
} }
skipWhitespace(); version = node->attr("version", version).getText();
encoding = node->attr("encoding", encoding).getText();
if (encoding != "utf-8" && encoding != "UTF-8") {
throw error("UTF-8 encoding is only supported");
}
} else {
goBack();
} }
skip(2); document = std::make_unique<Document>(version, encoding);
expect(element->getTag());
expect('>');
} }
// <element?>
else { void parseComment() {
throw error("invalid syntax"); expect("!--");
if (skipTo("-->")) {
skip(3);
} else {
throw error("comment close missing");
}
} }
return element;
std::string parseText() {
size_t start = pos;
while (hasNext()) {
char c = peek();
if (c == '<') {
break;
}
nextChar();
}
return Parser("[string]", std::string(source.substr(start, pos - start)))
.parseString('\0', false);
}
std::string parseXMLName() {
char c = peek();
if (!is_xml_identifier_start(c)) {
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_xml_identifier_part(source[pos])) {
pos++;
}
return std::string(source.substr(start, pos - start));
}
public:
Parser(std::string_view filename, std::string_view source)
: BasicParser(filename, source) {
}
std::unique_ptr<Document> parse() {
parseDeclaration();
std::unique_ptr<Node> root;
while (root == nullptr) {
root = parseElement();
}
document->setRoot(std::move(root));
return std::move(document);
}
};
} }
xmldocument Parser::parse() { std::unique_ptr<Document> xml::parse(
parseDeclaration(); std::string_view filename, std::string_view source
) {
xmlelement root = nullptr;
while (root == nullptr) {
root = parseElement();
}
document->setRoot(root);
return document;
}
xmldocument xml::parse(std::string_view filename, std::string_view source) {
Parser parser(filename, source); Parser parser(filename, source);
return parser.parse(); return parser.parse();
} }
@ -354,13 +366,13 @@ inline void newline(
static void stringifyElement( static void stringifyElement(
std::stringstream& ss, std::stringstream& ss,
const xmlelement& element, const Node& element,
bool nice, bool nice,
const std::string& indentStr, const std::string& indentStr,
int indent int indent
) { ) {
if (element->isText()) { if (element.isText()) {
std::string text = element->attr("#").getText(); std::string text = element.attr("#").getText();
util::replaceAll(text, "&", "&amp;"); util::replaceAll(text, "&", "&amp;");
util::replaceAll(text, "\"", "&quot;"); util::replaceAll(text, "\"", "&quot;");
util::replaceAll(text, "'", "&apos;"); util::replaceAll(text, "'", "&apos;");
@ -369,10 +381,10 @@ static void stringifyElement(
ss << text; ss << text;
return; return;
} }
const std::string& tag = element->getTag(); const std::string& tag = element.getTag();
ss << '<' << tag; ss << '<' << tag;
auto& attrs = element->getAttributes(); auto& attrs = element.getAttributes();
if (!attrs.empty()) { if (!attrs.empty()) {
ss << ' '; ss << ' ';
int count = 0; int count = 0;
@ -388,10 +400,10 @@ static void stringifyElement(
count++; count++;
} }
} }
auto& elements = element->getElements(); auto& elements = element.getElements();
if (elements.size() == 1 && elements[0]->isText()) { if (elements.size() == 1 && elements[0]->isText()) {
ss << ">"; ss << ">";
stringifyElement(ss, elements[0], nice, indentStr, indent + 1); stringifyElement(ss, *elements[0], nice, indentStr, indent + 1);
ss << "</" << tag << ">"; ss << "</" << tag << ">";
return; return;
} }
@ -399,7 +411,7 @@ static void stringifyElement(
ss << '>'; ss << '>';
for (auto& sub : elements) { for (auto& sub : elements) {
newline(ss, nice, indentStr, indent + 1); newline(ss, nice, indentStr, indent + 1);
stringifyElement(ss, sub, nice, indentStr, indent + 1); stringifyElement(ss, *sub, nice, indentStr, indent + 1);
} }
newline(ss, nice, indentStr, indent); newline(ss, nice, indentStr, indent);
ss << "</" << tag << ">"; ss << "</" << tag << ">";
@ -410,16 +422,16 @@ static void stringifyElement(
} }
std::string xml::stringify( std::string xml::stringify(
const xmldocument& document, bool nice, const std::string& indentStr const Document& document, bool nice, const std::string& indentStr
) { ) {
std::stringstream ss; std::stringstream ss;
// XML declaration // XML declaration
ss << "<?xml version=\"" << document->getVersion(); ss << "<?xml version=\"" << document.getVersion();
ss << "\" encoding=\"UTF-8\" ?>"; ss << "\" encoding=\"UTF-8\" ?>";
newline(ss, nice, indentStr, 0); newline(ss, nice, indentStr, 0);
stringifyElement(ss, document->getRoot(), nice, indentStr, 0); stringifyElement(ss, *document.getRoot(), nice, indentStr, 0);
return ss.str(); return ss.str();
} }

View File

@ -13,11 +13,6 @@ namespace xml {
class Attribute; class Attribute;
class Document; class Document;
using xmlattribute = Attribute;
using xmlelement = std::shared_ptr<Node>;
using xmldocument = std::shared_ptr<Document>;
using xmlelements_map = std::unordered_map<std::string, xmlattribute>;
class Attribute { class Attribute {
std::string name; std::string name;
std::string text; std::string text;
@ -40,13 +35,15 @@ namespace xml {
/// 'text' /// 'text'
class Node { class Node {
std::string tag; std::string tag;
std::unordered_map<std::string, xmlattribute> attrs; std::unordered_map<std::string, Attribute> attrs;
std::vector<xmlelement> elements; std::vector<std::unique_ptr<Node>> elements;
public: public:
Node(std::string tag); Node(std::string tag);
Node(const Node&) = delete;
/// @brief Add sub-element /// @brief Add sub-element
void add(const xmlelement& element); void add(std::unique_ptr<Node> element);
/// @brief Set attribute value. Creates attribute if does not exists /// @brief Set attribute value. Creates attribute if does not exists
/// @param name attribute name /// @param name attribute name
@ -67,15 +64,15 @@ namespace xml {
/// @brief Get attribute by name /// @brief Get attribute by name
/// @param name attribute name /// @param name attribute name
/// @throws std::runtime_error if element has no attribute /// @throws std::runtime_error if element has no attribute
/// @return xmlattribute - {name, value} /// @return xml attribute - {name, value}
const xmlattribute& attr(const std::string& name) const; const Attribute& attr(const std::string& name) const;
/// @brief Get attribute by name /// @brief Get attribute by name
/// @param name attribute name /// @param name attribute name
/// @param def default value will be returned wrapped in xmlattribute /// @param def default value will be returned wrapped in xmlattribute
/// if element has no attribute /// if element has no attribute
/// @return xmlattribute - {name, value} or {name, def} if not found*/ /// @return xml attribute - {name, value} or {name, def} if not found
xmlattribute attr(const std::string& name, const std::string& def) Attribute attr(const std::string& name, const std::string& def)
const; const;
/// @brief Check if element has attribute /// @brief Check if element has attribute
@ -86,51 +83,37 @@ namespace xml {
/// @param index sub-element index /// @param index sub-element index
/// @throws std::out_of_range if an invalid index given /// @throws std::out_of_range if an invalid index given
/// @return sub-element /// @return sub-element
xmlelement sub(size_t index); Node& sub(size_t index);
const Node& sub(size_t index) const;
/// @brief Get number of sub-elements /// @brief Get number of sub-elements
size_t size() const; size_t size() const;
const std::vector<xmlelement>& getElements() const; const std::vector<std::unique_ptr<Node>>& getElements() const;
const xmlelements_map& getAttributes() const; const std::unordered_map<std::string, Attribute>& getAttributes() const;
}; };
class Document { class Document {
xmlelement root = nullptr; std::unique_ptr<Node> root = nullptr;
std::string version; std::string version;
std::string encoding; std::string encoding;
public: public:
Document(std::string version, std::string encoding); Document(std::string version, std::string encoding);
void setRoot(const xmlelement& element); void setRoot(std::unique_ptr<Node> element);
xmlelement getRoot() const; const Node* getRoot() const;
const std::string& getVersion() const; const std::string& getVersion() const;
const std::string& getEncoding() const; const std::string& getEncoding() const;
}; };
class Parser : BasicParser {
xmldocument document;
xmlelement parseOpenTag();
xmlelement parseElement();
void parseDeclaration();
void parseComment();
std::string parseText();
std::string parseXMLName();
public:
Parser(std::string_view filename, std::string_view source);
xmldocument parse();
};
/// @brief Serialize XML Document to string /// @brief Serialize XML Document to string
/// @param document serializing document /// @param document serializing document
/// @param nice use human readable format (with indents and line-separators) /// @param nice use human readable format (with indents and line-separators)
/// @param indentStr indentation characters sequence (default - 4 spaces) /// @param indentStr indentation characters sequence (default - 4 spaces)
/// @return XML string /// @return XML string
extern std::string stringify( std::string stringify(
const xmldocument& document, const Document& document,
bool nice = true, bool nice = true,
const std::string& indentStr = " " const std::string& indentStr = " "
); );
@ -139,7 +122,9 @@ namespace xml {
/// @param filename file name will be shown in error messages /// @param filename file name will be shown in error messages
/// @param source xml source code string /// @param source xml source code string
/// @return xml document /// @return xml document
extern xmldocument parse( std::unique_ptr<Document> parse(
std::string_view filename, std::string_view source std::string_view filename, std::string_view source
); );
using xmlelement = Node;
} }

View File

@ -67,9 +67,7 @@ std::unique_ptr<UiDocument> UiDocument::read(
: scripting::create_doc_environment(penv, name); : scripting::create_doc_environment(penv, name);
gui::UiXmlReader reader(env); gui::UiXmlReader reader(env);
auto view = reader.readXML( auto view = reader.readXML(file.u8string(), *xmldoc->getRoot());
file.u8string(), xmldoc->getRoot()
);
view->setId("root"); view->setId("root");
uidocscript script {}; uidocscript script {};
auto scriptFile = fs::path(file.u8string()+".lua"); auto scriptFile = fs::path(file.u8string()+".lua");

View File

@ -56,8 +56,8 @@ static runnable create_runnable(
const xml::xmlelement& element, const xml::xmlelement& element,
const std::string& name const std::string& name
) { ) {
if (element->has(name)) { if (element.has(name)) {
std::string text = element->attr(name).getText(); std::string text = element.attr(name).getText();
if (!text.empty()) { if (!text.empty()) {
return scripting::create_runnable( return scripting::create_runnable(
reader.getEnvironment(), text, reader.getFilename() reader.getEnvironment(), text, reader.getFilename()
@ -83,78 +83,78 @@ static onaction create_action(
static void _readUINode( static void _readUINode(
const UiXmlReader& reader, const xml::xmlelement& element, UINode& node const UiXmlReader& reader, const xml::xmlelement& element, UINode& node
) { ) {
if (element->has("id")) { if (element.has("id")) {
node.setId(element->attr("id").getText()); node.setId(element.attr("id").getText());
} }
if (element->has("pos")) { if (element.has("pos")) {
node.setPos(element->attr("pos").asVec2()); node.setPos(element.attr("pos").asVec2());
} }
if (element->has("size")) { if (element.has("size")) {
node.setSize(element->attr("size").asVec2()); node.setSize(element.attr("size").asVec2());
} }
if (element->has("color")) { if (element.has("color")) {
glm::vec4 color = element->attr("color").asColor(); glm::vec4 color = element.attr("color").asColor();
glm::vec4 hoverColor = color; glm::vec4 hoverColor = color;
glm::vec4 pressedColor = color; glm::vec4 pressedColor = color;
if (element->has("hover-color")) { if (element.has("hover-color")) {
hoverColor = node.getHoverColor(); hoverColor = node.getHoverColor();
} }
if (element->has("pressed-color")) { if (element.has("pressed-color")) {
pressedColor = node.getPressedColor(); pressedColor = node.getPressedColor();
} }
node.setColor(color); node.setColor(color);
node.setHoverColor(hoverColor); node.setHoverColor(hoverColor);
node.setPressedColor(pressedColor); node.setPressedColor(pressedColor);
} }
if (element->has("margin")) { if (element.has("margin")) {
node.setMargin(element->attr("margin").asVec4()); node.setMargin(element.attr("margin").asVec4());
} }
if (element->has("z-index")) { if (element.has("z-index")) {
node.setZIndex(element->attr("z-index").asInt()); node.setZIndex(element.attr("z-index").asInt());
} }
if (element->has("interactive")) { if (element.has("interactive")) {
node.setInteractive(element->attr("interactive").asBool()); node.setInteractive(element.attr("interactive").asBool());
} }
if (element->has("visible")) { if (element.has("visible")) {
node.setVisible(element->attr("visible").asBool()); node.setVisible(element.attr("visible").asBool());
} }
if (element->has("enabled")) { if (element.has("enabled")) {
node.setEnabled(element->attr("enabled").asBool()); node.setEnabled(element.attr("enabled").asBool());
} }
if (element->has("position-func")) { if (element.has("position-func")) {
node.setPositionFunc(scripting::create_vec2_supplier( node.setPositionFunc(scripting::create_vec2_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("position-func").getText(), element.attr("position-func").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("size-func")) { if (element.has("size-func")) {
node.setSizeFunc(scripting::create_vec2_supplier( node.setSizeFunc(scripting::create_vec2_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("size-func").getText(), element.attr("size-func").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("hover-color")) { if (element.has("hover-color")) {
node.setHoverColor(element->attr("hover-color").asColor()); node.setHoverColor(element.attr("hover-color").asColor());
} }
if (element->has("pressed-color")) { if (element.has("pressed-color")) {
node.setPressedColor(element->attr("pressed-color").asColor()); node.setPressedColor(element.attr("pressed-color").asColor());
} }
std::string alignName = element->attr("align", "").getText(); std::string alignName = element.attr("align", "").getText();
node.setAlign(align_from_string(alignName, node.getAlign())); node.setAlign(align_from_string(alignName, node.getAlign()));
if (element->has("gravity")) { if (element.has("gravity")) {
node.setGravity(gravity_from_string( node.setGravity(gravity_from_string(
element->attr("gravity").getText() element.attr("gravity").getText()
)); ));
} }
if (element->has("tooltip")) { if (element.has("tooltip")) {
node.setTooltip(util::str2wstr_utf8(element->attr("tooltip").getText())); node.setTooltip(util::str2wstr_utf8(element.attr("tooltip").getText()));
} }
if (element->has("tooltip-delay")) { if (element.has("tooltip-delay")) {
node.setTooltipDelay(element->attr("tooltip-delay").asFloat()); node.setTooltipDelay(element.attr("tooltip-delay").asFloat());
} }
if (auto onclick = create_action(reader, element, "onclick")) { if (auto onclick = create_action(reader, element, "onclick")) {
@ -169,16 +169,16 @@ static void _readUINode(
static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element, Container& container) { static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element, Container& container) {
_readUINode(reader, element, container); _readUINode(reader, element, container);
if (element->has("scrollable")) { if (element.has("scrollable")) {
container.setScrollable(element->attr("scrollable").asBool()); container.setScrollable(element.attr("scrollable").asBool());
} }
if (element->has("scroll-step")) { if (element.has("scroll-step")) {
container.setScrollStep(element->attr("scroll-step").asInt()); container.setScrollStep(element.attr("scroll-step").asInt());
} }
for (auto& sub : element->getElements()) { for (auto& sub : element.getElements()) {
if (sub->isText()) if (sub->isText())
continue; continue;
auto subnode = reader.readUINode(sub); auto subnode = reader.readUINode(*sub);
if (subnode) { if (subnode) {
container.add(subnode); container.add(subnode);
} }
@ -196,8 +196,8 @@ void UiXmlReader::readUINode(UiXmlReader& reader, const xml::xmlelement& element
static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Panel& panel, bool subnodes=true) { static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Panel& panel, bool subnodes=true) {
_readUINode(reader, element, panel); _readUINode(reader, element, panel);
if (element->has("padding")) { if (element.has("padding")) {
glm::vec4 padding = element->attr("padding").asVec4(); glm::vec4 padding = element.attr("padding").asVec4();
panel.setPadding(padding); panel.setPadding(padding);
glm::vec2 size = panel.getSize(); glm::vec2 size = panel.getSize();
panel.setSize(glm::vec2( panel.setSize(glm::vec2(
@ -205,23 +205,23 @@ static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Pane
size.y + padding.y + padding.w size.y + padding.y + padding.w
)); ));
} }
if (element->has("size")) { if (element.has("size")) {
panel.setResizing(false); panel.setResizing(false);
} }
if (element->has("max-length")) { if (element.has("max-length")) {
panel.setMaxLength(element->attr("max-length").asInt()); panel.setMaxLength(element.attr("max-length").asInt());
} }
if (element->has("orientation")) { if (element.has("orientation")) {
auto &oname = element->attr("orientation").getText(); auto &oname = element.attr("orientation").getText();
if (oname == "horizontal") { if (oname == "horizontal") {
panel.setOrientation(Orientation::horizontal); panel.setOrientation(Orientation::horizontal);
} }
} }
if (subnodes) { if (subnodes) {
for (auto& sub : element->getElements()) { for (auto& sub : element.getElements()) {
if (sub->isText()) if (sub->isText())
continue; continue;
auto subnode = reader.readUINode(sub); auto subnode = reader.readUINode(*sub);
if (subnode) { if (subnode) {
panel.add(subnode); panel.add(subnode);
} }
@ -231,8 +231,8 @@ static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Pane
static std::wstring readAndProcessInnerText(const xml::xmlelement& element, const std::string& context) { static std::wstring readAndProcessInnerText(const xml::xmlelement& element, const std::string& context) {
std::wstring text = L""; std::wstring text = L"";
if (element->size() == 1) { if (element.size() == 1) {
std::string source = element->sub(0)->attr("#").getText(); std::string source = element.sub(0).attr("#").getText();
util::trim(source); util::trim(source);
text = util::str2wstr_utf8(source); text = util::str2wstr_utf8(source);
if (text[0] == '@') { if (text[0] == '@') {
@ -250,29 +250,29 @@ static std::shared_ptr<UINode> readLabel(UiXmlReader& reader, const xml::xmlelem
std::wstring text = readAndProcessInnerText(element, reader.getContext()); std::wstring text = readAndProcessInnerText(element, reader.getContext());
auto label = std::make_shared<Label>(text); auto label = std::make_shared<Label>(text);
_readUINode(reader, element, *label); _readUINode(reader, element, *label);
if (element->has("valign")) { if (element.has("valign")) {
label->setVerticalAlign( label->setVerticalAlign(
align_from_string(element->attr("valign").getText(), label->getVerticalAlign()) align_from_string(element.attr("valign").getText(), label->getVerticalAlign())
); );
} }
if (element->has("supplier")) { if (element.has("supplier")) {
label->textSupplier(scripting::create_wstring_supplier( label->textSupplier(scripting::create_wstring_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("supplier").getText(), element.attr("supplier").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("autoresize")) { if (element.has("autoresize")) {
label->setAutoResize(element->attr("autoresize").asBool()); label->setAutoResize(element.attr("autoresize").asBool());
} }
if (element->has("multiline")) { if (element.has("multiline")) {
label->setMultiline(element->attr("multiline").asBool()); label->setMultiline(element.attr("multiline").asBool());
if (!element->has("valign")) { if (!element.has("valign")) {
label->setVerticalAlign(Align::top); label->setVerticalAlign(Align::top);
} }
} }
if (element->has("text-wrap")) { if (element.has("text-wrap")) {
label->setTextWrapping(element->attr("text-wrap").asBool()); label->setTextWrapping(element.attr("text-wrap").asBool());
} }
return label; return label;
} }
@ -284,19 +284,19 @@ static std::shared_ptr<UINode> readContainer(UiXmlReader& reader, const xml::xml
} }
static std::shared_ptr<UINode> readPanel(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readPanel(UiXmlReader& reader, const xml::xmlelement& element) {
float interval = element->attr("interval", "2").asFloat(); float interval = element.attr("interval", "2").asFloat();
auto panel = std::make_shared<Panel>(glm::vec2(), glm::vec4(), interval); auto panel = std::make_shared<Panel>(glm::vec2(), glm::vec4(), interval);
_readPanel(reader, element, *panel); _readPanel(reader, element, *panel);
return panel; return panel;
} }
static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlelement& element) {
glm::vec4 padding = element->attr("padding", "10").asVec4(); glm::vec4 padding = element.attr("padding", "10").asVec4();
std::shared_ptr<Button> button; std::shared_ptr<Button> button;
auto& elements = element->getElements(); auto& elements = element.getElements();
if (!elements.empty() && elements[0]->getTag() != "#") { if (!elements.empty() && elements[0]->getTag() != "#") {
auto inner = reader.readUINode(element->getElements().at(0)); auto inner = reader.readUINode(*elements.at(0));
if (inner != nullptr) { if (inner != nullptr) {
button = std::make_shared<Button>(inner, padding); button = std::make_shared<Button>(inner, padding);
} else { } else {
@ -308,30 +308,32 @@ static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlele
button = std::make_shared<Button>(text, padding, nullptr); button = std::make_shared<Button>(text, padding, nullptr);
_readPanel(reader, element, *button, true); _readPanel(reader, element, *button, true);
} }
if (element->has("text-align")) { if (element.has("text-align")) {
button->setTextAlign(align_from_string(element->attr("text-align").getText(), button->getTextAlign())); button->setTextAlign(align_from_string(
element.attr("text-align").getText(), button->getTextAlign()
));
} }
return button; return button;
} }
static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmlelement& element) {
auto text = readAndProcessInnerText(element, reader.getContext()); auto text = readAndProcessInnerText(element, reader.getContext());
bool checked = element->attr("checked", "false").asBool(); bool checked = element.attr("checked", "false").asBool();
auto checkbox = std::make_shared<FullCheckBox>(text, glm::vec2(32), checked); auto checkbox = std::make_shared<FullCheckBox>(text, glm::vec2(32), checked);
_readPanel(reader, element, *checkbox); _readPanel(reader, element, *checkbox);
if (element->has("consumer")) { if (element.has("consumer")) {
checkbox->setConsumer(scripting::create_bool_consumer( checkbox->setConsumer(scripting::create_bool_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("consumer").getText(), element.attr("consumer").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("supplier")) { if (element.has("supplier")) {
checkbox->setSupplier(scripting::create_bool_supplier( checkbox->setSupplier(scripting::create_bool_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("supplier").getText(), element.attr("supplier").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
@ -339,15 +341,15 @@ static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmle
} }
static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlelement& element) {
auto placeholder = util::str2wstr_utf8(element->attr("placeholder", "").getText()); auto placeholder = util::str2wstr_utf8(element.attr("placeholder", "").getText());
auto hint = util::str2wstr_utf8(element->attr("hint", "").getText()); auto hint = util::str2wstr_utf8(element.attr("hint", "").getText());
auto text = readAndProcessInnerText(element, reader.getContext()); auto text = readAndProcessInnerText(element, reader.getContext());
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f)); auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
textbox->setHint(hint); textbox->setHint(hint);
_readContainer(reader, element, *textbox); _readContainer(reader, element, *textbox);
if (element->has("padding")) { if (element.has("padding")) {
glm::vec4 padding = element->attr("padding").asVec4(); glm::vec4 padding = element.attr("padding").asVec4();
textbox->setPadding(padding); textbox->setPadding(padding);
glm::vec2 size = textbox->getSize(); glm::vec2 size = textbox->getSize();
textbox->setSize(glm::vec2( textbox->setSize(glm::vec2(
@ -357,58 +359,58 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
} }
textbox->setText(text); textbox->setText(text);
if (element->has("syntax")) { if (element.has("syntax")) {
textbox->setSyntax(element->attr("syntax").getText()); textbox->setSyntax(element.attr("syntax").getText());
} }
if (element->has("multiline")) { if (element.has("multiline")) {
textbox->setMultiline(element->attr("multiline").asBool()); textbox->setMultiline(element.attr("multiline").asBool());
} }
if (element->has("text-wrap")) { if (element.has("text-wrap")) {
textbox->setTextWrapping(element->attr("text-wrap").asBool()); textbox->setTextWrapping(element.attr("text-wrap").asBool());
} }
if (element->has("editable")) { if (element.has("editable")) {
textbox->setEditable(element->attr("editable").asBool()); textbox->setEditable(element.attr("editable").asBool());
} }
if (element->has("autoresize")) { if (element.has("autoresize")) {
textbox->setAutoResize(element->attr("autoresize").asBool()); textbox->setAutoResize(element.attr("autoresize").asBool());
} }
if (element->has("line-numbers")) { if (element.has("line-numbers")) {
textbox->setShowLineNumbers(element->attr("line-numbers").asBool()); textbox->setShowLineNumbers(element.attr("line-numbers").asBool());
} }
if (element->has("consumer")) { if (element.has("consumer")) {
textbox->setTextConsumer(scripting::create_wstring_consumer( textbox->setTextConsumer(scripting::create_wstring_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("consumer").getText(), element.attr("consumer").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("sub-consumer")) { if (element.has("sub-consumer")) {
textbox->setTextSubConsumer(scripting::create_wstring_consumer( textbox->setTextSubConsumer(scripting::create_wstring_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("sub-consumer").getText(), element.attr("sub-consumer").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("supplier")) { if (element.has("supplier")) {
textbox->setTextSupplier(scripting::create_wstring_supplier( textbox->setTextSupplier(scripting::create_wstring_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("supplier").getText(), element.attr("supplier").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("focused-color")) { if (element.has("focused-color")) {
textbox->setFocusedColor(element->attr("focused-color").asColor()); textbox->setFocusedColor(element.attr("focused-color").asColor());
} }
if (element->has("error-color")) { if (element.has("error-color")) {
textbox->setErrorColor(element->attr("error-color").asColor()); textbox->setErrorColor(element.attr("error-color").asColor());
} }
if (element->has("text-color")) { if (element.has("text-color")) {
textbox->setTextColor(element->attr("text-color").asColor()); textbox->setTextColor(element.attr("text-color").asColor());
} }
if (element->has("validator")) { if (element.has("validator")) {
textbox->setTextValidator(scripting::create_wstring_validator( textbox->setTextValidator(scripting::create_wstring_validator(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("validator").getText(), element.attr("validator").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
@ -422,7 +424,7 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
} }
static std::shared_ptr<UINode> readImage(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readImage(UiXmlReader& reader, const xml::xmlelement& element) {
std::string src = element->attr("src", "").getText(); std::string src = element.attr("src", "").getText();
auto image = std::make_shared<Image>(src); auto image = std::make_shared<Image>(src);
_readUINode(reader, element, *image); _readUINode(reader, element, *image);
return image; return image;
@ -431,51 +433,56 @@ static std::shared_ptr<UINode> readImage(UiXmlReader& reader, const xml::xmlelem
static std::shared_ptr<UINode> readTrackBar(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readTrackBar(UiXmlReader& reader, const xml::xmlelement& element) {
const auto& env = reader.getEnvironment(); const auto& env = reader.getEnvironment();
const auto& file = reader.getFilename(); const auto& file = reader.getFilename();
float minv = element->attr("min", "0.0").asFloat(); float minv = element.attr("min", "0.0").asFloat();
float maxv = element->attr("max", "1.0").asFloat(); float maxv = element.attr("max", "1.0").asFloat();
float def = element->attr("value", "0.0").asFloat(); float def = element.attr("value", "0.0").asFloat();
float step = element->attr("step", "1.0").asFloat(); float step = element.attr("step", "1.0").asFloat();
int trackWidth = element->attr("track-width", "12").asInt(); int trackWidth = element.attr("track-width", "12").asInt();
auto bar = std::make_shared<TrackBar>(minv, maxv, def, step, trackWidth); auto bar = std::make_shared<TrackBar>(minv, maxv, def, step, trackWidth);
_readUINode(reader, element, *bar); _readUINode(reader, element, *bar);
if (element->has("consumer")) { if (element.has("consumer")) {
bar->setConsumer(scripting::create_number_consumer( bar->setConsumer(scripting::create_number_consumer(
env, element->attr("consumer").getText(), file)); env, element.attr("consumer").getText(), file));
} }
if (element->has("sub-consumer")) { if (element.has("sub-consumer")) {
bar->setSubConsumer(scripting::create_number_consumer( bar->setSubConsumer(scripting::create_number_consumer(
env, element->attr("sub-consumer").getText(), file)); env, element.attr("sub-consumer").getText(), file));
} }
if (element->has("supplier")) { if (element.has("supplier")) {
bar->setSupplier(scripting::create_number_supplier( bar->setSupplier(scripting::create_number_supplier(
env, element->attr("supplier").getText(), file)); env, element.attr("supplier").getText(), file));
} }
if (element->has("track-color")) { if (element.has("track-color")) {
bar->setTrackColor(element->attr("track-color").asColor()); bar->setTrackColor(element.attr("track-color").asColor());
} }
if (element->has("change-on-release")) { if (element.has("change-on-release")) {
bar->setChangeOnRelease(element->attr("change-on-release").asBool()); bar->setChangeOnRelease(element.attr("change-on-release").asBool());
} }
return bar; return bar;
} }
static std::shared_ptr<UINode> readInputBindBox(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readInputBindBox(UiXmlReader& reader, const xml::xmlelement& element) {
auto bindname = element->attr("binding").getText(); auto bindname = element.attr("binding").getText();
auto found = Events::bindings.find(bindname); auto found = Events::bindings.find(bindname);
if (found == Events::bindings.end()) { if (found == Events::bindings.end()) {
throw std::runtime_error("binding does not exists "+util::quote(bindname)); throw std::runtime_error("binding does not exists "+util::quote(bindname));
} }
glm::vec4 padding = element->attr("padding", "6").asVec4(); glm::vec4 padding = element.attr("padding", "6").asVec4();
auto bindbox = std::make_shared<InputBindBox>(found->second, padding); auto bindbox = std::make_shared<InputBindBox>(found->second, padding);
_readPanel(reader, element, *bindbox); _readPanel(reader, element, *bindbox);
return bindbox; return bindbox;
} }
static slotcallback readSlotFunc(InventoryView* view, UiXmlReader& reader, xml::xmlelement& element, const std::string& attr) { static slotcallback readSlotFunc(
InventoryView* view,
const UiXmlReader& reader,
const xml::xmlelement& element,
const std::string& attr
) {
auto consumer = scripting::create_int_array_consumer( auto consumer = scripting::create_int_array_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr(attr).getText() element.attr(attr).getText()
); );
return [=](uint slot, ItemStack&) { return [=](uint slot, ItemStack&) {
int args[] {int(view->getInventory()->getId()), int(slot)}; int args[] {int(view->getInventory()->getId()), int(slot)};
@ -483,22 +490,24 @@ static slotcallback readSlotFunc(InventoryView* view, UiXmlReader& reader, xml::
}; };
} }
static void readSlot(InventoryView* view, UiXmlReader& reader, xml::xmlelement element) { static void readSlot(
int index = element->attr("index", "0").asInt(); InventoryView* view, UiXmlReader& reader, const xml::xmlelement& element
bool itemSource = element->attr("item-source", "false").asBool(); ) {
bool taking = element->attr("taking", "true").asBool(); int index = element.attr("index", "0").asInt();
bool placing = element->attr("placing", "true").asBool(); bool itemSource = element.attr("item-source", "false").asBool();
bool taking = element.attr("taking", "true").asBool();
bool placing = element.attr("placing", "true").asBool();
SlotLayout layout(index, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr); SlotLayout layout(index, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr);
if (element->has("pos")) { if (element.has("pos")) {
layout.position = element->attr("pos").asVec2(); layout.position = element.attr("pos").asVec2();
} }
if (element->has("updatefunc")) { if (element.has("updatefunc")) {
layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc"); layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc");
} }
if (element->has("sharefunc")) { if (element.has("sharefunc")) {
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc"); layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");
} }
if (element->has("onrightclick")) { if (element.has("onrightclick")) {
layout.rightClick = readSlotFunc(view, reader, element, "onrightclick"); layout.rightClick = readSlotFunc(view, reader, element, "onrightclick");
} }
layout.taking = taking; layout.taking = taking;
@ -508,19 +517,21 @@ static void readSlot(InventoryView* view, UiXmlReader& reader, xml::xmlelement e
view->add(slot); view->add(slot);
} }
static void readSlotsGrid(InventoryView* view, UiXmlReader& reader, xml::xmlelement element) { static void readSlotsGrid(
int startIndex = element->attr("start-index", "0").asInt(); InventoryView* view, const UiXmlReader& reader, const xml::xmlelement& element
int rows = element->attr("rows", "0").asInt(); ) {
int cols = element->attr("cols", "0").asInt(); int startIndex = element.attr("start-index", "0").asInt();
int count = element->attr("count", "0").asInt(); int rows = element.attr("rows", "0").asInt();
int cols = element.attr("cols", "0").asInt();
int count = element.attr("count", "0").asInt();
const int slotSize = InventoryView::SLOT_SIZE; const int slotSize = InventoryView::SLOT_SIZE;
bool taking = element->attr("taking", "true").asBool(); bool taking = element.attr("taking", "true").asBool();
bool placing = element->attr("placing", "true").asBool(); bool placing = element.attr("placing", "true").asBool();
int interval = element->attr("interval", "-1").asInt(); int interval = element.attr("interval", "-1").asInt();
if (interval < 0) { if (interval < 0) {
interval = InventoryView::SLOT_INTERVAL; interval = InventoryView::SLOT_INTERVAL;
} }
int padding = element->attr("padding", "-1").asInt(); int padding = element.attr("padding", "-1").asInt();
if (padding < 0) { if (padding < 0) {
padding = interval; padding = interval;
} }
@ -531,18 +542,18 @@ static void readSlotsGrid(InventoryView* view, UiXmlReader& reader, xml::xmlelem
} else if (count == 0) { } else if (count == 0) {
count = rows * cols; count = rows * cols;
} }
bool itemSource = element->attr("item-source", "false").asBool(); bool itemSource = element.attr("item-source", "false").asBool();
SlotLayout layout(-1, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr); SlotLayout layout(-1, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr);
if (element->has("pos")) { if (element.has("pos")) {
layout.position = element->attr("pos").asVec2(); layout.position = element.attr("pos").asVec2();
} }
if (element->has("updatefunc")) { if (element.has("updatefunc")) {
layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc"); layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc");
} }
if (element->has("sharefunc")) { if (element.has("sharefunc")) {
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc"); layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");
} }
if (element->has("onrightclick")) { if (element.has("onrightclick")) {
layout.rightClick = readSlotFunc(view, reader, element, "onrightclick"); layout.rightClick = readSlotFunc(view, reader, element, "onrightclick");
} }
layout.padding = padding; layout.padding = padding;
@ -574,11 +585,11 @@ static std::shared_ptr<UINode> readInventory(UiXmlReader& reader, const xml::xml
reader.addIgnore("slots-grid"); reader.addIgnore("slots-grid");
reader.readUINode(reader, element, *view); reader.readUINode(reader, element, *view);
for (auto& sub : element->getElements()) { for (auto& sub : element.getElements()) {
if (sub->getTag() == "slot") { if (sub->getTag() == "slot") {
readSlot(view.get(), reader, sub); readSlot(view.get(), reader, *sub);
} else if (sub->getTag() == "slots-grid") { } else if (sub->getTag() == "slots-grid") {
readSlotsGrid(view.get(), reader, sub); readSlotsGrid(view.get(), reader, *sub);
} }
} }
return view; return view;
@ -621,18 +632,18 @@ void UiXmlReader::addIgnore(const std::string& tag) {
} }
std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element) { std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element) {
if (element->has("if")) { if (element.has("if")) {
const auto& cond = element->attr("if").getText(); const auto& cond = element.attr("if").getText();
if (cond.empty() || cond == "false" || cond == "nil") if (cond.empty() || cond == "false" || cond == "nil")
return nullptr; return nullptr;
} }
if (element->has("ifnot")) { if (element.has("ifnot")) {
const auto& cond = element->attr("ifnot").getText(); const auto& cond = element.attr("ifnot").getText();
if (!(cond.empty() || cond == "false" || cond == "nil")) if (!(cond.empty() || cond == "false" || cond == "nil"))
return nullptr; return nullptr;
} }
const std::string& tag = element->getTag(); const std::string& tag = element.getTag();
auto found = readers.find(tag); auto found = readers.find(tag);
if (found == readers.end()) { if (found == readers.end()) {
if (ignored.find(tag) != ignored.end()) { if (ignored.find(tag) != ignored.end()) {
@ -641,9 +652,9 @@ std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element)
throw std::runtime_error("unsupported element '"+tag+"'"); throw std::runtime_error("unsupported element '"+tag+"'");
} }
bool hascontext = element->has("context"); bool hascontext = element.has("context");
if (hascontext) { if (hascontext) {
contextStack.push(element->attr("context").getText()); contextStack.push(element.attr("context").getText());
} }
auto node = found->second(*this, element); auto node = found->second(*this, element);
if (hascontext) { if (hascontext) {
@ -658,8 +669,7 @@ std::shared_ptr<UINode> UiXmlReader::readXML(
) { ) {
this->filename = filename; this->filename = filename;
auto document = xml::parse(filename, source); auto document = xml::parse(filename, source);
auto root = document->getRoot(); return readUINode(*document->getRoot());
return readUINode(root);
} }
std::shared_ptr<UINode> UiXmlReader::readXML( std::shared_ptr<UINode> UiXmlReader::readXML(

View File

@ -11,7 +11,8 @@
namespace gui { namespace gui {
class UiXmlReader; class UiXmlReader;
using uinode_reader = std::function<std::shared_ptr<UINode>(UiXmlReader&, xml::xmlelement)>; using uinode_reader = std::function<
std::shared_ptr<UINode>(UiXmlReader&, const xml::xmlelement&)>;
class UiXmlReader { class UiXmlReader {
std::unordered_map<std::string, uinode_reader> readers; std::unordered_map<std::string, uinode_reader> readers;