2019-09-24 19:33:32 -04:00
|
|
|
//! The web target does not automatically insert the canvas element object into the web page, to
|
2022-06-11 18:57:19 +02:00
|
|
|
//! allow end users to determine how the page should be laid out. Use the [`WindowExtWebSys`] trait
|
2024-01-31 17:29:59 +04:00
|
|
|
//! to retrieve the canvas from the Window. Alternatively, use the [`WindowAttributesExtWebSys`] trait
|
2021-05-24 19:06:21 +02:00
|
|
|
//! to provide your own canvas.
|
2023-07-10 12:50:28 +02:00
|
|
|
//!
|
|
|
|
|
//! It is recommended **not** to apply certain CSS properties to the canvas:
|
|
|
|
|
//! - [`transform`]
|
|
|
|
|
//! - [`border`]
|
|
|
|
|
//! - [`padding`]
|
|
|
|
|
//!
|
|
|
|
|
//! The following APIs can't take them into account and will therefore provide inaccurate results:
|
|
|
|
|
//! - [`WindowEvent::Resized`] and [`Window::(set_)inner_size()`]
|
|
|
|
|
//! - [`WindowEvent::Occluded`]
|
|
|
|
|
//! - [`WindowEvent::CursorMoved`], [`WindowEvent::CursorEntered`], [`WindowEvent::CursorLeft`],
|
|
|
|
|
//! and [`WindowEvent::Touch`].
|
2023-07-10 23:55:43 +02:00
|
|
|
//! - [`Window::set_outer_position()`]
|
2023-07-10 12:50:28 +02:00
|
|
|
//!
|
|
|
|
|
//! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized
|
|
|
|
|
//! [`Window::(set_)inner_size()`]: crate::window::Window::inner_size()
|
|
|
|
|
//! [`WindowEvent::Occluded`]: crate::event::WindowEvent::Occluded
|
|
|
|
|
//! [`WindowEvent::CursorMoved`]: crate::event::WindowEvent::CursorMoved
|
|
|
|
|
//! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered
|
|
|
|
|
//! [`WindowEvent::CursorLeft`]: crate::event::WindowEvent::CursorLeft
|
|
|
|
|
//! [`WindowEvent::Touch`]: crate::event::WindowEvent::Touch
|
2023-07-10 23:55:43 +02:00
|
|
|
//! [`Window::set_outer_position()`]: crate::window::Window::set_outer_position()
|
2023-07-10 12:50:28 +02:00
|
|
|
//! [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
|
|
|
|
//! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
|
|
|
|
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
2020-01-15 21:20:14 -05:00
|
|
|
|
2024-01-12 11:51:19 +01:00
|
|
|
use std::error::Error;
|
|
|
|
|
use std::fmt::{self, Display, Formatter};
|
|
|
|
|
use std::future::Future;
|
|
|
|
|
use std::pin::Pin;
|
|
|
|
|
use std::task::{Context, Poll};
|
|
|
|
|
use std::time::Duration;
|
2019-09-24 19:33:32 -04:00
|
|
|
|
2024-01-14 18:54:52 +01:00
|
|
|
#[cfg(web_platform)]
|
2019-06-25 03:15:34 +02:00
|
|
|
use web_sys::HtmlCanvasElement;
|
|
|
|
|
|
2024-01-12 11:51:19 +01:00
|
|
|
use crate::cursor::CustomCursorBuilder;
|
|
|
|
|
use crate::event::Event;
|
2024-01-31 17:29:59 +04:00
|
|
|
use crate::event_loop::{ActiveEventLoop, EventLoop};
|
2024-01-14 18:54:52 +01:00
|
|
|
#[cfg(web_platform)]
|
2024-01-12 11:51:19 +01:00
|
|
|
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
|
|
|
|
|
use crate::platform_impl::{PlatformCustomCursor, PlatformCustomCursorBuilder};
|
2024-01-31 17:29:59 +04:00
|
|
|
use crate::window::{CustomCursor, Window, WindowAttributes};
|
2024-01-12 11:51:19 +01:00
|
|
|
|
2024-01-14 18:54:52 +01:00
|
|
|
#[cfg(not(web_platform))]
|
2024-01-04 12:54:35 +01:00
|
|
|
#[doc(hidden)]
|
|
|
|
|
pub struct HtmlCanvasElement;
|
|
|
|
|
|
2019-06-25 03:15:34 +02:00
|
|
|
pub trait WindowExtWebSys {
|
2023-12-25 00:54:01 +01:00
|
|
|
/// Only returns the canvas if called from inside the window context (the
|
|
|
|
|
/// main thread).
|
2023-06-05 02:44:54 +02:00
|
|
|
fn canvas(&self) -> Option<HtmlCanvasElement>;
|
2023-12-25 09:37:35 +01:00
|
|
|
|
|
|
|
|
/// Returns [`true`] if calling `event.preventDefault()` is enabled.
|
|
|
|
|
///
|
|
|
|
|
/// See [`Window::set_prevent_default()`] for more details.
|
|
|
|
|
fn prevent_default(&self) -> bool;
|
|
|
|
|
|
|
|
|
|
/// Sets whether `event.preventDefault()` should be called on events on the
|
|
|
|
|
/// canvas that have side effects.
|
|
|
|
|
///
|
|
|
|
|
/// For example, by default using the mouse wheel would cause the page to scroll, enabling this
|
|
|
|
|
/// would prevent that.
|
|
|
|
|
///
|
|
|
|
|
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
|
|
|
|
|
/// context menu with Shift+Rightclick.
|
|
|
|
|
fn set_prevent_default(&self, prevent_default: bool);
|
2019-06-25 03:15:34 +02:00
|
|
|
}
|
2020-01-15 21:20:14 -05:00
|
|
|
|
2023-08-14 21:19:57 +02:00
|
|
|
impl WindowExtWebSys for Window {
|
|
|
|
|
#[inline]
|
|
|
|
|
fn canvas(&self) -> Option<HtmlCanvasElement> {
|
|
|
|
|
self.window.canvas()
|
|
|
|
|
}
|
2023-12-25 09:37:35 +01:00
|
|
|
|
|
|
|
|
fn prevent_default(&self) -> bool {
|
|
|
|
|
self.window.prevent_default()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_prevent_default(&self, prevent_default: bool) {
|
|
|
|
|
self.window.set_prevent_default(prevent_default)
|
|
|
|
|
}
|
2023-08-14 21:19:57 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-31 17:29:59 +04:00
|
|
|
pub trait WindowAttributesExtWebSys {
|
2023-08-24 18:29:31 +02:00
|
|
|
/// Pass an [`HtmlCanvasElement`] to be used for this [`Window`]. If [`None`],
|
2024-01-31 17:29:59 +04:00
|
|
|
/// [`WindowAttributes::default()`] will create one.
|
2023-07-12 17:11:17 +02:00
|
|
|
///
|
|
|
|
|
/// In any case, the canvas won't be automatically inserted into the web page.
|
|
|
|
|
///
|
|
|
|
|
/// [`None`] by default.
|
2024-01-04 12:54:35 +01:00
|
|
|
#[cfg_attr(
|
2024-01-14 18:54:52 +01:00
|
|
|
not(web_platform),
|
2024-01-04 12:54:35 +01:00
|
|
|
doc = "",
|
|
|
|
|
doc = "[`HtmlCanvasElement`]: #only-available-on-wasm"
|
|
|
|
|
)]
|
2020-01-15 21:20:14 -05:00
|
|
|
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
|
2022-07-14 13:22:31 -02:30
|
|
|
|
2023-12-25 09:37:35 +01:00
|
|
|
/// Sets whether `event.preventDefault()` should be called on events on the
|
2023-12-25 00:54:01 +01:00
|
|
|
/// canvas that have side effects.
|
2022-07-14 13:22:31 -02:30
|
|
|
///
|
2023-12-25 09:37:35 +01:00
|
|
|
/// See [`Window::set_prevent_default()`] for more details.
|
2023-07-12 17:11:17 +02:00
|
|
|
///
|
|
|
|
|
/// Enabled by default.
|
2022-07-14 13:22:31 -02:30
|
|
|
fn with_prevent_default(self, prevent_default: bool) -> Self;
|
|
|
|
|
|
|
|
|
|
/// Whether the canvas should be focusable using the tab key. This is necessary to capture
|
|
|
|
|
/// canvas keyboard events.
|
2023-07-12 17:11:17 +02:00
|
|
|
///
|
|
|
|
|
/// Enabled by default.
|
2022-07-14 13:22:31 -02:30
|
|
|
fn with_focusable(self, focusable: bool) -> Self;
|
2023-07-12 17:11:52 +02:00
|
|
|
|
|
|
|
|
/// On window creation, append the canvas element to the web page if it isn't already.
|
|
|
|
|
///
|
|
|
|
|
/// Disabled by default.
|
|
|
|
|
fn with_append(self, append: bool) -> Self;
|
2020-01-15 21:20:14 -05:00
|
|
|
}
|
|
|
|
|
|
2024-01-31 17:29:59 +04:00
|
|
|
impl WindowAttributesExtWebSys for WindowAttributes {
|
2020-01-15 21:20:14 -05:00
|
|
|
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
|
2024-01-31 17:29:59 +04:00
|
|
|
self.platform_specific.set_canvas(canvas);
|
2020-01-15 21:20:14 -05:00
|
|
|
self
|
|
|
|
|
}
|
2022-07-14 13:22:31 -02:30
|
|
|
|
|
|
|
|
fn with_prevent_default(mut self, prevent_default: bool) -> Self {
|
2024-01-31 17:29:59 +04:00
|
|
|
self.platform_specific.prevent_default = prevent_default;
|
2022-07-14 13:22:31 -02:30
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn with_focusable(mut self, focusable: bool) -> Self {
|
2024-01-31 17:29:59 +04:00
|
|
|
self.platform_specific.focusable = focusable;
|
2022-07-14 13:22:31 -02:30
|
|
|
self
|
|
|
|
|
}
|
2023-07-12 17:11:52 +02:00
|
|
|
|
|
|
|
|
fn with_append(mut self, append: bool) -> Self {
|
2024-01-31 17:29:59 +04:00
|
|
|
self.platform_specific.append = append;
|
2023-07-12 17:11:52 +02:00
|
|
|
self
|
|
|
|
|
}
|
2020-01-15 21:20:14 -05:00
|
|
|
}
|
2022-07-14 01:17:18 +10:00
|
|
|
|
|
|
|
|
/// Additional methods on `EventLoop` that are specific to the web.
|
|
|
|
|
pub trait EventLoopExtWebSys {
|
|
|
|
|
/// A type provided by the user that can be passed through `Event::UserEvent`.
|
|
|
|
|
type UserEvent;
|
|
|
|
|
|
|
|
|
|
/// Initializes the winit event loop.
|
|
|
|
|
///
|
2023-10-25 17:42:51 +02:00
|
|
|
/// Unlike
|
|
|
|
|
#[cfg_attr(
|
2024-01-14 18:54:52 +01:00
|
|
|
all(web_platform, target_feature = "exception-handling"),
|
2023-10-25 17:42:51 +02:00
|
|
|
doc = "`run()`"
|
|
|
|
|
)]
|
|
|
|
|
#[cfg_attr(
|
2024-01-14 18:54:52 +01:00
|
|
|
not(all(web_platform, target_feature = "exception-handling")),
|
2023-10-25 17:42:51 +02:00
|
|
|
doc = "[`run()`]"
|
|
|
|
|
)]
|
|
|
|
|
/// [^1], this returns immediately, and doesn't throw an exception in order to
|
|
|
|
|
/// satisfy its [`!`] return type.
|
2023-06-23 15:01:42 -02:30
|
|
|
///
|
|
|
|
|
/// Once the event loop has been destroyed, it's possible to reinitialize another event loop
|
|
|
|
|
/// by calling this function again. This can be useful if you want to recreate the event loop
|
|
|
|
|
/// while the WebAssembly module is still loaded. For example, this can be used to recreate the
|
|
|
|
|
/// event loop when switching between tabs on a single page application.
|
2023-10-25 17:42:51 +02:00
|
|
|
///
|
|
|
|
|
#[cfg_attr(
|
2024-01-14 18:54:52 +01:00
|
|
|
not(all(web_platform, target_feature = "exception-handling")),
|
2023-10-25 17:42:51 +02:00
|
|
|
doc = "[`run()`]: EventLoop::run()"
|
|
|
|
|
)]
|
|
|
|
|
/// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`.
|
2022-07-14 01:17:18 +10:00
|
|
|
fn spawn<F>(self, event_handler: F)
|
|
|
|
|
where
|
2024-01-31 17:29:59 +04:00
|
|
|
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
2022-07-14 01:17:18 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T> EventLoopExtWebSys for EventLoop<T> {
|
|
|
|
|
type UserEvent = T;
|
|
|
|
|
|
|
|
|
|
fn spawn<F>(self, event_handler: F)
|
|
|
|
|
where
|
2024-01-31 17:29:59 +04:00
|
|
|
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
|
2022-07-14 01:17:18 +10:00
|
|
|
{
|
|
|
|
|
self.event_loop.spawn(event_handler)
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-07 12:12:35 +02:00
|
|
|
|
2024-01-31 17:29:59 +04:00
|
|
|
pub trait ActiveEventLoopExtWebSys {
|
2023-09-07 12:12:35 +02:00
|
|
|
/// Sets the strategy for [`ControlFlow::Poll`].
|
|
|
|
|
///
|
2023-09-08 18:39:23 +02:00
|
|
|
/// See [`PollStrategy`].
|
2023-09-07 12:12:35 +02:00
|
|
|
///
|
|
|
|
|
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
2023-09-08 18:39:23 +02:00
|
|
|
fn set_poll_strategy(&self, strategy: PollStrategy);
|
2023-09-07 12:12:35 +02:00
|
|
|
|
|
|
|
|
/// Gets the strategy for [`ControlFlow::Poll`].
|
|
|
|
|
///
|
2023-09-08 18:39:23 +02:00
|
|
|
/// See [`PollStrategy`].
|
2023-09-07 12:12:35 +02:00
|
|
|
///
|
|
|
|
|
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
2023-09-08 18:39:23 +02:00
|
|
|
fn poll_strategy(&self) -> PollStrategy;
|
2023-09-07 12:12:35 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-31 17:29:59 +04:00
|
|
|
impl ActiveEventLoopExtWebSys for ActiveEventLoop {
|
2023-09-07 12:12:35 +02:00
|
|
|
#[inline]
|
2023-09-08 18:39:23 +02:00
|
|
|
fn set_poll_strategy(&self, strategy: PollStrategy) {
|
|
|
|
|
self.p.set_poll_strategy(strategy);
|
2023-09-07 12:12:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2023-09-08 18:39:23 +02:00
|
|
|
fn poll_strategy(&self) -> PollStrategy {
|
|
|
|
|
self.p.poll_strategy()
|
2023-09-07 12:12:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Strategy used for [`ControlFlow::Poll`](crate::event_loop::ControlFlow::Poll).
|
|
|
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
2023-09-08 18:39:23 +02:00
|
|
|
pub enum PollStrategy {
|
2023-09-07 12:12:35 +02:00
|
|
|
/// Uses [`Window.requestIdleCallback()`] to queue the next event loop. If not available
|
|
|
|
|
/// this will fallback to [`setTimeout()`].
|
|
|
|
|
///
|
|
|
|
|
/// This strategy will wait for the browser to enter an idle period before running and might
|
|
|
|
|
/// be affected by browser throttling.
|
|
|
|
|
///
|
|
|
|
|
/// [`Window.requestIdleCallback()`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
|
|
|
|
|
/// [`setTimeout()`]: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
|
|
|
|
|
IdleCallback,
|
|
|
|
|
/// Uses the [Prioritized Task Scheduling API] to queue the next event loop. If not available
|
|
|
|
|
/// this will fallback to [`setTimeout()`].
|
|
|
|
|
///
|
|
|
|
|
/// This strategy will run as fast as possible without disturbing users from interacting with
|
|
|
|
|
/// the page and is not affected by browser throttling.
|
|
|
|
|
///
|
|
|
|
|
/// This is the default strategy.
|
|
|
|
|
///
|
|
|
|
|
/// [Prioritized Task Scheduling API]: https://developer.mozilla.org/en-US/docs/Web/API/Prioritized_Task_Scheduling_API
|
|
|
|
|
/// [`setTimeout()`]: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
|
|
|
|
|
#[default]
|
|
|
|
|
Scheduler,
|
|
|
|
|
}
|
2023-12-16 22:02:17 +02:00
|
|
|
|
|
|
|
|
pub trait CustomCursorExtWebSys {
|
2024-01-12 11:51:19 +01:00
|
|
|
/// Returns if this cursor is an animation.
|
|
|
|
|
fn is_animation(&self) -> bool;
|
|
|
|
|
|
2023-12-16 22:02:17 +02:00
|
|
|
/// Creates a new cursor from a URL pointing to an image.
|
|
|
|
|
/// It uses the [url css function](https://developer.mozilla.org/en-US/docs/Web/CSS/url),
|
|
|
|
|
/// but browser support for image formats is inconsistent. Using [PNG] is recommended.
|
|
|
|
|
///
|
|
|
|
|
/// [PNG]: https://en.wikipedia.org/wiki/PNG
|
2023-12-22 22:20:41 +01:00
|
|
|
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder;
|
2024-01-12 11:51:19 +01:00
|
|
|
|
|
|
|
|
/// Crates a new animated cursor from multiple [`CustomCursor`]s.
|
|
|
|
|
/// Supplied `cursors` can't be empty or other animations.
|
|
|
|
|
fn from_animation(
|
|
|
|
|
duration: Duration,
|
|
|
|
|
cursors: Vec<CustomCursor>,
|
|
|
|
|
) -> Result<CustomCursorBuilder, BadAnimation>;
|
2023-12-16 22:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CustomCursorExtWebSys for CustomCursor {
|
2024-01-12 11:51:19 +01:00
|
|
|
fn is_animation(&self) -> bool {
|
|
|
|
|
self.inner.animation
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-22 22:20:41 +01:00
|
|
|
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder {
|
|
|
|
|
CustomCursorBuilder {
|
|
|
|
|
inner: PlatformCustomCursorBuilder::Url {
|
2023-12-16 22:02:17 +02:00
|
|
|
url,
|
|
|
|
|
hotspot_x,
|
|
|
|
|
hotspot_y,
|
2023-12-22 22:20:41 +01:00
|
|
|
},
|
2023-12-16 22:02:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-12 11:51:19 +01:00
|
|
|
|
|
|
|
|
fn from_animation(
|
|
|
|
|
duration: Duration,
|
|
|
|
|
cursors: Vec<CustomCursor>,
|
|
|
|
|
) -> Result<CustomCursorBuilder, BadAnimation> {
|
|
|
|
|
if cursors.is_empty() {
|
|
|
|
|
return Err(BadAnimation::Empty);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if cursors.iter().any(CustomCursor::is_animation) {
|
|
|
|
|
return Err(BadAnimation::Animation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(CustomCursorBuilder {
|
|
|
|
|
inner: PlatformCustomCursorBuilder::Animation { duration, cursors },
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// An error produced when using [`CustomCursor::from_animation`] with invalid arguments.
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum BadAnimation {
|
|
|
|
|
/// Produced when no cursors were supplied.
|
|
|
|
|
Empty,
|
|
|
|
|
/// Produced when a supplied cursor is an animation.
|
|
|
|
|
Animation,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for BadAnimation {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Empty => write!(f, "No cursors supplied"),
|
|
|
|
|
Self::Animation => write!(f, "A supplied cursor is an animtion"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Error for BadAnimation {}
|
|
|
|
|
|
|
|
|
|
pub trait CustomCursorBuilderExtWebSys {
|
|
|
|
|
/// Async version of [`CustomCursorBuilder::build()`] which waits until the
|
|
|
|
|
/// cursor has completely finished loading.
|
2024-01-31 17:29:59 +04:00
|
|
|
fn build_async(self, window_target: &ActiveEventLoop) -> CustomCursorFuture;
|
2024-01-12 11:51:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CustomCursorBuilderExtWebSys for CustomCursorBuilder {
|
2024-01-31 17:29:59 +04:00
|
|
|
fn build_async(self, window_target: &ActiveEventLoop) -> CustomCursorFuture {
|
2024-01-12 11:51:19 +01:00
|
|
|
CustomCursorFuture(PlatformCustomCursor::build_async(
|
|
|
|
|
self.inner,
|
|
|
|
|
&window_target.p,
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-14 18:54:52 +01:00
|
|
|
#[cfg(not(web_platform))]
|
2024-01-12 11:51:19 +01:00
|
|
|
struct PlatformCustomCursorFuture;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct CustomCursorFuture(PlatformCustomCursorFuture);
|
|
|
|
|
|
|
|
|
|
impl Future for CustomCursorFuture {
|
|
|
|
|
type Output = Result<CustomCursor, CustomCursorError>;
|
|
|
|
|
|
|
|
|
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
|
|
|
Pin::new(&mut self.0)
|
|
|
|
|
.poll(cx)
|
|
|
|
|
.map_ok(|cursor| CustomCursor { inner: cursor })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub enum CustomCursorError {
|
|
|
|
|
Blob,
|
|
|
|
|
Decode(String),
|
|
|
|
|
Animation,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for CustomCursorError {
|
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Blob => write!(f, "failed to create `Blob`"),
|
|
|
|
|
Self::Decode(error) => write!(f, "failed to decode image: {error}"),
|
|
|
|
|
Self::Animation => write!(
|
|
|
|
|
f,
|
|
|
|
|
"found `CustomCursor` that is an animation when building an animation"
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-16 22:02:17 +02:00
|
|
|
}
|