Skip to main content

bevy_picking/
pointer.rs

1//! Types and systems for pointer inputs, such as position and buttons.
2//!
3//! The picking system is built around the concept of a 'Pointer', which is an
4//! abstract representation of a user input with a specific screen location. The cursor
5//! and touch input is provided under [`input`](`crate::input`), but you can also implement
6//! your own custom pointers by supplying a unique ID.
7//!
8//! The purpose of this module is primarily to provide a common interface that can be
9//! driven by lower-level input devices and consumed by higher-level interaction systems.
10
11use crate::backend::HitData;
12use bevy_camera::{Camera, NormalizedRenderTarget, RenderTarget};
13use bevy_ecs::{lifecycle::HookContext, prelude::*, world::DeferredWorld};
14use bevy_input::{mouse::MouseScrollUnit, touch::TouchPhase};
15use bevy_math::Vec2;
16use bevy_platform::collections::HashMap;
17use bevy_reflect::prelude::*;
18use bevy_window::PrimaryWindow;
19use core::{fmt::Debug, ops::Deref};
20use uuid::Uuid;
21
22/// Identifies a unique pointer entity. `Mouse` and `Touch` pointers are automatically spawned.
23///
24/// This component is needed because pointers can be spawned and despawned, but they need to have a
25/// stable ID that persists regardless of the Entity they are associated with.
26#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Component, Reflect)]
27#[component(immutable, on_insert, on_remove)]
28#[require(PointerLocation, PointerPressState, PointerInteraction)]
29#[reflect(Component, Default, Debug, Hash, PartialEq, Clone)]
30pub enum PointerId {
31    /// The mouse pointer.
32    #[default]
33    Mouse,
34    /// A touch input, usually numbered by window touch events from `winit`.
35    Touch(u64),
36    /// A custom, uniquely identified pointer. Useful for mocking inputs or implementing a software
37    /// controlled cursor.
38    #[reflect(ignore, clone)]
39    Custom(Uuid),
40}
41
42impl PointerId {
43    fn on_insert(mut world: DeferredWorld, context: HookContext) {
44        if let Some(id) = world.entity(context.entity).get::<PointerId>().copied() {
45            world
46                .resource_mut::<PointerMap>()
47                .inner
48                .insert(id, context.entity);
49        }
50    }
51
52    fn on_remove(mut world: DeferredWorld, context: HookContext) {
53        if let Some(id) = world.entity(context.entity).get::<PointerId>().copied() {
54            world.resource_mut::<PointerMap>().inner.remove(&id);
55        }
56    }
57
58    /// Returns true if the pointer is a touch input.
59    pub fn is_touch(&self) -> bool {
60        matches!(self, PointerId::Touch(_))
61    }
62    /// Returns true if the pointer is the mouse.
63    pub fn is_mouse(&self) -> bool {
64        matches!(self, PointerId::Mouse)
65    }
66    /// Returns true if the pointer is a custom input.
67    pub fn is_custom(&self) -> bool {
68        matches!(self, PointerId::Custom(_))
69    }
70    /// Returns the touch id if the pointer is a touch input.
71    pub fn get_touch_id(&self) -> Option<u64> {
72        if let PointerId::Touch(id) = self {
73            Some(*id)
74        } else {
75            None
76        }
77    }
78}
79
80/// Holds a list of entities this pointer is currently interacting with, sorted from nearest to
81/// farthest.
82#[derive(Debug, Default, Clone, Component, Reflect)]
83#[reflect(Component, Default, Debug, Clone)]
84pub struct PointerInteraction {
85    pub(crate) sorted_entities: Vec<(Entity, HitData)>,
86}
87
88impl PointerInteraction {
89    /// Returns the nearest hit entity and data about that intersection.
90    pub fn get_nearest_hit(&self) -> Option<&(Entity, HitData)> {
91        self.sorted_entities.first()
92    }
93}
94
95impl Deref for PointerInteraction {
96    type Target = Vec<(Entity, HitData)>;
97
98    fn deref(&self) -> &Self::Target {
99        &self.sorted_entities
100    }
101}
102
103/// A resource that maps each [`PointerId`] to their [`Entity`] for easy lookups.
104#[derive(Debug, Clone, Default, Resource, Reflect)]
105#[reflect(Debug, Clone, Default, Resource)]
106pub struct PointerMap {
107    inner: HashMap<PointerId, Entity>,
108}
109
110impl PointerMap {
111    /// Get the [`Entity`] of the supplied [`PointerId`].
112    pub fn get_entity(&self, pointer_id: PointerId) -> Option<Entity> {
113        self.inner.get(&pointer_id).copied()
114    }
115}
116
117/// Tracks the state of the pointer's buttons in response to [`PointerInput`] events.
118#[derive(Debug, Default, Clone, Component, Reflect, PartialEq, Eq)]
119#[reflect(Component, Default, Debug, PartialEq, Clone)]
120pub struct PointerPressState {
121    primary: bool,
122    secondary: bool,
123    middle: bool,
124}
125
126impl PointerPressState {
127    /// Returns true if the primary pointer button is pressed.
128    #[inline]
129    pub fn is_primary_pressed(&self) -> bool {
130        self.primary
131    }
132
133    /// Returns true if the secondary pointer button is pressed.
134    #[inline]
135    pub fn is_secondary_pressed(&self) -> bool {
136        self.secondary
137    }
138
139    /// Returns true if the middle (tertiary) pointer button is pressed.
140    #[inline]
141    pub fn is_middle_pressed(&self) -> bool {
142        self.middle
143    }
144
145    /// Returns true if any pointer button is pressed.
146    #[inline]
147    pub fn is_any_pressed(&self) -> bool {
148        self.primary || self.middle || self.secondary
149    }
150}
151
152/// The stage of the pointer button press event
153#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
154#[reflect(Clone, PartialEq)]
155pub enum PressDirection {
156    /// The pointer button was just pressed
157    Pressed,
158    /// The pointer button was just released
159    Released,
160}
161
162/// The button that was just pressed or released
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
164#[reflect(Clone, PartialEq)]
165pub enum PointerButton {
166    /// The primary pointer button
167    Primary,
168    /// The secondary pointer button
169    Secondary,
170    /// The tertiary pointer button
171    Middle,
172}
173
174impl PointerButton {
175    /// Iterator over all buttons that a pointer can have.
176    pub fn iter() -> impl Iterator<Item = PointerButton> {
177        [Self::Primary, Self::Secondary, Self::Middle].into_iter()
178    }
179}
180
181/// Component that tracks a pointer's current [`Location`].
182#[derive(Debug, Default, Clone, Component, Reflect, PartialEq)]
183#[reflect(Component, Default, Debug, PartialEq, Clone)]
184pub struct PointerLocation {
185    /// The [`Location`] of the pointer. Note that a location is both the target, and the position
186    /// on the target.
187    #[reflect(ignore, clone)]
188    pub location: Option<Location>,
189}
190
191impl PointerLocation {
192    ///Returns a [`PointerLocation`] associated with the given location
193    pub fn new(location: Location) -> Self {
194        Self {
195            location: Some(location),
196        }
197    }
198
199    /// Returns `Some(&`[`Location`]`)` if the pointer is active, or `None` if the pointer is
200    /// inactive.
201    pub fn location(&self) -> Option<&Location> {
202        self.location.as_ref()
203    }
204}
205
206/// The location of a pointer, including the current [`NormalizedRenderTarget`], and the x/y
207/// position of the pointer on this render target.
208///
209/// Note that:
210/// - a pointer can move freely between render targets
211/// - a pointer is not associated with a [`Camera`] because multiple cameras can target the same
212///   render target. It is up to picking backends to associate a Pointer's `Location` with a
213///   specific `Camera`, if any.
214#[derive(Debug, Clone, Reflect, PartialEq)]
215#[reflect(Debug, PartialEq, Clone)]
216pub struct Location {
217    /// The [`NormalizedRenderTarget`] associated with the pointer, usually a window.
218    pub target: NormalizedRenderTarget,
219    /// The position of the pointer in the `target`.
220    pub position: Vec2,
221}
222
223impl Location {
224    /// Returns `true` if this pointer's [`Location`] is within the [`Camera`]'s viewport.
225    ///
226    /// Note this returns `false` if the location and camera have different render targets.
227    #[inline]
228    pub fn is_in_viewport(
229        &self,
230        camera: &Camera,
231        render_target: &RenderTarget,
232        primary_window: &Query<Entity, With<PrimaryWindow>>,
233    ) -> bool {
234        if render_target
235            .normalize(Some(match primary_window.single() {
236                Ok(w) => w,
237                Err(_) => return false,
238            }))
239            .as_ref()
240            != Some(&self.target)
241        {
242            return false;
243        }
244
245        camera
246            .logical_viewport_rect()
247            .is_some_and(|rect| rect.contains(self.position))
248    }
249}
250
251/// Event sent to drive a pointer.
252#[derive(Debug, Clone, Copy, Reflect)]
253#[reflect(Clone)]
254pub enum PointerAction {
255    /// Causes the pointer to press a button.
256    Press(PointerButton),
257    /// Causes the pointer to release a button.
258    Release(PointerButton),
259    /// Move the pointer.
260    Move {
261        /// How much the pointer moved from the previous position.
262        delta: Vec2,
263    },
264    /// Scroll the pointer
265    Scroll {
266        /// The mouse scroll unit.
267        unit: MouseScrollUnit,
268        /// The horizontal scroll value.
269        x: f32,
270        /// The vertical scroll value.
271        y: f32,
272        /// Touch phase of the input.
273        ///
274        /// When using a mouse, this will always be [`TouchPhase::Moved`].
275        phase: TouchPhase,
276    },
277    /// Cancel the pointer. Often used for touch events.
278    Cancel,
279}
280
281/// An input event effecting a pointer.
282#[derive(Message, Debug, Clone, Reflect)]
283#[reflect(Clone)]
284pub struct PointerInput {
285    /// The id of the pointer.
286    pub pointer_id: PointerId,
287    /// The location of the pointer. For [`PointerAction::Move`], this is the location after the movement.
288    pub location: Location,
289    /// The action that the event describes.
290    pub action: PointerAction,
291}
292
293impl PointerInput {
294    /// Creates a new pointer input event.
295    ///
296    /// Note that `location` refers to the position of the pointer *after* the event occurred.
297    pub fn new(pointer_id: PointerId, location: Location, action: PointerAction) -> PointerInput {
298        PointerInput {
299            pointer_id,
300            location,
301            action,
302        }
303    }
304
305    /// Returns true if the `target_button` of this pointer was just pressed.
306    #[inline]
307    pub fn button_just_pressed(&self, target_button: PointerButton) -> bool {
308        if let PointerAction::Press(button) = self.action {
309            button == target_button
310        } else {
311            false
312        }
313    }
314
315    /// Returns true if the `target_button` of this pointer was just released.
316    #[inline]
317    pub fn button_just_released(&self, target_button: PointerButton) -> bool {
318        if let PointerAction::Release(button) = self.action {
319            button == target_button
320        } else {
321            false
322        }
323    }
324
325    /// Updates pointer entities according to the input events.
326    pub fn receive(
327        mut events: MessageReader<PointerInput>,
328        mut pointers: Query<(&PointerId, &mut PointerLocation, &mut PointerPressState)>,
329    ) {
330        for event in events.read() {
331            match event.action {
332                PointerAction::Press(button) => {
333                    pointers
334                        .iter_mut()
335                        .for_each(|(pointer_id, _, mut pointer)| {
336                            if *pointer_id == event.pointer_id {
337                                match button {
338                                    PointerButton::Primary => pointer.primary = true,
339                                    PointerButton::Secondary => pointer.secondary = true,
340                                    PointerButton::Middle => pointer.middle = true,
341                                }
342                            }
343                        });
344                }
345                PointerAction::Release(button) => {
346                    pointers
347                        .iter_mut()
348                        .for_each(|(pointer_id, _, mut pointer)| {
349                            if *pointer_id == event.pointer_id {
350                                match button {
351                                    PointerButton::Primary => pointer.primary = false,
352                                    PointerButton::Secondary => pointer.secondary = false,
353                                    PointerButton::Middle => pointer.middle = false,
354                                }
355                            }
356                        });
357                }
358                PointerAction::Move { .. } => {
359                    pointers.iter_mut().for_each(|(id, mut pointer, _)| {
360                        if *id == event.pointer_id {
361                            pointer.location = Some(event.location.to_owned());
362                        }
363                    });
364                }
365                _ => {}
366            }
367        }
368    }
369}