// SPDX-License-Identifier: MIT // // EmulationStation Desktop Edition // ThemeData.h // // Finds available themes on the file system and loads these, // including the parsing of individual theme components // (includes, features, variables, views, elements). // #ifndef ES_CORE_THEME_DATA_H #define ES_CORE_THEME_DATA_H #include "math/Vector2f.h" #include "math/Vector4f.h" #include "utils/FileSystemUtil.h" #include #include #include #include #include namespace pugi { class xml_node; } template class TextListComponent; class GuiComponent; class ImageComponent; class NinePatchComponent; class Sound; class TextComponent; class Window; namespace ThemeFlags { enum PropertyFlags : unsigned int { PATH = 1, POSITION = 2, SIZE = 4, ORIGIN = 8, COLOR = 16, FONT_PATH = 32, FONT_SIZE = 64, SOUND = 128, ALIGNMENT = 256, TEXT = 512, FORCE_UPPERCASE = 1024, LINE_SPACING = 2048, DELAY = 4096, Z_INDEX = 8192, ROTATION = 16384, VISIBLE = 32768, ALL = 0xFFFFFFFF }; } class ThemeException : public std::exception { public: std::string msg; virtual const char* what() const throw() { return msg.c_str(); } template friend ThemeException& operator<<(ThemeException& e, T msg); inline void setFiles(const std::deque& deque) { *this << "From theme \"" << deque.front() << "\"\n"; for (auto it = deque.cbegin() + 1; it != deque.cend(); it++) *this << " (from included file \"" << (*it) << "\")\n"; *this << " "; } }; template ThemeException& operator<<(ThemeException& e, T appendMsg) { std::stringstream ss; ss << e.msg << appendMsg; e.msg = ss.str(); return e; } struct ThemeSet { std::string path; inline std::string getName() const { return Utils::FileSystem::getStem(path); } inline std::string getThemePath(const std::string& system) const { return path + "/" + system + "/theme.xml"; } }; class ThemeData { public: class ThemeElement { public: bool extra; std::string type; struct Property { void operator= (const Vector4f& value) { r = value; v = Vector2f(value.x(), value.y()); } void operator= (const Vector2f& value) { v = value; } void operator= (const std::string& value) { s = value; } void operator= (const unsigned int& value) { i = value; } void operator= (const float& value) { f = value; } void operator= (const bool& value) { b = value; } Vector4f r; Vector2f v; std::string s; unsigned int i; float f; bool b; }; std::map< std::string, Property > properties; template const T get(const std::string& prop) const { if (std::is_same::value) return *(const T*)&properties.at(prop).v; else if (std::is_same::value) return *(const T*)&properties.at(prop).s; else if (std::is_same::value) return *(const T*)&properties.at(prop).i; else if (std::is_same::value) return *(const T*)&properties.at(prop).f; else if (std::is_same::value) return *(const T*)&properties.at(prop).b; else if (std::is_same::value) return *(const T*)&properties.at(prop).r; return T(); } inline bool has(const std::string& prop) const { return (properties.find(prop) != properties.cend()); } }; private: class ThemeView { public: std::map elements; std::vector orderedKeys; }; public: ThemeData(); // Throws ThemeException. void loadFile(std::map sysDataMap, const std::string& path); enum ElementPropertyType { NORMALIZED_RECT, NORMALIZED_PAIR, PATH, STRING, COLOR, FLOAT, BOOLEAN }; bool hasView(const std::string& view); // If expectedType is an empty string, will do no type checking. const ThemeElement* getElement(const std::string& view, const std::string& element, const std::string& expectedType) const; static std::vector makeExtras(const std::shared_ptr& theme, const std::string& view, Window* window); static const std::shared_ptr& getDefault(); static std::map getThemeSets(); static std::string getThemeFromCurrentSet(const std::string& system); private: static std::map< std::string, std::map > sElementMap; static std::vector sSupportedFeatures; static std::vector sSupportedViews; std::deque mPaths; float mVersion; void parseFeatures(const pugi::xml_node& themeRoot); void parseIncludes(const pugi::xml_node& themeRoot); void parseVariables(const pugi::xml_node& root); void parseViews(const pugi::xml_node& themeRoot); void parseView(const pugi::xml_node& viewNode, ThemeView& view); void parseElement(const pugi::xml_node& elementNode, const std::map& typeMap, ThemeElement& element); std::map mViews; }; #endif // ES_CORE_THEME_DATA_H