From b7fb47b33b014eacfb5ba6660956e91cacc8a085 Mon Sep 17 00:00:00 2001 From: Spruce Cloud <1888686+SpruceCloud@users.noreply.github.com> Date: Mon, 22 Jun 2026 22:03:49 +0200 Subject: [PATCH] Implement macOS cursor setting and hiding support --- src/platform/macos/cursor.rs | 19 ++++++++++--------- src/platform/macos/view.rs | 7 ++++++- src/platform/macos/window.rs | 20 ++++++++++++++++++-- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/platform/macos/cursor.rs b/src/platform/macos/cursor.rs index c13f556a..69991d83 100644 --- a/src/platform/macos/cursor.rs +++ b/src/platform/macos/cursor.rs @@ -9,6 +9,7 @@ use crate::MouseCursor; pub enum Cursor { Native(fn() -> Retained), Undocumented(Sel), + Hidden, } impl From for Cursor { @@ -60,19 +61,18 @@ impl From for Cursor { Cursor::Undocumented(sel!(busyButClickableCursor)) } - _ => Cursor::Native(NSCursor::arrowCursor), - // MouseCursor::Hidden => todo!(), - // MouseCursor::Move => todo!(), - // MouseCursor::AllScroll => todo!(), - // MouseCursor::Cell => todo!(), + MouseCursor::Move => Cursor::Native(NSCursor::arrowCursor), + MouseCursor::AllScroll => Cursor::Native(NSCursor::arrowCursor), + MouseCursor::Cell => Cursor::Native(NSCursor::crosshairCursor), + MouseCursor::Hidden => Cursor::Hidden, } } } impl Cursor { - pub fn load(&self) -> Retained { + pub fn load(&self) -> Option> { match self { - Cursor::Native(loader) => loader(), + Cursor::Native(loader) => Some(loader()), Cursor::Undocumented(sel) => { let class = NSCursor::class(); @@ -80,14 +80,15 @@ impl Cursor { let responds_to: bool = unsafe { msg_send![class, respondsToSelector: *sel] }; if !responds_to { - return NSCursor::arrowCursor(); + return Some(NSCursor::arrowCursor()); } let raw: *mut NSCursor = unsafe { class.send_message(*sel, ()) }; let cursor = unsafe { Retained::retain(raw) }; - cursor.unwrap_or_else(NSCursor::arrowCursor) + Some(cursor.unwrap_or_else(NSCursor::arrowCursor)) } + Cursor::Hidden => None, } } } diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index 53ec7fbf..7d1be511 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -11,7 +11,7 @@ use crate::{ use objc2::__framework_prelude::Retained; use objc2::rc::Weak; use objc2::runtime::{NSObjectProtocol, ProtocolObject}; -use objc2::{msg_send, AllocAnyThread}; +use objc2::{msg_send, AllocAnyThread, ClassType}; use objc2_app_kit::{ NSApplication, NSDragOperation, NSDraggingInfo, NSEvent, NSFilenamesPboardType, NSTrackingArea, NSTrackingAreaOptions, NSView, NSWindow, @@ -230,6 +230,11 @@ impl BaseviewView { impl Drop for BaseviewView { fn drop(&mut self) { self.state.closed.set(true); + if self.state.cursor_hidden.get() { + unsafe { + let _: () = objc2::msg_send![objc2_app_kit::NSCursor::class(), unhide]; + } + } } } diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 8e07bf55..35d00ae2 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use objc2::rc::{autoreleasepool, Weak}; use objc2::runtime::NSObjectProtocol; -use objc2::MainThreadMarker; +use objc2::{ClassType, MainThreadMarker}; use objc2_app_kit::{ NSApplication, NSApplicationActivationPolicy, NSPasteboard, NSPasteboardTypeString, }; @@ -156,7 +156,21 @@ impl<'a> Window<'a> { pub fn set_mouse_cursor(&self, cursor: MouseCursor) { let native_cursor = Cursor::from(cursor); - self.view.addCursorRect_cursor(self.view.bounds(), &native_cursor.load()); + unsafe { + if let Some(loaded_cursor) = native_cursor.load() { + if self.inner.state.cursor_hidden.get() { + let _: () = objc2::msg_send![objc2_app_kit::NSCursor::class(), unhide]; + self.inner.state.cursor_hidden.set(false); + } + let _: () = objc2::msg_send![&loaded_cursor, set]; + self.view.addCursorRect_cursor(self.view.bounds(), &loaded_cursor); + } else { + if !self.inner.state.cursor_hidden.get() { + let _: () = objc2::msg_send![objc2_app_kit::NSCursor::class(), hide]; + self.inner.state.cursor_hidden.set(true); + } + } + } } #[cfg(feature = "opengl")] @@ -169,6 +183,7 @@ pub(crate) struct WindowSharedState { /// The last known window info for this window. pub window_info: Cell, pub closed: Cell, + pub cursor_hidden: Cell, } impl WindowSharedState { @@ -176,6 +191,7 @@ impl WindowSharedState { Self { window_info: WindowInfo::from_logical_size(options.size, 1.0).into(), closed: false.into(), + cursor_hidden: false.into(), } } }