diff --git a/libs/ui/include/psemek/ui/selector.hpp b/libs/ui/include/psemek/ui/selector.hpp index a1f6e316..5c753c5b 100644 --- a/libs/ui/include/psemek/ui/selector.hpp +++ b/libs/ui/include/psemek/ui/selector.hpp @@ -26,8 +26,9 @@ namespace psemek::ui virtual std::size_t size() const; virtual void resize(std::size_t size); - virtual void add(std::shared_ptr child); - virtual std::shared_ptr get(std::size_t index); + virtual void add(std::shared_ptr child, bool submenu = false); + virtual std::shared_ptr get(std::size_t index) const; + virtual bool submenu(std::size_t index) const; virtual void clear(); virtual std::optional selected() const { return selected_; } @@ -35,6 +36,9 @@ namespace psemek::ui protected: + // extra width for each element with a submenu + virtual int submenu_extra() const { return 0; } + // extra distance between successive elements virtual int y_extra() const { return 0; } @@ -43,6 +47,7 @@ namespace psemek::ui private: container_impl container_; box_shape shape_; + std::vector submenu_; std::optional selected_; diff --git a/libs/ui/source/selector.cpp b/libs/ui/source/selector.cpp index 1adda650..5926142b 100644 --- a/libs/ui/source/selector.cpp +++ b/libs/ui/source/selector.cpp @@ -52,22 +52,27 @@ namespace psemek::ui geom::box selector::size_constraints() const { + auto st = merged_own_style(); + auto x_range = geom::interval::full(); float y_sum = 0.f; - for (auto c : children()) + for (std::size_t i = 0; i < size(); ++i) { + auto c = get(i); if (!c) continue; auto sc = c->size_constraints(); + + if (submenu_[i]) + sc[0] += *st->scale * submenu_extra(); + 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(); @@ -81,16 +86,21 @@ namespace psemek::ui { auto const p = geom::cast(e.position); - selected_ = std::nullopt; + std::optional new_selected; for (std::size_t i = 0; i < child_boxes_.size(); ++i) { if (geom::contains(child_boxes_[i], p)) { - selected_ = i; + new_selected = i; break; } } + if (new_selected && new_selected != selected_ && submenu_[*new_selected] && callback_) + post([cb = callback_, i = *new_selected]{ cb(i); }); + + selected_ = new_selected; + return true; } @@ -98,7 +108,7 @@ namespace psemek::ui { if (e.down && e.button == mouse_button::left && selected_) { - if (callback_) + if (!submenu_[*selected_] && callback_) post([cb = callback_, i = *selected_]{ cb(i); }); return true; } @@ -114,21 +124,31 @@ namespace psemek::ui void selector::resize(std::size_t size) { container_.resize(size); + submenu_.resize(size); } - void selector::add(std::shared_ptr child) + void selector::add(std::shared_ptr child, bool submenu) { - container_.add(child); + std::size_t index = container_.add(child); + if (index >= submenu_.size()) + submenu_.resize(index + 1); + submenu_[index] = submenu; } - std::shared_ptr selector::get(std::size_t index) + std::shared_ptr selector::get(std::size_t index) const { return container_.get(index); } + bool selector::submenu(std::size_t index) const + { + return submenu_[index]; + } + void selector::clear() { container_.clear(); + submenu_.clear(); } void selector::on_selected(std::function callback)