7#include "abstract_container_widget.hpp"
8#include "overlay_view_widget.hpp"
9#include "scroll_view_widget.hpp"
10#include "row_column_layout_widget.hpp"
11#include "menu_item_widget.hpp"
12#include "../stencils/label_stencil.hpp"
13#include "../GUI/draw_context.hpp"
14#include "../text/font_book.hpp"
15#include "../text/elusive_icon.hpp"
16#include "../observable.hpp"
36 template<
typename Value = value_type,
typename OptionList = option_list_type,
typename UnknownLabel = label>
40 Value &&value = value_type{},
42 UnknownLabel &&unknown_label =
label{
l10n(
"<unknown>")})
noexcept :
44 value(std::forward<Value>(value)),
45 option_list(std::forward<OptionList>(option_list)),
46 unknown_label(std::forward<UnknownLabel>(unknown_label))
55 void init() noexcept
override
57 _overlay_widget = super::make_widget<overlay_view_widget>();
63 _value_callback = this->value.subscribe([
this](
auto...) {
64 _request_reconstrain =
true;
66 _option_list_callback = this->option_list.subscribe([
this](
auto...) {
68 _request_reconstrain =
true;
70 _unknown_label_callback = this->unknown_label.subscribe([
this](
auto...) {
71 _request_reconstrain =
true;
82 ttlet index = get_value_as_index();
85 stencil::make_unique(alignment::middle_left, *unknown_label, theme::global->placeholderLabelStyle);
86 _text_stencil_color = theme::global->placeholderLabelStyle.color;
89 stencil::make_unique(alignment::middle_left, (*option_list)[index].second, theme::global->labelStyle);
90 _text_stencil_color = theme::global->labelStyle.color;
94 ttlet unknown_label_size =
95 stencil::make_unique(alignment::middle_left, *unknown_label, theme::global->placeholderLabelStyle)
98 ttlet overlay_width = _overlay_widget->preferred_size().minimum().width();
99 ttlet option_width =
std::max(overlay_width, unknown_label_size.width() + theme::global->margin * 2.0f);
100 ttlet option_height =
std::max(unknown_label_size.height(), _max_option_label_height) + theme::global->margin * 2.0f;
101 ttlet chevron_width = theme::global->smallSize;
103 _preferred_size = interval_extent2::make_minimum(
extent2{chevron_width + option_width, option_height});
115 need_layout |= std::exchange(_request_relayout,
false);
124 ttlet overlay_width =
125 clamp(
rectangle().width() - theme::global->smallSize, _overlay_widget->preferred_size().width());
126 ttlet overlay_height = _overlay_widget->preferred_size().maximum().height();
127 ttlet overlay_x = theme::global->smallSize;
128 ttlet overlay_y =
std::round(_size.height() * 0.5f - overlay_height * 0.5f);
129 ttlet overlay_rectangle_request =
aarectangle{overlay_x, overlay_y, overlay_width, overlay_height};
131 ttlet overlay_rectangle = _overlay_widget->make_overlay_rectangle_from_parent(overlay_rectangle_request);
132 ttlet overlay_clipping_rectangle = expand(overlay_rectangle, _overlay_widget->margin());
134 _overlay_widget->set_layout_parameters_from_parent(
135 overlay_rectangle, overlay_clipping_rectangle, _overlay_widget->draw_layer() - _draw_layer);
138 _chevrons_glyph = to_font_glyph_ids(elusive_icon::ChevronUp);
140 _chevrons_rectangle =
141 align(_left_box_rectangle, scale(chevrons_glyph_bbox, theme::global->small_icon_size), alignment::middle_center);
142 _chevrons_rectangle =
143 align(_left_box_rectangle, scale(chevrons_glyph_bbox, theme::global->small_icon_size), alignment::middle_center);
147 _left_box_rectangle.right() + theme::global->margin,
149 rectangle().width() - _left_box_rectangle.width() - theme::global->margin * 2.0f,
152 _text_stencil->set_layout_parameters(_option_rectangle,
base_line());
161 if (overlaps(context, this->_clipping_rectangle)) {
162 draw_outline(context);
163 draw_left_box(context);
164 draw_chevrons(context);
173 void request_redraw() const noexcept
override
175 super::request_redraw();
176 _overlay_widget->request_redraw();
181 ttlet lock = std::scoped_lock(gui_system_mutex);
184 if (event.cause.leftButton) {
187 if (event.type == mouse_event::Type::ButtonUp &&
rectangle().contains(event.position)) {
197 ttlet lock = std::scoped_lock(gui_system_mutex);
198 _request_relayout =
true;
202 using enum tt::command;
235 if (_visible_rectangle.contains(position)) {
236 r =
std::max(r,
hit_box{weak_from_this(), _draw_layer, *
enabled ? hit_box::Type::Button : hit_box::Type::Default});
245 return is_normal(group) && *
enabled;
250 keyboard_focus_group group,
251 keyboard_focus_direction direction)
const noexcept
253 ttlet lock = std::scoped_lock(gui_system_mutex);
264 template<
typename T,
typename... Args>
270 [[nodiscard]] color focus_color() const noexcept
override
273 return theme::global->accentColor;
275 return super::focus_color();
280 typename decltype(unknown_label)::callback_ptr_type _unknown_label_callback;
281 typename decltype(value)::callback_ptr_type _value_callback;
282 typename decltype(option_list)::callback_ptr_type _option_list_callback;
285 color _text_stencil_color;
287 float _max_option_label_height;
289 aarectangle _option_rectangle;
290 aarectangle _left_box_rectangle;
292 font_glyph_ids _chevrons_glyph;
293 aarectangle _chevrons_rectangle;
295 bool _selecting =
false;
303 [[nodiscard]] ssize_t get_value_as_index() const noexcept
306 for (ttlet & [ tag, unknown_label_text ] : *option_list) {
318 if (std::ssize(_menu_item_widgets) != 0) {
319 return _menu_item_widgets.
front();
327 ttlet i = get_value_as_index();
328 if (i >= 0 && i < std::ssize(_menu_item_widgets)) {
329 return _menu_item_widgets[i];
335 void start_selecting() noexcept
340 if (
auto selected_menu_item = get_selected_menu_item()) {
343 }
else if (
auto first_menu_item = get_first_menu_item()) {
351 void stop_selecting() noexcept
361 void repopulate_options() noexcept
363 ttlet
lock = std::scoped_lock(gui_system_mutex);
364 auto option_list_ = *option_list;
367 auto show_icon =
false;
368 for (ttlet & [ tag, label ] : option_list_) {
369 show_icon |= label.has_icon();
372 _column_widget->clear();
373 _menu_item_widgets.
clear();
374 _menu_item_callbacks.
clear();
375 for (ttlet & [ tag, text ] : option_list_) {
376 auto menu_item = _column_widget->make_widget<menu_item_widget<value_type>>(tag, this->value);
377 menu_item->set_show_check_mark(
true);
378 menu_item->set_show_icon(show_icon);
379 menu_item->label = text;
381 _menu_item_callbacks.
push_back(menu_item->subscribe([
this, tag] {
383 this->_selecting = false;
389 _max_option_label_height = 0.0f;
390 for (ttlet & [ tag, text ] : *option_list) {
391 _max_option_label_height =
std::max(
392 _max_option_label_height,
393 stencil::make_unique(alignment::middle_left, text, theme::global->labelStyle)->preferred_extent().height());
397 void draw_outline(draw_context context)
noexcept
401 context.draw_box_with_border_inside(
402 rectangle(), background_color(), focus_color(), corner_shapes{theme::global->roundingRadius});
405 void draw_left_box(draw_context context)
noexcept
409 ttlet corner_shapes =
tt::corner_shapes{theme::global->roundingRadius, 0.0f, theme::global->roundingRadius, 0.0f};
410 context.draw_box(translate_z(0.1f) * _left_box_rectangle, focus_color(), corner_shapes);
413 void draw_chevrons(draw_context context)
noexcept
417 context.draw_glyph(_chevrons_glyph, translate_z(0.2f) * _chevrons_rectangle, label_color());
420 void draw_value(draw_context context)
noexcept
423 _text_stencil->draw(context, label_color(), translate_z(0.1f));
Class which represents an axis-aligned rectangle.
Definition axis_aligned_rectangle.hpp:18
Definition corner_shapes.hpp:9
Class which represents an rectangle.
Definition rectangle.hpp:16
Draw context for drawing using the TTauri shaders.
Definition draw_context.hpp:33
Definition gui_window.hpp:37
void update_keyboard_target(std::shared_ptr< tt::widget > widget, keyboard_focus_group group=keyboard_focus_group::normal) noexcept
Change the keyboard focus to the given widget.
Definition hit_box.hpp:15
Definition mouse_event.hpp:15
static aarectangle getBoundingBox(font_glyph_ids const &glyphs) noexcept
Get the bounding box, including draw border of a glyph.
A localizable string.
Definition l10n.hpp:12
A localized text + icon label.
Definition label.hpp:76
Definition observable.hpp:20
int recurse_lock_count() const noexcept
This function should be used in tt_axiom() to check if the lock is held by current thread.
Definition unfair_recursive_mutex.hpp:60
Definition abstract_container_widget.hpp:11
void update_layout(hires_utc_clock::time_point display_time_point, bool need_layout) noexcept
Update the internal layout of the widget.
Definition abstract_container_widget.hpp:110
void draw(draw_context context, hires_utc_clock::time_point display_time_point) noexcept
Draw the widget.
Definition abstract_container_widget.hpp:124
hit_box hitbox_test(point2 position) const noexcept override
Find the widget that is under the mouse cursor.
Definition abstract_container_widget.hpp:154
std::shared_ptr< widget > find_next_widget(std::shared_ptr< widget > const ¤t_keyboard_widget, keyboard_focus_group group, keyboard_focus_direction direction) const noexcept
Find the next widget that handles keyboard focus.
Definition abstract_container_widget.hpp:187
bool update_constraints(hires_utc_clock::time_point display_time_point, bool need_reconstrain) noexcept
Update the constraints of the widget.
Definition abstract_container_widget.hpp:95
Definition row_column_layout_widget.hpp:16
Definition scroll_view_widget.hpp:14
Definition selection_widget.hpp:26
std::shared_ptr< widget > find_next_widget(std::shared_ptr< widget > const ¤t_widget, keyboard_focus_group group, keyboard_focus_direction direction) const noexcept
Find the next widget that handles keyboard focus.
Definition selection_widget.hpp:248
bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
Check if the widget will accept keyboard focus.
Definition selection_widget.hpp:242
void draw(draw_context context, hires_utc_clock::time_point display_time_point) noexcept override
Draw the widget.
Definition selection_widget.hpp:157
bool update_constraints(hires_utc_clock::time_point display_time_point, bool need_reconstrain) noexcept override
Update the constraints of the widget.
Definition selection_widget.hpp:75
bool handle_event(command command) noexcept override
Handle command.
Definition selection_widget.hpp:195
bool handle_event(mouse_event const &event) noexcept override
Definition selection_widget.hpp:179
hit_box hitbox_test(point2 position) const noexcept override
Find the widget that is under the mouse cursor.
Definition selection_widget.hpp:225
void update_layout(hires_utc_clock::time_point display_time_point, bool need_layout) noexcept override
Update the internal layout of the widget.
Definition selection_widget.hpp:111
void init() noexcept override
Should be called right after allocating and constructing a widget.
Definition selection_widget.hpp:55
observable< bool > enabled
The widget is enabled.
Definition widget.hpp:105
virtual std::shared_ptr< widget > find_next_widget(std::shared_ptr< widget > const ¤t_keyboard_widget, keyboard_focus_group group, keyboard_focus_direction direction) const noexcept
Find the next widget that handles keyboard focus.
aarectangle rectangle() const noexcept
Get the rectangle in local coordinates.
Definition widget.hpp:342
virtual bool handle_event(command command) noexcept
Handle command.
gui_window & window
Convenient reference to the Window.
Definition widget.hpp:101
virtual float base_line() const noexcept
Return the base-line where the text should be located.
Definition widget.hpp:351
abstract_container_widget const & parent() const noexcept
Get a reference to the parent.
int semantic_layer() const noexcept
The semantic layer of the widget.
Definition widget.hpp:189