HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
menu_button_widget.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2023.
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 "button_delegate.hpp"
13#include "label_widget.hpp"
14#include "../algorithm/algorithm.hpp"
15#include "../l10n/l10n.hpp"
16#include "../observer/observer.hpp"
17#include "../macros.hpp"
18#include <memory>
19#include <string>
20#include <array>
21#include <optional>
22#include <future>
23#include <coroutine>
24
25hi_export_module(hikogui.widgets.menu_button_widget);
26
27hi_export namespace hi {
28inline namespace v1 {
29
30template<typename Context>
32
37template<std::derived_from<widget> ButtonWidget>
38class menu_button_widget : public widget {
39public:
40 using super = widget;
41 using button_widget_type = ButtonWidget;
42 using button_attributes_type = button_widget_type::attributes_type;
43 using delegate_type = button_widget_type::delegate_type;
44
45 struct attributes_type {
49
53
56 observer<alignment> alignment = hi::alignment::middle_left();
57
58 attributes_type(attributes_type const&) noexcept = default;
59 attributes_type(attributes_type&&) noexcept = default;
60 attributes_type& operator=(attributes_type const&) noexcept = default;
61 attributes_type& operator=(attributes_type&&) noexcept = default;
62
63 template<menu_button_widget_attribute... Attributes>
64 explicit attributes_type(Attributes&&... attributes) noexcept
65 {
66 set_attributes<0>(std::forward<Attributes>(attributes)...);
67 }
68
69 template<size_t I>
70 void set_attributes() noexcept
71 {
72 }
73
74 template<size_t I, menu_button_widget_attribute First, menu_button_widget_attribute... Rest>
75 void set_attributes(First&& first, Rest&&... rest) noexcept
76 {
78 if constexpr (I == 0) {
80 } else if constexpr (I == 1) {
82 } else {
83 hi_static_no_default();
84 }
85 set_attributes<I + 1>(std::forward<Rest>(rest)...);
86
87 } else if constexpr (forward_of<First, observer<hi::alignment>>) {
89 set_attributes<I>(std::forward<Rest>(rest)...);
90
91 } else {
92 hi_static_no_default();
93 }
94 }
95 };
96
97 attributes_type attributes;
98
99 template<typename... Args>
100 [[nodiscard]] consteval static size_t num_default_delegate_arguments() noexcept
101 {
102 return button_widget_type::template num_default_delegate_arguments<Args...>();
103 }
104
105 template<size_t N, typename... Args>
106 [[nodiscard]] static auto make_default_delegate(Args&&... args)
107 {
108 return button_widget_type::template make_default_delegate<N, Args...>(std::forward<Args>(args)...);
109 }
110
111 hi_call_right_arguments(static, make_attributes, attributes_type);
112
113 menu_button_widget(attributes_type attributes, std::shared_ptr<delegate_type> delegate) noexcept :
114 super(), attributes(std::move(attributes))
115 {
116 _button_widget = std::make_unique<button_widget_type>(
117 button_attributes_type{this->attributes.alignment, keyboard_focus_group::menu}, std::move(delegate));
118 _button_widget->set_parent(this);
119
120 _label_widget = std::make_unique<label_widget>(this->attributes.label, this->attributes.alignment);
121 _label_widget->set_parent(this);
122
123 _shortcut_widget = std::make_unique<label_widget>(this->attributes.shortcut, this->attributes.alignment);
124 _shortcut_widget->set_parent(this);
125
126 // Link the state from the button, so that both this widget and the child widget react in the same way.
127 _button_widget->state = state;
128
129 _button_widget_cbt = _button_widget->subscribe([&] {
130 this->request_redraw();
131 this->notifier();
132 });
133
134 _button_widget_cbt();
135 }
136
143 template<typename... Args>
144 menu_button_widget(Args&&... args) requires(num_default_delegate_arguments<Args...>() != 0)
145 :
146 menu_button_widget(
147 parent,
148 make_attributes<num_default_delegate_arguments<Args...>()>(std::forward<Args>(args)...),
149 make_default_delegate<num_default_delegate_arguments<Args...>()>(std::forward<Args>(args)...))
150 {
151 }
152
154 [[nodiscard]] box_constraints update_constraints() noexcept override
155 {
156 _layout = {};
157
158 _grid.clear();
159 _grid.add_cell(0, 0, grid_cell_type::button);
160 _grid.add_cell(1, 0, grid_cell_type::label, true);
161 _grid.add_cell(2, 0, grid_cell_type::shortcut);
162
163 for (auto& cell : _grid) {
164 if (cell.value == grid_cell_type::button) {
165 auto constraints = _button_widget->update_constraints();
166 inplace_max(constraints.minimum.width(), theme().size() * 2.0f);
167 inplace_max(constraints.preferred.width(), theme().size() * 2.0f);
168 inplace_max(constraints.maximum.width(), theme().size() * 2.0f);
169 cell.set_constraints(constraints);
170
171 } else if (cell.value == grid_cell_type::label) {
172 cell.set_constraints(_label_widget->update_constraints());
173
174 } else if (cell.value == grid_cell_type::shortcut) {
175 auto constraints = _shortcut_widget->update_constraints();
176 inplace_max(constraints.minimum.width(), theme().size() * 3.0f);
177 inplace_max(constraints.preferred.width(), theme().size() * 3.0f);
178 inplace_max(constraints.maximum.width(), theme().size() * 3.0f);
179 cell.set_constraints(constraints);
180
181 } else {
182 hi_no_default();
183 }
184 }
185
186 auto constraints = _grid.constraints(os_settings::left_to_right());
187 constraints.minimum += extent2{theme().template margin<float>() * 2.0f, theme().template margin<float>() * 2.0f};
188 constraints.preferred += extent2{theme().template margin<float>() * 2.0f, theme().template margin<float>() * 2.0f};
189 constraints.maximum += extent2{theme().template margin<float>() * 2.0f, theme().template margin<float>() * 2.0f};
190 constraints.margins = {};
191 return constraints;
192 }
193
194 void set_layout(widget_layout const& context) noexcept override
195 {
196 if (compare_store(_layout, context)) {
197 auto shape = context.shape;
198 shape.rectangle -= theme().template margin<float>();
199 _grid.set_layout(shape, theme().baseline_adjustment());
200 }
201
202 for (auto const& cell : _grid) {
203 if (cell.value == grid_cell_type::button) {
204 _button_widget->set_layout(context.transform(cell.shape, transform_command::level));
205
206 } else if (cell.value == grid_cell_type::label) {
207 _label_widget->set_layout(context.transform(cell.shape));
208
209 } else if (cell.value == grid_cell_type::shortcut) {
210 _shortcut_widget->set_layout(context.transform(cell.shape));
211
212 } else {
213 hi_no_default();
214 }
215 }
216 }
217
218 void draw(draw_context const& context) noexcept override
219 {
220 if (mode() > widget_mode::invisible and overlaps(context, layout())) {
221 auto outline_color = focus() ? focus_color() : background_color();
222 context.draw_box(
223 layout(), layout().rectangle(), background_color(), outline_color, theme().border_width(), border_side::inside);
224
225 for (auto const& cell : _grid) {
226 if (cell.value == grid_cell_type::button) {
227 _button_widget->draw(context);
228
229 } else if (cell.value == grid_cell_type::label) {
230 _label_widget->draw(context);
231
232 } else if (cell.value == grid_cell_type::shortcut) {
233 _shortcut_widget->draw(context);
234
235 } else {
236 hi_no_default();
237 }
238 }
239 }
240 }
241
242 [[nodiscard]] generator<widget_intf&> children(bool include_invisible) noexcept override
243 {
244 co_yield *_button_widget;
245 co_yield *_label_widget;
246 co_yield *_shortcut_widget;
247 }
248
249 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
250 {
251 hi_axiom(loop::main().on_thread());
252
253 if (mode() >= widget_mode::partial and layout().contains(position)) {
254 // Accept the hitbox of the menu_button_widget on behalf of the button_widget.
255 return {_button_widget->id, _layout.elevation, hitbox_type::button};
256 } else {
257 return {};
258 }
259 }
261protected:
262 enum class grid_cell_type { button, label, shortcut };
263
264 grid_layout<grid_cell_type> _grid;
265
266 std::unique_ptr<button_widget_type> _button_widget;
267
268 std::unique_ptr<label_widget> _label_widget;
269 std::unique_ptr<label_widget> _shortcut_widget;
270
271 callback<void()> _button_widget_cbt;
272};
273
274} // namespace v1
275} // namespace hi::v1
Defines widget.
Defines label_widget.
Defines button_delegate and some default button delegates.
@ rectangle
The gui_event has rectangle data.
Definition gui_event_variant.hpp:44
@ partial
A widget is partially enabled.
Definition widget_state.hpp:73
@ invisible
The widget is invisible.
Definition widget_state.hpp:41
STL namespace.
The HikoGUI namespace.
Definition array_generic.hpp:21
The HikoGUI API version 1.
Definition array_generic.hpp:22
@ margin
A margin or padding value was modified.
Definition style_modify_mask.hpp:39
@ inside
The border is drawn inside the edge of a quad.
Definition draw_context_intf.hpp:35
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition misc.hpp:53
@ level
The child widget stays at the same elevation and layer.
Definition widget_layout.hpp:26
notifier< void()> notifier
Notifier which is called after an action is completed by a widget.
Definition widget_intf.hpp:45
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget_intf.hpp:241
widget_intf * parent() const noexcept
Pointer to the parent widget.
Definition widget_intf.hpp:113
observer< widget_state > state
The current state of the widget.
Definition widget_intf.hpp:49
A localizable message.
Definition txt.hpp:100
2D constraints.
Definition box_constraints.hpp:25
constexpr reference add_cell(size_t first_column, size_t first_row, size_t last_column, size_t last_row, Value &&value, bool beyond_maximum=false) noexcept
Check if the cell on the grid is already in use.
Definition grid_layout.hpp:1009
A observer pointing to the whole or part of a observed_base.
Definition observer_intf.hpp:32
Add menu-button around a small-button.
Definition menu_button_widget.hpp:38
menu_button_widget(Args &&... args)
Construct a widget with a label.
Definition menu_button_widget.hpp:144
Definition menu_button_widget.hpp:45
observer< alignment > alignment
The alignment of the button and on/off/other label.
Definition menu_button_widget.hpp:56
observer< hi::label > shortcut
The label to for the shortcut.
Definition menu_button_widget.hpp:52
observer< hi::label > label
The label to show when the button is in the 'on' state.
Definition menu_button_widget.hpp:48
void request_redraw() const noexcept override
Request the widget to be redrawn on the next frame.
Definition widget.hpp:136
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
True if T is a forwarded type of Forward.
Definition concepts.hpp:137
Definition label_widget.hpp:30
Definition menu_button_widget.hpp:31
T forward(T... args)
T move(T... args)