aboutsummaryrefslogtreecommitdiff
path: root/src/ui/style/StyleRuleSet.cpp
blob: c8d1250045b6788f2c88f51997d0ca71e66495de (plain)
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
#include "cru/ui/style/StyleRuleSet.h"
#include "cru/common/Event.h"
#include "cru/ui/controls/Control.h"
#include "gsl/gsl_assert"

#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();
}

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();
}

void StyleRuleSet::SetStyleRule(Index index, StyleRule rule) {
  Expects(index >= 0 && index < GetSize());
  rules_[index] = std::move(rule);
  RaiseChangeEvent();
}

void StyleRuleSet::Set(const StyleRuleSet& other, bool set_parent) {
  rules_ = other.rules_;
  if (set_parent) parent_ = other.parent_;

  RaiseChangeEvent();
}

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())
      if (rule.GetCondition()->Judge(control_)) {
        rule.GetStyler()->Apply(control_);
      }
  }
}
}  // namespace cru::ui::style