Implement window resizing for Windows
This commit is contained in:
parent
b8a5867436
commit
80b30ce617
|
@ -17,6 +17,7 @@ use winapi::um::winuser::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::ffi::{c_void, OsStr};
|
use std::ffi::{c_void, OsStr};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
@ -29,8 +30,8 @@ use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle};
|
||||||
const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
|
const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, WindowEvent, WindowHandler,
|
Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent,
|
||||||
WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::keyboard::KeyboardState;
|
use super::keyboard::KeyboardState;
|
||||||
|
@ -130,234 +131,261 @@ unsafe extern "system" fn wnd_proc(
|
||||||
if !window_state_ptr.is_null() {
|
if !window_state_ptr.is_null() {
|
||||||
let window_state = &*window_state_ptr;
|
let window_state = &*window_state_ptr;
|
||||||
|
|
||||||
match msg {
|
let result = wnd_proc_inner(hwnd, msg, wparam, lparam, window_state);
|
||||||
WM_MOUSEMOVE => {
|
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
|
||||||
|
|
||||||
let x = (lparam & 0xFFFF) as i16 as i32;
|
// If any of the above event handlers caused tasks to be pushed to the deferred tasks list,
|
||||||
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
|
// then we'll try to handle them now
|
||||||
|
loop {
|
||||||
|
// NOTE: This is written like this instead of using a `while let` loop to avoid exending
|
||||||
|
// the borrow of `window_state.deferred_tasks` into the call of
|
||||||
|
// `window_state.handle_deferred_task()` since that may also generate additional
|
||||||
|
// messages.
|
||||||
|
let task = match window_state.deferred_tasks.borrow_mut().pop_front() {
|
||||||
|
Some(task) => task,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
|
||||||
let physical_pos = PhyPoint { x, y };
|
window_state.handle_deferred_task(task);
|
||||||
let logical_pos = physical_pos.to_logical(&window_state.window_info.borrow());
|
}
|
||||||
let event = Event::Mouse(MouseEvent::CursorMoved {
|
|
||||||
position: logical_pos,
|
|
||||||
modifiers: window_state
|
|
||||||
.keyboard_state
|
|
||||||
.borrow()
|
|
||||||
.get_modifiers_from_mouse_wparam(wparam),
|
|
||||||
});
|
|
||||||
|
|
||||||
window_state.handler.borrow_mut().on_event(&mut window, event);
|
// The actual custom window proc has been moved to another function so we can always handle
|
||||||
|
// the deferred tasks regardless of whether the custom window proc returns early or not
|
||||||
return 0;
|
if let Some(result) = result {
|
||||||
}
|
return result;
|
||||||
WM_MOUSEWHEEL | WM_MOUSEHWHEEL => {
|
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
|
||||||
|
|
||||||
let value = (wparam >> 16) as i16;
|
|
||||||
let value = value as i32;
|
|
||||||
let value = value as f32 / WHEEL_DELTA as f32;
|
|
||||||
|
|
||||||
let event = Event::Mouse(MouseEvent::WheelScrolled {
|
|
||||||
delta: if msg == WM_MOUSEWHEEL {
|
|
||||||
ScrollDelta::Lines { x: 0.0, y: value }
|
|
||||||
} else {
|
|
||||||
ScrollDelta::Lines { x: value, y: 0.0 }
|
|
||||||
},
|
|
||||||
modifiers: window_state
|
|
||||||
.keyboard_state
|
|
||||||
.borrow()
|
|
||||||
.get_modifiers_from_mouse_wparam(wparam),
|
|
||||||
});
|
|
||||||
|
|
||||||
window_state.handler.borrow_mut().on_event(&mut window, event);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN
|
|
||||||
| WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => {
|
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
|
||||||
|
|
||||||
let mut mouse_button_counter = window_state.mouse_button_counter.get();
|
|
||||||
|
|
||||||
let button = match msg {
|
|
||||||
WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left),
|
|
||||||
WM_MBUTTONDOWN | WM_MBUTTONUP => Some(MouseButton::Middle),
|
|
||||||
WM_RBUTTONDOWN | WM_RBUTTONUP => Some(MouseButton::Right),
|
|
||||||
WM_XBUTTONDOWN | WM_XBUTTONUP => match GET_XBUTTON_WPARAM(wparam) {
|
|
||||||
XBUTTON1 => Some(MouseButton::Back),
|
|
||||||
XBUTTON2 => Some(MouseButton::Forward),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(button) = button {
|
|
||||||
let event = match msg {
|
|
||||||
WM_LBUTTONDOWN | WM_MBUTTONDOWN | WM_RBUTTONDOWN | WM_XBUTTONDOWN => {
|
|
||||||
// Capture the mouse cursor on button down
|
|
||||||
mouse_button_counter = mouse_button_counter.saturating_add(1);
|
|
||||||
SetCapture(hwnd);
|
|
||||||
MouseEvent::ButtonPressed {
|
|
||||||
button,
|
|
||||||
modifiers: window_state
|
|
||||||
.keyboard_state
|
|
||||||
.borrow()
|
|
||||||
.get_modifiers_from_mouse_wparam(wparam),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP => {
|
|
||||||
// Release the mouse cursor capture when all buttons are released
|
|
||||||
mouse_button_counter = mouse_button_counter.saturating_sub(1);
|
|
||||||
if mouse_button_counter == 0 {
|
|
||||||
ReleaseCapture();
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseEvent::ButtonReleased {
|
|
||||||
button,
|
|
||||||
modifiers: window_state
|
|
||||||
.keyboard_state
|
|
||||||
.borrow()
|
|
||||||
.get_modifiers_from_mouse_wparam(wparam),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window_state.mouse_button_counter.set(mouse_button_counter);
|
|
||||||
|
|
||||||
window_state.handler.borrow_mut().on_event(&mut window, Event::Mouse(event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WM_TIMER => {
|
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
|
||||||
|
|
||||||
if wparam == WIN_FRAME_TIMER {
|
|
||||||
window_state.handler.borrow_mut().on_frame(&mut window);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
WM_CLOSE => {
|
|
||||||
// Make sure to release the borrow before the DefWindowProc call
|
|
||||||
{
|
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
|
||||||
|
|
||||||
window_state
|
|
||||||
.handler
|
|
||||||
.borrow_mut()
|
|
||||||
.on_event(&mut window, Event::Window(WindowEvent::WillClose));
|
|
||||||
}
|
|
||||||
|
|
||||||
// DestroyWindow(hwnd);
|
|
||||||
// return 0;
|
|
||||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
|
||||||
}
|
|
||||||
WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP
|
|
||||||
| WM_INPUTLANGCHANGE => {
|
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
|
||||||
|
|
||||||
let opt_event = window_state
|
|
||||||
.keyboard_state
|
|
||||||
.borrow_mut()
|
|
||||||
.process_message(hwnd, msg, wparam, lparam);
|
|
||||||
|
|
||||||
if let Some(event) = opt_event {
|
|
||||||
window_state.handler.borrow_mut().on_event(&mut window, Event::Keyboard(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg != WM_SYSKEYDOWN {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WM_SIZE => {
|
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
|
||||||
|
|
||||||
let width = (lparam & 0xFFFF) as u16 as u32;
|
|
||||||
let height = ((lparam >> 16) & 0xFFFF) as u16 as u32;
|
|
||||||
|
|
||||||
let window_info = {
|
|
||||||
let mut window_info = window_state.window_info.borrow_mut();
|
|
||||||
*window_info = WindowInfo::from_physical_size(
|
|
||||||
PhySize { width, height },
|
|
||||||
window_info.scale(),
|
|
||||||
);
|
|
||||||
|
|
||||||
window_info.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
window_state
|
|
||||||
.handler
|
|
||||||
.borrow_mut()
|
|
||||||
.on_event(&mut window, Event::Window(WindowEvent::Resized(window_info)));
|
|
||||||
}
|
|
||||||
WM_DPICHANGED => {
|
|
||||||
// To avoid weirdness with the realtime borrow checker.
|
|
||||||
let new_rect = {
|
|
||||||
if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy {
|
|
||||||
let dpi = (wparam & 0xFFFF) as u16 as u32;
|
|
||||||
let scale_factor = dpi as f64 / 96.0;
|
|
||||||
|
|
||||||
let mut window_info = window_state.window_info.borrow_mut();
|
|
||||||
*window_info =
|
|
||||||
WindowInfo::from_logical_size(window_info.logical_size(), scale_factor);
|
|
||||||
|
|
||||||
Some((
|
|
||||||
RECT {
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
// todo: check if usize fits into i32
|
|
||||||
right: window_info.physical_size().width as i32,
|
|
||||||
bottom: window_info.physical_size().height as i32,
|
|
||||||
},
|
|
||||||
window_state.dw_style,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some((mut new_rect, dw_style)) = new_rect {
|
|
||||||
// Convert this desired "client rectangle" size to the actual "window rectangle"
|
|
||||||
// size (Because of course you have to do that).
|
|
||||||
AdjustWindowRectEx(&mut new_rect, dw_style, 0, 0);
|
|
||||||
|
|
||||||
// Windows makes us resize the window manually. This will trigger another `WM_SIZE` event,
|
|
||||||
// which we can then send the user the new scale factor.
|
|
||||||
SetWindowPos(
|
|
||||||
hwnd,
|
|
||||||
hwnd,
|
|
||||||
new_rect.left as i32,
|
|
||||||
new_rect.top as i32,
|
|
||||||
new_rect.right - new_rect.left,
|
|
||||||
new_rect.bottom - new_rect.top,
|
|
||||||
SWP_NOZORDER | SWP_NOMOVE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WM_NCDESTROY => {
|
|
||||||
unregister_wnd_class(window_state.window_class);
|
|
||||||
SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if msg == BV_WINDOW_MUST_CLOSE {
|
|
||||||
DestroyWindow(hwnd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DefWindowProcW(hwnd, msg, wparam, lparam)
|
DefWindowProcW(hwnd, msg, wparam, lparam)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Our custom `wnd_proc` handler. If the result contains a value, then this is returned after
|
||||||
|
/// handling any deferred tasks. otherwise the default window procedure is invoked.
|
||||||
|
unsafe fn wnd_proc_inner(
|
||||||
|
hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM, window_state: &WindowState,
|
||||||
|
) -> Option<LRESULT> {
|
||||||
|
match msg {
|
||||||
|
WM_MOUSEMOVE => {
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
let x = (lparam & 0xFFFF) as i16 as i32;
|
||||||
|
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
|
||||||
|
|
||||||
|
let physical_pos = PhyPoint { x, y };
|
||||||
|
let logical_pos = physical_pos.to_logical(&window_state.window_info.borrow());
|
||||||
|
let event = Event::Mouse(MouseEvent::CursorMoved {
|
||||||
|
position: logical_pos,
|
||||||
|
modifiers: window_state
|
||||||
|
.keyboard_state
|
||||||
|
.borrow()
|
||||||
|
.get_modifiers_from_mouse_wparam(wparam),
|
||||||
|
});
|
||||||
|
|
||||||
|
window_state.handler.borrow_mut().on_event(&mut window, event);
|
||||||
|
|
||||||
|
return Some(0);
|
||||||
|
}
|
||||||
|
WM_MOUSEWHEEL | WM_MOUSEHWHEEL => {
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
let value = (wparam >> 16) as i16;
|
||||||
|
let value = value as i32;
|
||||||
|
let value = value as f32 / WHEEL_DELTA as f32;
|
||||||
|
|
||||||
|
let event = Event::Mouse(MouseEvent::WheelScrolled {
|
||||||
|
delta: if msg == WM_MOUSEWHEEL {
|
||||||
|
ScrollDelta::Lines { x: 0.0, y: value }
|
||||||
|
} else {
|
||||||
|
ScrollDelta::Lines { x: value, y: 0.0 }
|
||||||
|
},
|
||||||
|
modifiers: window_state
|
||||||
|
.keyboard_state
|
||||||
|
.borrow()
|
||||||
|
.get_modifiers_from_mouse_wparam(wparam),
|
||||||
|
});
|
||||||
|
|
||||||
|
window_state.handler.borrow_mut().on_event(&mut window, event);
|
||||||
|
|
||||||
|
return Some(0);
|
||||||
|
}
|
||||||
|
WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN
|
||||||
|
| WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => {
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
let mut mouse_button_counter = window_state.mouse_button_counter.get();
|
||||||
|
|
||||||
|
let button = match msg {
|
||||||
|
WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left),
|
||||||
|
WM_MBUTTONDOWN | WM_MBUTTONUP => Some(MouseButton::Middle),
|
||||||
|
WM_RBUTTONDOWN | WM_RBUTTONUP => Some(MouseButton::Right),
|
||||||
|
WM_XBUTTONDOWN | WM_XBUTTONUP => match GET_XBUTTON_WPARAM(wparam) {
|
||||||
|
XBUTTON1 => Some(MouseButton::Back),
|
||||||
|
XBUTTON2 => Some(MouseButton::Forward),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(button) = button {
|
||||||
|
let event = match msg {
|
||||||
|
WM_LBUTTONDOWN | WM_MBUTTONDOWN | WM_RBUTTONDOWN | WM_XBUTTONDOWN => {
|
||||||
|
// Capture the mouse cursor on button down
|
||||||
|
mouse_button_counter = mouse_button_counter.saturating_add(1);
|
||||||
|
SetCapture(hwnd);
|
||||||
|
MouseEvent::ButtonPressed {
|
||||||
|
button,
|
||||||
|
modifiers: window_state
|
||||||
|
.keyboard_state
|
||||||
|
.borrow()
|
||||||
|
.get_modifiers_from_mouse_wparam(wparam),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP => {
|
||||||
|
// Release the mouse cursor capture when all buttons are released
|
||||||
|
mouse_button_counter = mouse_button_counter.saturating_sub(1);
|
||||||
|
if mouse_button_counter == 0 {
|
||||||
|
ReleaseCapture();
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseEvent::ButtonReleased {
|
||||||
|
button,
|
||||||
|
modifiers: window_state
|
||||||
|
.keyboard_state
|
||||||
|
.borrow()
|
||||||
|
.get_modifiers_from_mouse_wparam(wparam),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window_state.mouse_button_counter.set(mouse_button_counter);
|
||||||
|
|
||||||
|
window_state.handler.borrow_mut().on_event(&mut window, Event::Mouse(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WM_TIMER => {
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
if wparam == WIN_FRAME_TIMER {
|
||||||
|
window_state.handler.borrow_mut().on_frame(&mut window);
|
||||||
|
}
|
||||||
|
return Some(0);
|
||||||
|
}
|
||||||
|
WM_CLOSE => {
|
||||||
|
// Make sure to release the borrow before the DefWindowProc call
|
||||||
|
{
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
window_state
|
||||||
|
.handler
|
||||||
|
.borrow_mut()
|
||||||
|
.on_event(&mut window, Event::Window(WindowEvent::WillClose));
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestroyWindow(hwnd);
|
||||||
|
// return Some(0);
|
||||||
|
return Some(DefWindowProcW(hwnd, msg, wparam, lparam));
|
||||||
|
}
|
||||||
|
WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP
|
||||||
|
| WM_INPUTLANGCHANGE => {
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
let opt_event =
|
||||||
|
window_state.keyboard_state.borrow_mut().process_message(hwnd, msg, wparam, lparam);
|
||||||
|
|
||||||
|
if let Some(event) = opt_event {
|
||||||
|
window_state.handler.borrow_mut().on_event(&mut window, Event::Keyboard(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg != WM_SYSKEYDOWN {
|
||||||
|
return Some(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WM_SIZE => {
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
let width = (lparam & 0xFFFF) as u16 as u32;
|
||||||
|
let height = ((lparam >> 16) & 0xFFFF) as u16 as u32;
|
||||||
|
|
||||||
|
let window_info = {
|
||||||
|
let mut window_info = window_state.window_info.borrow_mut();
|
||||||
|
*window_info =
|
||||||
|
WindowInfo::from_physical_size(PhySize { width, height }, window_info.scale());
|
||||||
|
|
||||||
|
*window_info
|
||||||
|
};
|
||||||
|
|
||||||
|
window_state
|
||||||
|
.handler
|
||||||
|
.borrow_mut()
|
||||||
|
.on_event(&mut window, Event::Window(WindowEvent::Resized(window_info)));
|
||||||
|
}
|
||||||
|
WM_DPICHANGED => {
|
||||||
|
// To avoid weirdness with the realtime borrow checker.
|
||||||
|
let new_rect = {
|
||||||
|
if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy {
|
||||||
|
let dpi = (wparam & 0xFFFF) as u16 as u32;
|
||||||
|
let scale_factor = dpi as f64 / 96.0;
|
||||||
|
|
||||||
|
let mut window_info = window_state.window_info.borrow_mut();
|
||||||
|
*window_info =
|
||||||
|
WindowInfo::from_logical_size(window_info.logical_size(), scale_factor);
|
||||||
|
|
||||||
|
Some((
|
||||||
|
RECT {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
// todo: check if usize fits into i32
|
||||||
|
right: window_info.physical_size().width as i32,
|
||||||
|
bottom: window_info.physical_size().height as i32,
|
||||||
|
},
|
||||||
|
window_state.dw_style,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some((mut new_rect, dw_style)) = new_rect {
|
||||||
|
// Convert this desired "client rectangle" size to the actual "window rectangle"
|
||||||
|
// size (Because of course you have to do that).
|
||||||
|
AdjustWindowRectEx(&mut new_rect, dw_style, 0, 0);
|
||||||
|
|
||||||
|
// Windows makes us resize the window manually. This will trigger another `WM_SIZE` event,
|
||||||
|
// which we can then send the user the new scale factor.
|
||||||
|
SetWindowPos(
|
||||||
|
hwnd,
|
||||||
|
hwnd,
|
||||||
|
new_rect.left as i32,
|
||||||
|
new_rect.top as i32,
|
||||||
|
new_rect.right - new_rect.left,
|
||||||
|
new_rect.bottom - new_rect.top,
|
||||||
|
SWP_NOZORDER | SWP_NOMOVE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WM_NCDESTROY => {
|
||||||
|
unregister_wnd_class(window_state.window_class);
|
||||||
|
SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if msg == BV_WINDOW_MUST_CLOSE {
|
||||||
|
DestroyWindow(hwnd);
|
||||||
|
return Some(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn register_wnd_class() -> ATOM {
|
unsafe fn register_wnd_class() -> ATOM {
|
||||||
// We generate a unique name for the new window class to prevent name collisions
|
// We generate a unique name for the new window class to prevent name collisions
|
||||||
let class_name_str = format!("Baseview-{}", generate_guid());
|
let class_name_str = format!("Baseview-{}", generate_guid());
|
||||||
|
@ -390,6 +418,7 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) {
|
||||||
/// `handler` from indirectly triggering other events that would also need to be handled using
|
/// `handler` from indirectly triggering other events that would also need to be handled using
|
||||||
/// `handler`.
|
/// `handler`.
|
||||||
struct WindowState {
|
struct WindowState {
|
||||||
|
hwnd: HWND,
|
||||||
window_class: ATOM,
|
window_class: ATOM,
|
||||||
window_info: RefCell<WindowInfo>,
|
window_info: RefCell<WindowInfo>,
|
||||||
_parent_handle: Option<ParentHandle>,
|
_parent_handle: Option<ParentHandle>,
|
||||||
|
@ -399,6 +428,13 @@ struct WindowState {
|
||||||
scale_policy: WindowScalePolicy,
|
scale_policy: WindowScalePolicy,
|
||||||
dw_style: u32,
|
dw_style: u32,
|
||||||
|
|
||||||
|
/// Tasks that should be executed at the end of `wnd_proc`. This is needed to avoid mutably
|
||||||
|
/// borrowing the fields from `WindowState` more than once. For instance, when the window
|
||||||
|
/// handler requests a resize in response to a keyboard event, the window state will already be
|
||||||
|
/// borrowed in `wnd_proc`. So the `resize()` function below cannot also mutably borrow that
|
||||||
|
/// window state at the same time.
|
||||||
|
deferred_tasks: Rc<RefCell<VecDeque<WindowTask>>>,
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
gl_context: Rc<Option<GlContext>>,
|
gl_context: Rc<Option<GlContext>>,
|
||||||
}
|
}
|
||||||
|
@ -406,18 +442,64 @@ struct WindowState {
|
||||||
impl WindowState {
|
impl WindowState {
|
||||||
#[cfg(not(feature = "opengl"))]
|
#[cfg(not(feature = "opengl"))]
|
||||||
fn create_window(&self, hwnd: HWND) -> Window {
|
fn create_window(&self, hwnd: HWND) -> Window {
|
||||||
Window { hwnd }
|
Window { hwnd, deferred_tasks: self.deferred_tasks.clone() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
fn create_window(&self, hwnd: HWND) -> Window {
|
fn create_window(&self, hwnd: HWND) -> Window {
|
||||||
Window { hwnd, gl_context: self.gl_context.clone() }
|
Window {
|
||||||
|
hwnd,
|
||||||
|
deferred_tasks: self.deferred_tasks.clone(),
|
||||||
|
gl_context: self.gl_context.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle a deferred task as described in [`Self::deferred_tasks
|
||||||
|
pub(self) fn handle_deferred_task(&self, task: WindowTask) {
|
||||||
|
match task {
|
||||||
|
WindowTask::Resize(size) => {
|
||||||
|
let window_info = {
|
||||||
|
let mut window_info = self.window_info.borrow_mut();
|
||||||
|
let scaling = window_info.scale();
|
||||||
|
*window_info = WindowInfo::from_logical_size(size, scaling);
|
||||||
|
|
||||||
|
*window_info
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
SetWindowPos(
|
||||||
|
self.hwnd,
|
||||||
|
self.hwnd,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
window_info.physical_size().width as i32,
|
||||||
|
window_info.physical_size().height as i32,
|
||||||
|
SWP_NOZORDER | SWP_NOMOVE,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tasks that must be deferred until the end of [`wnd_proc()`] to avoid reentrant `WindowState`
|
||||||
|
/// borrows. See the docstring on [`WindowState::deferred_tasks`] for more information.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum WindowTask {
|
||||||
|
/// Resize the window to the given size. The size is in logical pixels. DPI scaling is applied
|
||||||
|
/// automatically.
|
||||||
|
Resize(Size),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
|
/// The HWND belonging to this window. The window's actual state is stored in the `WindowState`
|
||||||
|
/// struct associated with this HWND through `unsafe { GetWindowLongPtrW(self.hwnd,
|
||||||
|
/// GWLP_USERDATA) } as *const WindowState`.
|
||||||
hwnd: HWND,
|
hwnd: HWND,
|
||||||
|
|
||||||
|
/// See [`WindowState::deferred_tasks`].
|
||||||
|
deferred_tasks: Rc<RefCell<VecDeque<WindowTask>>>,
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
gl_context: Rc<Option<GlContext>>,
|
gl_context: Rc<Option<GlContext>>,
|
||||||
}
|
}
|
||||||
|
@ -546,11 +628,20 @@ impl Window {
|
||||||
GlContext::create(&handle, gl_config).expect("Could not create OpenGL context")
|
GlContext::create(&handle, gl_config).expect("Could not create OpenGL context")
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// The build closure shouldn't be enqueueing deferred tasks yet, but we'll try handling
|
||||||
|
// them just in case to avoid losing them
|
||||||
|
let deferred_tasks: Rc<RefCell<VecDeque<WindowTask>>> =
|
||||||
|
Rc::new(RefCell::new(VecDeque::new()));
|
||||||
|
|
||||||
#[cfg(not(feature = "opengl"))]
|
#[cfg(not(feature = "opengl"))]
|
||||||
let handler = build(&mut crate::Window::new(&mut Window { hwnd }));
|
let handler = build(&mut crate::Window::new(&mut Window {
|
||||||
|
hwnd,
|
||||||
|
deferred_tasks: deferred_tasks.clone(),
|
||||||
|
}));
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
let handler = build(&mut crate::Window::new(&mut Window {
|
let handler = build(&mut crate::Window::new(&mut Window {
|
||||||
hwnd,
|
hwnd,
|
||||||
|
deferred_tasks: deferred_tasks.clone(),
|
||||||
gl_context: gl_context.clone(),
|
gl_context: gl_context.clone(),
|
||||||
}));
|
}));
|
||||||
let handler = RefCell::new(Box::new(handler));
|
let handler = RefCell::new(Box::new(handler));
|
||||||
|
@ -559,6 +650,7 @@ impl Window {
|
||||||
let parent_handle = if parented { Some(parent_handle) } else { None };
|
let parent_handle = if parented { Some(parent_handle) } else { None };
|
||||||
|
|
||||||
let window_state = Box::new(WindowState {
|
let window_state = Box::new(WindowState {
|
||||||
|
hwnd,
|
||||||
window_class,
|
window_class,
|
||||||
window_info: RefCell::new(window_info),
|
window_info: RefCell::new(window_info),
|
||||||
_parent_handle: parent_handle,
|
_parent_handle: parent_handle,
|
||||||
|
@ -568,10 +660,18 @@ impl Window {
|
||||||
scale_policy: options.scale,
|
scale_policy: options.scale,
|
||||||
dw_style: flags,
|
dw_style: flags,
|
||||||
|
|
||||||
|
deferred_tasks: Rc::new(RefCell::new(VecDeque::with_capacity(4))),
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
gl_context,
|
gl_context,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If the plugin did queue up tasks as part of the build handler, then we'll process
|
||||||
|
// them now
|
||||||
|
for task in deferred_tasks.borrow_mut().drain(..) {
|
||||||
|
window_state.handle_deferred_task(task);
|
||||||
|
}
|
||||||
|
|
||||||
// Only works on Windows 10 unfortunately.
|
// Only works on Windows 10 unfortunately.
|
||||||
SetProcessDpiAwarenessContext(
|
SetProcessDpiAwarenessContext(
|
||||||
winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
|
winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
|
||||||
|
@ -633,6 +733,13 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, size: Size) {
|
||||||
|
// To avoid reentrant event handler calls we'll defer the actual resizing until after the
|
||||||
|
// event has been handled
|
||||||
|
let task = WindowTask::Resize(size);
|
||||||
|
self.deferred_tasks.borrow_mut().push_back(task);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
pub fn gl_context(&self) -> Option<&GlContext> {
|
pub fn gl_context(&self) -> Option<&GlContext> {
|
||||||
self.gl_context.as_ref().as_ref()
|
self.gl_context.as_ref().as_ref()
|
||||||
|
|
|
@ -99,12 +99,9 @@ impl<'a> Window<'a> {
|
||||||
self.window.close();
|
self.window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resize the window to the given size.
|
/// Resize the window to the given size. The size is always in logical pixels. DPI scaling will
|
||||||
///
|
/// automatically be accounted for.
|
||||||
/// # TODO
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||||
///
|
|
||||||
/// This is currently only supported on Linux.
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
pub fn resize(&mut self, size: Size) {
|
pub fn resize(&mut self, size: Size) {
|
||||||
self.window.resize(size);
|
self.window.resize(size);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue