Implement cached style merging

This commit is contained in:
Nikita Lisitsa 2021-02-27 20:21:50 +03:00
parent d0a117adf4
commit b37d2d25c7
8 changed files with 85 additions and 29 deletions

View file

@ -39,7 +39,7 @@ struct ui_example
: app("UI example", 1) : app("UI example", 1)
, ui_controller(&loop) , ui_controller(&loop)
{ {
auto style = std::make_shared<ui::style>(ui::default_style()); auto style = std::make_shared<ui::style>();
style->font = ui::make_default_9x12_font(); style->font = ui::make_default_9x12_font();
style->text_scale = 2; style->text_scale = 2;

View file

@ -44,8 +44,9 @@ namespace psemek::ui
virtual void enable() { set_enabled(true); } virtual void enable() { set_enabled(true); }
virtual void disable() { set_enabled(false); } virtual void disable() { set_enabled(false); }
virtual std::shared_ptr<struct style const> style() const; virtual std::shared_ptr<struct style const> style() const { return style_; }
virtual std::shared_ptr<struct style const> set_style(std::shared_ptr<struct style const> st); virtual std::shared_ptr<struct style const> set_style(std::shared_ptr<struct style const> st);
virtual std::shared_ptr<struct style const> merged_style() const;
virtual void draw(painter & p) const = 0; virtual void draw(painter & p) const = 0;
@ -59,6 +60,7 @@ namespace psemek::ui
async::executor * loop_ = nullptr; async::executor * loop_ = nullptr;
bool enabled_ = true; bool enabled_ = true;
std::shared_ptr<struct style const> style_; std::shared_ptr<struct style const> style_;
mutable std::shared_ptr<struct style const> merged_style_;
}; };
} }

View file

@ -31,6 +31,8 @@ namespace psemek::ui
std::shared_ptr<struct font> font; std::shared_ptr<struct font> font;
}; };
void merge(style & dst, style const & src);
style default_style(); style default_style();
} }

View file

