Windows: implement resize increments (#3623)
This commit is contained in:
parent
be79e8979a
commit
babbb715c5
6 changed files with 121 additions and 17 deletions
|
|
@ -179,7 +179,7 @@ Legend:
|
||||||
|Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ |
|
|Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ |
|
||||||
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||||
|Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|
|Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|
||||||
|Window resize increments |❌ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** |
|
|Window resize increments |✔️ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||||
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ |
|
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ |
|
||||||
|Window blur |❌ |❌ |❌ |✔️ |**N/A**|**N/A**|N/A |❌ |
|
|Window blur |❌ |❌ |❌ |✔️ |**N/A**|**N/A**|N/A |❌ |
|
||||||
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ changelog entry.
|
||||||
- On macOS, add services menu.
|
- On macOS, add services menu.
|
||||||
- On Windows, add `with_title_text_color`, and `with_corner_preference` on
|
- On Windows, add `with_title_text_color`, and `with_corner_preference` on
|
||||||
`WindowAttributesExtWindows`.
|
`WindowAttributesExtWindows`.
|
||||||
|
- On Windows, implement resize increments.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
@ -253,4 +254,4 @@ changelog entry.
|
||||||
- On Web, fix setting cursor icon overriding cursor visibility.
|
- On Web, fix setting cursor icon overriding cursor visibility.
|
||||||
- On Windows, fix cursor not confined to center of window when grabbed and hidden.
|
- On Windows, fix cursor not confined to center of window when grabbed and hidden.
|
||||||
- On macOS, fix sequence of mouse events being out of order when dragging on the trackpad.
|
- On macOS, fix sequence of mouse events being out of order when dragging on the trackpad.
|
||||||
- On Wayland, fix decoration glitch on close with some compositors
|
- On Wayland, fix decoration glitch on close with some compositors.
|
||||||
|
|
|
||||||
|
|
@ -52,17 +52,19 @@ use windows_sys::Win32::{
|
||||||
HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN,
|
HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN,
|
||||||
PT_TOUCH, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
|
PT_TOUCH, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
|
||||||
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS,
|
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS,
|
||||||
WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE,
|
WMSZ_BOTTOM, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP,
|
||||||
WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
|
WMSZ_TOPLEFT, WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY,
|
||||||
WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUT_DEVICE_CHANGE,
|
WM_DPICHANGED, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION,
|
||||||
WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,
|
WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT,
|
||||||
WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE,
|
WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN,
|
||||||
WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN,
|
WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE,
|
||||||
WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR,
|
WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY,
|
||||||
WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP,
|
WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE,
|
||||||
WM_TOUCH, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP,
|
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE,
|
||||||
WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT,
|
WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED,
|
||||||
WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
|
WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW, WS_EX_LAYERED,
|
||||||
|
WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP,
|
||||||
|
WS_VISIBLE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -1352,6 +1354,98 @@ unsafe fn public_window_callback_inner(
|
||||||
result = ProcResult::Value(0);
|
result = ProcResult::Value(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WM_SIZING => {
|
||||||
|
/// Calculate the amount to add to round `value` to the nearest multiple of `increment`.
|
||||||
|
fn snap_to_nearest_increment_delta(value: i32, increment: i32) -> i32 {
|
||||||
|
let half_one = increment / 2;
|
||||||
|
let half_two = increment - half_one;
|
||||||
|
half_one - (value - half_two) % increment
|
||||||
|
}
|
||||||
|
|
||||||
|
let scale_factor = userdata.window_state_lock().scale_factor;
|
||||||
|
let Some(inc) = userdata
|
||||||
|
.window_state_lock()
|
||||||
|
.resize_increments
|
||||||
|
.map(|inc| inc.to_physical(scale_factor))
|
||||||
|
.filter(|inc| inc.width > 0 && inc.height > 0)
|
||||||
|
else {
|
||||||
|
result = ProcResult::Value(0);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let side = wparam as u32;
|
||||||
|
// The desired new size of the window, decorations included.
|
||||||
|
let rect = unsafe { &mut *(lparam as *mut RECT) };
|
||||||
|
|
||||||
|
// We need to calculate the dimensions of the window decorations to get the true
|
||||||
|
// size of the window's contents
|
||||||
|
let adj_rect = userdata
|
||||||
|
.window_state_lock()
|
||||||
|
.window_flags
|
||||||
|
.adjust_rect(window, *rect)
|
||||||
|
.unwrap_or(*rect);
|
||||||
|
let deco_width = rect.left - adj_rect.left + adj_rect.right - rect.right;
|
||||||
|
let deco_height = rect.top - adj_rect.top + adj_rect.bottom - rect.bottom;
|
||||||
|
|
||||||
|
let width = rect.right - rect.left - deco_width;
|
||||||
|
let height = rect.bottom - rect.top - deco_height;
|
||||||
|
|
||||||
|
let mut width_delta = snap_to_nearest_increment_delta(width, inc.width);
|
||||||
|
let mut height_delta = snap_to_nearest_increment_delta(height, inc.height);
|
||||||
|
|
||||||
|
// Windows won't bound check the value of `rect` after we're done here, so we
|
||||||
|
// have to check manually. If the width/height we snap to would go out of bounds, just
|
||||||
|
// set it equal to the min/max bound.
|
||||||
|
let min_size = userdata
|
||||||
|
.window_state_lock()
|
||||||
|
.min_size
|
||||||
|
.map(|size| size.to_physical(scale_factor));
|
||||||
|
let max_size = userdata
|
||||||
|
.window_state_lock()
|
||||||
|
.max_size
|
||||||
|
.map(|size| size.to_physical(scale_factor));
|
||||||
|
let final_width = width + width_delta;
|
||||||
|
let final_height = height + height_delta;
|
||||||
|
if let Some(min_size) = min_size {
|
||||||
|
if final_width < min_size.width {
|
||||||
|
width_delta += min_size.width - final_width;
|
||||||
|
}
|
||||||
|
if final_height < min_size.height {
|
||||||
|
height_delta += min_size.height - final_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(max_size) = max_size {
|
||||||
|
if final_width > max_size.width {
|
||||||
|
width_delta -= final_width - max_size.width;
|
||||||
|
}
|
||||||
|
if final_height > max_size.height {
|
||||||
|
height_delta -= final_height - max_size.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match side {
|
||||||
|
WMSZ_LEFT | WMSZ_BOTTOMLEFT | WMSZ_TOPLEFT => {
|
||||||
|
rect.left -= width_delta;
|
||||||
|
}
|
||||||
|
WMSZ_RIGHT | WMSZ_BOTTOMRIGHT | WMSZ_TOPRIGHT => {
|
||||||
|
rect.right += width_delta;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
match side {
|
||||||
|
WMSZ_TOP | WMSZ_TOPLEFT | WMSZ_TOPRIGHT => {
|
||||||
|
rect.top -= height_delta;
|
||||||
|
}
|
||||||
|
WMSZ_BOTTOM | WMSZ_BOTTOMLEFT | WMSZ_BOTTOMRIGHT => {
|
||||||
|
rect.bottom += height_delta;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ProcResult::DefWindowProc(wparam);
|
||||||
|
}
|
||||||
|
|
||||||
WM_MENUCHAR => {
|
WM_MENUCHAR => {
|
||||||
result = ProcResult::Value((MNC_CLOSE << 16) as isize);
|
result = ProcResult::Value((MNC_CLOSE << 16) as isize);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -284,11 +284,16 @@ impl Window {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||||
None
|
let w = self.window_state_lock();
|
||||||
|
let scale_factor = w.scale_factor;
|
||||||
|
w.resize_increments
|
||||||
|
.map(|size| size.to_physical(scale_factor))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_resize_increments(&self, _increments: Option<Size>) {}
|
pub fn set_resize_increments(&self, increments: Option<Size>) {
|
||||||
|
self.window_state_lock().resize_increments = increments;
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_resizable(&self, resizable: bool) {
|
pub fn set_resizable(&self, resizable: bool) {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ pub(crate) struct WindowState {
|
||||||
pub min_size: Option<Size>,
|
pub min_size: Option<Size>,
|
||||||
pub max_size: Option<Size>,
|
pub max_size: Option<Size>,
|
||||||
|
|
||||||
|
pub resize_increments: Option<Size>,
|
||||||
|
|
||||||
pub window_icon: Option<Icon>,
|
pub window_icon: Option<Icon>,
|
||||||
pub taskbar_icon: Option<Icon>,
|
pub taskbar_icon: Option<Icon>,
|
||||||
|
|
||||||
|
|
@ -155,6 +157,8 @@ impl WindowState {
|
||||||
min_size: attributes.min_inner_size,
|
min_size: attributes.min_inner_size,
|
||||||
max_size: attributes.max_inner_size,
|
max_size: attributes.max_inner_size,
|
||||||
|
|
||||||
|
resize_increments: attributes.resize_increments,
|
||||||
|
|
||||||
window_icon: attributes.window_icon.clone(),
|
window_icon: attributes.window_icon.clone(),
|
||||||
taskbar_icon: None,
|
taskbar_icon: None,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -893,7 +893,7 @@ impl Window {
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - **iOS / Android / Web / Wayland / Windows / Orbital:** Always returns [`None`].
|
/// - **iOS / Android / Web / Wayland / Orbital:** Always returns [`None`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||||
let _span = tracing::debug_span!("winit::Window::resize_increments",).entered();
|
let _span = tracing::debug_span!("winit::Window::resize_increments",).entered();
|
||||||
|
|
@ -908,7 +908,7 @@ impl Window {
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - **macOS:** Increments are converted to logical size and then macOS rounds them to whole numbers.
|
/// - **macOS:** Increments are converted to logical size and then macOS rounds them to whole numbers.
|
||||||
/// - **Wayland / Windows:** Not implemented.
|
/// - **Wayland:** Not implemented.
|
||||||
/// - **iOS / Android / Web / Orbital:** Unsupported.
|
/// - **iOS / Android / Web / Orbital:** Unsupported.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_resize_increments<S: Into<Size>>(&self, increments: Option<S>) {
|
pub fn set_resize_increments<S: Into<Size>>(&self, increments: Option<S>) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue