Skip to main content

ferroui_base/styling/
selectors.rs

1use super::{
2    ChildSelector, DescendantSelector, NestingSelector, NotSelector, NthChildSelector, NthLastChildSelector,
3    OrSelector, PropertyEqualsSelector, Selector, TemplateSelector, TypeNameAndClassSelector,
4};
5use crate::{BoxedValue, FerroProperty, ObjectType, PropertyValue, StyledElement, StyledProperty, TypeInfo, Upcast};
6use std::rc::Rc;
7
8/// Functions for building [`Selector`]s.
9///
10/// Every function takes the selector that precedes the new one (`None` to
11/// start a selector). The same functions are available as chaining methods on
12/// [`Selector`]:
13///
14/// ```ignore
15/// // Button.big > :is(Control)
16/// let selector = Selectors::of_type::<Button>().class("big").child().is::<Control>();
17/// ```
18pub struct Selectors;
19
20impl Selectors {
21    /// Returns a selector which matches a previous selector's child.
22    pub fn child(previous: Option<Selector>) -> Selector {
23        Selector::new(ChildSelector::new(previous))
24    }
25
26    /// Returns a selector which matches a control's style class.
27    pub fn class(previous: Option<Selector>, name: &str) -> Selector {
28        if name.trim().is_empty() {
29            panic!("Name may not be empty");
30        }
31
32        if let Some(previous) = &previous {
33            if let Some(tac) = previous.node::<TypeNameAndClassSelector>() {
34                tac.add_class(name);
35                return previous.clone();
36            }
37        }
38
39        Selector::new(TypeNameAndClassSelector::for_class(previous, name))
40    }
41
42    /// Returns a selector which matches a descendant of a previous selector.
43    pub fn descendant(previous: Option<Selector>) -> Selector {
44        Selector::new(DescendantSelector::new(previous))
45    }
46
47    /// Returns a selector which matches a type or a derived type.
48    pub fn is_type_info(previous: Option<Selector>, type_: &'static TypeInfo) -> Selector {
49        Selector::new(TypeNameAndClassSelector::is(previous, type_))
50    }
51
52    /// Returns a selector which matches class `T` or a class derived from it.
53    pub fn is<T: ObjectType + Upcast<StyledElement>>() -> Selector {
54        Self::is_type_info(None, T::TYPE)
55    }
56
57    /// Returns a selector which matches a control's name.
58    pub fn name(previous: Option<Selector>, name: &str) -> Selector {
59        if name.trim().is_empty() {
60            panic!("Name may not be empty");
61        }
62
63        if let Some(previous) = &previous {
64            if let Some(tac) = previous.node::<TypeNameAndClassSelector>() {
65                tac.set_name(name);
66                return previous.clone();
67            }
68        }
69
70        Selector::new(TypeNameAndClassSelector::for_name(previous, name))
71    }
72
73    /// Returns the nesting selector, which stands for the selector of the
74    /// parent style.
75    pub fn nesting(_previous: Option<Selector>) -> Selector {
76        Selector::new(NestingSelector)
77    }
78
79    /// Returns a selector which inverts the results of selector argument.
80    pub fn not(previous: Option<Selector>, argument: Selector) -> Selector {
81        Selector::new(NotSelector::new(previous, argument))
82    }
83
84    /// Returns a selector which inverts the results of the selector built by
85    /// `argument`.
86    pub fn not_with(previous: Option<Selector>, argument: impl FnOnce(Option<Selector>) -> Selector) -> Selector {
87        Selector::new(NotSelector::new(previous, argument(None)))
88    }
89
90    /// Returns a selector which matches elements based on their position
91    /// among a group of siblings.
92    pub fn nth_child(previous: Option<Selector>, step: i32, offset: i32) -> Selector {
93        Selector::new(NthChildSelector::new(previous, step, offset))
94    }
95
96    /// Returns a selector which matches elements based on their position
97    /// among a group of siblings, counting from the end.
98    pub fn nth_last_child(previous: Option<Selector>, step: i32, offset: i32) -> Selector {
99        Selector::new(NthLastChildSelector::new(previous, step, offset))
100    }
101
102    /// Returns a selector which matches a type.
103    pub fn of_type_info(previous: Option<Selector>, type_: &'static TypeInfo) -> Selector {
104        Selector::new(TypeNameAndClassSelector::of_type(previous, type_))
105    }
106
107    /// Returns a selector which matches class `T` exactly.
108    pub fn of_type<T: ObjectType + Upcast<StyledElement>>() -> Selector {
109        Self::of_type_info(None, T::TYPE)
110    }
111
112    /// Returns a selector which ORs selectors.
113    pub fn or(selectors: impl IntoIterator<Item = Selector>) -> Selector {
114        Selector::new(OrSelector::new(selectors.into_iter().collect()))
115    }
116
117    /// Returns a selector which matches a control with the specified property
118    /// value.
119    pub fn property_equals<T: PropertyValue>(
120        previous: Option<Selector>,
121        property: &'static StyledProperty<T>,
122        value: T,
123    ) -> Selector {
124        Selector::new(PropertyEqualsSelector::new(previous, property, Rc::new(value)))
125    }
126
127    /// Returns a selector which matches a control with the specified property
128    /// value. `value` must hold exactly the property's value type.
129    pub fn property_equals_untyped(
130        previous: Option<Selector>,
131        property: &'static FerroProperty,
132        value: BoxedValue,
133    ) -> Selector {
134        Selector::new(PropertyEqualsSelector::new(previous, property, value))
135    }
136
137    /// Returns a selector which enters a lookless control's template.
138    pub fn template(previous: Option<Selector>) -> Selector {
139        Selector::new(TemplateSelector::new(previous))
140    }
141}
142
143impl Selector {
144    /// Returns a selector which matches this selector's child.
145    pub fn child(self) -> Selector {
146        Selectors::child(Some(self))
147    }
148
149    /// Returns a selector which additionally matches a control's style class.
150    pub fn class(self, name: &str) -> Selector {
151        Selectors::class(Some(self), name)
152    }
153
154    /// Returns a selector which matches a descendant of this selector.
155    pub fn descendant(self) -> Selector {
156        Selectors::descendant(Some(self))
157    }
158
159    /// Returns a selector which additionally matches class `T` or a class
160    /// derived from it.
161    pub fn is<T: ObjectType + Upcast<StyledElement>>(self) -> Selector {
162        Selectors::is_type_info(Some(self), T::TYPE)
163    }
164
165    /// Returns a selector which additionally matches a type or a derived
166    /// type.
167    pub fn is_type_info(self, type_: &'static TypeInfo) -> Selector {
168        Selectors::is_type_info(Some(self), type_)
169    }
170
171    /// Returns a selector which additionally matches a control's name.
172    pub fn name(self, name: &str) -> Selector {
173        Selectors::name(Some(self), name)
174    }
175
176    /// Returns the nesting selector.
177    pub fn nesting(self) -> Selector {
178        Selectors::nesting(Some(self))
179    }
180
181    /// Returns a selector which additionally inverts the results of
182    /// `argument`.
183    pub fn not(self, argument: Selector) -> Selector {
184        Selectors::not(Some(self), argument)
185    }
186
187    /// Returns a selector which additionally inverts the results of the
188    /// selector built by `argument`.
189    pub fn not_with(self, argument: impl FnOnce(Option<Selector>) -> Selector) -> Selector {
190        Selectors::not_with(Some(self), argument)
191    }
192
193    /// Returns a selector which additionally matches elements based on their
194    /// position among a group of siblings.
195    pub fn nth_child(self, step: i32, offset: i32) -> Selector {
196        Selectors::nth_child(Some(self), step, offset)
197    }
198
199    /// Returns a selector which additionally matches elements based on their
200    /// position among a group of siblings, counting from the end.
201    pub fn nth_last_child(self, step: i32, offset: i32) -> Selector {
202        Selectors::nth_last_child(Some(self), step, offset)
203    }
204
205    /// Returns a selector which additionally matches class `T` exactly.
206    pub fn of_type<T: ObjectType + Upcast<StyledElement>>(self) -> Selector {
207        Selectors::of_type_info(Some(self), T::TYPE)
208    }
209
210    /// Returns a selector which additionally matches a type.
211    pub fn of_type_info(self, type_: &'static TypeInfo) -> Selector {
212        Selectors::of_type_info(Some(self), type_)
213    }
214
215    /// Returns a selector which additionally matches a control with the
216    /// specified property value.
217    pub fn property_equals<T: PropertyValue>(self, property: &'static StyledProperty<T>, value: T) -> Selector {
218        Selectors::property_equals(Some(self), property, value)
219    }
220
221    /// Returns a selector which additionally matches a control with the
222    /// specified property value. `value` must hold exactly the property's
223    /// value type.
224    pub fn property_equals_untyped(self, property: &'static FerroProperty, value: BoxedValue) -> Selector {
225        Selectors::property_equals_untyped(Some(self), property, value)
226    }
227
228    /// Returns a selector which enters a lookless control's template.
229    pub fn template(self) -> Selector {
230        Selectors::template(Some(self))
231    }
232}