Skip to content

Commit 0188f83

Browse files
committed
Refactor, split and fix Win32 resize logic and DPI changes handling
1 parent bcbdb89 commit 0188f83

File tree

3 files changed

+232
-206
lines changed

3 files changed

+232
-206
lines changed

src/win/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod drop_target;
22
mod keyboard;
3+
mod win32_window;
34
mod window;
45

56
pub use window::*;

src/win/win32_window.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use crate::PhySize;
2+
use std::convert::TryFrom;
3+
use std::ffi::OsStr;
4+
use std::os::windows::ffi::OsStrExt;
5+
use std::ptr::null_mut;
6+
use winapi::shared::minwindef::{ATOM, DWORD};
7+
use winapi::shared::windef::{HWND, RECT};
8+
use winapi::um::winuser::{
9+
AdjustWindowRectEx, CreateWindowExW, GetDpiForWindow, SetWindowPos, SWP_NOACTIVATE, SWP_NOMOVE,
10+
SWP_NOZORDER, USER_DEFAULT_SCREEN_DPI, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX,
11+
WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE,
12+
};
13+
14+
// TODO: handle proper destruction of this window during errors/panics/etc.
15+
pub(crate) struct Win32Window {
16+
pub handle: HWND,
17+
style_flags: DWORD,
18+
}
19+
20+
impl Win32Window {
21+
pub fn create(window_class: ATOM, title: &str, size: PhySize, parent: Option<HWND>) -> Self {
22+
let mut title: Vec<u16> = OsStr::new(title).encode_wide().collect();
23+
title.push(0);
24+
25+
let style_flags = if parent.is_some() {
26+
WS_CHILD | WS_VISIBLE
27+
} else {
28+
WS_POPUPWINDOW
29+
| WS_CAPTION
30+
| WS_VISIBLE
31+
| WS_SIZEBOX
32+
| WS_MINIMIZEBOX
33+
| WS_MAXIMIZEBOX
34+
| WS_CLIPSIBLINGS
35+
};
36+
37+
let size = client_size_to_window_size(size, style_flags);
38+
39+
// TODO: handle errors
40+
let hwnd = unsafe {
41+
CreateWindowExW(
42+
0,
43+
window_class as _,
44+
title.as_ptr(),
45+
style_flags,
46+
0,
47+
0,
48+
size.width as i32,
49+
size.height as i32,
50+
parent.unwrap_or(null_mut()),
51+
null_mut(),
52+
null_mut(),
53+
null_mut(),
54+
)
55+
};
56+
57+
Win32Window { style_flags, handle: hwnd }
58+
}
59+
60+
/// Resizes the window.
61+
///
62+
/// This *will* immediately trigger a WM_SIZE event.
63+
pub fn resize(&self, size: PhySize) {
64+
let window_size = client_size_to_window_size(size, self.style_flags);
65+
66+
unsafe {
67+
SetWindowPos(
68+
self.handle,
69+
null_mut(), // Ignored by SWP_NOZORDER
70+
0, // Ignored by SWP_NOMOVE
71+
0, // Ignored by SWP_NOMOVE
72+
window_size.width as i32,
73+
window_size.height as i32,
74+
SWP_NOZORDER | SWP_NOMOVE,
75+
);
76+
}
77+
}
78+
79+
/// Sets both the position and size of the window, according to a given raw RECT.
80+
///
81+
/// This *will* immediately trigger a WM_SIZE event.
82+
pub fn set_raw_pos(&self, rect: &RECT) {
83+
unsafe {
84+
SetWindowPos(
85+
self.handle,
86+
null_mut(), // Ignored by SWP_NOZORDER
87+
rect.left,
88+
rect.top,
89+
rect.right.saturating_sub(rect.left),
90+
rect.bottom.saturating_sub(rect.top),
91+
SWP_NOZORDER | SWP_NOACTIVATE,
92+
);
93+
}
94+
}
95+
96+
/// Returns current the scale factor of the monitor the window is currently on.
97+
pub fn current_scale_factor(&self) -> f64 {
98+
// FIXME: Only works on Windows 10.
99+
let dpi = unsafe { GetDpiForWindow(self.handle) };
100+
dpi as f64 / USER_DEFAULT_SCREEN_DPI as f64
101+
}
102+
}
103+
104+
pub fn client_size_to_window_size(size: PhySize, window_flags: DWORD) -> PhySize {
105+
let mut rect = RECT {
106+
left: 0,
107+
top: 0,
108+
// In case the provided size overflows an i32, which would imply it is ridiculously large.
109+
right: i32::try_from(size.width).unwrap(),
110+
bottom: i32::try_from(size.height).unwrap(),
111+
};
112+
113+
// SAFETY: the provided rect pointer is guaranteed to be valid by the mutable reference.
114+
unsafe { AdjustWindowRectEx(&mut rect, window_flags, 0, 0) };
115+
116+
// These checks are made just in case AdjustWindowRectEx sends back invalid values.
117+
// Because this is so unlikely, we can afford to just panic here.
118+
let width = rect.right.saturating_sub(rect.left);
119+
let height = rect.bottom.saturating_sub(rect.top);
120+
121+
PhySize { width: u32::try_from(width).unwrap(), height: u32::try_from(height).unwrap() }
122+
}

0 commit comments

Comments
 (0)