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 #[inline]
462 pub fn has_name(&self, name: &str) -> bool {
463 self.name.borrow().as_deref() == Some(name)
464 }
465
466 pub fn set_name(&self, value: Option<String>) {
467 assert!(!self.styles_applied.get(), "Cannot set Name : styled element already styled.");
468 *self.name.borrow_mut() = value;
469 }
470
471 pub fn classes(&self) -> Classes {
476 self.classes_ref().clone()
477 }
478
479 #[inline]
480 fn classes_ref(&self) -> &Classes {
481 self.classes.get_or_init(Classes::new)
482 }
483
484 pub fn pseudo_classes(&self) -> &dyn IPseudoClasses {
486 self.classes_ref()
487 }
488
489 pub fn data_context(&self) -> Option<BoxedValue> {
494 self.get_value(Self::data_context_property())
495 }
496
497 pub fn set_data_context(&self, value: Option<BoxedValue>) {
498 self.set_value(Self::data_context_property(), value)
499 }
500
501 pub fn is_initialized(&self) -> bool {
506 self.is_initialized.get()
507 }
508
509 pub fn styles(&self) -> Ref<Styles> {
514 self.styles.get_or_init(|| Styles::with_owner(self.to_ref())).clone()
515 }
516
517 pub fn is_styles_initialized(&self) -> bool {
519 self.styles.get().is_some()
520 }
521
522 pub fn style_key(&self) -> &'static TypeInfo {
524 self.style_key_override()
525 }
526
527 pub fn resources(&self) -> Ref<ResourceDictionary> {
529 if let Some(resources) = &*self.resources.borrow() {
530 return resources.clone();
531 }
532 let resources = ResourceDictionary::with_owner(self.to_ref());
533 *self.resources.borrow_mut() = Some(resources.clone());
534 resources
535 }
536
537 pub fn resources_if_created(&self) -> Option<Ref<ResourceDictionary>> {
540 self.resources.borrow().clone()
541 }
542
543 pub fn set_resources(&self, value: Ref<ResourceDictionary>) {
544 let this = ResourceHostRef::Element(self.to_ref());
545 let old = self.resources.borrow_mut().take();
546 if let Some(old) = old {
547 old.remove_owner(&this);
548 }
549 *self.resources.borrow_mut() = Some(value.clone());
550 value.add_owner(&this);
551 }
552
553 pub fn theme(&self) -> Option<Ref<ControlTheme>> {
558 self.get_value(Self::theme_property())
559 }
560
561 pub fn set_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
562 self.set_value(Self::theme_property(), value.into().0)
563 }
564
565 pub fn actual_theme_variant(&self) -> Option<ThemeVariant> {
569 self.get_value(ThemeVariant::actual_theme_variant_property())
570 }
571
572 pub fn has_resources(&self) -> bool {
574 self.resources.borrow().as_ref().is_some_and(|r| r.has_resources())
575 || self.styles.get().is_some_and(|s| s.has_resources())
576 }
577
578 pub fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
581 let resources = self.resources.borrow().clone();
582 if let Some(resources) = resources {
583 if let Some(value) = resources.try_get_resource(key, theme) {
584 return Some(value);
585 }
586 }
587 match self.styles.get() {
588 Some(styles) => styles.try_get_resource(key, theme),
589 None => None,
590 }
591 }
592
593 pub fn try_find_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
596 let host: &dyn IResourceHost = self;
597 host.try_find_resource(key, theme)
598 }
599
600 pub fn find_resource(&self, key: &ResourceKey) -> ResourceValue {
603 let host: &dyn IResourceHost = self;
604 host.find_resource(key)
605 }
606
607 pub fn templated_parent(&self) -> Option<Ref<FerroObject>> {
609 self.templated_parent.borrow().as_ref().and_then(WeakRef::upgrade)
610 }
611
612 pub fn set_templated_parent(&self, value: impl Into<Nullable<FerroObject>>) {
615 let value = value.into().0;
616 let old = self.templated_parent();
617 if old != value {
618 *self.templated_parent.borrow_mut() = value.as_ref().map(Ref::downgrade);
619 self.raise_direct_property_changed(Self::templated_parent_property(), &old, &value);
620 }
621 }
622
623 pub fn logical_children(&self) -> &FerroList<Ref<StyledElement>> {
625 self.logical_children.get_or_init(|| {
626 let list = FerroList::new();
627 list.set_reset_behavior(ResetBehavior::Remove);
628 let weak = self.to_ref().downgrade();
629 list.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, Ref<StyledElement>>| {
630 if let Some(this) = weak.upgrade() {
631 this.logical_children_collection_changed(e);
632 }
633 }));
634 list
635 })
636 }
637
638 pub fn is_attached_to_logical_tree(&self) -> bool {
640 self.logical_root.borrow().is_some()
641 }
642
643 pub fn parent(&self) -> Option<Ref<StyledElement>> {
645 self.parent.borrow().as_ref().and_then(WeakRef::upgrade)
646 }
647
648 pub fn apply_styling(&self) -> bool {
659 match self.try_apply_styling() {
660 Ok(applied) => applied,
661 Err(error) => panic!("{error}"),
662 }
663 }
664
665 pub fn try_apply_styling(&self) -> Result<bool, DuplicateSetterError> {
671 if self.init_count.get() == 0
672 && (!self.styles_applied.get()
673 || !self.theme_applied.get()
674 || !self.templated_parent_theme_applied.get())
675 {
676 let values = self.values();
677 values.begin_styling();
678 let _end_styling = EndStyling(self);
679 if !self.theme_applied.get() {
680 self.apply_control_theme()?;
681 self.theme_applied.set(true);
682 }
683 if !self.templated_parent_theme_applied.get() {
684 self.apply_templated_parent_control_theme()?;
685 self.templated_parent_theme_applied.set(true);
686 }
687 if !self.styles_applied.get() {
688 self.apply_styles(&StyleHostRef::Element(self.to_ref()))?;
689 self.styles_applied.set(true);
690 }
691 }
692 Ok(self.styles_applied.get())
693 }
694
695 pub fn end_init(&self) {
699 if let Err(error) = self.try_end_init() {
700 panic!("{error}");
701 }
702 }
703
704 pub fn initialize_if_needed(&self) {
707 if self.init_count.get() == 0 && !self.is_initialized.get() {
708 self.is_initialized.set(true);
709 self.on_initialized();
710 if !self.initialized.is_empty() {
711 for (_, handler) in self.initialized.snapshot().iter() {
712 handler();
713 }
714 }
715 }
716 }
717
718 pub fn set_parent(&self, parent: impl Into<Nullable<StyledElement>>) {
725 let parent = parent.into().0;
726 let old = self.parent();
727 if parent == old {
728 return;
729 }
730 assert!(old.is_none() || parent.is_none(), "The Control already has a parent.");
731
732 if self.inheritance_parent().is_none() || parent.is_none() {
733 self.set_inheritance_parent(parent.clone().map(Ref::upcast::<FerroObject>));
734 }
735
736 *self.parent.borrow_mut() = parent.as_ref().map(Ref::downgrade);
737
738 let this = self.to_ref();
739 let old_root = self.logical_root();
740 if let Some(root) = old_root {
741 let e = LogicalTreeAttachmentEventArgs::new(root, this.clone(), old.clone());
742 self.on_detached_from_logical_tree_core(&e);
743 }
744
745 if let Some(new_root) = self.find_logical_root() {
746 let e = LogicalTreeAttachmentEventArgs::new(new_root, this, parent.clone());
747 self.on_attached_to_logical_tree_core(&e);
748 } else if parent.is_none() {
749 self.notify_resources_changed(ResourcesChangedEventArgs::create(), true);
759 }
760
761 self.raise_direct_property_changed(Self::parent_property(), &old, &parent);
762 }
763
764 pub fn notify_attached_to_logical_tree(&self, e: &LogicalTreeAttachmentEventArgs) {
769 self.on_attached_to_logical_tree_core(e);
770 }
771
772 pub fn notify_detached_from_logical_tree(&self, e: &LogicalTreeAttachmentEventArgs) {
775 self.on_detached_from_logical_tree_core(e);
776 }
777
778 fn logical_root(&self) -> Option<Ref<StyledElement>> {
779 self.logical_root.borrow().as_ref().and_then(WeakRef::upgrade)
780 }
781
782 fn find_logical_root(&self) -> Option<Ref<StyledElement>> {
783 if self.is_logical_root() {
784 return Some(self.to_ref());
785 }
786 let mut current = self.styling_parent();
787 while let Some(host) = current {
788 if let StyleHostRef::Element(e) = &host {
789 if e.is_logical_root() {
790 return Some(e.clone());
791 }
792 }
793 current = host.styling_parent();
794 }
795 None
796 }
797
798 fn on_attached_to_logical_tree_core(&self, e: &LogicalTreeAttachmentEventArgs) {
799 if self.parent().is_none() && !self.is_logical_root() {
800 panic!(
801 "AttachedToLogicalTreeCore called for '{}' but control has no logical parent.",
802 self.get_type().name()
803 );
804 }
805
806 if self.logical_root.borrow().is_none() {
814 crate::perf_count!(LogicalTreeAttachments);
815 *self.logical_root.borrow_mut() = Some(e.root().downgrade());
816 self.reevaluate_implicit_theme();
817 self.apply_styling();
818 self.notify_resources_changed(ResourcesChangedEventArgs::create(), false);
819 self.on_attached_to_logical_tree(e);
820 if !self.attached_to_logical_tree.is_empty() {
821 for (_, handler) in self.attached_to_logical_tree.snapshot().iter() {
822 handler(e);
823 }
824 }
825 }
826
827 if let Some(children) = self.logical_children.get() {
828 for child in children.snapshot().iter() {
831 let attached_to_root = child.logical_root().is_some_and(|r| r == *e.root());
832 if !attached_to_root && (child.parent().is_some() || !child.removed_from_logical_parent.get()) {
833 child.on_attached_to_logical_tree_core(e);
834 }
835 }
836 }
837 }
838
839 fn on_detached_from_logical_tree_core(&self, e: &LogicalTreeAttachmentEventArgs) {
840 if self.logical_root.borrow().is_some() {
841 crate::perf_count!(LogicalTreeDetachments);
842 *self.logical_root.borrow_mut() = None;
843 self.invalidate_styles(false);
844 self.on_detached_from_logical_tree(e);
845 if !self.detached_from_logical_tree.is_empty() {
846 for (_, handler) in self.detached_from_logical_tree.snapshot().iter() {
847 handler(e);
848 }
849 }
850
851 if let Some(children) = self.logical_children.get() {
852 for child in children.snapshot().iter() {
853 child.on_detached_from_logical_tree_core(e);
854 }
855 }
856 }
857 }
858
859 fn set_logical_parent_of(&self, children: &[Ref<StyledElement>]) {
860 for child in children {
861 child.removed_from_logical_parent.set(false);
862 if child.parent().is_none() {
863 child.set_parent(self.to_ref());
864 }
865 }
866 }
867
868 fn clear_logical_parent_of(&self, children: &[Ref<StyledElement>]) {
869 let this = self.to_ref();
870 for child in children {
871 child.removed_from_logical_parent.set(true);
872 if child.parent().is_some_and(|p| p == this) {
873 child.set_parent(None);
874 }
875 }
876 }
877
878 pub fn get_effective_theme(&self) -> Option<Ref<ControlTheme>> {
885 if let Some(theme) = self.theme() {
887 return Some(theme);
888 }
889
890 if matches!(*self.implicit_theme.borrow(), ImplicitTheme::Unknown) {
893 crate::perf_count!(ImplicitThemeLookups);
894 let key = ResourceKey::Type(self.style_key());
895 let found = self.try_find_resource(&key, None).flatten().and_then(|value| {
896 let value: &dyn AnyValue = &*value;
897 value.downcast_ref::<Ref<ControlTheme>>().cloned()
898 });
899 *self.implicit_theme.borrow_mut() = match found {
900 Some(theme) => ImplicitTheme::Theme(theme),
901 None => ImplicitTheme::Invalid,
902 };
903 }
904
905 match &*self.implicit_theme.borrow() {
906 ImplicitTheme::Theme(theme) => Some(theme.clone()),
907 _ => None,
908 }
909 }
910
911 fn apply_control_theme(&self) -> Result<(), DuplicateSetterError> {
912 if let Some(theme) = self.get_effective_theme() {
913 self.apply_control_theme_core(&theme, FrameType::Theme)?;
914 }
915 Ok(())
916 }
917
918 fn apply_templated_parent_control_theme(&self) -> Result<(), DuplicateSetterError> {
919 if self.templated_parent.borrow().is_none() {
922 return Ok(());
923 }
924 let parent_theme = self
925 .templated_parent()
926 .and_then(|p| p.downcast_ref::<StyledElement>().and_then(StyledElement::get_effective_theme));
927 if let Some(parent_theme) = parent_theme {
928 self.apply_control_theme_core(&parent_theme, FrameType::TemplatedParentTheme)?;
929 }
930 Ok(())
931 }
932
933 fn apply_control_theme_core(&self, theme: &ControlTheme, type_: FrameType) -> Result<(), DuplicateSetterError> {
934 debug_assert!(matches!(type_, FrameType::Theme | FrameType::TemplatedParentTheme));
935
936 if let Some(based_on) = theme.based_on() {
937 self.apply_control_theme_core(&based_on, type_)?;
938 }
939
940 theme.try_attach_checked(self, type_)?;
941
942 if theme.has_children() {
943 let children = theme.children().snapshot();
944 for child in children.iter() {
945 self.apply_style(&**child, None, type_)?;
946 }
947 }
948 Ok(())
949 }
950
951 fn apply_styles(&self, host: &StyleHostRef) -> Result<(), DuplicateSetterError> {
952 if let Some(parent) = host.styling_parent() {
953 self.apply_styles(&parent)?;
954 }
955 crate::perf_count!(StyleHostsWalked);
956
957 if host.is_styles_initialized() {
958 let styles = host.styles().snapshot();
959 for style in styles.iter() {
960 self.apply_style(&**style, Some(host), FrameType::Style)?;
961 }
962 }
963 Ok(())
964 }
965
966 fn apply_style(
967 &self,
968 style: &dyn IStyle,
969 host: Option<&StyleHostRef>,
970 type_: FrameType,
971 ) -> Result<(), DuplicateSetterError> {
972 if let Some(base) = style.as_object().and_then(|o| o.downcast_ref::<StyleBase>()) {
973 if let Some(s) = base.downcast_ref::<Style>() {
974 s.try_attach_checked(self, host, type_)?;
975 } else if let Some(m) = base.downcast_ref::<ContainerQuery>() {
976 m.try_attach_checked(self, host, type_)?;
977 }
978
979 if !base.has_children() {
981 return Ok(());
982 }
983 }
984
985 let children = style.children();
986 for child in children.iter() {
987 self.apply_style(&**child, host, type_)?;
988 }
989 Ok(())
990 }
991
992 fn reevaluate_implicit_theme(&self) {
993 if self.theme().is_some() {
996 return;
997 }
998
999 let old_implicit_theme = match self.implicit_theme.replace(ImplicitTheme::Unknown) {
1001 ImplicitTheme::Theme(theme) => Some(theme),
1002 _ => None,
1003 };
1004 let new_implicit_theme = self.get_effective_theme();
1005
1006 if new_implicit_theme != old_implicit_theme {
1008 self.on_control_theme_changed();
1009 self.theme_applied.set(false);
1010 }
1011 }
1012
1013 pub fn styles_added(&self, styles: &[Rc<dyn IStyle>]) {
1016 if Self::has_setters_or_animations(styles) {
1017 self.invalidate_styles(true);
1018 }
1019 }
1020
1021 pub fn styles_removed(&self, styles: &[Rc<dyn IStyle>]) {
1024 let mut all_styles = Vec::new();
1025 Self::flatten_styles(styles, &mut all_styles);
1026 if !all_styles.is_empty() {
1027 self.detach_styles(&all_styles);
1028 }
1029 }
1030
1031 fn detach_styles(&self, styles: &[Ref<StyleBase>]) {
1032 let values = self.values();
1033 values.begin_styling();
1034 values.remove_frames_where(self, |frame| {
1035 frame
1036 .as_any()
1037 .downcast_ref::<StyleInstance>()
1038 .is_some_and(|instance| styles.iter().any(|style| instance.is_source(style)))
1039 });
1040 values.end_styling(self);
1041
1042 if let Some(children) = self.logical_children.get() {
1043 if !children.is_empty() {
1044 for child in children.snapshot().iter() {
1045 child.detach_styles(styles);
1046 }
1047 }
1048 }
1049 }
1050
1051 pub fn notify_resources_changed(&self, e: ResourcesChangedEventArgs, propagate: bool) {
1054 if e == self.last_resources_changed_event_args.get() {
1056 return;
1057 }
1058
1059 self.last_resources_changed_event_args.set(e);
1060
1061 if !self.resources_changed.is_empty() {
1062 for (_, handler) in self.resources_changed.snapshot().iter() {
1063 handler(&e);
1064 }
1065 }
1066
1067 if propagate {
1068 self.notify_child_resources_changed(e);
1069 }
1070 }
1071
1072 fn flatten_styles(styles: &[Rc<dyn IStyle>], result: &mut Vec<Ref<StyleBase>>) {
1073 for style in styles {
1074 if let Some(s) = style.as_object().and_then(|o| o.downcast_ref::<Style>()) {
1075 result.push(s.to_ref().upcast());
1076 }
1077 let children = style.children();
1078 if !children.is_empty() {
1079 Self::flatten_styles(&children, result);
1080 }
1081 }
1082 }
1083
1084 fn has_setters_or_animations(styles: &[Rc<dyn IStyle>]) -> bool {
1085 styles.iter().any(|style| {
1086 if style
1087 .as_object()
1088 .and_then(|o| o.downcast_ref::<StyleBase>())
1089 .is_some_and(StyleBase::has_setters_or_animations)
1090 {
1091 return true;
1092 }
1093 let children = style.children();
1094 !children.is_empty() && Self::has_setters_or_animations(&children)
1095 })
1096 }
1097
1098 fn data_context_notifying(o: &FerroObject, update_started: bool) {
1101 if let Some(element) = o.downcast_ref::<StyledElement>() {
1102 element.data_context_notifying_core(update_started);
1103 }
1104 }
1105
1106 fn data_context_notifying_core(&self, update_started: bool) {
1107 if update_started {
1108 if !self.data_context_updating.get() {
1109 self.data_context_updating.set(true);
1110 self.on_data_context_begin_update();
1111
1112 if let Some(children) = self.logical_children.get() {
1113 let this: &FerroObject = self;
1114 for child in children.snapshot().iter() {
1115 if child.is_inheritance_parent(this) && !child.is_set(Self::data_context_property())
1116 {
1117 child.data_context_notifying_core(update_started);
1118 }
1119 }
1120 }
1121 }
1122 } else if self.data_context_updating.get() {
1123 self.on_data_context_end_update();
1124 self.data_context_updating.set(false);
1125 }
1126 }
1127
1128 pub fn set_data_context_with_priority(
1130 &self,
1131 value: Option<BoxedValue>,
1132 priority: BindingPriority,
1133 ) -> Option<Rc<dyn IDisposable>> {
1134 self.set_value_with_priority(Self::data_context_property(), value, priority)
1135 }
1136}
1137
1138struct EndStyling<'a>(&'a StyledElement);
1141
1142impl Drop for EndStyling<'_> {
1143 fn drop(&mut self) {
1144 self.0.values().end_styling(self.0);
1145 }
1146}
1147
1148impl IResourceNode for StyledElement {
1149 fn has_resources(&self) -> bool {
1150 StyledElement::has_resources(self)
1151 }
1152
1153 fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
1154 StyledElement::try_get_resource(self, key, theme)
1155 }
1156}
1157
1158impl IResourceHost for StyledElement {
1159 fn resources_changed(&self, handler: Rc<dyn Fn(&ResourcesChangedEventArgs)>) -> Rc<dyn IDisposable> {
1160 self.subscribe_resources_changed(handler)
1161 }
1162
1163 fn notify_hosted_resources_changed(&self, e: ResourcesChangedEventArgs) {
1164 self.notify_resources_changed(e, true)
1165 }
1166
1167 fn as_style_host(&self) -> Option<&dyn IStyleHost> {
1168 Some(self)
1169 }
1170
1171 fn as_theme_variant_host(&self) -> Option<&dyn IThemeVariantHost> {
1172 Some(self)
1173 }
1174}
1175
1176impl IStyleHost for StyledElement {
1177 fn is_styles_initialized(&self) -> bool {
1178 StyledElement::is_styles_initialized(self)
1179 }
1180
1181 fn styles(&self) -> Ref<Styles> {
1182 StyledElement::styles(self)
1183 }
1184
1185 fn styling_parent(&self) -> Option<StyleHostRef> {
1186 StyledElement::styling_parent(self)
1187 }
1188
1189 fn styles_added(&self, styles: &[Rc<dyn IStyle>]) {
1190 StyledElement::styles_added(self, styles)
1191 }
1192
1193 fn styles_removed(&self, styles: &[Rc<dyn IStyle>]) {
1194 StyledElement::styles_removed(self, styles)
1195 }
1196}
1197
1198impl IThemeVariantHost for StyledElement {
1199 fn actual_theme_variant(&self) -> Option<ThemeVariant> {
1200 StyledElement::actual_theme_variant(self)
1201 }
1202
1203 fn actual_theme_variant_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
1204 self.subscribe_actual_theme_variant_changed(handler)
1205 }
1206}