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