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 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
30template<fixed_string Name = "">
31class menu_button_widget final : public abstract_button_widget<Name / "menu-button"> {
32public:
33 using super = abstract_button_widget<Name / "menu-button">;
34 using delegate_type = typename super::delegate_type;
35 using super::prefix;
36
49 button_widget_attribute auto&&...attributes) noexcept :
51 {
52 this->alignment = alignment::middle_flush();
53 this->set_attributes<0>(hi_forward(attributes)...);
54 }
55
68 template<
69 different_from<std::shared_ptr<delegate_type>> Value,
70 forward_of<observer<observer_decay_t<Value>>> OnValue,
71 button_widget_attribute... Attributes>
72 menu_button_widget(widget *parent, Value&& value, OnValue&& on_value, Attributes&&...attributes) noexcept
73 requires requires { make_default_radio_button_delegate(hi_forward(value), hi_forward(on_value)); }
74 :
76 parent,
78 hi_forward(attributes)...)
79 {
80 }
81
83 [[nodiscard]] box_constraints update_constraints() noexcept override
84 {
85 _label_constraints = super::update_constraints();
86
87 // Make room for button and margin.
88 _short_cut_size = _check_size =
89 extent2i{theme<prefix>.line_height(this), theme<prefix>.line_height(this)};
90
91 // On left side a check mark, on right side short-cut. Around the label extra margin.
92 hilet extra_size = extent2i{
93 theme<prefix>.margin_left(this) + _check_size.width() + theme<prefix>.spacing_horizontal(this) +
94 theme<prefix>.spacing_horizontal(this) + _short_cut_size.width() + theme<prefix>.margin_right(this),
95 theme<prefix>.margin_top(this) + theme<prefix>.margin_bottom(this)};
96
97 auto constraints = _label_constraints + extra_size;
98 constraints.margins = 0;
99 return constraints;
100 }
101
102 void set_layout(widget_layout const& context) noexcept override
103 {
104 if (compare_store(this->layout, context)) {
105 hilet spacing = theme<prefix>.spacing_horizontal(this);
106 hilet cap_height = theme<prefix>.cap_height(this);
107
108 hilet inside_rectangle = context.rectangle() - spacing;
109
110 if (os_settings::left_to_right()) {
111 _check_rectangle = align(inside_rectangle, _check_size, alignment::middle_left());
112 _short_cut_rectangle = align(inside_rectangle, _short_cut_size, alignment::middle_right());
113 hilet label_rectangle = aarectanglei{
114 point2i{_check_rectangle.right() + spacing, 0},
115 point2i{_short_cut_rectangle.left() - spacing, context.height()}};
116 this->_on_label_shape = this->_off_label_shape = this->_other_label_shape =
117 box_shape{_label_constraints, label_rectangle, cap_height};
118
119 } else {
120 _short_cut_rectangle = align(inside_rectangle, _short_cut_size, alignment::middle_left());
121 _check_rectangle = align(inside_rectangle, _check_size, alignment::middle_right());
122 hilet label_rectangle = aarectanglei{
123 point2i{_short_cut_rectangle.right() + spacing, 0},
124 point2i{_check_rectangle.left() - spacing, context.height()}};
125 this->_on_label_shape = this->_off_label_shape = this->_other_label_shape =
126 box_shape{_label_constraints, label_rectangle, cap_height};
127 }
128
129 _check_glyph = find_glyph(elusive_icon::Ok);
130 hilet check_glyph_bb =
131 narrow_cast<aarectanglei>(_check_glyph.get_bounding_rectangle() * theme<prefix>.line_height(this));
132 _check_glyph_rectangle = align(_check_rectangle, check_glyph_bb, alignment::middle_center());
133 }
134
135 super::set_layout(context);
136 }
137
138 void draw(widget_draw_context& context) noexcept override
139 {
140 if (*this->mode > widget_mode::invisible and overlaps(context, this->layout)) {
141 draw_menu_button(context);
142 draw_check_mark(context);
143 this->draw_button(context);
144 }
145 }
146
147 [[nodiscard]] bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
148 {
149 return *this->mode >= widget_mode::partial and to_bool(group & hi::keyboard_focus_group::menu);
150 }
151
152 bool handle_event(gui_event const& event) noexcept override
153 {
154 using enum gui_event_type;
155
156 switch (event.type()) {
157 case gui_menu_next:
158 if (*this->mode >= widget_mode::partial and not this->is_last(keyboard_focus_group::menu)) {
159 this->process_event(gui_event::window_set_keyboard_target(
160 nullptr, keyboard_focus_group::menu, keyboard_focus_direction::forward));
161 return true;
162 }
163 break;
164
165 case gui_menu_prev:
166 if (*this->mode >= widget_mode::partial and not this->is_first(keyboard_focus_group::menu)) {
167 this->process_event(gui_event::window_set_keyboard_target(
168 nullptr, keyboard_focus_group::menu, keyboard_focus_direction::backward));
169 return true;
170 }
171 break;
172
173 case gui_activate:
174 if (*this->mode >= widget_mode::partial) {
175 this->activate();
176 this->process_event(gui_event::window_set_keyboard_target(
177 nullptr, keyboard_focus_group::normal, keyboard_focus_direction::forward));
178 this->process_event(gui_event::window_set_keyboard_target(
179 nullptr, keyboard_focus_group::normal, keyboard_focus_direction::backward));
180 return true;
181 }
182 break;
183
184 default:;
185 }
186
187 return super::handle_event(event);
188 }
190private:
191 box_constraints _label_constraints;
192
193 font_book::font_glyph_type _check_glyph;
194 extent2i _check_size;
195 aarectanglei _check_rectangle;
196 aarectanglei _check_glyph_rectangle;
197 extent2i _short_cut_size;
198 aarectanglei _short_cut_rectangle;
199
200 void draw_menu_button(widget_draw_context& context) noexcept
201 {
202 context.draw_box(
203 this->layout,
204 this->layout.rectangle(),
205 theme<prefix>.background_color(this),
206 theme<prefix>.border_color(this),
207 theme<prefix>.border_width(this),
209 }
210
211 void draw_check_mark(widget_draw_context& context) noexcept
212 {
213 // Checkmark or tristate.
214 if (*this->state != hi::widget_state::off) {
215 context.draw_glyph(
216 this->layout,
217 translate_z(0.1f) * narrow_cast<aarectangle>(_check_glyph_rectangle),
218 _check_glyph,
219 theme<prefix>.fill_color(this));
220 }
221 }
222};
223
224}} // namespace hi::v1
Defines abstract_button_widget.
#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
geo::extent< int, 2 > extent2i
A 2D extent.
Definition extent.hpp:512
gui_event_type
GUI event type.
Definition gui_event_type.hpp:21
std::shared_ptr< button_delegate > make_default_radio_button_delegate(auto &&value, auto &&on_value) noexcept
Make a shared pointer to a radio-button delegate.
Definition button_delegate.hpp:205
@ partial
A widget is partially enabled.
@ invisible
The widget is invisible.
DOXYGEN BUG.
Definition algorithm.hpp:13
auto find_glyph(font const &font, grapheme grapheme) noexcept
Find a glyph using the given code-point.
Definition font_book.hpp:223
geometry/margins.hpp
Definition cache.hpp:11
@ 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 utility.hpp:212
constexpr value_type & width() noexcept
Access the x-as-width element from the extent.
Definition extent.hpp:166
Definition widget.hpp:26
observer< widget_state > state
The state of the widget.
Definition widget.hpp:65
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
Base class for implementing button widgets.
Definition abstract_button_widget.hpp:33
observer< hi::alignment > alignment
The alignment of the button and on/off/other label.
Definition abstract_button_widget.hpp:58
std::shared_ptr< delegate_type > delegate
The delegate that controls the button widget.
Definition abstract_button_widget.hpp:42
A button delegate controls the state of a button widget.
Definition button_delegate.hpp:23
A button that is part of a menu.
Definition menu_button_widget.hpp:31
menu_button_widget(widget *parent, Value &&value, OnValue &&on_value, Attributes &&...attributes) noexcept
Construct a menu button widget with a default button delegate.
Definition menu_button_widget.hpp:72
menu_button_widget(widget *parent, std::shared_ptr< delegate_type > delegate, button_widget_attribute auto &&...attributes) noexcept
Construct a menu button widget.
Definition menu_button_widget.hpp:46
Definition abstract_button_widget.hpp:26
T align(T... args)
T move(T... args)