@ -37,9 +37,9 @@ namespace psemek::ui
void reshape(geom::box<float, 2> const & bbox) override void reshape(geom::box<float, 2> const & bbox) override
{ {
shape_.box = bbox; shape_.box = bbox;
auto s = style(); auto st = merged_style();
element * c = label() ? (element *)label() : icon(); element * c = label() ? (element *)label() : icon();
if (s && c) c->reshape(geom::shrink(bbox, 1.f * (*s->border_width + *s->inner_margin))); if (st && c) c->reshape(geom::shrink(bbox, 1.f * (*st->border_width + *st->inner_margin)));
} }
void on_state_changed() override void on_state_changed() override
@ -63,21 +63,21 @@ namespace psemek::ui
void draw(painter & p) const override void draw(painter & p) const override
{ {
auto s = style(); auto st = merged_style();
if (!s) return; if (!st) return;
if (s->shadow_offset != geom::vector{0, 0}) if (st->shadow_offset != geom::vector{0, 0})
p.draw_rect(shape_.box + geom::cast<float>(*s->shadow_offset), *s->shadow_color); p.draw_rect(shape_.box + geom::cast<float>(*st->shadow_offset), *st->shadow_color);
if (s->border_width > 0) if (st->border_width > 0)
p.draw_rect(shape_.box, *s->border_color); p.draw_rect(shape_.box, *st->border_color);
gfx::color_rgba color = *s->fg_color; gfx::color_rgba color = *st->fg_color;
if (state() == state_t::mouseover) if (state() == state_t::mouseover)
color = *s->highlight_color; color = *st->highlight_color;
else if (state() == state_t::mousedown) else if (state() == state_t::mousedown)
color = *s->action_color; color = *st->action_color;
p.draw_rect(geom::shrink(shape_.box, 1.f * (*s->border_width)), color); p.draw_rect(geom::shrink(shape_.box, 1.f * (*st->border_width)), color);
} }
geom::box<float, 2> size_constraints() const override geom::box<float, 2> size_constraints() const override
@ -90,7 +90,7 @@ namespace psemek::ui
{ {
sc = c->size_constraints(); sc = c->size_constraints();
if (auto st = style()) if (auto st = merged_style())
{ {
float extra = 2.f * (*st->border_width + *st->inner_margin); float extra = 2.f * (*st->border_width + *st->inner_margin);
sc[0] += extra; sc[0] += extra;
@ -113,7 +113,7 @@ namespace psemek::ui
void reshape(geom::box<float, 2> const & bbox) override void reshape(geom::box<float, 2> const & bbox) override
{ {
shape_.box = bbox; shape_.box = bbox;
auto st = style(); auto st = merged_style();
if (!st) return; if (!st) return;
for (auto c : children()) for (auto c : children())
if (c) c->reshape(geom::shrink(bbox, 1.f * (*st->border_width + *st->outer_margin))); if (c) c->reshape(geom::shrink(bbox, 1.f * (*st->border_width + *st->outer_margin)));
@ -127,7 +127,7 @@ namespace psemek::ui
if (c) if (c)
r = c->size_constraints(); r = c->size_constraints();
auto st = style(); auto st = merged_style();
if (st) if (st)
{ {
float extra = 2.f * (*st->border_width + *st->outer_margin); float extra = 2.f * (*st->border_width + *st->outer_margin);
@ -154,7 +154,7 @@ namespace psemek::ui
void draw(painter & p) const override void draw(painter & p) const override
{ {
auto st = style(); auto st = merged_style();
if (!st) return; if (!st) return;
if (st->shadow_offset != geom::vector{0, 0}) if (st->shadow_offset != geom::vector{0, 0})

View file

@ -1,5 +1,7 @@
#include <psemek/ui/element.hpp> #include <psemek/ui/element.hpp>
#include <psemek/util/recursive.hpp>
#include <psemek/log/log.hpp> #include <psemek/log/log.hpp>
namespace psemek::ui namespace psemek::ui
@ -37,19 +39,46 @@ namespace psemek::ui
return {{{0.f, inf}, {0.f, inf}}}; return {{{0.f, inf}, {0.f, inf}}};
} }
std::shared_ptr<struct style const> element::style() const
{
element const * e = this;
while (!e->style_ && e->parent()) e = e->parent();
return e->style_;
}
std::shared_ptr<style const> element::set_style(std::shared_ptr<struct style const> st) std::shared_ptr<style const> element::set_style(std::shared_ptr<struct style const> st)
{ {
std::swap(st, style_); std::swap(st, style_);
auto visitor = util::recursive([](auto && self, element * e) -> void {
e->merged_style_ = nullptr;
for (auto c : e->children())
if (c) self(c);
});
visitor(this);
return st; return st;
} }
std::shared_ptr<struct style const> element::merged_style() const
{
if (!merged_style_)
{
if (!style_)
{
if (parent())
merged_style_ = parent()->merged_style();
else
merged_style_ = std::make_shared<struct style>(default_style());
}
else
{
auto merged_style = std::make_shared<struct style>(*style_);
if (parent())
merge(*merged_style, *parent()->merged_style());
else
merge(*merged_style, default_style());
merged_style_ = merged_style;
}
}
return merged_style_;
}
void element::post(std::function<void()> f) void element::post(std::function<void()> f)
{ {
auto l = loop(); auto l = loop();

View file

@ -121,7 +121,7 @@ namespace psemek::ui
geom::box<float, 2> grid_layout::size_constraints() const geom::box<float, 2> grid_layout::size_constraints() const
{ {
auto st = style(); auto st = merged_style();
if (!st) return element::size_constraints(); if (!st) return element::size_constraints();
static float const inf = std::numeric_limits<float>::infinity(); static float const inf = std::numeric_limits<float>::infinity();
@ -163,7 +163,7 @@ namespace psemek::ui
void grid_layout::reshape(geom::box<float, 2> const & bbox) void grid_layout::reshape(geom::box<float, 2> const & bbox)
{ {
auto st = style(); auto st = merged_style();
if (!st) return; if (!st) return;
float const margin = *st->outer_margin; float const margin = *st->outer_margin;

View file

@ -58,7 +58,7 @@ namespace psemek::ui
if (!cached_state_->font) return; if (!cached_state_->font) return;
auto st = style(); auto st = merged_style();
if (!st) return; if (!st) return;
if (st->text_shadow_offset != geom::vector{0, 0}) if (st->text_shadow_offset != geom::vector{0, 0})
@ -91,7 +91,7 @@ namespace psemek::ui
if (text_.empty()) return state; if (text_.empty()) return state;
auto st = style(); auto st = merged_style();
if (!st) return state; if (!st) return state;
if (!st->font) return state; if (!st->font) return state;

View file

@ -3,6 +3,29 @@
namespace psemek::ui namespace psemek::ui
{ {
template <typename T>
void merge(std::optional<T> & dst, std::optional<T> const & src)
{
if (!dst) dst = src;
}
void merge(style & dst, style const & src)
{
merge(dst.bg_color, src.bg_color);
merge(dst.fg_color, src.fg_color);
merge(dst.highlight_color, src.highlight_color);
merge(dst.action_color, src.action_color);
merge(dst.border_color, src.border_color);
merge(dst.border_width, src.border_width);
merge(dst.shadow_offset, src.shadow_offset);
merge(dst.shadow_color, src.shadow_color);
merge(dst.inner_margin, src.inner_margin);
merge(dst.outer_margin, src.outer_margin);
merge(dst.text_color, src.text_color);
merge(dst.text_scale, src.text_scale);
merge(dst.text_shadow_offset, src.text_shadow_offset);
}
style default_style() style default_style()
{ {
style s; style s;