1use crate::platform::{IWindowBaseImpl, IWindowImpl, IWindowingPlatform, PlatformAllowedWindowActions, PlatformManager, Screen};
2use crate::primitives::{TemplateAppliedEventArgs, TemplatedControl, TemplatedControlImpl, TemplatedControlImplExt};
3use crate::window_icon::WindowIcon;
4use crate::{
5 ContentControlImpl, ControlImpl, SizeToContent, TopLevel, TopLevelImpl, TopLevelImplExt, WindowBase,
6 WindowBaseImpl, WindowCloseReason, WindowClosingEventArgs, WindowDecorations, WindowEdge, WindowResizeReason,
7 WindowResizedEventArgs, WindowStartupLocation, WindowState,
8};
9use ferroui_base::input::{IFocusScope, InputElementImpl, PointerPressedEventArgs};
10use ferroui_base::interactivity::{InteractiveImpl, RoutedEvent, RoutedEventArgs, RoutingStrategies};
11use ferroui_base::layout::{ILayoutManager, Layoutable, LayoutableImpl, LayoutableImplExt, MinMax};
12use ferroui_base::media::{Brushes, IBrush};
13use ferroui_base::reactive::{CompositeDisposable, Disposable, IDisposable, ObservableExt};
14use ferroui_base::styling::ControlTheme;
15use ferroui_base::threading::{Dispatcher, DispatcherTask};
16use ferroui_base::utilities::{HandlerList, MathUtilities};
17use ferroui_base::{
18 ferro_class, ferro_impl_classes, ferro_property, ferro_routed_event, instantiate, BoxedValue, DirectProperty,
19 FerroLocator, FerroObject, FerroObjectExtensions, FerroObjectImpl, FerroObjectImplExt, FerroProperty,
20 FerroPropertyChangedEventArgs, LocatorExtensions, Nullable, PixelPoint, PixelRect, PixelSize, PropertyValue, Ref,
21 Size, StaticType, StyledElementImpl, StyledProperty, StyledPropertyOptions, Thickness, TypeInfo, VisualImpl,
22};
23use std::cell::{Cell, RefCell};
24use std::future::Future;
25use std::pin::Pin;
26use std::rc::Rc;
27use std::task::{Context, Poll, Waker};
28
29#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
32#[repr(i32)]
33pub enum WindowClosingBehavior {
34 #[default]
39 OwnerAndChildWindows = 0,
40
41 OwnerWindowOnly = 1,
44}
45
46#[repr(C)]
48pub struct Window {
49 base: WindowBase,
50 window_impl: RefCell<Option<Rc<dyn IWindowImpl>>>,
51 children: RefCell<Vec<(Ref<Window>, bool)>>,
52 is_extended_into_window_decorations: Cell<bool>,
53 window_decoration_margin: Cell<Thickness>,
54 off_screen_margin: Cell<Thickness>,
55 can_handle_resized: Cell<bool>,
56 arrange_bounds: Cell<Size>,
57 is_forced_decoration_mode: Cell<bool>,
58 dialog_result: RefCell<Option<BoxedValue>>,
59 shown: Cell<bool>,
60 showing_as_dialog: Cell<bool>,
61 position_was_set: Cell<bool>,
62 was_shown_before: Cell<bool>,
63 modal_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
64 allowed_window_actions: Cell<PlatformAllowedWindowActions>,
65 last_window_state: Cell<WindowState>,
66 closing: HandlerList<dyn Fn(&WindowClosingEventArgs)>,
67 allowed_window_actions_changed: HandlerList<dyn Fn(PlatformAllowedWindowActions)>,
68}
69
70ferro_class! {
71 Window: WindowBase, virtuals WindowImpl: WindowBaseImpl {
72 fn handle_closing(this, reason: WindowCloseReason) -> bool;
76 fn on_closing(this, e: &WindowClosingEventArgs);
82 }
83}
84ferroui_base::ferro_class_info!(Window { new: Window::new });
85
86ferro_impl_classes!(Window: VisualImpl, InteractiveImpl, ContentControlImpl);
87
88impl ControlImpl for Window {
89 fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
90 crate::automation::peers::WindowAutomationPeer::new(this).upcast()
91 }
92}
93
94impl IFocusScope for Window {}
95
96impl InputElementImpl for Window {
97 fn is_focus_scope(_this: &Self) -> bool {
98 true
99 }
100}
101
102impl StyledElementImpl for Window {
103 fn style_key_override(_this: &Self) -> &'static TypeInfo {
104 <Window as StaticType>::TYPE
105 }
106}
107
108impl FerroObjectImpl for Window {
109 fn constructed(this: &Self) {
110 Self::parent_constructed(this);
111 this.initialize();
112 }
113
114 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
115 Self::parent_on_property_changed(this, change);
116
117 if change.property() == Self::window_decorations_property().as_property() {
118 let (_, typed_new_value) = change.get_old_and_new_value::<WindowDecorations>();
119
120 if let Some(platform_impl) = this.platform_impl() {
121 platform_impl.set_window_decorations(typed_new_value);
122 }
123 this.update_drawn_decorations();
124 } else if change.property() == Self::window_decorations_theme_property().as_property() {
125 this.update_drawn_decorations();
126 } else if change.property() == WindowBase::owner_property().as_property() {
127 let (old_parent, new_parent) = change.get_old_and_new_value::<Option<Ref<WindowBase>>>();
128 let old_parent = old_parent.and_then(|parent| parent.cast::<Window>());
129 let new_parent = new_parent.and_then(|parent| parent.cast::<Window>());
130
131 if let Some(old_parent) = &old_parent {
132 old_parent.remove_child(this);
133 }
134 if let Some(new_parent) = &new_parent {
135 new_parent.add_child(this, this.showing_as_dialog.get());
136 }
137
138 if let Some(platform_impl) = this.platform_impl() {
139 platform_impl.set_parent(new_parent.and_then(|parent| parent.platform_impl()));
140 }
141 } else if change.property() == Self::can_resize_property().as_property() {
142 this.coerce_value(Self::can_maximize_property().as_property());
143 }
144 }
145}
146
147impl TemplatedControlImpl for Window {
148 fn on_apply_template(this: &Self, e: &TemplateAppliedEventArgs) {
149 Self::parent_on_apply_template(this, e);
150 this.enable_visual_layer_manager_layers();
151 }
152}
153
154impl LayoutableImpl for Window {
155 fn measure_override(this: &Self, available_size: Size) -> Size {
156 let size_to_content = this.size_to_content();
157 let mut client_size = this.client_size();
158 if this.is_forced_decoration_mode.get() {
159 client_size = this.platform_impl().map(|platform_impl| platform_impl.client_size()).unwrap_or(client_size);
160 let inset = this.top_level_host().decoration_inset();
161 client_size = Size::new(
162 (client_size.width - inset.left - inset.right).max(0.0),
163 (client_size.height - inset.top - inset.bottom).max(0.0),
164 );
165 }
166 let mut max_auto_size =
167 this.platform_impl().map(|platform_impl| platform_impl.max_auto_size_hint()).unwrap_or(Size::INFINITY);
168 let use_auto_width = size_to_content.contains(SizeToContent::WIDTH);
169 let use_auto_height = size_to_content.contains(SizeToContent::HEIGHT);
170
171 let mut constraint = Size::new(
172 if use_auto_width || available_size.width.is_infinite() { client_size.width } else { available_size.width },
173 if use_auto_height || available_size.height.is_infinite() {
174 client_size.height
175 } else {
176 available_size.height
177 },
178 );
179
180 if this.max_width() > 0.0 && this.max_width() < max_auto_size.width {
181 max_auto_size = max_auto_size.with_width(this.max_width());
182 }
183 if this.max_height() > 0.0 && this.max_height() < max_auto_size.height {
184 max_auto_size = max_auto_size.with_height(this.max_height());
185 }
186
187 if use_auto_width {
188 constraint = constraint.with_width(max_auto_size.width);
189 }
190
191 if use_auto_height {
192 constraint = constraint.with_height(max_auto_size.height);
193 }
194
195 let mut result = Self::parent_measure_override(this, constraint);
196
197 if !use_auto_width {
198 if !available_size.width.is_infinite() {
199 result = result.with_width(available_size.width);
200 } else {
201 result = result.with_width(client_size.width);
202 }
203 }
204
205 if !use_auto_height {
206 if !available_size.height.is_infinite() {
207 result = result.with_height(available_size.height);
208 } else {
209 result = result.with_height(client_size.height);
210 }
211 }
212
213 result
214 }
215}
216
217impl TopLevelImpl for Window {
218 fn handle_closed(this: &Self) {
219 this.shown.set(false);
220
221 Self::parent_handle_closed(this);
222 *this.window_impl.borrow_mut() = None;
223
224 this.raise_event(&RoutedEventArgs::with_event(Self::window_closed_event()));
225
226 this.set_owner(None);
227 }
228
229 fn handle_resized(this: &Self, mut client_size: Size, reason: WindowResizeReason) {
230 if this.is_forced_decoration_mode.get() {
233 let inset = this.top_level_host().decoration_inset();
234 client_size = Size::new(
235 (client_size.width - inset.left - inset.right).max(0.0),
236 (client_size.height - inset.top - inset.bottom).max(0.0),
237 );
238 }
239
240 if this.can_handle_resized.get()
241 && (this.client_size() != client_size || this.width().is_nan() || this.height().is_nan())
242 {
243 let mut size_to_content = this.size_to_content();
244
245 if size_to_content != SizeToContent::MANUAL
249 && this.can_resize()
250 && reason == WindowResizeReason::Unspecified
251 || reason == WindowResizeReason::User
252 {
253 if client_size.width != this.client_size().width {
254 size_to_content &= !SizeToContent::WIDTH;
255 }
256 if client_size.height != this.client_size().height {
257 size_to_content &= !SizeToContent::HEIGHT;
258 }
259 this.set_size_to_content(size_to_content);
260 }
261
262 this.set_width(client_size.width);
263 this.set_height(client_size.height);
264 }
265
266 Self::parent_handle_resized(this, client_size, reason);
267 }
268}
269
270impl WindowBaseImpl for Window {
271 fn hide(this: &Self) {
273 let _freeze = this.freeze_visibility_change_handling();
274
275 if !this.shown.get() {
276 return;
277 }
278
279 this.stop_rendering();
280
281 let children = this.children.borrow().clone();
282 for (child, _) in children {
283 child.hide();
284 }
285
286 this.set_owner(None);
287 if let Some(platform_impl) = this.platform_impl() {
288 platform_impl.hide();
289 }
290 this.set_is_visible(false);
291
292 let modal_subscription = this.modal_subscription.borrow().clone();
293 if let Some(modal_subscription) = modal_subscription {
294 modal_subscription.dispose();
295 }
296 this.shown.set(false);
297 }
298
299 fn show(this: &Self) {
304 this.show_core(None, false);
305 }
306
307 fn arrange_set_bounds(this: &Self, size: Size) -> Size {
308 this.arrange_bounds.set(size);
309 if this.can_handle_resized.get() {
310 this.resize_platform_impl(size, WindowResizeReason::Layout);
311 }
312 this.client_size()
313 }
314
315 fn is_visible_changed(this: &Self, e: &FerroPropertyChangedEventArgs<'_>) {
316 if !this.ignore_visibility_changes() {
317 let is_visible = e.get_new_value::<bool>();
318
319 if this.shown.get() != is_visible {
320 if !this.shown.get() {
321 this.show();
322 } else {
323 this.hide();
324 }
325 }
326 }
327 }
328}
329
330impl WindowImpl for Window {
331 fn handle_closing(this: &Self, reason: WindowCloseReason) -> bool {
332 if !this.should_cancel_close(&WindowClosingEventArgs::new(reason, false)) {
333 this.close_internal();
334 return false;
335 }
336
337 true
338 }
339
340 fn on_closing(this: &Self, e: &WindowClosingEventArgs) {
341 for (_, handler) in this.closing.snapshot().iter() {
342 handler(e);
343 }
344 }
345}
346
347struct DialogCompletionState {
348 result: Option<Option<BoxedValue>>,
349 waker: Option<Waker>,
350}
351
352#[derive(Clone)]
354struct DialogCompletion(Rc<RefCell<DialogCompletionState>>);
355
356impl DialogCompletion {
357 fn new() -> Self {
358 Self(Rc::new(RefCell::new(DialogCompletionState { result: None, waker: None })))
359 }
360
361 fn set_result(&self, result: Option<BoxedValue>) {
362 let waker = {
363 let mut state = self.0.borrow_mut();
364 state.result = Some(result);
365 state.waker.take()
366 };
367 if let Some(waker) = waker {
368 waker.wake();
369 }
370 }
371}
372
373impl Future for DialogCompletion {
374 type Output = Option<BoxedValue>;
375
376 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
377 let mut state = self.0.borrow_mut();
378 match state.result.take() {
379 Some(result) => Poll::Ready(result),
380 None => {
381 state.waker = Some(cx.waker().clone());
382 Poll::Pending
383 }
384 }
385 }
386}
387
388struct CloseScope<'a> {
391 window: &'a Window,
392 close: Cell<bool>,
393 ignore_cancel: bool,
394}
395
396impl Drop for CloseScope<'_> {
397 fn drop(&mut self) {
398 if self.close.get() || self.ignore_cancel {
399 self.window.close_internal();
400 }
401 }
402}
403
404ferroui_base::ferro_properties! { impl Window {
405 ferro_property!(
406 pub fn size_to_content_property() -> StyledProperty<SizeToContent> {
408 FerroProperty::register::<Window, _>("SizeToContent", SizeToContent::MANUAL)
409 }
410 );
411
412 ferro_property!(
413 pub fn extend_client_area_to_decorations_hint_property() -> StyledProperty<bool> {
415 FerroProperty::register::<Window, _>("ExtendClientAreaToDecorationsHint", false)
416 }
417 );
418
419 ferro_property!(
420 pub fn extend_client_area_title_bar_height_hint_property() -> StyledProperty<f64> {
422 FerroProperty::register::<Window, _>("ExtendClientAreaTitleBarHeightHint", -1.0)
423 }
424 );
425
426 ferro_property!(
427 pub fn is_extended_into_window_decorations_property() -> DirectProperty<Window, bool> {
429 FerroProperty::register_direct::<Window, _>(
430 "IsExtendedIntoWindowDecorations",
431 |o| o.is_extended_into_window_decorations(),
432 None,
433 false,
434 )
435 }
436 );
437
438 ferro_property!(
439 pub fn window_decoration_margin_property() -> DirectProperty<Window, Thickness> {
441 FerroProperty::register_direct::<Window, _>(
442 "WindowDecorationMargin",
443 |o| o.window_decoration_margin(),
444 None,
445 Thickness::default(),
446 )
447 }
448 );
449
450 ferro_property!(
451 pub fn off_screen_margin_property() -> DirectProperty<Window, Thickness> {
453 FerroProperty::register_direct::<Window, _>(
454 "OffScreenMargin",
455 |o| o.off_screen_margin(),
456 None,
457 Thickness::default(),
458 )
459 }
460 );
461
462 ferro_property!(
463 pub fn window_decorations_property() -> StyledProperty<WindowDecorations> {
465 FerroProperty::register::<Window, _>("WindowDecorations", WindowDecorations::Full)
466 }
467 );
468
469 ferro_property!(
470 pub fn window_decorations_theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
472 FerroProperty::register::<Window, _>("WindowDecorationsTheme", None)
473 }
474 );
475
476 ferro_property!(
477 pub fn show_activated_property() -> StyledProperty<bool> {
479 FerroProperty::register::<Window, _>("ShowActivated", true)
480 }
481 );
482
483 ferro_property!(
484 pub fn show_in_taskbar_property() -> StyledProperty<bool> {
486 FerroProperty::register::<Window, _>("ShowInTaskbar", true)
487 }
488 );
489
490 ferro_property!(
491 pub fn closing_behavior_property() -> StyledProperty<WindowClosingBehavior> {
493 FerroProperty::register::<Window, _>("ClosingBehavior", WindowClosingBehavior::OwnerAndChildWindows)
494 }
495 );
496
497 ferro_property!(
498 pub fn window_state_property() -> DirectProperty<Window, WindowState> {
501 FerroProperty::register_direct::<Window, _>(
502 "WindowState",
503 |o| o.window_state(),
504 Some(|o, v| o.set_window_state(v)),
505 WindowState::Normal,
506 )
507 }
508 );
509
510 ferro_property!(
511 pub fn title_property() -> StyledProperty<Option<String>> {
513 FerroProperty::register::<Window, _>("Title", Some("Window".to_string()))
514 }
515 );
516
517 ferro_property!(
518 pub fn icon_property() -> StyledProperty<Option<Rc<WindowIcon>>> {
520 FerroProperty::register::<Window, _>("Icon", None)
521 }
522 );
523
524 ferro_property!(
525 pub fn window_startup_location_property() -> StyledProperty<WindowStartupLocation> {
527 FerroProperty::register::<Window, _>("WindowStartupLocation", WindowStartupLocation::Manual)
528 }
529 );
530
531 ferro_property!(
532 pub fn can_resize_property() -> StyledProperty<bool> {
534 FerroProperty::register::<Window, _>("CanResize", true)
535 }
536 );
537
538 ferro_property!(
539 pub fn can_minimize_property() -> StyledProperty<bool> {
541 FerroProperty::register::<Window, _>("CanMinimize", true)
542 }
543 );
544
545 ferro_property!(
546 pub fn can_maximize_property() -> StyledProperty<bool> {
548 FerroProperty::register_with::<Window, _>(
549 "CanMaximize",
550 StyledPropertyOptions::new(true).coerce(|target, value| {
551 value && !target.downcast_ref::<Window>().is_some_and(|window| !window.can_resize())
552 }),
553 )
554 }
555 );
556} }
557
558impl Window {
559 ferro_routed_event!(
560 pub fn window_closed_event() -> RoutedEvent<RoutedEventArgs> {
563 RoutedEvent::register::<Window, _>("WindowClosed", RoutingStrategies::DIRECT)
564 }
565 );
566
567 ferro_routed_event!(
568 pub fn window_opened_event() -> RoutedEvent<RoutedEventArgs> {
571 RoutedEvent::register::<Window, _>("WindowOpened", RoutingStrategies::DIRECT)
572 }
573 );
574
575 fn static_constructor() {
576 let white: Rc<dyn IBrush> = Brushes::white();
577 TemplatedControl::background_property().override_default_value::<Window>(Some(white));
578 let _ = Self::extend_client_area_title_bar_height_hint_property()
579 .changed()
580 .add_class_handler::<Window>(|w, _| w.on_title_bar_height_hint_changed());
581 }
582
583 pub fn construct(platform_impl: Rc<dyn IWindowImpl>) -> Self {
586 let window_base_impl: Rc<dyn IWindowBaseImpl> = platform_impl.clone();
587 Self {
588 base: WindowBase::construct(window_base_impl),
589 window_impl: RefCell::new(Some(platform_impl)),
590 children: RefCell::new(Vec::new()),
591 is_extended_into_window_decorations: Cell::new(false),
592 window_decoration_margin: Cell::new(Thickness::default()),
593 off_screen_margin: Cell::new(Thickness::default()),
594 can_handle_resized: Cell::new(false),
595 arrange_bounds: Cell::new(Size::default()),
596 is_forced_decoration_mode: Cell::new(false),
597 dialog_result: RefCell::new(None),
598 shown: Cell::new(false),
599 showing_as_dialog: Cell::new(false),
600 position_was_set: Cell::new(false),
601 was_shown_before: Cell::new(false),
602 modal_subscription: RefCell::new(None),
603 allowed_window_actions: Cell::new(PlatformAllowedWindowActions::ALL),
604 last_window_state: Cell::new(WindowState::Normal),
605 closing: HandlerList::new(),
606 allowed_window_actions_changed: HandlerList::new(),
607 }
608 }
609
610 pub fn new() -> Ref<Self> {
615 Self::with_impl(PlatformManager::create_window())
616 }
617
618 pub fn with_impl(platform_impl: Rc<dyn IWindowImpl>) -> Ref<Self> {
620 instantiate(Self::construct(platform_impl))
621 }
622
623 fn initialize(&self) {
625 let platform_impl = self.platform_impl().expect("the window has a platform implementation");
626 let weak = self.to_ref().downgrade();
627
628 platform_impl.set_closing(Some(Rc::new({
629 let weak = weak.clone();
630 move |reason| weak.upgrade().is_some_and(|this| this.handle_closing(reason))
631 })));
632 platform_impl.set_got_input_when_disabled(Some(Rc::new({
633 let weak = weak.clone();
634 move || {
635 if let Some(this) = weak.upgrade() {
636 this.on_got_input_when_disabled();
637 }
638 }
639 })));
640 platform_impl.set_window_state_changed(Some(Rc::new({
641 let weak = weak.clone();
642 move |state| {
643 if let Some(this) = weak.upgrade() {
644 this.handle_window_state_changed(state);
645 }
646 }
647 })));
648 platform_impl.set_drawn_decorations_request_changed(Some(Rc::new({
649 let weak = weak.clone();
650 move || {
651 if let Some(this) = weak.upgrade() {
652 this.update_drawn_decorations();
653 }
654 }
655 })));
656 platform_impl.set_extend_client_area_to_decorations_changed(Some(Rc::new({
657 let weak = weak.clone();
658 move |is_extended| {
659 if let Some(this) = weak.upgrade() {
660 this.extend_client_area_to_decorations_changed(is_extended);
661 }
662 }
663 })));
664 platform_impl.set_allowed_window_actions_changed(Some(Rc::new({
665 let weak = weak.clone();
666 move |actions| {
667 if let Some(this) = weak.upgrade() {
668 this.on_allowed_window_actions_changed(actions);
669 }
670 }
671 })));
672 self.allowed_window_actions.set(platform_impl.allowed_window_actions());
673
674 let skip = Cell::new(true);
676 let object: &FerroObject = self;
677 let _ = FerroObjectExtensions::get_observable(object, TopLevel::client_size_property()).subscribe_fn({
678 let weak = weak.clone();
679 move |x: Size| {
680 if skip.replace(false) {
681 return;
682 }
683 if let Some(this) = weak.upgrade() {
684 this.resize_platform_impl(x, WindowResizeReason::Application);
685 }
686 }
687 });
688 let _ = self.scaling_changed({
689 let weak = weak.clone();
690 move || {
691 if let Some(this) = weak.upgrade() {
692 this.on_scaling_changed_update_decorations();
693 }
694 }
695 });
696
697 self.bind_impl(Self::title_property(), |_, platform_impl, title| platform_impl.set_title(title.as_deref()));
698 self.bind_impl(Self::icon_property(), |this, _, icon| this.set_effective_icon(icon));
699 self.bind_impl(Self::can_resize_property(), |_, platform_impl, can_resize| platform_impl.can_resize(can_resize));
700 self.bind_impl(Self::can_minimize_property(), |_, platform_impl, can_minimize| {
701 platform_impl.set_can_minimize(can_minimize)
702 });
703 self.bind_impl(Self::can_maximize_property(), |_, platform_impl, can_maximize| {
704 platform_impl.set_can_maximize(can_maximize)
705 });
706 self.bind_impl(Self::show_in_taskbar_property(), |_, platform_impl, show| platform_impl.show_taskbar_icon(show));
707
708 self.bind_impl(Self::extend_client_area_to_decorations_hint_property(), |_, platform_impl, hint| {
709 platform_impl.set_extend_client_area_to_decorations_hint(hint)
710 });
711 self.bind_impl(Self::extend_client_area_title_bar_height_hint_property(), |_, platform_impl, height| {
712 platform_impl.set_extend_client_area_title_bar_height_hint(height)
713 });
714
715 fn update_min_max_size(this: &Window, platform_impl: &Rc<dyn IWindowImpl>, _: f64) {
716 platform_impl.set_min_max_size(
717 Size::new(this.min_width(), this.min_height()),
718 Size::new(this.max_width(), this.max_height()),
719 );
720 }
721 self.bind_impl(Layoutable::min_width_property(), update_min_max_size);
722 self.bind_impl(Layoutable::max_width_property(), update_min_max_size);
723 self.bind_impl(Layoutable::min_height_property(), update_min_max_size);
724 self.bind_impl(Layoutable::max_height_property(), update_min_max_size);
725 }
726
727 fn bind_impl<T: PropertyValue>(
730 &self,
731 property: &'static StyledProperty<T>,
732 on_value: impl Fn(&Window, &Rc<dyn IWindowImpl>, T) + 'static,
733 ) {
734 let weak = self.to_ref().downgrade();
735 self.create_platform_impl_binding(property, move |value| {
736 if let Some(this) = weak.upgrade() {
737 if let Some(platform_impl) = this.platform_impl() {
738 on_value(&this, &platform_impl, value);
739 }
740 }
741 });
742 }
743
744 pub fn platform_impl(&self) -> Option<Rc<dyn IWindowImpl>> {
747 WindowBase::platform_impl(self)?;
748 self.window_impl.borrow().clone()
749 }
750
751 pub fn owned_windows(&self) -> Vec<Ref<Window>> {
753 self.children.borrow().iter().map(|(child, _)| child.clone()).collect()
754 }
755
756 pub fn size_to_content(&self) -> SizeToContent {
771 self.get_value(Self::size_to_content_property())
772 }
773
774 pub fn set_size_to_content(&self, value: SizeToContent) {
775 self.set_value(Self::size_to_content_property(), value)
776 }
777
778 pub fn title(&self) -> Option<String> {
780 self.get_value(Self::title_property())
781 }
782
783 pub fn set_title(&self, value: Option<String>) {
784 self.set_value(Self::title_property(), value)
785 }
786
787 pub fn extend_client_area_to_decorations_hint(&self) -> bool {
790 self.get_value(Self::extend_client_area_to_decorations_hint_property())
791 }
792
793 pub fn set_extend_client_area_to_decorations_hint(&self, value: bool) {
794 self.set_value(Self::extend_client_area_to_decorations_hint_property(), value)
795 }
796
797 pub fn extend_client_area_title_bar_height_hint(&self) -> f64 {
802 self.get_value(Self::extend_client_area_title_bar_height_hint_property())
803 }
804
805 pub fn set_extend_client_area_title_bar_height_hint(&self, value: f64) {
806 self.set_value(Self::extend_client_area_title_bar_height_hint_property(), value)
807 }
808
809 pub fn is_extended_into_window_decorations(&self) -> bool {
811 self.is_extended_into_window_decorations.get()
812 }
813
814 fn set_is_extended_into_window_decorations(&self, value: bool) {
815 self.set_and_raise_cell(
816 Self::is_extended_into_window_decorations_property(),
817 &self.is_extended_into_window_decorations,
818 value,
819 );
820 }
821
822 pub fn window_decoration_margin(&self) -> Thickness {
825 self.window_decoration_margin.get()
826 }
827
828 fn set_window_decoration_margin(&self, value: Thickness) {
829 self.set_and_raise_cell(Self::window_decoration_margin_property(), &self.window_decoration_margin, value);
830 }
831
832 pub fn caption_button_insets(&self) -> Thickness {
838 if !self.is_extended_into_window_decorations() || self.window_state() == WindowState::FullScreen {
840 return Thickness::default();
841 }
842 if let Some(insets) = self.platform_impl().and_then(|platform_impl| platform_impl.caption_button_insets()) {
843 return insets;
844 }
845 if let Some(decorations) = self.top_level_host().decorations() {
846 if decorations.enabled_parts().contains(crate::chrome::DrawnWindowDecorationParts::TITLE_BAR) {
847 return Thickness::new(0.0, 0.0, decorations.caption_buttons_width(), 0.0);
848 }
849 }
850 Thickness::default()
851 }
852
853 pub fn off_screen_margin(&self) -> Thickness {
858 self.off_screen_margin.get()
859 }
860
861 fn set_off_screen_margin(&self, value: Thickness) {
862 self.set_and_raise_cell(Self::off_screen_margin_property(), &self.off_screen_margin, value);
863 }
864
865 pub fn window_decorations(&self) -> WindowDecorations {
867 self.get_value(Self::window_decorations_property())
868 }
869
870 pub fn set_window_decorations(&self, value: WindowDecorations) {
871 self.set_value(Self::window_decorations_property(), value)
872 }
873
874 pub fn window_decorations_theme(&self) -> Option<Ref<ControlTheme>> {
877 self.get_value(Self::window_decorations_theme_property())
878 }
879
880 pub fn set_window_decorations_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
881 self.set_value(Self::window_decorations_theme_property(), value.into().0)
882 }
883
884 #[deprecated(note = "Use window_decorations instead.")]
885 pub fn system_decorations(&self) -> WindowDecorations {
886 self.window_decorations()
887 }
888
889 #[deprecated(note = "Use set_window_decorations instead.")]
890 pub fn set_system_decorations(&self, value: WindowDecorations) {
891 self.set_window_decorations(value)
892 }
893
894 pub fn show_activated(&self) -> bool {
896 self.get_value(Self::show_activated_property())
897 }
898
899 pub fn set_show_activated(&self, value: bool) {
900 self.set_value(Self::show_activated_property(), value)
901 }
902
903 pub fn show_in_taskbar(&self) -> bool {
905 self.get_value(Self::show_in_taskbar_property())
906 }
907
908 pub fn set_show_in_taskbar(&self, value: bool) {
909 self.set_value(Self::show_in_taskbar_property(), value)
910 }
911
912 pub fn closing_behavior(&self) -> WindowClosingBehavior {
915 self.get_value(Self::closing_behavior_property())
916 }
917
918 pub fn set_closing_behavior(&self, value: WindowClosingBehavior) {
919 self.set_value(Self::closing_behavior_property(), value)
920 }
921
922 pub fn window_state(&self) -> WindowState {
925 match self.platform_impl() {
926 Some(platform_impl) if platform_impl.window_state_getter_is_usable() => platform_impl.window_state(),
927 _ => self.last_window_state.get(),
928 }
929 }
930
931 pub fn set_window_state(&self, value: WindowState) {
932 if let Some(platform_impl) = self.platform_impl() {
933 if platform_impl.window_state_getter_is_usable() {
934 platform_impl.set_window_state(value);
937
938 if platform_impl.window_state() != value {
941 let old_value = self.last_window_state.get();
944 self.last_window_state.set(platform_impl.window_state());
945 self.raise_direct_property_changed(
946 Self::window_state_property(),
947 &old_value,
948 &self.last_window_state.get(),
949 );
950 }
951 } else {
952 self.set_and_raise_cell(Self::window_state_property(), &self.last_window_state, value);
955 platform_impl.set_window_state(self.last_window_state.get());
956 }
957 }
958 }
959
960 pub fn can_resize(&self) -> bool {
962 self.get_value(Self::can_resize_property())
963 }
964
965 pub fn set_can_resize(&self, value: bool) {
966 self.set_value(Self::can_resize_property(), value)
967 }
968
969 pub fn can_minimize(&self) -> bool {
973 self.get_value(Self::can_minimize_property())
974 }
975
976 pub fn set_can_minimize(&self, value: bool) {
977 self.set_value(Self::can_minimize_property(), value)
978 }
979
980 pub fn can_maximize(&self) -> bool {
986 self.get_value(Self::can_maximize_property())
987 }
988
989 pub fn set_can_maximize(&self, value: bool) {
990 self.set_value(Self::can_maximize_property(), value)
991 }
992
993 pub fn allowed_window_actions(&self) -> PlatformAllowedWindowActions {
996 self.allowed_window_actions.get()
997 }
998
999 pub fn icon(&self) -> Option<Rc<WindowIcon>> {
1001 self.get_value(Self::icon_property())
1002 }
1003
1004 pub fn set_icon(&self, value: Option<Rc<WindowIcon>>) {
1005 self.set_value(Self::icon_property(), value)
1006 }
1007
1008 pub fn window_startup_location(&self) -> WindowStartupLocation {
1010 self.get_value(Self::window_startup_location_property())
1011 }
1012
1013 pub fn set_window_startup_location(&self, value: WindowStartupLocation) {
1014 self.set_value(Self::window_startup_location_property(), value)
1015 }
1016
1017 pub fn position(&self) -> PixelPoint {
1019 self.platform_impl().map(|platform_impl| platform_impl.position()).unwrap_or_default()
1020 }
1021
1022 pub fn set_position(&self, value: PixelPoint) {
1023 if let Some(platform_impl) = self.platform_impl() {
1024 platform_impl.move_(value);
1025 }
1026 self.position_was_set.set(true);
1027 }
1028
1029 pub fn is_dialog(&self) -> bool {
1031 self.showing_as_dialog.get()
1032 }
1033
1034 pub fn begin_move_drag(&self, e: &PointerPressedEventArgs) {
1037 if let Some(platform_impl) = self.platform_impl() {
1038 platform_impl.begin_move_drag(e);
1039 }
1040 }
1041
1042 pub fn begin_resize_drag(&self, edge: WindowEdge, e: &PointerPressedEventArgs) {
1046 if let Some(platform_impl) = self.platform_impl() {
1047 platform_impl.begin_resize_drag(edge, e);
1048 }
1049 }
1050
1051 pub fn closing(&self, handler: impl Fn(&WindowClosingEventArgs) + 'static) -> Rc<dyn IDisposable> {
1054 let token = self.closing.add(Rc::new(handler));
1055 let weak = self.to_ref().downgrade();
1056 Disposable::create(move || {
1057 if let Some(this) = weak.upgrade() {
1058 this.closing.remove(token);
1059 }
1060 })
1061 }
1062
1063 pub fn close(&self) {
1065 self.close_core(WindowCloseReason::WindowClosing, true, false);
1066 }
1067
1068 pub fn close_with_result(&self, dialog_result: Option<BoxedValue>) {
1074 *self.dialog_result.borrow_mut() = dialog_result;
1075 self.close_core(WindowCloseReason::WindowClosing, true, false);
1076 }
1077
1078 pub fn close_core(&self, reason: WindowCloseReason, is_programmatic: bool, ignore_cancel: bool) {
1082 let scope = CloseScope { window: self, close: Cell::new(true), ignore_cancel };
1083
1084 if self.should_cancel_close(&WindowClosingEventArgs::new(reason, is_programmatic)) {
1085 scope.close.set(false);
1086 }
1087 }
1088
1089 fn close_internal(&self) {
1090 let children = self.children.borrow().clone();
1091 for (child, _) in children {
1092 child.close_internal();
1093 }
1094
1095 if let Some(platform_impl) = self.platform_impl() {
1096 platform_impl.dispose();
1097 }
1098
1099 self.showing_as_dialog.set(false);
1100
1101 self.set_owner(None);
1102 }
1103
1104 fn should_cancel_close(&self, args: &WindowClosingEventArgs) -> bool {
1105 match self.closing_behavior() {
1106 WindowClosingBehavior::OwnerAndChildWindows => {
1107 let mut can_close = true;
1108
1109 let children = self.children.borrow().clone();
1110 if !children.is_empty() {
1111 let owner_closing_args;
1112 let child_args = if args.close_reason() == WindowCloseReason::WindowClosing {
1113 owner_closing_args =
1114 WindowClosingEventArgs::new(WindowCloseReason::OwnerWindowClosing, args.is_programmatic());
1115 &owner_closing_args
1116 } else {
1117 args
1118 };
1119
1120 for (child, _) in children {
1121 if child.should_cancel_close(child_args) {
1122 can_close = false;
1123 }
1124 }
1125 }
1126
1127 if can_close {
1128 self.on_closing(args);
1129
1130 return args.cancel();
1131 }
1132
1133 true
1134 }
1135 WindowClosingBehavior::OwnerWindowOnly => {
1136 self.on_closing(args);
1137
1138 args.cancel()
1139 }
1140 }
1141 }
1142
1143 fn handle_window_state_changed(&self, state: WindowState) {
1144 debug_assert!(self
1146 .platform_impl()
1147 .is_none_or(|platform_impl| !platform_impl.window_state_getter_is_usable()
1148 || platform_impl.window_state() == state));
1149
1150 self.set_and_raise_cell(Self::window_state_property(), &self.last_window_state, state);
1151
1152 if state == WindowState::Minimized {
1153 self.stop_rendering();
1154 } else {
1155 self.start_rendering();
1156 }
1157
1158 self.update_drawn_decoration_parts();
1160 }
1161
1162 pub fn allowed_window_actions_changed(
1166 &self,
1167 handler: impl Fn(PlatformAllowedWindowActions) + 'static,
1168 ) -> Rc<dyn IDisposable> {
1169 let token = self.allowed_window_actions_changed.add(Rc::new(handler));
1170 let weak = self.to_ref().downgrade();
1171 Disposable::create(move || {
1172 if let Some(this) = weak.upgrade() {
1173 this.allowed_window_actions_changed.remove(token);
1174 }
1175 })
1176 }
1177
1178 fn on_allowed_window_actions_changed(&self, actions: PlatformAllowedWindowActions) {
1179 self.allowed_window_actions.set(actions);
1180 for (_, handler) in self.allowed_window_actions_changed.snapshot().iter() {
1181 handler(actions);
1182 }
1183 }
1184
1185 fn extend_client_area_to_decorations_changed(&self, is_extended: bool) {
1186 self.set_is_extended_into_window_decorations(is_extended);
1187 self.set_off_screen_margin(
1188 self.platform_impl().map(|platform_impl| platform_impl.off_screen_margin()).unwrap_or_default(),
1189 );
1190
1191 self.update_drawn_decorations();
1192 }
1193
1194 fn update_drawn_decorations(&self) {
1195 let parts = self.compute_decoration_parts();
1196
1197 self.is_forced_decoration_mode.set(parts.is_some() && !self.is_extended_into_window_decorations());
1199
1200 self.top_level_host().update_drawn_decorations(parts, self.window_state(), self.window_decorations_theme());
1201
1202 if parts.is_some() {
1203 if let Some(decorations) = self.top_level_host().decorations() {
1205 decorations.set_render_scaling(self.render_scaling());
1206
1207 let hint = self.extend_client_area_title_bar_height_hint();
1208 if hint >= 0.0 {
1209 decorations.set_title_bar_height_override(hint);
1210 }
1211 }
1212 }
1213
1214 self.update_drawn_decoration_margins();
1215 }
1216
1217 fn on_scaling_changed_update_decorations(&self) {
1218 if let Some(decorations) = self.top_level_host().decorations() {
1219 decorations.set_render_scaling(self.render_scaling());
1220 }
1221 }
1222
1223 fn update_drawn_decoration_parts(&self) {
1226 if self.top_level_host().decorations().is_none() {
1227 return;
1228 }
1229
1230 self.top_level_host().update_drawn_decorations(
1231 self.compute_decoration_parts(),
1232 self.window_state(),
1233 self.window_decorations_theme(),
1234 );
1235 }
1236
1237 fn compute_decoration_parts(&self) -> Option<crate::chrome::DrawnWindowDecorationParts> {
1238 use crate::chrome::DrawnWindowDecorationParts;
1239 use crate::platform::PlatformRequestedDrawnDecoration;
1240
1241 let platform_impl = self.platform_impl();
1242 if !platform_impl.as_ref().is_some_and(|platform_impl| platform_impl.needs_managed_decorations()) {
1243 return None;
1244 }
1245
1246 let platform_needs = platform_impl
1247 .map(|platform_impl| platform_impl.requested_drawn_decorations())
1248 .unwrap_or(PlatformRequestedDrawnDecoration::NONE);
1249 let mut parts = DrawnWindowDecorationParts::NONE;
1250 if self.window_decorations() != WindowDecorations::None {
1251 if platform_needs.contains(PlatformRequestedDrawnDecoration::TITLE_BAR)
1252 && self.window_decorations() == WindowDecorations::Full
1253 {
1254 parts |= DrawnWindowDecorationParts::TITLE_BAR;
1255 }
1256 if platform_needs.contains(PlatformRequestedDrawnDecoration::SHADOW) {
1257 parts |= DrawnWindowDecorationParts::SHADOW;
1258 }
1259 if platform_needs.contains(PlatformRequestedDrawnDecoration::BORDER) {
1260 parts |= DrawnWindowDecorationParts::BORDER;
1261 }
1262 if platform_needs.contains(PlatformRequestedDrawnDecoration::RESIZE_GRIPS) && self.can_resize() {
1263 parts |= DrawnWindowDecorationParts::RESIZE_GRIPS;
1264 }
1265
1266 if self.window_state() == WindowState::FullScreen {
1268 parts &= !(DrawnWindowDecorationParts::SHADOW
1269 | DrawnWindowDecorationParts::BORDER
1270 | DrawnWindowDecorationParts::RESIZE_GRIPS
1271 | DrawnWindowDecorationParts::TITLE_BAR);
1272 }
1273 else if self.window_state() == WindowState::Maximized {
1275 parts &= !(DrawnWindowDecorationParts::SHADOW
1276 | DrawnWindowDecorationParts::BORDER
1277 | DrawnWindowDecorationParts::RESIZE_GRIPS);
1278 }
1279 }
1280
1281 Some(parts)
1282 }
1283
1284 fn on_title_bar_height_hint_changed(&self) {
1285 let Some(decorations) = self.top_level_host().decorations() else { return };
1286
1287 decorations.set_title_bar_height_override(self.extend_client_area_title_bar_height_hint());
1288
1289 self.update_drawn_decoration_margins();
1290 }
1291
1292 pub(crate) fn on_drawn_decorations_geometry_changed(&self) {
1296 self.update_drawn_decoration_margins();
1297 }
1298
1299 fn update_drawn_decoration_margins(&self) {
1300 if let Some(decorations) = self.top_level_host().decorations() {
1301 use crate::chrome::DrawnWindowDecorationParts;
1302
1303 let parts = decorations.enabled_parts();
1304 let title_bar_height =
1305 if parts.contains(DrawnWindowDecorationParts::TITLE_BAR) { decorations.title_bar_height() } else { 0.0 };
1306 let frame = if parts.contains(DrawnWindowDecorationParts::BORDER) {
1307 decorations.frame_thickness()
1308 } else {
1309 Thickness::default()
1310 };
1311 let shadow = if parts.contains(DrawnWindowDecorationParts::SHADOW) {
1312 decorations.shadow_thickness()
1313 } else {
1314 Thickness::default()
1315 };
1316
1317 if let Some(platform_impl) = self.platform_impl() {
1318 platform_impl.set_shadow_extents(shadow);
1319 }
1320
1321 let margin = Thickness::new(
1322 frame.left + shadow.left,
1323 title_bar_height + frame.top + shadow.top,
1324 frame.right + shadow.right,
1325 frame.bottom + shadow.bottom,
1326 );
1327
1328 if self.is_forced_decoration_mode.get() {
1329 self.set_window_decoration_margin(Thickness::default());
1332 self.top_level_host().set_decoration_inset(margin);
1333 } else {
1334 self.set_window_decoration_margin(margin);
1336 self.top_level_host().set_decoration_inset(Thickness::default());
1337 }
1338 return;
1339 }
1340
1341 let platform_impl = self.platform_impl();
1343 self.set_window_decoration_margin(
1344 platform_impl.as_ref().map(|platform_impl| platform_impl.extended_margins()).unwrap_or_default(),
1345 );
1346 self.top_level_host().set_decoration_inset(Thickness::default());
1347 if let Some(platform_impl) = platform_impl {
1348 platform_impl.set_shadow_extents(Thickness::default());
1349 }
1350 }
1351
1352 pub fn show_with_owner(&self, owner: &Window) {
1358 self.show_core(Some(owner), false);
1359 }
1360
1361 fn ensure_state_before_show(&self) {
1362 if self.platform_impl().is_none() {
1363 panic!("Cannot re-show a closed window.");
1364 }
1365 }
1366
1367 fn ensure_parent_state_before_show(&self, owner: &Window) {
1368 if owner.platform_impl().is_none() {
1369 panic!("Cannot show a window with a closed owner.");
1370 }
1371
1372 if std::ptr::eq(owner, self) {
1373 panic!("A Window cannot be its own owner.");
1374 }
1375
1376 if !owner.is_visible() {
1377 panic!("Cannot show window with non-visible owner.");
1378 }
1379 }
1380
1381 fn show_core(&self, owner: Option<&Window>, modal: bool) -> Option<DialogCompletion> {
1382 let _freeze = self.freeze_visibility_change_handling();
1383
1384 self.ensure_state_before_show();
1385
1386 if modal && owner.is_none() {
1387 panic!("A dialog requires an owner.");
1388 }
1389 if let Some(owner) = owner {
1390 self.ensure_parent_state_before_show(owner);
1391 }
1392
1393 if self.shown.get() {
1394 if modal {
1395 panic!("The window is already being shown.");
1396 }
1397 return None;
1398 }
1399
1400 self.showing_as_dialog.set(modal);
1401 self.raise_event(&RoutedEventArgs::with_event(Self::window_opened_event()));
1402
1403 self.ensure_initialized();
1404 self.apply_styling();
1405
1406 self.update_drawn_decorations();
1408
1409 if self.is_forced_decoration_mode.get() {
1411 let inset = self.top_level_host().decoration_inset();
1412 self.set_client_size(Size::new(
1413 (self.client_size().width - inset.left - inset.right).max(0.0),
1414 (self.client_size().height - inset.top - inset.bottom).max(0.0),
1415 ));
1416 }
1417
1418 self.shown.set(true);
1419 self.set_is_visible(true);
1420
1421 self.set_effective_icon(self.icon());
1422
1423 self.set_expected_scaling(owner);
1426
1427 let mut initial_size = Size::new(
1428 if self.width().is_nan() { self.client_size().width } else { self.width() },
1429 if self.height().is_nan() { self.client_size().height } else { self.height() },
1430 );
1431
1432 let min_max = MinMax::new(self);
1433
1434 initial_size = Size::new(
1435 MathUtilities::clamp(initial_size.width, min_max.min_width, min_max.max_width),
1436 MathUtilities::clamp(initial_size.height, min_max.min_height, min_max.max_height),
1437 );
1438
1439 let mut client_size_changed = initial_size != self.client_size();
1440 self.set_client_size(initial_size); self.layout_manager().execute_initial_layout_pass();
1444
1445 if self.size_to_content().contains(SizeToContent::WIDTH) {
1446 initial_size = initial_size.with_width(MathUtilities::clamp(
1447 self.arrange_bounds.get().width,
1448 min_max.min_width,
1449 min_max.max_width,
1450 ));
1451 client_size_changed |= initial_size != self.client_size();
1452 self.set_client_size(initial_size);
1453 }
1454
1455 if self.size_to_content().contains(SizeToContent::HEIGHT) {
1456 initial_size = initial_size.with_height(MathUtilities::clamp(
1457 self.arrange_bounds.get().height,
1458 min_max.min_height,
1459 min_max.max_height,
1460 ));
1461 client_size_changed |= initial_size != self.client_size();
1462 self.set_client_size(initial_size);
1463 }
1464
1465 self.set_owner(owner.map(|owner| owner.to_ref().upcast()));
1466
1467 self.apply_window_startup_location(owner);
1468
1469 self.set_desktop_scaling_override(None);
1470
1471 let platform_client_size =
1473 self.platform_impl().map(|platform_impl| platform_impl.client_size()).unwrap_or_default();
1474 let comparable_client_size = if self.is_forced_decoration_mode.get() {
1475 let inset = self.top_level_host().decoration_inset();
1476 Size::new(
1477 (platform_client_size.width - inset.left - inset.right).max(0.0),
1478 (platform_client_size.height - inset.top - inset.bottom).max(0.0),
1479 )
1480 } else {
1481 platform_client_size
1482 };
1483
1484 if client_size_changed || self.client_size() != comparable_client_size {
1485 self.resize_platform_impl(self.client_size(), WindowResizeReason::Layout);
1487
1488 self.renderer().resized(self.client_size());
1492 self.on_resized(&WindowResizedEventArgs::new(self.client_size(), WindowResizeReason::Layout));
1493
1494 if !self.width().is_nan() {
1495 self.set_width(self.client_size().width);
1496 }
1497 if !self.height().is_nan() {
1498 self.set_height(self.client_size().height);
1499 }
1500 }
1501
1502 self.set_frame_size(self.platform_impl().and_then(|platform_impl| platform_impl.frame_size()));
1503
1504 self.can_handle_resized.set(true);
1505
1506 self.start_rendering();
1507 if let Some(platform_impl) = self.platform_impl() {
1508 platform_impl.show(self.show_activated(), modal);
1509 }
1510
1511 let mut result = None;
1512 if let (true, Some(owner)) = (modal, owner) {
1513 let completion = DialogCompletion::new();
1514 let weak = self.to_ref().downgrade();
1515
1516 let taken = Cell::new(false);
1517 let closed_subscription = TopLevel::closed(self, {
1518 let weak = weak.clone();
1519 move || {
1520 if taken.replace(true) {
1521 return;
1522 }
1523 let modal_subscription =
1524 weak.upgrade().and_then(|this| this.modal_subscription.borrow().clone());
1525 if let Some(modal_subscription) = modal_subscription {
1526 modal_subscription.dispose();
1527 }
1528 }
1529 });
1530 let complete = Disposable::create({
1531 let owner = owner.to_ref().downgrade();
1532 let completion = completion.clone();
1533 move || {
1534 let this = weak.upgrade();
1535 if let Some(this) = &this {
1536 *this.modal_subscription.borrow_mut() = None;
1537 }
1538 if let Some(owner) = owner.upgrade() {
1539 owner.activate();
1540 }
1541 completion.set_result(this.and_then(|this| this.dialog_result.borrow().clone()));
1542 }
1543 });
1544
1545 let disposables: Rc<dyn IDisposable> =
1546 Rc::new(CompositeDisposable::from_disposables([closed_subscription, complete]));
1547 *self.modal_subscription.borrow_mut() = Some(disposables);
1548 result = Some(completion);
1549 }
1550
1551 self.on_opened();
1552 if !modal {
1553 self.was_shown_before.set(true);
1554 }
1555
1556 result
1557 }
1558
1559 fn resize_platform_impl(&self, mut size: Size, reason: WindowResizeReason) {
1560 let Some(platform_impl) = self.platform_impl() else { return };
1561
1562 if self.is_forced_decoration_mode.get() {
1564 let inset = self.top_level_host().decoration_inset();
1565 size = Size::new(size.width + inset.left + inset.right, size.height + inset.top + inset.bottom);
1566 if platform_impl.client_size() != size {
1567 platform_impl.resize(size, reason);
1568 }
1569 } else {
1570 platform_impl.resize(size, reason);
1571 }
1572 }
1573
1574 pub fn show_dialog(&self, owner: &Window) -> DispatcherTask<Option<BoxedValue>> {
1586 let completion = self.show_core(Some(owner), true).expect("showing a dialog creates its completion");
1587 Dispatcher::ui_thread().invoke_async_task_local(move || completion)
1588 }
1589
1590 pub fn show_dialog_typed<TResult: Clone + Default + 'static>(&self, owner: &Window) -> DispatcherTask<TResult> {
1602 let completion = self.show_core(Some(owner), true).expect("showing a dialog creates its completion");
1603 Dispatcher::ui_thread().invoke_async_task_local(move || async move {
1604 match completion.await {
1605 Some(dialog_result) => match dialog_result.downcast_ref::<TResult>() {
1606 Some(dialog_result) => dialog_result.clone(),
1607 None => panic!(
1608 "Unable to cast the dialog result of type {} to {}.",
1609 dialog_result.any_value_type_name(),
1610 std::any::type_name::<TResult>()
1611 ),
1612 },
1613 None => TResult::default(),
1614 }
1615 })
1616 }
1617
1618 pub fn sort_windows_by_z_order(windows: &mut [Ref<Window>]) {
1625 if windows.len() <= 1 {
1626 return;
1627 }
1628
1629 let platform = FerroLocator::current().get_required_service::<dyn IWindowingPlatform>();
1630
1631 let window_impls: Vec<Rc<dyn IWindowImpl>> = windows
1632 .iter()
1633 .enumerate()
1634 .map(|(i, window)| window.platform_impl().unwrap_or_else(|| panic!("Invalid window at index {i}")))
1635 .collect();
1636
1637 let mut z_order = vec![0i64; windows.len()];
1638 platform.get_windows_z_order(&window_impls, &mut z_order);
1639
1640 let mut keyed: Vec<(i64, Ref<Window>)> = z_order.into_iter().zip(windows.iter().cloned()).collect();
1641 keyed.sort_by_key(|(z, _)| *z);
1642 for (slot, (_, window)) in windows.iter_mut().zip(keyed) {
1643 *slot = window;
1644 }
1645 }
1646
1647 fn update_enabled(&self) {
1648 let is_enabled = !self.children.borrow().iter().any(|(_, is_dialog)| *is_dialog);
1649
1650 if let Some(platform_impl) = self.platform_impl() {
1651 platform_impl.set_enabled(is_enabled);
1652 }
1653 }
1654
1655 fn add_child(&self, window: &Window, is_dialog: bool) {
1656 self.children.borrow_mut().push((window.to_ref(), is_dialog));
1657 self.update_enabled();
1658 }
1659
1660 fn remove_child(&self, window: &Window) {
1661 self.children.borrow_mut().retain(|(child, _)| !std::ptr::eq(&**child, window));
1662
1663 self.update_enabled();
1664 }
1665
1666 fn on_got_input_when_disabled(&self) {
1667 let first_dialog_child =
1668 self.children.borrow().iter().find(|(_, is_dialog)| *is_dialog).map(|(child, _)| child.clone());
1669
1670 if let Some(first_dialog_child) = first_dialog_child {
1671 first_dialog_child.on_got_input_when_disabled();
1672 } else {
1673 self.activate();
1674 }
1675 }
1676
1677 fn set_expected_scaling(&self, owner: Option<&Window>) {
1678 if self.was_shown_before.get() {
1679 return;
1680 }
1681
1682 let location = self.get_effective_window_startup_location(owner);
1683
1684 match location {
1685 WindowStartupLocation::CenterOwner => {
1686 self.set_desktop_scaling_override(owner.map(|owner| owner.desktop_scaling()));
1687 }
1688 WindowStartupLocation::CenterScreen => {
1689 let scaling = owner
1690 .map(|owner| owner.desktop_scaling())
1691 .or_else(|| self.screens().screen_from_point(self.position()).map(|screen| screen.scaling()))
1692 .or_else(|| self.screens().primary().map(|screen| screen.scaling()));
1693 self.set_desktop_scaling_override(scaling);
1694 }
1695 WindowStartupLocation::Manual => {
1696 self.set_desktop_scaling_override(
1697 self.screens().screen_from_point(self.position()).map(|screen| screen.scaling()),
1698 );
1699 }
1700 }
1701 }
1702
1703 fn get_effective_window_startup_location(&self, owner: Option<&Window>) -> WindowStartupLocation {
1704 let mut startup_location = self.window_startup_location();
1705
1706 if startup_location == WindowStartupLocation::CenterOwner
1707 && owner.is_none_or(|owner| owner.window_state() == WindowState::Minimized)
1708 {
1709 startup_location = WindowStartupLocation::CenterScreen;
1712 }
1713
1714 startup_location
1715 }
1716
1717 fn apply_window_startup_location(&self, owner: Option<&Window>) {
1718 if self.was_shown_before.get() {
1719 return;
1720 }
1721
1722 let startup_location = self.get_effective_window_startup_location(owner);
1723
1724 let platform_frame = self
1726 .platform_impl()
1727 .and_then(|platform_impl| platform_impl.frame_size().map(|frame_size| (frame_size, platform_impl.client_size())));
1728 let rect = match platform_frame {
1729 Some((frame_size, platform_client_size)) => {
1730 let diff = frame_size - platform_client_size;
1732 PixelRect::from_size(PixelSize::from_size(self.client_size() + diff, self.desktop_scaling()))
1733 }
1734 None => PixelRect::from_size(PixelSize::from_size(self.client_size(), self.desktop_scaling())),
1735 };
1736
1737 let apply_screen_constraint = |screen: Option<&Rc<Screen>>, mut child_rect: PixelRect| -> PixelRect {
1738 if let Some(constraint) = screen.map(|screen| screen.working_area()) {
1739 let max_x = constraint.right() - rect.width;
1740 let max_y = constraint.bottom() - rect.height;
1741
1742 if constraint.x <= max_x {
1743 child_rect = child_rect.with_x(MathUtilities::clamp_i32(child_rect.x, constraint.x, max_x));
1744 }
1745 if constraint.y <= max_y {
1746 child_rect = child_rect.with_y(MathUtilities::clamp_i32(child_rect.y, constraint.y, max_y));
1747 }
1748 }
1749
1750 child_rect
1751 };
1752
1753 if startup_location == WindowStartupLocation::CenterScreen {
1754 let mut screen: Option<Rc<Screen>> = None;
1755
1756 if let Some(owner) = owner {
1757 screen = self
1758 .screens()
1759 .screen_from_window(owner)
1760 .or_else(|| self.screens().screen_from_point(owner.position()));
1761 }
1762
1763 let screen = screen
1764 .or_else(|| self.screens().screen_from_point(self.position()))
1765 .or_else(|| self.screens().primary());
1766
1767 if let Some(screen) = screen {
1768 let mut child_rect = screen.working_area().center_rect(rect);
1769
1770 if self.screens().screen_from_point(child_rect.position()).is_none() {
1771 child_rect = apply_screen_constraint(Some(&screen), child_rect);
1772 }
1773
1774 self.set_position(child_rect.position());
1775 }
1776 } else if startup_location == WindowStartupLocation::CenterOwner {
1777 let owner = owner.expect("the effective startup location is the owner's center only with an owner");
1778 let owner_size = owner.frame_size().unwrap_or_else(|| owner.client_size());
1779 let owner_rect = PixelRect::from_position_size(
1780 owner.position(),
1781 PixelSize::from_size(owner_size, owner.desktop_scaling()),
1782 );
1783 let mut child_rect = owner_rect.center_rect(rect);
1784
1785 let screen = self.screens().screen_from_window(owner);
1786
1787 child_rect = apply_screen_constraint(screen.as_ref(), child_rect);
1788
1789 self.set_position(child_rect.position());
1790 }
1791
1792 if !self.position_was_set.get() {
1794 if let Some(platform_impl) = self.platform_impl() {
1795 if self.desktop_scaling() != platform_impl.desktop_scaling() {
1796 platform_impl.move_(self.position());
1797 }
1798 }
1799 }
1800 }
1801
1802 fn set_effective_icon(&self, icon: Option<Rc<WindowIcon>>) {
1803 if let Some(platform_impl) = self.platform_impl() {
1804 platform_impl.set_icon(icon.map(|icon| icon.platform_impl()));
1805 }
1806 }
1807}