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:
Osspial 2018-03-23 05:35:35 -04:00 committed by Pierre Krieger
parent d667a395b6
commit bbcd3019e8
12 changed files with 270 additions and 24 deletions

View file

@ -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 {

View file

@ -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
@ -190,11 +200,11 @@ impl Window {
// TODO: not yet possible on wayland
Err(())
}
pub fn get_display(&self) -> &wl_display::WlDisplay {
&*self.display
}
pub fn get_surface(&self) -> &wl_surface::WlSurface {
&self.surface
}

View file

@ -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);

View file

@ -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;
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;
}
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;
}
unsafe {
(display.xlib.XSetNormalHints)(display.display, x_window.window, &mut size_hints);
display.check_errors().expect("Failed to call XSetNormalHints");
{
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 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 c_int;
(*size_hints).max_height = dimensions.1 as c_int;
}
unsafe {
(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 _