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 "../layout/layout.hpp"
12#include "../geometry/geometry.hpp"
13#include "../observer/observer.hpp"
14#include "../time/time.hpp"
15#include "../settings/settings.hpp"
16#include "../numeric/numeric.hpp"
17#include "../GUI/GUI.hpp"
18#include "../macros.hpp"
19#include <memory>
20#include <vector>
21#include <string>
22#include <ranges>
23
24hi_export_module(hikogui.widgets.widget);
25
26hi_export namespace hi { inline namespace v1 {
27
37class widget : public widget_intf {
38public:
42
45 observer<extent2> maximum = extent2::large();
46
50 {
51 }
52
53 virtual ~widget() {}
54 widget(const widget&) = delete;
55 widget& operator=(const widget&) = delete;
56 widget(widget&&) = delete;
57 widget& operator=(widget&&) = delete;
58
61 {
62 co_return;
63 }
64
72 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
73 {
74 return {};
75 }
76
83 [[nodiscard]] virtual hitbox hitbox_test_from_parent(point2 position) const noexcept
84 {
85 return hitbox_test(_layout.from_parent * position);
86 }
87
95 [[nodiscard]] virtual hitbox hitbox_test_from_parent(point2 position, hitbox sibling_hitbox) const noexcept
96 {
97 return std::max(sibling_hitbox, hitbox_test(_layout.from_parent * position));
98 }
99
103 [[nodiscard]] bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
104 {
105 hi_axiom(loop::main().on_thread());
106 return false;
107 }
108
110 {
111 _layout = {};
112 return {*minimum, *minimum, *maximum};
113 }
114
115 void set_layout(widget_layout const& context) noexcept override
116 {
117 _layout = context;
118 }
119
120 void draw(draw_context const& context) noexcept override {}
121
124 bool process_event(gui_event const& event) const noexcept override
125 {
126 if (parent != nullptr) {
127 return parent->process_event(event);
128 } else {
129 return true;
130 }
131 }
132
139
144 bool handle_event(gui_event const& event) noexcept override
145 {
146 hi_axiom(loop::main().on_thread());
147
148 switch (event.type()) {
149 case gui_event_type::keyboard_enter:
150 set_focus(true);
151 this->scroll_to_show();
152 return true;
153
154 case gui_event_type::keyboard_exit:
155 set_focus(false);
156 return true;
157
158 case gui_event_type::mouse_enter:
159 set_hover(true);
160 return true;
161
162 case gui_event_type::mouse_exit:
163 set_hover(false);
164 return true;
165
167 set_active(true);
168 // All widgets need the active value set.
169 return false;
170
172 set_active(false);
173 // All widgets need the active value unset.
174 return false;
175
176 case gui_event_type::gui_activate_stay:
177 process_event(gui_event_type::gui_activate);
178 if (accepts_keyboard_focus(keyboard_focus_group::menu)) {
179 // By going forward and backward we select the current parent,
180 // the widget that opened the menu-stack.
181 process_event(gui_event_type::gui_widget_next);
182 process_event(gui_event_type::gui_widget_prev);
183 }
184 return true;
185
186 case gui_event_type::gui_activate_next:
187 process_event(gui_event_type::gui_activate);
188 return process_event(gui_event_type::gui_widget_next);
189
190 case gui_event_type::gui_widget_next:
192 gui_event::window_set_keyboard_target(id, keyboard_focus_group::normal, keyboard_focus_direction::forward));
193 return true;
194
195 case gui_event_type::gui_widget_prev:
197 gui_event::window_set_keyboard_target(id, keyboard_focus_group::normal, keyboard_focus_direction::backward));
198 return true;
199
200 case gui_event_type::gui_menu_next:
201 if (mode() >= widget_mode::partial and accepts_keyboard_focus(keyboard_focus_group::menu)) {
203 gui_event::window_set_keyboard_target(id, keyboard_focus_group::menu, keyboard_focus_direction::forward));
204 return true;
205 }
206 break;
207
208 case gui_event_type::gui_menu_prev:
209 if (mode() >= widget_mode::partial and accepts_keyboard_focus(keyboard_focus_group::menu)) {
211 gui_event::window_set_keyboard_target(id, keyboard_focus_group::menu, keyboard_focus_direction::backward));
212 return true;
213 }
214 break;
215
216 case gui_event_type::gui_toolbar_next:
217 if (mode() >= widget_mode::partial and accepts_keyboard_focus(keyboard_focus_group::toolbar)) {
219 gui_event::window_set_keyboard_target(id, keyboard_focus_group::toolbar, keyboard_focus_direction::forward));
220 return true;
221 }
222 break;
223
224 case gui_event_type::gui_toolbar_prev:
225 if (mode() >= widget_mode::partial and accepts_keyboard_focus(keyboard_focus_group::toolbar)) {
227 gui_event::window_set_keyboard_target(id, keyboard_focus_group::toolbar, keyboard_focus_direction::backward));
228 return true;
229 }
230 break;
231
232 default:;
233 }
234
235 return false;
236 }
237
239 gui_event const& event,
240 std::vector<widget_id> const& reject_list = std::vector<widget_id>{}) noexcept override
241 {
242 hi_axiom(loop::main().on_thread());
243
244 auto handled = false;
245
246 for (auto& child : this->children(false)) {
247 handled |= child.handle_event_recursive(event, reject_list);
248 }
249
250 if (!std::ranges::any_of(reject_list, [&](auto const& x) {
251 return x == id;
252 })) {
254 }
255
256 return handled;
257 }
258
259 [[nodiscard]] virtual widget_id find_next_widget(
260 widget_id current_keyboard_widget,
261 keyboard_focus_group group,
262 keyboard_focus_direction direction) const noexcept override
263 {
264 hi_axiom(loop::main().on_thread());
265
266 auto found = false;
267
269 // If there was no current_keyboard_widget, then return this if it accepts focus.
270 return id;
271
272 } else if (current_keyboard_widget == id) {
273 found = true;
274 }
275
277 for (auto& child : children(false)) {
279 }
280
281 if (direction == keyboard_focus_direction::backward) {
283 }
284
285 for (auto *child : children_) {
286 hi_axiom_not_null(child);
287
288 if (found) {
289 // Find the first focus accepting widget.
290 if (auto tmp = child->find_next_widget({}, group, direction); tmp != 0) {
291 return tmp;
292 }
293
294 } else {
295 auto tmp = child->find_next_widget(current_keyboard_widget, group, direction);
297 // The current widget was found, but no next widget available in the child.
298 // Try the first widget that does accept keyboard focus.
299 found = true;
300
301 } else if (tmp != 0) {
302 // Return the next widget that was found in the child-widget.
303 return tmp;
304 }
305 }
306 }
307
308 if (found) {
309 // Either:
310 // 1. current_keyboard_widget was nullptr; this widget, nor its child widgets accept focus.
311 // 2. current_keyboard_wigget was this; none of the child widgets accept focus.
312 // 3. current_keyboard_widget is a child; none of the following widgets accept focus.
314 }
315
316 return {};
317 }
318
321 {
322 hi_axiom(loop::main().on_thread());
323
324 if (parent) {
325 parent->scroll_to_show(_layout.to_parent * rectangle);
326 }
327 }
328
329 void set_window(gui_window *window) noexcept override
330 {
331 if (parent) {
332 return parent->set_window(window);
333 } else {
334 return;
335 }
336 }
337
338 [[nodiscard]] gui_window *window() const noexcept override
339 {
340 if (parent) {
341 return parent->window();
342 } else {
343 return nullptr;
344 }
345 }
346
347 [[nodiscard]] hi::theme const& theme() const noexcept
348 {
349 auto const w = window();
350 hi_assert_not_null(w);
351 return w->theme;
352 }
353
354 [[nodiscard]] gfx_surface const *surface() const noexcept
355 {
356 if (auto w = window()) {
357 return w->surface.get();
358 } else {
359 return nullptr;
360 }
361 }
362
363 [[nodiscard]] virtual color background_color() const noexcept
364 {
365 if (mode() >= widget_mode::partial) {
366 if (phase() == widget_phase::hover) {
367 return theme().color(semantic_color::fill, _layout.layer + 1);
368 } else {
369 return theme().color(semantic_color::fill, _layout.layer);
370 }
371 } else {
372 return theme().color(semantic_color::fill, _layout.layer - 1);
373 }
374 }
375
376 [[nodiscard]] virtual color foreground_color() const noexcept
377 {
378 if (mode() >= widget_mode::partial) {
379 if (phase() == widget_phase::hover) {
380 return theme().color(semantic_color::border, _layout.layer + 1);
381 } else {
382 return theme().color(semantic_color::border, _layout.layer);
383 }
384 } else {
385 return theme().color(semantic_color::border, _layout.layer - 1);
386 }
387 }
388
389 [[nodiscard]] virtual color focus_color() const noexcept
390 {
391 if (mode() >= widget_mode::partial) {
392 if (focus()) {
393 return theme().color(semantic_color::accent);
394 } else if (phase() == widget_phase::hover) {
395 return theme().color(semantic_color::border, _layout.layer + 1);
396 } else {
397 return theme().color(semantic_color::border, _layout.layer);
398 }
399 } else {
400 return theme().color(semantic_color::border, _layout.layer - 1);
401 }
402 }
403
404 [[nodiscard]] virtual color accent_color() const noexcept
405 {
406 if (mode() >= widget_mode::partial) {
407 return theme().color(semantic_color::accent);
408 } else {
409 return theme().color(semantic_color::border, _layout.layer - 1);
410 }
411 }
412
413 [[nodiscard]] virtual color label_color() const noexcept
414 {
415 if (mode() >= widget_mode::partial) {
416 return theme().text_style(semantic_text_style::label)->color;
417 } else {
418 return theme().color(semantic_color::border, _layout.layer - 1);
419 }
420 }
421
422protected:
432 [[nodiscard]] aarectangle make_overlay_rectangle(aarectangle requested_rectangle) const noexcept
433 {
434 hi_axiom(loop::main().on_thread());
435
436 // Move the request_rectangle to window coordinates.
438 auto const window_bounds = aarectangle{layout().window_size} - theme().margin<float>();
441 }
442};
443
444}} // namespace hi::v1
@ end
Start from the end of the file.
@ begin
Start from the beginning of the file.
@ window_redraw
Request that part of the window gets redrawn on the next frame.
@ window_deactivate
The window is not longer the top-window.
@ window_activate
The window becomes the top-window.
@ partial
A widget is partially enabled.
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
The HikoGUI namespace.
Definition recursive_iterator.hpp:15
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:378
Class which represents an axis-aligned rectangle.
Definition aarectangle.hpp:33
A high-level geometric extent.
Definition extent2.hpp:32
A rectangle / parallelogram in 3D space.
Definition rectangle.hpp:25
Draw context for drawing using the HikoGUI shaders.
Definition draw_context_intf.hpp:209
A user interface event.
Definition gui_event.hpp:82
Definition widget_intf.hpp:24
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:30
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget_intf.hpp:206
void scroll_to_show() noexcept
Scroll to show the important part of the widget.
Definition widget_intf.hpp:312
widget_intf * parent
Pointer to the parent widget.
Definition widget_intf.hpp:35
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:56
constexpr aarectangle clipping_rectangle_on_window() const noexcept
Get the clipping rectangle in window coordinate system.
Definition widget_layout.hpp:198
translate2 from_window
This matrix transforms window coordinates to local coordinates.
Definition widget_layout.hpp:83
extent2 window_size
Size of the window.
Definition widget_layout.hpp:87
2D constraints.
Definition box_constraints.hpp:25
A observer pointing to the whole or part of a observed_base.
Definition observer_intf.hpp:32
An interactive graphical object as part of the user-interface.
Definition widget.hpp:37
bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
Check if the widget will accept keyboard focus.
Definition widget.hpp:103
observer< extent2 > minimum
The minimum size this widget is allowed to be.
Definition widget.hpp:41
virtual hitbox hitbox_test_from_parent(point2 position, hitbox sibling_hitbox) const noexcept
Call hitbox_test from a parent widget.
Definition widget.hpp:95
void scroll_to_show() noexcept
Scroll to show the important part of the widget.
Definition widget_intf.hpp:312
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:238
gui_window * window() const noexcept override
Get the window that the widget is owned by.
Definition widget.hpp:338
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:259
void scroll_to_show(hi::aarectangle rectangle) noexcept override
Scroll to show the given rectangle on the window.
Definition widget.hpp:320
void set_layout(widget_layout const &context) noexcept override
Update the internal layout of the widget.
Definition widget.hpp:115
generator< widget_intf & > children(bool include_invisible) noexcept override
Get a list of child widgets.
Definition widget.hpp:60
void draw(draw_context const &context) noexcept override
Draw the widget.
Definition widget.hpp:120
void request_redraw() const noexcept override
Request the widget to be redrawn on the next frame.
Definition widget.hpp:135
virtual hitbox hitbox_test_from_parent(point2 position) const noexcept
Call hitbox_test from a parent widget.
Definition widget.hpp:83
hitbox hitbox_test(point2 position) const noexcept override
Find the widget that is under the mouse cursor.
Definition widget.hpp:72
box_constraints update_constraints() noexcept override
Update the constraints of the widget.
Definition widget.hpp:109
void set_window(gui_window *window) noexcept override
Set the window for this tree of widgets.
Definition widget.hpp:329
widget(widget_intf const *parent) noexcept
Definition widget.hpp:49
bool process_event(gui_event const &event) const noexcept override
Send a event to the window.
Definition widget.hpp:124
bool handle_event(gui_event const &event) noexcept override
Handle command.
Definition widget.hpp:144
observer< extent2 > maximum
The maximum size this widget is allowed to be.
Definition widget.hpp:45
T addressof(T... args)
T max(T... args)
T push_back(T... args)
T reverse(T... args)