ferroui_base/styling/
style.rs1use super::{
2 DuplicateSetterError, ContainerQuery, ControlTheme, OrSelector, Selector, SelectorMatch, SelectorMatchResult, Setter, StyleBase,
3 StyleBaseImpl, StyleBaseImplExt, StyleHostRef, TemplateSelector,
4};
5use crate::property_store::FrameType;
6use crate::{ferro_class, instantiate, FerroObjectImpl, Ref, StyledElement};
7use std::cell::RefCell;
8use std::rc::Rc;
9
10#[repr(C)]
12pub struct Style {
13 base: StyleBase,
14 selector: RefCell<Option<Selector>>,
15}
16
17ferro_class!(Style: StyleBase);
18crate::ferro_class_info!(Style { new: Style::new });
19
20impl FerroObjectImpl for Style {}
21
22impl StyleBaseImpl for Style {
23 fn set_parent(this: &Self, parent: Option<&Ref<StyleBase>>) {
24 if let Some(parent) = parent {
25 let parent_has_selector = parent.downcast_ref::<Style>().is_some_and(|s| s.selector.borrow().is_some());
26 if parent_has_selector {
27 match &*this.selector.borrow() {
28 None => panic!("Child styles must have a selector."),
29 Some(selector) => selector.validate_nesting_selector(false),
30 }
31 } else if parent.is::<ControlTheme>() {
32 match &*this.selector.borrow() {
33 None => panic!("Child styles must have a selector."),
34 Some(selector) => selector.validate_nesting_selector(true),
35 }
36 }
37 }
38
39 Self::parent_set_parent(this, parent);
40 }
41
42 fn to_display_string(this: &Self) -> String {
44 match &*this.selector.borrow() {
45 Some(selector) => selector.to_string_with_owner(Some(this)),
46 None => "Style".to_string(),
47 }
48 }
49}
50
51impl Style {
52 pub fn construct() -> Self {
54 Self { base: StyleBase::construct(), selector: RefCell::new(None) }
55 }
56
57 pub fn new() -> Ref<Self> {
59 instantiate(Self::construct())
60 }
61
62 pub fn with_selector(selector: Selector) -> Ref<Self> {
64 let style = Self::new();
65 style.set_selector(Some(selector));
66 style
67 }
68
69 pub fn with_selector_fn(selector: impl FnOnce(Option<Selector>) -> Selector) -> Ref<Self> {
71 Self::with_selector(selector(None))
72 }
73
74 pub fn selector(&self) -> Option<Selector> {
76 self.selector.borrow().clone()
77 }
78
79 pub fn set_selector(&self, value: Option<Selector>) {
80 *self.selector.borrow_mut() = Self::validate_selector(value);
81 }
82
83 pub fn with_setters(selector: Selector, setters: impl IntoIterator<Item = Rc<Setter>>) -> Ref<Self> {
85 let style = Self::with_selector(selector);
86 for setter in setters {
87 style.add_setter(setter);
88 }
89 style
90 }
91
92 #[allow(dead_code)]
95 pub(crate) fn try_attach(
96 &self,
97 target: &StyledElement,
98 host: Option<&StyleHostRef>,
99 type_: FrameType,
100 ) -> SelectorMatchResult {
101 match self.try_attach_checked(target, host, type_) {
102 Ok(result) => result,
103 Err(error) => panic!("{error}"),
104 }
105 }
106
107 pub(crate) fn try_attach_checked(
110 &self,
111 target: &StyledElement,
112 host: Option<&StyleHostRef>,
113 type_: FrameType,
114 ) -> Result<SelectorMatchResult, DuplicateSetterError> {
115 let mut result = SelectorMatchResult::NeverThisType;
116
117 if self.has_setters_or_animations() {
118 crate::perf_count!(StylesEvaluated);
119 let parent = self.parent();
120 let (match_, can_share) = {
121 let selector = self.selector.borrow();
122 match &*selector {
123 Some(selector) => {
124 (selector.match_(target, parent.as_deref(), true), selector.node::<OrSelector>().is_none())
125 }
126 None => {
127 let is_host =
128 host.and_then(StyleHostRef::as_element).is_some_and(|h| std::ptr::eq::<StyledElement>(&**h, target));
129 let match_ = if is_host {
130 match parent.as_deref().and_then(|p| p.downcast_ref::<ContainerQuery>()) {
131 Some(container_query) => match container_query.query() {
132 Some(query) => {
133 let query_parent = container_query.parent();
134 query.match_(
135 target,
136 query_parent.as_deref(),
137 true,
138 container_query.name().as_deref(),
139 )
140 }
141 None => SelectorMatch::NEVER_THIS_INSTANCE,
142 },
143 None => SelectorMatch::ALWAYS_THIS_INSTANCE,
144 }
145 } else {
146 SelectorMatch::NEVER_THIS_INSTANCE
147 };
148 (match_, true)
149 }
150 }
151 };
152
153 result = match_.result();
154
155 if match_.is_match() {
156 crate::perf_count!(StylesMatched);
157 self.try_attach_instance(target, match_.into_activator(), type_, can_share)?;
158 }
159 }
160
161 Ok(result)
162 }
163
164 fn validate_selector(selector: Option<Selector>) -> Option<Selector> {
165 if selector.as_ref().is_some_and(|s| s.node::<TemplateSelector>().is_some()) {
166 panic!("Invalid selector: Template selector must be followed by control selector.");
167 }
168 selector
169 }
170}