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