Add warning when disabling secure.enable_security (#9943)
[oweals/minetest.git] / src / gui / StyleSpec.h
1 /*
2 Minetest
3 Copyright (C) 2019 rubenwardy
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "client/tile.h" // ITextureSource
21 #include "debug.h"
22 #include "irrlichttypes_extrabloated.h"
23 #include "util/string.h"
24 #include <array>
25
26 #pragma once
27
28 class StyleSpec
29 {
30 public:
31         enum Property
32         {
33                 TEXTCOLOR,
34                 BGCOLOR,
35                 BGCOLOR_HOVERED, // Note: Deprecated property
36                 BGCOLOR_PRESSED, // Note: Deprecated property
37                 NOCLIP,
38                 BORDER,
39                 BGIMG,
40                 BGIMG_HOVERED, // Note: Deprecated property
41                 BGIMG_MIDDLE,
42                 BGIMG_PRESSED, // Note: Deprecated property
43                 FGIMG,
44                 FGIMG_HOVERED, // Note: Deprecated property
45                 FGIMG_PRESSED, // Note: Deprecated property
46                 ALPHA,
47                 CONTENT_OFFSET,
48                 PADDING,
49                 NUM_PROPERTIES,
50                 NONE
51         };
52         enum State
53         {
54                 STATE_DEFAULT = 0,
55                 STATE_HOVERED = 1 << 0,
56                 STATE_PRESSED = 1 << 1,
57                 NUM_STATES = 1 << 2,
58                 STATE_INVALID = 1 << 3,
59         };
60
61 private:
62         std::array<bool, NUM_PROPERTIES> property_set{};
63         std::array<std::string, NUM_PROPERTIES> properties;
64         State state_map = STATE_DEFAULT;
65
66 public:
67         static Property GetPropertyByName(const std::string &name)
68         {
69                 if (name == "textcolor") {
70                         return TEXTCOLOR;
71                 } else if (name == "bgcolor") {
72                         return BGCOLOR;
73                 } else if (name == "bgcolor_hovered") {
74                         return BGCOLOR_HOVERED;
75                 } else if (name == "bgcolor_pressed") {
76                         return BGCOLOR_PRESSED;
77                 } else if (name == "noclip") {
78                         return NOCLIP;
79                 } else if (name == "border") {
80                         return BORDER;
81                 } else if (name == "bgimg") {
82                         return BGIMG;
83                 } else if (name == "bgimg_hovered") {
84                         return BGIMG_HOVERED;
85                 } else if (name == "bgimg_middle") {
86                         return BGIMG_MIDDLE;
87                 } else if (name == "bgimg_pressed") {
88                         return BGIMG_PRESSED;
89                 } else if (name == "fgimg") {
90                         return FGIMG;
91                 } else if (name == "fgimg_hovered") {
92                         return FGIMG_HOVERED;
93                 } else if (name == "fgimg_pressed") {
94                         return FGIMG_PRESSED;
95                 } else if (name == "alpha") {
96                         return ALPHA;
97                 } else if (name == "content_offset") {
98                         return CONTENT_OFFSET;
99                 } else if (name == "padding") {
100                         return PADDING;
101                 } else {
102                         return NONE;
103                 }
104         }
105
106         std::string get(Property prop, std::string def) const
107         {
108                 const auto &val = properties[prop];
109                 return val.empty() ? def : val;
110         }
111
112         void set(Property prop, const std::string &value)
113         {
114                 properties[prop] = value;
115                 property_set[prop] = true;
116         }
117
118         //! Parses a name and returns the corresponding state enum
119         static State getStateByName(const std::string &name)
120         {
121                 if (name == "default") {
122                         return STATE_DEFAULT;
123                 } else if (name == "hovered") {
124                         return STATE_HOVERED;
125                 } else if (name == "pressed") {
126                         return STATE_PRESSED;
127                 } else {
128                         return STATE_INVALID;
129                 }
130         }
131
132         //! Gets the state that this style is intended for
133         State getState() const
134         {
135                 return state_map;
136         }
137
138         //! Set the given state on this style
139         void addState(State state)
140         {
141                 FATAL_ERROR_IF(state >= NUM_STATES, "Out-of-bounds state received");
142
143                 state_map = static_cast<State>(state_map | state);
144         }
145
146         //! Using a list of styles mapped to state values, calculate the final
147         //  combined style for a state by propagating values in its component states
148         static StyleSpec getStyleFromStatePropagation(const std::array<StyleSpec, NUM_STATES> &styles, State state)
149         {
150                 StyleSpec temp = styles[StyleSpec::STATE_DEFAULT];
151                 temp.state_map = state;
152                 for (int i = StyleSpec::STATE_DEFAULT + 1; i <= state; i++) {
153                         if ((state & i) != 0) {
154                                 temp = temp | styles[i];
155                         }
156                 }
157
158                 return temp;
159         }
160
161         video::SColor getColor(Property prop, video::SColor def) const
162         {
163                 const auto &val = properties[prop];
164                 if (val.empty()) {
165                         return def;
166                 }
167
168                 parseColorString(val, def, false, 0xFF);
169                 return def;
170         }
171
172         video::SColor getColor(Property prop) const
173         {
174                 const auto &val = properties[prop];
175                 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
176
177                 video::SColor color;
178                 parseColorString(val, color, false, 0xFF);
179                 return color;
180         }
181
182         irr::core::rect<s32> getRect(Property prop, irr::core::rect<s32> def) const
183         {
184                 const auto &val = properties[prop];
185                 if (val.empty())
186                         return def;
187
188                 irr::core::rect<s32> rect;
189                 if (!parseRect(val, &rect))
190                         return def;
191
192                 return rect;
193         }
194
195         irr::core::rect<s32> getRect(Property prop) const
196         {
197                 const auto &val = properties[prop];
198                 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
199
200                 irr::core::rect<s32> rect;
201                 parseRect(val, &rect);
202                 return rect;
203         }
204
205         irr::core::vector2d<s32> getVector2i(Property prop, irr::core::vector2d<s32> def) const
206         {
207                 const auto &val = properties[prop];
208                 if (val.empty())
209                         return def;
210
211                 irr::core::vector2d<s32> vec;
212                 if (!parseVector2i(val, &vec))
213                         return def;
214
215                 return vec;
216         }
217
218         irr::core::vector2d<s32> getVector2i(Property prop) const
219         {
220                 const auto &val = properties[prop];
221                 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
222
223                 irr::core::vector2d<s32> vec;
224                 parseVector2i(val, &vec);
225                 return vec;
226         }
227
228         video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc,
229                         video::ITexture *def) const
230         {
231                 const auto &val = properties[prop];
232                 if (val.empty()) {
233                         return def;
234                 }
235
236                 video::ITexture *texture = tsrc->getTexture(val);
237
238                 return texture;
239         }
240
241         video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc) const
242         {
243                 const auto &val = properties[prop];
244                 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
245
246                 video::ITexture *texture = tsrc->getTexture(val);
247
248                 return texture;
249         }
250
251         bool getBool(Property prop, bool def) const
252         {
253                 const auto &val = properties[prop];
254                 if (val.empty()) {
255                         return def;
256                 }
257
258                 return is_yes(val);
259         }
260
261         inline bool isNotDefault(Property prop) const
262         {
263                 return !properties[prop].empty();
264         }
265
266         inline bool hasProperty(Property prop) const { return property_set[prop]; }
267
268         StyleSpec &operator|=(const StyleSpec &other)
269         {
270                 for (size_t i = 0; i < NUM_PROPERTIES; i++) {
271                         auto prop = (Property)i;
272                         if (other.hasProperty(prop)) {
273                                 set(prop, other.get(prop, ""));
274                         }
275                 }
276
277                 return *this;
278         }
279
280         StyleSpec operator|(const StyleSpec &other) const
281         {
282                 StyleSpec newspec = *this;
283                 newspec |= other;
284                 return newspec;
285         }
286
287 private:
288         bool parseRect(const std::string &value, irr::core::rect<s32> *parsed_rect) const
289         {
290                 irr::core::rect<s32> rect;
291                 std::vector<std::string> v_rect = split(value, ',');
292
293                 if (v_rect.size() == 1) {
294                         s32 x = stoi(v_rect[0]);
295                         rect.UpperLeftCorner = irr::core::vector2di(x, x);
296                         rect.LowerRightCorner = irr::core::vector2di(-x, -x);
297                 } else if (v_rect.size() == 2) {
298                         s32 x = stoi(v_rect[0]);
299                         s32 y = stoi(v_rect[1]);
300                         rect.UpperLeftCorner = irr::core::vector2di(x, y);
301                         rect.LowerRightCorner = irr::core::vector2di(-x, -y);
302                         // `-x` is interpreted as `w - x`
303                 } else if (v_rect.size() == 4) {
304                         rect.UpperLeftCorner = irr::core::vector2di(
305                                         stoi(v_rect[0]), stoi(v_rect[1]));
306                         rect.LowerRightCorner = irr::core::vector2di(
307                                         stoi(v_rect[2]), stoi(v_rect[3]));
308                 } else {
309                         warningstream << "Invalid rectangle string format: \"" << value
310                                         << "\"" << std::endl;
311                         return false;
312                 }
313
314                 *parsed_rect = rect;
315
316                 return true;
317         }
318
319         bool parseVector2i(const std::string &value, irr::core::vector2d<s32> *parsed_vec) const
320         {
321                 irr::core::vector2d<s32> vec;
322                 std::vector<std::string> v_vector = split(value, ',');
323
324                 if (v_vector.size() == 1) {
325                         s32 x = stoi(v_vector[0]);
326                         vec.X = x;
327                         vec.Y = x;
328                 } else if (v_vector.size() == 2) {
329                         s32 x = stoi(v_vector[0]);
330                         s32 y = stoi(v_vector[1]);
331                         vec.X = x;
332                         vec.Y = y;
333                 } else {
334                         warningstream << "Invalid vector2d string format: \"" << value
335                                         << "\"" << std::endl;
336                         return false;
337                 }
338
339                 *parsed_vec = vec;
340
341                 return true;
342         }
343 };