From 7b926c9c0b6ec9380ef01de53fe800b21ccdd0db Mon Sep 17 00:00:00 2001 From: Fredemus Date: Mon, 1 Apr 2024 04:42:20 +0200 Subject: [PATCH] Handle set_mouse_cursor event on windows --- src/mouse_cursor.rs | 6 ----- src/win/cursor.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++ src/win/mod.rs | 1 + src/win/window.rs | 54 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 src/win/cursor.rs diff --git a/src/mouse_cursor.rs b/src/mouse_cursor.rs index 16fc199d..0038f2c0 100644 --- a/src/mouse_cursor.rs +++ b/src/mouse_cursor.rs @@ -41,9 +41,3 @@ pub enum MouseCursor { ColResize, RowResize, } - -impl Default for MouseCursor { - fn default() -> Self { - Self::Default - } -} diff --git a/src/win/cursor.rs b/src/win/cursor.rs new file mode 100644 index 00000000..8cb9831a --- /dev/null +++ b/src/win/cursor.rs @@ -0,0 +1,55 @@ +use crate::MouseCursor; +use winapi::{ + shared::ntdef::LPCWSTR, + um::winuser::{ + IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, IDC_NO, IDC_SIZEALL, + IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_WAIT, + }, +}; + +pub fn cursor_to_lpcwstr(cursor: MouseCursor) -> LPCWSTR { + match cursor { + MouseCursor::Default => IDC_ARROW, + MouseCursor::Hand => IDC_HAND, + MouseCursor::HandGrabbing => IDC_SIZEALL, + MouseCursor::Help => IDC_HELP, + + // hiding cursor can't be turned into an lpcwstr since it's done by ShowCursor instead of SetCursor + MouseCursor::Hidden => IDC_ARROW, + + MouseCursor::Text => IDC_IBEAM, + MouseCursor::VerticalText => IDC_IBEAM, + + MouseCursor::Working => IDC_WAIT, + MouseCursor::PtrWorking => IDC_APPSTARTING, + + MouseCursor::NotAllowed => IDC_NO, + MouseCursor::PtrNotAllowed => IDC_NO, + + MouseCursor::ZoomIn => IDC_ARROW, + MouseCursor::ZoomOut => IDC_ARROW, + + MouseCursor::Alias => IDC_ARROW, + MouseCursor::Copy => IDC_ARROW, + MouseCursor::Move => IDC_SIZEALL, + MouseCursor::AllScroll => IDC_SIZEALL, + MouseCursor::Cell => IDC_CROSS, + MouseCursor::Crosshair => IDC_CROSS, + + MouseCursor::EResize => IDC_SIZEWE, + MouseCursor::NResize => IDC_SIZENS, + MouseCursor::NeResize => IDC_SIZENESW, + MouseCursor::NwResize => IDC_SIZENWSE, + MouseCursor::SResize => IDC_SIZENS, + MouseCursor::SeResize => IDC_SIZENWSE, + MouseCursor::SwResize => IDC_SIZENESW, + MouseCursor::WResize => IDC_SIZEWE, + MouseCursor::EwResize => IDC_SIZEWE, + MouseCursor::NsResize => IDC_SIZENS, + MouseCursor::NwseResize => IDC_SIZENWSE, + MouseCursor::NeswResize => IDC_SIZENESW, + + MouseCursor::ColResize => IDC_SIZEWE, + MouseCursor::RowResize => IDC_SIZENS, + } +} diff --git a/src/win/mod.rs b/src/win/mod.rs index f66c2bdb..00effa43 100644 --- a/src/win/mod.rs +++ b/src/win/mod.rs @@ -1,3 +1,4 @@ +mod cursor; mod drop_target; mod keyboard; mod window; diff --git a/src/win/window.rs b/src/win/window.rs index 8ed46b44..df3c4c62 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -7,15 +7,16 @@ use winapi::um::oleidl::LPDROPTARGET; use winapi::um::winuser::{ AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetDpiForWindow, GetFocus, GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, - RegisterClassW, ReleaseCapture, SetCapture, SetFocus, SetProcessDpiAwarenessContext, SetTimer, - SetWindowLongPtrW, SetWindowPos, TrackMouseEvent, TranslateMessage, UnregisterClassW, CS_OWNDC, - GET_XBUTTON_WPARAM, GWLP_USERDATA, IDC_ARROW, MSG, SWP_NOMOVE, SWP_NOZORDER, TRACKMOUSEEVENT, - WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE, WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN, - WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL, - WM_MOUSELEAVE, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY, WM_RBUTTONDOWN, WM_RBUTTONUP, - WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER, WM_USER, - WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, - WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, XBUTTON1, XBUTTON2, + RegisterClassW, ReleaseCapture, SetCapture, SetCursor, SetFocus, SetProcessDpiAwarenessContext, + SetTimer, SetWindowLongPtrW, SetWindowPos, TrackMouseEvent, TranslateMessage, UnregisterClassW, + CS_OWNDC, GET_XBUTTON_WPARAM, GWLP_USERDATA, HTCLIENT, IDC_ARROW, MSG, SWP_NOMOVE, + SWP_NOZORDER, TRACKMOUSEEVENT, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE, WM_DPICHANGED, + WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, + WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSELEAVE, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY, + WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, + WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, + WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, + XBUTTON1, XBUTTON2, }; use std::cell::{Cell, Ref, RefCell, RefMut}; @@ -37,6 +38,7 @@ use crate::{ WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; +use super::cursor::cursor_to_lpcwstr; use super::drop_target::DropTarget; use super::keyboard::KeyboardState; @@ -428,6 +430,24 @@ unsafe fn wnd_proc_inner( None } + // if WM_SETCURSOR returns `None`, WM_SETCURSOR continues to get handled by the outer window, + // If it return `Some(0)`, the current window decides what the cursor is + WM_SETCURSOR => { + let low_word = (lparam & 0xFFFF) as i16 as isize; + let mouse_in_window = low_word == HTCLIENT; + if mouse_in_window { + // Here we need to set the cursor back to what the state says, since it can have changed when outside the window + let cursor = + LoadCursorW(null_mut(), cursor_to_lpcwstr(*window_state.cursor_icon.borrow())); + unsafe { + SetCursor(cursor); + } + Some(0) + } else { + // cursor is being changed by some other window, e.g. when having mouse on the borders to resize it + None + } + } // NOTE: `WM_NCDESTROY` is handled in the outer function because this deallocates the window // state BV_WINDOW_MUST_CLOSE => { @@ -480,6 +500,7 @@ pub(super) struct WindowState { keyboard_state: RefCell, mouse_button_counter: Cell, mouse_was_outside_window: RefCell, + cursor_icon: RefCell, // Initialized late so the `Window` can hold a reference to this `WindowState` handler: RefCell>>, _drop_target: RefCell>>, @@ -544,6 +565,13 @@ impl WindowState { ) }; } + WindowTask::SetMouseCursor(icon) => { + *self.cursor_icon.borrow_mut() = icon; + unsafe { + let cursor = LoadCursorW(null_mut(), cursor_to_lpcwstr(icon)); + SetCursor(cursor); + } + } } } } @@ -555,6 +583,8 @@ pub(super) enum WindowTask { /// Resize the window to the given size. The size is in logical pixels. DPI scaling is applied /// automatically. Resize(Size), + // Change the icon of the mouse cursor. + SetMouseCursor(MouseCursor), } pub struct Window<'a> { @@ -685,6 +715,7 @@ impl Window<'_> { keyboard_state: RefCell::new(KeyboardState::new()), mouse_button_counter: Cell::new(0), mouse_was_outside_window: RefCell::new(true), + cursor_icon: RefCell::new(MouseCursor::Default), // The Window refers to this `WindowState`, so this `handler` needs to be // initialized later handler: RefCell::new(None), @@ -790,8 +821,9 @@ impl Window<'_> { self.state.deferred_tasks.borrow_mut().push_back(task); } - pub fn set_mouse_cursor(&mut self, _mouse_cursor: MouseCursor) { - todo!() + pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) { + let task = WindowTask::SetMouseCursor(mouse_cursor); + self.state.deferred_tasks.borrow_mut().push_back(task); } #[cfg(feature = "opengl")]