Skip to main content

ferroui_controls/
application.rs

1use crate::application_lifetimes::{IActivatableLifetime, IApplicationLifetime};
2use crate::templates::{DataTemplates, IDataTemplateHost};
3use crate::IGlobalDataTemplates;
4use ferroui_base::animation::IGlobalClock;
5use ferroui_base::controls::{
6    IResourceHost, IResourceNode, ResourceDictionary, ResourceHostRef, ResourceKey, ResourceValue,
7    ResourcesChangedEventArgs,
8};
9use ferroui_base::input::raw::IDragDropDevice;
10use ferroui_base::input::{
11    AccessKeyHandler, DragDropDevice, IAccessKeyHandler, IInputManager, IKeyboardNavigationHandler, InputManager,
12    KeyboardNavigationHandler,
13};
14use ferroui_base::media::MediaContext;
15use ferroui_base::platform::{IOptionalFeatureProvider, IPlatformSettings};
16use ferroui_base::reactive::{Disposable, IDisposable};
17use ferroui_base::styling::{
18    GlobalStylesHandler, IGlobalStyles, IStyle, IStyleHost, IThemeVariantHost, IThemeVariantRoot, StyleHostRef,
19    Styles, ThemeVariant,
20};
21use ferroui_base::threading::FerroSynchronizationContext;
22use ferroui_base::utilities::HandlerList;
23use ferroui_base::{
24    ferro_class, ferro_property, instantiate, BoxedValue, DirectProperty, FerroLocator, FerroObject, FerroObjectImpl,
25    FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, LocatorExtensions,
26    ObjectType, Ref, StyledElement, StyledProperty, Upcast, WeakRef,
27};
28use std::any::{Any, TypeId};
29use std::cell::{Cell, OnceCell, RefCell};
30use std::rc::{Rc, Weak};
31
32/// Encapsulates a FerroUI application.
33///
34/// The [`Application`] class encapsulates application-specific
35/// functionality, including:
36/// - A global set of [`data_templates`](Application::data_templates).
37/// - A global set of [`styles`](Application::styles).
38/// - An input manager.
39/// - Registers services needed by the rest of the framework in the
40///   [`register_services`](Application::register_services) method.
41/// - Tracks the lifetime of the application.
42///
43/// An application class derives from it and overrides the members of
44/// [`ApplicationImpl`]:
45///
46/// ```ignore
47/// #[repr(C)]
48/// pub struct App { base: Application }
49///
50/// ferro_class!(App: Application);
51/// ferro_impl_classes!(App: FerroObjectImpl);
52///
53/// impl ApplicationImpl for App {
54///     fn initialize(this: &Self) {
55///         this.styles().add(theme());
56///     }
57/// }
58///
59/// impl NewApplication for App {
60///     fn new_application() -> Ref<Self> {
61///         instantiate(Self { base: Application::construct() })
62///     }
63/// }
64/// ```
65#[repr(C)]
66pub struct Application {
67    base: FerroObject,
68    host: Rc<ApplicationHost>,
69    application_lifetime: RefCell<Option<Rc<dyn IApplicationLifetime>>>,
70    setup_completed: Cell<bool>,
71    input_manager: RefCell<Option<Rc<InputManager>>>,
72    name: RefCell<Option<String>>,
73}
74
75ferro_class! {
76    Application: FerroObject, virtuals ApplicationImpl: FerroObjectImpl {
77        /// Initializes the application by loading its styles and resources.
78        fn initialize(this);
79        /// Registers the services needed by the framework.
80        fn register_services(this);
81        /// Called when the framework has completed its initialization: the
82        /// place to create the main window or view of the lifetime.
83        fn on_framework_initialization_completed(this);
84    }
85}
86ferroui_base::ferro_class_info!(Application {
87    new: Application::new,
88    interfaces: [
89        Rc<dyn IResourceHost> => |application: Ref<Application>| application.as_resource_host(),
90        Rc<dyn IStyleHost> => |application: Ref<Application>| application.as_style_host(),
91        Rc<dyn IGlobalStyles> => |application: Ref<Application>| application.as_global_styles(),
92        Rc<dyn IThemeVariantHost> => |application: Ref<Application>| application.as_theme_variant_host(),
93        ResourceHostRef => |application: Ref<Application>| ResourceHostRef::Other(application.as_resource_host()),
94        StyleHostRef => |application: Ref<Application>| StyleHostRef::Other(application.as_style_host()),
95    ],
96});
97
98/// Creates an application class with its default constructor, for
99/// [`AppBuilder::configure`](crate::AppBuilder::configure).
100pub trait NewApplication: ObjectType + Upcast<Application> {
101    /// Creates the application.
102    fn new_application() -> Ref<Self>;
103}
104
105impl NewApplication for Application {
106    fn new_application() -> Ref<Self> {
107        Application::new()
108    }
109}
110
111impl FerroObjectImpl for Application {
112    fn constructed(this: &Self) {
113        Self::parent_constructed(this);
114        let _ = this.host.owner.set(this.to_ref().downgrade());
115        this.set_name(Some("FerroUI Application".to_string()));
116    }
117
118    fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
119        Self::parent_on_property_changed(this, change);
120
121        if change.property() == Self::actual_theme_variant_property().as_property() {
122            for (_, handler) in this.host.actual_theme_variant_changed.snapshot().iter() {
123                handler();
124            }
125        }
126    }
127}
128
129impl ApplicationImpl for Application {
130    fn initialize(_this: &Self) {}
131
132    fn register_services(this: &Self) {
133        FerroSynchronizationContext::install_if_needed();
134        let input_manager = Rc::new(InputManager::new());
135        *this.input_manager.borrow_mut() = Some(input_manager.clone());
136
137        this.initialize_theme_variant();
138
139        let host = this.host.clone();
140        let locator = FerroLocator::current_mutable();
141        locator
142            .bind::<dyn IAccessKeyHandler>()
143            .to_func(|| Some(AccessKeyHandler::new() as Rc<dyn IAccessKeyHandler>))
144            .bind::<dyn IGlobalDataTemplates>()
145            .to_constant(host.clone())
146            .bind::<dyn IGlobalStyles>()
147            .to_constant(host.clone())
148            .bind::<dyn IThemeVariantHost>()
149            .to_constant(host)
150            .bind::<dyn IInputManager>()
151            .to_constant(input_manager.clone())
152            .bind::<dyn crate::IToolTipService>()
153            .to_constant(crate::ToolTipService::new(&(input_manager as Rc<dyn IInputManager>)))
154            .bind::<dyn IKeyboardNavigationHandler>()
155            .to_func(|| Some(KeyboardNavigationHandler::new() as Rc<dyn IKeyboardNavigationHandler>))
156            .bind::<dyn IDragDropDevice>()
157            .to_constant(DragDropDevice::instance() as Rc<dyn IDragDropDevice>);
158
159        // As in the reference, the in-process drag source is kept as the
160        // default for platforms that do not register one.
161        {
162            use ferroui_base::input::platform::IPlatformDragSource;
163            use ferroui_base::LocatorExtensions;
164
165            if FerroLocator::current().get_service::<dyn IPlatformDragSource>().is_none() {
166                locator.bind::<dyn IPlatformDragSource>().to_func(|| {
167                    Some(crate::platform::InProcessDragSource::new() as Rc<dyn IPlatformDragSource>)
168                });
169            }
170        }
171
172        locator.bind::<dyn IGlobalClock>().to_constant(MediaContext::instance().clock());
173
174        this.setup_completed.set(true);
175    }
176
177    fn on_framework_initialization_completed(_this: &Self) {}
178}
179
180ferroui_base::ferro_properties! { impl Application {
181    ferro_property!(
182        /// Defines the `DataContext` property.
183        pub fn data_context_property() -> StyledProperty<Option<BoxedValue>> {
184            StyledElement::data_context_property().add_owner::<Application>()
185        }
186    );
187
188    ferro_property!(
189        /// Defines the `ActualThemeVariant` property: the UI theme variant
190        /// that is currently used by the application.
191        pub fn actual_theme_variant_property() -> StyledProperty<Option<ThemeVariant>> {
192            ThemeVariant::actual_theme_variant_property().add_owner::<Application>()
193        }
194    );
195
196    ferro_property!(
197        /// Defines the `RequestedThemeVariant` property: the UI theme
198        /// variant that is requested for the application.
199        pub fn requested_theme_variant_property() -> StyledProperty<Option<ThemeVariant>> {
200            ThemeVariant::requested_theme_variant_property().add_owner::<Application>()
201        }
202    );
203
204    ferro_property!(
205        /// Defines the `Name` property.
206        pub fn name_property() -> DirectProperty<Application, Option<String>> {
207            FerroProperty::register_direct::<Application, _>("Name", |o| o.name(), Some(|o, v| o.set_name(v)), None)
208        }
209    );
210} }
211
212impl Application {
213    fn static_constructor() {
214        // The application is a root of theme variant resolution.
215        ThemeVariant::register_theme_variant_root_type(<Application as ferroui_base::StaticType>::TYPE);
216        // The application has a data context that can be used for binding.
217        <dyn ferroui_base::IDataContextProvider>::register_type(<Application as ferroui_base::StaticType>::TYPE);
218    }
219
220    /// Creates the class data; see [`FerroObject::construct`].
221    pub fn construct() -> Self {
222        Self {
223            base: FerroObject::construct(),
224            host: ApplicationHost::new(),
225            application_lifetime: RefCell::new(None),
226            setup_completed: Cell::new(false),
227            input_manager: RefCell::new(None),
228            name: RefCell::new(None),
229        }
230    }
231
232    /// Creates an instance of the [`Application`] class.
233    pub fn new() -> Ref<Self> {
234        instantiate(Self::construct())
235    }
236
237    /// The application's data context.
238    ///
239    /// The data context property specifies the default object that will be
240    /// used for data binding.
241    pub fn data_context(&self) -> Option<BoxedValue> {
242        self.get_value(Self::data_context_property())
243    }
244
245    /// Sets the application's data context.
246    pub fn set_data_context(&self, value: Option<BoxedValue>) {
247        self.set_value(Self::data_context_property(), value)
248    }
249
250    /// The UI theme variant that is requested for the application.
251    pub fn requested_theme_variant(&self) -> Option<ThemeVariant> {
252        self.get_value(Self::requested_theme_variant_property())
253    }
254
255    /// Sets the UI theme variant that is requested for the application.
256    pub fn set_requested_theme_variant(&self, value: Option<ThemeVariant>) {
257        self.set_value(Self::requested_theme_variant_property(), value)
258    }
259
260    /// The UI theme variant that is currently used by the application.
261    ///
262    /// `None` until the services of the application have been registered.
263    pub fn actual_theme_variant(&self) -> Option<ThemeVariant> {
264        self.get_value(Self::actual_theme_variant_property())
265    }
266
267    /// The application is the root of the theme variant resolution.
268    pub fn is_theme_variant_root(&self) -> bool {
269        true
270    }
271
272    /// The current instance of the [`Application`] class: the application
273    /// registered with the service locator (see
274    /// [`bind_current`](Self::bind_current)).
275    pub fn current() -> Option<Ref<Application>> {
276        FerroLocator::current().get_service::<Ref<Application>>().map(|application| (*application).clone())
277    }
278
279    /// Registers `application` with the current service locator as the
280    /// application returned by [`current`](Self::current).
281    pub fn bind_current(application: Ref<Application>) {
282        FerroLocator::current_mutable().bind_to_self(Rc::new(application));
283    }
284
285    /// The application's global data templates.
286    pub fn data_templates(&self) -> DataTemplates {
287        self.host.data_templates()
288    }
289
290    /// Whether the data templates collection has been created.
291    pub fn is_data_templates_initialized(&self) -> bool {
292        self.host.data_templates.get().is_some()
293    }
294
295    /// The application's input manager, once the services have been
296    /// registered.
297    pub fn input_manager(&self) -> Option<Rc<InputManager>> {
298        self.input_manager.borrow().clone()
299    }
300
301    /// The application's global resource dictionary.
302    pub fn resources(&self) -> Ref<ResourceDictionary> {
303        self.host.resources()
304    }
305
306    /// Sets the application's global resource dictionary.
307    pub fn set_resources(&self, value: Ref<ResourceDictionary>) {
308        self.host.set_resources(value)
309    }
310
311    /// The application's global styles.
312    ///
313    /// Global styles apply to all windows in the application.
314    pub fn styles(&self) -> Ref<Styles> {
315        self.host.styles()
316    }
317
318    /// Whether the styles collection has been created.
319    pub fn is_styles_initialized(&self) -> bool {
320        self.host.styles.get().is_some()
321    }
322
323    /// Whether the application or its styles have resources.
324    pub fn has_resources(&self) -> bool {
325        self.host.has_resources()
326    }
327
328    /// The styling parent of the application, which is `None`.
329    pub fn styling_parent(&self) -> Option<StyleHostRef> {
330        None
331    }
332
333    /// The application lifetime; use it for things like setting the main
334    /// window or view and exiting the application from code.
335    pub fn application_lifetime(&self) -> Option<Rc<dyn IApplicationLifetime>> {
336        self.application_lifetime.borrow().clone()
337    }
338
339    /// Sets the application lifetime.
340    ///
341    /// # Panics
342    /// Panics when the application has already been initialized.
343    pub fn set_application_lifetime(&self, value: Option<Rc<dyn IApplicationLifetime>>) {
344        if self.setup_completed.get() {
345            panic!("It's not possible to change ApplicationLifetime after Application was initialized.");
346        }
347
348        *self.application_lifetime.borrow_mut() = value;
349    }
350
351    /// The contract for accessing global platform-specific settings.
352    ///
353    /// It can be `None` only if the application wasn't initialized yet. The
354    /// platform settings of a top-level are an equivalent which should
355    /// always be preferred over the global one, as specific top-levels
356    /// might have different settings set up.
357    pub fn platform_settings(&self) -> Option<Rc<dyn IPlatformSettings>> {
358        self.try_get::<dyn IPlatformSettings>()
359    }
360
361    /// Raised when the resources of the application change. Disposing the
362    /// returned handle unsubscribes.
363    pub fn resources_changed(&self, handler: impl Fn(&ResourcesChangedEventArgs) + 'static) -> Rc<dyn IDisposable> {
364        let handler: Rc<dyn Fn(&ResourcesChangedEventArgs)> = Rc::new(handler);
365        self.host.subscribe(|host| &host.resources_changed, handler)
366    }
367
368    /// Raised when the actual theme variant of the application has changed.
369    /// Disposing the returned handle unsubscribes.
370    pub fn actual_theme_variant_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
371        let handler: Rc<dyn Fn()> = Rc::new(handler);
372        self.host.subscribe(|host| &host.actual_theme_variant_changed, handler)
373    }
374
375    /// Tries to find a resource within the application: in its resource
376    /// dictionary, then in its styles.
377    pub fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
378        self.host.try_get_resource(key, theme)
379    }
380
381    /// The application name to be used for various platform-specific
382    /// purposes.
383    pub fn name(&self) -> Option<String> {
384        self.name.borrow().clone()
385    }
386
387    /// Sets the application name.
388    pub fn set_name(&self, value: Option<String>) {
389        self.set_and_raise(Self::name_property(), &self.name, value);
390    }
391
392    /// Queries for an optional feature.
393    ///
394    /// `feature_type` is the `TypeId` of the feature's trait object type;
395    /// the returned value holds the `Rc<dyn ..>` handle of the feature.
396    /// Features currently supported:
397    ///
398    /// - `dyn IPlatformSettings`
399    /// - `dyn IActivatableLifetime`
400    pub fn try_get_feature(&self, feature_type: TypeId) -> Option<Rc<dyn Any>> {
401        self.host.try_get_feature(feature_type)
402    }
403
404    /// Queries for an optional feature registered as `Rc<T>`; see
405    /// [`try_get_feature`](Self::try_get_feature).
406    pub fn try_get<T: ?Sized + 'static>(&self) -> Option<Rc<T>> {
407        self.try_get_feature(TypeId::of::<T>()).and_then(|feature| feature.downcast_ref::<Rc<T>>().cloned())
408    }
409
410    /// Establishes the actual theme variant of the application and keeps
411    /// it up to date with the color values of the platform settings.
412    ///
413    /// The subscription lasts as long as the platform settings do; it does
414    /// not keep the application alive.
415    pub fn initialize_theme_variant(&self) {
416        if let Some(platform_settings) = self.platform_settings() {
417            let weak = self.to_ref().downgrade();
418            platform_settings.color_values_changed(Rc::new(move |_| {
419                if let Some(this) = weak.upgrade() {
420                    this.update_actual_theme_variant();
421                }
422            }));
423        }
424        self.update_actual_theme_variant();
425    }
426
427    fn update_actual_theme_variant(&self) {
428        ThemeVariant::update_actual_theme_variant(self);
429    }
430
431    // --- interface handles ------------------------------------------------
432
433    /// The application as the host of the global styles.
434    pub fn as_global_styles(&self) -> Rc<dyn IGlobalStyles> {
435        self.host.clone()
436    }
437
438    /// The application as a style host.
439    pub fn as_style_host(&self) -> Rc<dyn IStyleHost> {
440        self.host.clone()
441    }
442
443    /// The application as a resource host.
444    pub fn as_resource_host(&self) -> Rc<dyn IResourceHost> {
445        self.host.clone()
446    }
447
448    /// The application as a theme variant host.
449    pub fn as_theme_variant_host(&self) -> Rc<dyn IThemeVariantHost> {
450        self.host.clone()
451    }
452
453    /// The application as the host of the global data templates.
454    pub fn as_global_data_templates(&self) -> Rc<dyn IGlobalDataTemplates> {
455        self.host.clone()
456    }
457
458    /// The application as a provider of optional features.
459    pub fn as_optional_feature_provider(&self) -> Rc<dyn IOptionalFeatureProvider> {
460        self.host.clone()
461    }
462
463    /// The application as a theme variant root.
464    pub fn as_theme_variant_root(&self) -> Rc<dyn IThemeVariantRoot> {
465        self.host.clone()
466    }
467
468    /// The application as an element with a data context that can be used
469    /// for binding.
470    pub fn as_data_context_provider(&self) -> Rc<dyn ferroui_base::IDataContextProvider> {
471        self.host.clone()
472    }
473}
474
475/// The interfaces of an application object, and the state they work on.
476///
477/// The application hands this out wherever one of its contracts is needed
478/// (`Rc<dyn IGlobalStyles>`, `Rc<dyn IResourceHost>`, ...); every handle of
479/// one application is the same object, so that hosts compare by identity.
480struct ApplicationHost {
481    this: Weak<ApplicationHost>,
482    owner: OnceCell<WeakRef<Application>>,
483    /// The application-global data templates.
484    data_templates: OnceCell<DataTemplates>,
485    styles: OnceCell<Ref<Styles>>,
486    resources: RefCell<Option<Ref<ResourceDictionary>>>,
487    styles_added: HandlerList<GlobalStylesHandler>,
488    styles_removed: HandlerList<GlobalStylesHandler>,
489    resources_changed: HandlerList<dyn Fn(&ResourcesChangedEventArgs)>,
490    actual_theme_variant_changed: HandlerList<dyn Fn()>,
491}
492
493impl ApplicationHost {
494    fn new() -> Rc<Self> {
495        Rc::new_cyclic(|this| Self {
496            this: this.clone(),
497            owner: OnceCell::new(),
498            data_templates: OnceCell::new(),
499            styles: OnceCell::new(),
500            resources: RefCell::new(None),
501            styles_added: HandlerList::new(),
502            styles_removed: HandlerList::new(),
503            resources_changed: HandlerList::new(),
504            actual_theme_variant_changed: HandlerList::new(),
505        })
506    }
507
508    fn host_ref(&self) -> ResourceHostRef {
509        let this: Rc<dyn IResourceHost> = self.this.upgrade().expect("the application host is alive");
510        ResourceHostRef::Other(this)
511    }
512
513    fn data_templates(&self) -> DataTemplates {
514        self.data_templates.get_or_init(DataTemplates::new).clone()
515    }
516
517    fn styles(&self) -> Ref<Styles> {
518        self.styles.get_or_init(|| Styles::with_owner(self.host_ref())).clone()
519    }
520
521    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.host_ref());
526        *self.resources.borrow_mut() = Some(resources.clone());
527        resources
528    }
529
530    fn set_resources(&self, value: Ref<ResourceDictionary>) {
531        let this = self.host_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    fn has_resources(&self) -> bool {
541        self.resources.borrow().as_ref().is_some_and(|r| r.has_resources())
542            || self.styles.get().is_some_and(|s| s.has_resources())
543    }
544
545    fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
546        let resources = self.resources.borrow().clone();
547        if let Some(resources) = resources {
548            if let Some(value) = resources.try_get_resource(key, theme) {
549                return Some(value);
550            }
551        }
552        self.styles().try_get_resource(key, theme)
553    }
554
555    fn try_get_feature(&self, feature_type: TypeId) -> Option<Rc<dyn Any>> {
556        if feature_type == TypeId::of::<dyn IPlatformSettings>() {
557            return FerroLocator::current().get_service_untyped(TypeId::of::<dyn IPlatformSettings>());
558        }
559
560        if feature_type == TypeId::of::<dyn IActivatableLifetime>() {
561            return FerroLocator::current().get_service_untyped(TypeId::of::<dyn IActivatableLifetime>());
562        }
563
564        // Do not return just any service from the locator.
565        None
566    }
567
568    fn subscribe<F: ?Sized + 'static>(
569        &self,
570        select: fn(&ApplicationHost) -> &HandlerList<F>,
571        handler: Rc<F>,
572    ) -> Rc<dyn IDisposable> {
573        let token = select(self).add(handler);
574        let weak = self.this.clone();
575        Disposable::create(move || {
576            if let Some(this) = weak.upgrade() {
577                select(&this).remove(token);
578            }
579        })
580    }
581}
582
583impl IResourceNode for ApplicationHost {
584    fn has_resources(&self) -> bool {
585        ApplicationHost::has_resources(self)
586    }
587
588    fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
589        ApplicationHost::try_get_resource(self, key, theme)
590    }
591}
592
593impl IResourceHost for ApplicationHost {
594    fn resources_changed(&self, handler: Rc<dyn Fn(&ResourcesChangedEventArgs)>) -> Rc<dyn IDisposable> {
595        self.subscribe(|host| &host.resources_changed, handler)
596    }
597
598    fn notify_hosted_resources_changed(&self, e: ResourcesChangedEventArgs) {
599        for (_, handler) in self.resources_changed.snapshot().iter() {
600            handler(&e);
601        }
602    }
603
604    fn as_style_host(&self) -> Option<&dyn IStyleHost> {
605        Some(self)
606    }
607
608    fn as_theme_variant_host(&self) -> Option<&dyn IThemeVariantHost> {
609        Some(self)
610    }
611}
612
613impl IStyleHost for ApplicationHost {
614    fn is_styles_initialized(&self) -> bool {
615        self.styles.get().is_some()
616    }
617
618    fn styles(&self) -> Ref<Styles> {
619        ApplicationHost::styles(self)
620    }
621
622    fn styling_parent(&self) -> Option<StyleHostRef> {
623        None
624    }
625
626    fn styles_added(&self, styles: &[Rc<dyn IStyle>]) {
627        for (_, handler) in self.styles_added.snapshot().iter() {
628            handler(styles);
629        }
630    }
631
632    fn styles_removed(&self, styles: &[Rc<dyn IStyle>]) {
633        for (_, handler) in self.styles_removed.snapshot().iter() {
634            handler(styles);
635        }
636    }
637}
638
639impl IGlobalStyles for ApplicationHost {
640    fn global_styles_added(&self, handler: Rc<GlobalStylesHandler>) -> Rc<dyn IDisposable> {
641        self.subscribe(|host| &host.styles_added, handler)
642    }
643
644    fn global_styles_removed(&self, handler: Rc<GlobalStylesHandler>) -> Rc<dyn IDisposable> {
645        self.subscribe(|host| &host.styles_removed, handler)
646    }
647}
648
649impl IThemeVariantHost for ApplicationHost {
650    fn actual_theme_variant(&self) -> Option<ThemeVariant> {
651        self.owner.get().and_then(WeakRef::upgrade).and_then(|application| application.actual_theme_variant())
652    }
653
654    fn actual_theme_variant_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
655        self.subscribe(|host| &host.actual_theme_variant_changed, handler)
656    }
657}
658
659impl IThemeVariantRoot for ApplicationHost {
660    fn is_theme_variant_root(&self) -> bool {
661        true
662    }
663}
664
665impl ferroui_base::IDataContextProvider for ApplicationHost {
666    fn data_context(&self) -> Option<BoxedValue> {
667        self.owner.get().and_then(WeakRef::upgrade).and_then(|owner| owner.data_context())
668    }
669
670    fn set_data_context(&self, value: Option<BoxedValue>) {
671        if let Some(owner) = self.owner.get().and_then(WeakRef::upgrade) {
672            owner.set_data_context(value);
673        }
674    }
675}
676
677impl IDataTemplateHost for ApplicationHost {
678    fn data_templates(&self) -> DataTemplates {
679        ApplicationHost::data_templates(self)
680    }
681
682    fn is_data_templates_initialized(&self) -> bool {
683        self.data_templates.get().is_some()
684    }
685}
686
687impl IGlobalDataTemplates for ApplicationHost {}
688
689impl IOptionalFeatureProvider for ApplicationHost {
690    fn try_get_feature(&self, feature_type: TypeId) -> Option<Rc<dyn Any>> {
691        ApplicationHost::try_get_feature(self, feature_type)
692    }
693}