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