mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 22:01:31 +11:00
Add ability to change the min/max size of windows at runtime (#405)
* Add min/max size setting for win32 and wayland backends * Implement dynamic min/max size on macos * Add min/max size setting for x11 * Add empty functions for remaining platforms * Improved min/max size setting for x11 * Added CHANGELOG entry for new min/max methods * Added documentation for new min/max methods * On win32, bound window size to min/max dimensions on window creation * On win32, force re-check of window size when changing min/max dimensions * Fix freeze when setting min and max size
This commit is contained in:
parent
d667a395b6
commit
bbcd3019e8
|
@ -1,4 +1,5 @@
|
|||
# Unreleased
|
||||
- Added `set_min_dimensions` and `set_max_dimensions` methods to `Window`, and implemented on Windows, X11, Wayland, and OSX.
|
||||
|
||||
- On X11, dropping a `Window` actually closes it now, and clicking the window's × button (or otherwise having the WM signal to close it) will result in the window closing.
|
||||
- Added `WindowBuilderExt` methods for macos: `with_titlebar_transparent`,
|
||||
|
|
|
@ -3,12 +3,13 @@ extern crate winit;
|
|||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
.with_min_dimensions(400, 200)
|
||||
.with_max_dimensions(800, 400)
|
||||
let window = winit::WindowBuilder::new()
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
window.set_min_dimensions(Some((400, 200)));
|
||||
window.set_max_dimensions(Some((800, 400)));
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
|
||||
|
|
|
@ -247,6 +247,12 @@ impl Window {
|
|||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
if self.native_window.is_null() {
|
||||
|
|
|
@ -452,6 +452,12 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {}
|
||||
#[inline]
|
||||
|
|
|
@ -306,6 +306,12 @@ impl Window {
|
|||
pub fn set_inner_size(&self, _x: u32, _y: u32) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!();
|
||||
|
|
|
@ -194,6 +194,22 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_min_dimensions(dimensions),
|
||||
&Window::Wayland(ref w) => w.set_min_dimensions(dimensions)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_max_dimensions(dimensions),
|
||||
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
match self {
|
||||
|
|
|
@ -136,6 +136,16 @@ impl Window {
|
|||
*(self.size.lock().unwrap()) = (x, y);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.frame.lock().unwrap().set_min_size(dimensions.map(|(w, h)| (w as i32, h as i32)));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.frame.lock().unwrap().set_max_size(dimensions.map(|(w, h)| (w as i32, h as i32)));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
// TODO
|
||||
|
|
|
@ -1,11 +1,54 @@
|
|||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_char, c_double, c_int, c_long, c_short, c_uchar, c_uint, c_ulong};
|
||||
|
||||
use super::{ffi, XConnection, XError};
|
||||
use events::ModifiersState;
|
||||
|
||||
pub struct XSmartPointer<'a, T> {
|
||||
xconn: &'a Arc<XConnection>,
|
||||
pub ptr: *mut T,
|
||||
}
|
||||
|
||||
impl<'a, T> XSmartPointer<'a, T> {
|
||||
// You're responsible for only passing things to this that should be XFree'd.
|
||||
// Returns None if ptr is null.
|
||||
pub fn new(xconn: &'a Arc<XConnection>, ptr: *mut T) -> Option<Self> {
|
||||
if !ptr.is_null() {
|
||||
Some(XSmartPointer {
|
||||
xconn,
|
||||
ptr,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for XSmartPointer<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFree)(self.ptr as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_atom(xconn: &Arc<XConnection>, name: &[u8]) -> Result<ffi::Atom, XError> {
|
||||
let atom_name: *const c_char = name.as_ptr() as _;
|
||||
let atom = (xconn.xlib.XInternAtom)(xconn.display, atom_name, ffi::False);
|
||||
|
|
|
@ -19,7 +19,7 @@ use window::MonitorId as RootMonitorId;
|
|||
|
||||
use platform::x11::monitor::get_available_monitors;
|
||||
|
||||
use super::{ffi, util, XConnection, WindowId, EventsLoop};
|
||||
use super::{ffi, util, XConnection, XError, WindowId, EventsLoop};
|
||||
|
||||
// TODO: remove me
|
||||
fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T {
|
||||
|
@ -230,23 +230,33 @@ impl Window2 {
|
|||
}
|
||||
|
||||
// set size hints
|
||||
let mut size_hints: ffi::XSizeHints = unsafe { mem::zeroed() };
|
||||
size_hints.flags = ffi::PSize;
|
||||
size_hints.width = dimensions.0 as i32;
|
||||
size_hints.height = dimensions.1 as i32;
|
||||
{
|
||||
let mut size_hints = {
|
||||
let size_hints = unsafe { (display.xlib.XAllocSizeHints)() };
|
||||
util::XSmartPointer::new(&display, size_hints)
|
||||
.expect("XAllocSizeHints returned null; out of memory")
|
||||
};
|
||||
(*size_hints).flags = ffi::PSize;
|
||||
(*size_hints).width = dimensions.0 as c_int;
|
||||
(*size_hints).height = dimensions.1 as c_int;
|
||||
if let Some(dimensions) = window_attrs.min_dimensions {
|
||||
size_hints.flags |= ffi::PMinSize;
|
||||
size_hints.min_width = dimensions.0 as i32;
|
||||
size_hints.min_height = dimensions.1 as i32;
|
||||
(*size_hints).flags |= ffi::PMinSize;
|
||||
(*size_hints).min_width = dimensions.0 as c_int;
|
||||
(*size_hints).min_height = dimensions.1 as c_int;
|
||||
}
|
||||
if let Some(dimensions) = window_attrs.max_dimensions {
|
||||
size_hints.flags |= ffi::PMaxSize;
|
||||
size_hints.max_width = dimensions.0 as i32;
|
||||
size_hints.max_height = dimensions.1 as i32;
|
||||
(*size_hints).flags |= ffi::PMaxSize;
|
||||
(*size_hints).max_width = dimensions.0 as c_int;
|
||||
(*size_hints).max_height = dimensions.1 as c_int;
|
||||
}
|
||||
unsafe {
|
||||
(display.xlib.XSetNormalHints)(display.display, x_window.window, &mut size_hints);
|
||||
display.check_errors().expect("Failed to call XSetNormalHints");
|
||||
(display.xlib.XSetWMNormalHints)(
|
||||
display.display,
|
||||
x_window.window,
|
||||
size_hints.ptr,
|
||||
);
|
||||
}
|
||||
display.check_errors().expect("Failed to call XSetWMNormalHints");
|
||||
}
|
||||
|
||||
// Opt into handling window close
|
||||
|
@ -603,6 +613,67 @@ impl Window2 {
|
|||
self.x.display.check_errors().expect("Failed to call XResizeWindow");
|
||||
}
|
||||
|
||||
unsafe fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
|
||||
where F: FnOnce(*mut ffi::XSizeHints) -> ()
|
||||
{
|
||||
let xconn = &self.x.display;
|
||||
|
||||
let size_hints = {
|
||||
let size_hints = (xconn.xlib.XAllocSizeHints)();
|
||||
util::XSmartPointer::new(&xconn, size_hints)
|
||||
.expect("XAllocSizeHints returned null; out of memory")
|
||||
};
|
||||
|
||||
let mut flags: c_long = mem::uninitialized();
|
||||
|
||||
(xconn.xlib.XGetWMNormalHints)(
|
||||
xconn.display,
|
||||
self.x.window,
|
||||
size_hints.ptr,
|
||||
&mut flags,
|
||||
);
|
||||
xconn.check_errors()?;
|
||||
|
||||
callback(size_hints.ptr);
|
||||
|
||||
(xconn.xlib.XSetWMNormalHints)(
|
||||
xconn.display,
|
||||
self.x.window,
|
||||
size_hints.ptr,
|
||||
);
|
||||
xconn.check_errors()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
unsafe {
|
||||
self.update_normal_hints(|size_hints| {
|
||||
if let Some((width, height)) = dimensions {
|
||||
(*size_hints).flags |= ffi::PMinSize;
|
||||
(*size_hints).min_width = width as c_int;
|
||||
(*size_hints).min_height = height as c_int;
|
||||
} else {
|
||||
(*size_hints).flags &= !ffi::PMinSize;
|
||||
}
|
||||
})
|
||||
}.expect("Failed to call XSetWMNormalHints");
|
||||
}
|
||||
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
unsafe {
|
||||
self.update_normal_hints(|size_hints| {
|
||||
if let Some((width, height)) = dimensions {
|
||||
(*size_hints).flags |= ffi::PMaxSize;
|
||||
(*size_hints).max_width = width as c_int;
|
||||
(*size_hints).max_height = height as c_int;
|
||||
} else {
|
||||
(*size_hints).flags &= !ffi::PMaxSize;
|
||||
}
|
||||
})
|
||||
}.expect("Failed to call XSetWMNormalHints");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_xlib_display(&self) -> *mut c_void {
|
||||
self.x.display.display as _
|
||||
|
|
|
@ -571,6 +571,20 @@ impl Window2 {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
unsafe {
|
||||
let (width, height) = dimensions.unwrap_or((0, 0));
|
||||
nswindow_set_min_dimensions(self.window.0, width.into(), height.into());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
unsafe {
|
||||
let (width, height) = dimensions.unwrap_or((!0, !0));
|
||||
nswindow_set_max_dimensions(self.window.0, width.into(), height.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
|
|
|
@ -153,6 +153,52 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
window_state.attributes.min_dimensions = dimensions;
|
||||
|
||||
// Make windows re-check the window size bounds.
|
||||
if let Some(inner_size) = self.get_inner_size() {
|
||||
unsafe {
|
||||
let mut rect = RECT { top: 0, left: 0, bottom: inner_size.1 as LONG, right: inner_size.0 as LONG };
|
||||
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
||||
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
||||
let dw_style_ex = winuser::GetWindowLongA(self.window.0, winuser::GWL_EXSTYLE) as DWORD;
|
||||
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
||||
let outer_x = (rect.right - rect.left).abs() as raw::c_int;
|
||||
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
||||
|
||||
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
window_state.attributes.max_dimensions = dimensions;
|
||||
|
||||
// Make windows re-check the window size bounds.
|
||||
if let Some(inner_size) = self.get_inner_size() {
|
||||
unsafe {
|
||||
let mut rect = RECT { top: 0, left: 0, bottom: inner_size.1 as LONG, right: inner_size.0 as LONG };
|
||||
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
||||
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
||||
let dw_style_ex = winuser::GetWindowLongA(self.window.0, winuser::GWL_EXSTYLE) as DWORD;
|
||||
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
||||
let outer_x = (rect.right - rect.left).abs() as raw::c_int;
|
||||
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
||||
|
||||
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
||||
panic!() // Deprecated function ; we don't care anymore
|
||||
|
@ -370,7 +416,17 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
|||
// creating the real window this time, by using the functions in `extra_functions`
|
||||
let real_window = {
|
||||
let (width, height) = if fullscreen || window.dimensions.is_some() {
|
||||
(Some(rect.right - rect.left), Some(rect.bottom - rect.top))
|
||||
let min_dimensions = window.min_dimensions
|
||||
.map(|d| (d.0 as raw::c_int, d.1 as raw::c_int))
|
||||
.unwrap_or((0, 0));
|
||||
let max_dimensions = window.max_dimensions
|
||||
.map(|d| (d.0 as raw::c_int, d.1 as raw::c_int))
|
||||
.unwrap_or((raw::c_int::max_value(), raw::c_int::max_value()));
|
||||
|
||||
(
|
||||
Some((rect.right - rect.left).min(max_dimensions.0).max(min_dimensions.0)),
|
||||
Some((rect.bottom - rect.top).min(max_dimensions.1).max(min_dimensions.1))
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
|
|
@ -255,6 +255,22 @@ impl Window {
|
|||
self.window.set_inner_size(x, y)
|
||||
}
|
||||
|
||||
/// Sets a minimum dimension size for the window.
|
||||
///
|
||||
/// Width and height are in pixels.
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.window.set_min_dimensions(dimensions)
|
||||
}
|
||||
|
||||
/// Sets a maximum dimension size for the window.
|
||||
///
|
||||
/// Width and height are in pixels.
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.window.set_max_dimensions(dimensions)
|
||||
}
|
||||
|
||||
/// DEPRECATED. Gets the native platform specific display for this window.
|
||||
/// This is typically only required when integrating with
|
||||
/// other libraries that need this information.
|
||||
|
|
Loading…
Reference in a new issue