aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/common/Event.hpp2
-rw-r--r--include/cru/ui/Base.hpp1
-rw-r--r--include/cru/ui/controls/Control.hpp1
-rw-r--r--include/cru/ui/style/StyleRuleSet.hpp40
-rw-r--r--src/ui/controls/Button.cpp4
-rw-r--r--src/ui/controls/Control.cpp4
-rw-r--r--src/ui/controls/TextBox.cpp2
-rw-r--r--src/ui/style/StyleRuleSet.cpp74
8 files changed, 102 insertions, 26 deletions
diff --git a/include/cru/common/Event.hpp b/include/cru/common/Event.hpp
index 7f7b4dd4..aa8fadbb 100644
--- a/include/cru/common/Event.hpp
+++ b/include/cru/common/Event.hpp
@@ -233,6 +233,8 @@ class EventRevokerGuard {
EventRevoker Release() { return std::move(*revoker_.release()); }
+ void Reset() { revoker_.reset(); }
+
void Reset(EventRevoker&& revoker) {
revoker_.reset(new EventRevoker(std::move(revoker)));
}
diff --git a/include/cru/ui/Base.hpp b/include/cru/ui/Base.hpp
index 57beb723..b2939a0b 100644
--- a/include/cru/ui/Base.hpp
+++ b/include/cru/ui/Base.hpp
@@ -42,6 +42,7 @@ class RenderObject;
namespace style {
class StyleRuleSet;
+class StyleRuleSetBind;
}
//-------------------- region: basic types --------------------
diff --git a/include/cru/ui/controls/Control.hpp b/include/cru/ui/controls/Control.hpp
index 0d34bc63..341b6ef2 100644
--- a/include/cru/ui/controls/Control.hpp
+++ b/include/cru/ui/controls/Control.hpp
@@ -152,5 +152,6 @@ class Control : public Object {
std::shared_ptr<platform::gui::ICursor> cursor_ = nullptr;
std::unique_ptr<style::StyleRuleSet> style_rule_set_;
+ std::unique_ptr<style::StyleRuleSetBind> style_rule_set_bind_;
};
} // namespace cru::ui::controls
diff --git a/include/cru/ui/style/StyleRuleSet.hpp b/include/cru/ui/style/StyleRuleSet.hpp
index 3ec71730..ba3f8b4c 100644
--- a/include/cru/ui/style/StyleRuleSet.hpp
+++ b/include/cru/ui/style/StyleRuleSet.hpp
@@ -2,13 +2,14 @@
#include "StyleRule.hpp"
#include "cru/common/Base.hpp"
#include "cru/common/Event.hpp"
-#include "gsl/gsl_assert"
+
+#include <cstddef>
namespace cru::ui::style {
class StyleRuleSet : public Object {
public:
- StyleRuleSet() : control_(nullptr) {}
- explicit StyleRuleSet(controls::Control* control) : control_(control) {}
+ StyleRuleSet() = default;
+ explicit StyleRuleSet(StyleRuleSet* parent);
CRU_DELETE_COPY(StyleRuleSet)
CRU_DELETE_MOVE(StyleRuleSet)
@@ -16,6 +17,9 @@ class StyleRuleSet : public Object {
~StyleRuleSet() override = default;
public:
+ StyleRuleSet* GetParent() const { return parent_; }
+ void SetParent(StyleRuleSet* parent);
+
gsl::index GetSize() const { return static_cast<gsl::index>(rules_.size()); }
const std::vector<StyleRule>& GetRules() const { return rules_; }
@@ -41,14 +45,42 @@ class StyleRuleSet : public Object {
const StyleRule& operator[](gsl::index index) const { return rules_[index]; }
+ // Triggered whenever a change happened to this (rule add or remove, parent
+ // change ...). Subscribe to this and update style change listeners and style.
+ IEvent<std::nullptr_t>* ChangeEvent() { return &change_event_; }
+
+ private:
+ void RaiseChangeEvent() { change_event_.Raise(nullptr); }
+
+ private:
+ Event<std::nullptr_t> change_event_;
+
+ StyleRuleSet* parent_ = nullptr;
+ EventRevokerGuard parent_change_event_guard_;
+
+ std::vector<StyleRule> rules_;
+};
+
+class StyleRuleSetBind {
+ public:
+ StyleRuleSetBind(controls::Control* control, StyleRuleSet* ruleset);
+
+ CRU_DELETE_COPY(StyleRuleSetBind)
+ CRU_DELETE_MOVE(StyleRuleSetBind)
+
+ ~StyleRuleSetBind() = default;
+
private:
+ void UpdateRuleSetChainCache();
void UpdateChangeListener();
void UpdateStyle();
private:
controls::Control* control_;
+ StyleRuleSet* ruleset_;
- std::vector<StyleRule> rules_;
+ // child first, parent last.
+ std::vector<StyleRuleSet*> ruleset_chain_cache_;
EventRevokerListGuard guard_;
};
diff --git a/src/ui/controls/Button.cpp b/src/ui/controls/Button.cpp
index 7858eadb..8bd9f93f 100644
--- a/src/ui/controls/Button.cpp
+++ b/src/ui/controls/Button.cpp
@@ -42,8 +42,8 @@ Button::Button() : click_detector_(this) {
}
});
- GetStyleRuleSet()->Set(
- UiManager::GetInstance()->GetThemeResources()->button_style);
+ GetStyleRuleSet()->SetParent(
+ &UiManager::GetInstance()->GetThemeResources()->button_style);
}
Button::~Button() = default;
diff --git a/src/ui/controls/Control.cpp b/src/ui/controls/Control.cpp
index 1c4ffe51..29c2c46a 100644
--- a/src/ui/controls/Control.cpp
+++ b/src/ui/controls/Control.cpp
@@ -16,7 +16,9 @@ using platform::gui::IUiApplication;
using platform::gui::SystemCursorType;
Control::Control() {
- style_rule_set_ = std::make_unique<style::StyleRuleSet>(this);
+ style_rule_set_ = std::make_unique<style::StyleRuleSet>();
+ style_rule_set_bind_ =
+ std::make_unique<style::StyleRuleSetBind>(this, style_rule_set_.get());
MouseEnterEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) {
this->is_mouse_over_ = true;
diff --git a/src/ui/controls/TextBox.cpp b/src/ui/controls/TextBox.cpp
index 0d14dbcf..d8317a8c 100644
--- a/src/ui/controls/TextBox.cpp
+++ b/src/ui/controls/TextBox.cpp
@@ -36,7 +36,7 @@ TextBox::TextBox()
border_render_object_->SetBorderEnabled(true);
- GetStyleRuleSet()->Set(theme_resources->text_box_style);
+ GetStyleRuleSet()->SetParent(&theme_resources->text_box_style);
}
TextBox::~TextBox() {}
diff --git a/src/ui/style/StyleRuleSet.cpp b/src/ui/style/StyleRuleSet.cpp
index 403fe114..24b88af9 100644
--- a/src/ui/style/StyleRuleSet.cpp
+++ b/src/ui/style/StyleRuleSet.cpp
@@ -1,17 +1,30 @@
#include "cru/ui/style/StyleRuleSet.hpp"
#include "cru/common/Event.hpp"
+#include "cru/ui/controls/Control.hpp"
#include "gsl/gsl_assert"
#include <unordered_set>
namespace cru::ui::style {
+StyleRuleSet::StyleRuleSet(StyleRuleSet* parent) { SetParent(parent); }
+
+void StyleRuleSet::SetParent(StyleRuleSet* parent) {
+ if (parent == parent_) return;
+ parent_change_event_guard_.Reset();
+ parent_ = parent;
+ if (parent != nullptr) {
+ parent_change_event_guard_.Reset(parent->ChangeEvent()->AddSpyOnlyHandler(
+ [this] { this->RaiseChangeEvent(); }));
+ }
+ RaiseChangeEvent();
+}
+
void StyleRuleSet::AddStyleRule(StyleRule rule, gsl::index index) {
Expects(index >= 0 && index <= GetSize());
rules_.insert(rules_.cbegin() + index, std::move(rule));
- UpdateChangeListener();
- UpdateStyle();
+ RaiseChangeEvent();
}
void StyleRuleSet::RemoveStyleRule(gsl::index index, gsl::index count) {
@@ -20,25 +33,48 @@ void StyleRuleSet::RemoveStyleRule(gsl::index index, gsl::index count) {
rules_.erase(rules_.cbegin() + index, rules_.cbegin() + index + count);
- UpdateChangeListener();
- UpdateStyle();
+ RaiseChangeEvent();
}
void StyleRuleSet::Set(const StyleRuleSet& other) {
rules_ = other.rules_;
- UpdateChangeListener();
- UpdateStyle();
+
+ RaiseChangeEvent();
+}
+
+StyleRuleSetBind::StyleRuleSetBind(controls::Control* control,
+ StyleRuleSet* ruleset)
+ : control_(control), ruleset_(ruleset) {
+ Expects(control);
+ Expects(ruleset);
+
+ ruleset->ChangeEvent()->AddSpyOnlyHandler([this] {
+ UpdateRuleSetChainCache();
+ UpdateChangeListener();
+ UpdateStyle();
+ });
}
-void StyleRuleSet::UpdateChangeListener() {
- if (control_ == nullptr) return;
+void StyleRuleSetBind::UpdateRuleSetChainCache() {
+ ruleset_chain_cache_.clear();
+ auto parent = ruleset_;
+ while (parent != nullptr) {
+ ruleset_chain_cache_.push_back(parent);
+ parent = parent->GetParent();
+ }
+}
+void StyleRuleSetBind::UpdateChangeListener() {
guard_.Clear();
std::unordered_set<IBaseEvent*> events;
- for (const auto& rule : rules_) {
- auto e = rule.GetCondition()->ChangeOn(control_);
- events.insert(e.cbegin(), e.cend());
+
+ // ruleset order does not matter
+ for (auto ruleset : ruleset_chain_cache_) {
+ for (const auto& rule : ruleset->GetRules()) {
+ auto e = rule.GetCondition()->ChangeOn(control_);
+ events.insert(e.cbegin(), e.cend());
+ }
}
for (auto e : events) {
@@ -46,13 +82,15 @@ void StyleRuleSet::UpdateChangeListener() {
}
}
-void StyleRuleSet::UpdateStyle() {
- if (control_ == nullptr) return;
-
- for (const auto& rule : rules_) {
- if (rule.GetCondition()->Judge(control_)) {
- rule.GetStyler()->Apply(control_);
- }
+void StyleRuleSetBind::UpdateStyle() {
+ // cache is parent last, but when calculate style, parent first, so iterate
+ // reverse.
+ for (auto iter = ruleset_chain_cache_.crbegin();
+ iter != ruleset_chain_cache_.crend(); ++iter) {
+ for (const auto& rule : (*iter)->GetRules())
+ if (rule.GetCondition()->Judge(control_)) {
+ rule.GetStyler()->Apply(control_);
+ }
}
}
} // namespace cru::ui::style