From 3f9a0ef143ae20c94852020618f8dc73bc71ded3 Mon Sep 17 00:00:00 2001 From: MrGVSV Date: Mon, 20 Dec 2021 22:04:45 +0000 Subject: [PATCH] Added `set_cursor_icon(...)` to `Window` (#3395) # Objective The window's cursor should be settable without having to implement a custom cursor icon solution. This will especially be helpful when creating user-interfaces that might like to use the cursor to denote some meaning (e.g., _clickable_, _resizable_, etc.). ## Solution Added a `CursorIcon` enum that maps one-to-one to winit's `CursorIcon` enum, as well as a method to set/get it for the given `Window`. --- crates/bevy_window/src/cursor.rs | 39 +++++++++++++++++++++++++++ crates/bevy_window/src/lib.rs | 6 +++-- crates/bevy_window/src/window.rs | 16 +++++++++++ crates/bevy_winit/src/converters.rs | 41 +++++++++++++++++++++++++++++ crates/bevy_winit/src/lib.rs | 4 +++ examples/window/window_settings.rs | 28 ++++++++++++++++++++ 6 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 crates/bevy_window/src/cursor.rs diff --git a/crates/bevy_window/src/cursor.rs b/crates/bevy_window/src/cursor.rs new file mode 100644 index 0000000000000..8fa0edfae1548 --- /dev/null +++ b/crates/bevy_window/src/cursor.rs @@ -0,0 +1,39 @@ +/// The icon to display for a window's cursor +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum CursorIcon { + Default, + Crosshair, + Hand, + Arrow, + Move, + Text, + Wait, + Help, + Progress, + NotAllowed, + ContextMenu, + Cell, + VerticalText, + Alias, + Copy, + NoDrop, + Grab, + Grabbing, + AllScroll, + ZoomIn, + ZoomOut, + EResize, + NResize, + NeResize, + NwResize, + SResize, + SeResize, + SwResize, + WResize, + EwResize, + NsResize, + NeswResize, + NwseResize, + ColResize, + RowResize, +} diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index c07eecf5989dd..a63ca57a96b7d 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -1,3 +1,4 @@ +mod cursor; mod event; mod raw_window_handle; mod system; @@ -5,6 +6,7 @@ mod window; mod windows; pub use crate::raw_window_handle::*; +pub use cursor::*; pub use event::*; pub use system::*; pub use window::*; @@ -13,8 +15,8 @@ pub use windows::*; pub mod prelude { #[doc(hidden)] pub use crate::{ - CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, ReceivedCharacter, Window, - WindowDescriptor, WindowMoved, Windows, + CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, ReceivedCharacter, + Window, WindowDescriptor, WindowMoved, Windows, }; } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 8ea6a02770a4e..db202c27173f5 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -19,6 +19,7 @@ impl WindowId { } } +use crate::CursorIcon; use std::fmt; use crate::raw_window_handle::RawWindowHandleWrapper; @@ -123,6 +124,7 @@ pub struct Window { vsync: bool, resizable: bool, decorations: bool, + cursor_icon: CursorIcon, cursor_visible: bool, cursor_locked: bool, physical_cursor_position: Option, @@ -162,6 +164,9 @@ pub enum WindowCommand { SetCursorLockMode { locked: bool, }, + SetCursorIcon { + icon: CursorIcon, + }, SetCursorVisibility { visible: bool, }, @@ -222,6 +227,7 @@ impl Window { decorations: window_descriptor.decorations, cursor_visible: window_descriptor.cursor_visible, cursor_locked: window_descriptor.cursor_locked, + cursor_icon: CursorIcon::Default, physical_cursor_position: None, raw_window_handle: RawWindowHandleWrapper::new(raw_window_handle), focused: true, @@ -474,6 +480,16 @@ impl Window { }); } + #[inline] + pub fn cursor_icon(&self) -> CursorIcon { + self.cursor_icon + } + + pub fn set_cursor_icon(&mut self, icon: CursorIcon) { + self.command_queue + .push(WindowCommand::SetCursorIcon { icon }); + } + /// The current mouse position, in physical pixels. #[inline] pub fn physical_cursor_position(&self) -> Option { diff --git a/crates/bevy_winit/src/converters.rs b/crates/bevy_winit/src/converters.rs index 9ccdca17f9df7..e1e7451d07dd9 100644 --- a/crates/bevy_winit/src/converters.rs +++ b/crates/bevy_winit/src/converters.rs @@ -5,6 +5,7 @@ use bevy_input::{ ElementState, }; use bevy_math::Vec2; +use bevy_window::CursorIcon; pub fn convert_keyboard_input(keyboard_input: &winit::event::KeyboardInput) -> KeyboardInput { KeyboardInput { @@ -225,3 +226,43 @@ pub fn convert_virtual_key_code(virtual_key_code: winit::event::VirtualKeyCode) winit::event::VirtualKeyCode::Cut => KeyCode::Cut, } } + +pub fn convert_cursor_icon(cursor_icon: CursorIcon) -> winit::window::CursorIcon { + match cursor_icon { + CursorIcon::Default => winit::window::CursorIcon::Default, + CursorIcon::Crosshair => winit::window::CursorIcon::Crosshair, + CursorIcon::Hand => winit::window::CursorIcon::Hand, + CursorIcon::Arrow => winit::window::CursorIcon::Arrow, + CursorIcon::Move => winit::window::CursorIcon::Move, + CursorIcon::Text => winit::window::CursorIcon::Text, + CursorIcon::Wait => winit::window::CursorIcon::Wait, + CursorIcon::Help => winit::window::CursorIcon::Help, + CursorIcon::Progress => winit::window::CursorIcon::Progress, + CursorIcon::NotAllowed => winit::window::CursorIcon::NotAllowed, + CursorIcon::ContextMenu => winit::window::CursorIcon::ContextMenu, + CursorIcon::Cell => winit::window::CursorIcon::Cell, + CursorIcon::VerticalText => winit::window::CursorIcon::VerticalText, + CursorIcon::Alias => winit::window::CursorIcon::Alias, + CursorIcon::Copy => winit::window::CursorIcon::Copy, + CursorIcon::NoDrop => winit::window::CursorIcon::NoDrop, + CursorIcon::Grab => winit::window::CursorIcon::Grab, + CursorIcon::Grabbing => winit::window::CursorIcon::Grabbing, + CursorIcon::AllScroll => winit::window::CursorIcon::AllScroll, + CursorIcon::ZoomIn => winit::window::CursorIcon::ZoomIn, + CursorIcon::ZoomOut => winit::window::CursorIcon::ZoomOut, + CursorIcon::EResize => winit::window::CursorIcon::EResize, + CursorIcon::NResize => winit::window::CursorIcon::NResize, + CursorIcon::NeResize => winit::window::CursorIcon::NeResize, + CursorIcon::NwResize => winit::window::CursorIcon::NwResize, + CursorIcon::SResize => winit::window::CursorIcon::SResize, + CursorIcon::SeResize => winit::window::CursorIcon::SeResize, + CursorIcon::SwResize => winit::window::CursorIcon::SwResize, + CursorIcon::WResize => winit::window::CursorIcon::WResize, + CursorIcon::EwResize => winit::window::CursorIcon::EwResize, + CursorIcon::NsResize => winit::window::CursorIcon::NsResize, + CursorIcon::NeswResize => winit::window::CursorIcon::NeswResize, + CursorIcon::NwseResize => winit::window::CursorIcon::NwseResize, + CursorIcon::ColResize => winit::window::CursorIcon::ColResize, + CursorIcon::RowResize => winit::window::CursorIcon::RowResize, + } +} diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index e2596e1f227ee..a833ecf44b57f 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -103,6 +103,10 @@ fn change_window(world: &mut World) { let window = winit_windows.get_window(id).unwrap(); window.set_decorations(decorations); } + bevy_window::WindowCommand::SetCursorIcon { icon } => { + let window = winit_windows.get_window(id).unwrap(); + window.set_cursor_icon(converters::convert_cursor_icon(icon)); + } bevy_window::WindowCommand::SetCursorLockMode { locked } => { let window = winit_windows.get_window(id).unwrap(); window diff --git a/examples/window/window_settings.rs b/examples/window/window_settings.rs index 8cb3f8beff38f..e00ba07416809 100644 --- a/examples/window/window_settings.rs +++ b/examples/window/window_settings.rs @@ -13,6 +13,7 @@ fn main() { .add_plugins(DefaultPlugins) .add_system(change_title) .add_system(toggle_cursor) + .add_system(cycle_cursor_icon) .run(); } @@ -33,3 +34,30 @@ fn toggle_cursor(input: Res>, mut windows: ResMut) { window.set_cursor_visibility(!window.cursor_visible()); } } + +/// This system cycles the cursor's icon through a small set of icons when clicking +fn cycle_cursor_icon( + input: Res>, + mut windows: ResMut, + mut index: Local, +) { + const ICONS: &[CursorIcon] = &[ + CursorIcon::Default, + CursorIcon::Hand, + CursorIcon::Wait, + CursorIcon::Text, + CursorIcon::Copy, + ]; + let window = windows.get_primary_mut().unwrap(); + if input.just_pressed(MouseButton::Left) { + *index = (*index + 1) % ICONS.len(); + window.set_cursor_icon(ICONS[*index]); + } else if input.just_pressed(MouseButton::Right) { + *index = if *index == 0 { + ICONS.len() - 1 + } else { + *index - 1 + }; + window.set_cursor_icon(ICONS[*index]); + } +}