Correctly feature gate custom_cursor (#16093)

# Objective

Currently there's no way to change the window's cursor icon with the
`custom_cursor` feature **disabled**. You should still be able to set
system cursor icons.

Connections:

- https://github.com/bevyengine/bevy/pull/15649

## Solution

Move some `custom_cursor` feature gates around, as to expose the
`CursorIcon` type again.

Note this refactoring was mainly piloted by hunting after the compiler
warnings -- I shouldn't have missed anything, but FYI.

## Testing

Disabled the `custom_cursor` feature, ran the `window_settings` example.
This commit is contained in:
Friz64 2024-11-02 02:47:32 +01:00 committed by GitHub
parent 17e504812b
commit 565616622b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 48 additions and 16 deletions

View file

@ -6,7 +6,6 @@ use bevy_input::{
ButtonState, ButtonState,
}; };
use bevy_math::{CompassOctant, Vec2}; use bevy_math::{CompassOctant, Vec2};
#[cfg(feature = "custom_cursor")]
use bevy_window::SystemCursorIcon; use bevy_window::SystemCursorIcon;
use bevy_window::{EnabledButtons, WindowLevel, WindowTheme}; use bevy_window::{EnabledButtons, WindowLevel, WindowTheme};
use winit::keyboard::{Key, NamedKey, NativeKey}; use winit::keyboard::{Key, NamedKey, NativeKey};
@ -630,7 +629,6 @@ pub fn convert_native_key(native_key: &NativeKey) -> bevy_input::keyboard::Nativ
} }
} }
#[cfg(feature = "custom_cursor")]
/// Converts a [`SystemCursorIcon`] to a [`winit::window::CursorIcon`]. /// Converts a [`SystemCursorIcon`] to a [`winit::window::CursorIcon`].
pub fn convert_system_cursor_icon(cursor_icon: SystemCursorIcon) -> winit::window::CursorIcon { pub fn convert_system_cursor_icon(cursor_icon: SystemCursorIcon) -> winit::window::CursorIcon {
match cursor_icon { match cursor_icon {

View file

@ -2,11 +2,18 @@
use crate::{ use crate::{
converters::convert_system_cursor_icon, converters::convert_system_cursor_icon,
state::{CursorSource, CustomCursorCache, CustomCursorCacheKey, PendingCursor}, state::{CursorSource, PendingCursor},
};
#[cfg(feature = "custom_cursor")]
use crate::{
state::{CustomCursorCache, CustomCursorCacheKey},
WinitCustomCursor, WinitCustomCursor,
}; };
use bevy_app::{App, Last, Plugin}; use bevy_app::{App, Last, Plugin};
#[cfg(feature = "custom_cursor")]
use bevy_asset::{Assets, Handle}; use bevy_asset::{Assets, Handle};
#[cfg(feature = "custom_cursor")]
use bevy_ecs::system::Res;
use bevy_ecs::{ use bevy_ecs::{
change_detection::DetectChanges, change_detection::DetectChanges,
component::Component, component::Component,
@ -14,21 +21,27 @@ use bevy_ecs::{
observer::Trigger, observer::Trigger,
query::With, query::With,
reflect::ReflectComponent, reflect::ReflectComponent,
system::{Commands, Local, Query, Res}, system::{Commands, Local, Query},
world::{OnRemove, Ref}, world::{OnRemove, Ref},
}; };
#[cfg(feature = "custom_cursor")]
use bevy_image::Image; use bevy_image::Image;
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_utils::{tracing::warn, HashSet}; #[cfg(feature = "custom_cursor")]
use bevy_utils::tracing::warn;
use bevy_utils::HashSet;
use bevy_window::{SystemCursorIcon, Window}; use bevy_window::{SystemCursorIcon, Window};
#[cfg(feature = "custom_cursor")]
use wgpu_types::TextureFormat; use wgpu_types::TextureFormat;
pub(crate) struct CursorPlugin; pub(crate) struct CursorPlugin;
impl Plugin for CursorPlugin { impl Plugin for CursorPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
#[cfg(feature = "custom_cursor")]
app.init_resource::<CustomCursorCache>();
app.register_type::<CursorIcon>() app.register_type::<CursorIcon>()
.init_resource::<CustomCursorCache>()
.add_systems(Last, update_cursors); .add_systems(Last, update_cursors);
app.add_observer(on_remove_cursor_icon); app.add_observer(on_remove_cursor_icon);
@ -39,6 +52,7 @@ impl Plugin for CursorPlugin {
#[derive(Component, Debug, Clone, Reflect, PartialEq, Eq)] #[derive(Component, Debug, Clone, Reflect, PartialEq, Eq)]
#[reflect(Component, Debug, Default, PartialEq)] #[reflect(Component, Debug, Default, PartialEq)]
pub enum CursorIcon { pub enum CursorIcon {
#[cfg(feature = "custom_cursor")]
/// Custom cursor image. /// Custom cursor image.
Custom(CustomCursor), Custom(CustomCursor),
/// System provided cursor icon. /// System provided cursor icon.
@ -57,12 +71,14 @@ impl From<SystemCursorIcon> for CursorIcon {
} }
} }
#[cfg(feature = "custom_cursor")]
impl From<CustomCursor> for CursorIcon { impl From<CustomCursor> for CursorIcon {
fn from(cursor: CustomCursor) -> Self { fn from(cursor: CustomCursor) -> Self {
CursorIcon::Custom(cursor) CursorIcon::Custom(cursor)
} }
} }
#[cfg(feature = "custom_cursor")]
/// Custom cursor image data. /// Custom cursor image data.
#[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash)]
pub enum CustomCursor { pub enum CustomCursor {
@ -90,8 +106,8 @@ pub enum CustomCursor {
fn update_cursors( fn update_cursors(
mut commands: Commands, mut commands: Commands,
windows: Query<(Entity, Ref<CursorIcon>), With<Window>>, windows: Query<(Entity, Ref<CursorIcon>), With<Window>>,
cursor_cache: Res<CustomCursorCache>, #[cfg(feature = "custom_cursor")] cursor_cache: Res<CustomCursorCache>,
images: Res<Assets<Image>>, #[cfg(feature = "custom_cursor")] images: Res<Assets<Image>>,
mut queue: Local<HashSet<Entity>>, mut queue: Local<HashSet<Entity>>,
) { ) {
for (entity, cursor) in windows.iter() { for (entity, cursor) in windows.iter() {
@ -100,6 +116,7 @@ fn update_cursors(
} }
let cursor_source = match cursor.as_ref() { let cursor_source = match cursor.as_ref() {
#[cfg(feature = "custom_cursor")]
CursorIcon::Custom(CustomCursor::Image { handle, hotspot }) => { CursorIcon::Custom(CustomCursor::Image { handle, hotspot }) => {
let cache_key = CustomCursorCacheKey::Asset(handle.id()); let cache_key = CustomCursorCacheKey::Asset(handle.id());
@ -170,6 +187,7 @@ fn on_remove_cursor_icon(trigger: Trigger<OnRemove, CursorIcon>, mut commands: C
)))); ))));
} }
#[cfg(feature = "custom_cursor")]
/// Returns the image data as a `Vec<u8>`. /// Returns the image data as a `Vec<u8>`.
/// Only supports rgba8 and rgba32float formats. /// Only supports rgba8 and rgba32float formats.
fn image_to_rgba_pixels(image: &Image) -> Option<Vec<u8>> { fn image_to_rgba_pixels(image: &Image) -> Option<Vec<u8>> {

View file

@ -44,7 +44,6 @@ use crate::{
pub mod accessibility; pub mod accessibility;
mod converters; mod converters;
#[cfg(feature = "custom_cursor")]
pub mod cursor; pub mod cursor;
mod state; mod state;
mod system; mod system;
@ -136,7 +135,6 @@ impl<T: Event> Plugin for WinitPlugin<T> {
); );
app.add_plugins(AccessKitPlugin); app.add_plugins(AccessKitPlugin);
#[cfg(feature = "custom_cursor")]
app.add_plugins(cursor::CursorPlugin); app.add_plugins(cursor::CursorPlugin);
let event_loop = event_loop_builder let event_loop = event_loop_builder

View file

@ -158,19 +158,19 @@ pub enum CustomCursorCacheKey {
#[derive(Debug, Clone, Default, Resource)] #[derive(Debug, Clone, Default, Resource)]
pub struct CustomCursorCache(pub HashMap<CustomCursorCacheKey, winit::window::CustomCursor>); pub struct CustomCursorCache(pub HashMap<CustomCursorCacheKey, winit::window::CustomCursor>);
#[cfg(feature = "custom_cursor")]
/// A source for a cursor. Consumed by the winit event loop. /// A source for a cursor. Consumed by the winit event loop.
#[derive(Debug)] #[derive(Debug)]
pub enum CursorSource { pub enum CursorSource {
#[cfg(feature = "custom_cursor")]
/// A custom cursor was identified to be cached, no reason to recreate it. /// A custom cursor was identified to be cached, no reason to recreate it.
CustomCached(CustomCursorCacheKey), CustomCached(CustomCursorCacheKey),
#[cfg(feature = "custom_cursor")]
/// A custom cursor was not cached, so it needs to be created by the winit event loop. /// A custom cursor was not cached, so it needs to be created by the winit event loop.
Custom((CustomCursorCacheKey, winit::window::CustomCursorSource)), Custom((CustomCursorCacheKey, winit::window::CustomCursorSource)),
/// A system cursor was requested. /// A system cursor was requested.
System(winit::window::CursorIcon), System(winit::window::CursorIcon),
} }
#[cfg(feature = "custom_cursor")]
/// Component that indicates what cursor should be used for a window. Inserted /// Component that indicates what cursor should be used for a window. Inserted
/// automatically after changing `CursorIcon` and consumed by the winit event /// automatically after changing `CursorIcon` and consumed by the winit event
/// loop. /// loop.
@ -560,6 +560,8 @@ impl<T: Event> ApplicationHandler<T> for WinitAppRunnerState<T> {
self.run_app_update(); self.run_app_update();
#[cfg(feature = "custom_cursor")] #[cfg(feature = "custom_cursor")]
self.update_cursors(event_loop); self.update_cursors(event_loop);
#[cfg(not(feature = "custom_cursor"))]
self.update_cursors();
self.ran_update_since_last_redraw = true; self.ran_update_since_last_redraw = true;
} else { } else {
self.redraw_requested = true; self.redraw_requested = true;
@ -787,15 +789,23 @@ impl<T: Event> WinitAppRunnerState<T> {
.send_batch(buffered_events); .send_batch(buffered_events);
} }
#[cfg(feature = "custom_cursor")] fn update_cursors(&mut self, #[cfg(feature = "custom_cursor")] event_loop: &ActiveEventLoop) {
fn update_cursors(&mut self, event_loop: &ActiveEventLoop) { #[cfg(feature = "custom_cursor")]
let mut windows_state: SystemState<( let mut windows_state: SystemState<(
NonSendMut<WinitWindows>, NonSendMut<WinitWindows>,
ResMut<CustomCursorCache>, ResMut<CustomCursorCache>,
Query<(Entity, &mut PendingCursor), Changed<PendingCursor>>, Query<(Entity, &mut PendingCursor), Changed<PendingCursor>>,
)> = SystemState::new(self.world_mut()); )> = SystemState::new(self.world_mut());
#[cfg(feature = "custom_cursor")]
let (winit_windows, mut cursor_cache, mut windows) = let (winit_windows, mut cursor_cache, mut windows) =
windows_state.get_mut(self.world_mut()); windows_state.get_mut(self.world_mut());
#[cfg(not(feature = "custom_cursor"))]
let mut windows_state: SystemState<(
NonSendMut<WinitWindows>,
Query<(Entity, &mut PendingCursor), Changed<PendingCursor>>,
)> = SystemState::new(self.world_mut());
#[cfg(not(feature = "custom_cursor"))]
let (winit_windows, mut windows) = windows_state.get_mut(self.world_mut());
for (entity, mut pending_cursor) in windows.iter_mut() { for (entity, mut pending_cursor) in windows.iter_mut() {
let Some(winit_window) = winit_windows.get_window(entity) else { let Some(winit_window) = winit_windows.get_window(entity) else {
@ -806,6 +816,7 @@ impl<T: Event> WinitAppRunnerState<T> {
}; };
let final_cursor: winit::window::Cursor = match pending_cursor { let final_cursor: winit::window::Cursor = match pending_cursor {
#[cfg(feature = "custom_cursor")]
CursorSource::CustomCached(cache_key) => { CursorSource::CustomCached(cache_key) => {
let Some(cached_cursor) = cursor_cache.0.get(&cache_key) else { let Some(cached_cursor) = cursor_cache.0.get(&cache_key) else {
error!("Cursor should have been cached, but was not found"); error!("Cursor should have been cached, but was not found");
@ -813,6 +824,7 @@ impl<T: Event> WinitAppRunnerState<T> {
}; };
cached_cursor.clone().into() cached_cursor.clone().into()
} }
#[cfg(feature = "custom_cursor")]
CursorSource::Custom((cache_key, cursor)) => { CursorSource::Custom((cache_key, cursor)) => {
let custom_cursor = event_loop.create_custom_cursor(cursor); let custom_cursor = event_loop.create_custom_cursor(cursor);
cursor_cache.0.insert(cache_key, custom_cursor.clone()); cursor_cache.0.insert(cache_key, custom_cursor.clone());

View file

@ -1,12 +1,14 @@
//! Illustrates how to change window settings and shows how to affect //! Illustrates how to change window settings and shows how to affect
//! the mouse pointer in various ways. //! the mouse pointer in various ways.
#[cfg(feature = "custom_cursor")]
use bevy::winit::cursor::CustomCursor;
use bevy::{ use bevy::{
core::FrameCount, core::FrameCount,
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*, prelude::*,
window::{CursorGrabMode, PresentMode, SystemCursorIcon, WindowLevel, WindowTheme}, window::{CursorGrabMode, PresentMode, SystemCursorIcon, WindowLevel, WindowTheme},
winit::cursor::{CursorIcon, CustomCursor}, winit::cursor::CursorIcon,
}; };
fn main() { fn main() {
@ -152,12 +154,16 @@ fn toggle_theme(mut window: Single<&mut Window>, input: Res<ButtonInput<KeyCode>
#[derive(Resource)] #[derive(Resource)]
struct CursorIcons(Vec<CursorIcon>); struct CursorIcons(Vec<CursorIcon>);
fn init_cursor_icons(mut commands: Commands, asset_server: Res<AssetServer>) { fn init_cursor_icons(
mut commands: Commands,
#[cfg(feature = "custom_cursor")] asset_server: Res<AssetServer>,
) {
commands.insert_resource(CursorIcons(vec![ commands.insert_resource(CursorIcons(vec![
SystemCursorIcon::Default.into(), SystemCursorIcon::Default.into(),
SystemCursorIcon::Pointer.into(), SystemCursorIcon::Pointer.into(),
SystemCursorIcon::Wait.into(), SystemCursorIcon::Wait.into(),
SystemCursorIcon::Text.into(), SystemCursorIcon::Text.into(),
#[cfg(feature = "custom_cursor")]
CustomCursor::Image { CustomCursor::Image {
handle: asset_server.load("branding/icon.png"), handle: asset_server.load("branding/icon.png"),
hotspot: (128, 128), hotspot: (128, 128),