1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
#include "cru/ui/style/StyleRuleSet.h"
#include "cru/common/Event.h"
#include "cru/ui/controls/Control.h"
#include "cru/ui/model/IListChangeNotify.h"
#include <unordered_set>
namespace cru::ui::style {
namespace {
bool CheckCycle(StyleRuleSet* t, StyleRuleSet* p) {
while (p != nullptr) {
if (p == t) {
return true;
}
p = p->GetParent().get();
}
return false;
}
} // namespace
StyleRuleSet::StyleRuleSet(std::shared_ptr<StyleRuleSet> parent) {
SetParent(std::move(parent));
}
void StyleRuleSet::SetParent(std::shared_ptr<StyleRuleSet> parent) {
if (parent == parent_) return;
if (CheckCycle(this, parent.get())) {
throw Exception(u"Cycle detected in StyleRuleSet parent.");
}
parent_change_event_guard_.Reset();
parent_ = std::move(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));
RaiseChangeEvent(model::ListChange::ItemAdd(index));
}
void StyleRuleSet::RemoveStyleRule(gsl::index index, gsl::index count) {
Expects(index >= 0);
Expects(count >= 0 && index + count <= GetSize());
rules_.erase(rules_.cbegin() + index, rules_.cbegin() + index + count);
RaiseChangeEvent(model::ListChange::ItemRemove(index, count));
}
void StyleRuleSet::SetStyleRule(Index index, StyleRule rule) {
Expects(index >= 0 && index < GetSize());
rules_[index] = std::move(rule);
RaiseChangeEvent(model::ListChange::ItemSet(index));
}
void StyleRuleSet::Set(const StyleRuleSet& other, bool set_parent) {
rules_ = other.rules_;
if (set_parent) parent_ = other.parent_;
list_change_event_.Raise(model::ListChange::Clear());
list_change_event_.Raise(model::ListChange::ItemAdd(0, GetSize()));
change_event_.Raise(nullptr);
}
StyleRuleSetBind::StyleRuleSetBind(controls::Control* control,
std::shared_ptr<StyleRuleSet> ruleset)
: control_(control), ruleset_(std::move(ruleset)) {
Expects(control);
Expects(ruleset_);
ruleset_->ChangeEvent()->AddSpyOnlyHandler([this] {
UpdateRuleSetChainCache();
UpdateChangeListener();
UpdateStyle();
});
}
void StyleRuleSetBind::UpdateRuleSetChainCache() {
ruleset_chain_cache_.clear();
auto parent = ruleset_;
while (parent != nullptr) {
ruleset_chain_cache_.push_back(parent.get());
parent = parent->GetParent();
}
}
void StyleRuleSetBind::UpdateChangeListener() {
guard_.Clear();
std::unordered_set<IBaseEvent*> events;
// 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) {
guard_ += e->AddSpyOnlyHandler([this] { this->UpdateStyle(); });
}
}
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()) {
rule.CheckAndApply(control_);
}
}
}
} // namespace cru::ui::style
|