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