82 hi_assert_not_null(delegate);
83 delegate->deinit(*
this);
94 hi_assert_not_null(this->delegate);
98 _off_label_widget = std::make_unique<label_widget>(
this,
off_label,
alignment, semantic_text_style::placeholder);
100 _overlay_widget = std::make_unique<overlay_widget>(
this);
105 _off_label_cbt = this->off_label.subscribe([&](
auto...) {
106 ++global_counter<
"selection_widget:off_label:constrain">;
110 _delegate_cbt = this->delegate->subscribe([&] {
111 _notification_from_delegate =
true;
112 ++global_counter<
"selection_widget:delegate:constrain">;
116 this->delegate->init(*
this);
207 co_yield *_overlay_widget;
208 co_yield *_current_label_widget;
209 co_yield *_off_label_widget;
214 hi_assert_not_null(_off_label_widget);
215 hi_assert_not_null(_current_label_widget);
216 hi_assert_not_null(_overlay_widget);
218 if (_notification_from_delegate.
exchange(
false)) {
219 repopulate_options();
223 _off_label_constraints = _off_label_widget->update_constraints();
224 _current_label_constraints = _current_label_widget->update_constraints();
225 _overlay_constraints = _overlay_widget->update_constraints();
227 hilet
extra_size = extent2{theme().size() + theme().margin<
float>() * 2.0f, theme().margin<
float>() * 2.0f};
232 _scroll_widget->
minimum.copy()->height() = theme().size();
235 r.preferred.width() =
std::max(r.preferred.width(), _overlay_constraints.preferred.
width() +
extra_size.width());
237 r.margins = theme().margin();
238 r.padding = theme().margin();
239 r.alignment = resolve(*
alignment, os_settings::left_to_right());
240 hi_axiom(r.holds_invariant());
244 void set_layout(widget_layout
const&
context)
noexcept override
247 if (os_settings::left_to_right()) {
248 _left_box_rectangle = aarectangle{0.0f, 0.0f, theme().size(),
context.height()};
252 _left_box_rectangle.right() + theme().margin<
float>(),
254 context.width() - _left_box_rectangle.width() - theme().margin<
float>() * 2.0f,
256 _off_label_shape = box_shape{_off_label_constraints,
option_rectangle, theme().baseline_adjustment()};
257 _current_label_shape = box_shape{_off_label_constraints,
option_rectangle, theme().baseline_adjustment()};
260 _left_box_rectangle = aarectangle{
context.width() - theme().size(), 0.0f, theme().size(),
context.height()};
264 theme().margin<
float>(),
266 context.width() - _left_box_rectangle.width() - theme().margin<
float>() * 2.0f,
268 _off_label_shape = box_shape{_off_label_constraints,
option_rectangle, theme().baseline_adjustment()};
269 _current_label_shape = box_shape{_off_label_constraints,
option_rectangle, theme().baseline_adjustment()};
272 _chevrons_glyph =
find_glyph(elusive_icon::ChevronUp);
273 hilet
chevrons_glyph_bbox = _chevrons_glyph.get_metrics().bounding_rectangle * theme().icon_size();
282 context.width() - theme().size(), _overlay_constraints.minimum.
width(), _overlay_constraints.maximum.
width());
288 _overlay_shape = box_shape{_overlay_constraints,
overlay_rectangle, theme().baseline_adjustment()};
289 _overlay_widget->set_layout(
context.transform(_overlay_shape, 20.0f));
291 _off_label_widget->set_layout(
context.transform(_off_label_shape));
292 _current_label_widget->set_layout(
context.transform(_current_label_shape));
295 void draw(draw_context
const&
context)
noexcept override
303 _off_label_widget->draw(
context);
304 _current_label_widget->draw(
context);
308 _overlay_widget->draw(
context);
312 bool handle_event(gui_event
const&
event)
noexcept override
314 switch (
event.type()) {
315 case gui_event_type::mouse_up:
317 return handle_event(gui_event_type::gui_activate);
321 case gui_event_type::gui_activate_next:
324 case gui_event_type::gui_activate:
330 ++global_counter<
"selection_widget:gui_activate:relayout">;
334 case gui_event_type::gui_cancel:
338 ++global_counter<
"selection_widget:gui_cancel:relayout">;
348 [[
nodiscard]] hitbox hitbox_test(point2 position)
const noexcept override
350 hi_axiom(loop::main().on_thread());
353 auto r = _overlay_widget->hitbox_test_from_parent(position);
355 if (
layout().contains(position)) {
356 r =
std::max(r, hitbox{
id, _layout.elevation, _has_options ? hitbox_type::button : hitbox_type::_default});
365 [[
nodiscard]]
bool accepts_keyboard_focus(keyboard_focus_group group)
const noexcept override
367 hi_axiom(loop::main().on_thread());
373 hi_axiom(loop::main().on_thread());
376 return theme().color(semantic_color::accent);
378 return super::focus_color();
384 notifier<>::callback_token _delegate_cbt;
388 box_constraints _current_label_constraints;
389 box_shape _current_label_shape;
392 box_constraints _off_label_constraints;
393 box_shape _off_label_shape;
395 aarectangle _left_box_rectangle;
397 font_book::font_glyph_type _chevrons_glyph;
398 aarectangle _chevrons_rectangle;
400 bool _selecting =
false;
401 bool _has_options =
false;
404 box_constraints _overlay_constraints;
405 box_shape _overlay_shape;
410 decltype(
off_label)::callback_token _off_label_cbt;
415 void set_attributes(label_widget_attribute
auto&& first, label_widget_attribute
auto&&...
rest)
noexcept
424 hi_static_no_default();
427 set_attributes(hi_forward(
rest)...);
432 hi_axiom(loop::main().on_thread());
434 if (
ssize(_menu_button_widgets) != 0) {
435 return _menu_button_widgets.
front();
443 hi_axiom(loop::main().on_thread());
445 for (hilet& button : _menu_button_widgets) {
455 hi_axiom(loop::main().on_thread());
471 hi_axiom(loop::main().on_thread());
479 hi_axiom(loop::main().on_thread());
480 hi_assert_not_null(delegate);
482 _column_widget->clear();
483 _menu_button_widgets.
clear();
484 _menu_button_tokens.
clear();
486 auto [options,
selected] = delegate->options_and_selected(*
this);
488 _has_options = size(options) > 0;
492 for (hilet& label : options) {
497 for (hilet& label : options) {
502 hi_assert_not_null(delegate);
503 delegate->set_selected(*this, index);
506 callback_flags::main));
519 _current_label_widget->label = options[
selected];
524 void draw_outline(draw_context
const&
context)
noexcept
531 theme().border_width(),
533 theme().rounding_radius());
536 void draw_left_box(draw_context
const&
context)
noexcept
538 hilet corner_radii = os_settings::left_to_right() ?
540 hi::corner_radii(0.0f, theme().rounding_radius<
float>(), 0.0f, theme().rounding_radius<
float>());
541 context.draw_box(
layout(), translate_z(0.1f) * _left_box_rectangle, focus_color(), corner_radii);
544 void draw_chevrons(draw_context
const&
context)
noexcept
546 context.draw_glyph(
layout(), translate_z(0.2f) * _chevrons_rectangle, _chevrons_glyph, label_color());