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 off_screen_margin(&self) -> Thickness {
837 self.off_screen_margin.get()
838 }
839
840 fn set_off_screen_margin(&self, value: Thickness) {
841 self.set_and_raise_cell(Self::off_screen_margin_property(), &self.off_screen_margin, value);
842 }
843
844 pub fn window_decorations(&self) -> WindowDecorations {
846 self.get_value(Self::window_decorations_property())
847 }
848
849 pub fn set_window_decorations(&self, value: WindowDecorations) {
850 self.set_value(Self::window_decorations_property(), value)
851 }
852
853 pub fn window_decorations_theme(&self) -> Option<Ref<ControlTheme>> {
856 self.get_value(Self::window_decorations_theme_property())
857 }
858
859 pub fn set_window_decorations_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
860 self.set_value(Self::window_decorations_theme_property(), value.into().0)
861 }
862
863 #[deprecated(note = "Use window_decorations instead.")]
864 pub fn system_decorations(&self) -> WindowDecorations {
865 self.window_decorations()
866 }
867
868 #[deprecated(note = "Use set_window_decorations instead.")]
869 pub fn set_system_decorations(&self, value: WindowDecorations) {
870 self.set_window_decorations(value)
871 }
872
873 pub fn show_activated(&self) -> bool {
875 self.get_value(Self::show_activated_property())
876 }
877
878 pub fn set_show_activated(&self, value: bool) {
879 self.set_value(Self::show_activated_property(), value)
880 }
881
882 pub fn show_in_taskbar(&self) -> bool {
884 self.get_value(Self::show_in_taskbar_property())
885 }
886
887 pub fn set_show_in_taskbar(&self, value: bool) {
888 self.set_value(Self::show_in_taskbar_property(), value)
889 }
890
891 pub fn closing_behavior(&self) -> WindowClosingBehavior {
894 self.get_value(Self::closing_behavior_property())
895 }
896
897 pub fn set_closing_behavior(&self, value: WindowClosingBehavior) {
898 self.set_value(Self::closing_behavior_property(), value)
899 }
900
901 pub fn window_state(&self) -> WindowState {
904 match self.platform_impl() {
905 Some(platform_impl) if platform_impl.window_state_getter_is_usable() => platform_impl.window_state(),
906 _ => self.last_window_state.get(),
907 }
908 }
909
910 pub fn set_window_state(&self, value: WindowState) {
911 if let Some(platform_impl) = self.platform_impl() {
912 if platform_impl.window_state_getter_is_usable() {
913 platform_impl.set_window_state(value);
916
917 if platform_impl.window_state() != value {
920 let old_value = self.last_window_state.get();
923 self.last_window_state.set(platform_impl.window_state());
924 self.raise_direct_property_changed(
925 Self::window_state_property(),
926 &old_value,
927 &self.last_window_state.get(),
928 );
929 }
930 } else {
931 self.set_and_raise_cell(Self::window_state_property(), &self.last_window_state, value);
934 platform_impl.set_window_state(self.last_window_state.get());
935 }
936 }
937 }
938
939 pub fn can_resize(&self) -> bool {
941 self.get_value(Self::can_resize_property())
942 }
943
944 pub fn set_can_resize(&self, value: bool) {
945 self.set_value(Self::can_resize_property(), value)
946 }
947
948 pub fn can_minimize(&self) -> bool {
952 self.get_value(Self::can_minimize_property())
953 }
954
955 pub fn set_can_minimize(&self, value: bool) {
956 self.set_value(Self::can_minimize_property(), value)
957 }
958
959 pub fn can_maximize(&self) -> bool {
965 self.get_value(Self::can_maximize_property())
966 }
967
968 pub fn set_can_maximize(&self, value: bool) {
969 self.set_value(Self::can_maximize_property(), value)
970 }
971
972 pub fn allowed_window_actions(&self) -> PlatformAllowedWindowActions {
975 self.allowed_window_actions.get()
976 }
977
978 pub fn icon(&self) -> Option<Rc<WindowIcon>> {
980 self.get_value(Self::icon_property())
981 }
982
983 pub fn set_icon(&self, value: Option<Rc<WindowIcon>>) {
984 self.set_value(Self::icon_property(), value)
985 }
986
987 pub fn window_startup_location(&self) -> WindowStartupLocation {
989 self.get_value(Self::window_startup_location_property())
990 }
991
992 pub fn set_window_startup_location(&self, value: WindowStartupLocation) {
993 self.set_value(Self::window_startup_location_property(), value)
994 }
995
996 pub fn position(&self) -> PixelPoint {
998 self.platform_impl().map(|platform_impl| platform_impl.position()).unwrap_or_default()
999 }
1000
1001 pub fn set_position(&self, value: PixelPoint) {
1002 if let Some(platform_impl) = self.platform_impl() {
1003 platform_impl.move_(value);
1004 }
1005 self.position_was_set.set(true);
1006 }
1007
1008 pub fn is_dialog(&self) -> bool {
1010 self.showing_as_dialog.get()
1011 }
1012
1013 pub fn begin_move_drag(&self, e: &PointerPressedEventArgs) {
1016 if let Some(platform_impl) = self.platform_impl() {
1017 platform_impl.begin_move_drag(e);
1018 }
1019 }
1020
1021 pub fn begin_resize_drag(&self, edge: WindowEdge, e: &PointerPressedEventArgs) {
1025 if let Some(platform_impl) = self.platform_impl() {
1026 platform_impl.begin_resize_drag(edge, e);
1027 }
1028 }
1029
1030 pub fn closing(&self, handler: impl Fn(&WindowClosingEventArgs) + 'static) -> Rc<dyn IDisposable> {
1033 let token = self.closing.add(Rc::new(handler));
1034 let weak = self.to_ref().downgrade();
1035 Disposable::create(move || {
1036 if let Some(this) = weak.upgrade() {
1037 this.closing.remove(token);
1038 }
1039 })
1040 }
1041
1042 pub fn close(&self) {
1044 self.close_core(WindowCloseReason::WindowClosing, true, false);
1045 }
1046
1047 pub fn close_with_result(&self, dialog_result: Option<BoxedValue>) {
1053 *self.dialog_result.borrow_mut() = dialog_result;
1054 self.close_core(WindowCloseReason::WindowClosing, true, false);
1055 }
1056
1057 pub fn close_core(&self, reason: WindowCloseReason, is_programmatic: bool, ignore_cancel: bool) {
1061 let scope = CloseScope { window: self, close: Cell::new(true), ignore_cancel };
1062
1063 if self.should_cancel_close(&WindowClosingEventArgs::new(reason, is_programmatic)) {
1064 scope.close.set(false);
1065 }
1066 }
1067
1068 fn close_internal(&self) {
1069 let children = self.children.borrow().clone();
1070 for (child, _) in children {
1071 child.close_internal();
1072 }
1073
1074 if let Some(platform_impl) = self.platform_impl() {
1075 platform_impl.dispose();
1076 }
1077
1078 self.showing_as_dialog.set(false);
1079
1080 self.set_owner(None);
1081 }
1082
1083 fn should_cancel_close(&self, args: &WindowClosingEventArgs) -> bool {
1084 match self.closing_behavior() {
1085 WindowClosingBehavior::OwnerAndChildWindows => {
1086 let mut can_close = true;
1087
1088 let children = self.children.borrow().clone();
1089 if !children.is_empty() {
1090 let owner_closing_args;
1091 let child_args = if args.close_reason() == WindowCloseReason::WindowClosing {
1092 owner_closing_args =
1093 WindowClosingEventArgs::new(WindowCloseReason::OwnerWindowClosing, args.is_programmatic());
1094 &owner_closing_args
1095 } else {
1096 args
1097 };
1098
1099 for (child, _) in children {
1100 if child.should_cancel_close(child_args) {
1101 can_close = false;
1102 }
1103 }
1104 }
1105
1106 if can_close {
1107 self.on_closing(args);
1108
1109 return args.cancel();
1110 }
1111
1112 true
1113 }
1114 WindowClosingBehavior::OwnerWindowOnly => {
1115 self.on_closing(args);
1116
1117 args.cancel()
1118 }
1119 }
1120 }
1121
1122 fn handle_window_state_changed(&self, state: WindowState) {
1123 debug_assert!(self
1125 .platform_impl()
1126 .is_none_or(|platform_impl| !platform_impl.window_state_getter_is_usable()
1127 || platform_impl.window_state() == state));
1128
1129 self.set_and_raise_cell(Self::window_state_property(), &self.last_window_state, state);
1130
1131 if state == WindowState::Minimized {
1132 self.stop_rendering();
1133 } else {
1134 self.start_rendering();
1135 }
1136
1137 self.update_drawn_decoration_parts();
1139 }
1140
1141 pub fn allowed_window_actions_changed(
1145 &self,
1146 handler: impl Fn(PlatformAllowedWindowActions) + 'static,
1147 ) -> Rc<dyn IDisposable> {
1148 let token = self.allowed_window_actions_changed.add(Rc::new(handler));
1149 let weak = self.to_ref().downgrade();
1150 Disposable::create(move || {
1151 if let Some(this) = weak.upgrade() {
1152 this.allowed_window_actions_changed.remove(token);
1153 }
1154 })
1155 }
1156
1157 fn on_allowed_window_actions_changed(&self, actions: PlatformAllowedWindowActions) {
1158 self.allowed_window_actions.set(actions);
1159 for (_, handler) in self.allowed_window_actions_changed.snapshot().iter() {
1160 handler(actions);
1161 }
1162 }
1163
1164 fn extend_client_area_to_decorations_changed(&self, is_extended: bool) {
1165 self.set_is_extended_into_window_decorations(is_extended);
1166 self.set_off_screen_margin(
1167 self.platform_impl().map(|platform_impl| platform_impl.off_screen_margin()).unwrap_or_default(),
1168 );
1169
1170 self.update_drawn_decorations();
1171 }
1172
1173 fn update_drawn_decorations(&self) {
1174 let parts = self.compute_decoration_parts();
1175
1176 self.is_forced_decoration_mode.set(parts.is_some() && !self.is_extended_into_window_decorations());
1178
1179 self.top_level_host().update_drawn_decorations(parts, self.window_state(), self.window_decorations_theme());
1180
1181 if parts.is_some() {
1182 if let Some(decorations) = self.top_level_host().decorations() {
1184 decorations.set_render_scaling(self.render_scaling());
1185
1186 let hint = self.extend_client_area_title_bar_height_hint();
1187 if hint >= 0.0 {
1188 decorations.set_title_bar_height_override(hint);
1189 }
1190 }
1191 }
1192
1193 self.update_drawn_decoration_margins();
1194 }
1195
1196 fn on_scaling_changed_update_decorations(&self) {
1197 if let Some(decorations) = self.top_level_host().decorations() {
1198 decorations.set_render_scaling(self.render_scaling());
1199 }
1200 }
1201
1202 fn update_drawn_decoration_parts(&self) {
1205 if self.top_level_host().decorations().is_none() {
1206 return;
1207 }
1208
1209 self.top_level_host().update_drawn_decorations(
1210 self.compute_decoration_parts(),
1211 self.window_state(),
1212 self.window_decorations_theme(),
1213 );
1214 }
1215
1216 fn compute_decoration_parts(&self) -> Option<crate::chrome::DrawnWindowDecorationParts> {
1217 use crate::chrome::DrawnWindowDecorationParts;
1218 use crate::platform::PlatformRequestedDrawnDecoration;
1219
1220 let platform_impl = self.platform_impl();
1221 if !platform_impl.as_ref().is_some_and(|platform_impl| platform_impl.needs_managed_decorations()) {
1222 return None;
1223 }
1224
1225 let platform_needs = platform_impl
1226 .map(|platform_impl| platform_impl.requested_drawn_decorations())
1227 .unwrap_or(PlatformRequestedDrawnDecoration::NONE);
1228 let mut parts = DrawnWindowDecorationParts::NONE;
1229 if self.window_decorations() != WindowDecorations::None {
1230 if platform_needs.contains(PlatformRequestedDrawnDecoration::TITLE_BAR)
1231 && self.window_decorations() == WindowDecorations::Full
1232 {
1233 parts |= DrawnWindowDecorationParts::TITLE_BAR;
1234 }
1235 if platform_needs.contains(PlatformRequestedDrawnDecoration::SHADOW) {
1236 parts |= DrawnWindowDecorationParts::SHADOW;
1237 }
1238 if platform_needs.contains(PlatformRequestedDrawnDecoration::BORDER) {
1239 parts |= DrawnWindowDecorationParts::BORDER;
1240 }
1241 if platform_needs.contains(PlatformRequestedDrawnDecoration::RESIZE_GRIPS) && self.can_resize() {
1242 parts |= DrawnWindowDecorationParts::RESIZE_GRIPS;
1243 }
1244
1245 if self.window_state() == WindowState::FullScreen {
1247 parts &= !(DrawnWindowDecorationParts::SHADOW
1248 | DrawnWindowDecorationParts::BORDER
1249 | DrawnWindowDecorationParts::RESIZE_GRIPS
1250 | DrawnWindowDecorationParts::TITLE_BAR);
1251 }
1252 else if self.window_state() == WindowState::Maximized {
1254 parts &= !(DrawnWindowDecorationParts::SHADOW
1255 | DrawnWindowDecorationParts::BORDER
1256 | DrawnWindowDecorationParts::RESIZE_GRIPS);
1257 }
1258 }
1259
1260 Some(parts)
1261 }
1262
1263 fn on_title_bar_height_hint_changed(&self) {
1264 let Some(decorations) = self.top_level_host().decorations() else { return };
1265
1266 decorations.set_title_bar_height_override(self.extend_client_area_title_bar_height_hint());
1267
1268 self.update_drawn_decoration_margins();
1269 }
1270
1271 pub(crate) fn on_drawn_decorations_geometry_changed(&self) {
1275 self.update_drawn_decoration_margins();
1276 }
1277
1278 fn update_drawn_decoration_margins(&self) {
1279 if let Some(decorations) = self.top_level_host().decorations() {
1280 use crate::chrome::DrawnWindowDecorationParts;
1281
1282 let parts = decorations.enabled_parts();
1283 let title_bar_height =
1284 if parts.contains(DrawnWindowDecorationParts::TITLE_BAR) { decorations.title_bar_height() } else { 0.0 };
1285 let frame = if parts.contains(DrawnWindowDecorationParts::BORDER) {
1286 decorations.frame_thickness()
1287 } else {
1288 Thickness::default()
1289 };
1290 let shadow = if parts.contains(DrawnWindowDecorationParts::SHADOW) {
1291 decorations.shadow_thickness()
1292 } else {
1293 Thickness::default()
1294 };
1295
1296 if let Some(platform_impl) = self.platform_impl() {
1297 platform_impl.set_shadow_extents(shadow);
1298 }
1299
1300 let margin = Thickness::new(
1301 frame.left + shadow.left,
1302 title_bar_height + frame.top + shadow.top,
1303 frame.right + shadow.right,
1304 frame.bottom + shadow.bottom,
1305 );
1306
1307 if self.is_forced_decoration_mode.get() {
1308 self.set_window_decoration_margin(Thickness::default());
1311 self.top_level_host().set_decoration_inset(margin);
1312 } else {
1313 self.set_window_decoration_margin(margin);
1315 self.top_level_host().set_decoration_inset(Thickness::default());
1316 }
1317 return;
1318 }
1319
1320 let platform_impl = self.platform_impl();
1322 self.set_window_decoration_margin(
1323 platform_impl.as_ref().map(|platform_impl| platform_impl.extended_margins()).unwrap_or_default(),
1324 );
1325 self.top_level_host().set_decoration_inset(Thickness::default());
1326 if let Some(platform_impl) = platform_impl {
1327 platform_impl.set_shadow_extents(Thickness::default());
1328 }
1329 }
1330
1331 pub fn show_with_owner(&self, owner: &Window) {
1337 self.show_core(Some(owner), false);
1338 }
1339
1340 fn ensure_state_before_show(&self) {
1341 if self.platform_impl().is_none() {
1342 panic!("Cannot re-show a closed window.");
1343 }
1344 }
1345
1346 fn ensure_parent_state_before_show(&self, owner: &Window) {
1347 if owner.platform_impl().is_none() {
1348 panic!("Cannot show a window with a closed owner.");
1349 }
1350
1351 if std::ptr::eq(owner, self) {
1352 panic!("A Window cannot be its own owner.");
1353 }
1354
1355 if !owner.is_visible() {
1356 panic!("Cannot show window with non-visible owner.");
1357 }
1358 }
1359
1360 fn show_core(&self, owner: Option<&Window>, modal: bool) -> Option<DialogCompletion> {
1361 let _freeze = self.freeze_visibility_change_handling();
1362
1363 self.ensure_state_before_show();
1364
1365 if modal && owner.is_none() {
1366 panic!("A dialog requires an owner.");
1367 }
1368 if let Some(owner) = owner {
1369 self.ensure_parent_state_before_show(owner);
1370 }
1371
1372 if self.shown.get() {
1373 if modal {
1374 panic!("The window is already being shown.");
1375 }
1376 return None;
1377 }
1378
1379 self.showing_as_dialog.set(modal);
1380 self.raise_event(&RoutedEventArgs::with_event(Self::window_opened_event()));
1381
1382 self.ensure_initialized();
1383 self.apply_styling();
1384
1385 self.update_drawn_decorations();
1387
1388 if self.is_forced_decoration_mode.get() {
1390 let inset = self.top_level_host().decoration_inset();
1391 self.set_client_size(Size::new(
1392 (self.client_size().width - inset.left - inset.right).max(0.0),
1393 (self.client_size().height - inset.top - inset.bottom).max(0.0),
1394 ));
1395 }
1396
1397 self.shown.set(true);
1398 self.set_is_visible(true);
1399
1400 self.set_effective_icon(self.icon());
1401
1402 self.set_expected_scaling(owner);
1405
1406 let mut initial_size = Size::new(
1407 if self.width().is_nan() { self.client_size().width } else { self.width() },
1408 if self.height().is_nan() { self.client_size().height } else { self.height() },
1409 );
1410
1411 let min_max = MinMax::new(self);
1412
1413 initial_size = Size::new(
1414 MathUtilities::clamp(initial_size.width, min_max.min_width, min_max.max_width),
1415 MathUtilities::clamp(initial_size.height, min_max.min_height, min_max.max_height),
1416 );
1417
1418 let mut client_size_changed = initial_size != self.client_size();
1419 self.set_client_size(initial_size); self.layout_manager().execute_initial_layout_pass();
1423
1424 if self.size_to_content().contains(SizeToContent::WIDTH) {
1425 initial_size = initial_size.with_width(MathUtilities::clamp(
1426 self.arrange_bounds.get().width,
1427 min_max.min_width,
1428 min_max.max_width,
1429 ));
1430 client_size_changed |= initial_size != self.client_size();
1431 self.set_client_size(initial_size);
1432 }
1433
1434 if self.size_to_content().contains(SizeToContent::HEIGHT) {
1435 initial_size = initial_size.with_height(MathUtilities::clamp(
1436 self.arrange_bounds.get().height,
1437 min_max.min_height,
1438 min_max.max_height,
1439 ));
1440 client_size_changed |= initial_size != self.client_size();
1441 self.set_client_size(initial_size);
1442 }
1443
1444 self.set_owner(owner.map(|owner| owner.to_ref().upcast()));
1445
1446 self.apply_window_startup_location(owner);
1447
1448 self.set_desktop_scaling_override(None);
1449
1450 let platform_client_size =
1452 self.platform_impl().map(|platform_impl| platform_impl.client_size()).unwrap_or_default();
1453 let comparable_client_size = if self.is_forced_decoration_mode.get() {
1454 let inset = self.top_level_host().decoration_inset();
1455 Size::new(
1456 (platform_client_size.width - inset.left - inset.right).max(0.0),
1457 (platform_client_size.height - inset.top - inset.bottom).max(0.0),
1458 )
1459 } else {
1460 platform_client_size
1461 };
1462
1463 if client_size_changed || self.client_size() != comparable_client_size {
1464 self.resize_platform_impl(self.client_size(), WindowResizeReason::Layout);
1466
1467 self.renderer().resized(self.client_size());
1471 self.on_resized(&WindowResizedEventArgs::new(self.client_size(), WindowResizeReason::Layout));
1472
1473 if !self.width().is_nan() {
1474 self.set_width(self.client_size().width);
1475 }
1476 if !self.height().is_nan() {
1477 self.set_height(self.client_size().height);
1478 }
1479 }
1480
1481 self.set_frame_size(self.platform_impl().and_then(|platform_impl| platform_impl.frame_size()));
1482
1483 self.can_handle_resized.set(true);
1484
1485 self.start_rendering();
1486 if let Some(platform_impl) = self.platform_impl() {
1487 platform_impl.show(self.show_activated(), modal);
1488 }
1489
1490 let mut result = None;
1491 if let (true, Some(owner)) = (modal, owner) {
1492 let completion = DialogCompletion::new();
1493 let weak = self.to_ref().downgrade();
1494
1495 let taken = Cell::new(false);
1496 let closed_subscription = TopLevel::closed(self, {
1497 let weak = weak.clone();
1498 move || {
1499 if taken.replace(true) {
1500 return;
1501 }
1502 let modal_subscription =
1503 weak.upgrade().and_then(|this| this.modal_subscription.borrow().clone());
1504 if let Some(modal_subscription) = modal_subscription {
1505 modal_subscription.dispose();
1506 }
1507 }
1508 });
1509 let complete = Disposable::create({
1510 let owner = owner.to_ref().downgrade();
1511 let completion = completion.clone();
1512 move || {
1513 let this = weak.upgrade();
1514 if let Some(this) = &this {
1515 *this.modal_subscription.borrow_mut() = None;
1516 }
1517 if let Some(owner) = owner.upgrade() {
1518 owner.activate();
1519 }
1520 completion.set_result(this.and_then(|this| this.dialog_result.borrow().clone()));
1521 }
1522 });
1523
1524 let disposables: Rc<dyn IDisposable> =
1525 Rc::new(CompositeDisposable::from_disposables([closed_subscription, complete]));
1526 *self.modal_subscription.borrow_mut() = Some(disposables);
1527 result = Some(completion);
1528 }
1529
1530 self.on_opened();
1531 if !modal {
1532 self.was_shown_before.set(true);
1533 }
1534
1535 result
1536 }
1537
1538 fn resize_platform_impl(&self, mut size: Size, reason: WindowResizeReason) {
1539 let Some(platform_impl) = self.platform_impl() else { return };
1540
1541 if self.is_forced_decoration_mode.get() {
1543 let inset = self.top_level_host().decoration_inset();
1544 size = Size::new(size.width + inset.left + inset.right, size.height + inset.top + inset.bottom);
1545 if platform_impl.client_size() != size {
1546 platform_impl.resize(size, reason);
1547 }
1548 } else {
1549 platform_impl.resize(size, reason);
1550 }
1551 }
1552
1553 pub fn show_dialog(&self, owner: &Window) -> DispatcherTask<Option<BoxedValue>> {
1565 let completion = self.show_core(Some(owner), true).expect("showing a dialog creates its completion");
1566 Dispatcher::ui_thread().invoke_async_task_local(move || completion)
1567 }
1568
1569 pub fn show_dialog_typed<TResult: Clone + Default + 'static>(&self, owner: &Window) -> DispatcherTask<TResult> {
1581 let completion = self.show_core(Some(owner), true).expect("showing a dialog creates its completion");
1582 Dispatcher::ui_thread().invoke_async_task_local(move || async move {
1583 match completion.await {
1584 Some(dialog_result) => match dialog_result.downcast_ref::<TResult>() {
1585 Some(dialog_result) => dialog_result.clone(),
1586 None => panic!(
1587 "Unable to cast the dialog result of type {} to {}.",
1588 dialog_result.any_value_type_name(),
1589 std::any::type_name::<TResult>()
1590 ),
1591 },
1592 None => TResult::default(),
1593 }
1594 })
1595 }
1596
1597 pub fn sort_windows_by_z_order(windows: &mut [Ref<Window>]) {
1604 if windows.len() <= 1 {
1605 return;
1606 }
1607
1608 let platform = FerroLocator::current().get_required_service::<dyn IWindowingPlatform>();
1609
1610 let window_impls: Vec<Rc<dyn IWindowImpl>> = windows
1611 .iter()
1612 .enumerate()
1613 .map(|(i, window)| window.platform_impl().unwrap_or_else(|| panic!("Invalid window at index {i}")))
1614 .collect();
1615
1616 let mut z_order = vec![0i64; windows.len()];
1617 platform.get_windows_z_order(&window_impls, &mut z_order);
1618
1619 let mut keyed: Vec<(i64, Ref<Window>)> = z_order.into_iter().zip(windows.iter().cloned()).collect();
1620 keyed.sort_by_key(|(z, _)| *z);
1621 for (slot, (_, window)) in windows.iter_mut().zip(keyed) {
1622 *slot = window;
1623 }
1624 }
1625
1626 fn update_enabled(&self) {
1627 let is_enabled = !self.children.borrow().iter().any(|(_, is_dialog)| *is_dialog);
1628
1629 if let Some(platform_impl) = self.platform_impl() {
1630 platform_impl.set_enabled(is_enabled);
1631 }
1632 }
1633
1634 fn add_child(&self, window: &Window, is_dialog: bool) {
1635 self.children.borrow_mut().push((window.to_ref(), is_dialog));
1636 self.update_enabled();
1637 }
1638
1639 fn remove_child(&self, window: &Window) {
1640 self.children.borrow_mut().retain(|(child, _)| !std::ptr::eq(&**child, window));
1641
1642 self.update_enabled();
1643 }
1644
1645 fn on_got_input_when_disabled(&self) {
1646 let first_dialog_child =
1647 self.children.borrow().iter().find(|(_, is_dialog)| *is_dialog).map(|(child, _)| child.clone());
1648
1649 if let Some(first_dialog_child) = first_dialog_child {
1650 first_dialog_child.on_got_input_when_disabled();
1651 } else {
1652 self.activate();
1653 }
1654 }
1655
1656 fn set_expected_scaling(&self, owner: Option<&Window>) {
1657 if self.was_shown_before.get() {
1658 return;
1659 }
1660
1661 let location = self.get_effective_window_startup_location(owner);
1662
1663 match location {
1664 WindowStartupLocation::CenterOwner => {
1665 self.set_desktop_scaling_override(owner.map(|owner| owner.desktop_scaling()));
1666 }
1667 WindowStartupLocation::CenterScreen => {
1668 let scaling = owner
1669 .map(|owner| owner.desktop_scaling())
1670 .or_else(|| self.screens().screen_from_point(self.position()).map(|screen| screen.scaling()))
1671 .or_else(|| self.screens().primary().map(|screen| screen.scaling()));
1672 self.set_desktop_scaling_override(scaling);
1673 }
1674 WindowStartupLocation::Manual => {
1675 self.set_desktop_scaling_override(
1676 self.screens().screen_from_point(self.position()).map(|screen| screen.scaling()),
1677 );
1678 }
1679 }
1680 }
1681
1682 fn get_effective_window_startup_location(&self, owner: Option<&Window>) -> WindowStartupLocation {
1683 let mut startup_location = self.window_startup_location();
1684
1685 if startup_location == WindowStartupLocation::CenterOwner
1686 && owner.is_none_or(|owner| owner.window_state() == WindowState::Minimized)
1687 {
1688 startup_location = WindowStartupLocation::CenterScreen;
1691 }
1692
1693 startup_location
1694 }
1695
1696 fn apply_window_startup_location(&self, owner: Option<&Window>) {
1697 if self.was_shown_before.get() {
1698 return;
1699 }
1700
1701 let startup_location = self.get_effective_window_startup_location(owner);
1702
1703 let platform_frame = self
1705 .platform_impl()
1706 .and_then(|platform_impl| platform_impl.frame_size().map(|frame_size| (frame_size, platform_impl.client_size())));
1707 let rect = match platform_frame {
1708 Some((frame_size, platform_client_size)) => {
1709 let diff = frame_size - platform_client_size;
1711 PixelRect::from_size(PixelSize::from_size(self.client_size() + diff, self.desktop_scaling()))
1712 }
1713 None => PixelRect::from_size(PixelSize::from_size(self.client_size(), self.desktop_scaling())),
1714 };
1715
1716 let apply_screen_constraint = |screen: Option<&Rc<Screen>>, mut child_rect: PixelRect| -> PixelRect {
1717 if let Some(constraint) = screen.map(|screen| screen.working_area()) {
1718 let max_x = constraint.right() - rect.width;
1719 let max_y = constraint.bottom() - rect.height;
1720
1721 if constraint.x <= max_x {
1722 child_rect = child_rect.with_x(MathUtilities::clamp_i32(child_rect.x, constraint.x, max_x));
1723 }
1724 if constraint.y <= max_y {
1725 child_rect = child_rect.with_y(MathUtilities::clamp_i32(child_rect.y, constraint.y, max_y));
1726 }
1727 }
1728
1729 child_rect
1730 };
1731
1732 if startup_location == WindowStartupLocation::CenterScreen {
1733 let mut screen: Option<Rc<Screen>> = None;
1734
1735 if let Some(owner) = owner {
1736 screen = self
1737 .screens()
1738 .screen_from_window(owner)
1739 .or_else(|| self.screens().screen_from_point(owner.position()));
1740 }
1741
1742 let screen = screen
1743 .or_else(|| self.screens().screen_from_point(self.position()))
1744 .or_else(|| self.screens().primary());
1745
1746 if let Some(screen) = screen {
1747 let mut child_rect = screen.working_area().center_rect(rect);
1748
1749 if self.screens().screen_from_point(child_rect.position()).is_none() {
1750 child_rect = apply_screen_constraint(Some(&screen), child_rect);
1751 }
1752
1753 self.set_position(child_rect.position());
1754 }
1755 } else if startup_location == WindowStartupLocation::CenterOwner {
1756 let owner = owner.expect("the effective startup location is the owner's center only with an owner");
1757 let owner_size = owner.frame_size().unwrap_or_else(|| owner.client_size());
1758 let owner_rect = PixelRect::from_position_size(
1759 owner.position(),
1760 PixelSize::from_size(owner_size, owner.desktop_scaling()),
1761 );
1762 let mut child_rect = owner_rect.center_rect(rect);
1763
1764 let screen = self.screens().screen_from_window(owner);
1765
1766 child_rect = apply_screen_constraint(screen.as_ref(), child_rect);
1767
1768 self.set_position(child_rect.position());
1769 }
1770
1771 if !self.position_was_set.get() {
1773 if let Some(platform_impl) = self.platform_impl() {
1774 if self.desktop_scaling() != platform_impl.desktop_scaling() {
1775 platform_impl.move_(self.position());
1776 }
1777 }
1778 }
1779 }
1780
1781 fn set_effective_icon(&self, icon: Option<Rc<WindowIcon>>) {
1782 if let Some(platform_impl) = self.platform_impl() {
1783 platform_impl.set_icon(icon.map(|icon| icon.platform_impl()));
1784 }
1785 }
1786}