psemek/libs/ui_legacy/source/element.cpp

274 lines
4.8 KiB
C++

#include <psemek/ui/element.hpp>
#include <psemek/util/recursive.hpp>
#include <psemek/util/range.hpp>
#include <psemek/log/log.hpp>
#include <SDL2/SDL_keyboard.h>
namespace psemek::ui
{
void element::set_parent(element * parent)
{
assert(!parent_ || !parent);
parent_ = parent;
if (loop())
post_delayed_callbacks();
style_updated();
}
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();
}
math::box<float, 2> element::events_bbox() const
{
return math::box<float, 2>::full();
}
void element::reshape()
{
reshape(shape().bbox());
}
math::box<float, 2> element::size_constraints() const
{
static float const inf = std::numeric_limits<float>::infinity();
return {{{0.f, inf}, {0.f, inf}}};
}
math::interval<float> element::width_constraints(float) const
{
return size_constraints()[0];
}
math::interval<float> element::height_constraints(float) const
{
return size_constraints()[1];
}
std::shared_ptr<style> element::set_style(std::shared_ptr<struct style> st)
{
if (style_)
style_->use_as_style.erase(this);
std::swap(st, style_);
if (style_)
style_->use_as_style.insert(this);
style_updated();
return st;
}
std::shared_ptr<struct style> element::set_own_style(std::shared_ptr<struct 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);
own_style_updated();
return st;
}
void element::style_updated() const
{
merged_style_ = nullptr;
own_style_updated();
for (auto c : children())
if (c) c->style_updated();
}
void element::own_style_updated() const
{
merged_own_style_ = nullptr;
}
std::shared_ptr<struct style> 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> 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_;
}
bool element::in_text_input()
{
return SDL_IsTextInputActive() == SDL_TRUE;
}
void element::start_text_input()
{
SDL_StartTextInput();
}
void element::stop_text_input()
{
SDL_StopTextInput();
}
std::optional<std::string> element::set_id(std::optional<std::string> id)
{
std::swap(id_, id);
return id;
}
std::optional<std::string> element::set_hint(std::optional<std::string> hint)
{
std::swap(hint_, hint);
return hint;
}
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()
{
if (auto p = parent())
{
p->post_reshape();
return;
}
if (reshape_posted_)
return;
if (!loop_)
return;
reshape_posted_ = true;
auto weak_self = weak_from_this();
post([weak_self]{
if (auto self = weak_self.lock())
{
self->reshape_posted_ = false;
if (!self->parent())
self->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();
}
element * find_element_by_id(element * root, std::string_view id)
{
auto visitor = util::recursive([&](auto && self, element * e) -> element *
{
if (!e) return nullptr;
if (e->id() && e->id() == id)
return e;
for (auto c : util::reversed(e->children()))
if (auto r = self(c))
return r;
return nullptr;
});
return visitor(root);
}
void send_fake_mouse_move_event(element * e, bool mouseover)
{
auto const box = e->shape().bbox();
if (mouseover)
e->on_event(mouse_move{math::cast<int>(box.center())});
else
e->on_event(mouse_move{math::cast<int>(box.corner(0.f, 0.f) - math::vector{1000.f, 1000.f})});
}
}