Handle set_mouse_cursor method on windows (#186)
This adds the ability to change the cursor on Windows platforms. For some reason there are very few default cursors included in Windows so a lot of cursors available on other platforms aren't available yet, which is why many of the `MouseCursor` options just maps to `IDC_ARROW`. I'll look into adding custom cursors next. `LoadCursorW` is supposedly superseded by [LoadImageW](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadcursorw) but I couldn't find a way to do it with that one that doesn't crash. Tested with Vizia's cursor icon example [here](https://github.com/Fredemus/vizia/tree/baseview-window-events) and everything seems to work as it should. Also tested by hovering over some buttons on a plugin.
This commit is contained in:
parent
905854d026
commit
be3d72d524
54
src/win/cursor.rs
Normal file
54
src/win/cursor.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
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,
|
||||
// an empty LPCWSTR results in the cursor being hidden
|
||||
MouseCursor::Hidden => std::ptr::null(),
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
mod cursor;
|
||||
mod drop_target;
|
||||
mod keyboard;
|
||||
mod window;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use winapi::shared::guiddef::GUID;
|
||||
use winapi::shared::minwindef::{ATOM, FALSE, LPARAM, LRESULT, UINT, WPARAM};
|
||||
use winapi::shared::minwindef::{ATOM, FALSE, LOWORD, LPARAM, LRESULT, UINT, WPARAM};
|
||||
use winapi::shared::windef::{HWND, RECT};
|
||||
use winapi::um::combaseapi::CoCreateGuid;
|
||||
use winapi::um::ole2::{OleInitialize, RegisterDragDrop, RevokeDragDrop};
|
||||
|
@ -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(s),
|
||||
// If it returns `Some(1)`, the current window decides what the cursor is
|
||||
WM_SETCURSOR => {
|
||||
let low_word = LOWORD(lparam as u32) 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.get()));
|
||||
unsafe {
|
||||
SetCursor(cursor);
|
||||
}
|
||||
Some(1)
|
||||
} 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<KeyboardState>,
|
||||
mouse_button_counter: Cell<usize>,
|
||||
mouse_was_outside_window: RefCell<bool>,
|
||||
cursor_icon: Cell<MouseCursor>,
|
||||
// Initialized late so the `Window` can hold a reference to this `WindowState`
|
||||
handler: RefCell<Option<Box<dyn WindowHandler>>>,
|
||||
_drop_target: RefCell<Option<Rc<DropTarget>>>,
|
||||
|
@ -685,6 +706,7 @@ impl Window<'_> {
|
|||
keyboard_state: RefCell::new(KeyboardState::new()),
|
||||
mouse_button_counter: Cell::new(0),
|
||||
mouse_was_outside_window: RefCell::new(true),
|
||||
cursor_icon: Cell::new(MouseCursor::Default),
|
||||
// The Window refers to this `WindowState`, so this `handler` needs to be
|
||||
// initialized later
|
||||
handler: RefCell::new(None),
|
||||
|
@ -790,8 +812,12 @@ 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) {
|
||||
self.state.cursor_icon.set(mouse_cursor);
|
||||
unsafe {
|
||||
let cursor = LoadCursorW(null_mut(), cursor_to_lpcwstr(mouse_cursor));
|
||||
SetCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "opengl")]
|
||||
|
|
Loading…
Reference in a new issue