diff --git a/winit-appkit/src/view.rs b/winit-appkit/src/view.rs index 396f8d1af..69b87a49d 100644 --- a/winit-appkit/src/view.rs +++ b/winit-appkit/src/view.rs @@ -3,13 +3,13 @@ use std::cell::{Cell, RefCell}; use std::collections::{HashMap, VecDeque}; use std::rc::Rc; -use dpi::{LogicalPosition, LogicalSize}; +use dpi::{LogicalPosition, PhysicalSize}; use objc2::rc::Retained; use objc2::runtime::{AnyObject, Sel}; use objc2::{AnyThread, DefinedClass, MainThreadMarker, define_class, msg_send}; use objc2_app_kit::{ NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSTrackingArea, - NSTrackingAreaOptions, NSView, NSWindow, + NSTrackingAreaOptions, NSView, NSViewLayerContentsRedrawPolicy, NSWindow, }; use objc2_core_foundation::CGRect; use objc2_foundation::{ @@ -161,10 +161,20 @@ define_class!( // resize occurring. // 2. Even when a window resize does occur on a new tabbed window, it contains the wrong // size (includes tab height). - let rect = self.frame(); - let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64); - let size = logical_size.to_physical::(self.scale_factor()); - self.queue_event(WindowEvent::SurfaceResized(size)); + self.surface_resized(); + // During live resize, AppKit may not let the normal event loop reach its next redraw + // point before stretching the current layer contents. Redraw immediately after the + // app has observed the new surface size. + self.redraw_during_live_resize(); + } + + #[unsafe(method(viewDidChangeBackingProperties))] + fn view_did_change_backing_properties(&self) { + let _entered = debug_span!("viewDidChangeBackingProperties").entered(); + // Moving between displays or changing scale can alter the drawable backing size + // without a matching frame-size change. + self.surface_resized(); + self.redraw_during_live_resize(); } #[unsafe(method(drawRect:))] @@ -795,6 +805,10 @@ impl WinitView { let this: Retained = unsafe { msg_send![super(this), init] }; *this.ivars().input_source.borrow_mut() = this.current_input_source(); + // Ask AppKit to redisplay the layer while the view is being resized so layer-backed + // surfaces keep painting. + this.setLayerContentsRedrawPolicy(NSViewLayerContentsRedrawPolicy::DuringViewResize); + // `MouseEnteredAndExited` enables receiving events through `mouseEntered:` and // `mouseExited:`. // @@ -853,6 +867,37 @@ impl WinitView { }); } + fn surface_resized(&self) { + let Some(window) = (**self).window() else { + return; + }; + let size = self.surface_size(); + let window_id = window_id(&window); + self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| { + app.window_event(event_loop, window_id, WindowEvent::SurfaceResized(size)); + }); + } + + /// Returns the drawable size from the view's backing-coordinate bounds. + pub(super) fn surface_size(&self) -> PhysicalSize { + // The view bounds are authoritative for full-size content views and during live resize. + // Deriving this from the window frame can exclude custom titlebar content or be stale. + let backing_bounds = self.convertRectToBacking(self.bounds()); + PhysicalSize::new( + backing_bounds.size.width.round().max(0.0) as u32, + backing_bounds.size.height.round().max(0.0) as u32, + ) + } + + fn redraw_during_live_resize(&self) { + let Some(window) = (**self).window() else { + return; + }; + if window.inLiveResize() { + self.ivars().app_state.handle_redraw(window_id(&window)); + } + } + fn scale_factor(&self) -> f64 { self.window().backingScaleFactor() as f64 } diff --git a/winit-appkit/src/window_delegate.rs b/winit-appkit/src/window_delegate.rs index e316973e7..4697cf820 100644 --- a/winit-appkit/src/window_delegate.rs +++ b/winit-appkit/src/window_delegate.rs @@ -912,10 +912,7 @@ impl WindowDelegate { fn handle_scale_factor_changed(&self, scale_factor: CGFloat) { let window = self.window(); - let content_size = window.contentRectForFrameRect(window.frame()).size; - let content_size = LogicalSize::new(content_size.width, content_size.height); - - let suggested_size = content_size.to_physical(scale_factor); + let suggested_size = self.view().surface_size(); let new_surface_size = Arc::new(Mutex::new(suggested_size)); self.queue_event(WindowEvent::ScaleFactorChanged { scale_factor, @@ -1040,9 +1037,7 @@ impl WindowDelegate { #[inline] pub fn surface_size(&self) -> PhysicalSize { - let content_rect = self.window().contentRectForFrameRect(self.window().frame()); - let logical = LogicalSize::new(content_rect.size.width, content_rect.size.height); - logical.to_physical(self.scale_factor()) + self.view().surface_size() } #[inline]