|
@@ -26,10 +26,6 @@ pub use self::gtk::*;
|
|
|
#[cfg(windows)]
|
|
|
pub use self::windows::*;
|
|
|
|
|
|
-#[cfg(windows)]
|
|
|
-type WindowDimensions = u32;
|
|
|
-#[cfg(not(windows))]
|
|
|
-type WindowDimensions = i32;
|
|
|
#[cfg(windows)]
|
|
|
type WindowPositions = i32;
|
|
|
#[cfg(not(windows))]
|
|
@@ -49,27 +45,22 @@ enum HitTestResult {
|
|
|
NoWhere,
|
|
|
}
|
|
|
|
|
|
+#[allow(clippy::too_many_arguments)]
|
|
|
fn hit_test(
|
|
|
- width: WindowDimensions,
|
|
|
- height: WindowDimensions,
|
|
|
- x: WindowPositions,
|
|
|
- y: WindowPositions,
|
|
|
+ left: WindowPositions,
|
|
|
+ top: WindowPositions,
|
|
|
+ right: WindowPositions,
|
|
|
+ bottom: WindowPositions,
|
|
|
+ cx: WindowPositions,
|
|
|
+ cy: WindowPositions,
|
|
|
border_x: WindowPositions,
|
|
|
border_y: WindowPositions,
|
|
|
) -> 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]
|
|
|
- let result = (LEFT * (x < left + border_x) as isize)
|
|
|
- | (RIGHT * (x >= right - border_x) as isize)
|
|
|
- | (TOP * (y < top + border_y) as isize)
|
|
|
- | (BOTTOM * (y >= bottom - border_y) as isize);
|
|
|
+ let result = (LEFT * (cx < left + border_x) as isize)
|
|
|
+ | (RIGHT * (cx >= right - border_x) as isize)
|
|
|
+ | (TOP * (cy < top + border_y) as isize)
|
|
|
+ | (BOTTOM * (cy >= bottom - border_y) as isize);
|
|
|
|
|
|
match result {
|
|
|
CLIENT => HitTestResult::Client,
|
|
@@ -89,117 +80,285 @@ fn hit_test(
|
|
|
mod windows {
|
|
|
use super::{hit_test, HitTestResult};
|
|
|
|
|
|
- use tao::window::{CursorIcon, ResizeDirection, Window};
|
|
|
- use windows::Win32::UI::WindowsAndMessaging::{
|
|
|
- GetSystemMetrics, SM_CXFRAME, SM_CXPADDEDBORDER, SM_CYFRAME,
|
|
|
- };
|
|
|
-
|
|
|
- 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}`
|
|
|
- )
|
|
|
- }
|
|
|
- })
|
|
|
-})()
|
|
|
-"#;
|
|
|
+ use windows::core::*;
|
|
|
+ use windows::Win32::System::LibraryLoader::*;
|
|
|
+ use windows::Win32::UI::WindowsAndMessaging::*;
|
|
|
+ use windows::Win32::{Foundation::*, UI::Shell::SetWindowSubclass};
|
|
|
+ use windows::Win32::{Graphics::Gdi::*, UI::Shell::DefSubclassProc};
|
|
|
|
|
|
impl HitTestResult {
|
|
|
- fn drag_resize_window(&self, window: &Window) {
|
|
|
- self.change_cursor(window);
|
|
|
- let edge = match self {
|
|
|
- HitTestResult::Left => ResizeDirection::West,
|
|
|
- HitTestResult::Right => ResizeDirection::East,
|
|
|
- HitTestResult::Top => ResizeDirection::North,
|
|
|
- HitTestResult::Bottom => ResizeDirection::South,
|
|
|
- HitTestResult::TopLeft => ResizeDirection::NorthWest,
|
|
|
- HitTestResult::TopRight => ResizeDirection::NorthEast,
|
|
|
- HitTestResult::BottomLeft => ResizeDirection::SouthWest,
|
|
|
- HitTestResult::BottomRight => ResizeDirection::SouthEast,
|
|
|
-
|
|
|
- // if not on an edge, don't start resizing
|
|
|
- _ => return,
|
|
|
- };
|
|
|
- let _ = window.drag_resize_window(edge);
|
|
|
+ fn to_win32(self) -> i32 {
|
|
|
+ match self {
|
|
|
+ HitTestResult::Left => HTLEFT as _,
|
|
|
+ HitTestResult::Right => HTRIGHT as _,
|
|
|
+ HitTestResult::Top => HTTOP as _,
|
|
|
+ HitTestResult::Bottom => HTBOTTOM as _,
|
|
|
+ HitTestResult::TopLeft => HTTOPLEFT as _,
|
|
|
+ HitTestResult::TopRight => HTTOPRIGHT as _,
|
|
|
+ HitTestResult::BottomLeft => HTBOTTOMLEFT as _,
|
|
|
+ HitTestResult::BottomRight => HTBOTTOMRIGHT as _,
|
|
|
+ _ => HTTRANSPARENT,
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ const CLASS_NAME: PCWSTR = w!("TAURI_DRAG_RESIZE_BORDERS");
|
|
|
+ const WINDOW_NAME: PCWSTR = w!("TAURI_DRAG_RESIZE_WINDOW");
|
|
|
+
|
|
|
+ pub fn attach_resize_handler(hwnd: isize) {
|
|
|
+ let parent = HWND(hwnd);
|
|
|
|
|
|
- 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);
|
|
|
+ let child = unsafe { FindWindowExW(parent, HWND::default(), CLASS_NAME, WINDOW_NAME) };
|
|
|
+ if child != HWND::default() {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 _,
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Returns whether handled or not
|
|
|
- pub fn handle_request<T: crate::UserEvent>(
|
|
|
- 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);
|
|
|
- }
|
|
|
+ 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);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- return true;
|
|
|
}
|
|
|
- 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);
|
|
|
- }
|
|
|
+
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
- return true;
|
|
|
+ _ => {}
|
|
|
}
|
|
|
|
|
|
- false
|
|
|
+ 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 border = window.scale_factor() * BORDERLESS_RESIZE_INSET;
|
|
|
let edge = hit_test(
|
|
|
- window.width(),
|
|
|
- window.height(),
|
|
|
+ 0.0,
|
|
|
+ 0.0,
|
|
|
+ window.width() as f64,
|
|
|
+ window.height() as f64,
|
|
|
client_x,
|
|
|
client_y,
|
|
|
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 border = window.scale_factor() * BORDERLESS_RESIZE_INSET;
|
|
|
let edge = hit_test(
|
|
|
- window.width(),
|
|
|
- window.height(),
|
|
|
+ 0.0,
|
|
|
+ 0.0,
|
|
|
+ window.width() as f64,
|
|
|
+ window.height() as f64,
|
|
|
client_x,
|
|
|
client_y,
|
|
|
border as _,
|