psemek/libs/ui/source/element.cpp

205 lines
3.8 KiB
C++

#include <psemek/ui/element.hpp>
#include <psemek/util/recursive.hpp>
#include <psemek/log/log.hpp>
namespace psemek::ui
{
void element::set_parent(element * parent)
{
if (!parent_ && parent && reshape_posted_)
parent->post_reshape();
parent_ = parent;
if (loop())
post_delayed_callbacks();
}
element * element::root()
{
element * r = this;
while (r->parent()) r = r->parent();
return r;
}
element const * element::root() const
{
element const * r = this;
while (r->parent()) r = r->parent();
return r;
}
async::event_loop * element::loop() const
{
element const * e = this;
while (!e->loop_ && e->parent()) e = e->parent();
return e->loop_;
}
void element::set_loop(async::event_loop * loop)
{
loop_ = loop;
post_delayed_callbacks();
}
void element::reshape()
{
reshape(shape().bbox());
}
geom::box<float, 2> element::size_constraints() const
{
static float const inf = std::numeric_limits<float>::infinity();
return {{{0.f, inf}, {0.f, inf}}};
}
std::shared_ptr<style const> element::set_style(std::shared_ptr<struct style const> st)
{
if (style_ != st)
{
if (style_)
style_->use_as_style.erase(this);
std::swap(st, style_);
if (style_)
style_->use_as_style.insert(this);
}
auto visitor = util::recursive([](auto && self, element * e) -> void {
e->merged_style_ = nullptr;
e->merged_own_style_ = nullptr;
for (auto c : e->children())
if (c) self(c);
});
visitor(this);
return st;
}
std::shared_ptr<struct style const> element::set_own_style(std::shared_ptr<struct style const> st)
{
if (style_ != st)
{
if (style_)
style_->use_as_own_style.erase(this);
std::swap(st, own_style_);
if (style_)
style_->use_as_own_style.insert(this);
}
auto visitor = util::recursive([](auto && self, element * e) -> void {
e->merged_style_ = nullptr;
e->merged_own_style_ = nullptr;
for (auto c : e->children())
if (c) self(c);
});
visitor(this);
return st;
}
std::shared_ptr<struct style const> element::merged_style() const
{
if (!merged_style_)
{
ui::style result;
if (style_)
merge(result, *style_);
if (parent())
merge(result, *parent()->merged_style());
merge(result, default_style());
merged_style_ = std::make_shared<ui::style>(result);
}
return merged_style_;
}
std::shared_ptr<struct style const> element::merged_own_style() const
{
if (!merged_own_style_)
{
ui::style result;
if (own_style_)
merge(result, *own_style_);
merge(result, *merged_style());
merged_own_style_ = std::make_shared<ui::style>(result);
}
return merged_own_style_;
}
element::~element()
{
set_style(nullptr);
set_own_style(nullptr);
}
void element::post(util::function<void()> f) const
{
auto l = loop();
if (l)
l->post(std::move(f));
else
delayed_callbacks_.push_back(std::move(f));
}
void element::post_reshape()
{
auto r = root();
if (r->reshape_posted_)
return;
r->reshape_posted_ = true;
auto weak_root = r->weak_from_this();
post([weak_root]{
if (auto root = weak_root.lock())
{
root->reshape_posted_ = false;
if (!root->parent())
root->reshape();
}
});
}
void element::release_children()
{
for (auto c : children())
if (c) c->set_parent(nullptr);
}
void element::post_delayed_callbacks() const
{
auto l = loop();
assert(l);
for (auto & c : delayed_callbacks_)
l->post(std::move(c));
delayed_callbacks_.clear();
for (auto const & c : children())
if (c)
c->post_delayed_callbacks();
}
void send_fake_mouse_move_event(element * e, bool mouseover)
{
auto const box = e->shape().bbox();
if (mouseover)
e->on_event(mouse_move{geom::cast<int>(box.center())});
else
e->on_event(mouse_move{geom::cast<int>(box.corner(0.f, 0.f) - geom::vector{1000.f, 1000.f})});
}
}