diff --git a/libs/ui/include/psemek/ui/element.hpp b/libs/ui/include/psemek/ui/element.hpp index 71f547ff..7433b5fc 100644 --- a/libs/ui/include/psemek/ui/element.hpp +++ b/libs/ui/include/psemek/ui/element.hpp @@ -38,6 +38,8 @@ namespace psemek::ui virtual bool on_event(key_press const &) { return false; } virtual bool on_event(text_input const &) { return false; } + virtual bool focused() const { return false; } + virtual struct shape const & shape() const = 0; virtual void reshape(geom::box const & bbox) = 0; virtual void reshape(); diff --git a/libs/ui/source/controller.cpp b/libs/ui/source/controller.cpp index a4640f26..abddd714 100644 --- a/libs/ui/source/controller.cpp +++ b/libs/ui/source/controller.cpp @@ -56,6 +56,7 @@ namespace psemek::ui async::event_loop * loop; painter_impl painter; std::shared_ptr root; + element * focused = nullptr; std::optional> mouse; float hint_delay = 0.f; float hint_timer = 0.f; @@ -66,9 +67,9 @@ namespace psemek::ui impl(async::event_loop * loop); template - bool event(E const & e); + element * event(E const & e); - std::pair event(mouse_move const & e); + element * hinted(); element * hint() const; @@ -87,49 +88,69 @@ namespace psemek::ui } template - bool controller::impl::event(E const & e) + element * controller::impl::event(E const & e) { - auto visitor = util::recursive([&](auto && self, element * elem) -> bool { - if (elem->hidden()) return false; + auto visitor = util::recursive([&](auto && self, element * elem) -> element * { + if (elem->hidden()) return nullptr; auto cs = elem->children(); for (auto it = cs.rbegin(); it != cs.rend(); ++it) - if (*it && self(*it)) return true; + if (*it) + if (auto p = self(*it)) + return p; - if (elem->enabled() && elem->on_event(e)) return true; - return false; + if (elem->enabled() && elem->on_event(e)) return elem; + return nullptr; + }); + + if (focused) + { + if (focused->focused()) + { + auto result = focused->on_event(e); + if (!focused->focused()) + focused = nullptr; + if (result) + return focused; + } + else + focused = nullptr; + } + + if (root) + if (auto result = visitor(root.get())) + { + if (!focused && result->focused()) + focused = result; + } + + return nullptr; + } + + element * controller::impl::hinted() + { + if (!mouse) + return nullptr; + + auto m = geom::cast(*mouse); + auto visitor = util::recursive([&](auto && self, element * elem) -> element * { + if (elem->hidden()) return nullptr; + + auto cs = elem->children(); + for (auto it = cs.rbegin(); it != cs.rend(); ++it) + if (*it) + if (auto p = self(*it)) + return p; + + if (elem->enabled() && elem->hint() && elem->shape().contains(m)) + return elem; + + return nullptr; }); if (root) return visitor(root.get()); - 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}; + return nullptr; } void controller::impl::update(float dt) @@ -194,7 +215,9 @@ namespace psemek::ui bool controller::event(mouse_move const & e) { impl().mouse = e.position; - auto [ result, new_hinted_element ] = impl().event(e); + auto result = impl().event(e); + + auto new_hinted_element = impl().hinted(); if (new_hinted_element != impl().hinted_element) {