22 using sptr = std::shared_ptr<component_stylist_t>;
25 return std::make_shared<component_stylist_t>();
30 auto& style_rules = component->get_style_data().rules;
34 for (const auto & selector : rule->selectors_) {
35 if (check_style_comb_selector(component, selector)) {
36 style_rules.emplace_back(selector.specificity(), rule);
43 return lhs.specificity > rhs.specificity;
48 ZoneScopedN(
"Apply Style");
51 apply_style_rules(component);
52 style_data.apply_override();
53 style_data.apply_transform();
59 for (
const auto& c : component->
children) {
66 ZoneScopedN(
"Apply Rules");
68 auto& style_rules = style_data.
rules;
69 const auto& bti = refl::type_info::from<style_base_t>();
72 std::unordered_set<const refl::field_info*> pending_base_fields{};
73 for (
const auto & bfti : bti.fields()) {
74 pending_base_fields.insert(&bfti);
76 std::unordered_set<const refl::field_info*> pending_fields{};
77 for (
const auto & fti : ti.fields()) {
78 pending_fields.insert(&fti);
82 std::unordered_set<const refl::field_info*> activated_base_fields{};
83 std::unordered_set<const refl::field_info*> activated_fields{};
86 std::unordered_set<const refl::field_info*> deactivated_base_fields{};
87 std::unordered_set<const refl::field_info*> deactivated_fields{};
90 for (
auto& rule_instance: style_rules) {
91 auto& [specificity, rule, is_active, _, __] = rule_instance;
92 const bool should_be_active = check_style_comb_selector_vector(component, rule->selectors_,
true,
true);
93 if (not is_active and should_be_active) {
95 ZoneScopedN(
"Activate Rule");
97 for (
auto field_it = pending_base_fields.begin(); field_it != pending_base_fields.end();) {
98 const refl::field_info* field = *field_it;
99 if (apply_rule(component, &base_s, field_it, pending_base_fields, rule)) {
100 rule_instance.active_base_properties.insert(field);
101 activated_base_fields.insert(field);
105 for (
auto field_it = pending_fields.begin(); field_it != pending_fields.end();) {
106 const refl::field_info* field = *field_it;
107 if (apply_rule(component, style_data.as_raw(), field_it, pending_fields, rule)) {
108 rule_instance.active_properties.insert(field);
109 activated_fields.insert(field);
113 rule_instance.active =
true;
114 }
else if (is_active and not should_be_active) {
115 ZoneScopedN(
"Deactivate Rule");
117 for (
const auto& prop: rule_instance.active_base_properties) {
118 deactivated_base_fields.insert(prop);
120 for (
const auto& prop: rule_instance.active_properties) {
121 deactivated_fields.insert(prop);
123 rule_instance.active_base_properties.clear();
124 rule_instance.active_properties.clear();
125 rule_instance.active =
false;
126 }
else if (is_active and should_be_active) {
128 ZoneScopedN(
"Update Rule");
131 for (
auto field_it = activated_base_fields.begin(); field_it != activated_base_fields.end(); ++field_it) {
132 rule_instance.active_base_properties.erase(*field_it);
135 for (
auto field_it = activated_fields.begin(); field_it != activated_fields.end(); ++field_it) {
136 rule_instance.active_properties.erase(*field_it);
141 for (
auto field_it = deactivated_base_fields.begin(); field_it != deactivated_base_fields.end();) {
142 const refl::field_info* field = *field_it;
143 if (apply_rule(component, &base_s, field_it, deactivated_base_fields, rule)) {
144 rule_instance.active_base_properties.insert(field);
148 for (
auto field_it = deactivated_fields.begin(); field_it != deactivated_fields.end();) {
149 const refl::field_info* field = *field_it;
150 if (apply_rule(component, style_data.as_raw(), field_it, deactivated_fields, rule)) {
151 rule_instance.active_properties.insert(field);
157 for (
auto field_it = pending_base_fields.begin(); field_it != pending_base_fields.end();) {
158 if (rule_instance.active_base_properties.contains(*field_it)) {
159 field_it = pending_base_fields.erase(field_it);
165 for (
auto field_it = pending_fields.begin(); field_it != pending_fields.end();) {
166 if (rule_instance.active_properties.contains(*field_it)) {
167 field_it = pending_fields.erase(field_it);
177 for (
const auto & field : deactivated_base_fields) {
178 style_data.reset_field(field,
true);
180 for (
const auto & field : deactivated_fields) {
181 style_data.reset_field(field,
false);
186 std::unordered_set<const refl::field_info *>::iterator &field_it,
187 std::unordered_set<const refl::field_info *> &pending_fields,
189 ZoneScopedN(
"Apply Rule");
190 const auto *field = *field_it;
191 if (rule->properties_.contains(field->name)) {
192 const auto &rule_field = rule->properties_.at(field->name);
194 if (apply_rule_property(component, style_obj, *field_it, rule_field)) {
195 field_it = pending_fields.erase(field_it);
204 const refl::field_info* field,
205 const refl::any &value) {
206 ZoneScopedN(
"Apply Property");
207 if (value.is(field->type())) {
208 field->type().assign_copy_of(value.data(), field->get_ptr(style_obj));
212 if (value.is(refl::type_info::from<std::string>())
213 and field->has_metadata<custom_style_parser>()) {
214 std::string str = value.as<std::string>();
216 refl::any rule_field_parsed =
217 field->get_metadata<custom_style_parser>().parser_function(str);
218 if (rule_field_parsed.is(field->type())) {
219 field->type().assign_copy_of(rule_field_parsed.data(), field->get_ptr(style_obj));
223 (
"Custom parser type mismatch '{}::{}', expected: '{}', found: '{}'",
226 field->type().name(),
227 rule_field_parsed.type().name());
231 if (field->type().is_type<vg::paint::type>()) {
232 if (value.is<color::Color>()) {
233 vg::paint::type& paint = field->get_ref<vg::paint::type>(style_obj);
234 const color::Color& paint_color = value.as<color::Color>();
241 (
"Type mismatch '{}::{}', expected: '{}', found: '{}'",
244 field->type().name(),
245 value.type().name());
250 bool check_style_comb_selector_vector(
const component_base_t::sptr& component,
const std::vector<StyleRuleCombinedSelector> &selectors,
251 bool check_tags =
false,
bool check_pseudo_states =
false) {
252 ZoneScopedN(
"Check Selectors");
253 for (
const auto &selector: selectors) {
254 if (check_style_comb_selector(component, selector, check_tags, check_pseudo_states)) {
261 bool check_style_comb_selector(
const component_base_t::sptr& component,
const StyleRuleCombinedSelector& selector,
bool check_tags =
false,
bool check_pseudo_states =
false) {
262 auto it = selector.selectors.rbegin();
263 if (not check_style_selector(component.get(), it->second, check_tags, check_pseudo_states)) {
270 while (it != selector.selectors.rend()) {
272 if (current->parent.has_value() and check_style_selector(current->parent.value(), it->second, check_tags, check_pseudo_states)) {
273 current = current->parent.value();
279 while (current->parent.has_value()) {
280 if (check_style_selector(current->parent.value(), it->second, check_tags, check_pseudo_states)) {
284 current = current->parent.value();
299 bool check_style_selector(
component_base_t* component,
const StyleRuleSelector& selector,
bool check_tags,
bool check_pseudo_states) {
300 ZoneScopedN(
"Check Selector");
301 auto& style_data = component->get_style_data();
302 if (component->name() != selector.component) {
307 for (
const auto & tag : selector.tags) {
308 if (not style_data.tags.contains(tag)) {
314 if (check_pseudo_states) {
315 if (selector.pseudo_states.contains(
"hover") and not component->state()->hovering) {
318 if (selector.pseudo_states.contains(
"focus") and not component->state()->focused) {