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

@ -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}");