From b6ddd709898492ed8f303c97e66a9700f77e8072 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 29 Mar 2022 18:14:43 +0300 Subject: [PATCH] Implement ui element hinting support --- libs/ui/include/psemek/ui/controller.hpp | 4 ++ libs/ui/include/psemek/ui/element.hpp | 7 +++ libs/ui/source/controller.cpp | 70 +++++++++++++++++++++++- libs/ui/source/element.cpp | 6 ++ 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/libs/ui/include/psemek/ui/controller.hpp b/libs/ui/include/psemek/ui/controller.hpp index 4e412608..90c44cd1 100644 --- a/libs/ui/include/psemek/ui/controller.hpp +++ b/libs/ui/include/psemek/ui/controller.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace psemek::ui { @@ -18,6 +19,9 @@ namespace psemek::ui std::shared_ptr set_root(std::shared_ptr r); element * root(); + void set_hint_delay(float seconds); + void on_hint(util::function callback); + void reshape(geom::box const & shape); bool event(mouse_move const & e); diff --git a/libs/ui/include/psemek/ui/element.hpp b/libs/ui/include/psemek/ui/element.hpp index c26ed04a..4999730a 100644 --- a/libs/ui/include/psemek/ui/element.hpp +++ b/libs/ui/include/psemek/ui/element.hpp @@ -11,6 +11,8 @@ #include #include +#include +#include namespace psemek::ui { @@ -59,6 +61,9 @@ namespace psemek::ui virtual void style_updated(); virtual void own_style_updated(); + virtual std::optional const & hint() const { return hint_; } + virtual std::optional set_hint(std::optional hint); + virtual void update(float /* dt */) {} virtual void draw(painter & p) const = 0; @@ -88,6 +93,8 @@ namespace psemek::ui mutable std::vector> delayed_callbacks_; + std::optional hint_; + void post_delayed_callbacks() const; }; diff --git a/libs/ui/source/controller.cpp b/libs/ui/source/controller.cpp index fa274538..54b011e7 100644 --- a/libs/ui/source/controller.cpp +++ b/libs/ui/source/controller.cpp @@ -57,12 +57,21 @@ namespace psemek::ui painter_impl painter; std::shared_ptr root; std::optional> mouse; + float hint_delay = 0.f; + float hint_timer = 0.f; + util::function on_hint; + element * hinted_element = nullptr; + bool hint_called = false; impl(async::event_loop * loop); template bool event(E const & e); + std::pair event(mouse_move const & e); + + element * hint() const; + void update(float dt); }; @@ -96,6 +105,33 @@ namespace psemek::ui return false; } + std::pair controller::impl::event(mouse_move const & e) + { + element * hinted = nullptr; + auto m = geom::cast(e.position); + auto visitor = util::recursive([&](auto && self, element * elem) -> bool { + if (elem->hidden()) return false; + + auto cs = elem->children(); + for (auto it = cs.rbegin(); it != cs.rend(); ++it) + if (*it && self(*it)) return true; + + if (elem->enabled()) + { + if (elem->hint() && elem->shape().contains(m) && !hinted) + hinted = elem; + if (elem->on_event(e)) + return true; + } + return false; + }); + + bool result = false; + if (root) + result = visitor(root.get()); + return {result, hinted}; + } + void controller::impl::update(float dt) { auto visitor = util::recursive([dt](auto && self, element * elem) -> void { @@ -140,6 +176,16 @@ namespace psemek::ui return impl().root->root.get(); } + void controller::set_hint_delay(float seconds) + { + impl().hint_delay = seconds; + } + + void controller::on_hint(util::function callback) + { + impl().on_hint = std::move(callback); + } + void controller::reshape(geom::box const & shape) { impl().root->reshape(shape); @@ -148,7 +194,18 @@ namespace psemek::ui bool controller::event(mouse_move const & e) { impl().mouse = e.position; - return impl().event(e); + auto [ result, new_hinted_element ] = impl().event(e); + + if (new_hinted_element != impl().hinted_element) + { + if (impl().hint_called && impl().on_hint) + impl().on_hint(nullptr); + impl().hint_timer = 0.f; + impl().hint_called = false; + } + impl().hinted_element = new_hinted_element; + + return result; } bool controller::event(mouse_click const & e) @@ -169,6 +226,17 @@ namespace psemek::ui void controller::update(float dt) { impl().update(dt); + + if (impl().hinted_element && !impl().hint_called) + { + impl().hint_timer += dt; + if (impl().hint_timer >= impl().hint_delay) + { + if (impl().on_hint) + impl().on_hint(impl().hinted_element); + impl().hint_called = true; + } + } } void controller::render(gfx::render_target const & rt) diff --git a/libs/ui/source/element.cpp b/libs/ui/source/element.cpp index 1d5680c6..d1d1264d 100644 --- a/libs/ui/source/element.cpp +++ b/libs/ui/source/element.cpp @@ -137,6 +137,12 @@ namespace psemek::ui return merged_own_style_; } + std::optional element::set_hint(std::optional hint) + { + std::swap(hint_, hint); + return hint; + } + element::~element() { set_style(nullptr);