HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
toolbar_tab_button_widget.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2021-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 "radio_delegate.hpp"
13#include "../macros.hpp"
14
15hi_export_module(hikogui.widgets.toolbar_tab_button_widget);
16
17hi_export namespace hi {
18inline namespace v1 {
19
20template<typename Context>
22
55public:
56 using super = widget;
57 using delegate_type = radio_delegate;
58
63
67
70 observer<semantic_text_style> text_style = semantic_text_style::label;
71
72 observer<alignment> alignment = alignment::top_center();
73
74 attributes_type(attributes_type const&) noexcept = default;
75 attributes_type(attributes_type&&) noexcept = default;
76 attributes_type& operator=(attributes_type const&) noexcept = default;
77 attributes_type& operator=(attributes_type&&) noexcept = default;
78
79 template<toolbar_tab_button_widget_attribute... Attributes>
80 explicit attributes_type(Attributes&&...attributes) noexcept
81 {
82 set_attributes<0>(std::forward<Attributes>(attributes)...);
83 }
84
85 template<size_t NumLabels>
86 void set_attributes() noexcept
87 {
88 }
89
90 template<size_t NumLabels, toolbar_tab_button_widget_attribute First, toolbar_tab_button_widget_attribute... Rest>
91 void set_attributes(First&& first, Rest&&...rest) noexcept
92 {
94 if constexpr (NumLabels == 0) {
95 on_label = first;
96 off_label = std::forward<First>(first);
97
98 } else if constexpr (NumLabels == 1) {
100 off_label = std::forward<First>(first);
101
102 } else {
103 hi_static_no_default("Maximum two label attributes (on/off) are allowed on a toolbar-tab-button");
104 }
105 return set_attributes<NumLabels + 1>(std::forward<Rest>(rest)...);
106
107 } else if constexpr (forward_of<First, observer<hi::alignment>>) {
108 alignment = std::forward<First>(first);
109 return set_attributes<NumLabels>(std::forward<Rest>(rest)...);
110
111 } else if constexpr (forward_of<First, observer<hi::semantic_text_style>>) {
112 text_style = std::forward<First>(first);
113 return set_attributes<NumLabels>(std::forward<Rest>(rest)...);
114
115 } else {
116 hi_static_no_default();
117 }
118 }
119 };
120
121 attributes_type attributes;
122
126
127 hi_num_valid_arguments(consteval static, num_default_delegate_arguments, default_radio_delegate);
128 hi_call_left_arguments(static, make_default_delegate, make_shared_ctad<default_radio_delegate>);
129 hi_call_right_arguments(static, make_attributes, attributes_type);
130
132 {
133 delegate->deinit(*this);
134 }
135
146 widget_intf const* parent,
147 attributes_type attributes,
149 super(parent), attributes(std::move(attributes)), delegate(std::move(delegate))
150 {
151 _on_label_widget = std::make_unique<label_widget>(
152 this, this->attributes.on_label, this->attributes.alignment, this->attributes.text_style);
153 _off_label_widget = std::make_unique<label_widget>(
154 this, this->attributes.off_label, this->attributes.alignment, this->attributes.text_style);
155
156 hi_axiom_not_null(this->delegate);
157 this->delegate->init(*this);
158 _delegate_cbt = this->delegate->subscribe([&] {
159 set_value(this->delegate->state(*this));
160 });
161 _delegate_cbt();
162 }
163
170 template<typename... Args>
172 requires(num_default_delegate_arguments<Args...>() != 0)
173 :
175 parent,
176 make_attributes<num_default_delegate_arguments<Args...>()>(std::forward<Args>(args)...),
177 make_default_delegate<num_default_delegate_arguments<Args...>()>(std::forward<Args>(args)...))
178 {
179 }
180
181 void request_redraw() const noexcept override
182 {
183 // A toolbar tab button draws a focus line across the whole toolbar
184 // which is beyond it's own clipping rectangle. The parent is the toolbar
185 // so it will include everything that needs to be redrawn.
186 if (parent != nullptr) {
188 } else {
190 }
191 }
192
194 [[nodiscard]] box_constraints update_constraints() noexcept override
195 {
196 _layout = {};
197 _on_label_constraints = _on_label_widget->update_constraints();
198 _off_label_constraints = _off_label_widget->update_constraints();
199
200 _label_constraints = max(_on_label_constraints, _off_label_constraints);
201
202 // On left side a check mark, on right side short-cut. Around the label extra margin.
203 auto const extra_size = extent2{theme().margin<float>() * 2.0f, theme().margin<float>()};
204 return _label_constraints + extra_size;
205 }
206
207 void set_layout(widget_layout const& context) noexcept override
208 {
209 if (compare_store(_layout, context)) {
210 auto const label_rectangle = aarectangle{
211 theme().margin<float>(),
212 0.0f,
213 context.width() - theme().margin<float>() * 2.0f,
214 context.height() - theme().margin<float>()};
215 _on_label_shape = _off_label_shape =
216 box_shape{_label_constraints, label_rectangle, theme().baseline_adjustment()};
217 }
218
219 _on_label_widget->set_mode(value() == widget_value::on ? widget_mode::display : widget_mode::invisible);
220 _off_label_widget->set_mode(value() != widget_value::on ? widget_mode::display : widget_mode::invisible);
221
222 _on_label_widget->set_layout(context.transform(_on_label_shape));
223 _off_label_widget->set_layout(context.transform(_off_label_shape));
224 }
225
226 void draw(draw_context const& context) noexcept override
227 {
228 if (mode() > widget_mode::invisible and overlaps(context, layout())) {
229 draw_toolbar_tab_button(context);
230 _on_label_widget->draw(context);
231 _off_label_widget->draw(context);
232 }
233 }
234
235 [[nodiscard]] bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
236 {
237 return mode() >= widget_mode::partial and to_bool(group & hi::keyboard_focus_group::toolbar);
238 }
239
240 [[nodiscard]] generator<widget_intf&> children(bool include_invisible) noexcept override
241 {
242 co_yield *_on_label_widget;
243 co_yield *_off_label_widget;
244 }
245
246 [[nodiscard]] color background_color() const noexcept override
247 {
248 hi_axiom(loop::main().on_thread());
249 if (phase() == widget_phase::pressed) {
250 return theme().color(semantic_color::fill, _layout.layer + 2);
251 } else {
252 return super::background_color();
253 }
254 }
255
256 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
257 {
258 hi_axiom(loop::main().on_thread());
259
260 if (mode() >= widget_mode::partial and layout().contains(position)) {
261 return {id, _layout.elevation, hitbox_type::button};
262 } else {
263 return {};
264 }
265 }
266
267 void activate() noexcept
268 {
269 delegate->activate(*this);
270
271 notifier();
272 }
273
274 bool handle_event(gui_event const& event) noexcept override
275 {
276 hi_axiom(loop::main().on_thread());
277
278 switch (event.type()) {
279 case gui_event_type::gui_activate:
280 if (mode() >= widget_mode::partial) {
281 activate();
282 return true;
283 }
284 break;
285
286 case gui_event_type::mouse_down:
287 if (mode() >= widget_mode::partial and event.mouse().cause.left_button) {
288 set_pressed(true);
289 return true;
290 }
291 break;
292
293 case gui_event_type::mouse_up:
294 if (mode() >= widget_mode::partial and event.mouse().cause.left_button) {
295 set_pressed(false);
296
297 if (layout().rectangle().contains(event.mouse().position)) {
298 handle_event(gui_event_type::gui_activate);
299 }
300 return true;
301 }
302 break;
303
304 default:;
305 }
306
307 return super::handle_event(event);
308 }
309 // @endprivatesection
310protected:
311 std::unique_ptr<label_widget> _on_label_widget;
312 box_constraints _on_label_constraints;
313 box_shape _on_label_shape;
314
315 std::unique_ptr<label_widget> _off_label_widget;
316 box_constraints _off_label_constraints;
317 box_shape _off_label_shape;
318
319 callback<void()> _delegate_cbt;
320
321private:
322 box_constraints _label_constraints;
323
324 void draw_toolbar_tab_button(draw_context const& context) noexcept
325 {
326 // Draw the outline of the button across the clipping rectangle to clip the
327 // bottom of the outline.
328 auto const offset = theme().margin<float>() + theme().border_width();
329 auto const outline_rectangle = aarectangle{0, -offset, layout().width(), layout().height() + offset};
330
331 // The focus line will be drawn by the parent widget (toolbar_widget) at 0.5.
332 auto const button_z = focus() ? translate_z(0.6f) : translate_z(0.0f);
333
334 // clang-format off
335 auto button_color = (phase() == widget_phase::hover or value() == widget_value::on) ?
336 theme().color(semantic_color::fill, _layout.layer - 1) :
337 theme().color(semantic_color::fill, _layout.layer);
338 // clang-format on
339
340 auto const corner_radii = hi::corner_radii(0.0f, 0.0f, theme().rounding_radius<float>(), theme().rounding_radius<float>());
341
342 context.draw_box(
343 layout(),
344 button_z * outline_rectangle,
345 button_color,
346 focus() ? focus_color() : button_color,
347 theme().border_width(),
349 corner_radii);
350 }
351};
352
353} // namespace v1
354} // namespace hi::v1
Defines widget.
@ rectangle
The gui_event has rectangle data.
widget_mode
The mode that the widget is operating at.
Definition widget_state.hpp:25
@ partial
A widget is partially enabled.
@ invisible
The widget is invisible.
@ display
The widget is in display-only mode.
The HikoGUI namespace.
Definition array_generic.hpp:20
border_side
The side where the border is drawn.
Definition draw_context_intf.hpp:28
@ inside
The border is drawn inside the edge of a quad.
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition misc.hpp:53
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Horizontal/Vertical alignment combination.
Definition alignment.hpp:244
The 4 radii of the corners of a quad or rectangle.
Definition corner_radii.hpp:26
A high-level geometric extent.
Definition extent2.hpp:32
Definition widget_intf.hpp:24
notifier< void()> notifier
Notifier which is called after an action is completed by a widget.
Definition widget_intf.hpp:39
widget_id id
The numeric identifier of a widget.
Definition widget_intf.hpp:30
virtual void request_redraw() const noexcept=0
Request the widget to be redrawn on the next frame.
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget_intf.hpp:206
widget_intf * parent
Pointer to the parent widget.
Definition widget_intf.hpp:35
A localizable message.
Definition txt.hpp:100
2D constraints.
Definition box_constraints.hpp:25
A observer pointing to the whole or part of a observed_base.
Definition observer_intf.hpp:32
void reset() noexcept
Reset the observer.
Definition observer_intf.hpp:421
A radio delegate controls the state of a radio button widget.
Definition radio_delegate.hpp:16
A default radio button delegate.
Definition radio_delegate.hpp:56
A graphical control element that allows the user to choose only one of a predefined set of mutually e...
Definition toolbar_tab_button_widget.hpp:54
toolbar_tab_button_widget(widget_intf const *parent, attributes_type attributes, std::shared_ptr< delegate_type > delegate) noexcept
Construct a toolbar tab button widget.
Definition toolbar_tab_button_widget.hpp:145
toolbar_tab_button_widget(widget_intf const *parent, Args &&...args)
Construct a toolbar tab button widget with a default radio delegate.
Definition toolbar_tab_button_widget.hpp:171
std::shared_ptr< delegate_type > delegate
The delegate that controls the button widget.
Definition toolbar_tab_button_widget.hpp:125
void request_redraw() const noexcept override
Request the widget to be redrawn on the next frame.
Definition toolbar_tab_button_widget.hpp:181
Definition toolbar_tab_button_widget.hpp:59
observer< label > off_label
The label to show when the button is in the 'off' state.
Definition toolbar_tab_button_widget.hpp:66
observer< semantic_text_style > text_style
The text style to button's label.
Definition toolbar_tab_button_widget.hpp:70
observer< label > on_label
The label to show when the button is in the 'on' state.
Definition toolbar_tab_button_widget.hpp:62
An interactive graphical object as part of the user-interface.
Definition widget.hpp:37
void request_redraw() const noexcept override
Request the widget to be redrawn on the next frame.
Definition widget.hpp:141
widget() noexcept
Constructor for creating sub views.
Definition widget.hpp:55
bool handle_event(gui_event const &event) noexcept override
Handle command.
Definition widget.hpp:150
True if T is a forwarded type of Forward.
Definition concepts.hpp:137
Definition label_widget.hpp:30
Definition toolbar_tab_button_widget.hpp:21
T move(T... args)