HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
window_widget.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2020-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.hpp"
12#include "toolbar_widget.hpp"
14#include "grid_widget.hpp"
17#include "../l10n/l10n.hpp"
18#include "../macros.hpp"
19
20namespace hi { inline namespace v1 {
21
28class window_widget final : public widget {
29public:
30 using super = widget;
31
32 observer<label> title;
33
34 window_widget(forward_of<observer<label>> auto&& title) noexcept : super(nullptr), title(hi_forward(title))
35 {
36 _toolbar = std::make_unique<toolbar_widget>(this);
37
38#if HI_OPERATING_SYSTEM == HI_OS_WINDOWS
39 _system_menu = &_toolbar->make_widget<system_menu_widget>();
40 this->_system_menu->icon = this->title.get<"icon">();
42
43#elif HI_OPERATING_SYSTEM == HI_OS_MACOS
44 _toolbar->make_widget<window_controls_macos_widget>();
45
46#else
47#error "Not implemented"
48#endif
49
50 _content = std::make_unique<grid_widget>(this);
51 }
52
58 {
59 hi_axiom(loop::main().on_thread());
60 return theme().color(semantic_color::fill, semantic_layer);
61 }
62
68 {
69 hi_axiom(loop::main().on_thread());
70 hi_assert_not_null(_content);
71 return *_content;
72 }
73
80 {
81 hi_axiom(loop::main().on_thread());
82 hi_assert_not_null(_toolbar);
83 return *_toolbar;
84 }
85
87 [[nodiscard]] generator<widget_intf&> children(bool include_invisible) noexcept override
88 {
89 co_yield *_toolbar;
90 co_yield *_content;
91 }
92 [[nodiscard]] box_constraints update_constraints() noexcept override
93 {
94 hi_assert_not_null(_content);
95 hi_assert_not_null(_toolbar);
96
97 _layout = {};
98 _content_constraints = _content->update_constraints();
99 _toolbar_constraints = _toolbar->update_constraints();
100
101 auto r = box_constraints{};
102 r.minimum.width() = std::max(
103 _toolbar_constraints.margins.left() + _toolbar_constraints.minimum.width() + _toolbar_constraints.margins.right(),
104 _content_constraints.margins.left() + _content_constraints.minimum.width() + _content_constraints.margins.right());
105 r.preferred.width() = std::max(
106 _toolbar_constraints.margins.left() + _toolbar_constraints.preferred.width() + _toolbar_constraints.margins.right(),
107 _content_constraints.margins.left() + _content_constraints.preferred.width() + _content_constraints.margins.right());
108 r.maximum.width() = std::min(
109 _toolbar_constraints.margins.left() + _toolbar_constraints.maximum.width() + _toolbar_constraints.margins.right(),
110 _content_constraints.margins.left() + _content_constraints.maximum.width() + _content_constraints.margins.right());
111
112 // clang-format off
113 r.minimum.height() =
114 _toolbar_constraints.margins.top() +
115 _toolbar_constraints.preferred.height() +
116 std::max(_toolbar_constraints.margins.bottom(), _content_constraints.margins.top()) +
117 _content_constraints.minimum.height() +
118 _content_constraints.margins.bottom();
119 r.preferred.height() =
120 _toolbar_constraints.margins.top() +
121 _toolbar_constraints.preferred.height() +
122 std::max(_toolbar_constraints.margins.bottom(), _content_constraints.margins.top()) +
123 _content_constraints.preferred.height() +
124 _content_constraints.margins.bottom();
125 r.maximum.height() =
126 _toolbar_constraints.margins.top() +
127 _toolbar_constraints.preferred.height() +
128 std::max(_toolbar_constraints.margins.bottom(), _content_constraints.margins.top()) +
129 _content_constraints.maximum.height() +
130 _content_constraints.margins.bottom();
131 // clang-format on
132
133 // The operating system also has a minimum and maximum size, these sizes
134 // are more important than the calculated sizes.
135 inplace_max(r.minimum.width(), os_settings::minimum_window_width());
136 inplace_max(r.minimum.height(), os_settings::minimum_window_height());
137
138 inplace_clamp(r.maximum.width(), r.minimum.width(), os_settings::maximum_window_width());
139 inplace_clamp(r.maximum.height(), r.minimum.height(), os_settings::maximum_window_height());
140
141 inplace_clamp(r.preferred.width(), r.minimum.width(), r.maximum.width());
142 inplace_clamp(r.preferred.height(), r.minimum.height(), r.maximum.height());
143
144 _can_resize_width = r.minimum.width() != r.maximum.width();
145 _can_resize_height = r.minimum.height() != r.maximum.height();
146
147 return r;
148 }
149 void set_layout(widget_layout const& context) noexcept override
150 {
151 if (compare_store(_layout, context)) {
152 hilet toolbar_height = _toolbar_constraints.preferred.height();
153 hilet between_margin = std::max(_toolbar_constraints.margins.bottom(), _content_constraints.margins.top());
154
155 hilet toolbar_rectangle = aarectangle{
156 point2{
157 _toolbar_constraints.margins.left(), context.height() - toolbar_height - _toolbar_constraints.margins.top()},
158 point2{
159 context.width() - _toolbar_constraints.margins.right(),
160 context.height() - _toolbar_constraints.margins.top()}};
161 _toolbar_shape = box_shape{_toolbar_constraints, toolbar_rectangle, theme().baseline_adjustment()};
162
163 hilet content_rectangle = aarectangle{
164 point2{_content_constraints.margins.left(), _content_constraints.margins.bottom()},
165 point2{context.width() - _content_constraints.margins.right(), toolbar_rectangle.bottom() - between_margin}};
166 _content_shape = box_shape{_content_constraints, content_rectangle, theme().baseline_adjustment()};
167 }
168 _toolbar->set_layout(context.transform(_toolbar_shape));
169 _content->set_layout(context.transform(_content_shape));
170 }
171 void draw(draw_context const& context) noexcept override
172 {
174 context.draw_box(_layout, _layout.rectangle(), background_color(), background_color());
175
176 _toolbar->draw(context);
177 _content->draw(context);
178 }
179 }
180 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
181 {
182 constexpr float BORDER_WIDTH = 10.0f;
183
184 hi_axiom(loop::main().on_thread());
185
186 auto r = _toolbar->hitbox_test_from_parent(position);
187 r = _content->hitbox_test_from_parent(position, r);
188
189 hilet is_on_l_edge = position.x() <= BORDER_WIDTH;
190 hilet is_on_r_edge = position.x() >= (layout().width() - BORDER_WIDTH);
191 hilet is_on_b_edge = position.y() <= BORDER_WIDTH;
192 hilet is_on_t_edge = position.y() >= (layout().height() - BORDER_WIDTH);
193
194 // Corner resize has always priority.
196 if (_can_resize_width and _can_resize_height) {
197 return {id, _layout.elevation, hitbox_type::bottom_left_resize_corner};
198 } else if (_can_resize_width) {
199 return {id, _layout.elevation, hitbox_type::left_resize_border};
200 } else if (_can_resize_height) {
201 return {id, _layout.elevation, hitbox_type::bottom_resize_border};
202 }
203 } else if (is_on_r_edge and is_on_b_edge) {
204 if (_can_resize_width and _can_resize_height) {
205 return {id, _layout.elevation, hitbox_type::bottom_right_resize_corner};
206 } else if (_can_resize_width) {
207 return {id, _layout.elevation, hitbox_type::right_resize_border};
208 } else if (_can_resize_height) {
209 return {id, _layout.elevation, hitbox_type::bottom_resize_border};
210 }
211 } else if (is_on_l_edge and is_on_t_edge) {
212 if (_can_resize_width and _can_resize_height) {
213 return {id, _layout.elevation, hitbox_type::top_left_resize_corner};
214 } else if (_can_resize_width) {
215 return {id, _layout.elevation, hitbox_type::left_resize_border};
216 } else if (_can_resize_height) {
217 return {id, _layout.elevation, hitbox_type::top_resize_border};
218 }
219 } else if (is_on_r_edge and is_on_t_edge) {
220 if (_can_resize_width and _can_resize_height) {
221 return {id, _layout.elevation, hitbox_type::top_right_resize_corner};
222 } else if (_can_resize_width) {
223 return {id, _layout.elevation, hitbox_type::right_resize_border};
224 } else if (_can_resize_height) {
225 return {id, _layout.elevation, hitbox_type::top_resize_border};
226 }
227 }
228
229 // Border resize only has priority if there is no scroll-bar in the way.
230 if (r.type != hitbox_type::scroll_bar) {
231 if (is_on_l_edge and _can_resize_width) {
232 return {id, _layout.elevation, hitbox_type::left_resize_border};
233 } else if (is_on_r_edge and _can_resize_width) {
234 return {id, _layout.elevation, hitbox_type::right_resize_border};
235 } else if (is_on_b_edge and _can_resize_height) {
236 return {id, _layout.elevation, hitbox_type::bottom_resize_border};
237 } else if (is_on_t_edge and _can_resize_height) {
238 return {id, _layout.elevation, hitbox_type::top_resize_border};
239 }
240 }
241
242 return r;
243 }
244 bool handle_event(gui_event const& event) noexcept override
245 {
246 using enum gui_event_type;
247
248 switch (event.type()) {
249 case gui_toolbar_open:
250 process_event(
251 gui_event::window_set_keyboard_target(id, keyboard_focus_group::toolbar, keyboard_focus_direction::forward));
252 return true;
253 }
255 }
256 bool process_event(gui_event const& event) const noexcept override
257 {
258 if (_window) {
259 return _window->process_event(event);
260 } else {
261 // Since there is no window, pretend that the message was handled.
262 return true;
263 }
264 }
265 void set_window(gui_window *window) noexcept override
266 {
267 _window = window;
268 if (_window) {
269 _window->set_title(*title);
270 }
271 }
272 [[nodiscard]] gui_window *window() const noexcept override
273 {
274 return _window;
275 }
277private:
278 gui_window *_window = nullptr;
279
281 box_constraints _content_constraints;
282 box_shape _content_shape;
283
285 box_constraints _toolbar_constraints;
286 box_shape _toolbar_shape;
287
288 mutable bool _can_resize_width;
289 mutable bool _can_resize_height;
290
291#if HI_OPERATING_SYSTEM == HI_OS_WINDOWS
292 system_menu_widget *_system_menu = nullptr;
293#endif
294};
295
296}} // namespace hi::v1
Defines widget.
Defines system_menu_widget.
Defines window_controls_macos_widget.
Defines window_controls_win32_widget.
Defines toolbar_widget.
Defines grid_widget.
@ right
Align the text to the right side.
gui_event_type
GUI event type.
Definition gui_event_type.hpp:24
@ invisible
The widget is invisible.
DOXYGEN BUG.
Definition algorithm.hpp:16
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition misc.hpp:56
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
This is a RGBA floating point color.
Definition color.hpp:45
constexpr float & width() noexcept
Access the x-as-width element from the extent.
Definition extent2.hpp:104
constexpr float & height() noexcept
Access the y-as-height element from the extent.
Definition extent2.hpp:115
widget_id id
The numeric identifier of a widget.
Definition widget_intf.hpp:23
2D constraints.
Definition box_constraints.hpp:25
A GUI widget that lays out child-widgets in a grid with variable sized cells.
Definition grid_widget.hpp:40
The system menu widget.
Definition system_menu_widget.hpp:27
A toolbar widget is located at the top of a window and lays out its children horizontally.
Definition toolbar_widget.hpp:35
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
int semantic_layer
The draw layer of the widget.
Definition widget.hpp:66
widget(widget *parent) noexcept
Definition widget.hpp:87
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:42
bool handle_event(gui_event const &event) noexcept override
Handle command.
Definition widget.hpp:198
Window control button widget.
Definition window_controls_macos_widget.hpp:27
Window control button widget.
Definition window_controls_win32_widget.hpp:27
The top-level window widget.
Definition window_widget.hpp:28
grid_widget & content() noexcept
Get a reference to the window's content widget.
Definition window_widget.hpp:67
toolbar_widget & toolbar() noexcept
Get a reference to window's toolbar widget.
Definition window_widget.hpp:79
color background_color() noexcept
The background color of the window.
Definition window_widget.hpp:57
True if T is a forwarded type of Forward.
Definition concepts.hpp:131
T max(T... args)
T min(T... args)