1 #pragma once
2 #ifndef ES_CORE_THEME_DATA_H
3 #define ES_CORE_THEME_DATA_H
4 
5 #include "math/Vector2f.h"
6 #include "math/Vector4f.h"
7 #include "utils/FileSystemUtil.h"
8 #include <deque>
9 #include <map>
10 #include <memory>
11 #include <sstream>
12 #include <vector>
13 
14 namespace pugi { class xml_node; }
15 
16 template<typename T>
17 class TextListComponent;
18 
19 class GuiComponent;
20 class ImageComponent;
21 class NinePatchComponent;
22 class Sound;
23 class TextComponent;
24 class Window;
25 
26 namespace ThemeFlags
27 {
28 	enum PropertyFlags : unsigned int
29 	{
30 		PATH = 1,
31 		POSITION = 2,
32 		SIZE = 4,
33 		ORIGIN = 8,
34 		COLOR = 16,
35 		FONT_PATH = 32,
36 		FONT_SIZE = 64,
37 		SOUND = 128,
38 		ALIGNMENT = 256,
39 		TEXT = 512,
40 		FORCE_UPPERCASE = 1024,
41 		LINE_SPACING = 2048,
42 		DELAY = 4096,
43 		Z_INDEX = 8192,
44 		ROTATION = 16384,
45 		VISIBLE = 32768,
46 		ALL = 0xFFFFFFFF
47 	};
48 }
49 
50 class ThemeException : public std::exception
51 {
52 public:
53 	std::string msg;
54 
what()55 	virtual const char* what() const throw() { return msg.c_str(); }
56 
57 	template<typename T>
58 	friend ThemeException& operator<<(ThemeException& e, T msg);
59 
setFiles(const std::deque<std::string> & deque)60 	inline void setFiles(const std::deque<std::string>& deque)
61 	{
62 		*this << "from theme \"" << deque.front() << "\"\n";
63 		for(auto it = deque.cbegin() + 1; it != deque.cend(); it++)
64 			*this << "  (from included file \"" << (*it) << "\")\n";
65 		*this << "    ";
66 	}
67 };
68 
69 template<typename T>
70 ThemeException& operator<<(ThemeException& e, T appendMsg)
71 {
72 	std::stringstream ss;
73 	ss << e.msg << appendMsg;
74 	e.msg = ss.str();
75 	return e;
76 }
77 
78 struct ThemeSet
79 {
80 	std::string path;
81 
getNameThemeSet82 	inline std::string getName() const { return Utils::FileSystem::getStem(path); }
getThemePathThemeSet83 	inline std::string getThemePath(const std::string& system) const { return path + "/" + system + "/theme.xml"; }
84 };
85 
86 class ThemeData
87 {
88 public:
89 
90 	class ThemeElement
91 	{
92 	public:
93 		bool extra;
94 		std::string type;
95 
96 		struct Property
97 		{
98 			void operator= (const Vector4f& value)     { r = value; v = Vector2f(value.x(), value.y()); }
99 			void operator= (const Vector2f& value)     { v = value; }
100 			void operator= (const std::string& value)  { s = value; }
101 			void operator= (const unsigned int& value) { i = value; }
102 			void operator= (const float& value)        { f = value; }
103 			void operator= (const bool& value)         { b = value; }
104 
105 			Vector4f     r;
106 			Vector2f     v;
107 			std::string  s;
108 			unsigned int i;
109 			float        f;
110 			bool         b;
111 		};
112 
113 		std::map< std::string, Property > properties;
114 
115 		template<typename T>
get(const std::string & prop)116 		const T get(const std::string& prop) const
117 		{
118 			if(     std::is_same<T, Vector2f>::value)     return *(const T*)&properties.at(prop).v;
119 			else if(std::is_same<T, std::string>::value)  return *(const T*)&properties.at(prop).s;
120 			else if(std::is_same<T, unsigned int>::value) return *(const T*)&properties.at(prop).i;
121 			else if(std::is_same<T, float>::value)        return *(const T*)&properties.at(prop).f;
122 			else if(std::is_same<T, bool>::value)         return *(const T*)&properties.at(prop).b;
123 			else if(std::is_same<T, Vector4f>::value)     return *(const T*)&properties.at(prop).r;
124 			return T();
125 		}
126 
has(const std::string & prop)127 		inline bool has(const std::string& prop) const { return (properties.find(prop) != properties.cend()); }
128 	};
129 
130 private:
131 	class ThemeView
132 	{
133 	public:
134 		std::map<std::string, ThemeElement> elements;
135 		std::vector<std::string> orderedKeys;
136 	};
137 
138 public:
139 
140 	ThemeData();
141 
142 	// throws ThemeException
143 	void loadFile(std::map<std::string, std::string> sysDataMap, const std::string& path);
144 
145 	enum ElementPropertyType
146 	{
147 		NORMALIZED_RECT,
148 		NORMALIZED_PAIR,
149 		PATH,
150 		STRING,
151 		COLOR,
152 		FLOAT,
153 		BOOLEAN
154 	};
155 
156 	bool hasView(const std::string& view);
157 
158 	// If expectedType is an empty string, will do no type checking.
159 	const ThemeElement* getElement(const std::string& view, const std::string& element, const std::string& expectedType) const;
160 
161 	static std::vector<GuiComponent*> makeExtras(const std::shared_ptr<ThemeData>& theme, const std::string& view, Window* window);
162 
163 	static const std::shared_ptr<ThemeData>& getDefault();
164 
165 	static std::map<std::string, ThemeSet> getThemeSets();
166 	static std::string getThemeFromCurrentSet(const std::string& system);
167 
168 private:
169 	static std::map< std::string, std::map<std::string, ElementPropertyType> > sElementMap;
170 	static std::vector<std::string> sSupportedFeatures;
171 	static std::vector<std::string> sSupportedViews;
172 
173 	std::deque<std::string> mPaths;
174 	float mVersion;
175 
176 	void parseFeatures(const pugi::xml_node& themeRoot);
177 	void parseIncludes(const pugi::xml_node& themeRoot);
178 	void parseVariables(const pugi::xml_node& root);
179 	void parseViews(const pugi::xml_node& themeRoot);
180 	void parseView(const pugi::xml_node& viewNode, ThemeView& view);
181 	void parseElement(const pugi::xml_node& elementNode, const std::map<std::string, ElementPropertyType>& typeMap, ThemeElement& element);
182 
183 	std::map<std::string, ThemeView> mViews;
184 };
185 
186 #endif // ES_CORE_THEME_DATA_H
187