ferroui_base/styling/
selector.rs1use super::activators::AndActivatorBuilder;
2use super::{
3 ContainerQuery, NestingSelector, SelectorMatch, SelectorMatchResult, Style, StyleBase, TemplateSelector,
4};
5use crate::{StyledElement, TypeInfo};
6use std::any::Any;
7use std::fmt;
8use std::rc::Rc;
9
10pub trait SelectorNode: 'static {
12 fn in_template(&self) -> bool;
14
15 fn is_combinator(&self) -> bool;
20
21 fn target_type(&self) -> Option<&'static TypeInfo>;
23
24 fn to_string(&self, owner: Option<&Style>) -> String;
27
28 fn to_string_with_next(&self, owner: Option<&Style>, _has_next: bool) -> String {
29 self.to_string(owner)
30 }
31
32 fn evaluate(&self, control: &StyledElement, parent: Option<&StyleBase>, subscribe: bool) -> SelectorMatch;
34
35 fn move_previous(&self) -> Option<&Selector>;
37
38 fn move_previous_or_parent(&self) -> Option<&Selector>;
40
41 fn validate_nesting_selector(&self, in_control_theme: bool, mut template_count: i32) {
42 if in_control_theme {
43 if !self.in_template() && self.is_combinator() {
44 panic!("ControlTheme style may not directly contain a child or descendent selector.");
45 }
46 if self.as_any().is::<TemplateSelector>() {
47 let count = template_count;
48 template_count += 1;
49 if count > 0 {
50 panic!("ControlTemplate styles cannot contain multiple template selectors.");
51 }
52 }
53 }
54
55 match self.move_previous_or_parent() {
56 None => {
57 if !self.as_any().is::<NestingSelector>() {
58 panic!("Child styles must have a nesting selector.");
59 }
60 }
61 Some(previous) => previous.0.validate_nesting_selector(in_control_theme, template_count),
62 }
63 }
64
65 fn as_any(&self) -> &dyn Any;
66}
67
68#[derive(Clone)]
74pub struct Selector(pub(crate) Rc<dyn SelectorNode>);
75
76
77impl PartialEq for Selector {
80 #[inline]
81 fn eq(&self, other: &Self) -> bool {
82 std::ptr::addr_eq(Rc::as_ptr(&self.0), Rc::as_ptr(&other.0))
83 }
84}
85
86impl Selector {
87 pub(crate) fn new(node: impl SelectorNode) -> Self {
88 Selector(Rc::new(node))
89 }
90
91 #[inline]
93 pub(crate) fn in_template(&self) -> bool {
94 self.0.in_template()
95 }
96
97 #[inline]
99 pub(crate) fn is_combinator(&self) -> bool {
100 self.0.is_combinator()
101 }
102
103 #[inline]
105 pub fn target_type(&self) -> Option<&'static TypeInfo> {
106 self.0.target_type()
107 }
108
109 pub(crate) fn node<T: 'static>(&self) -> Option<&T> {
110 self.0.as_any().downcast_ref::<T>()
111 }
112
113 pub fn match_(&self, control: &StyledElement, parent: Option<&StyleBase>, subscribe: bool) -> SelectorMatch {
120 let (mut match_, combinator) = Self::match_until_combinator(control, self, parent, subscribe);
124
125 if match_.is_match() {
128 if let Some(combinator) = combinator {
129 match_ = match_.and(combinator.match_(control, parent, subscribe));
130
131 match_ = match match_.result() {
135 SelectorMatchResult::AlwaysThisType => SelectorMatch::ALWAYS_THIS_INSTANCE,
136 SelectorMatchResult::NeverThisType => SelectorMatch::NEVER_THIS_INSTANCE,
137 _ => match_,
138 };
139 }
140 }
141
142 match_
143 }
144
145 pub fn to_string_with_owner(&self, owner: Option<&Style>) -> String {
148 self.0.to_string(owner)
149 }
150
151 pub(crate) fn to_string_with_next(&self, owner: Option<&Style>, has_next: bool) -> String {
152 self.0.to_string_with_next(owner, has_next)
153 }
154
155 pub(crate) fn validate_nesting_selector(&self, in_control_theme: bool) {
156 self.0.validate_nesting_selector(in_control_theme, 0)
157 }
158
159 fn match_until_combinator<'a>(
160 control: &StyledElement,
161 start: &'a Selector,
162 parent: Option<&StyleBase>,
163 subscribe: bool,
164 ) -> (SelectorMatch, Option<&'a Selector>) {
165 let mut combinator = None;
166 let mut activators = AndActivatorBuilder::new();
167 let result = Self::match_selector(control, start, parent, subscribe, &mut activators, &mut combinator);
168 let match_ = if result == SelectorMatchResult::Sometimes {
169 SelectorMatch::sometimes(activators.get())
170 } else {
171 SelectorMatch::new(result)
172 };
173 (match_, combinator)
174 }
175
176 fn match_selector<'a>(
177 control: &StyledElement,
178 selector: &'a Selector,
179 parent: Option<&StyleBase>,
180 subscribe: bool,
181 activators: &mut AndActivatorBuilder,
182 combinator: &mut Option<&'a Selector>,
183 ) -> SelectorMatchResult {
184 let previous = selector.0.move_previous();
185
186 if let Some(previous) = previous {
190 if !previous.is_combinator() {
191 let previous_match =
192 Self::match_selector(control, previous, parent, subscribe, activators, combinator);
193
194 if previous_match < SelectorMatchResult::Sometimes {
195 return previous_match;
196 }
197 }
198 }
199
200 let mut container_matches_sometimes = false;
201
202 if let Some(container) = parent.and_then(|p| p.downcast_ref::<ContainerQuery>()) {
204 let match_ = match container.query() {
205 Some(query) => {
206 let container_parent = container.parent();
207 query.evaluate(control, container_parent.as_deref(), subscribe, container.name().as_deref())
208 }
209 None => SelectorMatch::NEVER_THIS_INSTANCE,
210 };
211
212 if !match_.is_match() {
213 return match_.result();
214 }
215
216 container_matches_sometimes = match_.result() == SelectorMatchResult::Sometimes;
217
218 if container_matches_sometimes {
219 activators.add(match_.into_activator());
220 }
221 }
222
223 let match_ = selector.0.evaluate(control, parent, subscribe);
225
226 if !match_.is_match() {
227 *combinator = None;
228 return match_.result();
229 }
230
231 let result = match_.result();
232 activators.add(match_.into_activator());
233
234 if let Some(previous) = previous {
235 if previous.is_combinator() {
236 *combinator = Some(previous);
237 }
238 }
239
240 if container_matches_sometimes {
241 SelectorMatchResult::Sometimes
242 } else {
243 result
244 }
245 }
246}
247
248impl fmt::Display for Selector {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 f.write_str(&self.0.to_string(None))
251 }
252}
253
254impl fmt::Debug for Selector {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 f.write_str(&self.0.to_string(None))
257 }
258}