refactor & optimize xml parser
This commit is contained in:
parent
04b6b6b546
commit
84b3b82dba
@ -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
|
||||||
|
|||||||
@ -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, """, "\"");
|
while (true) {
|
||||||
util::replaceAll(text, "'", "'");
|
skipWhitespace();
|
||||||
util::replaceAll(text, "<", "<");
|
c = peek();
|
||||||
util::replaceAll(text, ">", ">");
|
if (c == '/' || c == '>' || c == '?') break;
|
||||||
util::replaceAll(text, "&", "&");
|
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, """, "\"");
|
||||||
|
util::replaceAll(text, "'", "'");
|
||||||
|
util::replaceAll(text, "<", "<");
|
||||||
|
util::replaceAll(text, ">", ">");
|
||||||
|
util::replaceAll(text, "&", "&");
|
||||||
|
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, "&", "&");
|
util::replaceAll(text, "&", "&");
|
||||||
util::replaceAll(text, "\"", """);
|
util::replaceAll(text, "\"", """);
|
||||||
util::replaceAll(text, "'", "'");
|
util::replaceAll(text, "'", "'");
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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");
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user