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)
, 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->text_scale = 2;

View file

@ -44,8 +44,9 @@ namespace psemek::ui
virtual void enable() { set_enabled(true); }
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> merged_style() const;
virtual void draw(painter & p) const = 0;
@ -59,6 +60,7 @@ namespace psemek::ui
async::executor * loop_ = nullptr;
bool enabled_ = true;
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;
};
void merge(style & dst, style const & src);
style default_style();
}

View file

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

View file

@ -1,5 +1,7 @@
#include <psemek/ui/element.hpp>
#include <psemek/util/recursive.hpp>
#include <psemek/log/log.hpp>
namespace psemek::ui
@ -37,19 +39,46 @@ namespace psemek::ui
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::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;
}
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)
{
auto l = loop();

View file

@ -121,7 +121,7 @@ namespace psemek::ui
geom::box<float, 2> grid_layout::size_constraints() const
{
auto st = style();
auto st = merged_style();
if (!st) return element::size_constraints();
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)
{
auto st = style();
auto st = merged_style();
if (!st) return;
float const margin = *st->outer_margin;

View file

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

View file

@ -3,6 +3,29 @@
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 s;