Skip to content

Commit

Permalink
Make web custom cursors use smart pointers
Browse files Browse the repository at this point in the history
  • Loading branch information
eero-lehtinen committed Nov 6, 2023
1 parent ae56516 commit ded1bdb
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 69 deletions.
1 change: 1 addition & 0 deletions src/custom_cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ impl CustomCursor {
#[derive(Debug, Clone)]
pub(crate) struct NoCustomCursor;

#[allow(dead_code)]
impl NoCustomCursor {
pub fn from_rgba(
rgba: Vec<u8>,
Expand Down
56 changes: 56 additions & 0 deletions src/platform_impl/web/custom_cursor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use std::sync::Arc;

use wasm_bindgen::{Clamped, JsCast};
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};

use crate::custom_cursor::BadCursor;

#[derive(Debug, Clone)]
pub struct WebCustomCursor {
pub(crate) inner: Arc<str>,
}

impl WebCustomCursor {
pub fn from_rgba(
rgba: Vec<u8>,
width: u32,
height: u32,
hotspot_x: u32,
hotspot_y: u32,
) -> Result<Self, BadCursor> {
#[allow(clippy::disallowed_methods)]
let cursor_icon_canvas = web_sys::window()
.unwrap()
.document()
.unwrap()
.create_element("canvas")
.unwrap()
.dyn_into::<HtmlCanvasElement>()
.unwrap();

#[allow(clippy::disallowed_methods)]
cursor_icon_canvas.set_width(width);
#[allow(clippy::disallowed_methods)]
cursor_icon_canvas.set_height(height);

let context = cursor_icon_canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<CanvasRenderingContext2d>()
.unwrap();

let image_data =
web_sys::ImageData::new_with_u8_clamped_array_and_sh(Clamped(&rgba), width, height);

context
.put_image_data(&image_data.unwrap(), 0.0, 0.0)
.unwrap();

let data_url = cursor_icon_canvas.to_data_url().unwrap();

Ok(Self {
inner: format!("url({data_url}) {hotspot_x} {hotspot_y}, auto").into(),
})
}
}
2 changes: 2 additions & 0 deletions src/platform_impl/web/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// compliant way.

mod r#async;
mod custom_cursor;
mod device;
mod error;
mod event_loop;
Expand All @@ -36,6 +37,7 @@ pub(crate) use self::event_loop::{
pub use self::monitor::{MonitorHandle, VideoMode};
pub use self::window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId};

pub(crate) use self::custom_cursor::WebCustomCursor as PlatformCustomCursor;
pub(crate) use self::keyboard::KeyEventExtra;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;
80 changes: 11 additions & 69 deletions src/platform_impl/web/window.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::cursor_image::CursorImage;
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{ExternalError, NotSupportedError, OsError as RootOE};
use crate::icon::Icon;
Expand All @@ -8,24 +7,24 @@ use crate::window::{
};
use crate::SendSyncWrapper;

use wasm_bindgen::{Clamped, JsCast};
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
use web_sys::HtmlCanvasElement;

use super::custom_cursor::WebCustomCursor;
use super::r#async::Dispatcher;
use super::{backend, monitor::MonitorHandle, EventLoopWindowTarget, Fullscreen};

use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::collections::VecDeque;
use std::rc::Rc;

pub struct Window {
inner: Dispatcher<Inner>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone)]
pub enum SelectedCursor {
BuiltIn(CursorIcon),
Custom(u64),
Custom(WebCustomCursor),
}

impl Default for SelectedCursor {
Expand All @@ -39,7 +38,6 @@ pub struct Inner {
pub window: web_sys::Window,
canvas: Rc<RefCell<backend::Canvas>>,
selected_cursor: RefCell<SelectedCursor>,
custom_cursors: RefCell<HashMap<u64, String>>,
destroy_fn: Option<Box<dyn FnOnce()>>,
}

Expand Down Expand Up @@ -69,7 +67,6 @@ impl Window {
window: window.clone(),
canvas,
selected_cursor: Default::default(),
custom_cursors: Default::default(),
destroy_fn: Some(destroy_fn),
};

Expand Down Expand Up @@ -221,63 +218,14 @@ impl Inner {
}

#[inline]
pub fn register_custom_cursor_icon(&self, key: u64, image: CursorImage) {
let cursor_icon_canvas = self
.canvas
.borrow()
.document()
.create_element("canvas")
.unwrap()
.dyn_into::<HtmlCanvasElement>()
.unwrap();

#[allow(clippy::disallowed_methods)]
cursor_icon_canvas.set_width(image.width);
#[allow(clippy::disallowed_methods)]
cursor_icon_canvas.set_height(image.height);

let context = cursor_icon_canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<CanvasRenderingContext2d>()
.unwrap();

let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(
Clamped(&image.rgba),
image.width,
image.height,
);

context
.put_image_data(&image_data.unwrap(), 0.0, 0.0)
.unwrap();

let data_url = cursor_icon_canvas.to_data_url().unwrap();

let pointer = format!(
"url({data_url}) {} {}, auto",
image.hotspot_x, image.hotspot_y,
);
self.custom_cursors.borrow_mut().insert(key, pointer);
let selected = *self.selected_cursor.borrow();
if let SelectedCursor::Custom(key) = selected {
self.set_custom_cursor_icon(key);
}
}

#[inline]
pub fn set_custom_cursor_icon(&self, key: u64) {
let Some(pointer) = self.custom_cursors.borrow().get(&key).cloned() else {
return;
};
*self.selected_cursor.borrow_mut() = SelectedCursor::Custom(key);
pub fn set_custom_cursor(&self, cursor: WebCustomCursor) {
*self.selected_cursor.borrow_mut() = SelectedCursor::Custom(cursor.clone());
if let Some(s) = backend::get_canvas_style_property(self.canvas.borrow().raw(), "cursor") {
if s == "none" {
return;
}
}
backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", &pointer);
backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", &cursor.inner);
}

#[inline]
Expand Down Expand Up @@ -306,16 +254,10 @@ impl Inner {
if !visible {
backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", "none");
} else {
let custom_cursors = self.custom_cursors.borrow();
let name = match *self.selected_cursor.borrow() {
let cursor = self.selected_cursor.borrow();
let name = match &*cursor {
SelectedCursor::BuiltIn(cursor) => cursor.name(),
SelectedCursor::Custom(key) => {
if let Some(data) = custom_cursors.get(&key) {
data
} else {
CursorIcon::default().name()
}
}
SelectedCursor::Custom(cursor) => &cursor.inner,
};
backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", name);
}
Expand Down

0 comments on commit ded1bdb

Please sign in to comment.