HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
widget.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2019-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 "widget_mode.hpp"
12#include "../layout/module.hpp"
13#include "../geometry/module.hpp"
14#include "../observer/module.hpp"
15#include "../time/module.hpp"
16#include "../settings/settings.hpp"
17#include "../numeric/module.hpp"
18#include "../GUI/GUI.hpp"
19#include "../coroutine/module.hpp"
20#include "../macros.hpp"
21#include <memory>
22#include <vector>
23#include <string>
24#include <ranges>
25
26namespace hi { inline namespace v1 {
27
37class widget : public widget_intf {
38public:
43
47
51
67
76
80
83 observer<extent2> maximum = extent2::large();
84
88 {
89 hi_axiom(loop::main().on_thread());
90
91 if (parent) {
92 logical_layer = parent->logical_layer + 1;
93 semantic_layer = parent->semantic_layer + 1;
94 }
95
96 _mode_cbt = mode.subscribe([&](auto...) {
97 ++global_counter<"widget:mode:constrain">;
99 });
100 }
101
102 virtual ~widget() {}
103 widget(const widget&) = delete;
104 widget& operator=(const widget&) = delete;
105 widget(widget&&) = delete;
106 widget& operator=(widget&&) = delete;
107
110 {
111 co_return;
112 }
113
121 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
122 {
123 return {};
124 }
125
132 [[nodiscard]] virtual hitbox hitbox_test_from_parent(point2 position) const noexcept
133 {
134 return hitbox_test(_layout.from_parent * position);
135 }
136
144 [[nodiscard]] virtual hitbox hitbox_test_from_parent(point2 position, hitbox sibling_hitbox) const noexcept
145 {
146 return std::max(sibling_hitbox, hitbox_test(_layout.from_parent * position));
147 }
148
152 [[nodiscard]] bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
153 {
154 hi_axiom(loop::main().on_thread());
155 return false;
156 }
157
159 {
160 _layout = {};
161 return {*minimum, *minimum, *maximum};
162 }
163
164 void set_layout(widget_layout const& context) noexcept override
165 {
166 _layout = context;
167 }
168
170 {
171 return _layout;
172 }
173
174 void draw(draw_context const& context) noexcept override {}
175
178 bool process_event(gui_event const& event) const noexcept override
179 {
180 if (parent != nullptr) {
181 return parent->process_event(event);
182 } else {
183 return true;
184 }
185 }
186
193
198 bool handle_event(gui_event const& event) noexcept override
199 {
200 hi_axiom(loop::main().on_thread());
201
202 switch (event.type()) {
204 case keyboard_enter:
205 focus = true;
206 this->scroll_to_show();
207 ++global_counter<"widget:keyboard_enter:redraw">;
209 return true;
210
211 case keyboard_exit:
212 focus = false;
213 ++global_counter<"widget:keyboard_exit:redraw">;
215 return true;
216
217 case mouse_enter:
218 hover = true;
219 ++global_counter<"widget:mouse_enter:redraw">;
221 return true;
222
223 case mouse_exit:
224 hover = false;
225 ++global_counter<"widget:mouse_exit:redraw">;
227 return true;
228
229 case gui_widget_next:
231 gui_event::window_set_keyboard_target(id, keyboard_focus_group::normal, keyboard_focus_direction::forward));
232 return true;
233
234 case gui_widget_prev:
236 gui_event::window_set_keyboard_target(id, keyboard_focus_group::normal, keyboard_focus_direction::backward));
237 return true;
238
239 case gui_activate_next:
240 process_event(gui_activate);
241 return process_event(gui_widget_next);
242
243 case gui_event_type::gui_toolbar_next:
244 if (*mode >= widget_mode::partial and accepts_keyboard_focus(keyboard_focus_group::toolbar) and
245 not is_last(keyboard_focus_group::toolbar)) {
247 gui_event::window_set_keyboard_target(id, keyboard_focus_group::toolbar, keyboard_focus_direction::forward));
248 return true;
249 }
250 break;
251
252 case gui_event_type::gui_toolbar_prev:
253 if (*mode >= widget_mode::partial and accepts_keyboard_focus(keyboard_focus_group::toolbar) and
254 not is_first(keyboard_focus_group::toolbar)) {
256 gui_event::window_set_keyboard_target(id, keyboard_focus_group::toolbar, keyboard_focus_direction::backward));
257 return true;
258 }
259 break;
260
261 default:;
262 }
263
264 return false;
265 }
266
268 gui_event const& event,
269 std::vector<widget_id> const& reject_list = std::vector<widget_id>{}) noexcept override
270 {
271 hi_axiom(loop::main().on_thread());
272
273 auto handled = false;
274
275 for (auto& child : this->children(false)) {
276 handled |= child.handle_event_recursive(event, reject_list);
277 }
278
279 if (!std::ranges::any_of(reject_list, [&](hilet& x) {
280 return x == id;
281 })) {
283 }
284
285 return handled;
286 }
287
288 [[nodiscard]] virtual widget_id find_next_widget(
289 widget_id current_keyboard_widget,
290 keyboard_focus_group group,
291 keyboard_focus_direction direction) const noexcept override
292 {
293 hi_axiom(loop::main().on_thread());
294
295 auto found = false;
296
298 // If there was no current_keyboard_widget, then return this if it accepts focus.
299 return id;
300
301 } else if (current_keyboard_widget == id) {
302 found = true;
303 }
304
306 for (auto& child : children(false)) {
308 }
309
310 if (direction == keyboard_focus_direction::backward) {
312 }
313
314 for (auto *child : children_) {
315 hi_axiom_not_null(child);
316
317 if (found) {
318 // Find the first focus accepting widget.
319 if (auto tmp = child->find_next_widget({}, group, direction)) {
320 return tmp;
321 }
322
323 } else {
324 auto tmp = child->find_next_widget(current_keyboard_widget, group, direction);
326 // The current widget was found, but no next widget available in the child.
327 // Try the first widget that does accept keyboard focus.
328 found = true;
329
330 } else if (tmp != nullptr) {
331 // Return the next widget that was found in the child-widget.
332 return tmp;
333 }
334 }
335 }
336
337 if (found) {
338 // Either:
339 // 1. current_keyboard_widget was nullptr; this widget, nor its child widgets accept focus.
340 // 2. current_keyboard_wigget was this; none of the child widgets accept focus.
341 // 3. current_keyboard_widget is a child; none of the following widgets accept focus.
343 }
344
345 return std::nullopt;
346 }
347
348 [[nodiscard]] widget_id find_first_widget(keyboard_focus_group group) const noexcept override
349 {
350 hi_axiom(loop::main().on_thread());
351
352 for (auto& child : children(false)) {
353 if (child.accepts_keyboard_focus(group)) {
354 return child.id;
355 }
356 }
357 return std::nullopt;
358 }
359
360 [[nodiscard]] widget_id find_last_widget(keyboard_focus_group group) const noexcept override
361 {
362 hi_axiom(loop::main().on_thread());
363
364 auto found = widget_id{};
365 for (auto& child : children(false)) {
366 if (child.accepts_keyboard_focus(group)) {
367 found = child.id;
368 }
369 }
370
371 return found;
372 }
373
376 [[nodiscard]] bool is_first(keyboard_focus_group group) const noexcept
377 {
378 hi_axiom(loop::main().on_thread());
379 return parent->find_first_widget(group) == id;
380 }
381
384 [[nodiscard]] bool is_last(keyboard_focus_group group) const noexcept
385 {
386 hi_axiom(loop::main().on_thread());
387 return parent->find_last_widget(group) == id;
388 }
389
392 {
393 hi_axiom(loop::main().on_thread());
394
395 if (parent) {
397 }
398 }
399
400 void set_window(gui_window *window) noexcept override
401 {
402 if (parent) {
403 return parent->set_window(window);
404 } else {
405 return;
406 }
407 }
408
409 [[nodiscard]] gui_window *window() const noexcept override
410 {
411 if (parent) {
412 return parent->window();
413 } else {
414 return nullptr;
415 }
416 }
417
418 [[nodiscard]] hi::theme const &theme() const noexcept
419 {
420 hilet w = window();
421 hi_assert_not_null(w);
422 return w->theme;
423 }
424
425 [[nodiscard]] gfx_surface const *surface() const noexcept
426 {
427 if (auto w = window()) {
428 return w->surface.get();
429 } else {
430 return nullptr;
431 }
432 }
433
434 [[nodiscard]] virtual color background_color() const noexcept
435 {
436 if (*mode >= widget_mode::partial) {
437 if (*hover) {
438 return theme().color(semantic_color::fill, semantic_layer + 1);
439 } else {
440 return theme().color(semantic_color::fill, semantic_layer);
441 }
442 } else {
443 return theme().color(semantic_color::fill, semantic_layer - 1);
444 }
445 }
446
447 [[nodiscard]] virtual color foreground_color() const noexcept
448 {
449 if (*mode >= widget_mode::partial) {
450 if (*hover) {
451 return theme().color(semantic_color::border, semantic_layer + 1);
452 } else {
453 return theme().color(semantic_color::border, semantic_layer);
454 }
455 } else {
456 return theme().color(semantic_color::border, semantic_layer - 1);
457 }
458 }
459
460 [[nodiscard]] virtual color focus_color() const noexcept
461 {
462 if (*mode >= widget_mode::partial) {
463 if (*focus) {
464 return theme().color(semantic_color::accent);
465 } else if (*hover) {
466 return theme().color(semantic_color::border, semantic_layer + 1);
467 } else {
468 return theme().color(semantic_color::border, semantic_layer);
469 }
470 } else {
471 return theme().color(semantic_color::border, semantic_layer - 1);
472 }
473 }
474
475 [[nodiscard]] virtual color accent_color() const noexcept
476 {
477 if (*mode >= widget_mode::partial) {
478 return theme().color(semantic_color::accent);
479 } else {
480 return theme().color(semantic_color::border, semantic_layer - 1);
481 }
482 }
483
484 [[nodiscard]] virtual color label_color() const noexcept
485 {
486 if (*mode >= widget_mode::partial) {
487 return theme().text_style(semantic_text_style::label)->color;
488 } else {
489 return theme().color(semantic_color::border, semantic_layer - 1);
490 }
491 }
492
493protected:
494 widget_layout _layout;
495
496 decltype(mode)::callback_token _mode_cbt;
497
507 [[nodiscard]] aarectangle make_overlay_rectangle(aarectangle requested_rectangle) const noexcept
508 {
509 hi_axiom(loop::main().on_thread());
510
511 // Move the request_rectangle to window coordinates.
513 hilet window_bounds = aarectangle{layout().window_size} - theme().margin<float>();
516 }
517};
518
519}} // namespace hi::v1
Defines widget_mode.
@ end
Start from the end of the file.
@ begin
Start from the beginning of the file.
gui_event_type
GUI event type.
Definition gui_event_type.hpp:24
@ window_redraw
Request that part of the window gets redrawn on the next frame.
@ window_reconstrain
Request that widget get constraint on the next frame.
@ partial
A widget is partially enabled.
@ enabled
The widget is fully enabled.
DOXYGEN BUG.
Definition algorithm.hpp:16
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Class which represents an axis-aligned rectangle.
Definition aarectangle.hpp:29
A high-level geometric extent.
Definition extent2.hpp:29
A rectangle / parallelogram in 3D space.
Definition rectangle.hpp:21
Draw context for drawing using the HikoGUI shaders.
Definition draw_context.hpp:208
A user interface event.
Definition gui_event.hpp:75
Definition widget_intf.hpp:17
virtual gui_window * window() const noexcept=0
Get the window that the widget is owned by.
widget_id id
The numeric identifier of a widget.
Definition widget_intf.hpp:23
void scroll_to_show() noexcept
Scroll to show the important part of the widget.
Definition widget_intf.hpp:196
widget_intf * parent
Pointer to the parent widget.
Definition widget_intf.hpp:28
virtual bool process_event(gui_event const &event) const noexcept=0
Send a event to the window.
virtual void scroll_to_show(hi::aarectangle rectangle) noexcept=0
Scroll to show the given rectangle on the window.
virtual generator< widget_intf & > children(bool include_invisible) noexcept=0
Get a list of child widgets.
virtual void set_window(gui_window *window) noexcept=0
Set the window for this tree of widgets.
The layout of a widget.
Definition widget_layout.hpp:38
translate2 to_parent
This matrix transforms local coordinates to the coordinates of the parent widget.
Definition widget_layout.hpp:53
constexpr aarectangle clipping_rectangle_on_window() const noexcept
Get the clipping rectangle in window coordinate system.
Definition widget_layout.hpp:168
translate2 from_parent
This matrix transforms parent widget's coordinates to local coordinates.
Definition widget_layout.hpp:57
translate2 from_window
This matrix transforms window coordinates to local coordinates.
Definition widget_layout.hpp:65
extent2 window_size
Size of the window.
Definition widget_layout.hpp:69
2D constraints.
Definition box_constraints.hpp:25
An interactive graphical object as part of the user-interface.
Definition widget.hpp:37
widget_layout const & layout() const noexcept override
Get the current layout for this widget.
Definition widget.hpp:169
bool is_first(keyboard_focus_group group) const noexcept
Is this widget the first widget in the parent container.
Definition widget.hpp:376
int logical_layer
The logical layer of the widget.
Definition widget.hpp:75
bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
Check if the widget will accept keyboard focus.
Definition widget.hpp:152
int semantic_layer
The draw layer of the widget.
Definition widget.hpp:66
observer< extent2 > minimum
The minimum size this widget is allowed to be.
Definition widget.hpp:79
observer< bool > hover
Mouse cursor is hovering over the widget.
Definition widget.hpp:46
virtual hitbox hitbox_test_from_parent(point2 position, hitbox sibling_hitbox) const noexcept
Call hitbox_test from a parent widget.
Definition widget.hpp:144
void scroll_to_show() noexcept
Scroll to show the important part of the widget.
Definition widget_intf.hpp:196
bool handle_event_recursive(gui_event const &event, std::vector< widget_id > const &reject_list=std::vector< widget_id >{}) noexcept override
Handle command recursive.
Definition widget.hpp:267
gui_window * window() const noexcept override
Get the window that the widget is owned by.
Definition widget.hpp:409
virtual widget_id find_next_widget(widget_id current_keyboard_widget, keyboard_focus_group group, keyboard_focus_direction direction) const noexcept override
Find the next widget that handles keyboard focus.
Definition widget.hpp:288
void scroll_to_show(hi::aarectangle rectangle) noexcept override
Scroll to show the given rectangle on the window.
Definition widget.hpp:391
void set_layout(widget_layout const &context) noexcept override
Update the internal layout of the widget.
Definition widget.hpp:164
widget(widget *parent) noexcept
Definition widget.hpp:87
generator< widget_intf & > children(bool include_invisible) noexcept override
Get a list of child widgets.
Definition widget.hpp:109
void draw(draw_context const &context) noexcept override
Draw the widget.
Definition widget.hpp:174
void request_redraw() const noexcept override
Request the widget to be redrawn on the next frame.
Definition widget.hpp:189
virtual hitbox hitbox_test_from_parent(point2 position) const noexcept
Call hitbox_test from a parent widget.
Definition widget.hpp:132
hitbox hitbox_test(point2 position) const noexcept override
Find the widget that is under the mouse cursor.
Definition widget.hpp:121
box_constraints update_constraints() noexcept override
Update the constraints of the widget.
Definition widget.hpp:158
void set_window(gui_window *window) noexcept override
Set the window for this tree of widgets.
Definition widget.hpp:400
bool is_last(keyboard_focus_group group) const noexcept
Is this widget the last widget in the parent container.
Definition widget.hpp:384
bool process_event(gui_event const &event) const noexcept override
Send a event to the window.
Definition widget.hpp:178
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:42
observer< bool > focus
The widget has keyboard focus.
Definition widget.hpp:50
bool handle_event(gui_event const &event) noexcept override
Handle command.
Definition widget.hpp:198
observer< extent2 > maximum
The maximum size this widget is allowed to be.
Definition widget.hpp:83
T addressof(T... args)
T max(T... args)
T push_back(T... args)
T reverse(T... args)