HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
radio_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
12
13namespace hi { inline namespace v1 {
14
42template<fixed_string Name = "">
43class radio_button_widget final : public widget {
44public:
45 using super = widget;
46 using delegate_type = radio_button_delegate;
47 constexpr static auto prefix = Name / "radio";
48
52
55 observer<label> label = tr("<not set>");
56
59 observer<hi::alignment> alignment = alignment::top_left();
60
73 button_widget_attribute auto&&...attributes) noexcept :
75 {
76 hi_assert_not_null(this->delegate);
77 this->set_attributes<0>(hi_forward(attributes)...);
78
79 _label_widget = std::make_unique<label_widget<prefix>>(this, on_label, alignment);
80
81 _grid.add_cell(0, 0, cell_type::button);
82 _grid.add_cell(1, 0, cell_type::label);
83
84 _delegate_cbt = this->delegate->subscribe([&] {
85 ++global_counter<"radio_button_widget:delegate:redraw">;
86 hi_assert_not_null(this->delegate);
87 state = this->delegate->state(this);
88
89 process_event({gui_event_type::window_redraw});
90 });
91 this->delegate->init(*this);
92 (*_delegate_cbt)();
93 }
94
107 template<
108 different_from<std::shared_ptr<delegate_type>> Value,
109 forward_of<observer<observer_decay_t<Value>>> OnValue,
110 button_widget_attribute... Attributes>
111 radio_button_widget(widget *parent, Value&& value, OnValue&& on_value, Attributes&&...attributes) noexcept
112 requires requires { make_default_radio_button_delegate(hi_forward(value), hi_forward(on_value)); }
113 :
115 parent,
117 hi_forward(attributes)...)
118 {
119 }
120
122 [[nodiscard]] box_constraints update_constraints() noexcept override
123 {
124 for (auto& cell : _grid) {
125 if (cell.value == cell_type::button) {
126 cell.set_constraints(box_constraints{
127 theme<prefix>.size(this),
128 theme<prefix>.size(this),
129 theme<prefix>.size(this),
130 *alignment,
131 theme<prefix>.margin(this),
132 -vector2::infinity()});
133
134 } else if (cell.value == cell_type::label) {
135 cell.set_constraints(max(_label_widget->update_constraints()));
136
137 } else {
139 }
140 }
141
142 return _grid.constraints(os_settings::left_to_right());
143 }
144
145 void set_layout(widget_layout const& context) noexcept override
146 {
147 if (compare_store(layout, context)) {
148 _grid.set_layout(context.shape, theme<prefix>.cap_height(this));
149 }
150
151 for (hilet& cell : _grid) {
152 if (cell.value == cell_type::button) {
153 _button_rectangle = align(cell.shape.rectangle, theme<prefix>.size(this), *alignment);
154 _pip_rectangle = align(_button_rectangle, theme<prefix / "pip">.size(this), alignment::middle_center());
155
156 } else if (cell.value == cell_type::label) {
157 _label_widget->set_layout(context.transform(cell.shape, 0.0f));
158
159 } else {
161 }
162 }
163 }
164
165 void draw(widget_draw_context& context) noexcept override
166 {
167 if (*mode > widget_mode::invisible and overlaps(context, layout)) {
168 for (hilet& cell : _grid) {
169 if (cell.value == cell_type::button) {
170 draw_button(context);
171 draw_pip(context);
172
173 } else if (cell.value == cell_type::label) {
174 _label_widget->draw(context);
175
176 } else {
178 }
179 }
180 }
181 }
182
183 [[nodiscard]] generator<widget const&> children(bool include_invisible) const noexcept override
184 {
185 co_yield *_label_widget;
186 }
187
188 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept final
189 {
190 hi_axiom(loop::main().on_thread());
191
192 if (*mode >= widget_mode::partial and layout.contains(position)) {
193 return {id, layout.elevation, hitbox_type::button};
194 } else {
195 return {};
196 }
197 }
198
199 [[nodiscard]] bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
200 {
201 hi_axiom(loop::main().on_thread());
202 return *mode >= widget_mode::partial and to_bool(group & hi::keyboard_focus_group::normal);
203 }
204
205 void activate() noexcept
206 {
208 delegate->activate(*this);
209 this->_state_changed();
210 }
211
212 bool handle_event(gui_event const& event) noexcept override
213 {
214 hi_axiom(loop::main().on_thread());
215
216 switch (event.type()) {
217 case gui_event_type::gui_activate:
218 if (*mode >= widget_mode::partial) {
219 activate();
220 return true;
221 }
222 break;
223
224 case gui_event_type::mouse_down:
225 if (*mode >= widget_mode::partial and event.mouse().cause.left_button) {
226 clicked = true;
228 return true;
229 }
230 break;
231
232 case gui_event_type::mouse_up:
233 if (*mode >= widget_mode::partial and event.mouse().cause.left_button) {
234 clicked = false;
235
236 if (layout.rectangle().contains(event.mouse().position)) {
237 handle_event(gui_event_type::gui_activate);
238 }
240 return true;
241 }
242 break;
243
244 default:;
245 }
246
247 return super::handle_event(event);
248 }
250private:
251 enum class cell_type { button, label };
252
253 static constexpr std::chrono::nanoseconds _animation_duration = std::chrono::milliseconds(150);
254
255 grid_layout<cell_type> _grid;
257
258 notifier<>::callback_token _delegate_cbt;
259
260 aarectangle _button_rectangle;
261 aarectangle _pip_rectangle;
262
263 animator<float> _animated_value = _animation_duration;
264
265 void draw_button(widget_draw_context& context) noexcept
266 {
267 context.draw_box(
268 this->layout,
269 _button_rectangle,
270 theme<prefix>.background_color(this),
271 theme<prefix>.border_color(this),
272 theme<prefix>.border_width(this),
274 theme<prefix>.border_radius(this));
275 }
276
277 void draw_pip(widget_draw_context& context) noexcept
278 {
279 _animated_value.update(this->state != widget_state::off ? 1.0f : 0.0f, context.display_time_point);
280 if (_animated_value.is_animating()) {
281 this->request_redraw();
282 }
283
284 // draw pip
285 auto float_value = _animated_value.current_value();
286 if (float_value > 0.0f) {
287 context.draw_box(
288 this->layout,
289 _pip_rectangle * float_value,
290 theme<prefix / "pip">.background_color(this),
291 theme<prefix / "pip">.border_color(this),
292 theme<prefix / "pip">.border_width(this),
294 theme<prefix / "pip">.border_radius(this) * float_value);
295 }
296 }
297};
298
299}} // namespace hi::v1
Defines radio_button_delegate and some default radio_button_delegate delegates.
#define hi_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:279
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:238
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition utility.hpp:29
@ window_redraw
Request that part of the window gets redrawn on the next frame.
std::shared_ptr< radio_button_delegate > make_default_radio_button_delegate(auto &&value, auto &&...args) noexcept
Make a shared pointer to a toggle-button delegate.
Definition radio_button_delegate.hpp:113
@ partial
A widget is partially enabled.
@ invisible
The widget is invisible.
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
@ inside
The border is drawn inside the edge of a quad.
@ outside
The border is drawn outside the edge of a quad.
@ off
The widget in the off-state.
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition utility.hpp:212
constexpr extent< value_type, 2 > size() const noexcept
Get size of the rectangle.
Definition axis_aligned_rectangle.hpp:183
Definition widget.hpp:26
widget_id id
The numeric identifier of a widget.
Definition widget.hpp:35
virtual void request_redraw() const noexcept
Request the widget to be redrawn on the next frame.
Definition widget.hpp:227
observer< bool > clicked
The widget is being clicked by the mouse.
Definition widget.hpp:57
observer< widget_state > state
The state of the widget.
Definition widget.hpp:65
virtual bool handle_event(gui_event const &event) noexcept
Handle command.
Definition widget.hpp:236
widget * parent
Pointer to the parent widget.
Definition widget.hpp:40
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:49
2D constraints.
Definition box_constraints.hpp:22
A radio button delegate controls the state of a radio button widget.
Definition radio_button_delegate.hpp:18
A graphical control element that allows the user to choose only one of a predefined set of mutually e...
Definition radio_button_widget.hpp:43
radio_button_widget(widget *parent, Value &&value, OnValue &&on_value, Attributes &&...attributes) noexcept
Construct a radio button widget with a default button delegate.
Definition radio_button_widget.hpp:111
observer< label > label
The label to show when the button is in the 'on' state.
Definition radio_button_widget.hpp:55
std::shared_ptr< delegate_type > delegate
The delegate that controls the button widget.
Definition radio_button_widget.hpp:51
radio_button_widget(widget *parent, std::shared_ptr< delegate_type > delegate, button_widget_attribute auto &&...attributes) noexcept
Construct a radio button widget.
Definition radio_button_widget.hpp:70
observer< hi::alignment > alignment
The alignment of the button and on/off/other label.
Definition radio_button_widget.hpp:59
Definition abstract_button_widget.hpp:26
T align(T... args)
T max(T... args)
T move(T... args)