Custom cursor improvements (#3292)

This commit is contained in:
daxpedda 2023-12-23 16:12:29 +01:00 committed by GitHub
parent 37946e0a3a
commit e5310ade08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 152 additions and 101 deletions

View file

@ -46,25 +46,12 @@ const PIXEL_SIZE: usize = 4;
/// let window = Window::new(&event_loop).unwrap();
/// window.set_custom_cursor(&custom_cursor);
/// ```
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct CustomCursor {
pub(crate) inner: Arc<PlatformCustomCursor>,
/// Platforms should make sure this is cheap to clone.
pub(crate) inner: PlatformCustomCursor,
}
impl Hash for CustomCursor {
fn hash<H: Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.inner).hash(state);
}
}
impl PartialEq for CustomCursor {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
}
}
impl Eq for CustomCursor {}
impl CustomCursor {
/// Creates a new cursor from an rgba buffer.
///
@ -80,7 +67,7 @@ impl CustomCursor {
hotspot_y: u16,
) -> Result<CustomCursorBuilder, BadImage> {
Ok(CustomCursorBuilder {
inner: PlatformCustomCursor::from_rgba(
inner: PlatformCustomCursorBuilder::from_rgba(
rgba.into(),
width,
height,
@ -102,7 +89,7 @@ pub struct CustomCursorBuilder {
impl CustomCursorBuilder {
pub fn build<T>(self, window_target: &EventLoopWindowTarget<T>) -> CustomCursor {
CustomCursor {
inner: self.inner.build(&window_target.p),
inner: PlatformCustomCursor::build(self.inner, &window_target.p),
}
}
}
@ -165,9 +152,54 @@ impl fmt::Display for BadImage {
impl Error for BadImage {}
/// Platforms export this directly as `PlatformCustomCursor` if they need to only work with images.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CursorImage {
/// Platforms export this directly as `PlatformCustomCursorBuilder` if they need to only work with images.
#[derive(Debug)]
pub(crate) struct OnlyCursorImageBuilder(pub(crate) CursorImage);
#[allow(dead_code)]
impl OnlyCursorImageBuilder {
pub(crate) fn from_rgba(
rgba: Vec<u8>,
width: u16,
height: u16,
hotspot_x: u16,
hotspot_y: u16,
) -> Result<Self, BadImage> {
CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y).map(Self)
}
}
/// Platforms export this directly as `PlatformCustomCursor` if they don't implement caching.
#[derive(Debug, Clone)]
pub(crate) struct OnlyCursorImage(pub(crate) Arc<CursorImage>);
impl Hash for OnlyCursorImage {
fn hash<H: Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.0).hash(state);
}
}
impl PartialEq for OnlyCursorImage {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for OnlyCursorImage {}
#[allow(dead_code)]
impl OnlyCursorImage {
fn build<T>(
builder: OnlyCursorImageBuilder,
_: &platform_impl::EventLoopWindowTarget<T>,
) -> Self {
Self(Arc::new(builder.0))
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub(crate) struct CursorImage {
pub(crate) rgba: Vec<u8>,
pub(crate) width: u16,
pub(crate) height: u16,
@ -175,9 +207,8 @@ pub struct CursorImage {
pub(crate) hotspot_y: u16,
}
#[allow(dead_code)]
impl CursorImage {
pub fn from_rgba(
pub(crate) fn from_rgba(
rgba: Vec<u8>,
width: u16,
height: u16,
@ -222,19 +253,15 @@ impl CursorImage {
hotspot_y,
})
}
fn build<T>(self, _: &platform_impl::EventLoopWindowTarget<T>) -> Arc<CursorImage> {
Arc::new(self)
}
}
// Platforms that don't support cursors will export this as `PlatformCustomCursor`.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub(crate) struct NoCustomCursor;
#[allow(dead_code)]
impl NoCustomCursor {
pub fn from_rgba(
pub(crate) fn from_rgba(
rgba: Vec<u8>,
width: u16,
height: u16,
@ -245,7 +272,7 @@ impl NoCustomCursor {
Ok(Self)
}
fn build<T>(self, _: &platform_impl::EventLoopWindowTarget<T>) -> Arc<NoCustomCursor> {
Arc::new(self)
fn build<T>(self, _: &platform_impl::EventLoopWindowTarget<T>) -> NoCustomCursor {
self
}
}

View file

@ -906,7 +906,7 @@ impl Window {
pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
pub(crate) fn set_custom_cursor(&self, _: Arc<PlatformCustomCursor>) {}
pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {}
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(

View file

@ -1,7 +1,6 @@
#![allow(clippy::unnecessary_cast)]
use std::collections::VecDeque;
use std::sync::Arc;
use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
use objc2::rc::Id;
@ -178,7 +177,7 @@ impl Inner {
debug!("`Window::set_cursor_icon` ignored on iOS")
}
pub(crate) fn set_custom_cursor(&self, _: Arc<PlatformCustomCursor>) {
pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {
debug!("`Window::set_custom_cursor` ignored on iOS")
}

View file

@ -40,8 +40,8 @@ pub use x11::XNotSupported;
#[cfg(x11_platform)]
use x11::{util::WindowType as XWindowType, X11Error, XConnection, XError};
pub(crate) use crate::cursor::CursorImage as PlatformCustomCursorBuilder;
pub(crate) use crate::cursor::CursorImage as PlatformCustomCursor;
pub(crate) use crate::cursor::OnlyCursorImage as PlatformCustomCursor;
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;
@ -427,7 +427,7 @@ impl Window {
}
#[inline]
pub(crate) fn set_custom_cursor(&self, cursor: Arc<PlatformCustomCursor>) {
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
x11_or_wayland!(match self; Window(w) => w.set_custom_cursor(cursor))
}

View file

@ -27,7 +27,7 @@ pub struct CustomCursor {
}
impl CustomCursor {
pub fn new(pool: &mut SlotPool, image: &CursorImage) -> Self {
pub(crate) fn new(pool: &mut SlotPool, image: &CursorImage) -> Self {
let (buffer, canvas) = pool
.create_buffer(
image.width as i32,

View file

@ -507,8 +507,11 @@ impl Window {
}
#[inline]
pub(crate) fn set_custom_cursor(&self, cursor: Arc<PlatformCustomCursor>) {
self.window_state.lock().unwrap().set_custom_cursor(&cursor);
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
self.window_state
.lock()
.unwrap()
.set_custom_cursor(&cursor.0);
}
#[inline]

View file

@ -726,7 +726,7 @@ impl WindowState {
}
/// Set the custom cursor icon.
pub fn set_custom_cursor(&mut self, cursor: &CursorImage) {
pub(crate) fn set_custom_cursor(&mut self, cursor: &CursorImage) {
let cursor = {
let mut pool = self.custom_cursor_pool.lock().unwrap();
CustomCursor::new(&mut pool, cursor)

View file

@ -19,7 +19,7 @@ impl XConnection {
.expect("Failed to set cursor");
}
pub fn set_custom_cursor(&self, window: xproto::Window, cursor: &CustomCursor) {
pub(crate) fn set_custom_cursor(&self, window: xproto::Window, cursor: &CustomCursor) {
self.update_cursor(window, cursor.inner.cursor)
.expect("Failed to set cursor");
}

View file

@ -1550,8 +1550,8 @@ impl UnownedWindow {
}
#[inline]
pub(crate) fn set_custom_cursor(&self, cursor: Arc<PlatformCustomCursor>) {
let new_cursor = unsafe { CustomCursor::new(&self.xconn, &cursor) };
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
let new_cursor = unsafe { CustomCursor::new(&self.xconn, &cursor.0) };
#[allow(clippy::mutex_atomic)]
if *self.cursor_visible.lock().unwrap() {

View file

@ -28,8 +28,8 @@ 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::cursor::CursorImage as PlatformCustomCursorBuilder;
pub(crate) use crate::cursor::OnlyCursorImage as PlatformCustomCursor;
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;

View file

@ -5,7 +5,6 @@ use std::f64;
use std::ops;
use std::os::raw::c_void;
use std::ptr::NonNull;
use std::sync::Arc;
use std::sync::{Mutex, MutexGuard};
use crate::{
@ -848,9 +847,9 @@ impl WinitWindow {
}
#[inline]
pub(crate) fn set_custom_cursor(&self, cursor: Arc<PlatformCustomCursor>) {
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
let view = self.view();
view.set_cursor_icon(NSCursor::from_image(&cursor));
view.set_cursor_icon(NSCursor::from_image(&cursor.0));
self.invalidateCursorRectsForView(&view);
}

View file

@ -352,7 +352,7 @@ impl Window {
#[inline]
pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
pub(crate) fn set_custom_cursor(&self, _: Arc<PlatformCustomCursor>) {}
pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {}
#[inline]
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {

View file

@ -1,6 +1,8 @@
use std::{
cell::RefCell,
future, mem,
future,
hash::{Hash, Hasher},
mem,
ops::DerefMut,
rc::{self, Rc},
sync::{self, Arc},
@ -25,7 +27,7 @@ use self::thread_safe::ThreadSafe;
use super::{backend::Style, r#async::AsyncSender, EventLoopWindowTarget};
#[derive(Debug)]
pub enum CustomCursorBuilder {
pub(crate) enum CustomCursorBuilder {
Image(CursorImage),
Url {
url: String,
@ -35,35 +37,6 @@ pub enum CustomCursorBuilder {
}
impl CustomCursorBuilder {
pub fn build<T>(self, window_target: &EventLoopWindowTarget<T>) -> Arc<CustomCursor> {
Lazy::force(&DROP_HANDLER);
match self {
Self::Image(image) => ImageState::from_rgba(
window_target.runner.window(),
window_target.runner.document().clone(),
&image,
),
Self::Url {
url,
hotspot_x,
hotspot_y,
} => ImageState::from_url(url, hotspot_x, hotspot_y),
}
}
}
#[derive(Debug)]
pub struct CustomCursor(Option<ThreadSafe<RefCell<ImageState>>>);
static DROP_HANDLER: Lazy<AsyncSender<ThreadSafe<RefCell<ImageState>>>> = Lazy::new(|| {
let (sender, receiver) = r#async::channel();
wasm_bindgen_futures::spawn_local(async move { while receiver.next().await.is_ok() {} });
sender
});
impl CustomCursor {
pub fn from_rgba(
rgba: Vec<u8>,
width: u16,
@ -75,9 +48,60 @@ impl CustomCursor {
rgba, width, height, hotspot_x, hotspot_y,
)?))
}
}
#[derive(Clone, Debug)]
pub struct CustomCursor(Arc<Inner>);
impl Hash for CustomCursor {
fn hash<H: Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.0).hash(state);
}
}
impl PartialEq for CustomCursor {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for CustomCursor {}
impl CustomCursor {
pub(crate) fn build<T>(
builder: CustomCursorBuilder,
window_target: &EventLoopWindowTarget<T>,
) -> Self {
Lazy::force(&DROP_HANDLER);
Self(match builder {
CustomCursorBuilder::Image(image) => ImageState::from_rgba(
window_target.runner.window(),
window_target.runner.document().clone(),
&image,
),
CustomCursorBuilder::Url {
url,
hotspot_x,
hotspot_y,
} => ImageState::from_url(url, hotspot_x, hotspot_y),
})
}
}
#[derive(Debug)]
struct Inner(Option<ThreadSafe<RefCell<ImageState>>>);
static DROP_HANDLER: Lazy<AsyncSender<ThreadSafe<RefCell<ImageState>>>> = Lazy::new(|| {
let (sender, receiver) = r#async::channel();
wasm_bindgen_futures::spawn_local(async move { while receiver.next().await.is_ok() {} });
sender
});
impl Inner {
fn new() -> Arc<Self> {
Arc::new(Self(Some(ThreadSafe::new(RefCell::new(
Arc::new(Inner(Some(ThreadSafe::new(RefCell::new(
ImageState::Loading(None),
)))))
}
@ -90,7 +114,7 @@ impl CustomCursor {
}
}
impl Drop for CustomCursor {
impl Drop for Inner {
fn drop(&mut self) {
let value = self
.0
@ -130,13 +154,13 @@ impl CursorState {
this.set_style();
}
pub fn set_custom_cursor(&self, cursor: Arc<CustomCursor>) {
pub(crate) fn set_custom_cursor(&self, cursor: CustomCursor) {
let mut this = self.0.borrow_mut();
match cursor.get().borrow_mut().deref_mut() {
match cursor.0.get().borrow_mut().deref_mut() {
ImageState::Loading(state) => {
this.cursor = SelectedCursor::ImageLoading {
state: cursor.clone(),
state: cursor.0.clone(),
previous: mem::take(&mut this.cursor).into(),
};
*state = Some(Rc::downgrade(&self.0));
@ -187,7 +211,7 @@ impl State {
enum SelectedCursor {
Named(CursorIcon),
ImageLoading {
state: Arc<CustomCursor>,
state: Arc<Inner>,
previous: Previous,
},
ImageReady(Rc<Image>),
@ -241,7 +265,7 @@ enum ImageState {
}
impl ImageState {
fn from_rgba(window: &Window, document: Document, image: &CursorImage) -> Arc<CustomCursor> {
fn from_rgba(window: &Window, document: Document, image: &CursorImage) -> Arc<Inner> {
// 1. Create an `ImageData` from the RGBA data.
// 2. Create an `ImageBitmap` from the `ImageData`.
// 3. Draw `ImageBitmap` on an `HTMLCanvasElement`.
@ -293,7 +317,7 @@ impl ImageState {
.expect("unexpected exception in `createImageBitmap()`"),
);
let this = CustomCursor::new();
let this = Inner::new();
wasm_bindgen_futures::spawn_local({
let weak = Arc::downgrade(&this);
@ -406,8 +430,8 @@ impl ImageState {
this
}
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> Arc<CustomCursor> {
let this = CustomCursor::new();
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> Arc<Inner> {
let this = Inner::new();
wasm_bindgen_futures::spawn_local(Self::decode(
Arc::downgrade(&this),
url,
@ -420,7 +444,7 @@ impl ImageState {
}
async fn decode(
weak: sync::Weak<CustomCursor>,
weak: sync::Weak<Inner>,
url: String,
object: bool,
hotspot_x: u16,

View file

@ -16,7 +16,6 @@ use web_sys::HtmlCanvasElement;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
use std::sync::Arc;
pub struct Window {
inner: Dispatcher<Inner>,
@ -218,7 +217,7 @@ impl Inner {
}
#[inline]
pub(crate) fn set_custom_cursor(&self, cursor: Arc<PlatformCustomCursor>) {
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
self.cursor.set_custom_cursor(cursor)
}

View file

@ -194,7 +194,7 @@ impl WinCursor {
}
}
pub fn new(image: &CursorImage) -> Result<Self, io::Error> {
pub(crate) fn new(image: &CursorImage) -> Result<Self, io::Error> {
let mut bgra = image.rgba.clone();
bgra.chunks_exact_mut(4).for_each(|chunk| chunk.swap(0, 2));

View file

@ -16,8 +16,8 @@ pub(crate) use self::{
};
pub use self::icon::WinIcon as PlatformIcon;
pub(crate) use crate::cursor::CursorImage as PlatformCustomCursor;
pub(crate) use crate::cursor::CursorImage as PlatformCustomCursorBuilder;
pub(crate) use crate::cursor::OnlyCursorImage as PlatformCustomCursor;
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
use crate::platform_impl::Fullscreen;
use crate::event::DeviceId as RootDeviceId;

View file

@ -405,8 +405,8 @@ impl Window {
}
#[inline]
pub(crate) fn set_custom_cursor(&self, cursor: Arc<PlatformCustomCursor>) {
let new_cursor = match WinCursor::new(&cursor) {
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
let new_cursor = match WinCursor::new(&cursor.0) {
Ok(cursor) => cursor,
Err(err) => {
warn!("Failed to create custom cursor: {err}");

View file

@ -1,5 +1,5 @@
//! The [`Window`] struct and associated types.
use std::{fmt, sync::Arc};
use std::fmt;
use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
@ -1355,7 +1355,7 @@ impl Window {
/// - **iOS / Android / Orbital:** Unsupported.
#[inline]
pub fn set_custom_cursor(&self, cursor: &CustomCursor) {
let cursor = Arc::clone(&cursor.inner);
let cursor = cursor.inner.clone();
self.window
.maybe_queue_on_main(move |w| w.set_custom_cursor(cursor))
}