bevy/crates/bevy_feathers/src/cursor.rs
Talin 65bddbd3e4
Bevy Feathers: an opinionated widget toolkit for building Bevy tooling (#19730)
# Objective

This PR introduces Bevy Feathers, an opinionated widget toolkit and
theming system intended for use by the Bevy Editor, World Inspector, and
other tools.

The `bevy_feathers` crate is incomplete and hidden behind an
experimental feature flag. The API is going to change significantly
before release.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-28 19:52:13 +00:00

71 lines
2.6 KiB
Rust

//! Provides a way to automatically set the mouse cursor based on hovered entity.
use bevy_app::{App, Plugin, PreUpdate};
use bevy_ecs::{
entity::Entity,
hierarchy::ChildOf,
resource::Resource,
schedule::IntoScheduleConfigs,
system::{Commands, Query, Res},
};
use bevy_picking::{hover::HoverMap, pointer::PointerId, PickingSystems};
use bevy_window::Window;
use bevy_winit::cursor::CursorIcon;
/// A component that specifies the cursor icon to be used when the mouse is not hovering over
/// any other entity. This is used to set the default cursor icon for the window.
#[derive(Resource, Debug, Clone, Default)]
pub struct DefaultCursorIcon(pub CursorIcon);
/// System which updates the window cursor icon whenever the mouse hovers over an entity with
/// a [`CursorIcon`] component. If no entity is hovered, the cursor icon is set to
/// the cursor in the [`DefaultCursorIcon`] resource.
pub(crate) fn update_cursor(
mut commands: Commands,
hover_map: Option<Res<HoverMap>>,
parent_query: Query<&ChildOf>,
cursor_query: Query<&CursorIcon>,
mut q_windows: Query<(Entity, &mut Window, Option<&CursorIcon>)>,
r_default_cursor: Res<DefaultCursorIcon>,
) {
let cursor = hover_map.and_then(|hover_map| match hover_map.get(&PointerId::Mouse) {
Some(hover_set) => hover_set.keys().find_map(|entity| {
cursor_query.get(*entity).ok().or_else(|| {
parent_query
.iter_ancestors(*entity)
.find_map(|e| cursor_query.get(e).ok())
})
}),
None => None,
});
let mut windows_to_change: Vec<Entity> = Vec::new();
for (entity, _window, prev_cursor) in q_windows.iter_mut() {
match (cursor, prev_cursor) {
(Some(cursor), Some(prev_cursor)) if cursor == prev_cursor => continue,
(None, None) => continue,
_ => {
windows_to_change.push(entity);
}
}
}
windows_to_change.iter().for_each(|entity| {
if let Some(cursor) = cursor {
commands.entity(*entity).insert(cursor.clone());
} else {
commands.entity(*entity).insert(r_default_cursor.0.clone());
}
});
}
/// Plugin that supports automatically changing the cursor based on the hovered entity.
pub struct CursorIconPlugin;
impl Plugin for CursorIconPlugin {
fn build(&self, app: &mut App) {
if app.world().get_resource::<DefaultCursorIcon>().is_none() {
app.init_resource::<DefaultCursorIcon>();
}
app.add_systems(PreUpdate, update_cursor.in_set(PickingSystems::Last));
}
}