HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
checkbox_widget.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2021-2022.
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
9#pragma once
10
11#include "checkbox_delegate.hpp"
12#include "../log.hpp"
13
14namespace hi { inline namespace v1 {
15
16template<typename Context>
18
45template<fixed_string Name = "">
46class checkbox_widget final : public widget {
47public:
48 using super = widget;
49 using delegate_type = checkbox_delegate;
50 constexpr static auto prefix = Name / "checkbox";
51
55
58 observer<label> on_label = tr("on");
59
62 observer<label> off_label = tr("off");
63
66 observer<label> other_label = tr("other");
67
70 observer<hi::alignment> alignment = alignment::top_left();
71
84 checkbox_widget_attribute auto&&...attributes) noexcept :
86 {
87 hi_assert_not_null(this->delegate);
88 this->set_attributes<0>(hi_forward(attributes)...);
89
90 _on_label_widget = std::make_unique<label_widget<prefix / "on">>(this, on_label, alignment);
91 _off_label_widget = std::make_unique<label_widget<prefix / "off">>(this, off_label, alignment);
92 _other_label_widget = std::make_unique<label_widget<prefix / "other">>(this, other_label, alignment);
93
94 _grid.add_cell(0, 0, cell_type::button);
95 _grid.add_cell(1, 0, cell_type::label);
96
97 _delegate_cbt = this->delegate->subscribe([&] {
98 ++global_counter<"checkbox_widget:delegate:redraw">;
99 hi_assert_not_null(this->delegate);
100 state = this->delegate->state(this);
101
102 _on_label_widget->mode = *state == widget_state::on ? widget_mode::display : widget_mode::invisible;
103 _off_label_widget->mode = *state == widget_state::off ? widget_mode::display : widget_mode::invisible;
104 _other_label_widget->mode = *state == widget_state::other ? widget_mode::display : widget_mode::invisible;
105
106 process_event({gui_event_type::window_redraw});
107 });
108 this->delegate->init(*this);
109 (*_delegate_cbt)();
110 }
111
123 widget *parent,
124 different_from<std::shared_ptr<delegate_type>> auto&& value,
125 checkbox_widget_attribute auto&&...attributes) noexcept
126 requires requires { make_default_checkbox_delegate(hi_forward(value)); }
128 {
129 }
130
142 template<
143 different_from<std::shared_ptr<delegate_type>> Value,
144 forward_of<observer<observer_decay_t<Value>>> OnValue,
145 checkbox_widget_attribute... Attributes>
146 checkbox_widget(widget *parent, Value&& value, OnValue&& on_value, Attributes&&...attributes) noexcept
147 requires requires { make_default_checkbox_delegate(hi_forward(value), hi_forward(on_value)); }
148 :
150 parent,
152 hi_forward(attributes)...)
153 {
154 }
155
168 template<
169 different_from<std::shared_ptr<delegate_type>> Value,
170 forward_of<observer<observer_decay_t<Value>>> OnValue,
171 forward_of<observer<observer_decay_t<Value>>> OffValue,
172 checkbox_widget_attribute... Attributes>
173 checkbox_widget(widget *parent, Value&& value, OnValue&& on_value, OffValue&& off_value, Attributes&&...attributes) noexcept
174 requires requires { make_default_checkbox_delegate(hi_forward(value), hi_forward(on_value), hi_forward(off_value)); }
175 :
177 parent,
178 make_default_checkbox_delegate(hi_forward(value), hi_forward(on_value), hi_forward(off_value)),
179 hi_forward(attributes)...)
180 {
181 }
182
184 [[nodiscard]] box_constraints update_constraints() noexcept override
185 {
186 _check_glyph = find_glyph(elusive_icon::Ok);
187 _minus_glyph = find_glyph(elusive_icon::Minus);
188
189 for (auto& cell : _grid) {
190 if (cell.value == cell_type::button) {
191 cell.set_constraints(box_constraints{
192 theme<prefix>.size(this),
193 theme<prefix>.size(this),
194 theme<prefix>.size(this),
195 *alignment,
196 theme<prefix>.margin(this),
197 -vector2::infinity()});
198
199 } else if (cell.value == cell_type::label) {
200 cell.set_constraints(
201 max(_on_label_widget->update_constraints(),
202 _off_label_widget->update_constraints(),
203 _other_label_widget->update_constraints()));
204
205 } else {
207 }
208 }
209
210 return _grid.constraints(os_settings::left_to_right());
211 }
212
213 void set_layout(widget_layout const& context) noexcept override
214 {
215 if (compare_store(layout, context)) {
216 _grid.set_layout(context.shape, theme<prefix>.cap_height(this));
217 }
218
219 for (hilet& cell : _grid) {
220 if (cell.value == cell_type::button) {
221 _button_rectangle = align(cell.shape.rectangle, theme<prefix>.size(this), *alignment);
222
223 hilet mark_size = std::min(theme<prefix / "mark">.width(this), theme<prefix / "mark">.height(this));
224 hilet check_glyph_bb = _check_glyph.get_bounding_rectangle() * mark_size;
225 hilet minus_glyph_bb = _minus_glyph.get_bounding_rectangle() * mark_size;
226
227 _check_glyph_rectangle = align(_button_rectangle, check_glyph_bb, alignment::middle_center());
228 _minus_glyph_rectangle = align(_button_rectangle, minus_glyph_bb, alignment::middle_center());
229
230 } else if (cell.value == cell_type::label) {
231 _on_label_widget->set_layout(context.transform(cell.shape, 0.0f));
232 _off_label_widget->set_layout(context.transform(cell.shape, 0.0f));
233 _other_label_widget->set_layout(context.transform(cell.shape, 0.0f));
234
235 } else {
237 }
238 }
239 }
240
241 void draw(widget_draw_context& context) noexcept override
242 {
243 if (*mode > widget_mode::invisible and overlaps(context, layout)) {
244 for (hilet& cell : _grid) {
245 if (cell.value == cell_type::button) {
246 draw_button(context);
247 draw_mark(context);
248
249 } else if (cell.value == cell_type::label) {
250 _on_label_widget->draw(context);
251 _off_label_widget->draw(context);
252 _other_label_widget->draw(context);
253
254 } else {
256 }
257 }
258 }
259 }
260
261 [[nodiscard]] generator<widget const&> children(bool include_invisible) const noexcept override
262 {
263 co_yield *_on_label_widget;
264 co_yield *_off_label_widget;
265 co_yield *_other_label_widget;
266 }
267
268 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept final
269 {
270 hi_axiom(loop::main().on_thread());
271
272 if (*mode >= widget_mode::partial and layout.contains(position)) {
273 return {id, layout.elevation, hitbox_type::button};
274 } else {
275 return {};
276 }
277 }
278
279 [[nodiscard]] bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
280 {
281 hi_axiom(loop::main().on_thread());
282 return *mode >= widget_mode::partial and to_bool(group & hi::keyboard_focus_group::normal);
283 }
284
285 void activate() noexcept
286 {
288 delegate->activate(*this);
289 this->_state_changed();
290 }
291
292 bool handle_event(gui_event const& event) noexcept override
293 {
294 hi_axiom(loop::main().on_thread());
295
296 switch (event.type()) {
297 case gui_event_type::gui_activate:
298 if (*mode >= widget_mode::partial) {
299 activate();
300 return true;
301 }
302 break;
303
304 case gui_event_type::mouse_down:
305 if (*mode >= widget_mode::partial and event.mouse().cause.left_button) {
306 clicked = true;
308 return true;
309 }
310 break;
311
312 case gui_event_type::mouse_up:
313 if (*mode >= widget_mode::partial and event.mouse().cause.left_button) {
314 clicked = false;
315
316 if (layout.rectangle().contains(event.mouse().position)) {
317 handle_event(gui_event_type::gui_activate);
318 }
320 return true;
321 }
322 break;
323
324 default:;
325 }
326
327 return super::handle_event(event);
328 }
330private:
331 enum class cell_type { button, label };
332
333 grid_layout<cell_type> _grid;
334
335 std::unique_ptr<label_widget<prefix / "on">> _on_label_widget;
336 std::unique_ptr<label_widget<prefix / "off">> _off_label_widget;
337 std::unique_ptr<label_widget<prefix / "other">> _other_label_widget;
338
339 notifier<>::callback_token _delegate_cbt;
340
341 aarectangle _button_rectangle;
342
343 font_book::font_glyph_type _check_glyph;
344 aarectangle _check_glyph_rectangle;
345 font_book::font_glyph_type _minus_glyph;
346 aarectangle _minus_glyph_rectangle;
347
348 void draw_button(widget_draw_context& context) noexcept
349 {
350 context.draw_box(
351 this->layout,
352 _button_rectangle,
353 theme<prefix>.background_color(this),
354 theme<prefix>.border_color(this),
355 theme<prefix>.border_width(this),
357 theme<prefix>.border_radius(this));
358 }
359
360 void draw_mark(widget_draw_context& context) noexcept
361 {
362 if (this->state == widget_state::on) {
363 // Checkmark
364 context.draw_glyph(
365 this->layout,
366 translate_z(0.1f) * _check_glyph_rectangle,
367 *_check_glyph.font,
368 _check_glyph.glyph,
369 theme<prefix>.fill_color(this));
370
371 } else if (this->state == widget_state::off) {
372 ;
373
374 } else {
375 // Tri-state
376 context.draw_glyph(
377 this->layout,
378 translate_z(0.1f) * _minus_glyph_rectangle,
379 *_minus_glyph.font,
380 _minus_glyph.glyph,
381 theme<prefix>.fill_color(this));
382 }
383 }
384
385 template<size_t LabelCount>
386 void set_attributes() noexcept
387 {
388 }
389
390 template<size_t LabelCount>
391 void set_attributes(checkbox_widget_attribute auto&& first, checkbox_widget_attribute auto&&...rest) noexcept
392 {
393 if constexpr (forward_of<decltype(first), observer<hi::label>>) {
394 if constexpr (LabelCount == 0) {
395 on_label = first;
396 off_label = first;
397 other_label = hi_forward(first);
398 } else if constexpr (LabelCount == 1) {
399 other_label.reset();
400 off_label.reset();
401 off_label = hi_forward(first);
402 } else if constexpr (LabelCount == 2) {
403 other_label = hi_forward(first);
404 } else {
406 }
407 set_attributes<LabelCount + 1>(hi_forward(rest)...);
408
409 } else if constexpr (forward_of<decltype(first), observer<hi::alignment>>) {
410 alignment = hi_forward(first);
411 set_attributes<LabelCount>(hi_forward(rest)...);
412
413 } else {
415 }
416 }
417};
418
419}} // namespace hi::v1
Defines checkbox_delegate and some default checkbox_delegate delegates.
#define hi_static_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:323
#define hi_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:279
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:238
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition utility.hpp:29
@ window_redraw
Request that part of the window gets redrawn on the next frame.
std::shared_ptr< checkbox_delegate > make_default_checkbox_delegate(auto &&value, auto &&...args) noexcept
Make a shared pointer to a toggle-button delegate.
Definition checkbox_delegate.hpp:146
@ partial
A widget is partially enabled.
@ invisible
The widget is invisible.
@ display
The widget is in display-only mode.
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
@ outside
The border is drawn outside the edge of a quad.
@ off
The widget in the off-state.
@ other
The widget is in the other-state.
@ on
The widget is in the on-state.
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition utility.hpp:212
constexpr extent< value_type, 2 > size() const noexcept
Get size of the rectangle.
Definition axis_aligned_rectangle.hpp:183
Definition widget.hpp:26
widget_id id
The numeric identifier of a widget.
Definition widget.hpp:35
virtual void request_redraw() const noexcept
Request the widget to be redrawn on the next frame.
Definition widget.hpp:227
observer< bool > clicked
The widget is being clicked by the mouse.
Definition widget.hpp:57
observer< widget_state > state
The state of the widget.
Definition widget.hpp:65
virtual bool handle_event(gui_event const &event) noexcept
Handle command.
Definition widget.hpp:236
widget * parent
Pointer to the parent widget.
Definition widget.hpp:40
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:49
2D constraints.
Definition box_constraints.hpp:22
A toggle delegate controls the state of a toggle widget.
Definition checkbox_delegate.hpp:18
A GUI widget that permits the user to make a binary choice.
Definition checkbox_widget.hpp:46
observer< label > on_label
The label to show when the button is in the 'on' state.
Definition checkbox_widget.hpp:58
observer< label > other_label
The label to show when the button is in the 'other' state.
Definition checkbox_widget.hpp:66
observer< hi::alignment > alignment
The alignment of the button and on/off/other label.
Definition checkbox_widget.hpp:70
checkbox_widget(widget *parent, Value &&value, OnValue &&on_value, OffValue &&off_value, Attributes &&...attributes) noexcept
Construct a checkbox widget with a default button delegate.
Definition checkbox_widget.hpp:173
checkbox_widget(widget *parent, different_from< std::shared_ptr< delegate_type > > auto &&value, checkbox_widget_attribute auto &&...attributes) noexcept
Construct a checkbox widget with a default button delegate.
Definition checkbox_widget.hpp:122
std::shared_ptr< delegate_type > delegate
The delegate that controls the button widget.
Definition checkbox_widget.hpp:54
checkbox_widget(widget *parent, std::shared_ptr< delegate_type > delegate, checkbox_widget_attribute auto &&...attributes) noexcept
Construct a checkbox widget.
Definition checkbox_widget.hpp:81
checkbox_widget(widget *parent, Value &&value, OnValue &&on_value, Attributes &&...attributes) noexcept
Construct a checkbox widget with a default button delegate.
Definition checkbox_widget.hpp:146
observer< label > off_label
The label to show when the button is in the 'off' state.
Definition checkbox_widget.hpp:62
The GUI widget displays and lays out text together with an icon.
Definition label_widget.hpp:42
Definition checkbox_widget.hpp:17
Definition label_widget.hpp:26
T align(T... args)
T max(T... args)
T min(T... args)
T move(T... args)