#include #include #include #include #include #include namespace psemek::ui { selector::selector() : container_(this) {} selector::~selector() { release_children(); } void selector::reshape(geom::box const & box) { shape_.box = box; child_boxes_.clear(); auto st = merged_own_style(); geom::interval x_range = geom::shrink(box[0], (*st->inner_margin)[0]); float y = box[1].min; for (auto c : children()) { float y_start = y; y += (*st->inner_margin)[1]; if (c) { auto sc = c->size_constraints(); c->reshape({{x_range, {y, y + sc[1].min}}}); y += sc[1].min; } y += (*st->inner_margin)[1]; child_boxes_.push_back({{box[0], {y_start, y}}}); y += y_extra() * *st->scale; } } geom::box selector::size_constraints() const { auto x_range = geom::interval::full(); float y_sum = 0.f; for (auto c : children()) { if (!c) continue; auto sc = c->size_constraints(); x_range &= sc[0]; y_sum += sc[1].min; } auto st = merged_own_style(); x_range += (*st->inner_margin)[0] * 2.f; y_sum += (*st->inner_margin)[1] * 2.f * container_.size(); if (!container_.empty()) y_sum += *st->scale * y_extra() * static_cast(container_.size() - 1); return {{x_range, {y_sum, y_sum}}}; } bool selector::on_event(mouse_move const & e) { auto const p = geom::cast(e.position); selected_ = std::nullopt; for (std::size_t i = 0; i < child_boxes_.size(); ++i) { if (geom::contains(child_boxes_[i], p)) { selected_ = i; break; } } return true; } bool selector::on_event(mouse_click const & e) { if (e.down && e.button == mouse_button::left && selected_) { if (callback_) post([cb = callback_, i = *selected_]{ cb(i); }); return true; } return false; } std::size_t selector::size() const { return container_.size(); } void selector::resize(std::size_t size) { container_.resize(size); } void selector::add(std::shared_ptr child) { container_.add(child); } std::shared_ptr selector::get(std::size_t index) { return container_.get(index); } void selector::clear() { container_.clear(); } void selector::on_selected(std::function callback) { callback_ = std::move(callback); } bool spawn(element * root, std::shared_ptr selector, geom::point const & position, std::function on_selected) { ui::screen * screen = find_last_parent_of_type(root); if (!screen) return false; auto event_interceptor = std::make_shared(); auto positioner = std::make_shared(); event_interceptor->set_child(positioner); positioner->set_child(selector); auto close = [event_interceptor = event_interceptor.get(), selector = selector.get()]{ selector->on_selected([](std::size_t){}); auto p = dynamic_cast(event_interceptor->parent()); if (p) p->remove_child(event_interceptor); }; selector->on_selected([close, cb = std::move(on_selected)](std::size_t index){ close(); cb(index); }); event_interceptor->on_mouse_click([close](ui::mouse_click const & e) -> bool { if (e.down && (e.button == ui::mouse_button::right || e.button == ui::mouse_button::left)) { close(); return true; } return false; }); event_interceptor->on_key_press([close](key_press const & e) -> bool { if (e.down && e.key == SDLK_ESCAPE) { close(); return true; } return false; }); send_fake_mouse_move_event(event_interceptor.get(), true); positioner->set_position(geom::cast(position), positioner::x_align::left, positioner::y_align::top); screen->add_child(event_interceptor, screen::x_policy::fill, screen::y_policy::fill); return true; } }