macOS: Clean up coordinate system calculations (#3302)

* Clean up macOS and iOS monitor code a bit

* Clean up window size methods

Use `setContentSize`, `setContentMinSize`, `setContentMaxSize` and `contentRectForFrameRect` to let the windowing system figure out the required scaling, instead of us doing it manually.

* Use a flipped NSView coordinate system

* Clean up window position methods
This commit is contained in:
Mads Marquart 2023-12-24 10:12:09 +01:00 committed by GitHub
parent 5a43ea8cd6
commit 4f6fd44c6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 173 additions and 217 deletions

View file

@ -11,4 +11,5 @@ disallowed-methods = [
{ path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
{ path = "icrate::AppKit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
{ path = "icrate::AppKit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
]

View file

@ -1,7 +1,7 @@
#![allow(clippy::single_match)]
use simple_logger::SimpleLogger;
use winit::dpi::PhysicalSize;
use winit::dpi::LogicalSize;
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
use winit::event_loop::EventLoop;
use winit::keyboard::{Key, NamedKey};
@ -126,7 +126,7 @@ fn main() -> Result<(), impl std::error::Error> {
"i" => {
with_min_size = !with_min_size;
let min_size = if with_min_size {
Some(PhysicalSize::new(100, 100))
Some(LogicalSize::new(100, 100))
} else {
None
};
@ -139,7 +139,7 @@ fn main() -> Result<(), impl std::error::Error> {
"a" => {
with_max_size = !with_max_size;
let max_size = if with_max_size {
Some(PhysicalSize::new(200, 200))
Some(LogicalSize::new(200, 200))
} else {
None
};

View file

@ -96,21 +96,13 @@ fn main() -> Result<(), impl std::error::Error> {
)),
(false, _) => None,
}),
"l" if state => {
if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked)
{
println!("error: {err}");
}
}
"g" if state => {
if let Err(err) =
window.set_cursor_grab(CursorGrabMode::Confined)
{
println!("error: {err}");
}
}
"g" | "l" if !state => {
if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) {
ch @ ("g" | "l") => {
let mode = match (ch, state) {
("l", true) => CursorGrabMode::Locked,
("g", true) => CursorGrabMode::Confined,
(_, _) => CursorGrabMode::None,
};
if let Err(err) = window.set_cursor_grab(mode) {
println!("error: {err}");
}
}
@ -123,10 +115,6 @@ fn main() -> Result<(), impl std::error::Error> {
println!("-> inner_size : {:?}", window.inner_size());
println!("-> fullscreen : {:?}", window.fullscreen());
}
"l" => window.set_min_inner_size(match state {
true => Some(WINDOW_SIZE),
false => None,
}),
"m" => window.set_maximized(state),
"p" => window.set_outer_position({
let mut position = window.outer_position().unwrap();
@ -140,12 +128,26 @@ fn main() -> Result<(), impl std::error::Error> {
"s" => {
let _ = window.request_inner_size(match state {
true => PhysicalSize::new(
WINDOW_SIZE.width + 100,
WINDOW_SIZE.height + 100,
WINDOW_SIZE.width + 50,
WINDOW_SIZE.height + 50,
),
false => WINDOW_SIZE,
});
}
"k" => window.set_min_inner_size(match state {
true => Some(PhysicalSize::new(
WINDOW_SIZE.width - 100,
WINDOW_SIZE.height - 100,
)),
false => None,
}),
"o" => window.set_max_inner_size(match state {
true => Some(PhysicalSize::new(
WINDOW_SIZE.width + 100,
WINDOW_SIZE.height + 100,
)),
false => None,
}),
"w" => {
if let Size::Physical(size) = WINDOW_SIZE.into() {
window

View file

@ -135,24 +135,13 @@ impl Ord for MonitorHandle {
impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Do this using the proper fmt API
#[derive(Debug)]
#[allow(dead_code)]
struct MonitorHandle {
name: Option<String>,
size: PhysicalSize<u32>,
position: PhysicalPosition<i32>,
scale_factor: f64,
}
let monitor_id_proxy = MonitorHandle {
name: self.name(),
size: self.size(),
position: self.position(),
scale_factor: self.scale_factor(),
};
monitor_id_proxy.fmt(f)
f.debug_struct("MonitorHandle")
.field("name", &self.name())
.field("size", &self.size())
.field("position", &self.position())
.field("scale_factor", &self.scale_factor())
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz())
.finish_non_exhaustive()
}
}

View file

@ -11,17 +11,17 @@ use core_graphics::display::{
CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayCopyDisplayMode,
};
use icrate::AppKit::NSScreen;
use icrate::Foundation::{ns_string, MainThreadMarker, NSNumber};
use icrate::Foundation::{ns_string, MainThreadMarker, NSNumber, NSPoint, NSRect};
use objc2::{rc::Id, runtime::AnyObject};
use super::ffi;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
#[derive(Clone)]
pub struct VideoMode {
pub(crate) size: (u32, u32),
pub(crate) bit_depth: u16,
pub(crate) refresh_rate_millihertz: u32,
size: PhysicalSize<u32>,
bit_depth: u16,
refresh_rate_millihertz: u32,
pub(crate) monitor: MonitorHandle,
pub(crate) native_mode: NativeDisplayMode,
}
@ -80,7 +80,7 @@ impl Clone for NativeDisplayMode {
impl VideoMode {
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
self.size
}
pub fn bit_depth(&self) -> u16 {
@ -154,26 +154,14 @@ pub fn primary_monitor() -> MonitorHandle {
impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Do this using the proper fmt API
#[derive(Debug)]
#[allow(dead_code)]
struct MonitorHandle {
name: Option<String>,
native_identifier: u32,
size: PhysicalSize<u32>,
position: PhysicalPosition<i32>,
scale_factor: f64,
}
let monitor_id_proxy = MonitorHandle {
name: self.name(),
native_identifier: self.native_identifier(),
size: self.size(),
position: self.position(),
scale_factor: self.scale_factor(),
};
monitor_id_proxy.fmt(f)
f.debug_struct("MonitorHandle")
.field("name", &self.name())
.field("native_identifier", &self.native_identifier())
.field("size", &self.size())
.field("position", &self.position())
.field("scale_factor", &self.scale_factor())
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz())
.finish_non_exhaustive()
}
}
@ -182,6 +170,8 @@ impl MonitorHandle {
MonitorHandle(id)
}
// TODO: Be smarter about this:
// <https://github.com/glfw/glfw/blob/57cbded0760a50b9039ee0cb3f3c14f60145567c/src/cocoa_monitor.m#L44-L126>
pub fn name(&self) -> Option<String> {
let MonitorHandle(display_id) = *self;
let screen_num = CGDisplay::new(display_id).model_number();
@ -203,11 +193,12 @@ impl MonitorHandle {
#[inline]
pub fn position(&self) -> PhysicalPosition<i32> {
// This is already in screen coordinates. If we were using `NSScreen`,
// then a conversion would've been needed:
// flip_window_screen_coordinates(self.ns_screen(mtm)?.frame())
let bounds = unsafe { CGDisplayBounds(self.native_identifier()) };
PhysicalPosition::from_logical::<_, f64>(
(bounds.origin.x as f64, bounds.origin.y as f64),
self.scale_factor(),
)
let position = LogicalPosition::new(bounds.origin.x, bounds.origin.y);
position.to_physical(self.scale_factor())
}
pub fn scale_factor(&self) -> f64 {
@ -268,13 +259,12 @@ impl MonitorHandle {
};
modes.into_iter().map(move |mode| {
let cg_refresh_rate_millihertz =
ffi::CGDisplayModeGetRefreshRate(mode).round() as i64;
let cg_refresh_rate_hertz = ffi::CGDisplayModeGetRefreshRate(mode).round() as i64;
// CGDisplayModeGetRefreshRate returns 0.0 for any display that
// isn't a CRT
let refresh_rate_millihertz = if cg_refresh_rate_millihertz > 0 {
(cg_refresh_rate_millihertz * 1000) as u32
let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 {
(cg_refresh_rate_hertz * 1000) as u32
} else {
refresh_rate_millihertz
};
@ -293,7 +283,7 @@ impl MonitorHandle {
};
VideoMode {
size: (
size: PhysicalSize::new(
ffi::CGDisplayModeGetPixelWidth(mode) as u32,
ffi::CGDisplayModeGetPixelHeight(mode) as u32,
),
@ -339,3 +329,24 @@ pub(crate) fn get_display_id(screen: &NSScreen) -> u32 {
obj.as_u32()
})
}
/// Core graphics screen coordinates are relative to the top-left corner of
/// the so-called "main" display, with y increasing downwards - which is
/// exactly what we want in Winit.
///
/// However, `NSWindow` and `NSScreen` changes these coordinates to:
/// 1. Be relative to the bottom-left corner of the "main" screen.
/// 2. Be relative to the bottom-left corner of the window/screen itself.
/// 3. Have y increasing upwards.
///
/// This conversion happens to be symmetric, so we only need this one function
/// to convert between the two coordinate systems.
pub(crate) fn flip_window_screen_coordinates(frame: NSRect) -> NSPoint {
// It is intentional that we use `CGMainDisplayID` (as opposed to
// `NSScreen::mainScreen`), because that's what the screen coordinates
// are relative to, no matter which display the window is currently on.
let main_screen_height = CGDisplay::main().bounds().size.height;
let y = main_screen_height - frame.size.height - frame.origin.y;
NSPoint::new(frame.origin.x, y)
}

View file

@ -1,7 +1,4 @@
use core_graphics::display::CGDisplay;
use icrate::Foundation::{CGFloat, NSNotFound, NSPoint, NSRange, NSRect, NSUInteger};
use crate::dpi::LogicalPosition;
use icrate::Foundation::{NSNotFound, NSRange, NSUInteger};
// Replace with `!` once stable
#[derive(Debug)]
@ -40,21 +37,3 @@ impl Drop for TraceGuard {
trace!(target: self.module_path, "Completed `{}`", self.called_from_fn);
}
}
// For consistency with other platforms, this will...
// 1. translate the bottom-left window corner into the top-left window corner
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
#[allow(clippy::unnecessary_cast)]
pub fn bottom_left_to_top_left(rect: NSRect) -> f64 {
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height) as f64
}
/// Converts from winit screen-coordinates to macOS screen-coordinates.
/// Winit: top-left is (0, 0) and y increasing downwards
/// macOS: bottom-left is (0, 0) and y increasing upwards
pub fn window_position(position: LogicalPosition<f64>) -> NSPoint {
NSPoint::new(
position.x as CGFloat,
CGDisplay::main().pixels_high() as CGFloat - position.y as CGFloat,
)
}

View file

@ -123,8 +123,8 @@ fn get_left_modifier_code(key: &Key) -> KeyCode {
#[derive(Debug, Default)]
pub struct ViewState {
cursor_state: RefCell<CursorState>,
ime_position: Cell<LogicalPosition<f64>>,
ime_size: Cell<LogicalSize<f64>>,
ime_position: Cell<NSPoint>,
ime_size: Cell<NSSize>,
modifiers: Cell<Modifiers>,
phys_modifiers: RefCell<HashMap<Key, ModLocationMask>>,
tracking_rect: Cell<Option<NSTrackingRectTag>>,
@ -162,6 +162,12 @@ declare_class!(
}
unsafe impl WinitView {
#[method(isFlipped)]
fn is_flipped(&self) -> bool {
// `winit` uses the upper-left corner as the origin.
true
}
#[method(viewDidMoveToWindow)]
fn view_did_move_to_window(&self) {
trace_scope!("viewDidMoveToWindow");
@ -365,14 +371,9 @@ declare_class!(
_actual_range: *mut NSRange,
) -> NSRect {
trace_scope!("firstRectForCharacterRange:actualRange:");
let window = self.window();
let content_rect = window.contentRectForFrameRect(window.frame());
let base_x = content_rect.origin.x as f64;
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
let x = base_x + self.ivars().ime_position.get().x;
let y = base_y - self.ivars().ime_position.get().y;
let LogicalSize { width, height } = self.ivars().ime_size.get();
NSRect::new(NSPoint::new(x as _, y as _), NSSize::new(width, height))
let rect = dbg!(NSRect::new(self.ivars().ime_position.get(), self.ivars().ime_size.get()));
// Return value is expected to be in screen coordinates, so we need a conversion here
unsafe { self.window().convertRectToScreen(self.convertRect_toView(rect, None)) }
}
#[method(insertText:replacementRange:)]
@ -876,11 +877,7 @@ impl WinitView {
}
}
pub(super) fn set_ime_cursor_area(
&self,
position: LogicalPosition<f64>,
size: LogicalSize<f64>,
) {
pub(super) fn set_ime_cursor_area(&self, position: NSPoint, size: NSSize) {
self.ivars().ime_position.set(position);
self.ivars().ime_size.set(size);
let input_context = self.inputContext().expect("input context");
@ -1026,12 +1023,12 @@ impl WinitView {
fn mouse_motion(&self, event: &NSEvent) {
let window_point = unsafe { event.locationInWindow() };
let view_point = self.convertPoint_fromView(window_point, None);
let view_rect = self.frame();
let frame = self.frame();
if view_point.x.is_sign_negative()
|| view_point.y.is_sign_negative()
|| view_point.x > view_rect.size.width
|| view_point.y > view_rect.size.height
|| view_point.x > frame.size.width
|| view_point.y > frame.size.height
{
let mouse_buttons_down = unsafe { NSEvent::pressedMouseButtons() };
if mouse_buttons_down == 0 {
@ -1040,15 +1037,13 @@ impl WinitView {
}
}
let x = view_point.x as f64;
let y = view_rect.size.height as f64 - view_point.y as f64;
let logical_position = LogicalPosition::new(x, y);
let view_point = LogicalPosition::new(view_point.x, view_point.y);
self.update_modifiers(event, false);
self.queue_event(WindowEvent::CursorMoved {
device_id: DEVICE_ID,
position: logical_position.to_physical(self.scale_factor()),
position: view_point.to_physical(self.scale_factor()),
});
}
}

View file

@ -18,7 +18,6 @@ use crate::{
event_loop::EventLoopWindowTarget,
ffi,
monitor::{self, MonitorHandle, VideoMode},
util,
view::WinitView,
window_delegate::WinitWindowDelegate,
Fullscreen, OsError, PlatformCustomCursor,
@ -52,7 +51,7 @@ use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, De
use super::cursor::cursor_from_icon;
use super::ffi::kCGFloatingWindowLevel;
use super::ffi::{kCGNormalWindowLevel, CGSMainConnectionID, CGSSetWindowBackgroundBlurRadius};
use super::monitor::get_display_id;
use super::monitor::{flip_window_screen_coordinates, get_display_id};
pub(crate) struct Window {
window: MainThreadBound<Id<WinitWindow>>,
@ -295,24 +294,25 @@ impl WinitWindow {
let scale_factor = NSScreen::mainScreen(mtm)
.map(|screen| screen.backingScaleFactor() as f64)
.unwrap_or(1.0);
let (width, height) = match attrs.inner_size {
let size = match attrs.inner_size {
Some(size) => {
let logical = size.to_logical(scale_factor);
(logical.width, logical.height)
let size = size.to_logical(scale_factor);
NSSize::new(size.width, size.height)
}
None => (800.0, 600.0),
None => NSSize::new(800.0, 600.0),
};
let (left, bottom) = match attrs.position {
let position = match attrs.position {
Some(position) => {
let logical = util::window_position(position.to_logical(scale_factor));
// macOS wants the position of the bottom left corner,
// but caller is setting the position of top left corner
(logical.x, logical.y - height)
let position = position.to_logical(scale_factor);
flip_window_screen_coordinates(NSRect::new(
NSPoint::new(position.x, position.y),
size,
))
}
// This value is ignored by calling win.center() below
None => (0.0, 0.0),
None => NSPoint::new(0.0, 0.0),
};
NSRect::new(NSPoint::new(left, bottom), NSSize::new(width, height))
NSRect::new(position, size)
}
};
@ -609,52 +609,44 @@ impl WinitWindow {
pub fn pre_present_notify(&self) {}
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let frame_rect = self.frame();
let position = LogicalPosition::new(
frame_rect.origin.x as f64,
util::bottom_left_to_top_left(frame_rect),
);
let scale_factor = self.scale_factor();
Ok(position.to_physical(scale_factor))
let position = flip_window_screen_coordinates(self.frame());
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
}
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let content_rect = self.contentRectForFrameRect(self.frame());
let position = LogicalPosition::new(
content_rect.origin.x as f64,
util::bottom_left_to_top_left(content_rect),
);
let scale_factor = self.scale_factor();
Ok(position.to_physical(scale_factor))
let position = flip_window_screen_coordinates(content_rect);
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
}
pub fn set_outer_position(&self, position: Position) {
let scale_factor = self.scale_factor();
let position = position.to_logical(scale_factor);
self.setFrameTopLeftPoint(util::window_position(position));
let position = position.to_logical(self.scale_factor());
let point = flip_window_screen_coordinates(NSRect::new(
NSPoint::new(position.x, position.y),
self.frame().size,
));
unsafe { self.setFrameOrigin(point) };
}
#[inline]
pub fn inner_size(&self) -> PhysicalSize<u32> {
let frame = self.contentView().unwrap().frame();
let logical: LogicalSize<f64> = (frame.size.width as f64, frame.size.height as f64).into();
let scale_factor = self.scale_factor();
logical.to_physical(scale_factor)
let content_rect = self.contentRectForFrameRect(self.frame());
let logical = LogicalSize::new(content_rect.size.width, content_rect.size.height);
logical.to_physical(self.scale_factor())
}
#[inline]
pub fn outer_size(&self) -> PhysicalSize<u32> {
let frame = self.frame();
let logical: LogicalSize<f64> = (frame.size.width as f64, frame.size.height as f64).into();
let scale_factor = self.scale_factor();
logical.to_physical(scale_factor)
let logical = LogicalSize::new(frame.size.width, frame.size.height);
logical.to_physical(self.scale_factor())
}
#[inline]
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
let scale_factor = self.scale_factor();
let size: LogicalSize<f64> = size.to_logical(scale_factor);
self.setContentSize(NSSize::new(size.width as CGFloat, size.height as CGFloat));
let size = size.to_logical(scale_factor);
self.setContentSize(NSSize::new(size.width, size.height));
None
}
@ -665,26 +657,18 @@ impl WinitWindow {
}));
let min_size = dimensions.to_logical::<CGFloat>(self.scale_factor());
let mut current_rect = self.frame();
let content_rect = self.contentRectForFrameRect(current_rect);
// Convert from client area size to window size
let min_size = NSSize::new(
min_size.width + (current_rect.size.width - content_rect.size.width), // this tends to be 0
min_size.height + (current_rect.size.height - content_rect.size.height),
);
self.setMinSize(min_size);
let min_size = NSSize::new(min_size.width, min_size.height);
unsafe { self.setContentMinSize(min_size) };
// If necessary, resize the window to match constraint
if current_rect.size.width < min_size.width {
current_rect.size.width = min_size.width;
self.setFrame_display(current_rect, false)
let mut current_size = self.contentRectForFrameRect(self.frame()).size;
if current_size.width < min_size.width {
current_size.width = min_size.width;
}
if current_rect.size.height < min_size.height {
// The origin point of a rectangle is at its bottom left in Cocoa.
// To ensure the window's top-left point remains the same:
current_rect.origin.y += current_rect.size.height - min_size.height;
current_rect.size.height = min_size.height;
self.setFrame_display(current_rect, false)
if current_size.height < min_size.height {
current_size.height = min_size.height;
}
self.setContentSize(current_size);
}
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
@ -695,26 +679,18 @@ impl WinitWindow {
let scale_factor = self.scale_factor();
let max_size = dimensions.to_logical::<CGFloat>(scale_factor);
let mut current_rect = self.frame();
let content_rect = self.contentRectForFrameRect(current_rect);
// Convert from client area size to window size
let max_size = NSSize::new(
max_size.width + (current_rect.size.width - content_rect.size.width), // this tends to be 0
max_size.height + (current_rect.size.height - content_rect.size.height),
);
self.setMaxSize(max_size);
let max_size = NSSize::new(max_size.width, max_size.height);
unsafe { self.setContentMaxSize(max_size) };
// If necessary, resize the window to match constraint
if current_rect.size.width > max_size.width {
current_rect.size.width = max_size.width;
self.setFrame_display(current_rect, false)
let mut current_size = self.contentRectForFrameRect(self.frame()).size;
if max_size.width < current_size.width {
current_size.width = max_size.width;
}
if current_rect.size.height > max_size.height {
// The origin point of a rectangle is at its bottom left in Cocoa.
// To ensure the window's top-left point remains the same:
current_rect.origin.y += current_rect.size.height - max_size.height;
current_rect.size.height = max_size.height;
self.setFrame_display(current_rect, false)
if max_size.height < current_size.height {
current_size.height = max_size.height;
}
self.setContentSize(current_size);
}
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
@ -1062,11 +1038,7 @@ impl WinitWindow {
let old_screen = self.screen().unwrap();
if old_screen != new_screen {
let mut screen_frame = new_screen.frame();
// The coordinate system here has its origin at bottom-left
// and Y goes up
screen_frame.origin.y += screen_frame.size.height;
self.setFrameTopLeftPoint(screen_frame.origin);
unsafe { self.setFrameOrigin(new_screen.frame().origin) };
}
}
@ -1291,7 +1263,11 @@ impl WinitWindow {
pub fn set_ime_cursor_area(&self, spot: Position, size: Size) {
let scale_factor = self.scale_factor();
let logical_spot = spot.to_logical(scale_factor);
let logical_spot = NSPoint::new(logical_spot.x, logical_spot.y);
let size = size.to_logical(scale_factor);
let size = NSSize::new(size.width, size.height);
self.view().set_ime_cursor_area(logical_spot, size);
}

View file

@ -7,16 +7,18 @@ use icrate::AppKit::{
NSApplicationPresentationHideMenuBar, NSApplicationPresentationOptions, NSDraggingDestination,
NSFilenamesPboardType, NSPasteboard, NSWindowDelegate, NSWindowOcclusionStateVisible,
};
use icrate::Foundation::{MainThreadMarker, NSArray, NSObject, NSObjectProtocol, NSSize, NSString};
use icrate::Foundation::{
MainThreadMarker, NSArray, NSObject, NSObjectProtocol, NSPoint, NSSize, NSString,
};
use objc2::rc::{autoreleasepool, Id};
use objc2::runtime::{AnyObject, ProtocolObject};
use objc2::{
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
};
use super::monitor::flip_window_screen_coordinates;
use super::{
app_state::AppState,
util,
window::{get_ns_theme, WinitWindow},
Fullscreen,
};
@ -35,7 +37,9 @@ pub(crate) struct State {
initial_fullscreen: Cell<bool>,
// During `windowDidResize`, we use this to only send Moved if the position changed.
previous_position: Cell<Option<(f64, f64)>>,
//
// This is expressed in native screen coordinates.
previous_position: Cell<Option<NSPoint>>,
// Used to prevent redundant events.
previous_scale_factor: Cell<f64>,
@ -450,34 +454,33 @@ impl WinitWindowDelegate {
}
fn queue_static_scale_factor_changed_event(&self) {
let scale_factor = self.ivars().window.scale_factor();
let window = &self.ivars().window;
let scale_factor = window.scale_factor();
if scale_factor == self.ivars().previous_scale_factor.get() {
return;
};
self.ivars().previous_scale_factor.set(scale_factor);
let suggested_size = self.view_size();
let content_size = window.contentRectForFrameRect(window.frame()).size;
let content_size = LogicalSize::new(content_size.width, content_size.height);
AppState::queue_static_scale_factor_changed_event(
self.ivars().window.clone(),
suggested_size.to_physical(scale_factor),
window.clone(),
content_size.to_physical(scale_factor),
scale_factor,
);
}
fn emit_move_event(&self) {
let rect = self.ivars().window.frame();
let x = rect.origin.x as f64;
let y = util::bottom_left_to_top_left(rect);
if self.ivars().previous_position.get() != Some((x, y)) {
self.ivars().previous_position.set(Some((x, y)));
let scale_factor = self.ivars().window.scale_factor();
let physical_pos = LogicalPosition::<f64>::from((x, y)).to_physical(scale_factor);
self.queue_event(WindowEvent::Moved(physical_pos));
let window = &self.ivars().window;
let frame = window.frame();
if self.ivars().previous_position.get() == Some(frame.origin) {
return;
}
}
self.ivars().previous_position.set(Some(frame.origin));
fn view_size(&self) -> LogicalSize<f64> {
let size = self.ivars().window.contentView().unwrap().frame().size;
LogicalSize::new(size.width as f64, size.height as f64)
let position = flip_window_screen_coordinates(frame);
let position =
LogicalPosition::new(position.x, position.y).to_physical(window.scale_factor());
self.queue_event(WindowEvent::Moved(position));
}
}