feat(all): Custom cursor images for all desktop platforms
There seems to be many PRs relating to this issue, but they don't include all platforms and for some reason lost steam. This PR again tries to make this feature happen, and does it for all desktop platforms (x11, wayland, macos, windows, web). I think the best user of this feature and the reason I'm doing this is Bevy and game engines in general. There non laggy hardware cursors with custom images are very important. Game devs also like their PNGs so supporting platform native cursor files is not that important, but I guess could be added too. Co-authored-by: daxpedda <daxpedda@gmail.com> Co-authored-by: Mads Marquart <mads@marquart.dk> Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
parent
7f6b16a6af
commit
af93167237
42 changed files with 1243 additions and 57 deletions
56
src/platform_impl/macos/appkit/bitmap_image_rep.rs
Normal file
56
src/platform_impl/macos/appkit/bitmap_image_rep.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use std::ffi::c_uchar;
|
||||
|
||||
use icrate::Foundation::{NSInteger, NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::Bool;
|
||||
use objc2::{extern_class, extern_methods, msg_send, msg_send_id, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NSImageRep;
|
||||
|
||||
unsafe impl ClassType for NSImageRep {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern "C" {
|
||||
static NSDeviceRGBColorSpace: &'static NSString;
|
||||
}
|
||||
|
||||
extern_class!(
|
||||
// <https://developer.apple.com/documentation/appkit/nsbitmapimagerep?language=objc>
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSBitmapImageRep;
|
||||
|
||||
unsafe impl ClassType for NSBitmapImageRep {
|
||||
type Super = NSImageRep;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSBitmapImageRep {
|
||||
pub fn init_rgba(width: NSInteger, height: NSInteger) -> Id<Self> {
|
||||
unsafe {
|
||||
msg_send_id![Self::alloc(),
|
||||
initWithBitmapDataPlanes: std::ptr::null_mut::<*mut c_uchar>(),
|
||||
pixelsWide: width,
|
||||
pixelsHigh: height,
|
||||
bitsPerSample: 8 as NSInteger,
|
||||
samplesPerPixel: 4 as NSInteger,
|
||||
hasAlpha: Bool::new(true),
|
||||
isPlanar: Bool::new(false),
|
||||
colorSpaceName: NSDeviceRGBColorSpace,
|
||||
bytesPerRow: width * 4,
|
||||
bitsPerPixel: 32 as NSInteger,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bitmap_data(&self) -> *mut u8 {
|
||||
unsafe { msg_send![self, bitmapData] }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -2,13 +2,14 @@ use once_cell::sync::Lazy;
|
|||
|
||||
use icrate::ns_string;
|
||||
use icrate::Foundation::{
|
||||
NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSString,
|
||||
NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSSize, NSString,
|
||||
};
|
||||
use objc2::rc::{DefaultId, Id};
|
||||
use objc2::runtime::Sel;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, sel, ClassType};
|
||||
|
||||
use super::NSImage;
|
||||
use super::{NSBitmapImageRep, NSImage};
|
||||
use crate::cursor::CursorImage;
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
extern_class!(
|
||||
|
|
@ -232,6 +233,23 @@ impl NSCursor {
|
|||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_image(cursor: &CursorImage) -> Id<Self> {
|
||||
let width = cursor.width;
|
||||
let height = cursor.height;
|
||||
|
||||
let bitmap = NSBitmapImageRep::init_rgba(width as isize, height as isize);
|
||||
let bitmap_data =
|
||||
unsafe { std::slice::from_raw_parts_mut(bitmap.bitmap_data(), cursor.rgba.len()) };
|
||||
bitmap_data.copy_from_slice(&cursor.rgba);
|
||||
|
||||
let image = NSImage::init_with_size(NSSize::new(width.into(), height.into()));
|
||||
image.add_representation(&bitmap);
|
||||
|
||||
let hotspot = NSPoint::new(cursor.hotspot_x as f64, cursor.hotspot_y as f64);
|
||||
|
||||
NSCursor::new(&image, hotspot)
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultId for NSCursor {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use icrate::Foundation::{NSData, NSObject, NSString};
|
||||
use icrate::Foundation::{NSData, NSObject, NSSize, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||
use objc2::{extern_class, extern_methods, msg_send, msg_send_id, mutability, ClassType};
|
||||
|
||||
use super::NSBitmapImageRep;
|
||||
|
||||
extern_class!(
|
||||
// TODO: Can this be mutable?
|
||||
|
|
@ -32,5 +34,13 @@ extern_methods!(
|
|||
pub fn new_with_data(data: &NSData) -> Id<Self> {
|
||||
unsafe { msg_send_id![Self::alloc(), initWithData: data] }
|
||||
}
|
||||
|
||||
pub fn init_with_size(size: NSSize) -> Id<Self> {
|
||||
unsafe { msg_send_id![Self::alloc(), initWithSize: size] }
|
||||
}
|
||||
|
||||
pub fn add_representation(&self, representation: &NSBitmapImageRep) {
|
||||
unsafe { msg_send![self, addRepresentation: representation] }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
mod appearance;
|
||||
mod application;
|
||||
mod bitmap_image_rep;
|
||||
mod button;
|
||||
mod color;
|
||||
mod control;
|
||||
|
|
@ -36,6 +37,7 @@ pub(crate) use self::application::{
|
|||
NSApp, NSApplication, NSApplicationActivationPolicy, NSApplicationPresentationOptions,
|
||||
NSRequestUserAttentionType,
|
||||
};
|
||||
pub(crate) use self::bitmap_image_rep::NSBitmapImageRep;
|
||||
pub(crate) use self::button::NSButton;
|
||||
pub(crate) use self::color::NSColor;
|
||||
pub(crate) use self::control::NSControl;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ pub(crate) use self::{
|
|||
use crate::event::DeviceId as RootDeviceId;
|
||||
|
||||
pub(crate) use self::window::Window;
|
||||
pub(crate) use crate::cursor::CursorImage as PlatformCustomCursor;
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use std::os::raw::c_void;
|
|||
use std::ptr::NonNull;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
use crate::cursor::CustomCursor;
|
||||
use crate::{
|
||||
dpi::{
|
||||
LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical,
|
||||
|
|
@ -834,6 +835,13 @@ impl WinitWindow {
|
|||
self.invalidateCursorRectsForView(&view);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_custom_cursor(&self, cursor: CustomCursor) {
|
||||
let view = self.view();
|
||||
view.set_cursor_icon(NSCursor::from_image(&cursor.inner));
|
||||
self.invalidateCursorRectsForView(&view);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
let associate_mouse_cursor = match mode {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue