1use crate::metadata::TemplatePartAttribute;
2use crate::platform::{
3 IInputPane, IInsetsManager, IPlatformHandle, IScreenImpl, ITopLevelImpl, PlatformThemeVariant, SafeAreaChangedArgs,
4};
5use crate::presentation_source::{try_get_service, ITopLevelRenderer, PresentationSource};
6use crate::primitives::{TemplateAppliedEventArgs, TemplatedControl, TemplatedControlImpl, TemplatedControlImplExt};
7use crate::top_level_host::TopLevelHost;
8use crate::{IToolTipService, ToolTip};
9use ferroui_base::{Point, Vector};
10use crate::{
11 Border, ContentControl, ContentControlImpl, Control, ControlImpl, PlatformInhibitionType, Screens, WindowResizeReason,
12 WindowTransparencyLevel, WindowTransparencyLevelCollection,
13};
14use ferroui_base::data::BindingPriority;
15use ferroui_base::input::platform::{ClipboardType, IClipboard, IPlatformClipboardManagerImpl};
16use ferroui_base::input::raw::{IRawInputEventArgs, RawKeyEventArgs, RawKeyEventType, RawPointerEventArgs, RawPointerEventType};
17use ferroui_base::input::{
18 FocusManager, IAccessKeyHandler, IInputManager, IInputRoot, IKeyboardNavigationHandler, InputElementImpl,
19 KeyEventArgs, KeyModifiers, KeyboardDevice, KeyboardNavigation, KeyboardNavigationMode, NavigationMethod,
20};
21use ferroui_base::interactivity::{
22 Interactive, InteractiveImpl, RoutedEvent, RoutedEventArgs, RoutedEventHandlerToken, RoutingStrategies,
23};
24use ferroui_base::layout::{ILayoutManager, LayoutHelper, LayoutManager, Layoutable, LayoutableImpl};
25use ferroui_base::logical_tree::LogicalTreeAttachmentEventArgs;
26use ferroui_base::media::{Brushes, IBrush, MediaContext, SolidColorBrush};
27use ferroui_base::platform::{
28 IOptionalFeatureProvider, IPlatformBehaviorInhibition, IPlatformSettings, ISystemNavigationManagerImpl,
29};
30use ferroui_base::reactive::{AnonymousObserver, Disposable, IDisposable};
31use ferroui_base::rendering::composition::CompositionTransparencyLevel;
32use ferroui_base::rendering::{IHitTester, IRenderer, RendererDiagnostics};
33use ferroui_base::styling::{
34 Container, ContainerSizing, IGlobalStyles, IStyle, IStyleHost, IThemeVariantHost, StyleHostRef, ThemeVariant,
35};
36use ferroui_base::threading::{Dispatcher, DispatcherTask};
37use ferroui_base::utilities::HandlerList;
38use ferroui_base::{
39 ferro_class, ferro_impl_classes, ferro_property, ferro_routed_event, AttachedProperty, DirectProperty,
40 FerroLocator, FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs,
41 IFerroDependencyResolver, PixelPoint, Rect, Ref, Size, StaticType, StyledElementImpl, StyledProperty,
42 StyledPropertyOptions, Visual, VisualImpl,
43};
44use std::any::Any;
45use std::cell::{Cell, OnceCell, RefCell};
46use std::collections::HashMap;
47use std::rc::Rc;
48
49type PlatformImplBindings = HashMap<usize, Vec<Rc<dyn Fn()>>>;
50
51#[repr(C)]
57pub struct TopLevel {
58 base: ContentControl,
59 dependency_resolver: RefCell<Option<Rc<dyn IFerroDependencyResolver>>>,
60 input_manager: RefCell<Option<Rc<dyn IInputManager>>>,
61 tooltip_service: RefCell<Option<Rc<dyn IToolTipService>>>,
62 access_key_handler: RefCell<Option<Rc<dyn IAccessKeyHandler>>>,
63 keyboard_navigation_handler: RefCell<Option<Rc<dyn IKeyboardNavigationHandler>>>,
64 global_styles: RefCell<Option<Rc<dyn IGlobalStyles>>>,
65 global_styles_subscriptions: RefCell<Vec<Rc<dyn IDisposable>>>,
66 application_theme_host: RefCell<Option<Rc<dyn IThemeVariantHost>>>,
67 application_theme_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
68 back_gesture_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
69 resources_changed_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
70 platform_impl_bindings: RefCell<PlatformImplBindings>,
71 scaling: Cell<f64>,
72 is_closed: Cell<bool>,
73 client_size: Cell<Size>,
74 frame_size: Cell<Option<Size>>,
75 actual_transparency_level: Cell<WindowTransparencyLevel>,
76 transparency_fallback_border: RefCell<Option<Ref<Border>>>,
77 visual_layer_manager: RefCell<Option<Ref<crate::primitives::VisualLayerManager>>>,
78 screens: RefCell<Option<Rc<Screens>>>,
79 storage_provider: RefCell<Option<Rc<dyn ferroui_base::platform::storage::IStorageProvider>>>,
80 source: OnceCell<Rc<PresentationSource>>,
81 top_level_host: OnceCell<Ref<TopLevelHost>>,
82 platform_impl: RefCell<Option<Rc<dyn ITopLevelImpl>>>,
83 insets_paddings: RefCell<Option<Rc<dyn IDisposable>>>,
84 insets_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
85 opened: HandlerList<dyn Fn()>,
86 closed: HandlerList<dyn Fn()>,
87 scaling_changed: HandlerList<dyn Fn()>,
88 platform_lost_focus: HandlerList<dyn Fn()>,
89 platform_deactivated: HandlerList<dyn Fn()>,
90 platform_position_changed: HandlerList<dyn Fn(PixelPoint)>,
91 opened_popups: RefCell<Vec<Ref<crate::primitives::Popup>>>,
92}
93
94ferro_class! {
95 TopLevel: ContentControl, virtuals TopLevelImpl: ContentControlImpl {
96 fn handle_closed(this);
98 fn handle_resized(this, client_size: Size, reason: WindowResizeReason);
102 fn on_opened(this);
104 fn on_closed(this);
106 fn opened_popups(this) -> Vec<Ref<crate::primitives::Popup>>;
109 }
110}
111
112ferro_impl_classes!(TopLevel: LayoutableImpl, InteractiveImpl, InputElementImpl, ControlImpl, ContentControlImpl);
113
114impl FerroObjectImpl for TopLevel {
115 fn constructed(this: &Self) {
116 Self::parent_constructed(this);
117 this.initialize();
118 }
119
120 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
121 Self::parent_on_property_changed(this, change);
122
123 if change.property() == ContentControl::content_property().as_property() {
124 this.invalidate_child_insets_padding();
125 } else {
126 let actions = this.platform_impl_bindings.borrow().get(&property_key(change.property())).cloned();
127 if let Some(actions) = actions {
128 for action in actions {
129 action();
130 }
131 }
132 }
133 }
134}
135
136impl StyledElementImpl for TopLevel {
137 fn is_logical_root(_this: &Self) -> bool {
138 true
139 }
140
141 fn styling_parent(this: &Self) -> Option<StyleHostRef> {
142 let global_styles = this.global_styles.borrow().clone()?;
143 let host: Rc<dyn IStyleHost> = global_styles;
144 Some(StyleHostRef::Other(host))
145 }
146
147 fn is_theme_variant_root(this: &Self) -> bool {
148 this.application_theme_host.borrow().is_none()
149 }
150}
151
152impl VisualImpl for TopLevel {
153 fn bypass_flow_direction_policies(_this: &Self) -> bool {
154 true
155 }
156
157 fn invalidate_mirror_transform(_this: &Self) {
158 }
160}
161
162impl TemplatedControlImpl for TopLevel {
163 fn on_apply_template(this: &Self, e: &TemplateAppliedEventArgs) {
164 Self::parent_on_apply_template(this, e);
165
166 {
167 *this.visual_layer_manager.borrow_mut() =
168 e.name_scope().find_as::<crate::primitives::VisualLayerManager>("PART_VisualLayerManager");
169 }
170
171 let Some(platform_impl) = this.platform_impl() else { return };
172
173 *this.transparency_fallback_border.borrow_mut() = e.name_scope().find_as::<Border>("PART_TransparencyFallback");
174 this.handle_transparency_level_changed(platform_impl.transparency_level());
175 }
176}
177
178impl TopLevelImpl for TopLevel {
179 fn handle_closed(this: &Self) {
180 let source = this.presentation_source().clone();
181 source.dispose();
182 this.stop_rendering();
183
184 debug_assert!(this.platform_impl.borrow().is_some());
185 let platform_impl = this.platform_impl.borrow_mut().take();
187 if let Some(platform_impl) = platform_impl {
188 platform_impl.set_closed(None);
192 platform_impl.set_paint(None);
193 platform_impl.set_resized(None);
194 platform_impl.set_transparency_level_changed(None);
195 platform_impl.set_platform_specific_scene_info_changed(None);
196 }
197 this.scaling.set(1.0);
198
199 for subscription in this.global_styles_subscriptions.borrow_mut().drain(..) {
200 subscription.dispose();
201 }
202 if let Some(subscription) = this.application_theme_subscription.borrow_mut().take() {
203 subscription.dispose();
204 }
205
206 if let Some(subscription) = this.back_gesture_subscription.borrow_mut().take() {
207 subscription.dispose();
208 }
209
210 let this_ref = this.to_ref();
211 let logical_args = LogicalTreeAttachmentEventArgs::new(this_ref.clone().upcast(), this_ref.clone().upcast(), None);
212 this.notify_detached_from_logical_tree(&logical_args);
213
214 source.set_root_visual(None);
215
216 this.on_closed();
217
218 ILayoutManager::dispose(&**source.layout_manager());
219 this.platform_impl_bindings.borrow_mut().clear();
220
221 if let Some(host) = this.top_level_host.get() {
225 host.visual_children().remove(&this_ref.upcast());
226 }
227 }
228
229 fn handle_resized(this: &Self, client_size: Size, _reason: WindowResizeReason) {
230 this.set_client_size(client_size);
231 this.set_width(client_size.width);
232 this.set_height(client_size.height);
233 this.layout_manager().execute_layout_pass();
234 this.renderer().resized(client_size);
235 }
236
237 fn on_opened(this: &Self) {
238 raise(&this.opened);
239 }
240
241 fn on_closed(this: &Self) {
242 raise(&this.closed);
243 }
244
245 fn opened_popups(this: &Self) -> Vec<Ref<crate::primitives::Popup>> {
246 this.opened_popups.borrow().clone()
247 }
248}
249
250fn raise(handlers: &HandlerList<dyn Fn()>) {
253 if handlers.is_empty() {
254 return;
255 }
256 let snapshot = handlers.snapshot();
257 let _ = Dispatcher::ui_thread().invoke_local(move || {
258 for (_, handler) in snapshot.iter() {
259 handler();
260 }
261 });
262}
263
264fn property_key(property: &'static FerroProperty) -> usize {
265 property as *const FerroProperty as usize
266}
267
268fn subscribe(handlers: &HandlerList<dyn Fn()>, owner: &TopLevel, select: fn(&TopLevel) -> &HandlerList<dyn Fn()>, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
269 let token = handlers.add(handler);
270 let weak = owner.to_ref().downgrade();
271 Disposable::create(move || {
272 if let Some(owner) = weak.upgrade() {
273 select(&owner).remove(token);
274 }
275 })
276}
277
278fn add_lost_focus(platform_impl: &Rc<dyn ITopLevelImpl>, handler: Rc<dyn Fn()>) {
281 let previous = platform_impl.lost_focus();
282 platform_impl.set_lost_focus(Some(Rc::new(move || {
283 if let Some(previous) = &previous {
284 previous();
285 }
286 handler();
287 })));
288}
289
290impl TopLevel {
291 pub const TEMPLATE_PARTS: &'static [TemplatePartAttribute] = &[
293 TemplatePartAttribute::new("PART_TransparencyFallback", <Border as StaticType>::TYPE),
294 TemplatePartAttribute::new(
295 "PART_VisualLayerManager",
296 <crate::primitives::VisualLayerManager as StaticType>::TYPE,
297 ),
298 ];
299
300 pub fn visual_layer_manager(&self) -> Option<Ref<crate::primitives::VisualLayerManager>> {
303 self.visual_layer_manager.borrow().clone()
304 }
305
306 pub fn enable_visual_layer_manager_layers(&self) {
310 if let Some(vlm) = self.visual_layer_manager() {
311 vlm.set_enable_overlay_layer(true);
312 vlm.set_enable_popup_overlay_layer(true);
313 vlm.set_enable_text_selector_layer(true);
314 }
315 }
316}
317
318ferroui_base::ferro_properties! { impl TopLevel, also [
319 TopLevel::system_bar_color_property,
320 TopLevel::auto_safe_area_padding_property,
321] {
322 ferro_property!(
323 pub fn client_size_property() -> DirectProperty<TopLevel, Size> {
325 FerroProperty::register_direct::<TopLevel, _>("ClientSize", |o| o.client_size(), None, Size::default())
326 }
327 );
328
329 ferro_property!(
330 pub fn frame_size_property() -> DirectProperty<TopLevel, Option<Size>> {
332 FerroProperty::register_direct::<TopLevel, _>("FrameSize", |o| o.frame_size(), None, None)
333 }
334 );
335
336 ferro_property!(
337 pub fn transparency_level_hint_property() -> StyledProperty<WindowTransparencyLevelCollection> {
339 FerroProperty::register::<TopLevel, _>("TransparencyLevelHint", WindowTransparencyLevelCollection::default())
340 }
341 );
342
343 ferro_property!(
344 pub fn actual_transparency_level_property() -> DirectProperty<TopLevel, WindowTransparencyLevel> {
346 FerroProperty::register_direct::<TopLevel, _>(
347 "ActualTransparencyLevel",
348 |o| o.actual_transparency_level(),
349 None,
350 WindowTransparencyLevel::none(),
351 )
352 }
353 );
354
355 ferro_property!(
356 pub fn transparency_background_fallback_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
358 let white: Rc<dyn IBrush> = Brushes::white();
359 FerroProperty::register::<TopLevel, _>("TransparencyBackgroundFallback", Some(white))
360 }
361 );
362} }
363
364impl TopLevel {
365 pub fn actual_theme_variant_property() -> &'static StyledProperty<Option<ThemeVariant>> {
367 ThemeVariant::actual_theme_variant_property()
368 }
369
370 pub fn requested_theme_variant_property() -> &'static StyledProperty<Option<ThemeVariant>> {
372 ThemeVariant::requested_theme_variant_property()
373 }
374
375 ferro_property!(for TopLevel;
376 pub fn system_bar_color_property() -> AttachedProperty<Option<Ref<SolidColorBrush>>> {
378 FerroProperty::register_attached_with::<TopLevel, Control, _>(
379 "SystemBarColor",
380 StyledPropertyOptions::new(None).inherits(true),
381 )
382 }
383 );
384
385 ferro_property!(for TopLevel;
386 pub fn auto_safe_area_padding_property() -> AttachedProperty<bool> {
388 FerroProperty::register_attached::<TopLevel, Control, _>("AutoSafeAreaPadding", true)
389 }
390 );
391
392 ferro_routed_event!(
393 pub fn back_requested_event() -> RoutedEvent<RoutedEventArgs> {
395 RoutedEvent::register::<TopLevel, _>("BackRequested", RoutingStrategies::BUBBLE)
396 }
397 );
398
399 fn static_constructor() {
400 KeyboardNavigation::tab_navigation_property().override_default_value::<TopLevel>(KeyboardNavigationMode::Cycle);
401 Container::sizing_property().override_default_value::<TopLevel>(ContainerSizing::WidthAndHeight);
402
403 Layoutable::affects_measure::<TopLevel>(&[Self::client_size_property().as_property()]);
404
405 Self::system_bar_color_property().changed().add_class_handler::<Control>(|view, e| {
406 let (_, new_value) = e.get_old_and_new_value::<Option<Ref<SolidColorBrush>>>();
407 if let Some(color_brush) = new_value {
408 if let Some(insets_manager) =
409 view.parent().and_then(|parent| parent.cast::<TopLevel>()).and_then(|tl| tl.insets_manager())
410 {
411 insets_manager.set_system_bar_color(Some(color_brush.color()));
412 }
413
414 if let Some(insets) = view.downcast_ref::<TopLevel>().and_then(|tl| tl.insets_manager()) {
415 insets.set_system_bar_color(Some(color_brush.color()));
416 }
417 }
418 });
419
420 Self::auto_safe_area_padding_property().changed().add_class_handler::<Control>(|view, _| {
421 let top_level = view
422 .to_ref()
423 .cast::<TopLevel>()
424 .or_else(|| view.parent().and_then(|parent| parent.cast::<TopLevel>()));
425 if let Some(top_level) = top_level {
426 top_level.invalidate_child_insets_padding();
427 }
428 });
429
430 let _ = ToolTip::service_enabled_property().changed().subscribe(Self::on_tool_tip_service_enabled_changed);
432 }
433
434 fn on_tool_tip_service_enabled_changed(args: &FerroPropertyChangedEventArgs<'_>) {
435 if args.get_new_value::<bool>() && args.priority() != BindingPriority::Inherited {
436 let Some(visual) = args.sender().downcast_ref::<Visual>() else { return };
437 let Some(top_level) = Self::get_top_level(Some(visual)) else { return };
438 let offset = visual
439 .translate_point(Point::default(), &top_level)
440 .expect("the top-level is an ancestor of the visual");
441 top_level.update_tool_tip(visual.bounds().translate(Vector::new(offset.x, offset.y)));
442 }
443 }
444
445 fn update_tool_tip(&self, dirty_rect: Rect) {
446 let tooltip_service = self.tooltip_service.borrow().clone();
447 let Some(tooltip_service) = tooltip_service else { return };
448 if !self.is_pointer_over() {
449 return;
450 }
451 let source = self.presentation_source();
452 let Some(last_pos) = source.get_last_pointer_position(self) else { return };
453
454 let client_point = Visual::point_to_client(self, last_pos);
455 if dirty_rect.contains(client_point) {
456 let input_root: Rc<dyn IInputRoot> = source.clone();
457 let candidate = self.input_hit_test_with(client_point, false).map(|element| element.upcast::<Visual>());
458 tooltip_service.update(&input_root, candidate);
459 }
460 }
461
462 pub fn construct(platform_impl: Rc<dyn ITopLevelImpl>) -> Self {
466 Self::construct_with_resolver(platform_impl, None)
467 }
468
469 pub fn construct_with_resolver(
472 platform_impl: Rc<dyn ITopLevelImpl>,
473 dependency_resolver: Option<Rc<dyn IFerroDependencyResolver>>,
474 ) -> Self {
475 Self {
476 base: ContentControl::construct(),
477 dependency_resolver: RefCell::new(dependency_resolver),
478 input_manager: RefCell::new(None),
479 tooltip_service: RefCell::new(None),
480 access_key_handler: RefCell::new(None),
481 keyboard_navigation_handler: RefCell::new(None),
482 global_styles: RefCell::new(None),
483 global_styles_subscriptions: RefCell::new(Vec::new()),
484 application_theme_host: RefCell::new(None),
485 application_theme_subscription: RefCell::new(None),
486 back_gesture_subscription: RefCell::new(None),
487 resources_changed_subscription: RefCell::new(None),
488 platform_impl_bindings: RefCell::new(HashMap::new()),
489 scaling: Cell::new(1.0),
490 is_closed: Cell::new(false),
491 client_size: Cell::new(Size::default()),
492 frame_size: Cell::new(None),
493 actual_transparency_level: Cell::new(WindowTransparencyLevel::none()),
494 transparency_fallback_border: RefCell::new(None),
495 visual_layer_manager: RefCell::new(None),
496 screens: RefCell::new(None),
497 storage_provider: RefCell::new(None),
498 source: OnceCell::new(),
499 top_level_host: OnceCell::new(),
500 platform_impl: RefCell::new(Some(platform_impl)),
501 insets_paddings: RefCell::new(None),
502 insets_subscription: RefCell::new(None),
503 opened: HandlerList::new(),
504 closed: HandlerList::new(),
505 scaling_changed: HandlerList::new(),
506 platform_lost_focus: HandlerList::new(),
507 platform_deactivated: HandlerList::new(),
508 platform_position_changed: HandlerList::new(),
509 opened_popups: RefCell::new(Vec::new()),
510 }
511 }
512
513 fn initialize(&self) {
515 let this = self.to_ref();
516 let platform_impl = self.platform_impl().expect(
517 "Could not create window implementation: maybe no windowing subsystem was initialized?",
518 );
519 let dependency_resolver =
520 self.dependency_resolver.borrow_mut().take().unwrap_or_else(FerroLocator::current);
521 let dependency_resolver: &dyn IFerroDependencyResolver = &*dependency_resolver;
522
523 let host_visual = TopLevelHost::new(&this);
524 if self.top_level_host.set(host_visual.clone()).is_err() {
525 unreachable!("the top-level is initialized once");
526 }
527 host_visual.set_parent(this.clone());
528 self.logical_children().add(host_visual.clone().upcast());
529
530 let source = PresentationSource::new(
531 host_visual.upcast(),
532 &this.clone().upcast(),
533 platform_impl.clone(),
534 dependency_resolver,
535 );
536 if self.source.set(source.clone()).is_err() {
537 unreachable!("the top-level is initialized once");
538 }
539 let _ = source.typed_renderer().scene_invalidated(Rc::new({
542 let weak = this.downgrade();
543 move |e| {
544 if let Some(this) = weak.upgrade() {
545 this.update_tool_tip(e.dirty_rect());
546 }
547 }
548 }));
549
550 self.scaling.set(LayoutHelper::validate_scaling(platform_impl.render_scaling()));
551 self.actual_transparency_level.set(platform_impl.transparency_level());
552
553 let renderer = source.typed_renderer();
554 renderer.set_transparency_level(to_composition_transparency_level(self.actual_transparency_level.get()));
555 renderer.set_platform_specific_scene_info(platform_impl.platform_specific_scene_info());
556
557 *self.access_key_handler.borrow_mut() = try_get_service::<dyn IAccessKeyHandler>(dependency_resolver);
558 *self.input_manager.borrow_mut() = try_get_service::<dyn IInputManager>(dependency_resolver);
559 *self.tooltip_service.borrow_mut() = try_get_service::<dyn IToolTipService>(dependency_resolver);
560 *self.keyboard_navigation_handler.borrow_mut() =
561 try_get_service::<dyn IKeyboardNavigationHandler>(dependency_resolver);
562 *self.global_styles.borrow_mut() = try_get_service::<dyn IGlobalStyles>(dependency_resolver);
563 *self.application_theme_host.borrow_mut() = try_get_service::<dyn IThemeVariantHost>(dependency_resolver);
564
565 let weak = this.downgrade();
566 platform_impl.set_closed(Some(Rc::new({
571 let this = this.clone();
572 move || this.ensure_closed()
573 })));
574 platform_impl.set_paint(Some(Rc::new({
575 let weak = weak.clone();
576 move |rect| {
577 if let Some(this) = weak.upgrade() {
578 this.handle_paint(rect);
579 }
580 }
581 })));
582 platform_impl.set_resized(Some(Rc::new({
583 let weak = weak.clone();
584 move |client_size, reason| {
585 if let Some(this) = weak.upgrade() {
586 this.handle_resized(client_size, reason);
587 }
588 }
589 })));
590 let _ = crate::presentation_source::add_scaling_changed(
593 &platform_impl,
594 Rc::new({
595 let weak = weak.clone();
596 move |scaling| {
597 if let Some(this) = weak.upgrade() {
598 this.handle_scaling_changed(scaling);
599 }
600 }
601 }),
602 );
603 platform_impl.set_transparency_level_changed(Some(Rc::new({
604 let weak = weak.clone();
605 move |level| {
606 if let Some(this) = weak.upgrade() {
607 this.handle_transparency_level_changed(level);
608 }
609 }
610 })));
611 platform_impl.set_platform_specific_scene_info_changed(Some(Rc::new({
612 let weak = weak.clone();
613 move |scene_info| {
614 if let Some(this) = weak.upgrade() {
615 this.handle_platform_specific_scene_info_changed(scene_info);
616 }
617 }
618 })));
619
620 self.create_platform_impl_binding(Self::transparency_level_hint_property(), {
621 let weak = weak.clone();
622 move |hint: WindowTransparencyLevelCollection| {
623 if let Some(platform_impl) = weak.upgrade().and_then(|this| this.platform_impl()) {
624 platform_impl.set_transparency_level_hint(&hint);
625 }
626 }
627 });
628
629 let keyboard_navigation_handler = self.keyboard_navigation_handler.borrow().clone();
630 if let Some(handler) = keyboard_navigation_handler {
631 handler.set_owner(&this.clone().upcast());
632 }
633 let access_key_handler = self.access_key_handler.borrow().clone();
634 if let Some(handler) = access_key_handler {
635 handler.set_owner(&this.clone().upcast());
636 }
637
638 let global_styles = self.global_styles.borrow().clone();
639 if let Some(global_styles) = &global_styles {
640 let added = global_styles.global_styles_added(Rc::new({
641 let weak = weak.clone();
642 move |styles: &[Rc<dyn IStyle>]| {
643 if let Some(this) = weak.upgrade() {
644 this.styles_added(styles);
645 }
646 }
647 }));
648 let removed = global_styles.global_styles_removed(Rc::new({
649 let weak = weak.clone();
650 move |styles: &[Rc<dyn IStyle>]| {
651 if let Some(this) = weak.upgrade() {
652 this.styles_removed(styles);
653 }
654 }
655 }));
656 self.global_styles_subscriptions.borrow_mut().extend([added, removed]);
657 }
658
659 let application_theme_host = self.application_theme_host.borrow().clone();
660 if let Some(host) = &application_theme_host {
661 let _ = self.set_value_with_priority(
662 Self::actual_theme_variant_property(),
663 host.actual_theme_variant(),
664 BindingPriority::Template,
665 );
666 let weak_host = Rc::downgrade(host);
667 let subscription = host.actual_theme_variant_changed(Rc::new({
668 let weak = weak.clone();
669 move || {
670 if let (Some(this), Some(host)) = (weak.upgrade(), weak_host.upgrade()) {
671 this.global_actual_theme_variant_changed(&*host);
672 }
673 }
674 }));
675 *self.application_theme_subscription.borrow_mut() = Some(subscription);
676 } else {
677 ThemeVariant::update_actual_theme_variant(self);
678 }
679
680 self.create_platform_impl_binding(Self::actual_theme_variant_property(), {
681 let weak = weak.clone();
682 move |variant: Option<ThemeVariant>| {
683 if let Some(platform_impl) = weak.upgrade().and_then(|this| this.platform_impl()) {
684 platform_impl.set_frame_theme_variant(variant.as_ref().and_then(to_platform_theme_variant));
685 }
686 }
687 });
688
689 self.set_client_size(platform_impl.client_size());
690
691 if let Some(application_resources) = &global_styles {
692 let subscription = application_resources.resources_changed(Rc::new({
693 let weak = weak.clone();
694 move |e| {
695 if let Some(this) = weak.upgrade() {
696 this.notify_resources_changed(*e, true);
697 }
698 }
699 }));
700 *self.resources_changed_subscription.borrow_mut() = Some(subscription);
701 }
702
703 add_lost_focus(
704 &platform_impl,
705 Rc::new({
706 let weak = weak.clone();
707 move || {
708 if let Some(this) = weak.upgrade() {
709 this.platform_impl_lost_focus();
710 for (_, handler) in this.platform_lost_focus.snapshot().iter() {
711 handler();
712 }
713 }
714 }
715 }),
716 );
717
718 if let Some(window_base_impl) = platform_impl.as_window_base_impl() {
723 window_base_impl.set_deactivated(Some(Rc::new({
724 let weak = weak.clone();
725 move || {
726 if let Some(this) = weak.upgrade() {
727 this.raise_platform_deactivated();
728 }
729 }
730 })));
731 window_base_impl.set_position_changed(Some(Rc::new({
732 let weak = weak.clone();
733 move |position| {
734 if let Some(this) = weak.upgrade() {
735 this.raise_platform_position_changed(position);
736 }
737 }
738 })));
739 }
740
741 if let Some(system_navigation_manager) = self.optional_feature::<dyn ISystemNavigationManagerImpl>() {
742 let _ = system_navigation_manager.back_requested(Rc::new({
745 let weak = weak.clone();
746 move |e: &Rc<RoutedEventArgs>| {
747 if let Some(this) = weak.upgrade() {
748 e.set_routed_event(Some(Self::back_requested_event()));
749 let e = e.clone();
750 let _ = Dispatcher::ui_thread().invoke_local(move || this.raise_event(&*e));
751 }
752 }
753 }));
754 }
755
756 let input_manager = self.input_manager.borrow().clone();
757 *self.back_gesture_subscription.borrow_mut() = input_manager.map(|input_manager| {
758 input_manager.pre_process().subscribe(Rc::new(AnonymousObserver::new({
759 let weak = weak.clone();
760 move |e: Rc<dyn IRawInputEventArgs>| {
761 if let Some(this) = weak.upgrade() {
762 this.back_gesture_pre_process(&*e);
763 }
764 }
765 })))
766 });
767 }
768
769 fn back_gesture_pre_process(&self, e: &dyn IRawInputEventArgs) {
770 let input_root = self.input_root();
771 if !std::ptr::addr_eq(Rc::as_ptr(e.root()), Rc::as_ptr(&input_root)) {
772 return;
773 }
774
775 let mut back_requested = false;
776
777 if let Some(raw_key_event_args) = e.downcast_ref::<RawKeyEventArgs>() {
778 if raw_key_event_args.type_() == RawKeyEventType::KeyDown {
779 if let Some(platform_settings) = self.platform_settings() {
780 let hotkeys = platform_settings.hotkey_configuration();
781
782 let mut key_event = KeyEventArgs::new();
783 key_event.key_modifiers = KeyModifiers::from_bits_retain(raw_key_event_args.modifiers().bits());
784 key_event.key = raw_key_event_args.key();
785 key_event.physical_key = raw_key_event_args.physical_key();
786 key_event.key_device_type = raw_key_event_args.key_device_type();
787 key_event.key_symbol = raw_key_event_args.key_symbol();
788
789 back_requested = hotkeys.back.iter().any(|key| key.matches(Some(&key_event)));
790 }
791 }
792 } else if let Some(pointer_event_args) = e.downcast_ref::<RawPointerEventArgs>() {
793 back_requested = pointer_event_args.type_() == RawPointerEventType::XButton1Down;
794 }
795
796 if back_requested {
797 let back_requested_event_args = Rc::new(RoutedEventArgs::with_event(Self::back_requested_event()));
798 let this = self.to_ref();
799 let args = back_requested_event_args.clone();
800 let _ = Dispatcher::ui_thread().invoke_local(move || this.raise_event(&*args));
801
802 e.set_handled(back_requested_event_args.handled());
803 }
804 }
805
806 pub fn opened(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
809 subscribe(&self.opened, self, |tl| &tl.opened, Rc::new(handler))
810 }
811
812 pub fn closed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
815 subscribe(&self.closed, self, |tl| &tl.closed, Rc::new(handler))
816 }
817
818 pub fn scaling_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
821 subscribe(&self.scaling_changed, self, |tl| &tl.scaling_changed, Rc::new(handler))
822 }
823
824 pub fn platform_lost_focus(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
834 subscribe(&self.platform_lost_focus, self, |tl| &tl.platform_lost_focus, Rc::new(handler))
835 }
836
837 pub fn platform_deactivated(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
841 subscribe(&self.platform_deactivated, self, |tl| &tl.platform_deactivated, Rc::new(handler))
842 }
843
844 pub fn platform_position_changed(&self, handler: impl Fn(PixelPoint) + 'static) -> Rc<dyn IDisposable> {
848 let token = self.platform_position_changed.add(Rc::new(handler));
849 let weak = self.to_ref().downgrade();
850 Disposable::create(move || {
851 if let Some(this) = weak.upgrade() {
852 this.platform_position_changed.remove(token);
853 }
854 })
855 }
856
857 pub(crate) fn raise_platform_deactivated(&self) {
858 for (_, handler) in self.platform_deactivated.snapshot().iter() {
859 handler();
860 }
861 }
862
863 pub(crate) fn raise_platform_position_changed(&self, position: PixelPoint) {
864 for (_, handler) in self.platform_position_changed.snapshot().iter() {
865 handler(position);
866 }
867 }
868
869 pub fn client_size(&self) -> Size {
871 self.client_size.get()
872 }
873
874 pub fn set_client_size(&self, value: Size) {
877 self.set_and_raise_cell(Self::client_size_property(), &self.client_size, value);
878 }
879
880 pub fn frame_size(&self) -> Option<Size> {
882 self.frame_size.get()
883 }
884
885 pub fn set_frame_size(&self, value: Option<Size>) {
888 self.set_and_raise_cell(Self::frame_size_property(), &self.frame_size, value);
889 }
890
891 pub fn transparency_level_hint(&self) -> WindowTransparencyLevelCollection {
897 self.get_value(Self::transparency_level_hint_property())
898 }
899
900 pub fn set_transparency_level_hint(&self, value: WindowTransparencyLevelCollection) {
901 self.set_value(Self::transparency_level_hint_property(), value)
902 }
903
904 pub fn actual_transparency_level(&self) -> WindowTransparencyLevel {
907 self.actual_transparency_level.get()
908 }
909
910 fn set_actual_transparency_level(&self, value: WindowTransparencyLevel) {
911 self.set_and_raise_cell(Self::actual_transparency_level_property(), &self.actual_transparency_level, value);
912 }
913
914 pub fn transparency_background_fallback(&self) -> Option<Rc<dyn IBrush>> {
917 self.get_value(Self::transparency_background_fallback_property())
918 }
919
920 pub fn set_transparency_background_fallback(&self, value: Option<Rc<dyn IBrush>>) {
921 self.set_value(Self::transparency_background_fallback_property(), value)
922 }
923
924 pub fn requested_theme_variant(&self) -> Option<ThemeVariant> {
926 self.get_value(Self::requested_theme_variant_property())
927 }
928
929 pub fn set_requested_theme_variant(&self, value: Option<ThemeVariant>) {
930 self.set_value(Self::requested_theme_variant_property(), value)
931 }
932
933 pub fn back_requested(&self, handler: impl Fn(&Interactive, &RoutedEventArgs) + 'static) -> RoutedEventHandlerToken {
936 self.add_handler(Self::back_requested_event(), handler)
937 }
938
939 pub fn layout_manager(&self) -> Rc<LayoutManager> {
941 self.presentation_source().layout_manager().clone()
942 }
943
944 pub fn platform_impl(&self) -> Option<Rc<dyn ITopLevelImpl>> {
947 self.platform_impl.borrow().clone()
948 }
949
950 pub fn try_get_platform_handle(&self) -> Option<Rc<dyn IPlatformHandle>> {
955 self.platform_impl().and_then(|platform_impl| platform_impl.handle())
956 }
957
958 pub fn create_platform_impl_binding<T: ferroui_base::PropertyValue>(
962 &self,
963 property: &'static StyledProperty<T>,
964 on_value: impl Fn(T) + 'static,
965 ) {
966 let weak = self.to_ref().downgrade();
967 let update_platform_impl: Rc<dyn Fn()> = Rc::new(move || {
968 if let Some(this) = weak.upgrade() {
969 if this.platform_impl.borrow().is_some() {
970 on_value(this.get_value(property));
971 }
972 }
973 });
974 self.platform_impl_bindings
975 .borrow_mut()
976 .entry(property_key(property.as_property()))
977 .or_default()
978 .push(update_platform_impl.clone());
979
980 update_platform_impl();
982 }
983
984 pub fn presentation_source(&self) -> &Rc<PresentationSource> {
986 self.source.get().expect("the presentation source is created with the top-level")
987 }
988
989 pub(crate) fn top_level_host(&self) -> &Ref<TopLevelHost> {
991 self.top_level_host.get().expect("the host is created with the top-level")
992 }
993
994 pub fn input_root(&self) -> Rc<dyn IInputRoot> {
996 self.presentation_source().clone()
997 }
998
999 pub fn renderer(&self) -> Rc<dyn ITopLevelRenderer> {
1001 self.presentation_source().typed_renderer().clone()
1002 }
1003
1004 pub fn hit_tester_override(&self) -> Option<Rc<dyn IHitTester>> {
1008 self.presentation_source().hit_tester_override()
1009 }
1010
1011 pub fn set_hit_tester_override(&self, value: Option<Rc<dyn IHitTester>>) {
1012 self.presentation_source().set_hit_tester_override(value)
1013 }
1014
1015 pub fn renderer_diagnostics(&self) -> Rc<RendererDiagnostics> {
1018 self.renderer().diagnostics()
1019 }
1020
1021 pub fn last_pointer_position(&self) -> Option<PixelPoint> {
1023 self.presentation_source().get_last_pointer_position(self)
1024 }
1025
1026 pub fn access_key_handler(&self) -> Option<Rc<dyn IAccessKeyHandler>> {
1028 self.access_key_handler.borrow().clone()
1029 }
1030
1031 pub fn tests_keyboard_navigation_handler(&self) -> Option<Rc<dyn IKeyboardNavigationHandler>> {
1033 self.keyboard_navigation_handler.borrow().clone()
1034 }
1035
1036 pub fn set_system_bar_color(control: &Control, color: Option<Ref<SolidColorBrush>>) {
1040 control.set_value(Self::system_bar_color_property(), color);
1041 }
1042
1043 pub fn get_system_bar_color(control: &Control) -> Option<Ref<SolidColorBrush>> {
1047 control.get_value(Self::system_bar_color_property())
1048 }
1049
1050 pub fn set_auto_safe_area_padding(control: &Control, value: bool) {
1054 control.set_value(Self::auto_safe_area_padding_property(), value);
1055 }
1056
1057 pub fn get_auto_safe_area_padding(control: &Control) -> bool {
1060 control.get_value(Self::auto_safe_area_padding_property())
1061 }
1062
1063 pub fn render_scaling(&self) -> f64 {
1065 self.scaling.get()
1066 }
1067
1068 fn optional_feature<T: ?Sized + 'static>(&self) -> Option<Rc<T>> {
1069 let platform_impl = self.platform_impl()?;
1070 let provider: &dyn IOptionalFeatureProvider = &*platform_impl;
1071 provider.try_get::<T>()
1072 }
1073
1074 pub fn storage_provider(&self) -> Rc<dyn ferroui_base::platform::storage::IStorageProvider> {
1079 use ferroui_base::platform::storage::{IStorageProvider, NoopStorageProvider};
1080 use ferroui_base::LocatorExtensions;
1081
1082 if let Some(storage_provider) = self.storage_provider.borrow().clone() {
1083 return storage_provider;
1084 }
1085
1086 let storage_provider = FerroLocator::current()
1087 .get_service::<dyn crate::platform::IStorageProviderFactory>()
1088 .map(|factory| factory.create_provider(&self.to_ref()))
1089 .or_else(|| self.optional_feature::<dyn IStorageProvider>())
1090 .unwrap_or_else(|| Rc::new(NoopStorageProvider));
1091 *self.storage_provider.borrow_mut() = Some(storage_provider.clone());
1092 storage_provider
1093 }
1094
1095 pub fn insets_manager(&self) -> Option<Rc<dyn IInsetsManager>> {
1097 self.optional_feature::<dyn IInsetsManager>()
1098 }
1099
1100 pub fn input_pane(&self) -> Option<Rc<dyn IInputPane>> {
1102 self.optional_feature::<dyn IInputPane>()
1103 }
1104
1105 pub fn launcher(&self) -> Rc<dyn ferroui_base::platform::storage::ILauncher> {
1109 use ferroui_base::platform::storage::{ILauncher, NoopLauncher};
1110
1111 self.optional_feature::<dyn ILauncher>().unwrap_or_else(|| Rc::new(NoopLauncher))
1112 }
1113
1114 pub fn screens(&self) -> Option<Rc<Screens>> {
1116 if let Some(screens) = self.screens.borrow().clone() {
1117 return Some(screens);
1118 }
1119 let screens = Screens::new(self.optional_feature::<dyn IScreenImpl>()?);
1120 *self.screens.borrow_mut() = Some(screens.clone());
1121 Some(screens)
1122 }
1123
1124 pub fn clipboard(&self) -> Option<Rc<dyn IClipboard>> {
1126 self.try_get_clipboard(ClipboardType::Default)
1127 }
1128
1129 pub fn try_get_clipboard(&self, type_: ClipboardType) -> Option<Rc<dyn IClipboard>> {
1132 if let Some(clipboard_manager) = self.optional_feature::<dyn IPlatformClipboardManagerImpl>() {
1133 return clipboard_manager.try_get_clipboard(type_);
1134 }
1135
1136 if type_ == ClipboardType::Default {
1137 self.optional_feature::<dyn IClipboard>()
1138 } else {
1139 None
1140 }
1141 }
1142
1143 pub fn focus_manager(&self) -> Rc<FocusManager> {
1145 self.presentation_source().focus_manager().clone()
1146 }
1147
1148 fn platform_settings(&self) -> Option<Rc<dyn IPlatformSettings>> {
1150 use ferroui_base::LocatorExtensions;
1151 FerroLocator::current().get_service::<dyn IPlatformSettings>()
1152 }
1153
1154 pub fn get_top_level(visual: Option<&Visual>) -> Option<Ref<TopLevel>> {
1156 let mut visual = visual.map(|visual| visual.to_ref());
1157 while let Some(current) = visual {
1158 if let Some(tl) = current.clone().cast::<TopLevel>() {
1159 return Some(tl);
1160 }
1161 visual = current.visual_parent();
1162 }
1163
1164 None
1165 }
1166
1167 pub fn request_platform_inhibition(
1180 &self,
1181 type_: PlatformInhibitionType,
1182 reason: &str,
1183 ) -> DispatcherTask<Rc<dyn IDisposable>> {
1184 let platform_behavior_inhibition = self.optional_feature::<dyn IPlatformBehaviorInhibition>();
1185 let reason = reason.to_owned();
1186
1187 Dispatcher::ui_thread().to_task_scheduler().start_local(async move {
1188 let Some(platform_behavior_inhibition) = platform_behavior_inhibition else {
1189 return Disposable::create(|| {});
1190 };
1191
1192 match type_ {
1193 PlatformInhibitionType::AppSleep => {
1194 platform_behavior_inhibition.set_inhibit_app_sleep(true, &reason).await;
1195 Disposable::create(move || {
1196 let lifted = platform_behavior_inhibition.set_inhibit_app_sleep(false, &reason);
1197 drop(Dispatcher::ui_thread().to_task_scheduler().start_local(lifted));
1198 })
1199 }
1200 }
1201 })
1202 }
1203
1204 pub fn request_animation_frame(&self, action: impl FnOnce(ferroui_base::animation::TimeSpan) + 'static) {
1206 Dispatcher::ui_thread().verify_access();
1207 MediaContext::instance().request_animation_frame(action);
1208 }
1209
1210 fn invalidate_child_insets_padding(&self) {
1211 let Some(child) = self.content().and_then(|content| Control::from_boxed(&content)) else { return };
1212 let Some(insets_manager) = self.insets_manager() else { return };
1213
1214 if let Some(subscription) = self.insets_subscription.borrow_mut().take() {
1215 subscription.dispose();
1216 }
1217 if let Some(insets_paddings) = self.insets_paddings.borrow_mut().take() {
1218 insets_paddings.dispose();
1219 }
1220
1221 if child.get_value(Self::auto_safe_area_padding_property()) {
1222 let weak = self.to_ref().downgrade();
1223 let subscription = insets_manager.safe_area_changed(Rc::new(move |_: &SafeAreaChangedArgs| {
1224 if let Some(this) = weak.upgrade() {
1225 this.invalidate_child_insets_padding();
1226 }
1227 }));
1228 *self.insets_subscription.borrow_mut() = Some(subscription);
1229 let insets_paddings = child.set_value_with_priority(
1231 TemplatedControl::padding_property(),
1232 insets_manager.safe_area_padding(),
1233 BindingPriority::Style,
1234 );
1235 *self.insets_paddings.borrow_mut() = insets_paddings;
1236 }
1237 }
1238
1239 fn handle_paint(&self, rect: Rect) {
1242 self.renderer().paint(rect);
1243 }
1244
1245 pub fn start_rendering(&self) {
1248 let renderer: Rc<dyn IRenderer> = self.renderer();
1249 MediaContext::instance().add_top_level(self.media_context_key(), renderer);
1250 }
1251
1252 pub fn stop_rendering(&self) {
1255 MediaContext::instance().remove_top_level(self.media_context_key());
1256 }
1257
1258 pub fn media_context_key(&self) -> usize {
1260 self as *const TopLevel as usize
1261 }
1262
1263 pub fn ensure_closed(&self) {
1267 if self.is_closed.replace(true) {
1268 return;
1269 }
1270 self.handle_closed();
1271 }
1272
1273 fn handle_scaling_changed(&self, scaling: f64) {
1276 if self.is_closed.get() {
1277 return;
1278 }
1279 self.scaling.set(LayoutHelper::validate_scaling(scaling));
1280 LayoutHelper::invalidate_self_and_children_measure(self);
1281 raise(&self.scaling_changed);
1282
1283 self.invalidate_child_insets_padding();
1284 }
1285
1286 fn handle_transparency_level_changed(&self, transparency_level: WindowTransparencyLevel) {
1287 let border = self.transparency_fallback_border.borrow().clone();
1288 if let Some(border) = border {
1289 if transparency_level == WindowTransparencyLevel::none() {
1290 border.set_background(self.transparency_background_fallback());
1291 } else {
1292 border.set_background(None);
1293 }
1294 }
1295
1296 self.set_actual_transparency_level(transparency_level);
1297 self.renderer().set_transparency_level(to_composition_transparency_level(transparency_level));
1298 }
1299
1300 fn handle_platform_specific_scene_info_changed(&self, scene_info: Option<std::sync::Arc<dyn Any + Send + Sync>>) {
1301 self.renderer().set_platform_specific_scene_info(scene_info);
1302 }
1303
1304 fn global_actual_theme_variant_changed(&self, sender: &dyn IThemeVariantHost) {
1305 let _ = self.set_value_with_priority(
1306 Self::actual_theme_variant_property(),
1307 sender.actual_theme_variant(),
1308 BindingPriority::Template,
1309 );
1310 }
1311
1312 pub fn add_opened_popup(&self, popup: &Ref<crate::primitives::Popup>) {
1315 self.opened_popups.borrow_mut().push(popup.clone());
1316 }
1317
1318 pub fn remove_opened_popup(&self, popup: &Ref<crate::primitives::Popup>) {
1321 let mut opened_popups = self.opened_popups.borrow_mut();
1322 if let Some(index) = opened_popups.iter().position(|p| p == popup) {
1323 opened_popups.remove(index);
1324 }
1325 }
1326
1327 fn platform_impl_lost_focus(&self) {
1328 let focused = self.focus_manager().get_focused_element();
1329 let focused = Self::get_top_level(focused.as_deref().map(|element| -> &Visual { element }));
1330
1331 if focused.is_some_and(|focused| std::ptr::eq(&*focused, self)) {
1332 if let Some(keyboard_device) = KeyboardDevice::instance() {
1333 keyboard_device.set_focused_element_with(None, NavigationMethod::Unspecified, KeyModifiers::NONE, false);
1334 }
1335 }
1336 }
1337}
1338
1339impl Drop for TopLevel {
1340 fn drop(&mut self) {
1341 if let Some(subscription) = self.resources_changed_subscription.borrow_mut().take() {
1342 subscription.dispose();
1343 }
1344 }
1345}
1346
1347fn to_platform_theme_variant(theme_variant: &ThemeVariant) -> Option<PlatformThemeVariant> {
1351 if *theme_variant == ThemeVariant::light() {
1352 Some(PlatformThemeVariant::Light)
1353 } else if *theme_variant == ThemeVariant::dark() {
1354 Some(PlatformThemeVariant::Dark)
1355 } else if let Some(inherit_variant) = theme_variant.inherit_variant() {
1356 to_platform_theme_variant(&inherit_variant)
1357 } else {
1358 None
1359 }
1360}
1361
1362fn to_composition_transparency_level(level: WindowTransparencyLevel) -> CompositionTransparencyLevel {
1363 if level == WindowTransparencyLevel::transparent() {
1364 return CompositionTransparencyLevel::Transparent;
1365 }
1366 if level == WindowTransparencyLevel::blur() {
1367 return CompositionTransparencyLevel::Blur;
1368 }
1369 if level == WindowTransparencyLevel::acrylic_blur() {
1370 return CompositionTransparencyLevel::AcrylicBlur;
1371 }
1372 if level == WindowTransparencyLevel::mica() {
1373 return CompositionTransparencyLevel::Mica;
1374 }
1375 CompositionTransparencyLevel::None
1376}
1377
1378struct TopLevelCloseable(Ref<TopLevel>);
1380
1381impl ferroui_base::input::ICloseable for TopLevelCloseable {
1382 fn closed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
1383 self.0.closed(move || handler())
1384 }
1385}
1386
1387impl TopLevel {
1388 pub fn as_closeable(&self) -> Rc<dyn ferroui_base::input::ICloseable> {
1390 Rc::new(TopLevelCloseable(self.to_ref()))
1391 }
1392}