Support focused ui elements

This commit is contained in:
Nikita Lisitsa 2022-04-08 14:49:09 +03:00
parent c06a465f51
commit bd6d0f9f93
2 changed files with 62 additions and 37 deletions

View file

@ -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<float, 2> const & bbox) = 0;
virtual void reshape();

View file

@ -56,6 +56,7 @@ namespace psemek::ui
async::event_loop * loop;
painter_impl painter;
std::shared_ptr<root_proxy> root;
element * focused = nullptr;
std::optional<geom::point<int, 2>> mouse;
float hint_delay = 0.f;
float hint_timer = 0.f;
@ -66,9 +67,9 @@ namespace psemek::ui
impl(async::event_loop * loop);
template <typename E>
bool event(E const & e);
element * event(E const & e);
std::pair<bool, element *> event(mouse_move const & e);
element * hinted();
element * hint() const;
@ -87,49 +88,69 @@ namespace psemek::ui
}
template <typename E>
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<float>(*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<bool, element *> controller::impl::event(mouse_move const & e)
{
element * hinted = nullptr;
auto m = geom::cast<float>(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)
{