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
8
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#include <coroutine>
20
21hi_export_module(hikogui.widgets.window_widget);
22
23hi_export namespace hi {
24inline namespace v1 {
25
32class window_widget : public widget {
33public:
34 using super = widget;
35
36 observer<label> title;
37
38 template<forward_of<observer<label>> Title>
39 window_widget(Title&& title) noexcept : super(), title(std::forward<Title>(title))
40 {
41 _toolbar = std::make_unique<toolbar_widget>();
42 _toolbar->set_parent(this);
43
44#if HI_OPERATING_SYSTEM == HI_OS_WINDOWS
45 _system_menu = &_toolbar->emplace<system_menu_widget>();
46 this->_system_menu->icon = this->title.sub<"icon">();
48
49#elif HI_OPERATING_SYSTEM == HI_OS_MACOS
50 _toolbar->emplace<window_controls_macos_widget>();
51
52#else
53#error "Not implemented"
54#endif
55
56 _content = std::make_unique<grid_widget>();
57 _content->set_parent(this);
58 }
59
64 [[nodiscard]] color background_color() noexcept
65 {
66 hi_axiom(loop::main().on_thread());
67 return theme().fill_color(_layout.layer);
68 }
69
74 [[nodiscard]] grid_widget& content() noexcept
75 {
76 hi_axiom(loop::main().on_thread());
77 hi_assert_not_null(_content);
78 return *_content;
79 }
80
86 [[nodiscard]] toolbar_widget& toolbar() noexcept
87 {
88 hi_axiom(loop::main().on_thread());
89 hi_assert_not_null(_toolbar);
90 return *_toolbar;
91 }
92
94 [[nodiscard]] generator<widget_intf&> children(bool include_invisible) noexcept override
95 {
96 if (_toolbar) {
97 co_yield *_toolbar;
98 }
99 if (_content) {
100 co_yield *_content;
101 }
102 }
103 [[nodiscard]] box_constraints update_constraints() noexcept override
104 {
105 hi_assert_not_null(_content);
106 hi_assert_not_null(_toolbar);
107
108 _layout = {};
109 _content_constraints = _content->update_constraints();
110 _toolbar_constraints = _toolbar->update_constraints();
111
112 auto r = box_constraints{};
113 r.minimum.width() = std::max(
114 _toolbar_constraints.margins.left() + _toolbar_constraints.minimum.width() + _toolbar_constraints.margins.right(),
115 _content_constraints.margins.left() + _content_constraints.minimum.width() + _content_constraints.margins.right());
116 r.preferred.width() = std::max(
117 _toolbar_constraints.margins.left() + _toolbar_constraints.preferred.width() + _toolbar_constraints.margins.right(),
118 _content_constraints.margins.left() + _content_constraints.preferred.width() + _content_constraints.margins.right());
119 r.maximum.width() = std::min(
120 _toolbar_constraints.margins.left() + _toolbar_constraints.maximum.width() + _toolbar_constraints.margins.right(),
121 _content_constraints.margins.left() + _content_constraints.maximum.width() + _content_constraints.margins.right());
122
123 // clang-format off
124 r.minimum.height() =
125 _toolbar_constraints.margins.top() +
126 _toolbar_constraints.preferred.height() +
127 std::max(_toolbar_constraints.margins.bottom(), _content_constraints.margins.top()) +
128 _content_constraints.minimum.height() +
129 _content_constraints.margins.bottom();
130 r.preferred.height() =
131 _toolbar_constraints.margins.top() +
132 _toolbar_constraints.preferred.height() +
133 std::max(_toolbar_constraints.margins.bottom(), _content_constraints.margins.top()) +
134 _content_constraints.preferred.height() +
135 _content_constraints.margins.bottom();
136 r.maximum.height() =
137 _toolbar_constraints.margins.top() +
138 _toolbar_constraints.preferred.height() +
139 std::max(_toolbar_constraints.margins.bottom(), _content_constraints.margins.top()) +
140 _content_constraints.maximum.height() +
141 _content_constraints.margins.bottom();
142 // clang-format on
143
144 // The operating system also has a minimum and maximum size, these sizes
145 // are more important than the calculated sizes.
146 inplace_max(r.minimum.width(), os_settings::minimum_window_width());
147 inplace_max(r.minimum.height(), os_settings::minimum_window_height());
148
149 inplace_clamp(r.maximum.width(), r.minimum.width(), os_settings::maximum_window_width());
150 inplace_clamp(r.maximum.height(), r.minimum.height(), os_settings::maximum_window_height());
151
152 inplace_clamp(r.preferred.width(), r.minimum.width(), r.maximum.width());
153 inplace_clamp(r.preferred.height(), r.minimum.height(), r.maximum.height());
154
155 _can_resize_width = r.minimum.width() != r.maximum.width();
156 _can_resize_height = r.minimum.height() != r.maximum.height();
157
158 return r;
159 }
160
161 void set_layout(widget_layout const& context) noexcept override
162 {
163 if (compare_store(_layout, context)) {
164 auto const toolbar_height = _toolbar_constraints.preferred.height();
165 auto const between_margin = std::max(_toolbar_constraints.margins.bottom(), _content_constraints.margins.top());
166
167 auto const toolbar_rectangle = aarectangle{
168 point2{
169 _toolbar_constraints.margins.left(), context.height() - toolbar_height - _toolbar_constraints.margins.top()},
170 point2{
171 context.width() - _toolbar_constraints.margins.right(),
172 context.height() - _toolbar_constraints.margins.top()}};
173 _toolbar_shape = box_shape{_toolbar_constraints, toolbar_rectangle, theme().baseline_adjustment()};
174
175 auto const content_rectangle = aarectangle{
176 point2{_content_constraints.margins.left(), _content_constraints.margins.bottom()},
177 point2{context.width() - _content_constraints.margins.right(), toolbar_rectangle.bottom() - between_margin}};
178 _content_shape = box_shape{_content_constraints, content_rectangle, theme().baseline_adjustment()};
179 }
180 _toolbar->set_layout(context.transform(_toolbar_shape));
181 _content->set_layout(context.transform(_content_shape));
182 }
183 void draw(draw_context const& context) noexcept override
184 {
185 if (mode() > widget_mode::invisible) {
186 context.draw_box(_layout, _layout.rectangle(), background_color(), background_color());
187
188 _toolbar->draw(context);
189 _content->draw(context);
190 }
191 }
192 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
193 {
194 constexpr float BORDER_WIDTH = 10.0f;
195
196 hi_axiom(loop::main().on_thread());
197
198 auto r = _toolbar->hitbox_test_from_parent(position);
199 r = _content->hitbox_test_from_parent(position, r);
200
201 auto const is_on_l_edge = position.x() <= BORDER_WIDTH;
202 auto const is_on_r_edge = position.x() >= (layout().width() - BORDER_WIDTH);
203 auto const is_on_b_edge = position.y() <= BORDER_WIDTH;
204 auto const is_on_t_edge = position.y() >= (layout().height() - BORDER_WIDTH);
205
206 // Corner resize has always priority.
207 if (is_on_l_edge and is_on_b_edge) {
208 if (_can_resize_width and _can_resize_height) {
209 return {id, _layout.elevation, hitbox_type::bottom_left_resize_corner};
210 } else if (_can_resize_width) {
211 return {id, _layout.elevation, hitbox_type::left_resize_border};
212 } else if (_can_resize_height) {
213 return {id, _layout.elevation, hitbox_type::bottom_resize_border};
214 }
215 } else if (is_on_r_edge and is_on_b_edge) {
216 if (_can_resize_width and _can_resize_height) {
217 return {id, _layout.elevation, hitbox_type::bottom_right_resize_corner};
218 } else if (_can_resize_width) {
219 return {id, _layout.elevation, hitbox_type::right_resize_border};
220 } else if (_can_resize_height) {
221 return {id, _layout.elevation, hitbox_type::bottom_resize_border};
222 }
223 } else if (is_on_l_edge and is_on_t_edge) {
224 if (_can_resize_width and _can_resize_height) {
225 return {id, _layout.elevation, hitbox_type::top_left_resize_corner};
226 } else if (_can_resize_width) {
227 return {id, _layout.elevation, hitbox_type::left_resize_border};
228 } else if (_can_resize_height) {
229 return {id, _layout.elevation, hitbox_type::top_resize_border};
230 }
231 } else if (is_on_r_edge and is_on_t_edge) {
232 if (_can_resize_width and _can_resize_height) {
233 return {id, _layout.elevation, hitbox_type::top_right_resize_corner};
234 } else if (_can_resize_width) {
235 return {id, _layout.elevation, hitbox_type::right_resize_border};
236 } else if (_can_resize_height) {
237 return {id, _layout.elevation, hitbox_type::top_resize_border};
238 }
239 }
240
241 // Border resize only has priority if there is no scroll-bar in the way.
242 if (r.type != hitbox_type::scroll_bar) {
243 if (is_on_l_edge and _can_resize_width) {
244 return {id, _layout.elevation, hitbox_type::left_resize_border};
245 } else if (is_on_r_edge and _can_resize_width) {
246 return {id, _layout.elevation, hitbox_type::right_resize_border};
247 } else if (is_on_b_edge and _can_resize_height) {
248 return {id, _layout.elevation, hitbox_type::bottom_resize_border};
249 } else if (is_on_t_edge and _can_resize_height) {
250 return {id, _layout.elevation, hitbox_type::top_resize_border};
251 }
252 }
253
254 return r;
255 }
256 bool handle_event(gui_event const& event) noexcept override
257 {
258 using enum gui_event_type;
259
260 switch (event.type()) {
261 case gui_toolbar_open:
262 process_event(
263 gui_event::window_set_keyboard_target(id, keyboard_focus_group::toolbar, keyboard_focus_direction::forward));
264 return true;
265 default:;
266 }
267 return super::handle_event(event);
268 }
269 bool process_event(gui_event const& event) const noexcept override
270 {
271 if (window) {
272 return window->process_event(event);
273 } else {
274 // Since there is no window, pretend that the message was handled.
275 return true;
276 }
277 }
279private:
280 std::unique_ptr<grid_widget> _content;
281 box_constraints _content_constraints;
282 box_shape _content_shape;
283
284 std::unique_ptr<toolbar_widget> _toolbar;
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 v1
297} // namespace hi::v1
Defines window_controls_win32_widget.
Defines widget.
Defines window_controls_macos_widget.
Defines system_menu_widget.
Defines toolbar_widget.
Defines grid_widget.
@ right
Align the text to the right side.
Definition alignment.hpp:136
gui_event_type
GUI event type.
Definition gui_event_type.hpp:24
@ invisible
The widget is invisible.
Definition widget_state.hpp:41
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
This is a RGBA floating point color.
Definition color_intf.hpp:49
widget_id id
The numeric identifier of a widget.
Definition widget_intf.hpp:31
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget_intf.hpp:241
A observer pointing to the whole or part of a observed_base.
Definition observer_intf.hpp:32
auto sub(auto const &index) const noexcept
Create a sub-observer by indexing into the value.
Definition observer_intf.hpp:473
A GUI widget that lays out child-widgets in a grid with variable sized cells.
Definition grid_widget.hpp:43
The system menu widget.
Definition system_menu_widget.hpp:30
A toolbar widget is located at the top of a window and lays out its children horizontally.
Definition toolbar_widget.hpp:38
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
bool handle_event(gui_event const &event) noexcept override
Handle command.
Definition widget.hpp:145
Window control button widget.
Definition window_controls_macos_widget.hpp:29
Window control button widget.
Definition window_controls_win32_widget.hpp:29
grid_widget & content() noexcept
Get a reference to the window's content widget.
Definition window_widget.hpp:74
toolbar_widget & toolbar() noexcept
Get a reference to window's toolbar widget.
Definition window_widget.hpp:86
color background_color() noexcept
The background color of the window.
Definition window_widget.hpp:64
T forward(T... args)
T max(T... args)
T min(T... args)