mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-24 06:11:30 +11:00
Add WindowBuilder::with_outer_position (#1866)
This commit is contained in:
parent
86748fbc68
commit
0d634a0061
|
@ -15,6 +15,7 @@
|
||||||
- On macOS, fix objects captured by the event loop closure not being dropped on panic.
|
- On macOS, fix objects captured by the event loop closure not being dropped on panic.
|
||||||
- On Windows, fixed `WindowEvent::ThemeChanged` not properly firing and fixed `Window::theme` returning the wrong theme.
|
- On Windows, fixed `WindowEvent::ThemeChanged` not properly firing and fixed `Window::theme` returning the wrong theme.
|
||||||
- On Web, added support for `DeviceEvent::MouseMotion` to listen for relative mouse movements.
|
- On Web, added support for `DeviceEvent::MouseMotion` to listen for relative mouse movements.
|
||||||
|
- Added `WindowBuilder::with_position` to allow setting the position of a `Window` on creation. Supported on Windows, macOS and X11.
|
||||||
- Added `Window::drag_window`. Implemented on Windows, macOS, X11 and Wayland.
|
- Added `Window::drag_window`. Implemented on Windows, macOS, X11 and Wayland.
|
||||||
- On X11, bump `mio` to 0.7.
|
- On X11, bump `mio` to 0.7.
|
||||||
|
|
||||||
|
|
|
@ -190,6 +190,24 @@ impl<'a> NormalHints<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||||
|
if has_flag(self.size_hints.flags, ffi::PPosition) {
|
||||||
|
Some((self.size_hints.x as i32, self.size_hints.y as i32))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_position(&mut self, position: Option<(i32, i32)>) {
|
||||||
|
if let Some((x, y)) = position {
|
||||||
|
self.size_hints.flags |= ffi::PPosition;
|
||||||
|
self.size_hints.x = x as c_int;
|
||||||
|
self.size_hints.y = y as c_int;
|
||||||
|
} else {
|
||||||
|
self.size_hints.flags &= !ffi::PPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WARNING: This hint is obsolete
|
// WARNING: This hint is obsolete
|
||||||
pub fn set_size(&mut self, size: Option<(u32, u32)>) {
|
pub fn set_size(&mut self, size: Option<(u32, u32)>) {
|
||||||
if let Some((width, height)) = size {
|
if let Some((width, height)) = size {
|
||||||
|
|
|
@ -23,6 +23,7 @@ pub use self::{
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
mem::{self, MaybeUninit},
|
mem::{self, MaybeUninit},
|
||||||
|
ops::BitAnd,
|
||||||
os::raw::*,
|
os::raw::*,
|
||||||
ptr,
|
ptr,
|
||||||
};
|
};
|
||||||
|
@ -39,6 +40,13 @@ pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_flag<T>(bitset: T, flag: T) -> bool
|
||||||
|
where
|
||||||
|
T: Copy + PartialEq + BitAnd<T, Output = T>,
|
||||||
|
{
|
||||||
|
bitset & flag == flag
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
|
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
|
||||||
pub struct Flusher<'a> {
|
pub struct Flusher<'a> {
|
||||||
xconn: &'a XConnection,
|
xconn: &'a XConnection,
|
||||||
|
|
|
@ -146,6 +146,10 @@ impl UnownedWindow {
|
||||||
.min_inner_size
|
.min_inner_size
|
||||||
.map(|size| size.to_physical::<u32>(scale_factor).into());
|
.map(|size| size.to_physical::<u32>(scale_factor).into());
|
||||||
|
|
||||||
|
let position = window_attrs
|
||||||
|
.position
|
||||||
|
.map(|position| position.to_physical::<i32>(scale_factor).into());
|
||||||
|
|
||||||
let dimensions = {
|
let dimensions = {
|
||||||
// x11 only applies constraints when the window is actively resized
|
// x11 only applies constraints when the window is actively resized
|
||||||
// by the user, so we have to manually apply the initial constraints
|
// by the user, so we have to manually apply the initial constraints
|
||||||
|
@ -211,8 +215,8 @@ impl UnownedWindow {
|
||||||
(xconn.xlib.XCreateWindow)(
|
(xconn.xlib.XCreateWindow)(
|
||||||
xconn.display,
|
xconn.display,
|
||||||
root,
|
root,
|
||||||
0,
|
position.map_or(0, |p: PhysicalPosition<i32>| p.x as c_int),
|
||||||
0,
|
position.map_or(0, |p: PhysicalPosition<i32>| p.y as c_int),
|
||||||
dimensions.0 as c_uint,
|
dimensions.0 as c_uint,
|
||||||
dimensions.1 as c_uint,
|
dimensions.1 as c_uint,
|
||||||
0,
|
0,
|
||||||
|
@ -344,6 +348,7 @@ impl UnownedWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut normal_hints = util::NormalHints::new(xconn);
|
let mut normal_hints = util::NormalHints::new(xconn);
|
||||||
|
normal_hints.set_position(position.map(|PhysicalPosition { x, y }| (x, y)));
|
||||||
normal_hints.set_size(Some(dimensions));
|
normal_hints.set_size(Some(dimensions));
|
||||||
normal_hints.set_min_size(min_inner_size.map(Into::into));
|
normal_hints.set_min_size(min_inner_size.map(Into::into));
|
||||||
normal_hints.set_max_size(max_inner_size.map(Into::into));
|
normal_hints.set_max_size(max_inner_size.map(Into::into));
|
||||||
|
@ -439,6 +444,12 @@ impl UnownedWindow {
|
||||||
window
|
window
|
||||||
.set_fullscreen_inner(window_attrs.fullscreen.clone())
|
.set_fullscreen_inner(window_attrs.fullscreen.clone())
|
||||||
.map(|flusher| flusher.queue());
|
.map(|flusher| flusher.queue());
|
||||||
|
|
||||||
|
if let Some(PhysicalPosition { x, y }) = position {
|
||||||
|
let shared_state = window.shared_state.get_mut();
|
||||||
|
|
||||||
|
shared_state.restore_position = Some((x, y));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if window_attrs.always_on_top {
|
if window_attrs.always_on_top {
|
||||||
window
|
window
|
||||||
|
|
|
@ -8,11 +8,12 @@ use std::ops::{BitAnd, Deref};
|
||||||
use cocoa::{
|
use cocoa::{
|
||||||
appkit::{NSApp, NSWindowStyleMask},
|
appkit::{NSApp, NSWindowStyleMask},
|
||||||
base::{id, nil},
|
base::{id, nil},
|
||||||
foundation::{NSAutoreleasePool, NSRect, NSString, NSUInteger},
|
foundation::{NSAutoreleasePool, NSPoint, NSRect, NSString, NSUInteger},
|
||||||
};
|
};
|
||||||
use core_graphics::display::CGDisplay;
|
use core_graphics::display::CGDisplay;
|
||||||
use objc::runtime::{Class, Object, Sel, BOOL, YES};
|
use objc::runtime::{Class, Object, Sel, BOOL, YES};
|
||||||
|
|
||||||
|
use crate::dpi::LogicalPosition;
|
||||||
use crate::platform_impl::platform::ffi;
|
use crate::platform_impl::platform::ffi;
|
||||||
|
|
||||||
// Replace with `!` once stable
|
// Replace with `!` once stable
|
||||||
|
@ -91,6 +92,16 @@ pub fn bottom_left_to_top_left(rect: NSRect) -> f64 {
|
||||||
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)
|
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
CGDisplay::main().pixels_high() as f64 - position.y,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe fn ns_string_id_ref(s: &str) -> IdRef {
|
pub unsafe fn ns_string_id_ref(s: &str) -> IdRef {
|
||||||
IdRef::new(NSString::alloc(nil).init_str(s))
|
IdRef::new(NSString::alloc(nil).init_str(s))
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,17 @@ fn create_window(
|
||||||
}
|
}
|
||||||
None => (800.0, 600.0),
|
None => (800.0, 600.0),
|
||||||
};
|
};
|
||||||
NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(width, height))
|
let (left, bottom) = 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)
|
||||||
|
}
|
||||||
|
// This value is ignored by calling win.center() below
|
||||||
|
None => (0.0, 0.0),
|
||||||
|
};
|
||||||
|
NSRect::new(NSPoint::new(left, bottom), NSSize::new(width, height))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,8 +259,9 @@ fn create_window(
|
||||||
if !pl_attrs.has_shadow {
|
if !pl_attrs.has_shadow {
|
||||||
ns_window.setHasShadow_(NO);
|
ns_window.setHasShadow_(NO);
|
||||||
}
|
}
|
||||||
|
if attrs.position.is_none() {
|
||||||
ns_window.center();
|
ns_window.center();
|
||||||
|
}
|
||||||
ns_window
|
ns_window
|
||||||
});
|
});
|
||||||
pool.drain();
|
pool.drain();
|
||||||
|
@ -496,17 +507,8 @@ impl UnownedWindow {
|
||||||
pub fn set_outer_position(&self, position: Position) {
|
pub fn set_outer_position(&self, position: Position) {
|
||||||
let scale_factor = self.scale_factor();
|
let scale_factor = self.scale_factor();
|
||||||
let position = position.to_logical(scale_factor);
|
let position = position.to_logical(scale_factor);
|
||||||
let dummy = NSRect::new(
|
|
||||||
NSPoint::new(
|
|
||||||
position.x,
|
|
||||||
// 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 - position.y,
|
|
||||||
),
|
|
||||||
NSSize::new(0f64, 0f64),
|
|
||||||
);
|
|
||||||
unsafe {
|
unsafe {
|
||||||
util::set_frame_top_left_point_async(*self.ns_window, dummy.origin);
|
util::set_frame_top_left_point_async(*self.ns_window, util::window_position(position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -832,6 +832,10 @@ unsafe fn init<T: 'static>(
|
||||||
force_window_active(win.window.0);
|
force_window_active(win.window.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(position) = attributes.position {
|
||||||
|
win.set_outer_position(position);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(win)
|
Ok(win)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,31 @@ pub struct WindowAttributes {
|
||||||
/// The default is `None`.
|
/// The default is `None`.
|
||||||
pub max_inner_size: Option<Size>,
|
pub max_inner_size: Option<Size>,
|
||||||
|
|
||||||
|
/// The desired position of the window. If this is `None`, some platform-specific position
|
||||||
|
/// will be chosen.
|
||||||
|
///
|
||||||
|
/// The default is `None`.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - **macOS**: The top left corner position of the window content, the window's "inner"
|
||||||
|
/// position. The window title bar will be placed above it.
|
||||||
|
/// The window will be positioned such that it fits on screen, maintaining
|
||||||
|
/// set `inner_size` if any.
|
||||||
|
/// If you need to precisely position the top left corner of the whole window you have to
|
||||||
|
/// use [`Window::set_outer_position`] after creating the window.
|
||||||
|
/// - **Windows**: The top left corner position of the window title bar, the window's "outer"
|
||||||
|
/// position.
|
||||||
|
/// There may be a small gap between this position and the window due to the specifics of the
|
||||||
|
/// Window Manager.
|
||||||
|
/// - **X11**: The top left corner of the window, the window's "outer" position.
|
||||||
|
/// - **Others**: Ignored.
|
||||||
|
///
|
||||||
|
/// See [`Window::set_outer_position`].
|
||||||
|
///
|
||||||
|
/// [`Window::set_outer_position`]: crate::window::Window::set_outer_position
|
||||||
|
pub position: Option<Position>,
|
||||||
|
|
||||||
/// Whether the window is resizable or not.
|
/// Whether the window is resizable or not.
|
||||||
///
|
///
|
||||||
/// The default is `true`.
|
/// The default is `true`.
|
||||||
|
@ -170,6 +195,7 @@ impl Default for WindowAttributes {
|
||||||
inner_size: None,
|
inner_size: None,
|
||||||
min_inner_size: None,
|
min_inner_size: None,
|
||||||
max_inner_size: None,
|
max_inner_size: None,
|
||||||
|
position: None,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
title: "winit window".to_owned(),
|
title: "winit window".to_owned(),
|
||||||
maximized: false,
|
maximized: false,
|
||||||
|
@ -223,6 +249,17 @@ impl WindowBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets a desired initial position for the window.
|
||||||
|
///
|
||||||
|
/// See [`WindowAttributes::position`] for details.
|
||||||
|
///
|
||||||
|
/// [`WindowAttributes::position`]: crate::window::WindowAttributes::position
|
||||||
|
#[inline]
|
||||||
|
pub fn with_position<P: Into<Position>>(mut self, position: P) -> Self {
|
||||||
|
self.window.position = Some(position.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets whether the window is resizable or not.
|
/// Sets whether the window is resizable or not.
|
||||||
///
|
///
|
||||||
/// See [`Window::set_resizable`] for details.
|
/// See [`Window::set_resizable`] for details.
|
||||||
|
|
Loading…
Reference in a new issue