#include #include "GUILoader.hh" #include "FontProvider.hh" #include "ImageProvider.hh" #include "Widget.hh" #include "TextWidget.hh" #include "LinearLayout.hh" struct Style { int width{WRAP_CONTENT}, height{WRAP_CONTENT}; SDL_Surface *background{nullptr}; SDL_Color bg{0, 0, 0, 255}, fg{255, 255, 255, 255}; int padLeft{0}, padTop{0}, padRight{0}, padBottom{0}; int alignHoriz{ALIGN_LEFT}, alignVert{ALIGN_TOP}; int alignContHoriz{ALIGN_LEFT}, alignContVert{ALIGN_TOP}; TTF_Font *font{nullptr}; }; class GUISAXParser { public: GUISAXParser() : state_(State::S_INITIAL), root_(nullptr) { } std::unique_ptr getResult() { return std::move(root_); } void startDocument() { printf("startDocument()\n"); state_ = State::S_INITIAL; } void endDocument() { printf("endDocument()\n"); if (state_ != State::S_FINISHED) throw Exception{"Invalid state at document end"}; } void startElement(const xmlChar *name, const xmlChar **attrs) { printf("startElement(%s)\n", name); const std::string ename{reinterpret_cast(name)}; std::unordered_map eattrs; while (*attrs != nullptr) { std::string k{reinterpret_cast(*attrs)}, v{reinterpret_cast(*(attrs+1))}; eattrs[k] = v; attrs += 2; } if (state_ == State::S_STYLE) throw Exception{"Invalid Element inside "}; printf("state -> S_GUI\n"); state_ = State::S_GUI; return; } if ((ename == "LinearLayout") || (ename == "TextWidget")) { if (state_ != State::S_WIDGETS) throw Exception{"Invalid state on "}; assert(!stack_.empty()); stack_.pop_back(); if (stack_.empty()) { printf("state -> S_DONE\n"); state_ = State::S_DONE; } return; } if (ename == "gui") { if (state_ != State::S_DONE) throw Exception{"Invalid state on "}; printf("state -> S_FINISHED\n"); state_ = State::S_FINISHED; return; } throw Exception{"Invalid element"}; } private: enum class State { S_INITIAL, S_GUI, S_STYLE, S_WIDGETS, S_DONE, S_FINISHED }; int parseDimension(std::string const& str) { if (str == "match_parent") return MATCH_PARENT; else if (str == "wrap_content") return WRAP_CONTENT; else if (str == "wrap_content_fill") return WRAP_CONTENT_FILL; try { return std::stoi(str); } catch (std::invalid_argument &ex) { throw Exception{"Invalid dimension"}; } } int parseAlign(std::string const& str) { if (str == "center") return ALIGN_CENTER; else if (str == "left") return ALIGN_LEFT; else if (str == "right") return ALIGN_RIGHT; else if (str == "top") return ALIGN_TOP; else if (str == "bottom") return ALIGN_BOTTOM; throw Exception{"Invalid alignment"}; } SDL_Color parseColor(std::string const& str) { SDL_Color ret; auto sep = str.find(','); if (sep == std::string::npos) throw Exception{"Invalid color"}; try { ret.r = std::stoi(str.substr(0, sep)); auto lastsep = sep; sep = str.find(',', sep+1); if (sep == std::string::npos) throw Exception{"Invalid color"}; ret.g = std::stoi(str.substr(lastsep+1, sep-lastsep)); lastsep = sep; sep = str.find(',', sep+1); bool alpha = true; if (sep == std::string::npos) alpha = false; ret.b = std::stoi(str.substr(lastsep+1, sep-lastsep)); if (!alpha) ret.a = 255; else ret.a = std::stoi(str.substr(sep+1)); } catch (std::invalid_argument &ex) { throw Exception{"Invalid color"}; } return ret; } std::tuple parseFont(std::string const& str) { auto sep = str.find(','); if (sep == std::string::npos) return make_tuple(str, 12); else return make_tuple(str.substr(0, sep), std::stoi(str.substr(sep+1))); } State state_; std::vector stack_; std::unique_ptr root_; std::unordered_map styles_; }; static void _startDocument(void *userdata) { static_cast(userdata)->startDocument(); } static void _endDocument(void *userdata) { static_cast(userdata)->endDocument(); } static void _startElement(void *userdata, const xmlChar *name, const xmlChar **attrs) { static_cast(userdata)->startElement(name, attrs); } static void _endElement(void *userdata, const xmlChar *name) { static_cast(userdata)->endElement(name); } static xmlSAXHandler saxHandlers{ nullptr, // internalSubsetSAXFunc internalSubset; nullptr, // isStandaloneSAXFunc isStandalone; nullptr, // hasInternalSubsetSAXFunc hasInternalSubset; nullptr, // hasExternalSubsetSAXFunc hasExternalSubset; nullptr, // resolveEntitySAXFunc resolveEntity; nullptr, // getEntitySAXFunc getEntity; nullptr, // entityDeclSAXFunc entityDecl; nullptr, // notationDeclSAXFunc notationDecl; nullptr, // attributeDeclSAXFunc attributeDecl; nullptr, // elementDeclSAXFunc elementDecl; nullptr, // unparsedEntityDeclSAXFunc unparsedEntityDecl; nullptr, // setDocumentLocatorSAXFunc setDocumentLocator; &_startDocument, // startDocumentSAXFunc startDocument; &_endDocument, // endDocumentSAXFunc endDocument; &_startElement, // startElementSAXFunc startElement; &_endElement, // endElementSAXFunc endElement; nullptr, // referenceSAXFunc reference; nullptr, // charactersSAXFunc characters; nullptr, // ignorableWhitespaceSAXFunc ignorableWhitespace; nullptr, // processingInstructionSAXFunc processingInstruction; nullptr, // commentSAXFunc comment; nullptr, // warningSAXFunc warning; nullptr, // errorSAXFunc error; nullptr, // fatalErrorSAXFunc fatalError; /* unused error() get all the errors */ nullptr, // getParameterEntitySAXFunc getParameterEntity; nullptr, // cdataBlockSAXFunc cdataBlock; nullptr, // externalSubsetSAXFunc externalSubset; false, // unsigned int initialized; // /* The following fields are extensions available only on version 2 */ nullptr, // void *_private; nullptr, // startElementNsSAX2Func startElementNs; nullptr, // endElementNsSAX2Func endElementNs; nullptr // xmlStructuredErrorFunc serror; }; std::unique_ptr loadGUIFromFile(std::string const& filename) { GUISAXParser parser{}; xmlSAXUserParseFile(&saxHandlers, &parser, filename.c_str()); return parser.getResult(); }