ferroui_diagnostics/diagnostics/controls/
application.rs1use super::TopLevelGroup;
5use crate::diagnostics::{ClassicDesktopStyleApplicationLifetimeTopLevelGroup, IDevToolsTopLevelGroup};
6use ferroui_base::controls::ResourceDictionary;
7use ferroui_base::input::{ICloseable, InputManager};
8use ferroui_base::reactive::{Disposable, IDisposable};
9use ferroui_base::styling::{Styles, ThemeVariant};
10use ferroui_base::utilities::HandlerList;
11use ferroui_base::{
12 ferro_class, ferro_class_info, ferro_properties, instantiate, BoxedValue, FerroObjectImpl, FerroObjectImplExt,
13 FerroPropertyChangedEventArgs, Ref, StyledProperty,
14};
15use ferroui_controls::application_lifetimes::{ControlledApplicationLifetimeExitEventArgs, IApplicationLifetime};
16use ferroui_controls::presentation_source::ITopLevelRenderer;
17use ferroui_controls::templates::DataTemplates;
18use ferroui_controls::{ThemeVariantScope, TopLevel};
19use std::cell::RefCell;
20use std::rc::Rc;
21
22#[repr(C)]
26pub struct Application {
27 base: TopLevelGroup,
28 application: Ref<ferroui_controls::Application>,
29 closed: HandlerList<dyn Fn()>,
30 renderer_root: RefCell<Option<Rc<dyn ITopLevelRenderer>>>,
31 application_property_changed: RefCell<Option<Rc<dyn IDisposable>>>,
33}
34
35ferro_class!(Application: TopLevelGroup);
36ferro_class_info!(Application {});
37
38ferro_properties! {
39 impl Application {
40 pub fn requested_theme_variant_property() -> StyledProperty<Option<ThemeVariant>> {
42 ThemeVariantScope::requested_theme_variant_property().add_owner::<Application>()
43 }
44 }
45}
46
47impl FerroObjectImpl for Application {
48 fn constructed(this: &Self) {
49 Self::parent_constructed(this);
50
51 let application = this.application.clone();
52 let lifetime = application.application_lifetime();
53
54 if let Some(controller) = lifetime.as_ref().and_then(|lifetime| lifetime.as_controlled_application_lifetime()) {
55 let subscription: Rc<RefCell<Option<Rc<dyn IDisposable>>>> = Rc::new(RefCell::new(None));
57 let handler_subscription = subscription.clone();
58 let weak = this.to_ref().downgrade();
59 let exit = controller.exit(Rc::new(move |_: &ControlledApplicationLifetimeExitEventArgs| {
60 let own = handler_subscription.borrow_mut().take();
61 if let Some(own) = own {
62 own.dispose();
63 }
64 if let Some(this) = weak.upgrade() {
65 this.raise_closed();
66 }
67 }));
68 *subscription.borrow_mut() = Some(exit);
69 }
70
71 let renderer_root = match lifetime.as_ref() {
72 Some(lifetime) => {
73 if let Some(classic) = lifetime.as_classic_desktop_style_application_lifetime() {
74 classic.main_window().map(|main_window| main_window.renderer())
75 } else if let Some(single) = lifetime.as_single_view_application_lifetime() {
76 single
77 .main_view()
78 .and_then(|main_view| main_view.visual_root())
79 .and_then(|visual_root| visual_root.cast::<TopLevel>())
80 .map(|top_level| top_level.renderer())
81 } else {
82 None
83 }
84 }
85 None => None,
86 };
87 *this.renderer_root.borrow_mut() = renderer_root;
88
89 this.set_current_value(Self::requested_theme_variant_property(), application.requested_theme_variant());
90
91 let weak = this.to_ref().downgrade();
92 let subscription = application.property_changed(move |e| {
93 if let Some(this) = weak.upgrade() {
94 this.application_on_property_changed(e);
95 }
96 });
97 *this.application_property_changed.borrow_mut() = Some(subscription);
98 }
99
100 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
101 Self::parent_on_property_changed(this, change);
102
103 if change.property() == Self::requested_theme_variant_property().as_property() {
104 this.application.set_requested_theme_variant(change.get_new_value::<Option<ThemeVariant>>());
105 }
106 }
107}
108
109impl Application {
110 pub fn construct(
111 group: Rc<dyn IDevToolsTopLevelGroup>,
112 application: Ref<ferroui_controls::Application>,
113 ) -> Self {
114 debug_assert!(
115 group.as_any().is::<ClassicDesktopStyleApplicationLifetimeTopLevelGroup>(),
116 "the group of an application is the group of the windows of its lifetime"
117 );
118 Self {
119 base: TopLevelGroup::construct(group),
120 application,
121 closed: HandlerList::new(),
122 renderer_root: RefCell::new(None),
123 application_property_changed: RefCell::new(None),
124 }
125 }
126
127 pub fn new(group: Rc<dyn IDevToolsTopLevelGroup>, application: Ref<ferroui_controls::Application>) -> Ref<Self> {
131 instantiate(Self::construct(group, application))
132 }
133
134 pub fn closed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
136 let token = self.closed.add(Rc::new(handler));
137 let weak = self.to_ref().downgrade();
138 Disposable::create(move || {
139 if let Some(this) = weak.upgrade() {
140 this.closed.remove(token);
141 }
142 })
143 }
144
145 fn raise_closed(&self) {
146 for (_, handler) in self.closed.snapshot().iter() {
147 handler();
148 }
149 }
150
151 pub fn as_closeable(this: Ref<Self>) -> Rc<dyn ICloseable> {
153 Rc::new(ApplicationHandle(this))
154 }
155
156 pub fn as_disposable(this: Ref<Self>) -> Rc<dyn IDisposable> {
158 Rc::new(ApplicationHandle(this))
159 }
160
161 pub fn instance(&self) -> Ref<ferroui_controls::Application> {
163 self.application.clone()
164 }
165
166 pub fn data_context(&self) -> Option<BoxedValue> {
168 self.application.data_context()
169 }
170
171 pub fn data_templates(&self) -> DataTemplates {
173 self.application.data_templates()
174 }
175
176 pub fn input_manager(&self) -> Option<Rc<InputManager>> {
178 self.application.input_manager()
179 }
180
181 pub fn resources(&self) -> Ref<ResourceDictionary> {
183 self.application.resources()
184 }
185
186 pub fn styles(&self) -> Ref<Styles> {
190 self.application.styles()
191 }
192
193 pub fn application_lifetime(&self) -> Option<Rc<dyn IApplicationLifetime>> {
199 self.application.application_lifetime()
200 }
201
202 pub fn name(&self) -> Option<String> {
204 self.application.name()
205 }
206
207 pub fn renderer_root(&self) -> Option<Rc<dyn ITopLevelRenderer>> {
210 self.renderer_root.borrow().clone()
211 }
212
213 pub fn requested_theme_variant(&self) -> Option<ThemeVariant> {
215 self.get_value(Self::requested_theme_variant_property())
216 }
217
218 pub fn set_requested_theme_variant(&self, value: Option<ThemeVariant>) {
219 self.set_value(Self::requested_theme_variant_property(), value);
220 }
221
222 pub fn dispose(&self) {
224 let subscription = self.application_property_changed.borrow_mut().take();
225 if let Some(subscription) = subscription {
226 subscription.dispose();
227 }
228 }
229
230 fn application_on_property_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
231 if e.property() == ferroui_controls::Application::requested_theme_variant_property().as_property() {
232 self.set_current_value(
233 Self::requested_theme_variant_property(),
234 e.get_new_value::<Option<ThemeVariant>>(),
235 );
236 }
237 }
238}
239
240struct ApplicationHandle(Ref<Application>);
242
243impl ICloseable for ApplicationHandle {
244 fn closed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
245 self.0.closed(move || handler())
246 }
247}
248
249impl IDisposable for ApplicationHandle {
250 fn dispose(&self) {
251 self.0.dispose();
252 }
253}