diff --git a/src/macos/cursor.rs b/src/macos/cursor.rs new file mode 100644 index 00000000..17846cce --- /dev/null +++ b/src/macos/cursor.rs @@ -0,0 +1,78 @@ +use cocoa::base::id; +use objc::{runtime::Sel, msg_send, sel, sel_impl, class}; + +use crate::MouseCursor; + +#[derive(Debug)] +pub enum Cursor { + Native(&'static str), + Undocumented(&'static str), +} + +impl From for Cursor { + fn from(cursor: MouseCursor) -> Self { + match cursor { + MouseCursor::Default => Cursor::Native("arrowCursor"), + MouseCursor::Pointer => Cursor::Native("pointingHandCursor"), + MouseCursor::Hand => Cursor::Native("openHandCursor"), + MouseCursor::HandGrabbing => Cursor::Native("closedHandCursor"), + MouseCursor::Text => Cursor::Native("IBeamCursor"), + MouseCursor::VerticalText => Cursor::Native("IBeamCursorForVerticalLayout"), + MouseCursor::Copy => Cursor::Native("dragCopyCursor"), + MouseCursor::Alias => Cursor::Native("dragLinkCursor"), + MouseCursor::NotAllowed | MouseCursor::PtrNotAllowed => { + Cursor::Native("operationNotAllowedCursor") + } + // MouseCursor:: => Cursor::Native("contextualMenuCursor"), + MouseCursor::Crosshair => Cursor::Native("crosshairCursor"), + MouseCursor::EResize => Cursor::Native("resizeRightCursor"), + MouseCursor::NResize => Cursor::Native("resizeUpCursor"), + MouseCursor::WResize => Cursor::Native("resizeLeftCursor"), + MouseCursor::SResize => Cursor::Native("resizeDownCursor"), + MouseCursor::EwResize | MouseCursor::ColResize => Cursor::Native("resizeLeftRightCursor"), + MouseCursor::NsResize | MouseCursor::RowResize => Cursor::Native("resizeUpDownCursor"), + + MouseCursor::Help => Cursor::Undocumented("_helpCursor"), + MouseCursor::ZoomIn => Cursor::Undocumented("_zoomInCursor"), + MouseCursor::ZoomOut => Cursor::Undocumented("_zoomOutCursor"), + MouseCursor::NeResize => Cursor::Undocumented("_windowResizeNorthEastCursor"), + MouseCursor::NwResize => Cursor::Undocumented("_windowResizeNorthWestCursor"), + MouseCursor::SeResize => Cursor::Undocumented("_windowResizeSouthEastCursor"), + MouseCursor::SwResize => Cursor::Undocumented("_windowResizeSouthWestCursor"), + MouseCursor::NeswResize => Cursor::Undocumented("_windowResizeNorthEastSouthWestCursor"), + MouseCursor::NwseResize => Cursor::Undocumented("_windowResizeNorthWestSouthEastCursor"), + + MouseCursor::Working | MouseCursor::PtrWorking => { + Cursor::Undocumented("busyButClickableCursor") + } + + _ => Cursor::Native("arrowCursor"), + + // MouseCursor::Hidden => todo!(), + // MouseCursor::Move => todo!(), + // MouseCursor::AllScroll => todo!(), + // MouseCursor::Cell => todo!(), + } + } +} + +impl Cursor { + pub unsafe fn load(&self) -> id { + match self { + Cursor::Native(cursor_name) => { + let sel = Sel::register(cursor_name); + msg_send![class!(NSCursor), performSelector: sel] + } + Cursor::Undocumented(cursor_name) => { + let class = class!(NSCursor); + let sel = Sel::register(cursor_name); + let sel = if msg_send![class, respondsToSelector: sel] { + sel + } else { + sel!(arrowCursor) + }; + msg_send![class, performSelector: sel] + } + } + } +} \ No newline at end of file diff --git a/src/macos/mod.rs b/src/macos/mod.rs index e1fa7b87..ef4ee2bc 100644 --- a/src/macos/mod.rs +++ b/src/macos/mod.rs @@ -1,5 +1,6 @@ mod keyboard; mod view; mod window; +mod cursor; pub use window::*; diff --git a/src/macos/window.rs b/src/macos/window.rs index 54046ddd..e6f9ea90 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -20,10 +20,11 @@ use objc::{msg_send, runtime::Object, sel, sel_impl}; use raw_window_handle::{AppKitHandle, HasRawWindowHandle, RawWindowHandle}; use crate::{ - Event, EventStatus, Size, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, + Event, EventStatus, Size, MouseCursor, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; +use super::cursor::Cursor; use super::keyboard::KeyboardState; use super::view::{create_view, BASEVIEW_STATE_IVAR}; @@ -338,6 +339,18 @@ impl Window { } } + pub fn set_mouse_cursor(&self, cursor: MouseCursor) { + let native_cursor = Cursor::from(cursor); + unsafe { + let bounds: NSRect = msg_send![self.ns_view as id, bounds]; + let cursor = native_cursor.load(); + let _: () = msg_send![self.ns_view as id, + addCursorRect:bounds + cursor:cursor + ]; + } + } + #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option<&GlContext> { self.gl_context.as_ref() diff --git a/src/mouse_cursor.rs b/src/mouse_cursor.rs index 16fc199d..4abfbf19 100644 --- a/src/mouse_cursor.rs +++ b/src/mouse_cursor.rs @@ -3,6 +3,7 @@ pub enum MouseCursor { Default, Hand, HandGrabbing, + Pointer, Help, Hidden, diff --git a/src/win/cursor.rs b/src/win/cursor.rs new file mode 100644 index 00000000..aaee35eb --- /dev/null +++ b/src/win/cursor.rs @@ -0,0 +1,38 @@ +use winapi::{ + shared::ntdef::PCWSTR, + um::winuser::{ + IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, IDC_NO, IDC_SIZEALL, IDC_WAIT, + }, +}; + +use crate::MouseCursor; + +impl MouseCursor { + pub(crate) fn to_windows_cursor(self) -> PCWSTR { + match self { + MouseCursor::Default => IDC_ARROW, + MouseCursor::Hand | MouseCursor::Pointer => IDC_HAND, + MouseCursor::HandGrabbing + | MouseCursor::Move + | MouseCursor::ZoomIn + | MouseCursor::ZoomOut + | MouseCursor::AllScroll => IDC_SIZEALL, + MouseCursor::Help => IDC_HELP, + MouseCursor::Text | MouseCursor::VerticalText => IDC_IBEAM, + MouseCursor::Working | MouseCursor::PtrWorking => IDC_WAIT, + MouseCursor::NotAllowed | MouseCursor::PtrNotAllowed => IDC_NO, + MouseCursor::Crosshair => IDC_CROSS, + MouseCursor::EResize + | MouseCursor::WResize + | MouseCursor::EwResize + | MouseCursor::ColResize => IDC_SIZEALL, + MouseCursor::NResize + | MouseCursor::SResize + | MouseCursor::NsResize + | MouseCursor::RowResize => IDC_SIZEALL, + MouseCursor::NeResize | MouseCursor::SwResize | MouseCursor::NeswResize => IDC_SIZEALL, + MouseCursor::NwResize | MouseCursor::SeResize | MouseCursor::NwseResize => IDC_SIZEALL, + _ => IDC_ARROW, + } + } +} diff --git a/src/win/mod.rs b/src/win/mod.rs index f66c2bdb..a7b9d3a1 100644 --- a/src/win/mod.rs +++ b/src/win/mod.rs @@ -1,5 +1,6 @@ mod drop_target; mod keyboard; mod window; +mod cursor; pub use window::*; diff --git a/src/win/window.rs b/src/win/window.rs index 2c68b99c..51f9bd1c 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -6,7 +6,7 @@ use winapi::um::ole2::{RegisterDragDrop, OleInitialize, RevokeDragDrop}; use winapi::um::oleidl::LPDROPTARGET; use winapi::um::winuser::{ AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, - GetDpiForWindow, GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, RegisterClassW, + GetDpiForWindow, GetMessageW, GetWindowLongPtrW, LoadCursorW, SetCursor, PostMessageW, RegisterClassW, ReleaseCapture, SetCapture, SetProcessDpiAwarenessContext, SetTimer, SetWindowLongPtrW, SetWindowPos, TranslateMessage, UnregisterClassW, CS_OWNDC, GET_XBUTTON_WPARAM, GWLP_USERDATA, IDC_ARROW, MSG, SWP_NOMOVE, SWP_NOZORDER, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE, @@ -31,7 +31,7 @@ use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle}; const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1; use crate::{ - Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent, + Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, MouseCursor, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; @@ -433,7 +433,7 @@ unsafe fn register_wnd_class() -> ATOM { cbClsExtra: 0, cbWndExtra: 0, hIcon: null_mut(), - hCursor: LoadCursorW(null_mut(), IDC_ARROW), + hCursor: null_mut(), // If the class cursor is not NULL, the system restores the class cursor each time the mouse is moved. hbrBackground: null_mut(), lpszMenuName: null_mut(), }; @@ -760,6 +760,13 @@ impl Window<'_> { } } + pub fn set_mouse_cursor(&mut self, cursor: MouseCursor) { + unsafe { + let cursor = LoadCursorW(null_mut(), cursor.to_windows_cursor()); + SetCursor(cursor); + } + } + pub fn close(&mut self) { unsafe { PostMessageW(self.state.hwnd, BV_WINDOW_MUST_CLOSE, 0, 0); diff --git a/src/window.rs b/src/window.rs index c0ef1ac8..1b6b704d 100644 --- a/src/window.rs +++ b/src/window.rs @@ -5,6 +5,7 @@ use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use crate::event::{Event, EventStatus}; use crate::window_open_options::WindowOpenOptions; use crate::Size; +use crate::MouseCursor; #[cfg(target_os = "macos")] use crate::macos as platform; @@ -114,6 +115,11 @@ impl<'a> Window<'a> { pub fn resize(&mut self, size: Size) { self.window.resize(size); } + + /// Set the cursor to the given cursor type + pub fn set_mouse_cursor(&mut self, cursor: MouseCursor) { + self.window.set_mouse_cursor(cursor); + } /// If provided, then an OpenGL context will be created for this window. You'll be able to /// access this context through [crate::Window::gl_context]. diff --git a/src/x11/cursor.rs b/src/x11/cursor.rs index 8e8fd88c..73b95d15 100644 --- a/src/x11/cursor.rs +++ b/src/x11/cursor.rs @@ -62,6 +62,7 @@ pub(super) fn get_xcursor(display: *mut x11::xlib::Display, cursor: MouseCursor) MouseCursor::Hand => loadn(&[b"hand2\0", b"hand1\0"]), MouseCursor::HandGrabbing => loadn(&[b"closedhand\0", b"grabbing\0"]), + MouseCursor::Pointer => loadn(&[b"hand2\0"]), MouseCursor::Help => load(b"question_arrow\0"), MouseCursor::Hidden => create_empty_cursor(display),