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::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 = "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::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)]
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::LogicalSize;
|
||||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
use winit::keyboard::{Key, NamedKey};
|
use winit::keyboard::{Key, NamedKey};
|
||||||
|
|
@ -126,7 +126,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||||
"i" => {
|
"i" => {
|
||||||
with_min_size = !with_min_size;
|
with_min_size = !with_min_size;
|
||||||
let min_size = if with_min_size {
|
let min_size = if with_min_size {
|
||||||
Some(PhysicalSize::new(100, 100))
|
Some(LogicalSize::new(100, 100))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
@ -139,7 +139,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||||
"a" => {
|
"a" => {
|
||||||
with_max_size = !with_max_size;
|
with_max_size = !with_max_size;
|
||||||
let max_size = if with_max_size {
|
let max_size = if with_max_size {
|
||||||
Some(PhysicalSize::new(200, 200))
|
Some(LogicalSize::new(200, 200))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -96,21 +96,13 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||||
)),
|
)),
|
||||||
(false, _) => None,
|
(false, _) => None,
|
||||||
}),
|
}),
|
||||||
"l" if state => {
|
ch @ ("g" | "l") => {
|
||||||
if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked)
|
let mode = match (ch, state) {
|
||||||
{
|
("l", true) => CursorGrabMode::Locked,
|
||||||
println!("error: {err}");
|
("g", true) => CursorGrabMode::Confined,
|
||||||
}
|
(_, _) => CursorGrabMode::None,
|
||||||
}
|
};
|
||||||
"g" if state => {
|
if let Err(err) = window.set_cursor_grab(mode) {
|
||||||
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) {
|
|
||||||
println!("error: {err}");
|
println!("error: {err}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,10 +115,6 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||||
println!("-> inner_size : {:?}", window.inner_size());
|
println!("-> inner_size : {:?}", window.inner_size());
|
||||||
println!("-> fullscreen : {:?}", window.fullscreen());
|
println!("-> fullscreen : {:?}", window.fullscreen());
|
||||||
}
|
}
|
||||||
"l" => window.set_min_inner_size(match state {
|
|
||||||
true => Some(WINDOW_SIZE),
|
|
||||||
false => None,
|
|
||||||
}),
|
|
||||||
"m" => window.set_maximized(state),
|
"m" => window.set_maximized(state),
|
||||||
"p" => window.set_outer_position({
|
"p" => window.set_outer_position({
|
||||||
let mut position = window.outer_position().unwrap();
|
let mut position = window.outer_position().unwrap();
|
||||||
|
|
@ -140,12 +128,26 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||||
"s" => {
|
"s" => {
|
||||||
let _ = window.request_inner_size(match state {
|
let _ = window.request_inner_size(match state {
|
||||||
true => PhysicalSize::new(
|
true => PhysicalSize::new(
|
||||||
WINDOW_SIZE.width + 100,
|
WINDOW_SIZE.width + 50,
|
||||||
WINDOW_SIZE.height + 100,
|
WINDOW_SIZE.height + 50,
|
||||||
),
|
),
|
||||||
false => WINDOW_SIZE,
|
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" => {
|
"w" => {
|
||||||
if let Size::Physical(size) = WINDOW_SIZE.into() {
|
if let Size::Physical(size) = WINDOW_SIZE.into() {
|
||||||
window
|
window
|
||||||
|
|
|
||||||
|
|
@ -135,24 +135,13 @@ impl Ord for MonitorHandle {
|
||||||
|
|
||||||
impl fmt::Debug for MonitorHandle {
|
impl fmt::Debug for MonitorHandle {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
// TODO: Do this using the proper fmt API
|
f.debug_struct("MonitorHandle")
|
||||||
#[derive(Debug)]
|
.field("name", &self.name())
|
||||||
#[allow(dead_code)]
|
.field("size", &self.size())
|
||||||
struct MonitorHandle {
|
.field("position", &self.position())
|
||||||
name: Option<String>,
|
.field("scale_factor", &self.scale_factor())
|
||||||
size: PhysicalSize<u32>,
|
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz())
|
||||||
position: PhysicalPosition<i32>,
|
.finish_non_exhaustive()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,17 @@ use core_graphics::display::{
|
||||||
CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayCopyDisplayMode,
|
CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayCopyDisplayMode,
|
||||||
};
|
};
|
||||||
use icrate::AppKit::NSScreen;
|
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 objc2::{rc::Id, runtime::AnyObject};
|
||||||
|
|
||||||
use super::ffi;
|
use super::ffi;
|
||||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VideoMode {
|
pub struct VideoMode {
|
||||||
pub(crate) size: (u32, u32),
|
size: PhysicalSize<u32>,
|
||||||
pub(crate) bit_depth: u16,
|
bit_depth: u16,
|
||||||
pub(crate) refresh_rate_millihertz: u32,
|
refresh_rate_millihertz: u32,
|
||||||
pub(crate) monitor: MonitorHandle,
|
pub(crate) monitor: MonitorHandle,
|
||||||
pub(crate) native_mode: NativeDisplayMode,
|
pub(crate) native_mode: NativeDisplayMode,
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +80,7 @@ impl Clone for NativeDisplayMode {
|
||||||
|
|
||||||
impl VideoMode {
|
impl VideoMode {
|
||||||
pub fn size(&self) -> PhysicalSize<u32> {
|
pub fn size(&self) -> PhysicalSize<u32> {
|
||||||
self.size.into()
|
self.size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bit_depth(&self) -> u16 {
|
pub fn bit_depth(&self) -> u16 {
|
||||||
|
|
@ -154,26 +154,14 @@ pub fn primary_monitor() -> MonitorHandle {
|
||||||
|
|
||||||
impl fmt::Debug for MonitorHandle {
|
impl fmt::Debug for MonitorHandle {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
// TODO: Do this using the proper fmt API
|
f.debug_struct("MonitorHandle")
|
||||||
#[derive(Debug)]
|
.field("name", &self.name())
|
||||||
#[allow(dead_code)]
|
.field("native_identifier", &self.native_identifier())
|
||||||
struct MonitorHandle {
|
.field("size", &self.size())
|
||||||
name: Option<String>,
|
.field("position", &self.position())
|
||||||
native_identifier: u32,
|
.field("scale_factor", &self.scale_factor())
|
||||||
size: PhysicalSize<u32>,
|
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz())
|
||||||
position: PhysicalPosition<i32>,
|
.finish_non_exhaustive()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,6 +170,8 @@ impl MonitorHandle {
|
||||||
MonitorHandle(id)
|
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> {
|
pub fn name(&self) -> Option<String> {
|
||||||
let MonitorHandle(display_id) = *self;
|
let MonitorHandle(display_id) = *self;
|
||||||
let screen_num = CGDisplay::new(display_id).model_number();
|
let screen_num = CGDisplay::new(display_id).model_number();
|
||||||
|
|
@ -203,11 +193,12 @@ impl MonitorHandle {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
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()) };
|
let bounds = unsafe { CGDisplayBounds(self.native_identifier()) };
|
||||||
PhysicalPosition::from_logical::<_, f64>(
|
let position = LogicalPosition::new(bounds.origin.x, bounds.origin.y);
|
||||||
(bounds.origin.x as f64, bounds.origin.y as f64),
|
position.to_physical(self.scale_factor())
|
||||||
self.scale_factor(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scale_factor(&self) -> f64 {
|
pub fn scale_factor(&self) -> f64 {
|
||||||
|
|
@ -268,13 +259,12 @@ impl MonitorHandle {
|
||||||
};
|
};
|
||||||
|
|
||||||
modes.into_iter().map(move |mode| {
|
modes.into_iter().map(move |mode| {
|
||||||
let cg_refresh_rate_millihertz =
|
let cg_refresh_rate_hertz = ffi::CGDisplayModeGetRefreshRate(mode).round() as i64;
|
||||||
ffi::CGDisplayModeGetRefreshRate(mode).round() as i64;
|
|
||||||
|
|
||||||
// CGDisplayModeGetRefreshRate returns 0.0 for any display that
|
// CGDisplayModeGetRefreshRate returns 0.0 for any display that
|
||||||
// isn't a CRT
|
// isn't a CRT
|
||||||
let refresh_rate_millihertz = if cg_refresh_rate_millihertz > 0 {
|
let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 {
|
||||||
(cg_refresh_rate_millihertz * 1000) as u32
|
(cg_refresh_rate_hertz * 1000) as u32
|
||||||
} else {
|
} else {
|
||||||
refresh_rate_millihertz
|
refresh_rate_millihertz
|
||||||
};
|
};
|
||||||
|
|
@ -293,7 +283,7 @@ impl MonitorHandle {
|
||||||
};
|
};
|
||||||
|
|
||||||
VideoMode {
|
VideoMode {
|
||||||
size: (
|
size: PhysicalSize::new(
|
||||||
ffi::CGDisplayModeGetPixelWidth(mode) as u32,
|
ffi::CGDisplayModeGetPixelWidth(mode) as u32,
|
||||||
ffi::CGDisplayModeGetPixelHeight(mode) as u32,
|
ffi::CGDisplayModeGetPixelHeight(mode) as u32,
|
||||||
),
|
),
|
||||||
|
|
@ -339,3 +329,24 @@ pub(crate) fn get_display_id(screen: &NSScreen) -> u32 {
|
||||||
obj.as_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::{NSNotFound, NSRange, NSUInteger};
|
||||||
use icrate::Foundation::{CGFloat, NSNotFound, NSPoint, NSRange, NSRect, NSUInteger};
|
|
||||||
|
|
||||||
use crate::dpi::LogicalPosition;
|
|
||||||
|
|
||||||
// Replace with `!` once stable
|
// Replace with `!` once stable
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -40,21 +37,3 @@ impl Drop for TraceGuard {
|
||||||
trace!(target: self.module_path, "Completed `{}`", self.called_from_fn);
|
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)]
|
#[derive(Debug, Default)]
|
||||||
pub struct ViewState {
|
pub struct ViewState {
|
||||||
cursor_state: RefCell<CursorState>,
|
cursor_state: RefCell<CursorState>,
|
||||||
ime_position: Cell<LogicalPosition<f64>>,
|
ime_position: Cell<NSPoint>,
|
||||||
ime_size: Cell<LogicalSize<f64>>,
|
ime_size: Cell<NSSize>,
|
||||||
modifiers: Cell<Modifiers>,
|
modifiers: Cell<Modifiers>,
|
||||||
phys_modifiers: RefCell<HashMap<Key, ModLocationMask>>,
|
phys_modifiers: RefCell<HashMap<Key, ModLocationMask>>,
|
||||||
tracking_rect: Cell<Option<NSTrackingRectTag>>,
|
tracking_rect: Cell<Option<NSTrackingRectTag>>,
|
||||||
|
|
@ -162,6 +162,12 @@ declare_class!(
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl WinitView {
|
unsafe impl WinitView {
|
||||||
|
#[method(isFlipped)]
|
||||||
|
fn is_flipped(&self) -> bool {
|
||||||
|
// `winit` uses the upper-left corner as the origin.
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[method(viewDidMoveToWindow)]
|
#[method(viewDidMoveToWindow)]
|
||||||
fn view_did_move_to_window(&self) {
|
fn view_did_move_to_window(&self) {
|
||||||
trace_scope!("viewDidMoveToWindow");
|
trace_scope!("viewDidMoveToWindow");
|
||||||
|
|
@ -365,14 +371,9 @@ declare_class!(
|
||||||
_actual_range: *mut NSRange,
|
_actual_range: *mut NSRange,
|
||||||
) -> NSRect {
|
) -> NSRect {
|
||||||
trace_scope!("firstRectForCharacterRange:actualRange:");
|
trace_scope!("firstRectForCharacterRange:actualRange:");
|
||||||
let window = self.window();
|
let rect = dbg!(NSRect::new(self.ivars().ime_position.get(), self.ivars().ime_size.get()));
|
||||||
let content_rect = window.contentRectForFrameRect(window.frame());
|
// Return value is expected to be in screen coordinates, so we need a conversion here
|
||||||
let base_x = content_rect.origin.x as f64;
|
unsafe { self.window().convertRectToScreen(self.convertRect_toView(rect, None)) }
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(insertText:replacementRange:)]
|
#[method(insertText:replacementRange:)]
|
||||||
|
|
@ -876,11 +877,7 @@ impl WinitView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_ime_cursor_area(
|
pub(super) fn set_ime_cursor_area(&self, position: NSPoint, size: NSSize) {
|
||||||
&self,
|
|
||||||
position: LogicalPosition<f64>,
|
|
||||||
size: LogicalSize<f64>,
|
|
||||||
) {
|
|
||||||
self.ivars().ime_position.set(position);
|
self.ivars().ime_position.set(position);
|
||||||
self.ivars().ime_size.set(size);
|
self.ivars().ime_size.set(size);
|
||||||
let input_context = self.inputContext().expect("input context");
|
let input_context = self.inputContext().expect("input context");
|
||||||
|
|
@ -1026,12 +1023,12 @@ impl WinitView {
|
||||||
fn mouse_motion(&self, event: &NSEvent) {
|
fn mouse_motion(&self, event: &NSEvent) {
|
||||||
let window_point = unsafe { event.locationInWindow() };
|
let window_point = unsafe { event.locationInWindow() };
|
||||||
let view_point = self.convertPoint_fromView(window_point, None);
|
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()
|
if view_point.x.is_sign_negative()
|
||||||
|| view_point.y.is_sign_negative()
|
|| view_point.y.is_sign_negative()
|
||||||
|| view_point.x > view_rect.size.width
|
|| view_point.x > frame.size.width
|
||||||
|| view_point.y > view_rect.size.height
|
|| view_point.y > frame.size.height
|
||||||
{
|
{
|
||||||
let mouse_buttons_down = unsafe { NSEvent::pressedMouseButtons() };
|
let mouse_buttons_down = unsafe { NSEvent::pressedMouseButtons() };
|
||||||
if mouse_buttons_down == 0 {
|
if mouse_buttons_down == 0 {
|
||||||
|
|
@ -1040,15 +1037,13 @@ impl WinitView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = view_point.x as f64;
|
let view_point = LogicalPosition::new(view_point.x, view_point.y);
|
||||||
let y = view_rect.size.height as f64 - view_point.y as f64;
|
|
||||||
let logical_position = LogicalPosition::new(x, y);
|
|
||||||
|
|
||||||
self.update_modifiers(event, false);
|
self.update_modifiers(event, false);
|
||||||
|
|
||||||
self.queue_event(WindowEvent::CursorMoved {
|
self.queue_event(WindowEvent::CursorMoved {
|
||||||
device_id: DEVICE_ID,
|
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,
|
event_loop::EventLoopWindowTarget,
|
||||||
ffi,
|
ffi,
|
||||||
monitor::{self, MonitorHandle, VideoMode},
|
monitor::{self, MonitorHandle, VideoMode},
|
||||||
util,
|
|
||||||
view::WinitView,
|
view::WinitView,
|
||||||
window_delegate::WinitWindowDelegate,
|
window_delegate::WinitWindowDelegate,
|
||||||
Fullscreen, OsError, PlatformCustomCursor,
|
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::cursor::cursor_from_icon;
|
||||||
use super::ffi::kCGFloatingWindowLevel;
|
use super::ffi::kCGFloatingWindowLevel;
|
||||||
use super::ffi::{kCGNormalWindowLevel, CGSMainConnectionID, CGSSetWindowBackgroundBlurRadius};
|
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 {
|
pub(crate) struct Window {
|
||||||
window: MainThreadBound<Id<WinitWindow>>,
|
window: MainThreadBound<Id<WinitWindow>>,
|
||||||
|
|
@ -295,24 +294,25 @@ impl WinitWindow {
|
||||||
let scale_factor = NSScreen::mainScreen(mtm)
|
let scale_factor = NSScreen::mainScreen(mtm)
|
||||||
.map(|screen| screen.backingScaleFactor() as f64)
|
.map(|screen| screen.backingScaleFactor() as f64)
|
||||||
.unwrap_or(1.0);
|
.unwrap_or(1.0);
|
||||||
let (width, height) = match attrs.inner_size {
|
let size = match attrs.inner_size {
|
||||||
Some(size) => {
|
Some(size) => {
|
||||||
let logical = size.to_logical(scale_factor);
|
let size = size.to_logical(scale_factor);
|
||||||
(logical.width, logical.height)
|
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) => {
|
Some(position) => {
|
||||||
let logical = util::window_position(position.to_logical(scale_factor));
|
let position = position.to_logical(scale_factor);
|
||||||
// macOS wants the position of the bottom left corner,
|
flip_window_screen_coordinates(NSRect::new(
|
||||||
// but caller is setting the position of top left corner
|
NSPoint::new(position.x, position.y),
|
||||||
(logical.x, logical.y - height)
|
size,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
// This value is ignored by calling win.center() below
|
// 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 pre_present_notify(&self) {}
|
||||||
|
|
||||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||||
let frame_rect = self.frame();
|
let position = flip_window_screen_coordinates(self.frame());
|
||||||
let position = LogicalPosition::new(
|
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||||
let content_rect = self.contentRectForFrameRect(self.frame());
|
let content_rect = self.contentRectForFrameRect(self.frame());
|
||||||
let position = LogicalPosition::new(
|
let position = flip_window_screen_coordinates(content_rect);
|
||||||
content_rect.origin.x as f64,
|
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
|
||||||
util::bottom_left_to_top_left(content_rect),
|
|
||||||
);
|
|
||||||
let scale_factor = self.scale_factor();
|
|
||||||
Ok(position.to_physical(scale_factor))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_outer_position(&self, position: Position) {
|
pub fn set_outer_position(&self, position: Position) {
|
||||||
let scale_factor = self.scale_factor();
|
let position = position.to_logical(self.scale_factor());
|
||||||
let position = position.to_logical(scale_factor);
|
let point = flip_window_screen_coordinates(NSRect::new(
|
||||||
self.setFrameTopLeftPoint(util::window_position(position));
|
NSPoint::new(position.x, position.y),
|
||||||
|
self.frame().size,
|
||||||
|
));
|
||||||
|
unsafe { self.setFrameOrigin(point) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||||
let frame = self.contentView().unwrap().frame();
|
let content_rect = self.contentRectForFrameRect(self.frame());
|
||||||
let logical: LogicalSize<f64> = (frame.size.width as f64, frame.size.height as f64).into();
|
let logical = LogicalSize::new(content_rect.size.width, content_rect.size.height);
|
||||||
let scale_factor = self.scale_factor();
|
logical.to_physical(self.scale_factor())
|
||||||
logical.to_physical(scale_factor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||||
let frame = self.frame();
|
let frame = self.frame();
|
||||||
let logical: LogicalSize<f64> = (frame.size.width as f64, frame.size.height as f64).into();
|
let logical = LogicalSize::new(frame.size.width, frame.size.height);
|
||||||
let scale_factor = self.scale_factor();
|
logical.to_physical(self.scale_factor())
|
||||||
logical.to_physical(scale_factor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||||
let scale_factor = self.scale_factor();
|
let scale_factor = self.scale_factor();
|
||||||
let size: LogicalSize<f64> = size.to_logical(scale_factor);
|
let size = size.to_logical(scale_factor);
|
||||||
self.setContentSize(NSSize::new(size.width as CGFloat, size.height as CGFloat));
|
self.setContentSize(NSSize::new(size.width, size.height));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -665,26 +657,18 @@ impl WinitWindow {
|
||||||
}));
|
}));
|
||||||
let min_size = dimensions.to_logical::<CGFloat>(self.scale_factor());
|
let min_size = dimensions.to_logical::<CGFloat>(self.scale_factor());
|
||||||
|
|
||||||
let mut current_rect = self.frame();
|
let min_size = NSSize::new(min_size.width, min_size.height);
|
||||||
let content_rect = self.contentRectForFrameRect(current_rect);
|
unsafe { self.setContentMinSize(min_size) };
|
||||||
// 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);
|
|
||||||
// If necessary, resize the window to match constraint
|
// If necessary, resize the window to match constraint
|
||||||
if current_rect.size.width < min_size.width {
|
let mut current_size = self.contentRectForFrameRect(self.frame()).size;
|
||||||
current_rect.size.width = min_size.width;
|
if current_size.width < min_size.width {
|
||||||
self.setFrame_display(current_rect, false)
|
current_size.width = min_size.width;
|
||||||
}
|
}
|
||||||
if current_rect.size.height < min_size.height {
|
if current_size.height < min_size.height {
|
||||||
// The origin point of a rectangle is at its bottom left in Cocoa.
|
current_size.height = min_size.height;
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
self.setContentSize(current_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||||||
|
|
@ -695,26 +679,18 @@ impl WinitWindow {
|
||||||
let scale_factor = self.scale_factor();
|
let scale_factor = self.scale_factor();
|
||||||
let max_size = dimensions.to_logical::<CGFloat>(scale_factor);
|
let max_size = dimensions.to_logical::<CGFloat>(scale_factor);
|
||||||
|
|
||||||
let mut current_rect = self.frame();
|
let max_size = NSSize::new(max_size.width, max_size.height);
|
||||||
let content_rect = self.contentRectForFrameRect(current_rect);
|
unsafe { self.setContentMaxSize(max_size) };
|
||||||
// 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);
|
|
||||||
// If necessary, resize the window to match constraint
|
// If necessary, resize the window to match constraint
|
||||||
if current_rect.size.width > max_size.width {
|
let mut current_size = self.contentRectForFrameRect(self.frame()).size;
|
||||||
current_rect.size.width = max_size.width;
|
if max_size.width < current_size.width {
|
||||||
self.setFrame_display(current_rect, false)
|
current_size.width = max_size.width;
|
||||||
}
|
}
|
||||||
if current_rect.size.height > max_size.height {
|
if max_size.height < current_size.height {
|
||||||
// The origin point of a rectangle is at its bottom left in Cocoa.
|
current_size.height = max_size.height;
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
self.setContentSize(current_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||||
|
|
@ -1062,11 +1038,7 @@ impl WinitWindow {
|
||||||
|
|
||||||
let old_screen = self.screen().unwrap();
|
let old_screen = self.screen().unwrap();
|
||||||
if old_screen != new_screen {
|
if old_screen != new_screen {
|
||||||
let mut screen_frame = new_screen.frame();
|
unsafe { self.setFrameOrigin(new_screen.frame().origin) };
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1291,7 +1263,11 @@ impl WinitWindow {
|
||||||
pub fn set_ime_cursor_area(&self, spot: Position, size: Size) {
|
pub fn set_ime_cursor_area(&self, spot: Position, size: Size) {
|
||||||
let scale_factor = self.scale_factor();
|
let scale_factor = self.scale_factor();
|
||||||
let logical_spot = spot.to_logical(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 = size.to_logical(scale_factor);
|
||||||
|
let size = NSSize::new(size.width, size.height);
|
||||||
|
|
||||||
self.view().set_ime_cursor_area(logical_spot, size);
|
self.view().set_ime_cursor_area(logical_spot, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,18 @@ use icrate::AppKit::{
|
||||||
NSApplicationPresentationHideMenuBar, NSApplicationPresentationOptions, NSDraggingDestination,
|
NSApplicationPresentationHideMenuBar, NSApplicationPresentationOptions, NSDraggingDestination,
|
||||||
NSFilenamesPboardType, NSPasteboard, NSWindowDelegate, NSWindowOcclusionStateVisible,
|
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::rc::{autoreleasepool, Id};
|
||||||
use objc2::runtime::{AnyObject, ProtocolObject};
|
use objc2::runtime::{AnyObject, ProtocolObject};
|
||||||
use objc2::{
|
use objc2::{
|
||||||
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
|
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::monitor::flip_window_screen_coordinates;
|
||||||
use super::{
|
use super::{
|
||||||
app_state::AppState,
|
app_state::AppState,
|
||||||
util,
|
|
||||||
window::{get_ns_theme, WinitWindow},
|
window::{get_ns_theme, WinitWindow},
|
||||||
Fullscreen,
|
Fullscreen,
|
||||||
};
|
};
|
||||||
|
|
@ -35,7 +37,9 @@ pub(crate) struct State {
|
||||||
initial_fullscreen: Cell<bool>,
|
initial_fullscreen: Cell<bool>,
|
||||||
|
|
||||||
// During `windowDidResize`, we use this to only send Moved if the position changed.
|
// 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.
|
// Used to prevent redundant events.
|
||||||
previous_scale_factor: Cell<f64>,
|
previous_scale_factor: Cell<f64>,
|
||||||
|
|
@ -450,34 +454,33 @@ impl WinitWindowDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queue_static_scale_factor_changed_event(&self) {
|
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() {
|
if scale_factor == self.ivars().previous_scale_factor.get() {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.ivars().previous_scale_factor.set(scale_factor);
|
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(
|
AppState::queue_static_scale_factor_changed_event(
|
||||||
self.ivars().window.clone(),
|
window.clone(),
|
||||||
suggested_size.to_physical(scale_factor),
|
content_size.to_physical(scale_factor),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_move_event(&self) {
|
fn emit_move_event(&self) {
|
||||||
let rect = self.ivars().window.frame();
|
let window = &self.ivars().window;
|
||||||
let x = rect.origin.x as f64;
|
let frame = window.frame();
|
||||||
let y = util::bottom_left_to_top_left(rect);
|
if self.ivars().previous_position.get() == Some(frame.origin) {
|
||||||
if self.ivars().previous_position.get() != Some((x, y)) {
|
return;
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
self.ivars().previous_position.set(Some(frame.origin));
|
||||||
|
|
||||||
fn view_size(&self) -> LogicalSize<f64> {
|
let position = flip_window_screen_coordinates(frame);
|
||||||
let size = self.ivars().window.contentView().unwrap().frame().size;
|
let position =
|
||||||
LogicalSize::new(size.width as f64, size.height as f64)
|
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