#[doc(inline)]
pub use cursor_icon::CursorIcon;
use crate::ImageBitmap;
pub const MAX_POINTER_ICON_SIZE: u32 = 128;
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum PointerIconError {
#[error(
"pointer icon is {width}x{height}, larger than the {MAX_POINTER_ICON_SIZE}px platform limit"
)]
TooLarge {
width: u32,
height: u32,
},
#[error("pointer icon hotspot ({x},{y}) lies outside its {width}x{height} bitmap")]
HotspotOutsideBitmap {
x: u32,
y: u32,
width: u32,
height: u32,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CustomPointerIcon {
image: ImageBitmap,
hotspot_x: u32,
hotspot_y: u32,
}
impl CustomPointerIcon {
pub fn new(
image: ImageBitmap,
hotspot_x: u32,
hotspot_y: u32,
) -> Result<Self, PointerIconError> {
let (width, height) = (image.width(), image.height());
if width > MAX_POINTER_ICON_SIZE || height > MAX_POINTER_ICON_SIZE {
return Err(PointerIconError::TooLarge { width, height });
}
if hotspot_x >= width || hotspot_y >= height {
return Err(PointerIconError::HotspotOutsideBitmap {
x: hotspot_x,
y: hotspot_y,
width,
height,
});
}
Ok(Self {
image,
hotspot_x,
hotspot_y,
})
}
pub fn image(&self) -> &ImageBitmap {
&self.image
}
pub fn hotspot_x(&self) -> u32 {
self.hotspot_x
}
pub fn hotspot_y(&self) -> u32 {
self.hotspot_y
}
pub fn id(&self) -> u64 {
self.image.id().rotate_left(17) ^ ((self.hotspot_x as u64) << 32 | self.hotspot_y as u64)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PointerIcon {
System(CursorIcon),
Custom(CustomPointerIcon),
}
impl PointerIcon {
pub const DEFAULT: Self = Self::System(CursorIcon::Default);
pub const POINTER: Self = Self::System(CursorIcon::Pointer);
pub const TEXT: Self = Self::System(CursorIcon::Text);
pub fn custom(
image: ImageBitmap,
hotspot_x: u32,
hotspot_y: u32,
) -> Result<Self, PointerIconError> {
CustomPointerIcon::new(image, hotspot_x, hotspot_y).map(Self::Custom)
}
pub fn css_keyword(&self) -> Option<&'static str> {
match self {
Self::System(icon) => Some(icon.name()),
Self::Custom(_) => None,
}
}
}
impl Default for PointerIcon {
fn default() -> Self {
Self::DEFAULT
}
}
impl From<CursorIcon> for PointerIcon {
fn from(icon: CursorIcon) -> Self {
Self::System(icon)
}
}
impl From<CustomPointerIcon> for PointerIcon {
fn from(icon: CustomPointerIcon) -> Self {
Self::Custom(icon)
}
}
#[cfg(test)]
#[path = "tests/pointer_icon_tests.rs"]
mod tests;