Implement ui element hinting support
This commit is contained in:
parent
c03689c5b4
commit
b6ddd70989
4 changed files with 86 additions and 1 deletions
|
|
@ -4,6 +4,7 @@
|
||||||
#include <psemek/ui/font.hpp>
|
#include <psemek/ui/font.hpp>
|
||||||
#include <psemek/gfx/render_target.hpp>
|
#include <psemek/gfx/render_target.hpp>
|
||||||
#include <psemek/util/pimpl.hpp>
|
#include <psemek/util/pimpl.hpp>
|
||||||
|
#include <psemek/util/function.hpp>
|
||||||
|
|
||||||
namespace psemek::ui
|
namespace psemek::ui
|
||||||
{
|
{
|
||||||
|
|
@ -18,6 +19,9 @@ namespace psemek::ui
|
||||||
std::shared_ptr<element> set_root(std::shared_ptr<element> r);
|
std::shared_ptr<element> set_root(std::shared_ptr<element> r);
|
||||||
element * root();
|
element * root();
|
||||||
|
|
||||||
|
void set_hint_delay(float seconds);
|
||||||
|
void on_hint(util::function<void(element *)> callback);
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & shape);
|
void reshape(geom::box<float, 2> const & shape);
|
||||||
|
|
||||||
bool event(mouse_move const & e);
|
bool event(mouse_move const & e);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
#include <psemek/util/function.hpp>
|
#include <psemek/util/function.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace psemek::ui
|
namespace psemek::ui
|
||||||
{
|
{
|
||||||
|
|
@ -59,6 +61,9 @@ namespace psemek::ui
|
||||||
virtual void style_updated();
|
virtual void style_updated();
|
||||||
virtual void own_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 update(float /* dt */) {}
|
||||||
|
|
||||||
virtual void draw(painter & p) const = 0;
|
virtual void draw(painter & p) const = 0;
|
||||||
|
|
@ -88,6 +93,8 @@ namespace psemek::ui
|
||||||
|
|
||||||
mutable std::vector<util::function<void()>> delayed_callbacks_;
|
mutable std::vector<util::function<void()>> delayed_callbacks_;
|
||||||
|
|
||||||
|
std::optional<std::string> hint_;
|
||||||
|
|
||||||
void post_delayed_callbacks() const;
|
void post_delayed_callbacks() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,12 +57,21 @@ namespace psemek::ui
|
||||||
painter_impl painter;
|
painter_impl painter;
|
||||||
std::shared_ptr<root_proxy> root;
|
std::shared_ptr<root_proxy> root;
|
||||||
std::optional<geom::point<int, 2>> mouse;
|
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);
|
impl(async::event_loop * loop);
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
bool event(E const & e);
|
bool event(E const & e);
|
||||||
|
|
||||||
|
std::pair<bool, element *> event(mouse_move const & e);
|
||||||
|
|
||||||
|
element * hint() const;
|
||||||
|
|
||||||
void update(float dt);
|
void update(float dt);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -96,6 +105,33 @@ namespace psemek::ui
|
||||||
return false;
|
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)
|
void controller::impl::update(float dt)
|
||||||
{
|
{
|
||||||
auto visitor = util::recursive([dt](auto && self, element * elem) -> void {
|
auto visitor = util::recursive([dt](auto && self, element * elem) -> void {
|
||||||
|
|
@ -140,6 +176,16 @@ namespace psemek::ui
|
||||||
return impl().root->root.get();
|
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)
|
void controller::reshape(geom::box<float, 2> const & shape)
|
||||||
{
|
{
|
||||||
impl().root->reshape(shape);
|
impl().root->reshape(shape);
|
||||||
|
|
@ -148,7 +194,18 @@ namespace psemek::ui
|
||||||
bool controller::event(mouse_move const & e)
|
bool controller::event(mouse_move const & e)
|
||||||
{
|
{
|
||||||
impl().mouse = e.position;
|
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)
|
bool controller::event(mouse_click const & e)
|
||||||
|
|
@ -169,6 +226,17 @@ namespace psemek::ui
|
||||||
void controller::update(float dt)
|
void controller::update(float dt)
|
||||||
{
|
{
|
||||||
impl().update(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)
|
void controller::render(gfx::render_target const & rt)
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,12 @@ namespace psemek::ui
|
||||||
return merged_own_style_;
|
return merged_own_style_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> element::set_hint(std::optional<std::string> hint)
|
||||||
|
{
|
||||||
|
std::swap(hint_, hint);
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
|
||||||
element::~element()
|
element::~element()
|
||||||
{
|
{
|
||||||
set_style(nullptr);
|
set_style(nullptr);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue