Implement ui element hinting support

This commit is contained in:
Nikita Lisitsa 2022-03-29 18:14:43 +03:00
parent c03689c5b4
commit b6ddd70989
4 changed files with 86 additions and 1 deletions

View file

@ -4,6 +4,7 @@
#include <psemek/ui/font.hpp>
#include <psemek/gfx/render_target.hpp>
#include <psemek/util/pimpl.hpp>
#include <psemek/util/function.hpp>
namespace psemek::ui
{
@ -18,6 +19,9 @@ namespace psemek::ui
std::shared_ptr<element> set_root(std::shared_ptr<element> r);
element * root();
void set_hint_delay(float seconds);
void on_hint(util::function<void(element *)> callback);
void reshape(geom::box<float, 2> const & shape);
bool event(mouse_move const & e);

View file

@ -11,6 +11,8 @@
#include <psemek/util/function.hpp>
#include <memory>
#include <optional>
#include <string>
namespace psemek::ui
{
@ -59,6 +61,9 @@ namespace psemek::ui
virtual void style_updated();
virtual void own_style_updated();
virtual std::optional<std::string> const & hint() const { return hint_; }
virtual std::optional<std::string> set_hint(std::optional<std::string> hint);
virtual void update(float /* dt */) {}
virtual void draw(painter & p) const = 0;
@ -88,6 +93,8 @@ namespace psemek::ui
mutable std::vector<util::function<void()>> delayed_callbacks_;
std::optional<std::string> hint_;
void post_delayed_callbacks() const;
};

View file

@ -57,12 +57,21 @@ namespace psemek::ui
painter_impl painter;
std::shared_ptr<root_proxy> root;
std::optional<geom::point<int, 2>> mouse;
float hint_delay = 0.f;
float hint_timer = 0.f;
util::function<void(element *)> on_hint;
element * hinted_element = nullptr;
bool hint_called = false;
impl(async::event_loop * loop);
template <typename E>
bool event(E const & e);
std::pair<bool, element *> event(mouse_move const & e);
element * hint() const;
void update(float dt);
};
@ -96,6 +105,33 @@ namespace psemek::ui
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};
}
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<void(element *)> callback)
{
impl().on_hint = std::move(callback);
}
void controller::reshape(geom::box<float, 2> 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)

View file

@ -137,6 +137,12 @@ namespace psemek::ui
return merged_own_style_;
}
std::optional<std::string> element::set_hint(std::optional<std::string> hint)
{
std::swap(hint_, hint);
return hint;
}
element::~element()
{
set_style(nullptr);