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
10
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
48 struct attributes_type {
52
56
60
63 observer<alignment> alignment = hi::alignment::top_left();
64
65 attributes_type(attributes_type const&) noexcept = default;
66 attributes_type(attributes_type&&) noexcept = default;
67 attributes_type& operator=(attributes_type const&) noexcept = default;
68 attributes_type& operator=(attributes_type&&) noexcept = default;
69
70 template<with_label_widget_attribute... Attributes>
71 explicit attributes_type(Attributes&&... attributes) noexcept
72 {
73 set_attributes<0>(std::forward<Attributes>(attributes)...);
74 }
75
76 template<size_t I>
77 void set_attributes() noexcept
78 {
79 }
80
81 template<size_t I, with_label_widget_attribute First, with_label_widget_attribute... Rest>
82 void set_attributes(First&& first, Rest&&... rest) noexcept
83 {
84 if constexpr (forward_of<decltype(first), observer<hi::label>>) {
85 if constexpr (I == 0) {
86 on_label = first;
87 off_label = first;
89 } else if constexpr (I == 1) {
93 } else if constexpr (I == 2) {
95 } else {
96 hi_static_no_default();
97 }
98 set_attributes<I + 1>(std::forward<Rest>(rest)...);
99
100 } else if constexpr (forward_of<decltype(first), observer<hi::alignment>>) {
102 set_attributes<I>(std::forward<Rest>(rest)...);
103
104 } else {
105 hi_static_no_default();
106 }
107 }
108 };
109
110 attributes_type attributes;
111
112 template<typename... Args>
113 [[nodiscard]] consteval static size_t num_default_delegate_arguments() noexcept
114 {
115 return button_widget_type::template num_default_delegate_arguments<Args...>();
116 }
117
118 template<size_t N, typename... Args>
119 [[nodiscard]] static auto make_default_delegate(Args&&... args)
120 {
121 return button_widget_type::template make_default_delegate<N, Args...>(std::forward<Args>(args)...);
122 }
123
124 hi_call_right_arguments(static, make_attributes, attributes_type);
125
126 with_label_widget(attributes_type attributes, std::shared_ptr<delegate_type> delegate) noexcept :
127 super(), attributes(std::move(attributes))
128 {
129 _button_widget =
130 std::make_unique<button_widget_type>(button_attributes_type{this->attributes.alignment}, std::move(delegate));
131 _button_widget->set_parent(this);
132
133 _on_label_widget = std::make_unique<label_widget>(this->attributes.on_label, this->attributes.alignment);
134 _on_label_widget->set_parent(this);
135
136 _off_label_widget = std::make_unique<label_widget>(this->attributes.off_label, this->attributes.alignment);
137 _off_label_widget->set_parent(this);
138
139 _other_label_widget = std::make_unique<label_widget>(this->attributes.other_label, this->attributes.alignment);
140 _other_label_widget->set_parent(this);
141
142 _button_widget_cbt = _button_widget->subscribe([&] {
143 set_value(_button_widget->value());
144
145 _on_label_widget->set_mode(value() == widget_value::on ? widget_mode::display : widget_mode::invisible);
146 _off_label_widget->set_mode(value() == widget_value::off ? widget_mode::display : widget_mode::invisible);
147 _other_label_widget->set_mode(value() == widget_value::other ? widget_mode::display : widget_mode::invisible);
148
149 this->request_redraw();
150 this->notifier();
151 });
152
153 _button_widget_cbt();
154 }
155
162 template<typename... Args>
163 with_label_widget(Args&&... args) requires(num_default_delegate_arguments<Args...>() != 0)
164 :
165 with_label_widget(
166 make_attributes<num_default_delegate_arguments<Args...>()>(std::forward<Args>(args)...),
167 make_default_delegate<num_default_delegate_arguments<Args...>()>(std::forward<Args>(args)...))
168 {
169 }
170
172 [[nodiscard]] box_constraints update_constraints() noexcept override
173 {
174 _layout = {};
175
176 // Resolve as if in left-to-right mode, the grid will flip itself.
177 auto const resolved_alignment = resolve(*attributes.alignment, true);
178
179 _grid.clear();
180 if (resolved_alignment == horizontal_alignment::left) {
181 // button label
182 _grid.add_cell(0, 0, grid_cell_type::button);
183 _grid.add_cell(1, 0, grid_cell_type::label, true);
184 } else if (resolved_alignment == horizontal_alignment::right) {
185 // label button
186 _grid.add_cell(0, 0, grid_cell_type::label, true);
187 _grid.add_cell(1, 0, grid_cell_type::button);
188 } else if (resolved_alignment == vertical_alignment::top) {
189 // button
190 // label
191 _grid.add_cell(0, 0, grid_cell_type::button);
192 _grid.add_cell(0, 1, grid_cell_type::label, true);
193 } else if (resolved_alignment == vertical_alignment::bottom) {
194 // label
195 // button
196 _grid.add_cell(0, 0, grid_cell_type::label, true);
197 _grid.add_cell(0, 1, grid_cell_type::button);
198 } else {
199 hi_no_default("alignment is not allowed to be middle-center.");
200 }
201
202 for (auto& cell : _grid) {
203 if (cell.value == grid_cell_type::button) {
204 cell.set_constraints(_button_widget->update_constraints());
205
206 } else if (cell.value == grid_cell_type::label) {
207 auto const on_label_constraints = _on_label_widget->update_constraints();
208 auto const off_label_constraints = _off_label_widget->update_constraints();
209 auto const other_label_constraints = _other_label_widget->update_constraints();
210 cell.set_constraints(max(on_label_constraints, off_label_constraints, other_label_constraints));
211
212 } else {
213 hi_no_default();
214 }
215 }
216
217 return _grid.constraints(os_settings::left_to_right());
218 }
219
220 void set_layout(widget_layout const& context) noexcept override
221 {
222 if (compare_store(_layout, context)) {
223 _grid.set_layout(context.shape, theme().baseline_adjustment());
224 }
225
226 for (auto const& cell : _grid) {
227 if (cell.value == grid_cell_type::button) {
228 _button_widget->set_layout(context.transform(cell.shape, transform_command::level));
229
230 } else if (cell.value == grid_cell_type::label) {
231 _on_label_widget->set_layout(context.transform(cell.shape));
232 _off_label_widget->set_layout(context.transform(cell.shape));
233 _other_label_widget->set_layout(context.transform(cell.shape));
234
235 } else {
236 hi_no_default();
237 }
238 }
239 }
240
241 void draw(draw_context const& context) noexcept override
242 {
243 if (mode() > widget_mode::invisible and overlaps(context, layout())) {
244 for (auto const& cell : _grid) {
245 if (cell.value == grid_cell_type::button) {
246 _button_widget->draw(context);
247
248 } else if (cell.value == grid_cell_type::label) {
249 _on_label_widget->draw(context);
250 _off_label_widget->draw(context);
251 _other_label_widget->draw(context);
252
253 } else {
254 hi_no_default();
255 }
256 }
257 }
258 }
259
260 [[nodiscard]] generator<widget_intf&> children(bool include_invisible) noexcept override
261 {
262 co_yield *_button_widget;
263 if (include_invisible or _on_label_widget->mode() > widget_mode::invisible) {
264 co_yield *_on_label_widget;
265 }
266 if (include_invisible or _off_label_widget->mode() > widget_mode::invisible) {
267 co_yield *_off_label_widget;
268 }
269 if (include_invisible or _other_label_widget->mode() > widget_mode::invisible) {
270 co_yield *_other_label_widget;
271 }
272 }
273
274 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
275 {
276 hi_axiom(loop::main().on_thread());
277
278 if (mode() >= widget_mode::partial and layout().contains(position)) {
279 // Accept the hitbox of the with_label_widget on behalf of the button_widget.
280 return {_button_widget->id, _layout.elevation, hitbox_type::button};
281 } else {
282 return {};
283 }
284 }
286protected:
287 enum class grid_cell_type { button, label };
288
289 grid_layout<grid_cell_type> _grid;
290
291 std::unique_ptr<button_widget_type> _button_widget;
292
293 std::unique_ptr<label_widget> _on_label_widget;
294 std::unique_ptr<label_widget> _off_label_widget;
295 std::unique_ptr<label_widget> _other_label_widget;
296
297 callback<void()> _button_widget_cbt;
298};
299
300} // namespace v1
301} // namespace hi::v1
Defines widget.
Defines label_widget.
Defines button_delegate and some default button delegates.
@ bottom
Align to the bottom.
Definition alignment.hpp:40
@ top
Align to the top.
Definition alignment.hpp:32
@ right
Align the text to the right side.
Definition alignment.hpp:136
@ left
Align the text to the left side.
Definition alignment.hpp:118
@ partial
A widget is partially enabled.
Definition widget_state.hpp:73
@ invisible
The widget is invisible.
Definition widget_state.hpp:41
@ display
The widget is in display-only mode.
Definition widget_state.hpp:55
STL namespace.
The HikoGUI namespace.
Definition array_generic.hpp:21
The HikoGUI API version 1.
Definition array_generic.hpp:22
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.
Definition widget_layout.hpp:26
notifier< void()> notifier
Notifier which is called after an action is completed by a widget.
Definition widget_intf.hpp:45
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget_intf.hpp:241
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
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:423
void request_redraw() const noexcept override
Request the widget to be redrawn on the next frame.
Definition widget.hpp:136
widget() noexcept
Constructor for creating sub views.
Definition widget.hpp:50
box_constraints update_constraints() noexcept override
Update the constraints of the widget.
Definition widget.hpp:110
with_label_widget(Args &&... args)
Construct a widget with a label.
Definition with_label_widget.hpp:163
Definition with_label_widget.hpp:48
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 forward(T... args)
T max(T... args)
T move(T... args)