diff --git a/CHANGELOG.md b/CHANGELOG.md index 71526355..69b9b20d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ - Properly calculate the minimum and maximum window size on Windows, including window decorations. - Map more `MouseCursor` variants to cursor icons on Windows. - Discard the stray mouse down event being delivered after window resize on macOS. +- Corrected `get_position` on macOS to return outer frame position, not content area position. +- Corrected `set_position` on macOS to set outer frame position, not content area position. +- Added `get_inner_position` method to `Window`, which gets the position of the window's client area. This is implemented on all applicable platforms (all desktop platforms other than Wayland, where this isn't possible). # Version 0.12.0 (2018-04-06) diff --git a/src/platform/android/mod.rs b/src/platform/android/mod.rs index 52ef3903..7d1e4600 100644 --- a/src/platform/android/mod.rs +++ b/src/platform/android/mod.rs @@ -243,6 +243,11 @@ impl Window { None } + #[inline] + pub fn get_inner_position(&self) -> Option<(i32, i32)> { + None + } + #[inline] pub fn set_position(&self, _x: i32, _y: i32) { } diff --git a/src/platform/emscripten/mod.rs b/src/platform/emscripten/mod.rs index 90591957..23a229d2 100644 --- a/src/platform/emscripten/mod.rs +++ b/src/platform/emscripten/mod.rs @@ -417,6 +417,11 @@ impl Window { Some((0, 0)) } + #[inline] + pub fn get_inner_position(&self) -> Option<(i32, i32)> { + Some((0, 0)) + } + #[inline] pub fn set_position(&self, _: i32, _: i32) { } diff --git a/src/platform/ios/mod.rs b/src/platform/ios/mod.rs index c83a1fc0..c32a0f89 100644 --- a/src/platform/ios/mod.rs +++ b/src/platform/ios/mod.rs @@ -288,6 +288,11 @@ impl Window { None } + #[inline] + pub fn get_inner_position(&self) -> Option<(i32, i32)> { + None + } + #[inline] pub fn set_position(&self, _x: i32, _y: i32) { } diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 27d9f064..242690d8 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -162,6 +162,14 @@ impl Window { } } + #[inline] + pub fn get_inner_position(&self) -> Option<(i32, i32)> { + match self { + &Window::X(ref m) => m.get_inner_position(), + &Window::Wayland(ref m) => m.get_inner_position(), + } + } + #[inline] pub fn set_position(&self, x: i32, y: i32) { match self { diff --git a/src/platform/linux/wayland/window.rs b/src/platform/linux/wayland/window.rs index 6cfa10d3..c6aac44e 100644 --- a/src/platform/linux/wayland/window.rs +++ b/src/platform/linux/wayland/window.rs @@ -113,6 +113,12 @@ impl Window { None } + #[inline] + pub fn get_inner_position(&self) -> Option<(i32, i32)> { + // Not possible with wayland + None + } + #[inline] pub fn set_position(&self, _x: i32, _y: i32) { // Not possible with wayland diff --git a/src/platform/linux/x11/window.rs b/src/platform/linux/x11/window.rs index 7d4ef6d3..56fe14f2 100644 --- a/src/platform/linux/x11/window.rs +++ b/src/platform/linux/x11/window.rs @@ -919,6 +919,11 @@ impl Window2 { self.get_geometry().map(|geo| geo.get_position()) } + #[inline] + pub fn get_inner_position(&self) -> Option<(i32, i32)> { + self.get_geometry().map(|geo| geo.get_inner_position()) + } + pub fn set_position(&self, mut x: i32, mut y: i32) { if let Some(ref wm_name) = self.wm_name { // There are a few WMs that set client area position rather than window position, so diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index bdae5aea..2b4dba5d 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -543,41 +543,54 @@ impl Window2 { unsafe { NSWindow::orderOut_(*self.window, nil); } } - pub fn get_position(&self) -> Option<(i32, i32)> { - unsafe { - let content_rect = NSWindow::contentRectForFrameRect_(*self.window, NSWindow::frame(*self.window)); + // 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 + fn bottom_left_to_top_left(rect: NSRect) -> i32 { + (CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)) as _ + } - // TODO: consider extrapolating the calculations for the y axis to - // a private method - Some((content_rect.origin.x as i32, (CGDisplay::main().pixels_high() as f64 - (content_rect.origin.y + content_rect.size.height)) as i32)) - } + pub fn get_position(&self) -> Option<(i32, i32)> { + let frame_rect = unsafe { NSWindow::frame(*self.window) }; + Some(( + frame_rect.origin.x as i32, + Self::bottom_left_to_top_left(frame_rect), + )) + } + + pub fn get_inner_position(&self) -> Option<(i32, i32)> { + let content_rect = unsafe { + NSWindow::contentRectForFrameRect_( + *self.window, + NSWindow::frame(*self.window), + ) + }; + Some(( + content_rect.origin.x as i32, + Self::bottom_left_to_top_left(content_rect), + )) } pub fn set_position(&self, x: i32, y: i32) { + let dummy = NSRect::new( + NSPoint::new( + x as f64, + // While it's true that we're setting the top-left position, it still needs to be + // in a bottom-left coordinate system. + CGDisplay::main().pixels_high() as f64 - y as f64, + ), + NSSize::new(0f64, 0f64), + ); unsafe { - let frame = NSWindow::frame(*self.view); - - // NOTE: `setFrameOrigin` might not give desirable results when - // setting window, as it treats bottom left as origin. - // `setFrameTopLeftPoint` treats top left as origin (duh), but - // does not equal the value returned by `get_window_position` - // (there is a difference by 22 for me on yosemite) - - // TODO: consider extrapolating the calculations for the y axis to - // a private method - let dummy = NSRect::new(NSPoint::new(x as f64, CGDisplay::main().pixels_high() as f64 - (frame.size.height + y as f64)), NSSize::new(0f64, 0f64)); - let conv = NSWindow::frameRectForContentRect_(*self.window, dummy); - - // NSWindow::setFrameTopLeftPoint_(*self.window, conv.origin); - NSWindow::setFrameOrigin_(*self.window, conv.origin); + NSWindow::setFrameTopLeftPoint_(*self.window, dummy.origin); } } #[inline] pub fn get_inner_size(&self) -> Option<(u32, u32)> { + let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels unsafe { let view_frame = NSView::frame(*self.view); - let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels Some(((view_frame.size.width*factor) as u32, (view_frame.size.height*factor) as u32)) } } diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index 4ed36749..df426df8 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -129,6 +129,17 @@ impl Window { Some((rect.left as i32, rect.top as i32)) } + pub fn get_inner_position(&self) -> Option<(i32, i32)> { + use std::mem; + + let mut position: POINT = unsafe{ mem::zeroed() }; + if unsafe{ winuser::ClientToScreen(self.window.0, &mut position) } == 0 { + return None; + } + + Some((position.x, position.y)) + } + /// See the docs in the crate root file. pub fn set_position(&self, x: i32, y: i32) { unsafe { diff --git a/src/window.rs b/src/window.rs index 7724d079..1d5ce02e 100644 --- a/src/window.rs +++ b/src/window.rs @@ -181,6 +181,15 @@ impl Window { self.window.get_position() } + /// Returns the position of the top-left hand corner of the window's client area relative to the + /// top-left hand corner of the desktop. + /// + /// The same conditions that apply to `get_position` apply to this method. + #[inline] + pub fn get_inner_position(&self) -> Option<(i32, i32)> { + self.window.get_inner_position() + } + /// Modifies the position of the window. /// /// See `get_position` for more information about the coordinates.