diff --git a/winit-appkit/src/app_state.rs b/winit-appkit/src/app_state.rs index 7c2d13790d..4316ff5181 100644 --- a/winit-appkit/src/app_state.rs +++ b/winit-appkit/src/app_state.rs @@ -269,6 +269,10 @@ impl AppState { self.control_flow.set(value) } + pub(super) fn is_handling_event(&self) -> bool { + self.event_handler.in_use() + } + pub fn control_flow(&self) -> ControlFlow { self.control_flow.get() } diff --git a/winit-appkit/src/window_delegate.rs b/winit-appkit/src/window_delegate.rs index a2077a58aa..0b0cd80ce1 100644 --- a/winit-appkit/src/window_delegate.rs +++ b/winit-appkit/src/window_delegate.rs @@ -1081,6 +1081,20 @@ impl WindowDelegate { }); } + fn defer_if_handling_event(&self, f: impl FnOnce(Retained) + 'static) -> bool { + // AppKit state transitions such as zoom/fullscreen can synchronously run resize/display + // callbacks. Starting them from inside a winit event callback prevents those callbacks + // from being delivered immediately, so defer the transition to the next run-loop turn. + if !self.ivars().app_state.is_handling_event() { + return false; + } + + let mtm = MainThreadMarker::from(self); + let this = self.retain(); + MainRunLoop::get(mtm).queue_closure(move || f(this)); + true + } + fn handle_scale_factor_changed(&self, scale_factor: CGFloat) { let window = self.window(); @@ -1600,6 +1614,10 @@ impl WindowDelegate { #[inline] pub fn set_maximized(&self, maximized: bool) { + if self.defer_if_handling_event(move |this| this.set_maximized(maximized)) { + return; + } + let mtm = MainThreadMarker::from(self); let is_zoomed = self.is_zoomed(); if is_zoomed == maximized { @@ -1645,9 +1663,6 @@ impl WindowDelegate { #[inline] pub(crate) fn set_fullscreen(&self, fullscreen: Option) { - let mtm = MainThreadMarker::from(self); - let app = NSApplication::sharedApplication(mtm); - if self.ivars().is_simple_fullscreen.get() { return; } @@ -1676,6 +1691,18 @@ impl WindowDelegate { return; } + if !self.ivars().initial_fullscreen.get() + && self.defer_if_handling_event({ + let fullscreen = fullscreen.clone(); + move |this| this.set_fullscreen(fullscreen) + }) + { + return; + } + + let mtm = MainThreadMarker::from(self); + let app = NSApplication::sharedApplication(mtm); + // If the fullscreen is on a different monitor, we must move the window // to that monitor before we toggle fullscreen (as `toggleFullScreen` // does not take a screen parameter, but uses the current screen)