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:
parent
5a43ea8cd6
commit
4f6fd44c6c
9 changed files with 173 additions and 217 deletions
|
|
@ -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" },
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue