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 drop_target;
|
||||||
mod keyboard;
|
mod keyboard;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use winapi::shared::guiddef::GUID;
|
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::shared::windef::{HWND, RECT};
|
||||||
use winapi::um::combaseapi::CoCreateGuid;
|
use winapi::um::combaseapi::CoCreateGuid;
|
||||||
use winapi::um::ole2::{OleInitialize, RegisterDragDrop, RevokeDragDrop};
|
use winapi::um::ole2::{OleInitialize, RegisterDragDrop, RevokeDragDrop};
|
||||||
|
@ -7,15 +7,16 @@ use winapi::um::oleidl::LPDROPTARGET;
|
||||||
use winapi::um::winuser::{
|
use winapi::um::winuser::{
|
||||||
AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW,
|
AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW,
|
||||||
GetDpiForWindow, GetFocus, GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW,
|
GetDpiForWindow, GetFocus, GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW,
|
||||||
RegisterClassW, ReleaseCapture, SetCapture, SetFocus, SetProcessDpiAwarenessContext, SetTimer,
|
RegisterClassW, ReleaseCapture, SetCapture, SetCursor, SetFocus, SetProcessDpiAwarenessContext,
|
||||||
SetWindowLongPtrW, SetWindowPos, TrackMouseEvent, TranslateMessage, UnregisterClassW, CS_OWNDC,
|
SetTimer, SetWindowLongPtrW, SetWindowPos, TrackMouseEvent, TranslateMessage, UnregisterClassW,
|
||||||
GET_XBUTTON_WPARAM, GWLP_USERDATA, IDC_ARROW, MSG, SWP_NOMOVE, SWP_NOZORDER, TRACKMOUSEEVENT,
|
CS_OWNDC, GET_XBUTTON_WPARAM, GWLP_USERDATA, HTCLIENT, IDC_ARROW, MSG, SWP_NOMOVE,
|
||||||
WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE, WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN,
|
SWP_NOZORDER, TRACKMOUSEEVENT, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE, WM_DPICHANGED,
|
||||||
WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL,
|
WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,
|
||||||
WM_MOUSELEAVE, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY, WM_RBUTTONDOWN, WM_RBUTTONUP,
|
WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSELEAVE, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY,
|
||||||
WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER, WM_USER,
|
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN,
|
||||||
WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX,
|
WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD,
|
||||||
WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, XBUTTON1, XBUTTON2,
|
WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE,
|
||||||
|
XBUTTON1, XBUTTON2,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
use std::cell::{Cell, Ref, RefCell, RefMut};
|
||||||
|
@ -37,6 +38,7 @@ use crate::{
|
||||||
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::cursor::cursor_to_lpcwstr;
|
||||||
use super::drop_target::DropTarget;
|
use super::drop_target::DropTarget;
|
||||||
use super::keyboard::KeyboardState;
|
use super::keyboard::KeyboardState;
|
||||||
|
|
||||||
|
@ -428,6 +430,24 @@ unsafe fn wnd_proc_inner(
|
||||||
|
|
||||||
None
|
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
|
// NOTE: `WM_NCDESTROY` is handled in the outer function because this deallocates the window
|
||||||
// state
|
// state
|
||||||
BV_WINDOW_MUST_CLOSE => {
|
BV_WINDOW_MUST_CLOSE => {
|
||||||
|
@ -480,6 +500,7 @@ pub(super) struct WindowState {
|
||||||
keyboard_state: RefCell<KeyboardState>,
|
keyboard_state: RefCell<KeyboardState>,
|
||||||
mouse_button_counter: Cell<usize>,
|
mouse_button_counter: Cell<usize>,
|
||||||
mouse_was_outside_window: RefCell<bool>,
|
mouse_was_outside_window: RefCell<bool>,
|
||||||
|
cursor_icon: Cell<MouseCursor>,
|
||||||
// Initialized late so the `Window` can hold a reference to this `WindowState`
|
// Initialized late so the `Window` can hold a reference to this `WindowState`
|
||||||
handler: RefCell<Option<Box<dyn WindowHandler>>>,
|
handler: RefCell<Option<Box<dyn WindowHandler>>>,
|
||||||
_drop_target: RefCell<Option<Rc<DropTarget>>>,
|
_drop_target: RefCell<Option<Rc<DropTarget>>>,
|
||||||
|
@ -685,6 +706,7 @@ impl Window<'_> {
|
||||||
keyboard_state: RefCell::new(KeyboardState::new()),
|
keyboard_state: RefCell::new(KeyboardState::new()),
|
||||||
mouse_button_counter: Cell::new(0),
|
mouse_button_counter: Cell::new(0),
|
||||||
mouse_was_outside_window: RefCell::new(true),
|
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
|
// The Window refers to this `WindowState`, so this `handler` needs to be
|
||||||
// initialized later
|
// initialized later
|
||||||
handler: RefCell::new(None),
|
handler: RefCell::new(None),
|
||||||
|
@ -790,8 +812,12 @@ impl Window<'_> {
|
||||||
self.state.deferred_tasks.borrow_mut().push_back(task);
|
self.state.deferred_tasks.borrow_mut().push_back(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_mouse_cursor(&mut self, _mouse_cursor: MouseCursor) {
|
pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) {
|
||||||
todo!()
|
self.state.cursor_icon.set(mouse_cursor);
|
||||||
|
unsafe {
|
||||||
|
let cursor = LoadCursorW(null_mut(), cursor_to_lpcwstr(mouse_cursor));
|
||||||
|
SetCursor(cursor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
|
|
Loading…
Reference in a new issue