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 system_backdrop_target: RefCell<Option<Rc<crate::media::TopLevelBackdropTarget>>>,
80 connected_system_backdrop: RefCell<Option<Ref<crate::media::SystemBackdrop>>>,
81 visual_layer_manager: RefCell<Option<Ref<crate::primitives::VisualLayerManager>>>,
82 screens: RefCell<Option<Rc<Screens>>>,
83 storage_provider: RefCell<Option<Rc<dyn ferroui_base::platform::storage::IStorageProvider>>>,
84 source: OnceCell<Rc<PresentationSource>>,
85 top_level_host: OnceCell<Ref<TopLevelHost>>,
86 platform_impl: RefCell<Option<Rc<dyn ITopLevelImpl>>>,
87 insets_paddings: RefCell<Option<Rc<dyn IDisposable>>>,
88 insets_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
89 opened: HandlerList<dyn Fn()>,
90 closed: HandlerList<dyn Fn()>,
91 scaling_changed: HandlerList<dyn Fn()>,
92 platform_lost_focus: HandlerList<dyn Fn()>,
93 platform_deactivated: HandlerList<dyn Fn()>,
94 platform_position_changed: HandlerList<dyn Fn(PixelPoint)>,
95 opened_popups: RefCell<Vec<Ref<crate::primitives::Popup>>>,
96}
97
98ferro_class! {
99 TopLevel: ContentControl, virtuals TopLevelImpl: ContentControlImpl {
100 fn handle_closed(this);
102 fn handle_resized(this, client_size: Size, reason: WindowResizeReason);
106 fn on_opened(this);
108 fn on_closed(this);
110 fn opened_popups(this) -> Vec<Ref<crate::primitives::Popup>>;
113 }
114}
115
116ferro_impl_classes!(TopLevel: LayoutableImpl, InteractiveImpl, InputElementImpl, ControlImpl, ContentControlImpl);
117
118impl FerroObjectImpl for TopLevel {
119 fn constructed(this: &Self) {
120 Self::parent_constructed(this);
121 this.initialize();
122 }
123
124 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
125 Self::parent_on_property_changed(this, change);
126
127 if change.property() == ContentControl::content_property().as_property() {
128 this.invalidate_child_insets_padding();
129 } else if change.property() == Self::system_backdrop_property().as_property() {
130 this.connect_system_backdrop();
131 } else {
132 let actions = this.platform_impl_bindings.borrow().get(&property_key(change.property())).cloned();
133 if let Some(actions) = actions {
134 for action in actions {
135 action();
136 }
137 }
138 }
139 }
140}
141
142impl StyledElementImpl for TopLevel {
143 fn is_logical_root(_this: &Self) -> bool {
144 true
145 }
146
147 fn styling_parent(this: &Self) -> Option<StyleHostRef> {
148 let global_styles = this.global_styles.borrow().clone()?;
149 let host: Rc<dyn IStyleHost> = global_styles;
150 Some(StyleHostRef::Other(host))
151 }
152
153 fn is_theme_variant_root(this: &Self) -> bool {
154 this.application_theme_host.borrow().is_none()
155 }
156}
157
158impl VisualImpl for TopLevel {
159 fn bypass_flow_direction_policies(_this: &Self) -> bool {
160 true
161 }
162
163 fn invalidate_mirror_transform(_this: &Self) {
164 }
166}
167
168impl TemplatedControlImpl for TopLevel {
169 fn on_apply_template(this: &Self, e: &TemplateAppliedEventArgs) {
170 Self::parent_on_apply_template(this, e);
171
172 {
173 *this.visual_layer_manager.borrow_mut() =
174 e.name_scope().find_as::<crate::primitives::VisualLayerManager>("PART_VisualLayerManager");
175 }
176
177 let Some(platform_impl) = this.platform_impl() else { return };
178
179 *this.transparency_fallback_border.borrow_mut() = e.name_scope().find_as::<Border>("PART_TransparencyFallback");
180 this.handle_transparency_level_changed(platform_impl.transparency_level());
181 }
182}
183
184impl TopLevelImpl for TopLevel {
185 fn handle_closed(this: &Self) {
186 this.disconnect_system_backdrop();
187 let source = this.presentation_source().clone();
188 source.dispose();
189 this.stop_rendering();
190
191 debug_assert!(this.platform_impl.borrow().is_some());
192 let platform_impl = this.platform_impl.borrow_mut().take();
194 if let Some(platform_impl) = platform_impl {
195 platform_impl.set_closed(None);
199 platform_impl.set_paint(None);
200 platform_impl.set_resized(None);
201 platform_impl.set_transparency_level_changed(None);
202 platform_impl.set_platform_specific_scene_info_changed(None);
203 }
204 this.scaling.set(1.0);
205
206 for subscription in this.global_styles_subscriptions.borrow_mut().drain(..) {
207 subscription.dispose();
208 }
209 if let Some(subscription) = this.application_theme_subscription.borrow_mut().take() {
210 subscription.dispose();
211 }
212
213 if let Some(subscription) = this.back_gesture_subscription.borrow_mut().take() {
214 subscription.dispose();
215 }
216
217 let this_ref = this.to_ref();
218 let logical_args = LogicalTreeAttachmentEventArgs::new(this_ref.clone().upcast(), this_ref.clone().upcast(), None);
219 this.notify_detached_from_logical_tree(&logical_args);
220
221 source.set_root_visual(None);
222
223 this.on_closed();
224
225 ILayoutManager::dispose(&**source.layout_manager());
226 this.platform_impl_bindings.borrow_mut().clear();
227
228 if let Some(host) = this.top_level_host.get() {
232 host.visual_children().remove(&this_ref.upcast());
233 }
234 }
235
236 fn handle_resized(this: &Self, client_size: Size, _reason: WindowResizeReason) {
237 this.set_client_size(client_size);
238 this.set_width(client_size.width);
239 this.set_height(client_size.height);
240 this.layout_manager().execute_layout_pass();
241 this.renderer().resized(client_size);
242 }
243
244 fn on_opened(this: &Self) {
245 raise(&this.opened);
246 }
247
248 fn on_closed(this: &Self) {
249 raise(&this.closed);
250 }
251
252 fn opened_popups(this: &Self) -> Vec<Ref<crate::primitives::Popup>> {
253 this.opened_popups.borrow().clone()
254 }
255}
256
257fn raise(handlers: &HandlerList<dyn Fn()>) {
260 if handlers.is_empty() {
261 return;
262 }
263 let snapshot = handlers.snapshot();
264 let _ = Dispatcher::ui_thread().invoke_local(move || {
265 for (_, handler) in snapshot.iter() {
266 handler();
267 }
268 });
269}
270
271fn property_key(property: &'static FerroProperty) -> usize {
272 property as *const FerroProperty as usize
273}
274
275fn subscribe(handlers: &HandlerList<dyn Fn()>, owner: &TopLevel, select: fn(&TopLevel) -> &HandlerList<dyn Fn()>, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
276 let token = handlers.add(handler);
277 let weak = owner.to_ref().downgrade();
278 Disposable::create(move || {
279 if let Some(owner) = weak.upgrade() {
280 select(&owner).remove(token);
281 }
282 })
283}
284
285fn add_lost_focus(platform_impl: &Rc<dyn ITopLevelImpl>, handler: Rc<dyn Fn()>) {
288 let previous = platform_impl.lost_focus();
289 platform_impl.set_lost_focus(Some(Rc::new(move || {
290 if let Some(previous) = &previous {
291 previous();
292 }
293 handler();
294 })));
295}
296
297impl TopLevel {
298 pub const TEMPLATE_PARTS: &'static [TemplatePartAttribute] = &[
300 TemplatePartAttribute::new("PART_TransparencyFallback", <Border as StaticType>::TYPE),
301 TemplatePartAttribute::new(
302 "PART_VisualLayerManager",
303 <crate::primitives::VisualLayerManager as StaticType>::TYPE,
304 ),
305 ];
306
307 pub fn visual_layer_manager(&self) -> Option<Ref<crate::primitives::VisualLayerManager>> {
310 self.visual_layer_manager.borrow().clone()
311 }
312
313 pub fn enable_visual_layer_manager_layers(&self) {
317 if let Some(vlm) = self.visual_layer_manager() {
318 vlm.set_enable_overlay_layer(true);
319 vlm.set_enable_popup_overlay_layer(true);
320 vlm.set_enable_text_selector_layer(true);
321 }
322 }
323}
324
325ferroui_base::ferro_properties! { impl TopLevel, also [
326 TopLevel::system_bar_color_property,
327 TopLevel::auto_safe_area_padding_property,
328] {
329 ferro_property!(
330 pub fn client_size_property() -> DirectProperty<TopLevel, Size> {
332 FerroProperty::register_direct::<TopLevel, _>("ClientSize", |o| o.client_size(), None, Size::default())
333 }
334 );
335
336 ferro_property!(
337 pub fn frame_size_property() -> DirectProperty<TopLevel, Option<Size>> {
339 FerroProperty::register_direct::<TopLevel, _>("FrameSize", |o| o.frame_size(), None, None)
340 }
341 );
342
343 ferro_property!(
344 pub fn transparency_level_hint_property() -> StyledProperty<WindowTransparencyLevelCollection> {
346 FerroProperty::register::<TopLevel, _>("TransparencyLevelHint", WindowTransparencyLevelCollection::default())
347 }
348 );
349
350 ferro_property!(
351 pub fn actual_transparency_level_property() -> DirectProperty<TopLevel, WindowTransparencyLevel> {
353 FerroProperty::register_direct::<TopLevel, _>(
354 "ActualTransparencyLevel",
355 |o| o.actual_transparency_level(),
356 None,
357 WindowTransparencyLevel::none(),
358 )
359 }
360 );
361
362 ferro_property!(
363 pub fn transparency_background_fallback_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
365 let white: Rc<dyn IBrush> = Brushes::white();
366 FerroProperty::register::<TopLevel, _>("TransparencyBackgroundFallback", Some(white))
367 }
368 );
369
370 ferro_property!(
371 pub fn system_backdrop_property() -> StyledProperty<Option<Ref<crate::media::SystemBackdrop>>> {
375 FerroProperty::register::<TopLevel, _>("SystemBackdrop", None)
376 }
377 );
378} }
379
380impl TopLevel {
381 pub fn actual_theme_variant_property() -> &'static StyledProperty<Option<ThemeVariant>> {
383 ThemeVariant::actual_theme_variant_property()
384 }
385
386 pub fn requested_theme_variant_property() -> &'static StyledProperty<Option<ThemeVariant>> {
388 ThemeVariant::requested_theme_variant_property()
389 }
390
391 ferro_property!(for TopLevel;
392 pub fn system_bar_color_property() -> AttachedProperty<Option<Ref<SolidColorBrush>>> {
394 FerroProperty::register_attached_with::<TopLevel, Control, _>(
395 "SystemBarColor",
396 StyledPropertyOptions::new(None).inherits(true),
397 )
398 }
399 );
400
401 ferro_property!(for TopLevel;
402 pub fn auto_safe_area_padding_property() -> AttachedProperty<bool> {
404 FerroProperty::register_attached::<TopLevel, Control, _>("AutoSafeAreaPadding", true)
405 }
406 );
407
408 ferro_routed_event!(
409 pub fn back_requested_event() -> RoutedEvent<RoutedEventArgs> {
411 RoutedEvent::register::<TopLevel, _>("BackRequested", RoutingStrategies::BUBBLE)
412 }
413 );
414
415 fn static_constructor() {
416 KeyboardNavigation::tab_navigation_property().override_default_value::<TopLevel>(KeyboardNavigationMode::Cycle);
417 Container::sizing_property().override_default_value::<TopLevel>(ContainerSizing::WidthAndHeight);
418
419 Layoutable::affects_measure::<TopLevel>(&[Self::client_size_property().as_property()]);
420
421 Self::system_bar_color_property().changed().add_class_handler::<Control>(|view, e| {
422 let (_, new_value) = e.get_old_and_new_value::<Option<Ref<SolidColorBrush>>>();
423 if let Some(color_brush) = new_value {
424 if let Some(insets_manager) =
425 view.parent().and_then(|parent| parent.cast::<TopLevel>()).and_then(|tl| tl.insets_manager())
426 {
427 insets_manager.set_system_bar_color(Some(color_brush.color()));
428 }
429
430 if let Some(insets) = view.downcast_ref::<TopLevel>().and_then(|tl| tl.insets_manager()) {
431 insets.set_system_bar_color(Some(color_brush.color()));
432 }
433 }
434 });
435
436 Self::auto_safe_area_padding_property().changed().add_class_handler::<Control>(|view, _| {
437 let top_level = view
438 .to_ref()
439 .cast::<TopLevel>()
440 .or_else(|| view.parent().and_then(|parent| parent.cast::<TopLevel>()));
441 if let Some(top_level) = top_level {
442 top_level.invalidate_child_insets_padding();
443 }
444 });
445
446 let _ = ToolTip::service_enabled_property().changed().subscribe(Self::on_tool_tip_service_enabled_changed);
448 }
449
450 fn on_tool_tip_service_enabled_changed(args: &FerroPropertyChangedEventArgs<'_>) {
451 if args.get_new_value::<bool>() && args.priority() != BindingPriority::Inherited {
452 let Some(visual) = args.sender().downcast_ref::<Visual>() else { return };
453 let Some(top_level) = Self::get_top_level(Some(visual)) else { return };
454 let offset = visual
455 .translate_point(Point::default(), &top_level)
456 .expect("the top-level is an ancestor of the visual");
457 top_level.update_tool_tip(visual.bounds().translate(Vector::new(offset.x, offset.y)));
458 }
459 }
460
461 fn update_tool_tip(&self, dirty_rect: Rect) {
462 let tooltip_service = self.tooltip_service.borrow().clone();
463 let Some(tooltip_service) = tooltip_service else { return };
464 if !self.is_pointer_over() {
465 return;
466 }
467 let source = self.presentation_source();
468 let Some(last_pos) = source.get_last_pointer_position(self) else { return };
469
470 let client_point = Visual::point_to_client(self, last_pos);
471 if dirty_rect.contains(client_point) {
472 let input_root: Rc<dyn IInputRoot> = source.clone();
473 let candidate = self.input_hit_test_with(client_point, false).map(|element| element.upcast::<Visual>());
474 tooltip_service.update(&input_root, candidate);
475 }
476 }
477
478 pub fn construct(platform_impl: Rc<dyn ITopLevelImpl>) -> Self {
482 Self::construct_with_resolver(platform_impl, None)
483 }
484
485 pub fn construct_with_resolver(
488 platform_impl: Rc<dyn ITopLevelImpl>,
489 dependency_resolver: Option<Rc<dyn IFerroDependencyResolver>>,
490 ) -> Self {
491 Self {
492 base: ContentControl::construct(),
493 dependency_resolver: RefCell::new(dependency_resolver),
494 input_manager: RefCell::new(None),
495 tooltip_service: RefCell::new(None),
496 access_key_handler: RefCell::new(None),
497 keyboard_navigation_handler: RefCell::new(None),
498 global_styles: RefCell::new(None),
499 global_styles_subscriptions: RefCell::new(Vec::new()),
500 application_theme_host: RefCell::new(None),
501 application_theme_subscription: RefCell::new(None),
502 back_gesture_subscription: RefCell::new(None),
503 resources_changed_subscription: RefCell::new(None),
504 platform_impl_bindings: RefCell::new(HashMap::new()),
505 scaling: Cell::new(1.0),
506 is_closed: Cell::new(false),
507 client_size: Cell::new(Size::default()),
508 frame_size: Cell::new(None),
509 actual_transparency_level: Cell::new(WindowTransparencyLevel::none()),
510 transparency_fallback_border: RefCell::new(None),
511 system_backdrop_target: RefCell::new(None),
512 connected_system_backdrop: RefCell::new(None),
513 visual_layer_manager: RefCell::new(None),
514 screens: RefCell::new(None),
515 storage_provider: RefCell::new(None),
516 source: OnceCell::new(),
517 top_level_host: OnceCell::new(),
518 platform_impl: RefCell::new(Some(platform_impl)),
519 insets_paddings: RefCell::new(None),
520 insets_subscription: RefCell::new(None),
521 opened: HandlerList::new(),
522 closed: HandlerList::new(),
523 scaling_changed: HandlerList::new(),
524 platform_lost_focus: HandlerList::new(),
525 platform_deactivated: HandlerList::new(),
526 platform_position_changed: HandlerList::new(),
527 opened_popups: RefCell::new(Vec::new()),
528 }
529 }
530
531 fn initialize(&self) {
533 let this = self.to_ref();
534 let platform_impl = self.platform_impl().expect(
535 "Could not create window implementation: maybe no windowing subsystem was initialized?",
536 );
537 let dependency_resolver =
538 self.dependency_resolver.borrow_mut().take().unwrap_or_else(FerroLocator::current);
539 let dependency_resolver: &dyn IFerroDependencyResolver = &*dependency_resolver;
540
541 let host_visual = TopLevelHost::new(&this);
542 if self.top_level_host.set(host_visual.clone()).is_err() {
543 unreachable!("the top-level is initialized once");
544 }
545 host_visual.set_parent(this.clone());
546 self.logical_children().add(host_visual.clone().upcast());
547
548 let source = PresentationSource::new(
549 host_visual.upcast(),
550 &this.clone().upcast(),
551 platform_impl.clone(),
552 dependency_resolver,
553 );
554 if self.source.set(source.clone()).is_err() {
555 unreachable!("the top-level is initialized once");
556 }
557 let _ = source.typed_renderer().scene_invalidated(Rc::new({
560 let weak = this.downgrade();
561 move |e| {
562 if let Some(this) = weak.upgrade() {
563 this.update_tool_tip(e.dirty_rect());
564 }
565 }
566 }));
567
568 self.scaling.set(LayoutHelper::validate_scaling(platform_impl.render_scaling()));
569 self.actual_transparency_level.set(platform_impl.transparency_level());
570 crate::media::on_top_level_created(self);
572
573 let renderer = source.typed_renderer();
574 renderer.set_transparency_level(to_composition_transparency_level(self.actual_transparency_level.get()));
575 renderer.set_platform_specific_scene_info(platform_impl.platform_specific_scene_info());
576
577 *self.access_key_handler.borrow_mut() = try_get_service::<dyn IAccessKeyHandler>(dependency_resolver);
578 *self.input_manager.borrow_mut() = try_get_service::<dyn IInputManager>(dependency_resolver);
579 *self.tooltip_service.borrow_mut() = try_get_service::<dyn IToolTipService>(dependency_resolver);
580 *self.keyboard_navigation_handler.borrow_mut() =
581 try_get_service::<dyn IKeyboardNavigationHandler>(dependency_resolver);
582 *self.global_styles.borrow_mut() = try_get_service::<dyn IGlobalStyles>(dependency_resolver);
583 *self.application_theme_host.borrow_mut() = try_get_service::<dyn IThemeVariantHost>(dependency_resolver);
584
585 let weak = this.downgrade();
586 platform_impl.set_closed(Some(Rc::new({
591 let this = this.clone();
592 move || this.ensure_closed()
593 })));
594 platform_impl.set_paint(Some(Rc::new({
595 let weak = weak.clone();
596 move |rect| {
597 if let Some(this) = weak.upgrade() {
598 this.handle_paint(rect);
599 }
600 }
601 })));
602 platform_impl.set_resized(Some(Rc::new({
603 let weak = weak.clone();
604 move |client_size, reason| {
605 if let Some(this) = weak.upgrade() {
606 this.handle_resized(client_size, reason);
607 }
608 }
609 })));
610 let _ = crate::presentation_source::add_scaling_changed(
613 &platform_impl,
614 Rc::new({
615 let weak = weak.clone();
616 move |scaling| {
617 if let Some(this) = weak.upgrade() {
618 this.handle_scaling_changed(scaling);
619 }
620 }
621 }),
622 );
623 platform_impl.set_transparency_level_changed(Some(Rc::new({
624 let weak = weak.clone();
625 move |level| {
626 if let Some(this) = weak.upgrade() {
627 this.handle_transparency_level_changed(level);
628 }
629 }
630 })));
631 platform_impl.set_platform_specific_scene_info_changed(Some(Rc::new({
632 let weak = weak.clone();
633 move |scene_info| {
634 if let Some(this) = weak.upgrade() {
635 this.handle_platform_specific_scene_info_changed(scene_info);
636 }
637 }
638 })));
639
640 self.create_platform_impl_binding(Self::transparency_level_hint_property(), {
641 let weak = weak.clone();
642 move |hint: WindowTransparencyLevelCollection| {
643 if let Some(platform_impl) = weak.upgrade().and_then(|this| this.platform_impl()) {
644 platform_impl.set_transparency_level_hint(&hint);
645 }
646 }
647 });
648
649 let keyboard_navigation_handler = self.keyboard_navigation_handler.borrow().clone();
650 if let Some(handler) = keyboard_navigation_handler {
651 handler.set_owner(&this.clone().upcast());
652 }
653 let access_key_handler = self.access_key_handler.borrow().clone();
654 if let Some(handler) = access_key_handler {
655 handler.set_owner(&this.clone().upcast());
656 }
657
658 let global_styles = self.global_styles.borrow().clone();
659 if let Some(global_styles) = &global_styles {
660 let added = global_styles.global_styles_added(Rc::new({
661 let weak = weak.clone();
662 move |styles: &[Rc<dyn IStyle>]| {
663 if let Some(this) = weak.upgrade() {
664 this.styles_added(styles);
665 }
666 }
667 }));
668 let removed = global_styles.global_styles_removed(Rc::new({
669 let weak = weak.clone();
670 move |styles: &[Rc<dyn IStyle>]| {
671 if let Some(this) = weak.upgrade() {
672 this.styles_removed(styles);
673 }
674 }
675 }));
676 self.global_styles_subscriptions.borrow_mut().extend([added, removed]);
677 }
678
679 let application_theme_host = self.application_theme_host.borrow().clone();
680 if let Some(host) = &application_theme_host {
681 let _ = self.set_value_with_priority(
682 Self::actual_theme_variant_property(),
683 host.actual_theme_variant(),
684 BindingPriority::Template,
685 );
686 let weak_host = Rc::downgrade(host);
687 let subscription = host.actual_theme_variant_changed(Rc::new({
688 let weak = weak.clone();
689 move || {
690 if let (Some(this), Some(host)) = (weak.upgrade(), weak_host.upgrade()) {
691 this.global_actual_theme_variant_changed(&*host);
692 }
693 }
694 }));
695 *self.application_theme_subscription.borrow_mut() = Some(subscription);
696 } else {
697 ThemeVariant::update_actual_theme_variant(self);
698 }
699
700 self.create_platform_impl_binding(Self::actual_theme_variant_property(), {
701 let weak = weak.clone();
702 move |variant: Option<ThemeVariant>| {
703 if let Some(platform_impl) = weak.upgrade().and_then(|this| this.platform_impl()) {
704 platform_impl.set_frame_theme_variant(variant.as_ref().and_then(to_platform_theme_variant));
705 }
706 }
707 });
708
709 self.set_client_size(platform_impl.client_size());
710
711 if let Some(application_resources) = &global_styles {
712 let subscription = application_resources.resources_changed(Rc::new({
713 let weak = weak.clone();
714 move |e| {
715 if let Some(this) = weak.upgrade() {
716 this.notify_resources_changed(*e, true);
717 }
718 }
719 }));
720 *self.resources_changed_subscription.borrow_mut() = Some(subscription);
721 }
722
723 add_lost_focus(
724 &platform_impl,
725 Rc::new({
726 let weak = weak.clone();
727 move || {
728 if let Some(this) = weak.upgrade() {
729 this.platform_impl_lost_focus();
730 for (_, handler) in this.platform_lost_focus.snapshot().iter() {
731 handler();
732 }
733 }
734 }
735 }),
736 );
737
738 if let Some(window_base_impl) = platform_impl.as_window_base_impl() {
743 window_base_impl.set_deactivated(Some(Rc::new({
744 let weak = weak.clone();
745 move || {
746 if let Some(this) = weak.upgrade() {
747 this.raise_platform_deactivated();
748 }
749 }
750 })));
751 window_base_impl.set_position_changed(Some(Rc::new({
752 let weak = weak.clone();
753 move |position| {
754 if let Some(this) = weak.upgrade() {
755 this.raise_platform_position_changed(position);
756 }
757 }
758 })));
759 }
760
761 if let Some(system_navigation_manager) = self.optional_feature::<dyn ISystemNavigationManagerImpl>() {
762 let _ = system_navigation_manager.back_requested(Rc::new({
765 let weak = weak.clone();
766 move |e: &Rc<RoutedEventArgs>| {
767 if let Some(this) = weak.upgrade() {
768 e.set_routed_event(Some(Self::back_requested_event()));
769 let e = e.clone();
770 let _ = Dispatcher::ui_thread().invoke_local(move || this.raise_event(&*e));
771 }
772 }
773 }));
774 }
775
776 let input_manager = self.input_manager.borrow().clone();
777 *self.back_gesture_subscription.borrow_mut() = input_manager.map(|input_manager| {
778 input_manager.pre_process().subscribe(Rc::new(AnonymousObserver::new({
779 let weak = weak.clone();
780 move |e: Rc<dyn IRawInputEventArgs>| {
781 if let Some(this) = weak.upgrade() {
782 this.back_gesture_pre_process(&*e);
783 }
784 }
785 })))
786 });
787 }
788
789 fn back_gesture_pre_process(&self, e: &dyn IRawInputEventArgs) {
790 let input_root = self.input_root();
791 if !std::ptr::addr_eq(Rc::as_ptr(e.root()), Rc::as_ptr(&input_root)) {
792 return;
793 }
794
795 let mut back_requested = false;
796
797 if let Some(raw_key_event_args) = e.downcast_ref::<RawKeyEventArgs>() {
798 if raw_key_event_args.type_() == RawKeyEventType::KeyDown {
799 if let Some(platform_settings) = self.platform_settings() {
800 let hotkeys = platform_settings.hotkey_configuration();
801
802 let mut key_event = KeyEventArgs::new();
803 key_event.key_modifiers = KeyModifiers::from_bits_retain(raw_key_event_args.modifiers().bits());
804 key_event.key = raw_key_event_args.key();
805 key_event.physical_key = raw_key_event_args.physical_key();
806 key_event.key_device_type = raw_key_event_args.key_device_type();
807 key_event.key_symbol = raw_key_event_args.key_symbol();
808
809 back_requested = hotkeys.back.iter().any(|key| key.matches(Some(&key_event)));
810 }
811 }
812 } else if let Some(pointer_event_args) = e.downcast_ref::<RawPointerEventArgs>() {
813 back_requested = pointer_event_args.type_() == RawPointerEventType::XButton1Down;
814 }
815
816 if back_requested {
817 let back_requested_event_args = Rc::new(RoutedEventArgs::with_event(Self::back_requested_event()));
818 let this = self.to_ref();
819 let args = back_requested_event_args.clone();
820 let _ = Dispatcher::ui_thread().invoke_local(move || this.raise_event(&*args));
821
822 e.set_handled(back_requested_event_args.handled());
823 }
824 }
825
826 pub fn opened(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
829 subscribe(&self.opened, self, |tl| &tl.opened, Rc::new(handler))
830 }
831
832 pub fn closed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
835 subscribe(&self.closed, self, |tl| &tl.closed, Rc::new(handler))
836 }
837
838 pub fn scaling_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
841 subscribe(&self.scaling_changed, self, |tl| &tl.scaling_changed, Rc::new(handler))
842 }
843
844 pub fn platform_lost_focus(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
854 subscribe(&self.platform_lost_focus, self, |tl| &tl.platform_lost_focus, Rc::new(handler))
855 }
856
857 pub fn platform_deactivated(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
861 subscribe(&self.platform_deactivated, self, |tl| &tl.platform_deactivated, Rc::new(handler))
862 }
863
864 pub fn platform_position_changed(&self, handler: impl Fn(PixelPoint) + 'static) -> Rc<dyn IDisposable> {
868 let token = self.platform_position_changed.add(Rc::new(handler));
869 let weak = self.to_ref().downgrade();
870 Disposable::create(move || {
871 if let Some(this) = weak.upgrade() {
872 this.platform_position_changed.remove(token);
873 }
874 })
875 }
876
877 pub(crate) fn raise_platform_deactivated(&self) {
878 for (_, handler) in self.platform_deactivated.snapshot().iter() {
879 handler();
880 }
881 }
882
883 pub(crate) fn raise_platform_position_changed(&self, position: PixelPoint) {
884 for (_, handler) in self.platform_position_changed.snapshot().iter() {
885 handler(position);
886 }
887 }
888
889 pub fn client_size(&self) -> Size {
891 self.client_size.get()
892 }
893
894 pub fn set_client_size(&self, value: Size) {
897 self.set_and_raise_cell(Self::client_size_property(), &self.client_size, value);
898 }
899
900 pub fn frame_size(&self) -> Option<Size> {
902 self.frame_size.get()
903 }
904
905 pub fn set_frame_size(&self, value: Option<Size>) {
908 self.set_and_raise_cell(Self::frame_size_property(), &self.frame_size, value);
909 }
910
911 pub fn transparency_level_hint(&self) -> WindowTransparencyLevelCollection {
917 self.get_value(Self::transparency_level_hint_property())
918 }
919
920 pub fn set_transparency_level_hint(&self, value: WindowTransparencyLevelCollection) {
921 self.set_value(Self::transparency_level_hint_property(), value)
922 }
923
924 pub fn actual_transparency_level(&self) -> WindowTransparencyLevel {
927 self.actual_transparency_level.get()
928 }
929
930 fn set_actual_transparency_level(&self, value: WindowTransparencyLevel) {
931 self.set_and_raise_cell(Self::actual_transparency_level_property(), &self.actual_transparency_level, value);
932 }
933
934 pub fn transparency_background_fallback(&self) -> Option<Rc<dyn IBrush>> {
937 self.get_value(Self::transparency_background_fallback_property())
938 }
939
940 pub fn set_transparency_background_fallback(&self, value: Option<Rc<dyn IBrush>>) {
941 self.set_value(Self::transparency_background_fallback_property(), value)
942 }
943
944 pub fn requested_theme_variant(&self) -> Option<ThemeVariant> {
946 self.get_value(Self::requested_theme_variant_property())
947 }
948
949 pub fn set_requested_theme_variant(&self, value: Option<ThemeVariant>) {
950 self.set_value(Self::requested_theme_variant_property(), value)
951 }
952
953 pub fn back_requested(&self, handler: impl Fn(&Interactive, &RoutedEventArgs) + 'static) -> RoutedEventHandlerToken {
956 self.add_handler(Self::back_requested_event(), handler)
957 }
958
959 pub fn layout_manager(&self) -> Rc<LayoutManager> {
961 self.presentation_source().layout_manager().clone()
962 }
963
964 pub fn platform_impl(&self) -> Option<Rc<dyn ITopLevelImpl>> {
967 self.platform_impl.borrow().clone()
968 }
969
970 pub fn try_get_platform_handle(&self) -> Option<Rc<dyn IPlatformHandle>> {
975 self.platform_impl().and_then(|platform_impl| platform_impl.handle())
976 }
977
978 pub fn create_platform_impl_binding<T: ferroui_base::PropertyValue>(
982 &self,
983 property: &'static StyledProperty<T>,
984 on_value: impl Fn(T) + 'static,
985 ) {
986 let weak = self.to_ref().downgrade();
987 let update_platform_impl: Rc<dyn Fn()> = Rc::new(move || {
988 if let Some(this) = weak.upgrade() {
989 if this.platform_impl.borrow().is_some() {
990 on_value(this.get_value(property));
991 }
992 }
993 });
994 self.platform_impl_bindings
995 .borrow_mut()
996 .entry(property_key(property.as_property()))
997 .or_default()
998 .push(update_platform_impl.clone());
999
1000 update_platform_impl();
1002 }
1003
1004 pub fn presentation_source(&self) -> &Rc<PresentationSource> {
1006 self.source.get().expect("the presentation source is created with the top-level")
1007 }
1008
1009 pub(crate) fn top_level_host(&self) -> &Ref<TopLevelHost> {
1011 self.top_level_host.get().expect("the host is created with the top-level")
1012 }
1013
1014 pub fn input_root(&self) -> Rc<dyn IInputRoot> {
1016 self.presentation_source().clone()
1017 }
1018
1019 pub fn renderer(&self) -> Rc<dyn ITopLevelRenderer> {
1021 self.presentation_source().typed_renderer().clone()
1022 }
1023
1024 pub fn hit_tester_override(&self) -> Option<Rc<dyn IHitTester>> {
1028 self.presentation_source().hit_tester_override()
1029 }
1030
1031 pub fn set_hit_tester_override(&self, value: Option<Rc<dyn IHitTester>>) {
1032 self.presentation_source().set_hit_tester_override(value)
1033 }
1034
1035 pub fn renderer_diagnostics(&self) -> Rc<RendererDiagnostics> {
1038 self.renderer().diagnostics()
1039 }
1040
1041 pub fn last_pointer_position(&self) -> Option<PixelPoint> {
1043 self.presentation_source().get_last_pointer_position(self)
1044 }
1045
1046 pub fn access_key_handler(&self) -> Option<Rc<dyn IAccessKeyHandler>> {
1048 self.access_key_handler.borrow().clone()
1049 }
1050
1051 pub fn tests_keyboard_navigation_handler(&self) -> Option<Rc<dyn IKeyboardNavigationHandler>> {
1053 self.keyboard_navigation_handler.borrow().clone()
1054 }
1055
1056 pub fn set_system_bar_color(control: &Control, color: Option<Ref<SolidColorBrush>>) {
1060 control.set_value(Self::system_bar_color_property(), color);
1061 }
1062
1063 pub fn get_system_bar_color(control: &Control) -> Option<Ref<SolidColorBrush>> {
1067 control.get_value(Self::system_bar_color_property())
1068 }
1069
1070 pub fn set_auto_safe_area_padding(control: &Control, value: bool) {
1074 control.set_value(Self::auto_safe_area_padding_property(), value);
1075 }
1076
1077 pub fn get_auto_safe_area_padding(control: &Control) -> bool {
1080 control.get_value(Self::auto_safe_area_padding_property())
1081 }
1082
1083 pub fn render_scaling(&self) -> f64 {
1085 self.scaling.get()
1086 }
1087
1088 fn optional_feature<T: ?Sized + 'static>(&self) -> Option<Rc<T>> {
1089 let platform_impl = self.platform_impl()?;
1090 let provider: &dyn IOptionalFeatureProvider = &*platform_impl;
1091 provider.try_get::<T>()
1092 }
1093
1094 pub fn storage_provider(&self) -> Rc<dyn ferroui_base::platform::storage::IStorageProvider> {
1099 use ferroui_base::platform::storage::{IStorageProvider, NoopStorageProvider};
1100 use ferroui_base::LocatorExtensions;
1101
1102 if let Some(storage_provider) = self.storage_provider.borrow().clone() {
1103 return storage_provider;
1104 }
1105
1106 let storage_provider = FerroLocator::current()
1107 .get_service::<dyn crate::platform::IStorageProviderFactory>()
1108 .map(|factory| factory.create_provider(&self.to_ref()))
1109 .or_else(|| self.optional_feature::<dyn IStorageProvider>())
1110 .unwrap_or_else(|| Rc::new(NoopStorageProvider));
1111 *self.storage_provider.borrow_mut() = Some(storage_provider.clone());
1112 storage_provider
1113 }
1114
1115 pub fn insets_manager(&self) -> Option<Rc<dyn IInsetsManager>> {
1117 self.optional_feature::<dyn IInsetsManager>()
1118 }
1119
1120 pub fn input_pane(&self) -> Option<Rc<dyn IInputPane>> {
1122 self.optional_feature::<dyn IInputPane>()
1123 }
1124
1125 pub fn launcher(&self) -> Rc<dyn ferroui_base::platform::storage::ILauncher> {
1129 use ferroui_base::platform::storage::{ILauncher, NoopLauncher};
1130
1131 self.optional_feature::<dyn ILauncher>().unwrap_or_else(|| Rc::new(NoopLauncher))
1132 }
1133
1134 pub fn screens(&self) -> Option<Rc<Screens>> {
1136 if let Some(screens) = self.screens.borrow().clone() {
1137 return Some(screens);
1138 }
1139 let screens = Screens::new(self.optional_feature::<dyn IScreenImpl>()?);
1140 *self.screens.borrow_mut() = Some(screens.clone());
1141 Some(screens)
1142 }
1143
1144 pub fn clipboard(&self) -> Option<Rc<dyn IClipboard>> {
1146 self.try_get_clipboard(ClipboardType::Default)
1147 }
1148
1149 pub fn try_get_clipboard(&self, type_: ClipboardType) -> Option<Rc<dyn IClipboard>> {
1152 if let Some(clipboard_manager) = self.optional_feature::<dyn IPlatformClipboardManagerImpl>() {
1153 return clipboard_manager.try_get_clipboard(type_);
1154 }
1155
1156 if type_ == ClipboardType::Default {
1157 self.optional_feature::<dyn IClipboard>()
1158 } else {
1159 None
1160 }
1161 }
1162
1163 pub fn focus_manager(&self) -> Rc<FocusManager> {
1165 self.presentation_source().focus_manager().clone()
1166 }
1167
1168 fn platform_settings(&self) -> Option<Rc<dyn IPlatformSettings>> {
1170 use ferroui_base::LocatorExtensions;
1171 FerroLocator::current().get_service::<dyn IPlatformSettings>()
1172 }
1173
1174 pub fn get_top_level(visual: Option<&Visual>) -> Option<Ref<TopLevel>> {
1176 let mut visual = visual.map(|visual| visual.to_ref());
1177 while let Some(current) = visual {
1178 if let Some(tl) = current.clone().cast::<TopLevel>() {
1179 return Some(tl);
1180 }
1181 visual = current.visual_parent();
1182 }
1183
1184 None
1185 }
1186
1187 pub fn request_platform_inhibition(
1200 &self,
1201 type_: PlatformInhibitionType,
1202 reason: &str,
1203 ) -> DispatcherTask<Rc<dyn IDisposable>> {
1204 let platform_behavior_inhibition = self.optional_feature::<dyn IPlatformBehaviorInhibition>();
1205 let reason = reason.to_owned();
1206
1207 Dispatcher::ui_thread().to_task_scheduler().start_local(async move {
1208 let Some(platform_behavior_inhibition) = platform_behavior_inhibition else {
1209 return Disposable::create(|| {});
1210 };
1211
1212 match type_ {
1213 PlatformInhibitionType::AppSleep => {
1214 platform_behavior_inhibition.set_inhibit_app_sleep(true, &reason).await;
1215 Disposable::create(move || {
1216 let lifted = platform_behavior_inhibition.set_inhibit_app_sleep(false, &reason);
1217 drop(Dispatcher::ui_thread().to_task_scheduler().start_local(lifted));
1218 })
1219 }
1220 }
1221 })
1222 }
1223
1224 pub fn request_animation_frame(&self, action: impl FnOnce(ferroui_base::animation::TimeSpan) + 'static) {
1226 Dispatcher::ui_thread().verify_access();
1227 MediaContext::instance().request_animation_frame(action);
1228 }
1229
1230 fn invalidate_child_insets_padding(&self) {
1231 let Some(child) = self.content().and_then(|content| Control::from_boxed(&content)) else { return };
1232 let Some(insets_manager) = self.insets_manager() else { return };
1233
1234 if let Some(subscription) = self.insets_subscription.borrow_mut().take() {
1235 subscription.dispose();
1236 }
1237 if let Some(insets_paddings) = self.insets_paddings.borrow_mut().take() {
1238 insets_paddings.dispose();
1239 }
1240
1241 if child.get_value(Self::auto_safe_area_padding_property()) {
1242 let weak = self.to_ref().downgrade();
1243 let subscription = insets_manager.safe_area_changed(Rc::new(move |_: &SafeAreaChangedArgs| {
1244 if let Some(this) = weak.upgrade() {
1245 this.invalidate_child_insets_padding();
1246 }
1247 }));
1248 *self.insets_subscription.borrow_mut() = Some(subscription);
1249 let insets_paddings = child.set_value_with_priority(
1251 TemplatedControl::padding_property(),
1252 insets_manager.safe_area_padding(),
1253 BindingPriority::Style,
1254 );
1255 *self.insets_paddings.borrow_mut() = insets_paddings;
1256 }
1257 }
1258
1259 fn handle_paint(&self, rect: Rect) {
1262 self.renderer().paint(rect);
1263 }
1264
1265 pub fn start_rendering(&self) {
1268 let renderer: Rc<dyn IRenderer> = self.renderer();
1269 MediaContext::instance().add_top_level(self.media_context_key(), renderer);
1270 }
1271
1272 pub fn stop_rendering(&self) {
1275 MediaContext::instance().remove_top_level(self.media_context_key());
1276 }
1277
1278 pub fn media_context_key(&self) -> usize {
1280 self as *const TopLevel as usize
1281 }
1282
1283 pub fn ensure_closed(&self) {
1287 if self.is_closed.replace(true) {
1288 return;
1289 }
1290 self.handle_closed();
1291 }
1292
1293 fn handle_scaling_changed(&self, scaling: f64) {
1296 if self.is_closed.get() {
1297 return;
1298 }
1299 self.scaling.set(LayoutHelper::validate_scaling(scaling));
1300 LayoutHelper::invalidate_self_and_children_measure(self);
1301 raise(&self.scaling_changed);
1302
1303 self.invalidate_child_insets_padding();
1304 }
1305
1306 fn handle_transparency_level_changed(&self, transparency_level: WindowTransparencyLevel) {
1307 let border = self.transparency_fallback_border.borrow().clone();
1308 if let Some(border) = border {
1309 if let Some(target) = self.active_system_backdrop_target() {
1310 border.set_background(Some(target.host_brush()));
1312 } else if transparency_level == WindowTransparencyLevel::none() {
1313 border.set_background(self.transparency_background_fallback());
1314 } else {
1315 border.set_background(None);
1316 }
1317 }
1318
1319 self.set_actual_transparency_level(transparency_level);
1320 self.renderer().set_transparency_level(to_composition_transparency_level(transparency_level));
1321 }
1322
1323 fn active_system_backdrop_target(&self) -> Option<Rc<crate::media::TopLevelBackdropTarget>> {
1325 self.connected_system_backdrop
1326 .borrow()
1327 .is_some()
1328 .then(|| self.system_backdrop_target.borrow().clone())
1329 .flatten()
1330 }
1331
1332 pub fn system_backdrop(&self) -> Option<Ref<crate::media::SystemBackdrop>> {
1334 self.get_value(Self::system_backdrop_property())
1335 }
1336
1337 pub fn set_system_backdrop(&self, value: Option<Ref<crate::media::SystemBackdrop>>) {
1338 self.set_value(Self::system_backdrop_property(), value)
1339 }
1340
1341 pub fn system_backdrop_target(&self) -> Rc<crate::media::TopLevelBackdropTarget> {
1343 self.system_backdrop_target
1344 .borrow_mut()
1345 .get_or_insert_with(|| crate::media::TopLevelBackdropTarget::new(self))
1346 .clone()
1347 }
1348
1349 fn connect_system_backdrop(&self) {
1352 self.disconnect_system_backdrop();
1353 let Some(backdrop) = self.system_backdrop() else {
1354 self.update_system_backdrop_visual();
1355 return;
1356 };
1357 if self.platform_impl().is_none() {
1358 return;
1359 }
1360 let target = self.system_backdrop_target();
1361 *self.connected_system_backdrop.borrow_mut() = Some(backdrop.clone());
1362 let target: Rc<dyn ferroui_base::rendering::composition::system_backdrops::ICompositionSupportsSystemBackdrop> =
1363 target;
1364 backdrop.on_target_connected(&target, self);
1365 self.update_system_backdrop_visual();
1366 }
1367
1368 fn disconnect_system_backdrop(&self) {
1369 let previous = self.connected_system_backdrop.borrow_mut().take();
1370 if let Some(previous) = previous {
1371 if let Some(target) = self.system_backdrop_target.borrow().clone() {
1372 let target: Rc<dyn ferroui_base::rendering::composition::system_backdrops::ICompositionSupportsSystemBackdrop> =
1373 target;
1374 previous.on_target_disconnected(&target);
1375 }
1376 }
1377 self.update_system_backdrop_visual();
1378 }
1379
1380 pub(crate) fn update_system_backdrop_visual(&self) {
1385 let active = self.active_system_backdrop_target().is_some();
1386 if active {
1387 self.pseudo_classes().add_pseudo(":system-backdrop");
1388 } else {
1389 self.pseudo_classes().remove_pseudo(":system-backdrop");
1390 }
1391 let level = self.actual_transparency_level();
1392 let border = self.transparency_fallback_border.borrow().clone();
1393 if let Some(border) = border {
1394 if let Some(target) = self.active_system_backdrop_target() {
1395 border.set_background(Some(target.host_brush()));
1396 } else if level == WindowTransparencyLevel::none() {
1397 border.set_background(self.transparency_background_fallback());
1398 } else {
1399 border.set_background(None);
1400 }
1401 }
1402 }
1403
1404 fn handle_platform_specific_scene_info_changed(&self, scene_info: Option<std::sync::Arc<dyn Any + Send + Sync>>) {
1405 self.renderer().set_platform_specific_scene_info(scene_info);
1406 }
1407
1408 fn global_actual_theme_variant_changed(&self, sender: &dyn IThemeVariantHost) {
1409 let _ = self.set_value_with_priority(
1410 Self::actual_theme_variant_property(),
1411 sender.actual_theme_variant(),
1412 BindingPriority::Template,
1413 );
1414 }
1415
1416 pub fn add_opened_popup(&self, popup: &Ref<crate::primitives::Popup>) {
1419 self.opened_popups.borrow_mut().push(popup.clone());
1420 }
1421
1422 pub fn remove_opened_popup(&self, popup: &Ref<crate::primitives::Popup>) {
1425 let mut opened_popups = self.opened_popups.borrow_mut();
1426 if let Some(index) = opened_popups.iter().position(|p| p == popup) {
1427 opened_popups.remove(index);
1428 }
1429 }
1430
1431 fn platform_impl_lost_focus(&self) {
1432 let focused = self.focus_manager().get_focused_element();
1433 let focused = Self::get_top_level(focused.as_deref().map(|element| -> &Visual { element }));
1434
1435 if focused.is_some_and(|focused| std::ptr::eq(&*focused, self)) {
1436 if let Some(keyboard_device) = KeyboardDevice::instance() {
1437 keyboard_device.set_focused_element_with(None, NavigationMethod::Unspecified, KeyModifiers::NONE, false);
1438 }
1439 }
1440 }
1441}
1442
1443impl Drop for TopLevel {
1444 fn drop(&mut self) {
1445 if let Some(subscription) = self.resources_changed_subscription.borrow_mut().take() {
1446 subscription.dispose();
1447 }
1448 }
1449}
1450
1451fn to_platform_theme_variant(theme_variant: &ThemeVariant) -> Option<PlatformThemeVariant> {
1455 if *theme_variant == ThemeVariant::light() {
1456 Some(PlatformThemeVariant::Light)
1457 } else if *theme_variant == ThemeVariant::dark() {
1458 Some(PlatformThemeVariant::Dark)
1459 } else if let Some(inherit_variant) = theme_variant.inherit_variant() {
1460 to_platform_theme_variant(&inherit_variant)
1461 } else {
1462 None
1463 }
1464}
1465
1466fn to_composition_transparency_level(level: WindowTransparencyLevel) -> CompositionTransparencyLevel {
1467 if level == WindowTransparencyLevel::transparent() {
1468 return CompositionTransparencyLevel::Transparent;
1469 }
1470 if level == WindowTransparencyLevel::blur() {
1471 return CompositionTransparencyLevel::Blur;
1472 }
1473 if level == WindowTransparencyLevel::acrylic_blur() {
1474 return CompositionTransparencyLevel::AcrylicBlur;
1475 }
1476 if level == WindowTransparencyLevel::mica() {
1477 return CompositionTransparencyLevel::Mica;
1478 }
1479 CompositionTransparencyLevel::None
1480}
1481
1482struct TopLevelCloseable(Ref<TopLevel>);
1484
1485impl ferroui_base::input::ICloseable for TopLevelCloseable {
1486 fn closed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
1487 self.0.closed(move || handler())
1488 }
1489}
1490
1491impl TopLevel {
1492 pub fn as_closeable(&self) -> Rc<dyn ferroui_base::input::ICloseable> {
1494 Rc::new(TopLevelCloseable(self.to_ref()))
1495 }
1496}