mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-08-16 11:20:28 +03:00
fix(core/wry): implement resizing natively on Windows (#9862)
closes #7388 closes #9510 closes #9464 ref #9268 ref #9053 ref #8770 ref #8750 ref #4012
This commit is contained in:
parent
fafc238f72
commit
f29b788110
10
.changes/undecorated-resizing.md
Normal file
10
.changes/undecorated-resizing.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
"tauri": "patch:bug"
|
||||||
|
"tauri-runtime-wry": "patch:bug"
|
||||||
|
---
|
||||||
|
|
||||||
|
On Windows, handle resizing undecorated windows natively which improves performance and fixes a couple of annoyances with previous JS implementation:
|
||||||
|
- No more cursor flickering when moving the cursor across an edge.
|
||||||
|
- Can resize from top even when `data-tauri-drag-region` element exists there.
|
||||||
|
- Upon starting rezing, clicks don't go through elements behind it so no longer accidental clicks.
|
||||||
|
|
@ -1988,8 +1988,6 @@ pub struct WindowWrapper {
|
|||||||
webviews: Vec<WebviewWrapper>,
|
webviews: Vec<WebviewWrapper>,
|
||||||
window_event_listeners: WindowEventListeners,
|
window_event_listeners: WindowEventListeners,
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
is_window_fullscreen: bool,
|
|
||||||
#[cfg(windows)]
|
|
||||||
is_window_transparent: bool,
|
is_window_transparent: bool,
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
surface: Option<softbuffer::Surface<Arc<Window>, Arc<Window>>>,
|
surface: Option<softbuffer::Surface<Arc<Window>, Arc<Window>>>,
|
||||||
@ -2773,7 +2771,15 @@ fn handle_user_message<T: UserEvent>(
|
|||||||
WindowMessage::Destroy => {
|
WindowMessage::Destroy => {
|
||||||
panic!("cannot handle `WindowMessage::Destroy` on the main thread")
|
panic!("cannot handle `WindowMessage::Destroy` on the main thread")
|
||||||
}
|
}
|
||||||
WindowMessage::SetDecorations(decorations) => window.set_decorations(decorations),
|
WindowMessage::SetDecorations(decorations) => {
|
||||||
|
window.set_decorations(decorations);
|
||||||
|
#[cfg(windows)]
|
||||||
|
if decorations {
|
||||||
|
undecorated_resizing::detach_resize_handler(window.hwnd());
|
||||||
|
} else {
|
||||||
|
undecorated_resizing::attach_resize_handler(window.hwnd());
|
||||||
|
}
|
||||||
|
}
|
||||||
WindowMessage::SetShadow(_enable) => {
|
WindowMessage::SetShadow(_enable) => {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
window.set_undecorated_shadow(_enable);
|
window.set_undecorated_shadow(_enable);
|
||||||
@ -2806,10 +2812,6 @@ fn handle_user_message<T: UserEvent>(
|
|||||||
} else {
|
} else {
|
||||||
window.set_fullscreen(None)
|
window.set_fullscreen(None)
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
|
||||||
if let Some(w) = windows.0.borrow_mut().get_mut(&id) {
|
|
||||||
w.is_window_fullscreen = fullscreen;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
WindowMessage::SetFocus => {
|
WindowMessage::SetFocus => {
|
||||||
window.set_focus();
|
window.set_focus();
|
||||||
@ -3197,8 +3199,6 @@ fn handle_user_message<T: UserEvent>(
|
|||||||
Message::CreateRawWindow(window_id, handler, sender) => {
|
Message::CreateRawWindow(window_id, handler, sender) => {
|
||||||
let (label, builder) = handler();
|
let (label, builder) = handler();
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let is_window_fullscreen = builder.window.fullscreen.is_some();
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let is_window_transparent = builder.window.transparent;
|
let is_window_transparent = builder.window.transparent;
|
||||||
|
|
||||||
@ -3232,8 +3232,6 @@ fn handle_user_message<T: UserEvent>(
|
|||||||
window_event_listeners: Default::default(),
|
window_event_listeners: Default::default(),
|
||||||
webviews: Vec::new(),
|
webviews: Vec::new(),
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
is_window_fullscreen,
|
|
||||||
#[cfg(windows)]
|
|
||||||
is_window_transparent,
|
is_window_transparent,
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
surface,
|
surface,
|
||||||
@ -3577,8 +3575,6 @@ fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let is_window_transparent = window_builder.inner.window.transparent;
|
let is_window_transparent = window_builder.inner.window.transparent;
|
||||||
#[cfg(windows)]
|
|
||||||
let is_window_fullscreen = window_builder.inner.window.fullscreen.is_some();
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
@ -3727,8 +3723,6 @@ fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
|
|||||||
webviews,
|
webviews,
|
||||||
window_event_listeners,
|
window_event_listeners,
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
is_window_fullscreen,
|
|
||||||
#[cfg(windows)]
|
|
||||||
is_window_transparent,
|
is_window_transparent,
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
surface,
|
surface,
|
||||||
@ -3818,11 +3812,6 @@ fn create_webview<T: UserEvent>(
|
|||||||
.with_accept_first_mouse(webview_attributes.accept_first_mouse)
|
.with_accept_first_mouse(webview_attributes.accept_first_mouse)
|
||||||
.with_hotkeys_zoom(webview_attributes.zoom_hotkeys_enabled);
|
.with_hotkeys_zoom(webview_attributes.zoom_hotkeys_enabled);
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
if kind == WebviewKind::WindowContent {
|
|
||||||
webview_builder = webview_builder.with_initialization_script(undecorated_resizing::SCRIPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if webview_attributes.drag_drop_handler_enabled {
|
if webview_attributes.drag_drop_handler_enabled {
|
||||||
let proxy = context.proxy.clone();
|
let proxy = context.proxy.clone();
|
||||||
let window_id_ = window_id.clone();
|
let window_id_ = window_id.clone();
|
||||||
@ -4054,15 +4043,19 @@ fn create_webview<T: UserEvent>(
|
|||||||
.build()
|
.build()
|
||||||
.map_err(|e| Error::CreateWebview(Box::new(e)))?;
|
.map_err(|e| Error::CreateWebview(Box::new(e)))?;
|
||||||
|
|
||||||
#[cfg(any(
|
|
||||||
target_os = "linux",
|
|
||||||
target_os = "dragonfly",
|
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "netbsd",
|
|
||||||
target_os = "openbsd"
|
|
||||||
))]
|
|
||||||
if kind == WebviewKind::WindowContent {
|
if kind == WebviewKind::WindowContent {
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "openbsd"
|
||||||
|
))]
|
||||||
undecorated_resizing::attach_resize_handler(&webview);
|
undecorated_resizing::attach_resize_handler(&webview);
|
||||||
|
#[cfg(windows)]
|
||||||
|
if window.is_resizable() && !window.is_decorated() {
|
||||||
|
undecorated_resizing::attach_resize_handler(window.hwnd());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -4127,13 +4120,6 @@ fn create_ipc_handler<T: UserEvent>(
|
|||||||
ipc_handler: Option<WebviewIpcHandler<T, Wry<T>>>,
|
ipc_handler: Option<WebviewIpcHandler<T, Wry<T>>>,
|
||||||
) -> Box<IpcHandler> {
|
) -> Box<IpcHandler> {
|
||||||
Box::new(move |request| {
|
Box::new(move |request| {
|
||||||
#[cfg(windows)]
|
|
||||||
if _kind == WebviewKind::WindowContent
|
|
||||||
&& undecorated_resizing::handle_request(context.clone(), *window_id.lock().unwrap(), &request)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(handler) = &ipc_handler {
|
if let Some(handler) = &ipc_handler {
|
||||||
handler(
|
handler(
|
||||||
DetachedWebview {
|
DetachedWebview {
|
||||||
|
@ -26,10 +26,6 @@ pub use self::gtk::*;
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub use self::windows::*;
|
pub use self::windows::*;
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
type WindowDimensions = u32;
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
type WindowDimensions = i32;
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
type WindowPositions = i32;
|
type WindowPositions = i32;
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
@ -49,27 +45,22 @@ enum HitTestResult {
|
|||||||
NoWhere,
|
NoWhere,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn hit_test(
|
fn hit_test(
|
||||||
width: WindowDimensions,
|
left: WindowPositions,
|
||||||
height: WindowDimensions,
|
top: WindowPositions,
|
||||||
x: WindowPositions,
|
right: WindowPositions,
|
||||||
y: WindowPositions,
|
bottom: WindowPositions,
|
||||||
|
cx: WindowPositions,
|
||||||
|
cy: WindowPositions,
|
||||||
border_x: WindowPositions,
|
border_x: WindowPositions,
|
||||||
border_y: WindowPositions,
|
border_y: WindowPositions,
|
||||||
) -> HitTestResult {
|
) -> HitTestResult {
|
||||||
#[cfg(windows)]
|
|
||||||
let (top, left) = (0, 0);
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let (top, left) = (0., 0.);
|
|
||||||
|
|
||||||
let bottom = top + height as WindowPositions;
|
|
||||||
let right = left + width as WindowPositions;
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let result = (LEFT * (x < left + border_x) as isize)
|
let result = (LEFT * (cx < left + border_x) as isize)
|
||||||
| (RIGHT * (x >= right - border_x) as isize)
|
| (RIGHT * (cx >= right - border_x) as isize)
|
||||||
| (TOP * (y < top + border_y) as isize)
|
| (TOP * (cy < top + border_y) as isize)
|
||||||
| (BOTTOM * (y >= bottom - border_y) as isize);
|
| (BOTTOM * (cy >= bottom - border_y) as isize);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
CLIENT => HitTestResult::Client,
|
CLIENT => HitTestResult::Client,
|
||||||
@ -89,117 +80,285 @@ fn hit_test(
|
|||||||
mod windows {
|
mod windows {
|
||||||
use super::{hit_test, HitTestResult};
|
use super::{hit_test, HitTestResult};
|
||||||
|
|
||||||
use tao::window::{CursorIcon, ResizeDirection, Window};
|
use windows::core::*;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::{
|
use windows::Win32::System::LibraryLoader::*;
|
||||||
GetSystemMetrics, SM_CXFRAME, SM_CXPADDEDBORDER, SM_CYFRAME,
|
use windows::Win32::UI::WindowsAndMessaging::*;
|
||||||
};
|
use windows::Win32::{Foundation::*, UI::Shell::SetWindowSubclass};
|
||||||
|
use windows::Win32::{Graphics::Gdi::*, UI::Shell::DefSubclassProc};
|
||||||
const MESSAGE_MOUSEMOVE: &str = "__internal_on_mousemove__|";
|
|
||||||
const MESSAGE_MOUSEDOWN: &str = "__internal_on_mousedown__|";
|
|
||||||
pub const SCRIPT: &str = r#"
|
|
||||||
;(function () {
|
|
||||||
document.addEventListener('mousemove', (e) => {
|
|
||||||
window.ipc.postMessage(
|
|
||||||
`__internal_on_mousemove__|${e.clientX},${e.clientY}`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
document.addEventListener('mousedown', (e) => {
|
|
||||||
if (e.button === 0) {
|
|
||||||
window.ipc.postMessage(
|
|
||||||
`__internal_on_mousedown__|${e.clientX},${e.clientY}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})()
|
|
||||||
"#;
|
|
||||||
|
|
||||||
impl HitTestResult {
|
impl HitTestResult {
|
||||||
fn drag_resize_window(&self, window: &Window) {
|
fn to_win32(self) -> i32 {
|
||||||
self.change_cursor(window);
|
match self {
|
||||||
let edge = match self {
|
HitTestResult::Left => HTLEFT as _,
|
||||||
HitTestResult::Left => ResizeDirection::West,
|
HitTestResult::Right => HTRIGHT as _,
|
||||||
HitTestResult::Right => ResizeDirection::East,
|
HitTestResult::Top => HTTOP as _,
|
||||||
HitTestResult::Top => ResizeDirection::North,
|
HitTestResult::Bottom => HTBOTTOM as _,
|
||||||
HitTestResult::Bottom => ResizeDirection::South,
|
HitTestResult::TopLeft => HTTOPLEFT as _,
|
||||||
HitTestResult::TopLeft => ResizeDirection::NorthWest,
|
HitTestResult::TopRight => HTTOPRIGHT as _,
|
||||||
HitTestResult::TopRight => ResizeDirection::NorthEast,
|
HitTestResult::BottomLeft => HTBOTTOMLEFT as _,
|
||||||
HitTestResult::BottomLeft => ResizeDirection::SouthWest,
|
HitTestResult::BottomRight => HTBOTTOMRIGHT as _,
|
||||||
HitTestResult::BottomRight => ResizeDirection::SouthEast,
|
_ => HTTRANSPARENT,
|
||||||
|
}
|
||||||
// if not on an edge, don't start resizing
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
let _ = window.drag_resize_window(edge);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn change_cursor(&self, window: &Window) {
|
|
||||||
let cursor = match self {
|
|
||||||
HitTestResult::Left => CursorIcon::WResize,
|
|
||||||
HitTestResult::Right => CursorIcon::EResize,
|
|
||||||
HitTestResult::Top => CursorIcon::NResize,
|
|
||||||
HitTestResult::Bottom => CursorIcon::SResize,
|
|
||||||
HitTestResult::TopLeft => CursorIcon::NwResize,
|
|
||||||
HitTestResult::TopRight => CursorIcon::NeResize,
|
|
||||||
HitTestResult::BottomLeft => CursorIcon::SwResize,
|
|
||||||
HitTestResult::BottomRight => CursorIcon::SeResize,
|
|
||||||
|
|
||||||
// if not on an edge, don't change the cursor, otherwise we cause flickering
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
window.set_cursor_icon(cursor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether handled or not
|
const CLASS_NAME: PCWSTR = w!("TAURI_DRAG_RESIZE_BORDERS");
|
||||||
pub fn handle_request<T: crate::UserEvent>(
|
const WINDOW_NAME: PCWSTR = w!("TAURI_DRAG_RESIZE_WINDOW");
|
||||||
context: crate::Context<T>,
|
|
||||||
window_id: crate::WindowId,
|
|
||||||
request: &http::Request<String>,
|
|
||||||
) -> bool {
|
|
||||||
if let Some(args) = request.body().strip_prefix(MESSAGE_MOUSEMOVE) {
|
|
||||||
if let Some(window) = context.main_thread.windows.0.borrow().get(&window_id) {
|
|
||||||
if let Some(w) = window.inner.as_ref() {
|
|
||||||
if !w.is_decorated()
|
|
||||||
&& w.is_resizable()
|
|
||||||
&& !w.is_maximized()
|
|
||||||
&& !window.is_window_fullscreen
|
|
||||||
{
|
|
||||||
let (x, y) = args.split_once(',').unwrap();
|
|
||||||
let (x, y) = (x.parse().unwrap(), y.parse().unwrap());
|
|
||||||
let size = w.inner_size();
|
|
||||||
let padded_border = unsafe { GetSystemMetrics(SM_CXPADDEDBORDER) };
|
|
||||||
let border_x = unsafe { GetSystemMetrics(SM_CXFRAME) + padded_border };
|
|
||||||
let border_y = unsafe { GetSystemMetrics(SM_CYFRAME) + padded_border };
|
|
||||||
hit_test(size.width, size.height, x, y, border_x, border_y).change_cursor(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
pub fn attach_resize_handler(hwnd: isize) {
|
||||||
}
|
let parent = HWND(hwnd);
|
||||||
if let Some(args) = request.body().strip_prefix(MESSAGE_MOUSEDOWN) {
|
|
||||||
if let Some(window) = context.main_thread.windows.0.borrow().get(&window_id) {
|
|
||||||
if let Some(w) = window.inner.as_ref() {
|
|
||||||
if !w.is_decorated()
|
|
||||||
&& w.is_resizable()
|
|
||||||
&& !w.is_maximized()
|
|
||||||
&& !window.is_window_fullscreen
|
|
||||||
{
|
|
||||||
let (x, y) = args.split_once(',').unwrap();
|
|
||||||
let (x, y) = (x.parse().unwrap(), y.parse().unwrap());
|
|
||||||
let size = w.inner_size();
|
|
||||||
let padded_border = unsafe { GetSystemMetrics(SM_CXPADDEDBORDER) };
|
|
||||||
let border_x = unsafe { GetSystemMetrics(SM_CXFRAME) + padded_border };
|
|
||||||
let border_y = unsafe { GetSystemMetrics(SM_CYFRAME) + padded_border };
|
|
||||||
hit_test(size.width, size.height, x, y, border_x, border_y).drag_resize_window(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
let child = unsafe { FindWindowExW(parent, HWND::default(), CLASS_NAME, WINDOW_NAME) };
|
||||||
|
if child != HWND::default() {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
let class = WNDCLASSEXW {
|
||||||
|
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
|
||||||
|
style: WNDCLASS_STYLES::default(),
|
||||||
|
lpfnWndProc: Some(drag_resize_window_proc),
|
||||||
|
cbClsExtra: 0,
|
||||||
|
cbWndExtra: 0,
|
||||||
|
hInstance: unsafe { HINSTANCE(GetModuleHandleW(PCWSTR::null()).unwrap_or_default().0) },
|
||||||
|
hIcon: HICON::default(),
|
||||||
|
hCursor: HCURSOR::default(),
|
||||||
|
hbrBackground: HBRUSH::default(),
|
||||||
|
lpszMenuName: PCWSTR::null(),
|
||||||
|
lpszClassName: CLASS_NAME,
|
||||||
|
hIconSm: HICON::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { RegisterClassExW(&class) };
|
||||||
|
|
||||||
|
let mut rect = RECT::default();
|
||||||
|
unsafe { GetClientRect(parent, &mut rect).unwrap() };
|
||||||
|
let width = rect.right - rect.left;
|
||||||
|
let height = rect.bottom - rect.top;
|
||||||
|
|
||||||
|
let drag_window = unsafe {
|
||||||
|
CreateWindowExW(
|
||||||
|
WINDOW_EX_STYLE::default(),
|
||||||
|
CLASS_NAME,
|
||||||
|
WINDOW_NAME,
|
||||||
|
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
parent,
|
||||||
|
HMENU::default(),
|
||||||
|
GetModuleHandleW(PCWSTR::null()).unwrap_or_default(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
set_drag_hwnd_rgn(drag_window, width, height);
|
||||||
|
|
||||||
|
let _ = SetWindowPos(
|
||||||
|
drag_window,
|
||||||
|
HWND_TOP,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE,
|
||||||
|
);
|
||||||
|
|
||||||
|
let _ = SetWindowSubclass(
|
||||||
|
parent,
|
||||||
|
Some(subclass_parent),
|
||||||
|
(WM_USER + 1) as _,
|
||||||
|
drag_window.0 as _,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn subclass_parent(
|
||||||
|
parent: HWND,
|
||||||
|
msg: u32,
|
||||||
|
wparam: WPARAM,
|
||||||
|
lparam: LPARAM,
|
||||||
|
_: usize,
|
||||||
|
child: usize,
|
||||||
|
) -> LRESULT {
|
||||||
|
if msg == WM_SIZE {
|
||||||
|
let child = HWND(child as _);
|
||||||
|
|
||||||
|
if is_maximized(parent).unwrap_or(false) {
|
||||||
|
let _ = SetWindowPos(
|
||||||
|
child,
|
||||||
|
HWND_TOP,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let mut rect = RECT::default();
|
||||||
|
if GetClientRect(parent, &mut rect).is_ok() {
|
||||||
|
let width = rect.right - rect.left;
|
||||||
|
let height = rect.bottom - rect.top;
|
||||||
|
|
||||||
|
let _ = SetWindowPos(
|
||||||
|
child,
|
||||||
|
HWND_TOP,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE,
|
||||||
|
);
|
||||||
|
|
||||||
|
set_drag_hwnd_rgn(child, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DefSubclassProc(parent, msg, wparam, lparam)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn drag_resize_window_proc(
|
||||||
|
child: HWND,
|
||||||
|
msg: u32,
|
||||||
|
wparam: WPARAM,
|
||||||
|
lparam: LPARAM,
|
||||||
|
) -> LRESULT {
|
||||||
|
match msg {
|
||||||
|
WM_NCHITTEST => {
|
||||||
|
let parent = GetParent(child);
|
||||||
|
let style = GetWindowLongPtrW(parent, GWL_STYLE);
|
||||||
|
let style = WINDOW_STYLE(style as u32);
|
||||||
|
|
||||||
|
let is_resizable = (style & WS_SIZEBOX).0 != 0;
|
||||||
|
if !is_resizable {
|
||||||
|
return DefWindowProcW(child, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rect = RECT::default();
|
||||||
|
if GetWindowRect(child, &mut rect).is_err() {
|
||||||
|
return DefWindowProcW(child, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (cx, cy) = (GET_X_LPARAM(lparam) as i32, GET_Y_LPARAM(lparam) as i32);
|
||||||
|
|
||||||
|
let padded_border = GetSystemMetrics(SM_CXPADDEDBORDER);
|
||||||
|
let border_x = GetSystemMetrics(SM_CXFRAME) + padded_border;
|
||||||
|
let border_y = GetSystemMetrics(SM_CYFRAME) + padded_border;
|
||||||
|
|
||||||
|
let res = hit_test(
|
||||||
|
rect.left,
|
||||||
|
rect.top,
|
||||||
|
rect.right,
|
||||||
|
rect.bottom,
|
||||||
|
cx,
|
||||||
|
cy,
|
||||||
|
border_x,
|
||||||
|
border_y,
|
||||||
|
);
|
||||||
|
|
||||||
|
return LRESULT(res.to_win32() as _);
|
||||||
|
}
|
||||||
|
|
||||||
|
WM_NCLBUTTONDOWN => {
|
||||||
|
let parent = GetParent(child);
|
||||||
|
let style = GetWindowLongPtrW(parent, GWL_STYLE);
|
||||||
|
let style = WINDOW_STYLE(style as u32);
|
||||||
|
|
||||||
|
let is_resizable = (style & WS_SIZEBOX).0 != 0;
|
||||||
|
if !is_resizable {
|
||||||
|
return DefWindowProcW(child, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rect = RECT::default();
|
||||||
|
if GetWindowRect(child, &mut rect).is_err() {
|
||||||
|
return DefWindowProcW(child, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (cx, cy) = (GET_X_LPARAM(lparam) as i32, GET_Y_LPARAM(lparam) as i32);
|
||||||
|
|
||||||
|
let padded_border = GetSystemMetrics(SM_CXPADDEDBORDER);
|
||||||
|
let border_x = GetSystemMetrics(SM_CXFRAME) + padded_border;
|
||||||
|
let border_y = GetSystemMetrics(SM_CYFRAME) + padded_border;
|
||||||
|
|
||||||
|
let res = hit_test(
|
||||||
|
rect.left,
|
||||||
|
rect.top,
|
||||||
|
rect.right,
|
||||||
|
rect.bottom,
|
||||||
|
cx,
|
||||||
|
cy,
|
||||||
|
border_x,
|
||||||
|
border_y,
|
||||||
|
);
|
||||||
|
|
||||||
|
if res != HitTestResult::NoWhere {
|
||||||
|
let points = POINTS {
|
||||||
|
x: cx as i16,
|
||||||
|
y: cy as i16,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = PostMessageW(
|
||||||
|
parent,
|
||||||
|
WM_NCLBUTTONDOWN,
|
||||||
|
WPARAM(res.to_win32() as _),
|
||||||
|
LPARAM(&points as *const _ as _),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LRESULT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DefWindowProcW(child, msg, wparam, lparam)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detach_resize_handler(hwnd: isize) {
|
||||||
|
let hwnd = HWND(hwnd);
|
||||||
|
|
||||||
|
let child = unsafe { FindWindowExW(hwnd, HWND::default(), CLASS_NAME, WINDOW_NAME) };
|
||||||
|
if child == HWND::default() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = unsafe { DestroyWindow(child) };
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn set_drag_hwnd_rgn(hwnd: HWND, width: i32, height: i32) {
|
||||||
|
let padded_border = GetSystemMetrics(SM_CXPADDEDBORDER);
|
||||||
|
let border_x = GetSystemMetrics(SM_CXFRAME) + padded_border;
|
||||||
|
let border_y = GetSystemMetrics(SM_CYFRAME) + padded_border;
|
||||||
|
|
||||||
|
let hrgn1 = CreateRectRgn(0, 0, width, height);
|
||||||
|
let hrgn2 = CreateRectRgn(border_x, border_y, width - border_x, height - border_y);
|
||||||
|
CombineRgn(hrgn1, hrgn1, hrgn2, RGN_DIFF);
|
||||||
|
SetWindowRgn(hwnd, hrgn1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_maximized(window: HWND) -> windows::core::Result<bool> {
|
||||||
|
let mut placement = WINDOWPLACEMENT {
|
||||||
|
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
|
||||||
|
..WINDOWPLACEMENT::default()
|
||||||
|
};
|
||||||
|
unsafe { GetWindowPlacement(window, &mut placement)? };
|
||||||
|
Ok(placement.showCmd == SW_MAXIMIZE.0 as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of the `GET_X_LPARAM` macro.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[inline]
|
||||||
|
fn GET_X_LPARAM(lparam: LPARAM) -> i16 {
|
||||||
|
((lparam.0 as usize) & 0xFFFF) as u16 as i16
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of the `GET_Y_LPARAM` macro.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[inline]
|
||||||
|
fn GET_Y_LPARAM(lparam: LPARAM) -> i16 {
|
||||||
|
(((lparam.0 as usize) & 0xFFFF_0000) >> 16) as u16 as i16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,8 +414,10 @@ mod gtk {
|
|||||||
let (client_x, client_y) = (root_x - window_x as f64, root_y - window_y as f64);
|
let (client_x, client_y) = (root_x - window_x as f64, root_y - window_y as f64);
|
||||||
let border = window.scale_factor() * BORDERLESS_RESIZE_INSET;
|
let border = window.scale_factor() * BORDERLESS_RESIZE_INSET;
|
||||||
let edge = hit_test(
|
let edge = hit_test(
|
||||||
window.width(),
|
0.0,
|
||||||
window.height(),
|
0.0,
|
||||||
|
window.width() as f64,
|
||||||
|
window.height() as f64,
|
||||||
client_x,
|
client_x,
|
||||||
client_y,
|
client_y,
|
||||||
border as _,
|
border as _,
|
||||||
@ -294,8 +455,10 @@ mod gtk {
|
|||||||
let (client_x, client_y) = (root_x - window_x as f64, root_y - window_y as f64);
|
let (client_x, client_y) = (root_x - window_x as f64, root_y - window_y as f64);
|
||||||
let border = window.scale_factor() * BORDERLESS_RESIZE_INSET;
|
let border = window.scale_factor() * BORDERLESS_RESIZE_INSET;
|
||||||
let edge = hit_test(
|
let edge = hit_test(
|
||||||
window.width(),
|
0.0,
|
||||||
window.height(),
|
0.0,
|
||||||
|
window.width() as f64,
|
||||||
|
window.height() as f64,
|
||||||
client_x,
|
client_x,
|
||||||
client_y,
|
client_y,
|
||||||
border as _,
|
border as _,
|
||||||
|
Loading…
Reference in New Issue
Block a user