HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
style.hpp
1// Copyright Take Vos 2024.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "style_path.hpp"
8#include "style_attributes.hpp"
9#include "style_pseudo_class.hpp"
10#include "../units/units.hpp"
11#include "../dispatch/dispatch.hpp"
12#include "../macros.hpp"
13#include <cstdint>
14#include <functional>
15#include <utility>
16
17hi_export_module(hikogui.theme : style);
18
19hi_export namespace hi {
20inline namespace v1 {
21
22class style {
23public:
24 using attributes_from_theme_type = std::function<style_attributes(style_path, style_pseudo_class)>;
25 using notifier_type = notifier<void(style_modify_mask, bool)>;
26 using callback_type = notifier_type::callback_type;
27 using callback_proto = notifier_type::callback_proto;
28
29 unit::pixels_f width;
30 unit::pixels_f height;
31 unit::pixels_f margin_left;
32 unit::pixels_f margin_bottom;
33 unit::pixels_f margin_right;
34 unit::pixels_f margin_top;
35 unit::pixels_f padding_left;
36 unit::pixels_f padding_bottom;
37 unit::pixels_f padding_right;
38 unit::pixels_f padding_top;
39 unit::pixels_f border_width;
40 unit::pixels_f border_bottom_left_radius;
41 unit::pixels_f border_bottom_right_radius;
42 unit::pixels_f border_top_left_radius;
43 unit::pixels_f border_top_right_radius;
44
45 float width_px;
46 float height_px;
47 float margin_left_px;
48 float margin_bottom_px;
49 float margin_right_px;
50 float margin_top_px;
51 float padding_left_px;
52 float padding_bottom_px;
53 float padding_right_px;
54 float padding_top_px;
55 float border_width_px;
56 float border_bottom_left_radius_px;
57 float border_bottom_right_radius_px;
58 float border_top_left_radius_px;
59 float border_top_right_radius_px;
60
61 hi::margins margins_px;
62 hi::margins padding_px;
63 hi::corner_radii border_radius_px;
64
67
68 color foreground_color;
69 color background_color;
70 color border_color;
71
72 style(style const&) noexcept = delete;
73 style(style&&) noexcept = delete;
74 style& operator=(style const&) noexcept = delete;
75 style& operator=(style&&) noexcept = delete;
76 style() noexcept = default;
77
78 void set_name(std::string name)
79 {
80 _name = name;
81 reload(true);
82 }
83
84 [[nodiscard]] std::string const& name() const noexcept
85 {
86 return _name;
87 }
88
89 void set_id(std::string id)
90 {
91 _id = std::move(id);
92 reload(true);
93 }
94
95 [[nodiscard]] std::string const& id() const noexcept
96 {
97 return _id;
98 }
99
100 void set_classes(std::vector<std::string> classes)
101 {
102 _classes = std::move(classes);
103 reload(true);
104 }
105
106 [[nodiscard]] std::vector<std::string> const& classes() const noexcept
107 {
108 return _classes;
109 }
110
111 void set_parent_path(style_path new_parent_path) noexcept
112 {
113 _parent_path = new_parent_path;
114 reload(true);
115 }
116
117 [[nodiscard]] style_path const &parent_path() const noexcept
118 {
119 return _parent_path;
120 }
121
122 [[nodiscard]] style_path path() const noexcept
123 {
124 auto r = parent_path();
125 r.emplace_back(_name, _id, _classes);
126 return r;
127 }
128
148 style& operator=(std::string style_string)
149 {
150 if (auto const optional_style = parse_style(style_string)) {
151 std::tie(_override_attributes, _id, _classes) = *optional_style;
152 reload(true);
153 } else if (optional_style.has_error()) {
154 throw parse_error(optional_style.error());
155 } else {
156 hi_no_default();
157 }
158 return *this;
159 }
160
161 [[nodiscard]] attributes_from_theme_type const& attributes_from_theme() const noexcept
162 {
163 return _attributes_from_theme;
164 }
165
166 void set_attributes_from_theme(attributes_from_theme_type new_attributes_from_theme)
167 {
168 _attributes_from_theme = std::move(new_attributes_from_theme);
169 reload(false);
170 }
171
172 [[nodiscard]] unit::pixel_density pixel_density() const noexcept
173 {
174 return _pixel_density;
175 }
176
177 void set_pixel_density(unit::pixel_density new_pixel_density)
178 {
179 _pixel_density = new_pixel_density;
180 update_attributes(style_modify_mask::pixel_density);
181 _notifier(style_modify_mask::pixel_density, false);
182 }
183
184 void set_pseudo_class(style_pseudo_class new_pseudo_class)
185 {
186 auto const old_pseudo_class = std::exchange(_pseudo_class, new_pseudo_class);
187
188 auto const i = std::to_underlying(old_pseudo_class);
189 auto const j = std::to_underlying(new_pseudo_class);
190 auto const mask = _pseudo_class_modifications[i + j * style_pseudo_class_size];
191
192 update_attributes(mask);
193 _notifier(mask, false);
194 }
195
206 void reload(bool path_has_changed = false) noexcept
207 {
208 if (not _attributes_from_theme) {
209 // The attributes_from_theme function may not yet been set when the
210 // path is configured or when the widget's tree is being setup.
211 return;
212 }
213
214 for (auto i = size_t{0}; i != style_pseudo_class_size; ++i) {
215 _loaded_attributes[i] = _attributes_from_theme(path(), static_cast<style_pseudo_class>(i));
216 _loaded_attributes[i].apply(_override_attributes);
217 }
218
219 for (auto i = size_t{0}; i != style_pseudo_class_size; ++i) {
220 auto const& src = _loaded_attributes[i];
221 for (auto j = size_t{0}; j != style_pseudo_class_size; ++j) {
222 auto const& dst = _loaded_attributes[j];
223 _pseudo_class_modifications[i + j * style_pseudo_class_size] = compare(src, dst);
224 }
225 }
226
227 update_attributes(style_modify_mask::all);
228 _notifier(style_modify_mask::all, path_has_changed);
229 }
230
245 template<forward_of<callback_proto> Func>
246 [[nodiscard]] callback_type subscribe(Func&& func, callback_flags flags = callback_flags::synchronous) noexcept
247 {
248 return _notifier.subscribe(std::forward<Func>(func), flags);
249 }
250
251private:
252 std::string _name;
253 std::string _id;
255
256 style_path _parent_path;
257 unit::pixel_density _pixel_density;
258 style_pseudo_class _pseudo_class;
259
262 attributes_from_theme_type _attributes_from_theme;
263
266 style_attributes _override_attributes;
267
271
275
278 style_attributes _attributes;
279
280 notifier_type _notifier;
281
282 void update_attributes(style_modify_mask mask)
283 {
284 if (to_bool(mask & style_modify_mask::color)) {
285 foreground_color = _attributes.foreground_color();
286 background_color = _attributes.background_color();
287 border_color = _attributes.border_color();
288 }
289
290 if (to_bool(mask & style_modify_mask::size)) {
291 width = _attributes.width() * _pixel_density;
292 height = _attributes.height() * _pixel_density;
293 width_px = width.in(unit::pixels);
294 height_px = height.in(unit::pixels);
295 }
296
297 if (to_bool(mask & style_modify_mask::margin)) {
298 margin_left = _attributes.margin_left() * _pixel_density;
299 margin_bottom = _attributes.margin_bottom() * _pixel_density;
300 margin_right = _attributes.margin_right() * _pixel_density;
301 margin_top = _attributes.margin_top() * _pixel_density;
302 padding_left = _attributes.padding_left() * _pixel_density;
303 padding_bottom = _attributes.padding_bottom() * _pixel_density;
304 padding_right = _attributes.padding_right() * _pixel_density;
305 padding_top = _attributes.padding_top() * _pixel_density;
306 margin_left_px = margin_left.in(unit::pixels);
307 margin_bottom_px = margin_bottom.in(unit::pixels);
308 margin_right_px = margin_right.in(unit::pixels);
309 margin_top_px = margin_top.in(unit::pixels);
310 padding_left_px = padding_left.in(unit::pixels);
311 padding_bottom_px = padding_bottom.in(unit::pixels);
312 padding_right_px = padding_right.in(unit::pixels);
313 padding_top_px = padding_top.in(unit::pixels);
314 margins_px = hi::margins{margin_left_px, margin_bottom_px, margin_right_px, margin_top_px};
315 padding_px = hi::margins{padding_left_px, padding_bottom_px, padding_right_px, padding_top_px};
316 }
317
318 if (to_bool(mask & style_modify_mask::weight)) {
319 border_width = _attributes.border_width() * _pixel_density;
320 border_bottom_left_radius = _attributes.border_bottom_left_radius() * _pixel_density;
321 border_bottom_right_radius = _attributes.border_bottom_right_radius() * _pixel_density;
322 border_top_left_radius = _attributes.border_top_left_radius() * _pixel_density;
323 border_top_right_radius = _attributes.border_top_right_radius() * _pixel_density;
324 border_width_px = border_width.in(unit::pixels);
325 border_bottom_left_radius_px = border_bottom_left_radius.in(unit::pixels);
326 border_bottom_right_radius_px = border_bottom_right_radius.in(unit::pixels);
327 border_top_left_radius_px = border_top_left_radius.in(unit::pixels);
328 border_top_right_radius_px = border_top_right_radius.in(unit::pixels);
329 border_radius_px = hi::corner_radii{
330 border_bottom_left_radius_px,
331 border_bottom_right_radius_px,
332 border_top_left_radius_px,
333 border_top_right_radius_px};
334 }
335
336 if (to_bool(mask & style_modify_mask::alignment)) {
337 horizontal_alignment = _attributes.horizontal_alignment();
338 vertical_alignment = _attributes.vertical_alignment();
339 }
340 }
341};
342
343} // namespace v1
344}
vertical_alignment
Vertical alignment.
Definition alignment.hpp:25
horizontal_alignment
Horizontal alignment.
Definition alignment.hpp:102
The HikoGUI namespace.
Definition array_generic.hpp:21
The HikoGUI API version 1.
Definition array_generic.hpp:22
style_modify_mask
Definition style_modify_mask.hpp:16
@ alignment
A alignment was changed.
Definition style_modify_mask.hpp:43
@ color
A color value was modified.
Definition style_modify_mask.hpp:27
@ weight
A border-width or border-radius was modified.
Definition style_modify_mask.hpp:31
@ all
All values were modified (except the theme).
Definition style_modify_mask.hpp:23
@ pixel_density
The attributes that need to be modified when the pixel density changes.
Definition style_modify_mask.hpp:59
@ margin
A margin or padding value was modified.
Definition style_modify_mask.hpp:39
@ size
A size value was modified.
Definition style_modify_mask.hpp:35
style_pseudo_class
The different dynamic states of a widget from the point of view of styles.
Definition style_pseudo_class.hpp:18
This is a RGBA floating point color.
Definition color_intf.hpp:49
The 4 radii of the corners of a quad or rectangle.
Definition corner_radii.hpp:26
The left, bottom, right and top margins.
Definition margins.hpp:25
Definition style.hpp:22
style & operator=(std::string style_string)
Parse the given string to configure this style.
Definition style.hpp:148
callback_type subscribe(Func &&func, callback_flags flags=callback_flags::synchronous) noexcept
Add a callback to the style.
Definition style.hpp:246
void reload(bool path_has_changed=false) noexcept
Reload the style attributes from the current theme.
Definition style.hpp:206
Definition style_attributes.hpp:19
Definition style_path.hpp:33
Definition pixel_density.hpp:18
Exception thrown during parsing on an error.
Definition exception_intf.hpp:48
T forward(T... args)
T move(T... args)
T tie(T... args)