HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
with_label_widget.hpp
Go to the documentation of this file.
1
2
3// Copyright Take Vos 2021-2022.
4// Distributed under the Boost Software License, Version 1.0.
5// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
6
11#pragma once
12
13#include "widget.hpp"
14#include "button_delegate.hpp"
15#include "label_widget.hpp"
16#include "../utility/utility.hpp"
17#include "../algorithm/algorithm.hpp"
18#include "../l10n/l10n.hpp"
19#include "../observer/observer.hpp"
20#include "../macros.hpp"
21#include <memory>
22#include <string>
23#include <array>
24#include <optional>
25#include <future>
26#include <coroutine>
27
28hi_export_module(hikogui.widgets.with_label_widget);
29
30hi_export namespace hi {
31inline namespace v1 {
32
33template<typename Context>
35
40template<std::derived_from<widget> ButtonWidget>
41class with_label_widget : public widget {
42public:
43 using super = widget;
44 using button_widget_type = ButtonWidget;
45 using button_attributes_type = button_widget_type::attributes_type;
46 using delegate_type = button_widget_type::delegate_type;
47
52
56
60
63 observer<alignment> alignment = hi::alignment::top_left();
64
67 observer<semantic_text_style> text_style = semantic_text_style::label;
68
69 attributes_type(attributes_type const&) noexcept = default;
70 attributes_type(attributes_type&&) noexcept = default;
71 attributes_type& operator=(attributes_type const&) noexcept = default;
72 attributes_type& operator=(attributes_type&&) noexcept = default;
73
74 template<with_label_widget_attribute... Attributes>
75 explicit attributes_type(Attributes&&...attributes) noexcept
76 {
77 set_attributes<0>(std::forward<Attributes>(attributes)...);
78 }
79
80 template<size_t I>
81 void set_attributes() noexcept
82 {
83 }
84
85 template<size_t I, with_label_widget_attribute First, with_label_widget_attribute... Rest>
86 void set_attributes(First&& first, Rest&&...rest) noexcept
87 {
88 if constexpr (forward_of<decltype(first), observer<hi::label>>) {
89 if constexpr (I == 0) {
90 on_label = first;
91 off_label = first;
92 other_label = std::forward<First>(first);
93 } else if constexpr (I == 1) {
96 off_label = std::forward<First>(first);
97 } else if constexpr (I == 2) {
98 other_label = std::forward<First>(first);
99 } else {
100 hi_static_no_default();
101 }
102 set_attributes<I + 1>(std::forward<Rest>(rest)...);
103
104 } else if constexpr (forward_of<decltype(first), observer<hi::alignment>>) {
105 alignment = std::forward<First>(first);
106 set_attributes<I>(std::forward<Rest>(rest)...);
107
108 } else if constexpr (forward_of<decltype(first), observer<hi::semantic_text_style>>) {
109 text_style = std::forward<First>(first);
110 set_attributes<I>(std::forward<Rest>(rest)...);
111
112 } else {
113 hi_static_no_default();
114 }
115 }
116 };
117
118 attributes_type attributes;
119
120 template<typename... Args>
121 [[nodiscard]] consteval static size_t num_default_delegate_arguments() noexcept
122 {
123 return button_widget_type::template num_default_delegate_arguments<Args...>();
124 }
125
126 template<size_t N, typename... Args>
127 [[nodiscard]] static auto make_default_delegate(Args&&...args)
128 {
129 return button_widget_type::template make_default_delegate<N, Args...>(std::forward<Args>(args)...);
130 }
131
132 hi_call_right_arguments(static, make_attributes, attributes_type);
133
134 with_label_widget(
135 widget_intf const* parent,
136 attributes_type attributes,
137 std::shared_ptr<delegate_type> delegate) noexcept :
138 super(parent), attributes(std::move(attributes))
139 {
140 _button_widget =
141 std::make_unique<button_widget_type>(this, button_attributes_type{this->attributes.alignment}, std::move(delegate));
142 _on_label_widget = std::make_unique<label_widget>(
143 this, this->attributes.on_label, this->attributes.alignment, this->attributes.text_style);
144 _off_label_widget = std::make_unique<label_widget>(
145 this, this->attributes.off_label, this->attributes.alignment, this->attributes.text_style);
146 _other_label_widget = std::make_unique<label_widget>(
147 this, this->attributes.other_label, this->attributes.alignment, this->attributes.text_style);
148
149 _button_widget_cbt = _button_widget->subscribe([&] {
150 set_value(_button_widget->value());
151
152 _on_label_widget->set_mode(value() == widget_value::on ? widget_mode::display : widget_mode::invisible);
153 _off_label_widget->set_mode(value() == widget_value::off ? widget_mode::display : widget_mode::invisible);
154 _other_label_widget->set_mode(value() == widget_value::other ? widget_mode::display : widget_mode::invisible);
155
156 this->request_redraw();
157 this->notifier();
158 });
159
160 _button_widget_cbt();
161 }
162
169 template<typename... Args>
170 with_label_widget(widget_intf const* parent, Args&&...args)
171 requires(num_default_delegate_arguments<Args...>() != 0)
172 :
174 parent,
175 make_attributes<num_default_delegate_arguments<Args...>()>(std::forward<Args>(args)...),
176 make_default_delegate<num_default_delegate_arguments<Args...>()>(std::forward<Args>(args)...))
177 {
178 }
179
181 [[nodiscard]] box_constraints update_constraints() noexcept override
182 {
183 _layout = {};
184
185 // Resolve as if in left-to-right mode, the grid will flip itself.
186 auto const resolved_alignment = resolve(*attributes.alignment, true);
187
188 _grid.clear();
189 if (resolved_alignment == horizontal_alignment::left) {
190 // button label
191 _grid.add_cell(0, 0, grid_cell_type::button);
192 _grid.add_cell(1, 0, grid_cell_type::label, true);
193 } else if (resolved_alignment == horizontal_alignment::right) {
194 // label button
195 _grid.add_cell(0, 0, grid_cell_type::label, true);
196 _grid.add_cell(1, 0, grid_cell_type::button);
197 } else if (resolved_alignment == vertical_alignment::top) {
198 // button
199 // label
200 _grid.add_cell(0, 0, grid_cell_type::button);
201 _grid.add_cell(0, 1, grid_cell_type::label, true);
202 } else if (resolved_alignment == vertical_alignment::bottom) {
203 // label
204 // button
205 _grid.add_cell(0, 0, grid_cell_type::label, true);
206 _grid.add_cell(0, 1, grid_cell_type::button);
207 } else {
208 hi_no_default("alignment is not allowed to be middle-center.");
209 }
210
211 for (auto& cell : _grid) {
212 if (cell.value == grid_cell_type::button) {
213 cell.set_constraints(_button_widget->update_constraints());
214
215 } else if (cell.value == grid_cell_type::label) {
216 auto const on_label_constraints = _on_label_widget->update_constraints();
217 auto const off_label_constraints = _off_label_widget->update_constraints();
218 auto const other_label_constraints = _other_label_widget->update_constraints();
219 cell.set_constraints(max(on_label_constraints, off_label_constraints, other_label_constraints));
220
221 } else {
222 hi_no_default();
223 }
224 }
225
226 return _grid.constraints(os_settings::left_to_right());
227 }
228
229 void set_layout(widget_layout const& context) noexcept override
230 {
231 if (compare_store(_layout, context)) {
232 _grid.set_layout(context.shape, theme().baseline_adjustment());
233 }
234
235 for (auto const& cell : _grid) {
236 if (cell.value == grid_cell_type::button) {
237 _button_widget->set_layout(context.transform(cell.shape, transform_command::level));
238
239 } else if (cell.value == grid_cell_type::label) {
240 _on_label_widget->set_layout(context.transform(cell.shape));
241 _off_label_widget->set_layout(context.transform(cell.shape));
242 _other_label_widget->set_layout(context.transform(cell.shape));
243
244 } else {
245 hi_no_default();
246 }
247 }
248 }
249
250 void draw(draw_context const& context) noexcept override
251 {
252 if (mode() > widget_mode::invisible and overlaps(context, layout())) {
253 for (auto const& cell : _grid) {
254 if (cell.value == grid_cell_type::button) {
255 _button_widget->draw(context);
256
257 } else if (cell.value == grid_cell_type::label) {
258 _on_label_widget->draw(context);
259 _off_label_widget->draw(context);
260 _other_label_widget->draw(context);
261
262 } else {
263 hi_no_default();
264 }
265 }
266 }
267 }
268
269 [[nodiscard]] generator<widget_intf&> children(bool include_invisible) noexcept override
270 {
271 co_yield *_button_widget;
272 if (include_invisible or _on_label_widget->mode() > widget_mode::invisible) {
273 co_yield *_on_label_widget;
274 }
275 if (include_invisible or _off_label_widget->mode() > widget_mode::invisible) {
276 co_yield *_off_label_widget;
277 }
278 if (include_invisible or _other_label_widget->mode() > widget_mode::invisible) {
279 co_yield *_other_label_widget;
280 }
281 }
282
283 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
284 {
285 hi_axiom(loop::main().on_thread());
286
287 if (mode() >= widget_mode::partial and layout().contains(position)) {
288 // Accept the hitbox of the with_label_widget on behalf of the button_widget.
289 return {_button_widget->id, _layout.elevation, hitbox_type::button};
290 } else {
291 return {};
292 }
293 }
295protected:
296 enum class grid_cell_type { button, label };
297
298 grid_layout<grid_cell_type> _grid;
299
301
302 std::unique_ptr<label_widget> _on_label_widget;
303 std::unique_ptr<label_widget> _off_label_widget;
304 std::unique_ptr<label_widget> _other_label_widget;
305
306 callback<void()> _button_widget_cbt;
307};
308
309} // namespace v1
310} // namespace hi::v1
Defines widget.
Defines label_widget.
Defines button_delegate and some default button delegates.
@ bottom
Align to the bottom.
@ top
Align to the top.
@ right
Align the text to the right side.
@ left
Align the text to the left side.
widget_mode
The mode that the widget is operating at.
Definition widget_state.hpp:25
@ partial
A widget is partially enabled.
@ invisible
The widget is invisible.
@ display
The widget is in display-only mode.
The HikoGUI namespace.
Definition array_generic.hpp:20
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition misc.hpp:53
@ level
The child widget stays at the same elevation and layer.
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Horizontal/Vertical alignment combination.
Definition alignment.hpp:244
Definition widget_intf.hpp:24
notifier< void()> notifier
Notifier which is called after an action is completed by a widget.
Definition widget_intf.hpp:39
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget_intf.hpp:206
widget_intf * parent
Pointer to the parent widget.
Definition widget_intf.hpp:35
A localizable message.
Definition txt.hpp:100
2D constraints.
Definition box_constraints.hpp:25
constexpr reference add_cell(size_t first_column, size_t first_row, size_t last_column, size_t last_row, Value &&value, bool beyond_maximum=false) noexcept
Check if the cell on the grid is already in use.
Definition grid_layout.hpp:1009
constexpr void set_layout(box_shape const &shape, float baseline_adjustment) noexcept
Layout the cells based on the width and height.
Definition grid_layout.hpp:1081
A observer pointing to the whole or part of a observed_base.
Definition observer_intf.hpp:32
void reset() noexcept
Reset the observer.
Definition observer_intf.hpp:421
An interactive graphical object as part of the user-interface.
Definition widget.hpp:37
void request_redraw() const noexcept override
Request the widget to be redrawn on the next frame.
Definition widget.hpp:141
widget() noexcept
Constructor for creating sub views.
Definition widget.hpp:55
Add labels to a button.
Definition with_label_widget.hpp:41
with_label_widget(widget_intf const *parent, Args &&...args)
Construct a widget with a label.
Definition with_label_widget.hpp:170
Definition with_label_widget.hpp:48
observer< semantic_text_style > text_style
The text style to button's label.
Definition with_label_widget.hpp:67
observer< label > off_label
The label to show when the button is in the 'off' state.
Definition with_label_widget.hpp:55
observer< alignment > alignment
The alignment of the button and on/off/other label.
Definition with_label_widget.hpp:63
observer< label > on_label
The label to show when the button is in the 'on' state.
Definition with_label_widget.hpp:51
observer< label > other_label
The label to show when the button is in the 'other' state.
Definition with_label_widget.hpp:59
True if T is a forwarded type of Forward.
Definition concepts.hpp:137
Definition label_widget.hpp:30
Definition with_label_widget.hpp:34
T move(T... args)