diff --git a/crates/bevy_winit/src/cursor.rs b/crates/bevy_winit/src/cursor.rs index 885a78297c..2dbcb12d7c 100644 --- a/crates/bevy_winit/src/cursor.rs +++ b/crates/bevy_winit/src/cursor.rs @@ -37,7 +37,7 @@ use bevy_window::{SystemCursorIcon, Window}; use tracing::warn; #[cfg(feature = "custom_cursor")] -pub use crate::custom_cursor::CustomCursor; +pub use crate::custom_cursor::{CustomCursor, CustomCursorImage}; pub(crate) struct CursorPlugin; @@ -91,14 +91,16 @@ fn update_cursors( let cursor_source = match cursor.as_ref() { #[cfg(feature = "custom_cursor")] - CursorIcon::Custom(CustomCursor::Image { - handle, - texture_atlas, - flip_x, - flip_y, - rect, - hotspot, - }) => { + CursorIcon::Custom(CustomCursor::Image(c)) => { + let CustomCursorImage { + handle, + texture_atlas, + flip_x, + flip_y, + rect, + hotspot, + } = c; + let cache_key = CustomCursorCacheKey::Image { id: handle.id(), texture_atlas_layout_id: texture_atlas.as_ref().map(|a| a.layout.id()), @@ -155,14 +157,15 @@ fn update_cursors( target_family = "wasm", target_os = "unknown" ))] - CursorIcon::Custom(CustomCursor::Url { url, hotspot }) => { - let cache_key = CustomCursorCacheKey::Url(url.clone()); + CursorIcon::Custom(CustomCursor::Url(c)) => { + let cache_key = CustomCursorCacheKey::Url(c.url.clone()); if cursor_cache.0.contains_key(&cache_key) { CursorSource::CustomCached(cache_key) } else { use crate::CustomCursorExtWebSys; - let source = WinitCustomCursor::from_url(url.clone(), hotspot.0, hotspot.1); + let source = + WinitCustomCursor::from_url(c.url.clone(), c.hotspot.0, c.hotspot.1); CursorSource::Custom((cache_key, source)) } } diff --git a/crates/bevy_winit/src/custom_cursor.rs b/crates/bevy_winit/src/custom_cursor.rs index 7cc0defe35..cc5854e7ed 100644 --- a/crates/bevy_winit/src/custom_cursor.rs +++ b/crates/bevy_winit/src/custom_cursor.rs @@ -2,46 +2,57 @@ use bevy_app::{App, Plugin}; use bevy_asset::{Assets, Handle}; use bevy_image::{Image, TextureAtlas, TextureAtlasLayout, TextureAtlasPlugin}; use bevy_math::{ops, Rect, URect, UVec2, Vec2}; -use bevy_reflect::Reflect; +use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use wgpu_types::TextureFormat; use crate::{cursor::CursorIcon, state::CustomCursorCache}; +/// A custom cursor created from an image. +#[derive(Debug, Clone, Default, Reflect, PartialEq, Eq, Hash)] +#[reflect(Debug, Default, Hash, PartialEq)] +pub struct CustomCursorImage { + /// Handle to the image to use as the cursor. The image must be in 8 bit int + /// or 32 bit float rgba. PNG images work well for this. + pub handle: Handle, + /// An optional texture atlas used to render the image. + pub texture_atlas: Option, + /// Whether the image should be flipped along its x-axis. + pub flip_x: bool, + /// Whether the image should be flipped along its y-axis. + pub flip_y: bool, + /// An optional rectangle representing the region of the image to render, + /// instead of rendering the full image. This is an easy one-off alternative + /// to using a [`TextureAtlas`]. + /// + /// When used with a [`TextureAtlas`], the rect is offset by the atlas's + /// minimal (top-left) corner position. + pub rect: Option, + /// X and Y coordinates of the hotspot in pixels. The hotspot must be within + /// the image bounds. + pub hotspot: (u16, u16), +} + +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +/// A custom cursor created from a URL. +#[derive(Debug, Clone, Default, Reflect, PartialEq, Eq, Hash)] +#[reflect(Debug, Default, Hash, PartialEq)] +pub struct CustomCursorUrl { + /// Web URL to an image to use as the cursor. PNGs are preferred. Cursor + /// creation can fail if the image is invalid or not reachable. + pub url: String, + /// X and Y coordinates of the hotspot in pixels. The hotspot must be within + /// the image bounds. + pub hotspot: (u16, u16), +} + /// Custom cursor image data. #[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash)] pub enum CustomCursor { - /// Image to use as a cursor. - Image { - /// The image must be in 8 bit int or 32 bit float rgba. PNG images - /// work well for this. - handle: Handle, - /// The (optional) texture atlas used to render the image. - texture_atlas: Option, - /// Whether the image should be flipped along its x-axis. - flip_x: bool, - /// Whether the image should be flipped along its y-axis. - flip_y: bool, - /// An optional rectangle representing the region of the image to - /// render, instead of rendering the full image. This is an easy one-off - /// alternative to using a [`TextureAtlas`]. - /// - /// When used with a [`TextureAtlas`], the rect is offset by the atlas's - /// minimal (top-left) corner position. - rect: Option, - /// X and Y coordinates of the hotspot in pixels. The hotspot must be - /// within the image bounds. - hotspot: (u16, u16), - }, + /// Use an image as the cursor. + Image(CustomCursorImage), #[cfg(all(target_family = "wasm", target_os = "unknown"))] - /// A URL to an image to use as the cursor. - Url { - /// Web URL to an image to use as the cursor. PNGs preferred. Cursor - /// creation can fail if the image is invalid or not reachable. - url: String, - /// X and Y coordinates of the hotspot in pixels. The hotspot must be - /// within the image bounds. - hotspot: (u16, u16), - }, + /// Use a URL to an image as the cursor. + Url(CustomCursorUrl), } impl From for CursorIcon { diff --git a/examples/window/custom_cursor_image.rs b/examples/window/custom_cursor_image.rs index b63a90e2df..bdc82c82d2 100644 --- a/examples/window/custom_cursor_image.rs +++ b/examples/window/custom_cursor_image.rs @@ -3,8 +3,10 @@ use std::time::Duration; -use bevy::winit::cursor::CustomCursor; -use bevy::{prelude::*, winit::cursor::CursorIcon}; +use bevy::{ + prelude::*, + winit::cursor::{CursorIcon, CustomCursor, CustomCursorImage}, +}; fn main() { App::new() @@ -39,7 +41,7 @@ fn setup_cursor_icon( let animation_config = AnimationConfig::new(0, 199, 1, 4); commands.entity(*window).insert(( - CursorIcon::Custom(CustomCursor::Image { + CursorIcon::Custom(CustomCursor::Image(CustomCursorImage { // Image to use as the cursor. handle: asset_server .load("cursors/kenney_crosshairPack/Tilesheet/crosshairs_tilesheet_white.png"), @@ -56,7 +58,7 @@ fn setup_cursor_icon( // The hotspot is the point in the cursor image that will be // positioned at the mouse cursor's position. hotspot: (0, 0), - }), + })), animation_config, )); } @@ -112,15 +114,11 @@ impl AnimationConfig { /// `last_sprite_index`. fn execute_animation(time: Res