Luanti 5.11.0-dev
 
Loading...
Searching...
No Matches
StyleSpec.h
Go to the documentation of this file.
1// Luanti
2// SPDX-License-Identifier: LGPL-2.1-or-later
3// Copyright (C) 2019 rubenwardy
4
5#pragma once
6
8#include "client/fontengine.h"
9#include "debug.h"
11#include "util/string.h"
12#include <algorithm>
13#include <array>
14#include <vector>
15
16
18{
19public:
21 {
24 BGCOLOR_HOVERED, // Note: Deprecated property
25 BGCOLOR_PRESSED, // Note: Deprecated property
29 BGIMG_HOVERED, // Note: Deprecated property
31 BGIMG_PRESSED, // Note: Deprecated property
33 FGIMG_HOVERED, // Note: Deprecated property
35 FGIMG_PRESSED, // Note: Deprecated property
48 NONE
49 };
50
51 // State is a bitfield, it's possible to have multiple of these at once
52 enum State : u8
53 {
55 STATE_FOCUSED = 1 << 0,
56 STATE_HOVERED = 1 << 1,
57 STATE_PRESSED = 1 << 2,
58 NUM_STATES = 1 << 3, // This includes all permutations
59 STATE_INVALID = 1 << 4,
60 };
61
62private:
63 std::array<bool, NUM_PROPERTIES> property_set{};
64 std::array<std::string, NUM_PROPERTIES> properties;
66
67public:
68 static Property GetPropertyByName(const std::string &name)
69 {
70 if (name == "textcolor") {
71 return TEXTCOLOR;
72 } else if (name == "bgcolor") {
73 return BGCOLOR;
74 } else if (name == "bgcolor_hovered") {
75 return BGCOLOR_HOVERED;
76 } else if (name == "bgcolor_pressed") {
77 return BGCOLOR_PRESSED;
78 } else if (name == "noclip") {
79 return NOCLIP;
80 } else if (name == "border") {
81 return BORDER;
82 } else if (name == "bgimg") {
83 return BGIMG;
84 } else if (name == "bgimg_hovered") {
85 return BGIMG_HOVERED;
86 } else if (name == "bgimg_middle") {
87 return BGIMG_MIDDLE;
88 } else if (name == "bgimg_pressed") {
89 return BGIMG_PRESSED;
90 } else if (name == "fgimg") {
91 return FGIMG;
92 } else if (name == "fgimg_hovered") {
93 return FGIMG_HOVERED;
94 } else if (name == "fgimg_middle") {
95 return FGIMG_MIDDLE;
96 } else if (name == "fgimg_pressed") {
97 return FGIMG_PRESSED;
98 } else if (name == "alpha") {
99 return ALPHA;
100 } else if (name == "content_offset") {
101 return CONTENT_OFFSET;
102 } else if (name == "padding") {
103 return PADDING;
104 } else if (name == "font") {
105 return FONT;
106 } else if (name == "font_size") {
107 return FONT_SIZE;
108 } else if (name == "colors") {
109 return COLORS;
110 } else if (name == "bordercolors") {
111 return BORDERCOLORS;
112 } else if (name == "borderwidths") {
113 return BORDERWIDTHS;
114 } else if (name == "sound") {
115 return SOUND;
116 } else if (name == "spacing") {
117 return SPACING;
118 } else if (name == "size") {
119 return SIZE;
120 } else {
121 return NONE;
122 }
123 }
124
125 std::string get(Property prop, std::string def) const
126 {
127 const auto &val = properties[prop];
128 return val.empty() ? def : val;
129 }
130
131 void set(Property prop, const std::string &value)
132 {
133 properties[prop] = value;
134 property_set[prop] = true;
135 }
136
138 static State getStateByName(const std::string &name)
139 {
140 if (name == "default") {
141 return STATE_DEFAULT;
142 } else if (name == "focused") {
143 return STATE_FOCUSED;
144 } else if (name == "hovered") {
145 return STATE_HOVERED;
146 } else if (name == "pressed") {
147 return STATE_PRESSED;
148 } else {
149 return STATE_INVALID;
150 }
151 }
152
155 {
156 return state_map;
157 }
158
160 void addState(State state)
161 {
162 FATAL_ERROR_IF(state >= NUM_STATES, "Out-of-bounds state received");
163
164 state_map = static_cast<State>(state_map | state);
165 }
166
168 // combined style for a state by propagating values in its component states
169 static StyleSpec getStyleFromStatePropagation(const std::array<StyleSpec, NUM_STATES> &styles, State state)
170 {
171 StyleSpec temp = styles[StyleSpec::STATE_DEFAULT];
172 temp.state_map = state;
173 for (int i = StyleSpec::STATE_DEFAULT + 1; i <= state; i++) {
174 if ((state & i) != 0) {
175 temp = temp | styles[i];
176 }
177 }
178
179 return temp;
180 }
181
182 video::SColor getColor(Property prop, video::SColor def) const
183 {
184 const auto &val = properties[prop];
185 if (val.empty()) {
186 return def;
187 }
188
189 parseColorString(val, def, false, 0xFF);
190 return def;
191 }
192
193 video::SColor getColor(Property prop) const
194 {
195 const auto &val = properties[prop];
196 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
197
198 video::SColor color;
199 parseColorString(val, color, false, 0xFF);
200 return color;
201 }
202
203 std::array<video::SColor, 4> getColorArray(Property prop,
204 std::array<video::SColor, 4> def) const
205 {
206 const auto &val = properties[prop];
207 if (val.empty())
208 return def;
209
210 std::vector<std::string> strs;
211 if (!parseArray(val, strs))
212 return def;
213
214 for (size_t i = 0; i <= 3; i++) {
215 video::SColor color;
216 if (parseColorString(strs[i], color, false, 0xff))
217 def[i] = color;
218 }
219
220 return def;
221 }
222
223 std::array<s32, 4> getIntArray(Property prop, std::array<s32, 4> def) const
224 {
225 const auto &val = properties[prop];
226 if (val.empty())
227 return def;
228
229 std::vector<std::string> strs;
230 if (!parseArray(val, strs))
231 return def;
232
233 for (size_t i = 0; i <= 3; i++)
234 def[i] = stoi(strs[i]);
235
236 return def;
237 }
238
239 irr::core::rect<s32> getRect(Property prop, irr::core::rect<s32> def) const
240 {
241 const auto &val = properties[prop];
242 if (val.empty())
243 return def;
244
245 irr::core::rect<s32> rect;
246 if (!parseRect(val, &rect))
247 return def;
248
249 return rect;
250 }
251
252 irr::core::rect<s32> getRect(Property prop) const
253 {
254 const auto &val = properties[prop];
255 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
256
257 irr::core::rect<s32> rect;
258 parseRect(val, &rect);
259 return rect;
260 }
261
263 {
264 const auto &val = properties[prop];
265 if (val.empty())
266 return def;
267
268 v2f32 vec;
269 if (!parseVector2f(val, &vec))
270 return def;
271
272 return vec;
273 }
274
276 {
277 const auto &val = properties[prop];
278 if (val.empty())
279 return def;
280
281 v2f32 vec;
282 if (!parseVector2f(val, &vec))
283 return def;
284
285 return v2s32(vec.X, vec.Y);
286 }
287
289 {
290 const auto &val = properties[prop];
291 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
292
293 v2f32 vec;
294 parseVector2f(val, &vec);
295 return v2s32(vec.X, vec.Y);
296 }
297
298 gui::IGUIFont *getFont() const
299 {
300 FontSpec spec(FONT_SIZE_UNSPECIFIED, FM_Standard, false, false);
301
302 const std::string &font = properties[FONT];
303 const std::string &size = properties[FONT_SIZE];
304
305 if (font.empty() && size.empty())
306 return nullptr;
307
308 std::vector<std::string> modes = split(font, ',');
309
310 for (size_t i = 0; i < modes.size(); i++) {
311 if (modes[i] == "normal")
312 spec.mode = FM_Standard;
313 else if (modes[i] == "mono")
314 spec.mode = FM_Mono;
315 else if (modes[i] == "bold")
316 spec.bold = true;
317 else if (modes[i] == "italic")
318 spec.italic = true;
319 }
320
321 if (!size.empty()) {
322 int calc_size = 1;
323
324 if (size[0] == '*') {
325 std::string new_size = size.substr(1); // Remove '*' (invalid for stof)
326 calc_size = stof(new_size) * g_fontengine->getFontSize(spec.mode);
327 } else if (size[0] == '+' || size[0] == '-') {
328 calc_size = stoi(size) + g_fontengine->getFontSize(spec.mode);
329 } else {
330 calc_size = stoi(size);
331 }
332
333 spec.size = (unsigned)std::min(std::max(calc_size, 1), 999);
334 }
335
336 return g_fontengine->getFont(spec);
337 }
338
339 video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc,
340 video::ITexture *def) const
341 {
342 const auto &val = properties[prop];
343 if (val.empty()) {
344 return def;
345 }
346
347 video::ITexture *texture = tsrc->getTexture(val);
348
349 return texture;
350 }
351
352 video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc) const
353 {
354 const auto &val = properties[prop];
355 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
356
357 video::ITexture *texture = tsrc->getTexture(val);
358
359 return texture;
360 }
361
362 bool getBool(Property prop, bool def) const
363 {
364 const auto &val = properties[prop];
365 if (val.empty()) {
366 return def;
367 }
368
369 return is_yes(val);
370 }
371
372 inline bool isNotDefault(Property prop) const
373 {
374 return !properties[prop].empty();
375 }
376
377 inline bool hasProperty(Property prop) const { return property_set[prop]; }
378
380 {
381 for (size_t i = 0; i < NUM_PROPERTIES; i++) {
382 auto prop = (Property)i;
383 if (other.hasProperty(prop)) {
384 set(prop, other.get(prop, ""));
385 }
386 }
387
388 return *this;
389 }
390
391 StyleSpec operator|(const StyleSpec &other) const
392 {
393 StyleSpec newspec = *this;
394 newspec |= other;
395 return newspec;
396 }
397
398private:
399 bool parseArray(const std::string &value, std::vector<std::string> &arr) const
400 {
401 std::vector<std::string> strs = split(value, ',');
402
403 if (strs.size() == 1) {
404 arr = {strs[0], strs[0], strs[0], strs[0]};
405 } else if (strs.size() == 2) {
406 arr = {strs[0], strs[1], strs[0], strs[1]};
407 } else if (strs.size() == 4) {
408 arr = strs;
409 } else {
410 warningstream << "Invalid array size (" << strs.size()
411 << " arguments): \"" << value << "\"" << std::endl;
412 return false;
413 }
414 return true;
415 }
416
417 bool parseRect(const std::string &value, irr::core::rect<s32> *parsed_rect) const
418 {
419 irr::core::rect<s32> rect;
420 std::vector<std::string> v_rect = split(value, ',');
421
422 if (v_rect.size() == 1) {
423 s32 x = stoi(v_rect[0]);
424 rect.UpperLeftCorner = irr::core::vector2di(x, x);
425 rect.LowerRightCorner = irr::core::vector2di(-x, -x);
426 } else if (v_rect.size() == 2) {
427 s32 x = stoi(v_rect[0]);
428 s32 y = stoi(v_rect[1]);
429 rect.UpperLeftCorner = irr::core::vector2di(x, y);
430 rect.LowerRightCorner = irr::core::vector2di(-x, -y);
431 // `-x` is interpreted as `w - x`
432 } else if (v_rect.size() == 4) {
433 rect.UpperLeftCorner = irr::core::vector2di(
434 stoi(v_rect[0]), stoi(v_rect[1]));
435 rect.LowerRightCorner = irr::core::vector2di(
436 stoi(v_rect[2]), stoi(v_rect[3]));
437 } else {
438 warningstream << "Invalid rectangle string format: \"" << value
439 << "\"" << std::endl;
440 return false;
441 }
442
443 *parsed_rect = rect;
444
445 return true;
446 }
447
448 bool parseVector2f(const std::string &value, v2f32 *parsed_vec) const
449 {
450 v2f32 vec;
451 std::vector<std::string> v_vector = split(value, ',');
452
453 if (v_vector.size() == 1) {
454 f32 x = stof(v_vector[0]);
455 vec.X = x;
456 vec.Y = x;
457 } else if (v_vector.size() == 2) {
458 vec.X = stof(v_vector[0]);
459 vec.Y = stof(v_vector[1]);
460 } else {
461 warningstream << "Invalid 2d vector string format: \"" << value
462 << "\"" << std::endl;
463 return false;
464 }
465
466 *parsed_vec = vec;
467
468 return true;
469 }
470};
unsigned int getFontSize(FontMode mode)
get font size for a specific mode
Definition fontengine.cpp:152
irr::gui::IGUIFont * getFont(FontSpec spec)
Definition fontengine.cpp:80
Definition texturesource.h:25
virtual video::ITexture * getTexture(const std::string &name, u32 *id=nullptr)=0
Definition StyleSpec.h:18
gui::IGUIFont * getFont() const
Definition StyleSpec.h:298
static State getStateByName(const std::string &name)
Parses a name and returns the corresponding state enum.
Definition StyleSpec.h:138
video::SColor getColor(Property prop) const
Definition StyleSpec.h:193
std::array< bool, NUM_PROPERTIES > property_set
Definition StyleSpec.h:63
bool getBool(Property prop, bool def) const
Definition StyleSpec.h:362
State
Definition StyleSpec.h:53
@ STATE_HOVERED
Definition StyleSpec.h:56
@ STATE_INVALID
Definition StyleSpec.h:59
@ STATE_FOCUSED
Definition StyleSpec.h:55
@ STATE_DEFAULT
Definition StyleSpec.h:54
@ STATE_PRESSED
Definition StyleSpec.h:57
@ NUM_STATES
Definition StyleSpec.h:58
bool parseVector2f(const std::string &value, v2f32 *parsed_vec) const
Definition StyleSpec.h:448
State getState() const
Gets the state that this style is intended for.
Definition StyleSpec.h:154
std::string get(Property prop, std::string def) const
Definition StyleSpec.h:125
v2s32 getVector2i(Property prop) const
Definition StyleSpec.h:288
Property
Definition StyleSpec.h:21
@ FONT_SIZE
Definition StyleSpec.h:40
@ BGCOLOR_PRESSED
Definition StyleSpec.h:25
@ SOUND
Definition StyleSpec.h:44
@ BORDERCOLORS
Definition StyleSpec.h:42
@ BGIMG_PRESSED
Definition StyleSpec.h:31
@ ALPHA
Definition StyleSpec.h:36
@ BGCOLOR_HOVERED
Definition StyleSpec.h:24
@ SPACING
Definition StyleSpec.h:45
@ BGIMG_MIDDLE
Definition StyleSpec.h:30
@ NOCLIP
Definition StyleSpec.h:26
@ SIZE
Definition StyleSpec.h:46
@ PADDING
Definition StyleSpec.h:38
@ NUM_PROPERTIES
Definition StyleSpec.h:47
@ COLORS
Definition StyleSpec.h:41
@ FONT
Definition StyleSpec.h:39
@ BORDER
Definition StyleSpec.h:27
@ FGIMG_PRESSED
Definition StyleSpec.h:35
@ NONE
Definition StyleSpec.h:48
@ TEXTCOLOR
Definition StyleSpec.h:22
@ BGCOLOR
Definition StyleSpec.h:23
@ BGIMG
Definition StyleSpec.h:28
@ BGIMG_HOVERED
Definition StyleSpec.h:29
@ FGIMG
Definition StyleSpec.h:32
@ BORDERWIDTHS
Definition StyleSpec.h:43
@ FGIMG_MIDDLE
Definition StyleSpec.h:34
@ CONTENT_OFFSET
Definition StyleSpec.h:37
@ FGIMG_HOVERED
Definition StyleSpec.h:33
v2s32 getVector2i(Property prop, v2s32 def) const
Definition StyleSpec.h:275
static StyleSpec getStyleFromStatePropagation(const std::array< StyleSpec, NUM_STATES > &styles, State state)
Using a list of styles mapped to state values, calculate the final.
Definition StyleSpec.h:169
std::array< video::SColor, 4 > getColorArray(Property prop, std::array< video::SColor, 4 > def) const
Definition StyleSpec.h:203
static Property GetPropertyByName(const std::string &name)
Definition StyleSpec.h:68
irr::core::rect< s32 > getRect(Property prop) const
Definition StyleSpec.h:252
bool isNotDefault(Property prop) const
Definition StyleSpec.h:372
StyleSpec operator|(const StyleSpec &other) const
Definition StyleSpec.h:391
void addState(State state)
Set the given state on this style.
Definition StyleSpec.h:160
bool parseArray(const std::string &value, std::vector< std::string > &arr) const
Definition StyleSpec.h:399
video::ITexture * getTexture(Property prop, ISimpleTextureSource *tsrc) const
Definition StyleSpec.h:352
irr::core::rect< s32 > getRect(Property prop, irr::core::rect< s32 > def) const
Definition StyleSpec.h:239
video::SColor getColor(Property prop, video::SColor def) const
Definition StyleSpec.h:182
State state_map
Definition StyleSpec.h:65
StyleSpec & operator|=(const StyleSpec &other)
Definition StyleSpec.h:379
void set(Property prop, const std::string &value)
Definition StyleSpec.h:131
video::ITexture * getTexture(Property prop, ISimpleTextureSource *tsrc, video::ITexture *def) const
Definition StyleSpec.h:339
bool parseRect(const std::string &value, irr::core::rect< s32 > *parsed_rect) const
Definition StyleSpec.h:417
std::array< s32, 4 > getIntArray(Property prop, std::array< s32, 4 > def) const
Definition StyleSpec.h:223
bool hasProperty(Property prop) const
Definition StyleSpec.h:377
std::array< std::string, NUM_PROPERTIES > properties
Definition StyleSpec.h:64
v2f32 getVector2f(Property prop, v2f32 def) const
Definition StyleSpec.h:262
#define FATAL_ERROR_IF(expr, msg)
Definition debug.h:36
FontEngine * g_fontengine
reference to access font engine, has to be initialized by main
Definition fontengine.cpp:19
@ FM_Standard
Definition fontengine.h:23
@ FM_Mono
Definition fontengine.h:24
#define FONT_SIZE_UNSPECIFIED
Definition fontengine.h:20
core::vector2d< f32 > v2f32
Definition irr_v2d.h:15
core::vector2d< s32 > v2s32
Definition irr_v2d.h:13
thread_local LogStream warningstream
bool parseColorString(const std::string &value, video::SColor &color, bool quiet, unsigned char default_alpha)
Definition string.cpp:600
#define stoi
Definition string.h:431
#define stof
Definition string.h:432
std::vector< std::basic_string< T > > split(const std::basic_string< T > &s, T delim)
Definition string.h:611
bool is_yes(std::string_view str)
Returns whether str should be regarded as (bool) true.
Definition string.h:381
Definition fontengine.h:30
bool italic
Definition fontengine.h:45
bool bold
Definition fontengine.h:44
unsigned int size
Definition fontengine.h:42
FontMode mode
Definition fontengine.h:43