1use super::{style_ptr_eq, ControlTheme, IStyle, SelectorMatchResult, Style, StyleHostRef, ThemeVariant};
2use crate::collections::{
3 CollectionChangedHandler, FerroList, NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs,
4 ResetBehavior,
5};
6use crate::controls::{
7 IResourceNode, IResourceProvider, ResourceDictionary, ResourceHostRef, ResourceKey, ResourceValue,
8 WeakResourceHost,
9};
10use crate::property_store::FrameType;
11use crate::reactive::{Disposable, IDisposable};
12use crate::utilities::HandlerList;
13use crate::{
14 ferro_class, instantiate, FerroObject, FerroObjectImpl, FerroObjectImplExt, ObjectType, Ref, StyledElement,
15 Upcast,
16};
17use std::cell::RefCell;
18use std::rc::Rc;
19
20#[repr(C)]
22pub struct Styles {
23 base: FerroObject,
24 styles: FerroList<Rc<dyn IStyle>>,
25 owner: RefCell<Option<WeakResourceHost>>,
26 resources: RefCell<Option<Ref<ResourceDictionary>>>,
27 collection_changed: HandlerList<CollectionChangedHandler<Rc<dyn IStyle>>>,
28 owner_changed: HandlerList<dyn Fn()>,
29}
30
31ferro_class!(Styles: FerroObject);
32crate::ferro_class_info!(Styles {
33 new: Styles::new,
34 interfaces: [Rc<dyn IStyle>, Rc<dyn IResourceProvider>],
35});
36
37impl FerroObjectImpl for Styles {
38 fn constructed(this: &Self) {
39 Self::parent_constructed(this);
40
41 this.styles.set_reset_behavior(ResetBehavior::Remove);
42 let weak = this.to_ref().downgrade();
43 this.styles.add_collection_changed(Rc::new(
44 move |e: &NotifyCollectionChangedEventArgs<'_, Rc<dyn IStyle>>| {
45 if let Some(this) = weak.upgrade() {
46 this.on_collection_changed(e);
47 }
48 },
49 ));
50 this.styles.set_validate(Some(Rc::new(|item: &Rc<dyn IStyle>| {
51 if let Some(theme) = item.as_object().and_then(|o| o.downcast_ref::<ControlTheme>()) {
52 panic!(
53 "ControlTheme (for {}) cannot be added to a Styles collection.",
54 theme.target_type().map(|t| t.name()).unwrap_or_default()
55 );
56 }
57 })));
58 }
59}
60
61impl Styles {
62 pub fn construct() -> Self {
64 Self {
65 base: FerroObject::construct(),
66 styles: FerroList::new(),
67 owner: RefCell::new(None),
68 resources: RefCell::new(None),
69 collection_changed: HandlerList::new(),
70 owner_changed: HandlerList::new(),
71 }
72 }
73
74 pub fn new() -> Ref<Self> {
76 instantiate(Self::construct())
77 }
78
79 pub fn with_owner(owner: impl Into<ResourceHostRef>) -> Ref<Self> {
81 let this = Self::construct();
82 *this.owner.borrow_mut() = Some(owner.into().downgrade());
83 instantiate(this)
84 }
85
86 pub fn collection_changed(
89 &self,
90 handler: impl for<'a> Fn(&NotifyCollectionChangedEventArgs<'a, Rc<dyn IStyle>>) + 'static,
91 ) -> Rc<dyn IDisposable> {
92 let token = self.collection_changed.add(Rc::new(handler));
93 let weak = self.to_ref().downgrade();
94 Disposable::create(move || {
95 if let Some(this) = weak.upgrade() {
96 this.collection_changed.remove(token);
97 }
98 })
99 }
100
101 pub fn owner_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
103 self.subscribe_owner_changed(Rc::new(handler))
104 }
105
106 fn subscribe_owner_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
107 let token = self.owner_changed.add(handler);
108 let weak = self.to_ref().downgrade();
109 Disposable::create(move || {
110 if let Some(this) = weak.upgrade() {
111 this.owner_changed.remove(token);
112 }
113 })
114 }
115
116 #[inline]
118 pub fn count(&self) -> usize {
119 self.styles.count()
120 }
121
122 pub fn owner(&self) -> Option<ResourceHostRef> {
124 let owner = self.owner.borrow().clone();
125 owner.and_then(|o| o.upgrade())
126 }
127
128 fn set_owner(&self, value: Option<&ResourceHostRef>) {
129 let old = self.owner();
130 if old.as_ref() != value {
131 *self.owner.borrow_mut() = value.map(ResourceHostRef::downgrade);
132 if !self.owner_changed.is_empty() {
133 for (_, handler) in self.owner_changed.snapshot().iter() {
134 handler();
135 }
136 }
137 }
138 }
139
140 pub fn resources(&self) -> Ref<ResourceDictionary> {
142 if let Some(resources) = &*self.resources.borrow() {
143 return resources.clone();
144 }
145 let resources = ResourceDictionary::new();
146 self.set_resources(resources.clone());
147 resources
148 }
149
150 pub fn set_resources(&self, value: Ref<ResourceDictionary>) {
151 let current_owner = self.owner();
152
153 if let Some(owner) = ¤t_owner {
154 let old = self.resources.borrow().clone();
155 if let Some(old) = old {
156 old.remove_owner(owner);
157 }
158 }
159
160 *self.resources.borrow_mut() = Some(value.clone());
161
162 if let Some(owner) = ¤t_owner {
163 value.add_owner(owner);
164 }
165 }
166
167 pub fn has_resources(&self) -> bool {
169 if self.resources.borrow().as_ref().is_some_and(|r| r.count() > 0) {
170 return true;
171 }
172
173 if self.styles.is_empty() {
174 return false;
175 }
176
177 self.styles
178 .snapshot()
179 .iter()
180 .any(|i| i.as_resource_provider().is_some_and(|provider| provider.has_resources()))
181 }
182
183 pub fn get(&self, index: usize) -> Rc<dyn IStyle> {
185 self.styles.get(index)
186 }
187
188 pub fn set(&self, index: usize, value: impl Into<Rc<dyn IStyle>>) {
192 self.styles.remove_at(index);
193 self.styles.insert(index, value.into());
194 }
195
196 #[inline]
198 pub fn snapshot(&self) -> Rc<Vec<Rc<dyn IStyle>>> {
199 self.styles.snapshot()
200 }
201
202 pub fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
205 let resources = self.resources.borrow().clone();
206 if let Some(resources) = resources {
207 if let Some(value) = resources.try_get_resource(key, theme) {
208 return Some(value);
209 }
210 }
211
212 if !self.styles.is_empty() {
213 for style in self.styles.snapshot().iter().rev() {
214 if let Some(value) = style.try_get_resource(key, theme) {
215 return Some(value);
216 }
217 }
218 }
219
220 None
221 }
222
223 pub fn add_range(&self, items: impl IntoIterator<Item = Rc<dyn IStyle>>) {
224 self.styles.add_range(items)
225 }
226
227 pub fn insert_range(&self, index: usize, items: impl IntoIterator<Item = Rc<dyn IStyle>>) {
228 self.styles.insert_range(index, items)
229 }
230
231 pub fn move_item(&self, old_index: usize, new_index: usize) {
232 self.styles.move_item(old_index, new_index)
233 }
234
235 pub fn move_range(&self, old_index: usize, count: usize, new_index: usize) {
236 self.styles.move_range(old_index, count, new_index)
237 }
238
239 pub fn remove_all(&self, items: impl IntoIterator<Item = Rc<dyn IStyle>>) {
240 for item in items {
241 self.remove(item);
242 }
243 }
244
245 pub fn remove_range(&self, index: usize, count: usize) {
246 self.styles.remove_range(index, count)
247 }
248
249 pub fn index_of(&self, item: impl Into<Rc<dyn IStyle>>) -> Option<usize> {
251 let item = item.into();
252 self.styles.snapshot().iter().position(|s| style_ptr_eq(s, &item))
253 }
254
255 pub fn insert(&self, index: usize, item: impl Into<Rc<dyn IStyle>>) {
256 self.styles.insert(index, item.into())
257 }
258
259 pub fn remove_at(&self, index: usize) {
260 self.styles.remove_at(index)
261 }
262
263 pub fn add(&self, item: impl Into<Rc<dyn IStyle>>) {
265 self.styles.add(item.into())
266 }
267
268 pub fn clear(&self) {
270 self.styles.clear()
271 }
272
273 pub fn contains(&self, item: impl Into<Rc<dyn IStyle>>) -> bool {
274 self.index_of(item).is_some()
275 }
276
277 pub fn remove(&self, item: impl Into<Rc<dyn IStyle>>) -> bool {
279 match self.index_of(item) {
280 Some(index) => {
281 self.styles.remove_at(index);
282 true
283 }
284 None => false,
285 }
286 }
287
288 pub fn add_owner(&self, owner: &ResourceHostRef) {
290 if self.owner().is_some() {
291 panic!("The Styles already has a owner.");
292 }
293
294 self.set_owner(Some(owner));
295 let resources = self.resources.borrow().clone();
296 if let Some(resources) = resources {
297 resources.add_owner(owner);
298 }
299
300 for child in self.styles.snapshot().iter() {
301 if let Some(r) = child.as_resource_provider() {
302 r.add_owner(owner);
303 }
304 }
305 }
306
307 pub fn remove_owner(&self, owner: &ResourceHostRef) {
309 if self.owner().as_ref() == Some(owner) {
310 self.set_owner(None);
311 let resources = self.resources.borrow().clone();
312 if let Some(resources) = resources {
313 resources.remove_owner(owner);
314 }
315
316 for child in self.styles.snapshot().iter() {
317 if let Some(r) = child.as_resource_provider() {
318 r.remove_owner(owner);
319 }
320 }
321 }
322 }
323
324 #[allow(dead_code)]
327 pub(crate) fn try_attach(&self, target: &StyledElement, host: Option<&StyleHostRef>) -> SelectorMatchResult {
328 let mut result = SelectorMatchResult::NeverThisType;
329
330 for s in self.styles.snapshot().iter() {
331 let Some(style) = s.as_object().and_then(|o| o.downcast_ref::<Style>()) else { continue };
332
333 let r = style.try_attach(target, host, FrameType::Style);
334 if r > result {
335 result = r;
336 }
337 }
338
339 result
340 }
341
342 fn internal_add(items: &[Rc<dyn IStyle>], owner: Option<&ResourceHostRef>) {
343 if let Some(owner) = owner {
344 for item in items {
345 if let Some(provider) = item.as_resource_provider() {
346 provider.add_owner(owner);
347 }
348 }
349
350 if let Some(host) = owner.as_style_host() {
351 host.styles_added(items);
352 }
353 }
354 }
355
356 fn internal_remove(items: &[Rc<dyn IStyle>], owner: Option<&ResourceHostRef>) {
357 if let Some(owner) = owner {
358 for item in items {
359 if let Some(provider) = item.as_resource_provider() {
360 provider.remove_owner(owner);
361 }
362 }
363
364 if let Some(host) = owner.as_style_host() {
365 host.styles_removed(items);
366 }
367 }
368 }
369
370 fn on_collection_changed(&self, e: &NotifyCollectionChangedEventArgs<'_, Rc<dyn IStyle>>) {
371 if e.action == NotifyCollectionChangedAction::Reset {
372 panic!("Reset should not be called on Styles.");
373 }
374
375 let current_owner = self.owner();
376
377 match e.action {
378 NotifyCollectionChangedAction::Add => Self::internal_add(e.new_items, current_owner.as_ref()),
379 NotifyCollectionChangedAction::Remove => Self::internal_remove(e.old_items, current_owner.as_ref()),
380 NotifyCollectionChangedAction::Replace => {
381 Self::internal_remove(e.old_items, current_owner.as_ref());
382 Self::internal_add(e.new_items, current_owner.as_ref());
383 }
384 _ => {}
385 }
386
387 if !self.collection_changed.is_empty() {
388 for (_, handler) in self.collection_changed.snapshot().iter() {
389 handler(e);
390 }
391 }
392 }
393}
394
395struct StylesHandle(Ref<Styles>);
397
398impl IResourceNode for StylesHandle {
399 fn reference_id(&self) -> *const () {
400 let object: &FerroObject = (*self.0).upcast();
401 object as *const FerroObject as *const ()
402 }
403
404 fn has_resources(&self) -> bool {
405 self.0.has_resources()
406 }
407
408 fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
409 self.0.try_get_resource(key, theme)
410 }
411}
412
413impl IResourceProvider for StylesHandle {
414 fn owner(&self) -> Option<ResourceHostRef> {
415 self.0.owner()
416 }
417
418 fn owner_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
419 self.0.subscribe_owner_changed(handler)
420 }
421
422 fn add_owner(&self, owner: &ResourceHostRef) {
423 self.0.add_owner(owner)
424 }
425
426 fn remove_owner(&self, owner: &ResourceHostRef) {
427 self.0.remove_owner(owner)
428 }
429
430 fn as_object(&self) -> Option<&FerroObject> {
431 Some((*self.0).upcast())
432 }
433}
434
435impl IStyle for StylesHandle {
436 fn children(&self) -> Rc<Vec<Rc<dyn IStyle>>> {
437 self.0.styles.snapshot()
438 }
439
440 fn as_object(&self) -> Option<&FerroObject> {
441 Some((*self.0).upcast())
442 }
443
444 fn as_resource_provider(&self) -> Option<&dyn IResourceProvider> {
445 Some(self)
446 }
447}
448
449pub fn styles_as_style<T: ObjectType + Upcast<Styles>>(styles: &Ref<T>) -> Rc<dyn IStyle> {
452 Rc::new(StylesHandle(styles.clone().upcast()))
453}
454
455impl From<Ref<Styles>> for Rc<dyn IStyle> {
456 fn from(value: Ref<Styles>) -> Self {
457 Rc::new(StylesHandle(value))
458 }
459}
460
461impl From<Ref<Styles>> for Rc<dyn IResourceProvider> {
462 fn from(value: Ref<Styles>) -> Self {
463 Rc::new(StylesHandle(value))
464 }
465}
466
467impl From<&Ref<Styles>> for Rc<dyn IResourceProvider> {
468 fn from(value: &Ref<Styles>) -> Self {
469 Rc::new(StylesHandle(value.clone()))
470 }
471}
472
473impl From<&Ref<Styles>> for Rc<dyn IStyle> {
474 fn from(value: &Ref<Styles>) -> Self {
475 Rc::new(StylesHandle(value.clone()))
476 }
477}