1use crate::animation::Animatable;
2use crate::collections::{FerroList, NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs, ResetBehavior};
3use crate::controls::{
4 Classes, IPseudoClasses, IResourceHost, IResourceNode, ResourceDictionary, ResourceHostRef, ResourceKey,
5 ResourceValue, ResourcesChangedEventArgs,
6};
7use crate::data::BindingPriority;
8use crate::logical_tree::{IChildIndexProvider, LogicalTreeAttachmentEventArgs};
9use crate::property_store::FrameType;
10use crate::reactive::{Disposable, IDisposable};
11use crate::styling::{
12 DuplicateSetterError, ContainerQuery, ControlTheme, IStyle, IStyleHost, IThemeVariantHost, Style, StyleBase, StyleHostRef,
13 StyleInstance, Styles, ThemeVariant,
14};
15use crate::utilities::HandlerList;
16use crate::InitializationError;
17use crate::{
18 ferro_class, ferro_property, instantiate, AnyValue, BoxedValue, DirectProperty, FerroObject, FerroObjectImpl,
19 FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Nullable, Ref, StyledProperty,
20 StyledPropertyOptions, TypeInfo, WeakRef,
21};
22use std::cell::{Cell, OnceCell, RefCell};
23use std::rc::Rc;
24
25#[repr(C)]
32pub struct StyledElement {
33 base: Animatable,
34 init_count: Cell<u32>,
35 name: RefCell<Option<String>>,
36 classes: OnceCell<Classes>,
37 logical_root: RefCell<Option<WeakRef<StyledElement>>>,
38 logical_children: OnceCell<FerroList<Ref<StyledElement>>>,
39 parent: RefCell<Option<WeakRef<StyledElement>>>,
40 resources: RefCell<Option<Ref<ResourceDictionary>>>,
41 styles: OnceCell<Ref<Styles>>,
42 styles_applied: Cell<bool>,
43 theme_applied: Cell<bool>,
44 templated_parent_theme_applied: Cell<bool>,
45 templated_parent: RefCell<Option<WeakRef<FerroObject>>>,
46 data_context_updating: Cell<bool>,
47 implicit_theme: RefCell<ImplicitTheme>,
48 last_resources_changed_event_args: Cell<ResourcesChangedEventArgs>,
49 is_initialized: Cell<bool>,
50 removed_from_logical_parent: Cell<bool>,
54 attached_to_logical_tree: HandlerList<dyn Fn(&LogicalTreeAttachmentEventArgs)>,
55 detached_from_logical_tree: HandlerList<dyn Fn(&LogicalTreeAttachmentEventArgs)>,
56 data_context_changed: HandlerList<dyn Fn()>,
57 initialized: HandlerList<dyn Fn()>,
58 resources_changed: HandlerList<dyn Fn(&ResourcesChangedEventArgs)>,
59 actual_theme_variant_changed: HandlerList<dyn Fn()>,
60}
61
62enum ImplicitTheme {
64 Unknown,
66 Invalid,
68 Theme(Ref<ControlTheme>),
69}
70
71ferro_class! {
72 StyledElement: Animatable, virtuals StyledElementImpl: FerroObjectImpl {
73 fn begin_init(this);
76 fn try_end_init(this) -> Result<(), InitializationError>;
85 fn logical_children_collection_changed(this, e: &NotifyCollectionChangedEventArgs<'_, Ref<StyledElement>>);
87 fn on_attached_to_logical_tree(this, e: &LogicalTreeAttachmentEventArgs);
89 fn on_detached_from_logical_tree(this, e: &LogicalTreeAttachmentEventArgs);
91 fn on_data_context_changed(this);
93 fn on_data_context_begin_update(this);
95 fn on_data_context_end_update(this);
97 fn on_initialized(this);
99 fn style_key_override(this) -> &'static TypeInfo;
102 fn on_control_theme_changed(this);
104 fn on_templated_parent_control_theme_changed(this);
106 fn invalidate_styles(this, recurse: bool);
109 fn is_logical_root(this) -> bool;
111 fn styling_parent(this) -> Option<StyleHostRef>;
115 fn notify_child_resources_changed(this, e: ResourcesChangedEventArgs);
118 fn child_index_provider(this) -> Option<Rc<dyn IChildIndexProvider>>;
121 fn is_theme_variant_root(this) -> bool;
125 }
126}
127crate::ferro_class_info!(StyledElement {
128 new: StyledElement::new,
129 interfaces: [
130 Rc<dyn crate::ISupportInitialize>,
131 Rc<dyn crate::INamed>,
132 crate::controls::ResourceHostRef,
133 crate::styling::StyleHostRef,
134 ],
135});
136
137crate::ferro_overrides! { impl FerroObjectImpl for StyledElement {
138 fn constructed(this: &Self) {
139 Self::parent_constructed(this);
140 if this.is_logical_root() {
141 *this.logical_root.borrow_mut() = Some(this.to_ref().downgrade());
142 }
143 }
144
145 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
146 Self::parent_on_property_changed(this, change);
147
148 if change.property() == Self::theme_property().as_property() {
149 this.on_control_theme_changed();
150 } else if change.property() == ThemeVariant::actual_theme_variant_property().as_property()
151 && !this.actual_theme_variant_changed.is_empty()
152 {
153 for (_, handler) in this.actual_theme_variant_changed.snapshot().iter() {
154 handler();
155 }
156 }
157 }
158} }
159
160impl StyledElementImpl for StyledElement {
161 fn begin_init(this: &Self) {
162 this.init_count.set(this.init_count.get() + 1);
163 }
164
165 fn try_end_init(this: &Self) -> Result<(), InitializationError> {
166 let count = this.init_count.get();
167 assert!(count != 0, "BeginInit was not called.");
168 this.init_count.set(count - 1);
169 if count == 1 && this.logical_root.borrow().is_some() {
170 this.try_apply_styling()?;
171 this.initialize_if_needed();
172 }
173 Ok(())
174 }
175
176 fn logical_children_collection_changed(this: &Self, e: &NotifyCollectionChangedEventArgs<'_, Ref<StyledElement>>) {
177 match e.action {
178 NotifyCollectionChangedAction::Add => this.set_logical_parent_of(e.new_items),
179 NotifyCollectionChangedAction::Remove => this.clear_logical_parent_of(e.old_items),
180 NotifyCollectionChangedAction::Replace => {
181 this.clear_logical_parent_of(e.old_items);
182 this.set_logical_parent_of(e.new_items);
183 }
184 NotifyCollectionChangedAction::Reset => {
185 panic!("Reset should not be signaled on LogicalChildren collection")
186 }
187 NotifyCollectionChangedAction::Move => {}
188 }
189 }
190
191 fn on_attached_to_logical_tree(_this: &Self, _e: &LogicalTreeAttachmentEventArgs) {}
192
193 fn on_detached_from_logical_tree(_this: &Self, _e: &LogicalTreeAttachmentEventArgs) {}
194
195 fn on_data_context_changed(this: &Self) {
196 if !this.data_context_changed.is_empty() {
197 for (_, handler) in this.data_context_changed.snapshot().iter() {
198 handler();
199 }
200 }
201 }
202
203 fn on_data_context_begin_update(_this: &Self) {}
204
205 fn on_data_context_end_update(_this: &Self) {}
206
207 fn on_initialized(_this: &Self) {}
208
209 fn style_key_override(this: &Self) -> &'static TypeInfo {
210 this.get_type()
211 }
212
213 fn on_control_theme_changed(this: &Self) {
214 let values = this.values();
215 values.begin_styling();
216 values.remove_frames(this, FrameType::Theme);
217 values.end_styling(this);
218 this.theme_applied.set(false);
219 }
220
221 fn on_templated_parent_control_theme_changed(this: &Self) {
222 let values = this.values();
223 values.begin_styling();
224 values.remove_frames(this, FrameType::TemplatedParentTheme);
225 values.end_styling(this);
226 this.templated_parent_theme_applied.set(false);
227 }
228
229 fn invalidate_styles(this: &Self, recurse: bool) {
230 let values = this.values();
231 values.begin_styling();
232 values.remove_frames(this, FrameType::Style);
233 values.end_styling(this);
234 this.styles_applied.set(false);
235
236 if recurse {
237 for child in this.inheritance_children() {
238 if let Some(child) = child.downcast_ref::<StyledElement>() {
239 child.invalidate_styles(recurse);
240 }
241 }
242 }
243 }
244
245 fn is_logical_root(_this: &Self) -> bool {
246 false
247 }
248
249 fn styling_parent(this: &Self) -> Option<StyleHostRef> {
250 this.inheritance_parent().and_then(|p| p.downcast::<StyledElement>().ok()).map(StyleHostRef::Element)
255 }
256
257 fn is_theme_variant_root(_this: &Self) -> bool {
258 false
259 }
260
261 fn notify_child_resources_changed(this: &Self, e: ResourcesChangedEventArgs) {
262 if let Some(children) = this.logical_children.get() {
263 if !children.is_empty() {
264 for child in children.snapshot().iter() {
265 child.notify_resources_changed(e, true);
266 }
267 }
268 }
269 }
270
271 fn child_index_provider(_this: &Self) -> Option<Rc<dyn IChildIndexProvider>> {
272 None
273 }
274}
275
276crate::ferro_properties! { impl StyledElement, also [
279 crate::styling::ThemeVariant::actual_theme_variant_property,
280 crate::styling::ThemeVariant::requested_theme_variant_property,
281] {
282 ferro_property!(
283 pub fn data_context_property() -> StyledProperty<Option<BoxedValue>> {
285 let property = FerroProperty::register_with::<StyledElement, _>(
286 "DataContext",
287 StyledPropertyOptions::new(None).inherits(true).notifying(Self::data_context_notifying),
288 );
289 property.changed().add_class_handler::<StyledElement>(|x, _| x.on_data_context_changed());
290 property
291 }
292 );
293
294 ferro_property!(
295 pub fn name_property() -> DirectProperty<StyledElement, Option<String>> {
297 FerroProperty::register_direct::<StyledElement, _>("Name", |o| o.name(), Some(|o, v| o.set_name(v)), None)
298 }
299 );
300
301 ferro_property!(
302 pub fn parent_property() -> DirectProperty<StyledElement, Option<Ref<StyledElement>>> {
304 FerroProperty::register_direct::<StyledElement, _>("Parent", |o| o.parent(), None, None)
305 }
306 );
307
308 ferro_property!(
309 pub fn templated_parent_property() -> DirectProperty<StyledElement, Option<Ref<FerroObject>>> {
311 FerroProperty::register_direct::<StyledElement, _>(
312 "TemplatedParent",
313 |o| o.templated_parent(),
314 None,
315 None,
316 )
317 }
318 );
319
320 ferro_property!(
321 pub fn theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
323 FerroProperty::register::<StyledElement, _>("Theme", None)
324 }
325 );
326} }
327
328impl StyledElement {
329 pub fn construct() -> Self {
331 Self {
332 base: Animatable::construct(),
333 init_count: Cell::new(0),
334 name: RefCell::new(None),
335 classes: OnceCell::new(),
336 logical_root: RefCell::new(None),
337 logical_children: OnceCell::new(),
338 parent: RefCell::new(None),
339 resources: RefCell::new(None),
340 styles: OnceCell::new(),
341 styles_applied: Cell::new(false),
342 theme_applied: Cell::new(false),
343 templated_parent_theme_applied: Cell::new(false),
344 templated_parent: RefCell::new(None),
345 data_context_updating: Cell::new(false),
346 implicit_theme: RefCell::new(ImplicitTheme::Unknown),
347 last_resources_changed_event_args: Cell::new(ResourcesChangedEventArgs::default()),
348 is_initialized: Cell::new(false),
349 removed_from_logical_parent: Cell::new(false),
350 attached_to_logical_tree: HandlerList::new(),
351 detached_from_logical_tree: HandlerList::new(),
352 data_context_changed: HandlerList::new(),
353 initialized: HandlerList::new(),
354 resources_changed: HandlerList::new(),
355 actual_theme_variant_changed: HandlerList::new(),
356 }
357 }
358
359 pub fn new() -> Ref<Self> {
360 instantiate(Self::construct())
361 }
362
363 pub fn attached_to_logical_tree(
367 &self,
368 handler: impl Fn(&LogicalTreeAttachmentEventArgs) + 'static,
369 ) -> Rc<dyn IDisposable> {
370 let token = self.attached_to_logical_tree.add(Rc::new(handler));
371 let weak = self.to_ref().downgrade();
372 Disposable::create(move || {
373 if let Some(this) = weak.upgrade() {
374 this.attached_to_logical_tree.remove(token);
375 }
376 })
377 }
378
379 pub fn detached_from_logical_tree(
381 &self,
382 handler: impl Fn(&LogicalTreeAttachmentEventArgs) + 'static,
383 ) -> Rc<dyn IDisposable> {
384 let token = self.detached_from_logical_tree.add(Rc::new(handler));
385 let weak = self.to_ref().downgrade();
386 Disposable::create(move || {
387 if let Some(this) = weak.upgrade() {
388 this.detached_from_logical_tree.remove(token);
389 }
390 })
391 }
392
393 pub fn data_context_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
395 let token = self.data_context_changed.add(Rc::new(handler));
396 let weak = self.to_ref().downgrade();
397 Disposable::create(move || {
398 if let Some(this) = weak.upgrade() {
399 this.data_context_changed.remove(token);
400 }
401 })
402 }
403
404 pub fn initialized(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
406 let token = self.initialized.add(Rc::new(handler));
407 let weak = self.to_ref().downgrade();
408 Disposable::create(move || {
409 if let Some(this) = weak.upgrade() {
410 this.initialized.remove(token);
411 }
412 })
413 }
414
415 pub fn resources_changed(
418 &self,
419 handler: impl Fn(&ResourcesChangedEventArgs) + 'static,
420 ) -> Rc<dyn IDisposable> {
421 self.subscribe_resources_changed(Rc::new(handler))
422 }
423
424 fn subscribe_resources_changed(&self, handler: Rc<dyn Fn(&ResourcesChangedEventArgs)>) -> Rc<dyn IDisposable> {
425 let token = self.resources_changed.add(handler);
426 let weak = self.to_ref().downgrade();
427 Disposable::create(move || {
428 if let Some(this) = weak.upgrade() {
429 this.resources_changed.remove(token);
430 }
431 })
432 }
433
434 pub fn actual_theme_variant_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
436 self.subscribe_actual_theme_variant_changed(Rc::new(handler))
437 }
438
439 fn subscribe_actual_theme_variant_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
440 let token = self.actual_theme_variant_changed.add(handler);
441 let weak = self.to_ref().downgrade();
442 Disposable::create(move || {
443 if let Some(this) = weak.upgrade() {
444 this.actual_theme_variant_changed.remove(token);
445 }
446 })
447 }
448
449 pub fn name(&self) -> Option<String> {
456 self.name.borrow().clone()
457 }
458
459 pub fn set_name(&self, value: Option<String>) {
460 assert!(!self.styles_applied.get(), "Cannot set Name : styled element already styled.");
461 *self.name.borrow_mut() = value;
462 }
463
464 pub fn classes(&self) -> Classes {
469 self.classes_ref().clone()
470 }
471
472 #[inline]
473 fn classes_ref(&self) -> &Classes {
474 self.classes.get_or_init(Classes::new)
475 }
476
477 pub fn pseudo_classes(&self) -> &dyn IPseudoClasses {
479 self.classes_ref()
480 }
481
482 pub fn data_context(&self) -> Option<BoxedValue> {
487 self.get_value(Self::data_context_property())
488 }
489
490 pub fn set_data_context(&self, value: Option<BoxedValue>) {
491 self.set_value(Self::data_context_property(), value)
492 }
493
494 pub fn is_initialized(&self) -> bool {
499 self.is_initialized.get()
500 }
501
502 pub fn styles(&self) -> Ref<Styles> {
507 self.styles.get_or_init(|| Styles::with_owner(self.to_ref())).clone()
508 }
509
510 pub fn is_styles_initialized(&self) -> bool {
512 self.styles.get().is_some()
513 }
514
515 pub fn style_key(&self) -> &'static TypeInfo {
517 self.style_key_override()
518 }
519
520 pub fn resources(&self) -> Ref<ResourceDictionary> {
522 if let Some(resources) = &*self.resources.borrow() {
523 return resources.clone();
524 }
525 let resources = ResourceDictionary::with_owner(self.to_ref());
526 *self.resources.borrow_mut() = Some(resources.clone());
527 resources
528 }
529
530 pub fn set_resources(&self, value: Ref<ResourceDictionary>) {
531 let this = ResourceHostRef::Element(self.to_ref());
532 let old = self.resources.borrow_mut().take();
533 if let Some(old) = old {
534 old.remove_owner(&this);
535 }
536 *self.resources.borrow_mut() = Some(value.clone());
537 value.add_owner(&this);
538 }
539
540 pub fn theme(&self) -> Option<Ref<ControlTheme>> {
545 self.get_value(Self::theme_property())
546 }
547
548 pub fn set_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
549 self.set_value(Self::theme_property(), value.into().0)
550 }
551
552 pub fn actual_theme_variant(&self) -> Option<ThemeVariant> {
556 self.get_value(ThemeVariant::actual_theme_variant_property())
557 }
558
559 pub fn has_resources(&self) -> bool {
561 self.resources.borrow().as_ref().is_some_and(|r| r.has_resources())
562 || self.styles.get().is_some_and(|s| s.has_resources())
563 }
564
565 pub fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
568 let resources = self.resources.borrow().clone();
569 if let Some(resources) = resources {
570 if let Some(value) = resources.try_get_resource(key, theme) {
571 return Some(value);
572 }
573 }
574 match self.styles.get() {
575 Some(styles) => styles.try_get_resource(key, theme),
576 None => None,
577 }
578 }
579
580 pub fn try_find_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
583 let host: &dyn IResourceHost = self;
584 host.try_find_resource(key, theme)
585 }
586
587 pub fn find_resource(&self, key: &ResourceKey) -> ResourceValue {
590 let host: &dyn IResourceHost = self;
591 host.find_resource(key)
592 }
593
594 pub fn templated_parent(&self) -> Option<Ref<FerroObject>> {
596 self.templated_parent.borrow().as_ref().and_then(WeakRef::upgrade)
597 }
598
599 pub fn set_templated_parent(&self, value: impl Into<Nullable<FerroObject>>) {
602 let value = value.into().0;
603 let old = self.templated_parent();
604 if old != value {
605 *self.templated_parent.borrow_mut() = value.as_ref().map(Ref::downgrade);
606 self.raise_direct_property_changed(Self::templated_parent_property(), &old, &value);
607 }
608 }
609
610 pub fn logical_children(&self) -> &FerroList<Ref<StyledElement>> {
612 self.logical_children.get_or_init(|| {
613 let list = FerroList::new();
614 list.set_reset_behavior(ResetBehavior::Remove);
615 let weak = self.to_ref().downgrade();
616 list.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, Ref<StyledElement>>| {
617 if let Some(this) = weak.upgrade() {
618 this.logical_children_collection_changed(e);
619 }
620 }));
621 list
622 })
623 }
624
625 pub fn is_attached_to_logical_tree(&self) -> bool {
627 self.logical_root.borrow().is_some()
628 }
629
630 pub fn parent(&self) -> Option<Ref<StyledElement>> {
632 self.parent.borrow().as_ref().and_then(WeakRef::upgrade)
633 }
634
635 pub fn apply_styling(&self) -> bool {
646 match self.try_apply_styling() {
647 Ok(applied) => applied,
648 Err(error) => panic!("{error}"),
649 }
650 }
651
652 pub fn try_apply_styling(&self) -> Result<bool, DuplicateSetterError> {
658 if self.init_count.get() == 0
659 && (!self.styles_applied.get()
660 || !self.theme_applied.get()
661 || !self.templated_parent_theme_applied.get())
662 {
663 let values = self.values();
664 values.begin_styling();
665 let _end_styling = EndStyling(self);
666 if !self.theme_applied.get() {
667 self.apply_control_theme()?;
668 self.theme_applied.set(true);
669 }
670 if !self.templated_parent_theme_applied.get() {
671 self.apply_templated_parent_control_theme()?;
672 self.templated_parent_theme_applied.set(true);
673 }
674 if !self.styles_applied.get() {
675 self.apply_styles(&StyleHostRef::Element(self.to_ref()))?;
676 self.styles_applied.set(true);
677 }
678 }
679 Ok(self.styles_applied.get())
680 }
681
682 pub fn end_init(&self) {
686 if let Err(error) = self.try_end_init() {
687 panic!("{error}");
688 }
689 }
690
691 pub fn initialize_if_needed(&self) {
694 if self.init_count.get() == 0 && !self.is_initialized.get() {
695 self.is_initialized.set(true);
696 self.on_initialized();
697 if !self.initialized.is_empty() {
698 for (_, handler) in self.initialized.snapshot().iter() {
699 handler();
700 }
701 }
702 }
703 }
704
705 pub fn set_parent(&self, parent: impl Into<Nullable<StyledElement>>) {
712 let parent = parent.into().0;
713 let old = self.parent();
714 if parent == old {
715 return;
716 }
717 assert!(old.is_none() || parent.is_none(), "The Control already has a parent.");
718
719 if self.inheritance_parent().is_none() || parent.is_none() {
720 self.set_inheritance_parent(parent.clone().map(Ref::upcast::<FerroObject>));
721 }
722
723 *self.parent.borrow_mut() = parent.as_ref().map(Ref::downgrade);
724
725 let this = self.to_ref();
726 let old_root = self.logical_root();
727 if let Some(root) = old_root {
728 let e = LogicalTreeAttachmentEventArgs::new(root, this.clone(), old.clone());
729 self.on_detached_from_logical_tree_core(&e);
730 }
731
732 if let Some(new_root) = self.find_logical_root() {
733 let e = LogicalTreeAttachmentEventArgs::new(new_root, this, parent.clone());
734 self.on_attached_to_logical_tree_core(&e);
735 } else if parent.is_none() {
736 self.notify_resources_changed(ResourcesChangedEventArgs::create(), true);
746 }
747
748 self.raise_direct_property_changed(Self::parent_property(), &old, &parent);
749 }
750
751 pub fn notify_attached_to_logical_tree(&self, e: &LogicalTreeAttachmentEventArgs) {
756 self.on_attached_to_logical_tree_core(e);
757 }
758
759 pub fn notify_detached_from_logical_tree(&self, e: &LogicalTreeAttachmentEventArgs) {
762 self.on_detached_from_logical_tree_core(e);
763 }
764
765 fn logical_root(&self) -> Option<Ref<StyledElement>> {
766 self.logical_root.borrow().as_ref().and_then(WeakRef::upgrade)
767 }
768
769 fn find_logical_root(&self) -> Option<Ref<StyledElement>> {
770 if self.is_logical_root() {
771 return Some(self.to_ref());
772 }
773 let mut current = self.styling_parent();
774 while let Some(host) = current {
775 if let StyleHostRef::Element(e) = &host {
776 if e.is_logical_root() {
777 return Some(e.clone());
778 }
779 }
780 current = host.styling_parent();
781 }
782 None
783 }
784
785 fn on_attached_to_logical_tree_core(&self, e: &LogicalTreeAttachmentEventArgs) {
786 if self.parent().is_none() && !self.is_logical_root() {
787 panic!(
788 "AttachedToLogicalTreeCore called for '{}' but control has no logical parent.",
789 self.get_type().name()
790 );
791 }
792
793 if self.logical_root.borrow().is_none() {
801 crate::perf_count!(LogicalTreeAttachments);
802 *self.logical_root.borrow_mut() = Some(e.root().downgrade());
803 self.reevaluate_implicit_theme();
804 self.apply_styling();
805 self.notify_resources_changed(ResourcesChangedEventArgs::create(), false);
806 self.on_attached_to_logical_tree(e);
807 if !self.attached_to_logical_tree.is_empty() {
808 for (_, handler) in self.attached_to_logical_tree.snapshot().iter() {
809 handler(e);
810 }
811 }
812 }
813
814 if let Some(children) = self.logical_children.get() {
815 for child in children.snapshot().iter() {
818 let attached_to_root = child.logical_root().is_some_and(|r| r == *e.root());
819 if !attached_to_root && (child.parent().is_some() || !child.removed_from_logical_parent.get()) {
820 child.on_attached_to_logical_tree_core(e);
821 }
822 }
823 }
824 }
825
826 fn on_detached_from_logical_tree_core(&self, e: &LogicalTreeAttachmentEventArgs) {
827 if self.logical_root.borrow().is_some() {
828 crate::perf_count!(LogicalTreeDetachments);
829 *self.logical_root.borrow_mut() = None;
830 self.invalidate_styles(false);
831 self.on_detached_from_logical_tree(e);
832 if !self.detached_from_logical_tree.is_empty() {
833 for (_, handler) in self.detached_from_logical_tree.snapshot().iter() {
834 handler(e);
835 }
836 }
837
838 if let Some(children) = self.logical_children.get() {
839 for child in children.snapshot().iter() {
840 child.on_detached_from_logical_tree_core(e);
841 }
842 }
843 }
844 }
845
846 fn set_logical_parent_of(&self, children: &[Ref<StyledElement>]) {
847 for child in children {
848 child.removed_from_logical_parent.set(false);
849 if child.parent().is_none() {
850 child.set_parent(self.to_ref());
851 }
852 }
853 }
854
855 fn clear_logical_parent_of(&self, children: &[Ref<StyledElement>]) {
856 let this = self.to_ref();
857 for child in children {
858 child.removed_from_logical_parent.set(true);
859 if child.parent().is_some_and(|p| p == this) {
860 child.set_parent(None);
861 }
862 }
863 }
864
865 pub fn get_effective_theme(&self) -> Option<Ref<ControlTheme>> {
872 if let Some(theme) = self.theme() {
874 return Some(theme);
875 }
876
877 if matches!(*self.implicit_theme.borrow(), ImplicitTheme::Unknown) {
880 crate::perf_count!(ImplicitThemeLookups);
881 let key = ResourceKey::Type(self.style_key());
882 let found = self.try_find_resource(&key, None).flatten().and_then(|value| {
883 let value: &dyn AnyValue = &*value;
884 value.downcast_ref::<Ref<ControlTheme>>().cloned()
885 });
886 *self.implicit_theme.borrow_mut() = match found {
887 Some(theme) => ImplicitTheme::Theme(theme),
888 None => ImplicitTheme::Invalid,
889 };
890 }
891
892 match &*self.implicit_theme.borrow() {
893 ImplicitTheme::Theme(theme) => Some(theme.clone()),
894 _ => None,
895 }
896 }
897
898 fn apply_control_theme(&self) -> Result<(), DuplicateSetterError> {
899 if let Some(theme) = self.get_effective_theme() {
900 self.apply_control_theme_core(&theme, FrameType::Theme)?;
901 }
902 Ok(())
903 }
904
905 fn apply_templated_parent_control_theme(&self) -> Result<(), DuplicateSetterError> {
906 if self.templated_parent.borrow().is_none() {
909 return Ok(());
910 }
911 let parent_theme = self
912 .templated_parent()
913 .and_then(|p| p.downcast_ref::<StyledElement>().and_then(StyledElement::get_effective_theme));
914 if let Some(parent_theme) = parent_theme {
915 self.apply_control_theme_core(&parent_theme, FrameType::TemplatedParentTheme)?;
916 }
917 Ok(())
918 }
919
920 fn apply_control_theme_core(&self, theme: &ControlTheme, type_: FrameType) -> Result<(), DuplicateSetterError> {
921 debug_assert!(matches!(type_, FrameType::Theme | FrameType::TemplatedParentTheme));
922
923 if let Some(based_on) = theme.based_on() {
924 self.apply_control_theme_core(&based_on, type_)?;
925 }
926
927 theme.try_attach_checked(self, type_)?;
928
929 if theme.has_children() {
930 let children = theme.children().snapshot();
931 for child in children.iter() {
932 self.apply_style(&**child, None, type_)?;
933 }
934 }
935 Ok(())
936 }
937
938 fn apply_styles(&self, host: &StyleHostRef) -> Result<(), DuplicateSetterError> {
939 if let Some(parent) = host.styling_parent() {
940 self.apply_styles(&parent)?;
941 }
942 crate::perf_count!(StyleHostsWalked);
943
944 if host.is_styles_initialized() {
945 let styles = host.styles().snapshot();
946 for style in styles.iter() {
947 self.apply_style(&**style, Some(host), FrameType::Style)?;
948 }
949 }
950 Ok(())
951 }
952
953 fn apply_style(
954 &self,
955 style: &dyn IStyle,
956 host: Option<&StyleHostRef>,
957 type_: FrameType,
958 ) -> Result<(), DuplicateSetterError> {
959 if let Some(base) = style.as_object().and_then(|o| o.downcast_ref::<StyleBase>()) {
960 if let Some(s) = base.downcast_ref::<Style>() {
961 s.try_attach_checked(self, host, type_)?;
962 } else if let Some(m) = base.downcast_ref::<ContainerQuery>() {
963 m.try_attach_checked(self, host, type_)?;
964 }
965
966 if !base.has_children() {
968 return Ok(());
969 }
970 }
971
972 let children = style.children();
973 for child in children.iter() {
974 self.apply_style(&**child, host, type_)?;
975 }
976 Ok(())
977 }
978
979 fn reevaluate_implicit_theme(&self) {
980 if self.theme().is_some() {
983 return;
984 }
985
986 let old_implicit_theme = match self.implicit_theme.replace(ImplicitTheme::Unknown) {
988 ImplicitTheme::Theme(theme) => Some(theme),
989 _ => None,
990 };
991 let new_implicit_theme = self.get_effective_theme();
992
993 if new_implicit_theme != old_implicit_theme {
995 self.on_control_theme_changed();
996 self.theme_applied.set(false);
997 }
998 }
999
1000 pub fn styles_added(&self, styles: &[Rc<dyn IStyle>]) {
1003 if Self::has_setters_or_animations(styles) {
1004 self.invalidate_styles(true);
1005 }
1006 }
1007
1008 pub fn styles_removed(&self, styles: &[Rc<dyn IStyle>]) {
1011 let mut all_styles = Vec::new();
1012 Self::flatten_styles(styles, &mut all_styles);
1013 if !all_styles.is_empty() {
1014 self.detach_styles(&all_styles);
1015 }
1016 }
1017
1018 fn detach_styles(&self, styles: &[Ref<StyleBase>]) {
1019 let values = self.values();
1020 values.begin_styling();
1021 values.remove_frames_where(self, |frame| {
1022 frame
1023 .as_any()
1024 .downcast_ref::<StyleInstance>()
1025 .is_some_and(|instance| styles.iter().any(|style| instance.is_source(style)))
1026 });
1027 values.end_styling(self);
1028
1029 if let Some(children) = self.logical_children.get() {
1030 if !children.is_empty() {
1031 for child in children.snapshot().iter() {
1032 child.detach_styles(styles);
1033 }
1034 }
1035 }
1036 }
1037
1038 pub fn notify_resources_changed(&self, e: ResourcesChangedEventArgs, propagate: bool) {
1041 if e == self.last_resources_changed_event_args.get() {
1043 return;
1044 }
1045
1046 self.last_resources_changed_event_args.set(e);
1047
1048 if !self.resources_changed.is_empty() {
1049 for (_, handler) in self.resources_changed.snapshot().iter() {
1050 handler(&e);
1051 }
1052 }
1053
1054 if propagate {
1055 self.notify_child_resources_changed(e);
1056 }
1057 }
1058
1059 fn flatten_styles(styles: &[Rc<dyn IStyle>], result: &mut Vec<Ref<StyleBase>>) {
1060 for style in styles {
1061 if let Some(s) = style.as_object().and_then(|o| o.downcast_ref::<Style>()) {
1062 result.push(s.to_ref().upcast());
1063 }
1064 let children = style.children();
1065 if !children.is_empty() {
1066 Self::flatten_styles(&children, result);
1067 }
1068 }
1069 }
1070
1071 fn has_setters_or_animations(styles: &[Rc<dyn IStyle>]) -> bool {
1072 styles.iter().any(|style| {
1073 if style
1074 .as_object()
1075 .and_then(|o| o.downcast_ref::<StyleBase>())
1076 .is_some_and(StyleBase::has_setters_or_animations)
1077 {
1078 return true;
1079 }
1080 let children = style.children();
1081 !children.is_empty() && Self::has_setters_or_animations(&children)
1082 })
1083 }
1084
1085 fn data_context_notifying(o: &FerroObject, update_started: bool) {
1088 if let Some(element) = o.downcast_ref::<StyledElement>() {
1089 element.data_context_notifying_core(update_started);
1090 }
1091 }
1092
1093 fn data_context_notifying_core(&self, update_started: bool) {
1094 if update_started {
1095 if !self.data_context_updating.get() {
1096 self.data_context_updating.set(true);
1097 self.on_data_context_begin_update();
1098
1099 if let Some(children) = self.logical_children.get() {
1100 let this: &FerroObject = self;
1101 for child in children.snapshot().iter() {
1102 if child.is_inheritance_parent(this) && !child.is_set(Self::data_context_property())
1103 {
1104 child.data_context_notifying_core(update_started);
1105 }
1106 }
1107 }
1108 }
1109 } else if self.data_context_updating.get() {
1110 self.on_data_context_end_update();
1111 self.data_context_updating.set(false);
1112 }
1113 }
1114
1115 pub fn set_data_context_with_priority(
1117 &self,
1118 value: Option<BoxedValue>,
1119 priority: BindingPriority,
1120 ) -> Option<Rc<dyn IDisposable>> {
1121 self.set_value_with_priority(Self::data_context_property(), value, priority)
1122 }
1123}
1124
1125struct EndStyling<'a>(&'a StyledElement);
1128
1129impl Drop for EndStyling<'_> {
1130 fn drop(&mut self) {
1131 self.0.values().end_styling(self.0);
1132 }
1133}
1134
1135impl IResourceNode for StyledElement {
1136 fn has_resources(&self) -> bool {
1137 StyledElement::has_resources(self)
1138 }
1139
1140 fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
1141 StyledElement::try_get_resource(self, key, theme)
1142 }
1143}
1144
1145impl IResourceHost for StyledElement {
1146 fn resources_changed(&self, handler: Rc<dyn Fn(&ResourcesChangedEventArgs)>) -> Rc<dyn IDisposable> {
1147 self.subscribe_resources_changed(handler)
1148 }
1149
1150 fn notify_hosted_resources_changed(&self, e: ResourcesChangedEventArgs) {
1151 self.notify_resources_changed(e, true)
1152 }
1153
1154 fn as_style_host(&self) -> Option<&dyn IStyleHost> {
1155 Some(self)
1156 }
1157
1158 fn as_theme_variant_host(&self) -> Option<&dyn IThemeVariantHost> {
1159 Some(self)
1160 }
1161}
1162
1163impl IStyleHost for StyledElement {
1164 fn is_styles_initialized(&self) -> bool {
1165 StyledElement::is_styles_initialized(self)
1166 }
1167
1168 fn styles(&self) -> Ref<Styles> {
1169 StyledElement::styles(self)
1170 }
1171
1172 fn styling_parent(&self) -> Option<StyleHostRef> {
1173 StyledElement::styling_parent(self)
1174 }
1175
1176 fn styles_added(&self, styles: &[Rc<dyn IStyle>]) {
1177 StyledElement::styles_added(self, styles)
1178 }
1179
1180 fn styles_removed(&self, styles: &[Rc<dyn IStyle>]) {
1181 StyledElement::styles_removed(self, styles)
1182 }
1183}
1184
1185impl IThemeVariantHost for StyledElement {
1186 fn actual_theme_variant(&self) -> Option<ThemeVariant> {
1187 StyledElement::actual_theme_variant(self)
1188 }
1189
1190 fn actual_theme_variant_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
1191 self.subscribe_actual_theme_variant_changed(handler)
1192 }
1193}