Skip to main content

bevy_picking/
events.rs

1//! This module defines a stateful set of interaction events driven by the `PointerInput` stream
2//! and the hover state of each Pointer.
3//!
4//! # Usage
5//!
6//! To receive events from this module, you must use an [`Observer`] or [`MessageReader`] with [`PointerEvent`]s.
7//! The simplest example, registering a callback when an entity is hovered over by a pointer, looks like this:
8//!
9//! ```rust
10//! # use bevy_ecs::prelude::*;
11//! # use bevy_picking::prelude::*;
12//! # let mut world = World::default();
13//! world.spawn_empty()
14//!     .observe(|event: On<PointerOver>| {
15//!         println!("I am being hovered over");
16//!     });
17//! ```
18//!
19//! Observers give us three important properties:
20//! 1. They allow for attaching event handlers to specific entities,
21//! 2. they allow events to bubble up the entity hierarchy,
22//! 3. and they allow events of different types to be called in a specific order.
23//!
24//! The order in which interaction events are received is extremely important, and you can read more
25//! about it on the docs for the dispatcher system: [`pointer_events`]. This system runs in
26//! [`PreUpdate`](bevy_app::PreUpdate) in [`PickingSystems::Hover`](crate::PickingSystems::Hover). All pointer-event
27//! observers resolve during the sync point between [`pointer_events`] and
28//! [`update_interactions`](crate::hover::update_interactions).
29//!
30//! # Events Types
31//!
32//! The events this module defines fall into a few broad categories:
33//! + Hovering and movement: [`PointerOver`], [`PointerEnter`], [`PointerMove`], [`PointerLeave`], and [`PointerOut`].
34//! + Clicking and pressing: [`PointerPress`], [`PointerRelease`], and [`PointerClick`].
35//! + Dragging and dropping: [`PointerDragStart`], [`PointerDrag`], [`PointerDragEnd`], [`PointerDragEnter`], [`PointerDragOver`], [`PointerDragDrop`], [`PointerDragLeave`].
36//!
37//! These events each have a [`Pointer`] field, which contains information about the pointer.
38
39use core::{fmt::Debug, time::Duration};
40use std::collections::HashSet;
41
42use bevy_camera::NormalizedRenderTarget;
43use bevy_derive::{Deref, DerefMut};
44use bevy_ecs::{
45    entity::{EntityHashMap, EntityHashSet},
46    prelude::*,
47    query::QueryData,
48    system::SystemParam,
49    traversal::Traversal,
50};
51use bevy_input::{
52    mouse::{MouseScrollPixelsPerLine, MouseScrollUnit},
53    touch::TouchPhase,
54};
55use bevy_math::Vec2;
56use bevy_platform::collections::HashMap;
57use bevy_platform::time::Instant;
58use bevy_reflect::prelude::*;
59use bevy_window::Window;
60use tracing::debug;
61
62use crate::{
63    backend::{prelude::PointerLocation, HitData},
64    hover::{get_hovered_entities, is_directly_hovered, HoverMap, PreviousHoverMap},
65    pointer::{Location, PointerAction, PointerButton, PointerId, PointerInput, PointerMap},
66    PickingSettings,
67};
68///
69/// Stores the common data needed for all pointer events.
70///
71/// The documentation for the [`pointer_events`] explains the events this module exposes and
72/// the order in which they fire.
73#[derive(Clone, PartialEq, Debug, Reflect)]
74pub struct Pointer {
75    /// The pointer that triggered this event
76    pub id: PointerId,
77    /// The [`NormalizedRenderTarget`] associated with the pointer, usually a window.
78    pub target: NormalizedRenderTarget,
79    /// The position of the pointer in the `target`.
80    pub position: Vec2,
81    /// Whether to propagate the event via `PointerTraversal`
82    /// For [`PointerEnter`] and [`PointerLeave`] events, this is set to false.
83    pub(crate) propagate: bool,
84}
85
86impl Pointer {
87    /// Creates a new [`Pointer`].
88    pub fn new(id: PointerId, location: Location) -> Self {
89        Self {
90            id,
91            target: location.target,
92            position: location.position,
93            propagate: true,
94        }
95    }
96
97    pub(crate) fn without_propagate(&self) -> Self {
98        Self {
99            id: self.id,
100            target: self.target.clone(),
101            position: self.position,
102            propagate: false,
103        }
104    }
105
106    /// Returns the [`Location`] of this pointer.
107    pub fn location(&self) -> Location {
108        Location {
109            position: self.position,
110            target: self.target.clone(),
111        }
112    }
113}
114
115/// A traversal query (i.e. it implements [`Traversal`]) intended for use with [`Pointer`] events.
116///
117/// Unless shortcircuited out by the [`Pointer`] event itself, this will always traverse to the
118/// parent if the entity being visited has one. Otherwise, it propagates to the pointer's
119/// window and stops there.
120#[derive(QueryData)]
121pub struct PointerTraversal {
122    child_of: Option<&'static ChildOf>,
123    window: Option<&'static Window>,
124}
125
126/// An [`EntityEvent`] that contains a [`Pointer`].
127pub trait PointerEvent: EntityEvent {
128    /// Returns the [`Pointer`] stored on this [`EntityEvent`].
129    fn pointer(&self) -> &Pointer;
130}
131
132impl<E> Traversal<E> for PointerTraversal
133where
134    E: PointerEvent,
135{
136    fn traverse(item: Self::Item<'_, '_>, event: &E) -> Option<Entity> {
137        if !event.pointer().propagate {
138            return None;
139        }
140
141        let PointerTraversalItem { child_of, window } = item;
142
143        // Send event to parent, if it has one.
144        if let Some(child_of) = child_of {
145            return Some(child_of.parent());
146        };
147
148        // Otherwise, send it to the window entity (unless this is a window entity).
149        if window.is_none()
150            && let NormalizedRenderTarget::Window(window_ref) = event.pointer().target
151        {
152            return Some(window_ref.entity());
153        }
154
155        None
156    }
157}
158
159macro_rules! impl_pointer_event {
160    ($event:ident) => {
161        impl PointerEvent for $event {
162            fn pointer(&self) -> &Pointer {
163                &self.pointer
164            }
165        }
166    };
167}
168
169/// Fires when a pointer is canceled, and its current interaction state is dropped.
170#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
171#[entity_event(propagate = PointerTraversal, auto_propagate)]
172#[reflect(Clone, PartialEq)]
173pub struct PointerCancel {
174    /// The entity this pointer event happened for.
175    pub entity: Entity,
176    /// The pointer that triggered this event
177    pub pointer: Pointer,
178    /// Information about the picking intersection.
179    pub hit: HitData,
180}
181
182impl_pointer_event!(PointerCancel);
183
184/// Fires when a pointer crosses into the bounds of a [target entity](EntityEvent::event_target).
185/// Unlike [`PointerEnter`], this event bubbles up to all of the
186/// [target entity's](EntityEvent::event_target) ancestors (traversed via the [`ChildOf`] relationship)
187/// without restriction. Refer to [`pointer_events`] for more information on how these events are triggered.
188/// Refer to [`PointerTraversal`] for how [`Pointer`] events are propagated.
189#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
190#[entity_event(propagate = PointerTraversal, auto_propagate)]
191#[reflect(Clone, PartialEq)]
192pub struct PointerOver {
193    /// The entity this pointer event happened for.
194    pub entity: Entity,
195    /// The pointer that triggered this event
196    pub pointer: Pointer,
197    /// Information about the picking intersection.
198    pub hit: HitData,
199}
200
201impl_pointer_event!(PointerOver);
202
203/// Fires when a pointer crosses into the bounds of a [target entity](EntityEvent::event_target).
204/// Unlike [`PointerOver`], this event bubbles up through a subset of the
205/// [target entity's](EntityEvent::event_target) ancestors
206/// (traversed via the [`ChildOf`] relationship).
207///
208/// ### Event Propagation
209/// An ancestor of a [target entity](EntityEvent::event_target) will receive an [`PointerEnter`] event
210/// when the ancestor does not have a direct relation to any entity hovered by the
211/// pointer in the previous frame. For example, for a given pointer:
212///
213/// If the previously hovered entity C has the following entity ancestry: A -> B -> C
214///
215/// And the currently hovered entity E has the following entity ancestry: A -> D -> E
216///
217/// [`PointerEnter`] events would be sent for both E and its direct ancestor D.
218/// An [`PointerEnter`] event would not be sent for A because it is a shared ancestor of both C and E.
219///
220/// Note: An [`PointerEnter`] event may be fired for an ancestor even if the pointer does not enter
221/// within the ancestor's bounds. More concretely, if a child's bounds extend beyond the parent's
222/// and the pointer enters the child's bounds without crossing into the parent's,
223/// two [`PointerEnter`] events are still emitted for both the child and the parent.
224/// This matches the triggering behavior of `mouseenter` events on the web.
225/// To find out whether a pointer is within the target entity's bounds
226/// immediately upon entering, check the value of [`is_in_bounds`](PointerEnter::is_in_bounds).
227///
228/// Refer to [`pointer_events`] for more information on how these events are triggered.
229#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
230#[entity_event(propagate = PointerTraversal, auto_propagate)]
231#[reflect(Clone, PartialEq)]
232pub struct PointerEnter {
233    /// The entity this pointer event happened for.
234    pub entity: Entity,
235    /// The pointer that triggered this event
236    pub pointer: Pointer,
237    /// Information about the picking intersection.
238    pub hit: HitData,
239    /// Whether this pointer directly entered into the target entity's bounds at the
240    /// time of the event.
241    /// This may be false if this entity's child's bounds extended beyond the entity and
242    /// the pointer entered within the child's bounds only.
243    pub is_in_bounds: bool,
244}
245
246impl_pointer_event!(PointerEnter);
247
248/// Fires when a pointer crosses out of the bounds of a [target entity](EntityEvent::event_target).
249/// Unlike [`PointerLeave`], this event bubbles up to all of the
250/// [target entity's](EntityEvent::event_target) ancestors (traversed via the [`ChildOf`] relationship)
251/// without restriction. Refer to [`pointer_events`] for more information on how these events are triggered.
252/// Refer to [`PointerTraversal`] for how [`Pointer`] events are propagated.
253#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
254#[entity_event(propagate = PointerTraversal, auto_propagate)]
255#[reflect(Clone, PartialEq)]
256pub struct PointerOut {
257    /// The entity this pointer event happened for.
258    pub entity: Entity,
259    /// The pointer that triggered this event
260    pub pointer: Pointer,
261    /// Information about the latest prior picking intersection.
262    pub hit: HitData,
263}
264
265impl_pointer_event!(PointerOut);
266
267/// Fires when a pointer crosses out of the bounds of a [target entity](EntityEvent::event_target).
268/// Unlike [`PointerOut`], this event bubbles up through a subset of the
269/// [target entity's](EntityEvent::event_target) ancestors
270/// (traversed via the [`ChildOf`] relationship).
271///
272/// ### Event Propagation
273/// An ancestor of a [target entity](EntityEvent::event_target) will receive a [`PointerLeave`] event
274/// when the ancestor does not have a direct relation to any entity hovered by the
275/// pointer in the current frame. For example, for a given pointer:
276///
277/// If the previously hovered entity C has the following entity ancestry: A -> B -> C
278///
279/// And the currently hovered entity E has the following entity ancestry: A -> D -> E
280///
281/// [`PointerLeave`] events would be sent for both C and its direct ancestor B.
282/// A [`PointerLeave`] event would not be sent for A because it is a shared ancestor of both C and E.
283///
284/// Note: A [`PointerLeave`] event may be fired for an ancestor even if the pointer does not leave
285/// the ancestor's bounds. More concretely, if a child's bounds extend beyond the parent's
286/// and the pointer leaves from within those extended bounds,
287/// two [`PointerLeave`] events are still emitted for both the child and the parent.
288/// This matches the triggering behavior of `mouseleave` events on the web.
289/// To find out whether the pointer was within the target entity's bounds
290/// right before leaving, check the value of [`was_in_bounds`](PointerLeave::was_in_bounds).
291///
292/// Refer to [`pointer_events`] for more information on how these events are triggered.
293#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
294#[entity_event(propagate = PointerTraversal, auto_propagate)]
295#[reflect(Clone, PartialEq)]
296pub struct PointerLeave {
297    /// The entity this pointer event happened for.
298    pub entity: Entity,
299    /// The pointer that triggered this event
300    pub pointer: Pointer,
301    /// Information about the latest prior picking intersection.
302    pub hit: HitData,
303    /// Whether this pointer directly exited out of the target entity's bounds
304    /// at the time of the event.
305    /// This may be false if this entity's child's bounds extended beyond the entity and
306    /// the pointer exited out of the child's bounds only.
307    pub was_in_bounds: bool,
308}
309
310impl_pointer_event!(PointerLeave);
311
312/// Fires when a pointer button is pressed over the [target entity](EntityEvent::event_target).
313#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
314#[entity_event(propagate = PointerTraversal, auto_propagate)]
315#[reflect(Clone, PartialEq)]
316pub struct PointerPress {
317    /// The entity this pointer event happened for.
318    pub entity: Entity,
319    /// The pointer that triggered this event
320    pub pointer: Pointer,
321    /// Pointer button pressed to trigger this event.
322    pub button: PointerButton,
323    /// Information about the picking intersection.
324    pub hit: HitData,
325    /// Number of consecutive presses, starting at `1`.
326    pub count: u8,
327}
328
329impl_pointer_event!(PointerPress);
330
331/// Fires when a pointer button is released over the [target entity](EntityEvent::event_target).
332#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
333#[entity_event(propagate = PointerTraversal, auto_propagate)]
334#[reflect(Clone, PartialEq)]
335pub struct PointerRelease {
336    /// The entity this pointer event happened for.
337    pub entity: Entity,
338    /// The pointer that triggered this event
339    pub pointer: Pointer,
340    /// Pointer button lifted to trigger this event.
341    pub button: PointerButton,
342    /// Information about the picking intersection.
343    pub hit: HitData,
344}
345
346impl_pointer_event!(PointerRelease);
347
348/// Fires when a pointer sends a pointer pressed event followed by a pointer released event, with the same
349/// [target entity](EntityEvent::event_target) for both events.
350#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
351#[entity_event(propagate = PointerTraversal, auto_propagate)]
352#[reflect(Clone, PartialEq)]
353pub struct PointerClick {
354    /// The entity this pointer event happened for.
355    pub entity: Entity,
356    /// The pointer that triggered this event
357    pub pointer: Pointer,
358    /// Pointer button pressed and lifted to trigger this event.
359    pub button: PointerButton,
360    /// Information about the picking intersection.
361    pub hit: HitData,
362    /// Duration between the pointer pressed and lifted for this click
363    pub duration: Duration,
364    /// Number of consecutive clicks, starting at `1`.
365    pub count: u8,
366}
367
368impl_pointer_event!(PointerClick);
369
370/// Fires while a pointer is moving over the [target entity](EntityEvent::event_target).
371#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
372#[entity_event(propagate = PointerTraversal, auto_propagate)]
373#[reflect(Clone, PartialEq)]
374pub struct PointerMove {
375    /// The entity this pointer event happened for.
376    pub entity: Entity,
377    /// The pointer that triggered this event
378    pub pointer: Pointer,
379    /// Information about the picking intersection.
380    pub hit: HitData,
381    /// The change in position since the last move event.
382    ///
383    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
384    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
385    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
386    /// world-space.
387    pub delta: Vec2,
388}
389
390impl_pointer_event!(PointerMove);
391
392/// Fires when the [target entity](EntityEvent::event_target) receives a pointer pressed event followed by a pointer move event.
393#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
394#[entity_event(propagate = PointerTraversal, auto_propagate)]
395#[reflect(Clone, PartialEq)]
396pub struct PointerDragStart {
397    /// The entity this pointer event happened for.
398    pub entity: Entity,
399    /// The pointer that triggered this event
400    pub pointer: Pointer,
401    /// Pointer button pressed and moved to trigger this event.
402    pub button: PointerButton,
403    /// Information about the picking intersection.
404    pub hit: HitData,
405}
406
407impl_pointer_event!(PointerDragStart);
408
409/// Fires while the [target entity](EntityEvent::event_target) is being dragged.
410#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
411#[entity_event(propagate = PointerTraversal, auto_propagate)]
412#[reflect(Clone, PartialEq)]
413pub struct PointerDrag {
414    /// The entity this pointer event happened for.
415    pub entity: Entity,
416    /// The pointer that triggered this event
417    pub pointer: Pointer,
418    /// Pointer button pressed and moved to trigger this event.
419    pub button: PointerButton,
420    /// The total distance vector of a drag, measured from drag start to the current position.
421    ///
422    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
423    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
424    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
425    /// world-space.
426    pub distance: Vec2,
427    /// The change in position since the last drag event.
428    ///
429    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
430    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
431    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
432    /// world-space.
433    pub delta: Vec2,
434}
435
436impl_pointer_event!(PointerDrag);
437
438/// Fires when a pointer is dragging the [target entity](EntityEvent::event_target) and a pointer released event is received.
439#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
440#[entity_event(propagate = PointerTraversal, auto_propagate)]
441#[reflect(Clone, PartialEq)]
442pub struct PointerDragEnd {
443    /// The entity this pointer event happened for.
444    pub entity: Entity,
445    /// The pointer that triggered this event
446    pub pointer: Pointer,
447    /// Pointer button pressed, moved, and released to trigger this event.
448    pub button: PointerButton,
449    /// The vector of drag movement measured from start to final pointer position.
450    ///
451    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
452    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
453    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
454    /// world-space.
455    pub distance: Vec2,
456}
457
458impl_pointer_event!(PointerDragEnd);
459
460/// Fires when a pointer dragging the `dragged` entity enters the [target entity](EntityEvent::event_target)
461#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
462#[entity_event(propagate = PointerTraversal, auto_propagate)]
463#[reflect(Clone, PartialEq)]
464pub struct PointerDragEnter {
465    /// The entity this pointer event happened for.
466    pub entity: Entity,
467    /// The pointer that triggered this event
468    pub pointer: Pointer,
469    /// Pointer button pressed to enter drag.
470    pub button: PointerButton,
471    /// The entity that was being dragged when the pointer entered the [target entity](EntityEvent::event_target).
472    pub dragged: Entity,
473    /// Information about the picking intersection.
474    pub hit: HitData,
475}
476
477impl_pointer_event!(PointerDragEnter);
478
479/// Fires while the `dragged` entity is being dragged over the [target entity](EntityEvent::event_target).
480#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
481#[entity_event(propagate = PointerTraversal, auto_propagate)]
482#[reflect(Clone, PartialEq)]
483pub struct PointerDragOver {
484    /// The entity this pointer event happened for.
485    pub entity: Entity,
486    /// The pointer that triggered this event
487    pub pointer: Pointer,
488    /// Pointer button pressed while dragging over.
489    pub button: PointerButton,
490    /// The entity that was being dragged when the pointer was over the [target entity](EntityEvent::event_target).
491    pub dragged: Entity,
492    /// Information about the picking intersection.
493    pub hit: HitData,
494}
495
496impl_pointer_event!(PointerDragOver);
497
498/// Fires when a pointer dragging the `dragged` entity leaves the [target entity](EntityEvent::event_target).
499#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
500#[entity_event(propagate = PointerTraversal, auto_propagate)]
501#[reflect(Clone, PartialEq)]
502pub struct PointerDragLeave {
503    /// The entity this pointer event happened for.
504    pub entity: Entity,
505    /// The pointer that triggered this event
506    pub pointer: Pointer,
507    /// Pointer button pressed while leaving drag.
508    pub button: PointerButton,
509    /// The entity that was being dragged when the pointer left the [target entity](EntityEvent::event_target).
510    pub dragged: Entity,
511    /// Information about the latest prior picking intersection.
512    pub hit: HitData,
513}
514
515impl_pointer_event!(PointerDragLeave);
516
517/// Fires when a pointer drops the `dropped` entity onto the [target entity](EntityEvent::event_target).
518#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
519#[entity_event(propagate = PointerTraversal, auto_propagate)]
520#[reflect(Clone, PartialEq)]
521pub struct PointerDragDrop {
522    /// The entity this pointer event happened for.
523    pub entity: Entity,
524    /// The pointer that triggered this event
525    pub pointer: Pointer,
526    /// Pointer button released to drop.
527    pub button: PointerButton,
528    /// The entity that was dropped onto the [target entity](EntityEvent::event_target).
529    pub dropped: Entity,
530    /// Information about the picking intersection.
531    pub hit: HitData,
532}
533
534impl_pointer_event!(PointerDragDrop);
535
536/// Fires while a pointer is scrolling over the [target entity](EntityEvent::event_target).
537#[derive(EntityEvent, Message, Clone, PartialEq, Debug, Reflect)]
538#[entity_event(propagate = PointerTraversal, auto_propagate)]
539#[reflect(Clone, PartialEq)]
540pub struct PointerScroll {
541    /// The entity this pointer event happened for.
542    pub entity: Entity,
543    /// The pointer that triggered this event
544    pub pointer: Pointer,
545    /// The mouse scroll unit.
546    pub unit: MouseScrollUnit,
547    /// The horizontal scroll value.
548    pub x: f32,
549    /// The vertical scroll value.
550    pub y: f32,
551    /// Information about the picking intersection.
552    pub hit: HitData,
553    /// Touch phase of the input.
554    ///
555    /// When using a mouse, this will always be [`TouchPhase::Moved`].
556    pub phase: TouchPhase,
557}
558
559impl_pointer_event!(PointerScroll);
560
561impl PointerScroll {
562    /// Converts the units to [`MouseScrollUnit::Line`]
563    pub fn to_lines(&self, conversion_ratio: &MouseScrollPixelsPerLine) -> Self {
564        if self.unit == MouseScrollUnit::Pixel {
565            PointerScroll {
566                entity: self.entity,
567                pointer: self.pointer.clone(),
568                unit: MouseScrollUnit::Line,
569                x: self.x / *conversion_ratio,
570                y: self.y / *conversion_ratio,
571                hit: self.hit.clone(),
572                phase: self.phase,
573            }
574        } else {
575            self.clone()
576        }
577    }
578    /// Converts the units to [`MouseScrollUnit::Pixel`]
579    pub fn to_pixels(&self, conversion_ratio: &MouseScrollPixelsPerLine) -> Self {
580        if self.unit == MouseScrollUnit::Line {
581            PointerScroll {
582                entity: self.entity,
583                pointer: self.pointer.clone(),
584                unit: MouseScrollUnit::Pixel,
585                x: self.x * *conversion_ratio,
586                y: self.y * *conversion_ratio,
587                hit: self.hit.clone(),
588                phase: self.phase,
589            }
590        } else {
591            self.clone()
592        }
593    }
594}
595
596/// Dragging state.
597#[derive(Clone, PartialEq, Debug, Reflect)]
598#[reflect(Clone, PartialEq)]
599pub struct DragEntry {
600    /// The position of the pointer at drag start.
601    ///
602    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
603    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
604    /// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or
605    /// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to
606    /// convert from screen-space to world-space.
607    pub start_pos: Vec2,
608    /// The latest position of the pointer during this drag, used to compute deltas.
609    ///
610    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
611    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
612    /// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or
613    /// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to
614    /// convert from screen-space to world-space.
615    pub latest_pos: Vec2,
616}
617
618/// An entry in the cache that drives the `pointer_events` system, storing additional data
619/// about pointer button presses.
620#[derive(Debug, Clone, Default, Reflect)]
621#[reflect(Debug, Clone, Default)]
622pub struct PointerButtonState {
623    /// Stores the press location and start time for each button currently being pressed by the pointer.
624    pub pressing: EntityHashMap<(Location, Instant, HitData)>,
625    /// Stores the latest click time and count for each clicked entity.
626    pub clicking: EntityHashMap<(Instant, u8)>,
627    /// Stores the starting and current locations for each entity currently being dragged by the pointer.
628    pub dragging: EntityHashMap<DragEntry>,
629    /// Stores the hit data for each entity currently being dragged over by the pointer.
630    pub dragging_over: EntityHashMap<HitData>,
631}
632
633impl PointerButtonState {
634    /// Clears all press and drag data tracked for this button on its pointer.
635    pub fn clear(&mut self) {
636        self.pressing.clear();
637        self.dragging.clear();
638        self.dragging_over.clear();
639    }
640}
641
642/// A cache map containing the ancestry of hovered entities
643#[derive(Debug, Clone, Default, Deref, DerefMut, Reflect)]
644#[reflect(Debug, Clone, Default)]
645pub struct HoveredEntityAncestors(EntityHashMap<EntityHashSet>);
646
647impl HoveredEntityAncestors {
648    /// Clears self and rebuilds a map of every hovered entity to its ancestors.
649    ///
650    /// This map is used to calculate which entities should receive [`PointerEnter`] or [`PointerLeave`] events.
651    pub fn rebuild(
652        &mut self,
653        hover_map: &HoverMap,
654        pointer_state: &PointerState,
655        ancestors_query: &Query<&ChildOf>,
656    ) {
657        self.clear();
658        for hovered_entity in hover_map
659            .iter()
660            .flat_map(|(_, hashmap)| hashmap.iter().map(|data| *data.0))
661        {
662            // If the ancestors were already added into the map, do not re-fetch
663            if self.contains_key(&hovered_entity) {
664                continue;
665            }
666            // If the ancestors were previously fetched, just re-use the entry.
667            if let Some(previous_entry) =
668                pointer_state.hovered_entity_ancestors.get(&hovered_entity)
669            {
670                self.insert(hovered_entity, previous_entry.clone());
671            } else {
672                let mut ancestors = EntityHashSet::new();
673                for member in ancestors_query.iter_ancestors(hovered_entity) {
674                    ancestors.insert(member);
675                }
676                self.insert(hovered_entity, ancestors);
677            }
678        }
679    }
680
681    /// Returns a new combined `HashSet` of ancestors for the provided `hover_entities`
682    pub fn get_ancestors_union(&self, hover_entities: &EntityHashSet) -> EntityHashSet {
683        hover_entities
684            .iter()
685            .flat_map(|entity| self.get(entity))
686            .flat_map(|set| set.iter().copied())
687            .collect::<EntityHashSet>()
688    }
689
690    /// Returns the ancestors for the provided `hover_entity`, if it has been created
691    pub fn get_ancestors(&self, hover_entity: &Entity) -> Option<&EntityHashSet> {
692        self.get(hover_entity)
693    }
694}
695
696/// State for all pointers.
697#[derive(Debug, Clone, Default, Resource, Reflect)]
698#[reflect(Debug, Clone, Default, Resource)]
699pub struct PointerState {
700    /// Pressing and dragging state, organized by pointer and button.
701    pub pointer_buttons: HashMap<(PointerId, PointerButton), PointerButtonState>,
702    /// A cache map providing the set of an entity's ancestors for a given hovered entity.
703    pub hovered_entity_ancestors: HoveredEntityAncestors,
704}
705
706impl PointerState {
707    /// Retrieves the current state for a specific pointer and button, if it has been created.
708    pub fn get(&self, pointer_id: PointerId, button: PointerButton) -> Option<&PointerButtonState> {
709        self.pointer_buttons.get(&(pointer_id, button))
710    }
711
712    /// Provides write access to the state of a pointer and button, creating it if it does not yet exist.
713    pub fn get_mut(
714        &mut self,
715        pointer_id: PointerId,
716        button: PointerButton,
717    ) -> &mut PointerButtonState {
718        self.pointer_buttons
719            .entry((pointer_id, button))
720            .or_default()
721    }
722
723    /// Retrieves the ancestors for a given hovered entity
724    pub fn get_ancestors(&self, hovered_entity: &Entity) -> Option<&EntityHashSet> {
725        self.hovered_entity_ancestors.get_ancestors(hovered_entity)
726    }
727
728    /// Retrieves the union of ancestors for the given hovered entities
729    pub fn get_ancestors_union(&self, hovered_entities: &EntityHashSet) -> EntityHashSet {
730        self.hovered_entity_ancestors
731            .get_ancestors_union(hovered_entities)
732    }
733
734    /// Clears all the data associated with all of the buttons on a pointer. Does not free the underlying memory.
735    pub fn clear(&mut self, pointer_id: PointerId) {
736        for button in PointerButton::iter() {
737            if let Some(state) = self.pointer_buttons.get_mut(&(pointer_id, button)) {
738                state.clear();
739            }
740        }
741    }
742}
743
744/// A helper system param for accessing the picking event writers.
745#[derive(SystemParam)]
746pub struct PickingMessageWriters<'w> {
747    cancel_events: MessageWriter<'w, PointerCancel>,
748    click_events: MessageWriter<'w, PointerClick>,
749    pressed_events: MessageWriter<'w, PointerPress>,
750    drag_drop_events: MessageWriter<'w, PointerDragDrop>,
751    drag_end_events: MessageWriter<'w, PointerDragEnd>,
752    drag_enter_events: MessageWriter<'w, PointerDragEnter>,
753    drag_events: MessageWriter<'w, PointerDrag>,
754    drag_leave_events: MessageWriter<'w, PointerDragLeave>,
755    drag_over_events: MessageWriter<'w, PointerDragOver>,
756    drag_start_events: MessageWriter<'w, PointerDragStart>,
757    scroll_events: MessageWriter<'w, PointerScroll>,
758    move_events: MessageWriter<'w, PointerMove>,
759    out_events: MessageWriter<'w, PointerOut>,
760    over_events: MessageWriter<'w, PointerOver>,
761    leave_events: MessageWriter<'w, PointerLeave>,
762    enter_events: MessageWriter<'w, PointerEnter>,
763    released_events: MessageWriter<'w, PointerRelease>,
764}
765
766/// Dispatches interaction events to the target entities.
767///
768/// Within a single frame, events are dispatched in the following order:
769/// + [`PointerOut`] → [`PointerLeave`] → [`PointerDragLeave`].
770/// + [`PointerDragEnter`] → [`PointerEnter`] → [`PointerOver`].
771/// + Any number of any of the following:
772///   + For each movement: [`PointerDragStart`] → [`PointerDrag`] → [`PointerDragOver`] → [`PointerMove`].
773///   + For each button press: [`PointerPress`] or [`PointerClick`] → [`PointerRelease`] → [`PointerDragDrop`] → [`PointerDragEnd`] → [`PointerDragLeave`].
774///   + For each pointer cancellation: [`PointerCancel`].
775///
776/// Additionally, across multiple frames, the following are also strictly
777/// ordered by the interaction state machine:
778/// + When a pointer moves over the target:
779///   [`PointerOver`], [`PointerEnter`], [`PointerMove`], [`PointerLeave`], [`PointerOut`].
780/// + When a pointer presses buttons on the target:
781///   [`PointerPress`], [`PointerClick`], [`PointerRelease`].
782/// + When a pointer drags the target:
783///   [`PointerDragStart`], [`PointerDrag`], [`PointerDragEnd`].
784/// + When a pointer drags something over the target:
785///   [`PointerDragEnter`], [`PointerDragOver`], [`PointerDragDrop`], [`PointerDragLeave`].
786/// + When a pointer is canceled:
787///   No other events will follow the [`PointerCancel`] event for that pointer.
788///
789/// Four events -- [`PointerOver`], [`PointerEnter`], [`PointerLeave`] and [`PointerOut`] -- are driven only by the [`HoverMap`].
790/// The rest rely on additional data from the [`PointerInput`] event stream. To
791/// receive these events for a custom pointer, you must add [`PointerInput`]
792/// events.
793///
794/// When the pointer goes from hovering entity A to entity B, entity A will
795/// receive [`PointerOut`] and [`PointerEnter`] and then entity B will receive [`PointerLeave`] and [`PointerOver`].
796/// No entity will ever receive both an [`PointerOver`] and an [`PointerOut`] or
797/// an [`PointerEnter`] and a [`PointerLeave`] event during the same frame.
798///
799/// When we account for event bubbling, the two pairs of events,
800/// [`PointerOut`] [`PointerOver`] and [`PointerEnter`] [`PointerLeave`], behave differently. When the hovering focus shifts
801/// between children, parent entities may receive redundant [`PointerOut`] → [`PointerOver`] pairs. In
802/// the case of [`PointerEnter`] → [`PointerLeave`], shared parent entities will not receive [`PointerEnter`]
803/// or [`PointerLeave`].
804///
805/// Both [`PointerClick`] and [`PointerRelease`] target the entity hovered in the *previous frame*,
806/// rather than the current frame. This is because touch pointers hover nothing
807/// on the frame they are released. The end effect is that these two events can
808/// be received sequentially after an [`PointerOut`] event (but always on the same frame
809/// as the [`PointerOut`] event).
810///
811/// Note: Though it is common for the [`PointerInput`] stream may contain
812/// multiple pointer movements and presses each frame, the hover state is
813/// determined only by the pointer's *final position*. Since the hover state
814/// ultimately determines which entities receive events, this may mean that an
815/// entity can receive events from before or after it was actually hovered.
816pub fn pointer_events(
817    // Input
818    mut input_events: MessageReader<PointerInput>,
819    // ECS State
820    pointers: Query<&PointerLocation>,
821    ancestors_query: Query<&ChildOf>,
822    pointer_map: Res<PointerMap>,
823    hover_map: Res<HoverMap>,
824    previous_hover_map: Res<PreviousHoverMap>,
825    picking_settings: Res<PickingSettings>,
826    mut pointer_state: ResMut<PointerState>,
827    mut hovered_entity_ancestors: Local<HoveredEntityAncestors>,
828    mut sent_leave: Local<HashSet<(PointerId, Entity)>>,
829    mut sent_enter: Local<HashSet<(PointerId, Entity)>>,
830    // Output
831    mut commands: Commands,
832    mut message_writers: PickingMessageWriters,
833) {
834    // Setup utilities
835    let now = Instant::now();
836    let pointer_location = |pointer_id: PointerId| {
837        pointer_map
838            .get_entity(pointer_id)
839            .and_then(|entity| pointers.get(entity).ok())
840            .and_then(|pointer| pointer.location.clone())
841    };
842    hovered_entity_ancestors.rebuild(&hover_map, &pointer_state, &ancestors_query);
843    sent_leave.clear();
844    sent_enter.clear();
845
846    // If the entity was hovered by a specific pointer last frame...
847    for (pointer_id, hovered_entity, hit) in previous_hover_map
848        .iter()
849        .flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
850    {
851        // ...but is now not being hovered by that same pointer...
852        if !hover_map
853            .get(&pointer_id)
854            .iter()
855            .any(|e| e.contains_key(&hovered_entity))
856        {
857            let Some(location) = pointer_location(pointer_id) else {
858                debug!(
859                    "Unable to get location for pointer {:?} during pointer out",
860                    pointer_id
861                );
862                continue;
863            };
864
865            let pointer = Pointer::new(pointer_id, location);
866
867            // Always send Out events
868            let out_event = PointerOut {
869                pointer: pointer.clone(),
870                entity: hovered_entity,
871                hit: hit.clone(),
872            };
873            commands.trigger(out_event.clone());
874            message_writers.out_events.write(out_event);
875
876            // Potentially send `Leave` events to the entity and all of its ancestors
877            let mut entities_to_send_leave =
878                pointer_state.get_ancestors(&hovered_entity).map_or_else(
879                    || {
880                        ancestors_query
881                            .iter_ancestors(hovered_entity)
882                            .collect::<EntityHashSet>()
883                    },
884                    Clone::clone,
885                );
886            entities_to_send_leave.insert(hovered_entity);
887            // Ensure we do not double send to any other entities that have already been sent to during this loop
888            entities_to_send_leave.retain(|entity| !sent_leave.contains(&(pointer_id, *entity)));
889            if !entities_to_send_leave.is_empty() {
890                // Fetch the currently hovered entities and their ancestors
891                let new_hovered_entities = get_hovered_entities(&hover_map, &pointer_id);
892                let new_hovered_ancestors =
893                    hovered_entity_ancestors.get_ancestors_union(&new_hovered_entities);
894                let union = new_hovered_entities
895                    .union(&new_hovered_ancestors)
896                    .copied()
897                    .collect::<EntityHashSet>();
898                // Keep entities and ancestors that are not going to continue to be hovered over
899                entities_to_send_leave.retain(|entity| !union.contains(entity));
900                // Send `Leave` events for those entities.
901                // Note that `Leave` events send without propagation; we manually calculated
902                // which ancestors should receive one.
903                for leave_event in entities_to_send_leave.iter().map(|entity| PointerLeave {
904                    pointer: pointer.without_propagate(),
905                    hit: hit.clone(),
906                    was_in_bounds: is_directly_hovered(&previous_hover_map.0, &pointer_id, entity),
907                    entity: *entity,
908                }) {
909                    let entity = leave_event.entity;
910                    commands.trigger(leave_event.clone());
911                    message_writers.leave_events.write(leave_event);
912                    sent_leave.insert((pointer_id, entity));
913                }
914            }
915
916            // Possibly send DragLeave events
917            for button in PointerButton::iter() {
918                let state = pointer_state.get_mut(pointer_id, button);
919                state.dragging_over.remove(&hovered_entity);
920                for drag_target in state.dragging.keys() {
921                    let drag_leave_event = PointerDragLeave {
922                        pointer: pointer.clone(),
923                        entity: hovered_entity,
924                        button,
925                        dragged: *drag_target,
926                        hit: hit.clone(),
927                    };
928                    commands.trigger(drag_leave_event.clone());
929                    message_writers.drag_leave_events.write(drag_leave_event);
930                }
931            }
932        }
933    }
934
935    // Iterate all currently hovered entities for each pointer
936    for (pointer_id, hovered_entity, hit) in hover_map
937        .iter()
938        .flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
939    {
940        // Continue if the pointer does not have a valid location.
941        let Some(location) = pointer_location(pointer_id) else {
942            debug!(
943                "Unable to get location for pointer {:?} during pointer over",
944                pointer_id
945            );
946            continue;
947        };
948
949        let pointer = Pointer::new(pointer_id, location);
950
951        // For each button update its `dragging_over` state and possibly emit DragEnter events.
952        for button in PointerButton::iter() {
953            let state = pointer_state.get_mut(pointer_id, button);
954
955            // Only update the `dragging_over` state if there is at least one entity being dragged.
956            // Only emit DragEnter events for this `hovered_entity`, if it had no previous `dragging_over` state.
957            if !state.dragging.is_empty()
958                && state
959                    .dragging_over
960                    .insert(hovered_entity, hit.clone())
961                    .is_none()
962            {
963                for drag_target in state.dragging.keys() {
964                    let drag_enter_event = PointerDragEnter {
965                        pointer: pointer.clone(),
966                        entity: hovered_entity,
967                        button,
968                        dragged: *drag_target,
969                        hit: hit.clone(),
970                    };
971                    commands.trigger(drag_enter_event.clone());
972                    message_writers.drag_enter_events.write(drag_enter_event);
973                }
974            }
975        }
976
977        // If the `hovered_entity` was not hovered by the same pointer the previous frame...
978        if !previous_hover_map
979            .get(&pointer_id)
980            .iter()
981            .any(|e| e.contains_key(&hovered_entity))
982        {
983            // Potentially send `Enter` events to the entity and all of its ancestors
984            let mut entities_to_send_enter = hovered_entity_ancestors
985                .get_ancestors(&hovered_entity)
986                .map_or_else(
987                    || {
988                        ancestors_query
989                            .iter_ancestors(hovered_entity)
990                            .collect::<EntityHashSet>()
991                    },
992                    Clone::clone,
993                );
994            entities_to_send_enter.insert(hovered_entity);
995            // Ensure we do not double send to any other entities that have already been sent to during this loop
996            entities_to_send_enter
997                .retain(|entity: &Entity| !sent_enter.contains(&(pointer_id, *entity)));
998            if !entities_to_send_enter.is_empty() {
999                // Fetch the previously hovered entities and their ancestors
1000                let prev_hovered_entities = get_hovered_entities(&previous_hover_map, &pointer_id);
1001                let prev_hovered_ancestors =
1002                    pointer_state.get_ancestors_union(&prev_hovered_entities);
1003                let union = prev_hovered_entities
1004                    .union(&prev_hovered_ancestors)
1005                    .copied()
1006                    .collect::<EntityHashSet>();
1007                // Keep entities and ancestors that were not hovered over previously
1008                entities_to_send_enter.retain(|entity| !union.contains(entity));
1009                // Send `Enter` events for those entities.
1010                // Note that `Enter` events send without propagation; we manually calculated
1011                // which ancestors should receive one.
1012                for enter_event in entities_to_send_enter.iter().map(|entity| PointerEnter {
1013                    pointer: pointer.clone(),
1014                    entity: *entity,
1015                    hit: hit.clone(),
1016                    is_in_bounds: is_directly_hovered(&hover_map.0, &pointer_id, entity),
1017                }) {
1018                    let entity = enter_event.entity;
1019                    commands.trigger(enter_event.clone());
1020                    message_writers.enter_events.write(enter_event);
1021                    sent_enter.insert((pointer_id, entity));
1022                }
1023            }
1024
1025            // Always send Over events
1026            let over_event = PointerOver {
1027                pointer: pointer.clone(),
1028                entity: hovered_entity,
1029                hit: hit.clone(),
1030            };
1031            commands.trigger(over_event.clone());
1032            message_writers.over_events.write(over_event);
1033        }
1034    }
1035
1036    // Update pointer_state with the current hovered entity ancestors
1037    // We swap with the Local SystemParam's map, which will be rebuilt
1038    // on the next invocation of `pointer_events`
1039    core::mem::swap(
1040        &mut hovered_entity_ancestors.0,
1041        &mut pointer_state.hovered_entity_ancestors,
1042    );
1043
1044    // Dispatch input events...
1045    for PointerInput {
1046        pointer_id,
1047        location,
1048        action,
1049    } in input_events.read().cloned()
1050    {
1051        let pointer = Pointer::new(pointer_id, location.clone());
1052        match action {
1053            PointerAction::Press(button) => {
1054                let state = pointer_state.get_mut(pointer_id, button);
1055                state.clicking.retain(|_, (last_click, _)| {
1056                    now - *last_click <= picking_settings.multi_click_interval
1057                });
1058
1059                // If it's a press, emit a Pressed event and mark the hovered entities as pressed
1060                for (hovered_entity, hit) in hover_map
1061                    .get(&pointer_id)
1062                    .iter()
1063                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
1064                {
1065                    let count = state
1066                        .clicking
1067                        .get(&hovered_entity)
1068                        .map_or(1, |(_, count)| count.saturating_add(1));
1069                    state.clicking.insert(hovered_entity, (now, count));
1070                    let pressed_event = PointerPress {
1071                        pointer: pointer.clone(),
1072                        entity: hovered_entity,
1073                        button,
1074                        hit: hit.clone(),
1075                        count,
1076                    };
1077                    commands.trigger(pressed_event.clone());
1078                    message_writers.pressed_events.write(pressed_event);
1079                    // Also insert the press into the state
1080                    state
1081                        .pressing
1082                        .insert(hovered_entity, (location.clone(), now, hit));
1083                }
1084            }
1085            PointerAction::Release(button) => {
1086                let state = pointer_state.get_mut(pointer_id, button);
1087                state.clicking.retain(|_, (last_click, _)| {
1088                    now - *last_click <= picking_settings.multi_click_interval
1089                });
1090
1091                // Emit Click and Release events on all the previously hovered entities.
1092                for (hovered_entity, hit) in previous_hover_map
1093                    .get(&pointer_id)
1094                    .iter()
1095                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
1096                {
1097                    // If this pointer previously pressed the hovered entity, emit a Click event
1098                    if let Some((_, press_instant, _)) = state.pressing.get(&hovered_entity) {
1099                        let count = state
1100                            .clicking
1101                            .get(&hovered_entity)
1102                            .map_or(1, |(_, count)| *count);
1103                        state.clicking.insert(hovered_entity, (now, count));
1104                        let click_event = PointerClick {
1105                            pointer: pointer.clone(),
1106                            entity: hovered_entity,
1107                            button,
1108                            hit: hit.clone(),
1109                            duration: now - *press_instant,
1110                            count,
1111                        };
1112                        commands.trigger(click_event.clone());
1113                        message_writers.click_events.write(click_event);
1114                    }
1115                    // Always send the Release event
1116                    let released_event = PointerRelease {
1117                        pointer: pointer.clone(),
1118                        button,
1119                        hit: hit.clone(),
1120                        entity: hovered_entity,
1121                    };
1122                    commands.trigger(released_event.clone());
1123                    message_writers.released_events.write(released_event);
1124                }
1125
1126                // Then emit the drop events.
1127                for (drag_target, drag) in state.dragging.drain() {
1128                    // Emit DragDrop
1129                    for (dragged_over, hit) in state.dragging_over.iter() {
1130                        let drag_drop_event = PointerDragDrop {
1131                            pointer: pointer.clone(),
1132                            entity: *dragged_over,
1133                            button,
1134                            dropped: drag_target,
1135                            hit: hit.clone(),
1136                        };
1137                        commands.trigger(drag_drop_event.clone());
1138                        message_writers.drag_drop_events.write(drag_drop_event);
1139                    }
1140                    // Emit DragEnd
1141                    let drag_end_event = PointerDragEnd {
1142                        pointer: pointer.clone(),
1143                        entity: drag_target,
1144                        button,
1145                        distance: drag.latest_pos - drag.start_pos,
1146                    };
1147                    commands.trigger(drag_end_event.clone());
1148                    message_writers.drag_end_events.write(drag_end_event);
1149                    // Emit DragLeave
1150                    for (dragged_over, hit) in state.dragging_over.iter() {
1151                        let drag_leave_event = PointerDragLeave {
1152                            pointer: pointer.clone(),
1153                            entity: *dragged_over,
1154                            button,
1155                            dragged: drag_target,
1156                            hit: hit.clone(),
1157                        };
1158                        commands.trigger(drag_leave_event.clone());
1159                        message_writers.drag_leave_events.write(drag_leave_event);
1160                    }
1161                }
1162
1163                // Finally, we can clear the state of everything relating to presses or drags.
1164                state.clear();
1165            }
1166            // Moved
1167            PointerAction::Move { delta } => {
1168                if delta == Vec2::ZERO {
1169                    continue; // If delta is zero, the following events will not be triggered.
1170                }
1171                // Triggers during movement even if not over an entity
1172                for button in PointerButton::iter() {
1173                    let state = pointer_state.get_mut(pointer_id, button);
1174
1175                    // Emit DragEntry and DragStart the first time we move while pressing an entity
1176                    for (press_target, (location, _, hit)) in state.pressing.iter() {
1177                        if state.dragging.contains_key(press_target) {
1178                            continue; // This entity is already logged as being dragged
1179                        }
1180                        state.dragging.insert(
1181                            *press_target,
1182                            DragEntry {
1183                                start_pos: location.position,
1184                                latest_pos: location.position,
1185                            },
1186                        );
1187                        let drag_start_event = PointerDragStart {
1188                            pointer: pointer.clone(),
1189                            entity: *press_target,
1190                            button,
1191                            hit: hit.clone(),
1192                        };
1193
1194                        commands.trigger(drag_start_event.clone());
1195                        message_writers.drag_start_events.write(drag_start_event);
1196
1197                        // Insert dragging over state and emit DragEnter for hovered entities.
1198                        for (hovered_entity, hit) in hover_map
1199                            .get(&pointer_id)
1200                            .iter()
1201                            .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
1202                            .filter(|(hovered_entity, _)| *hovered_entity != *press_target)
1203                        {
1204                            // Inserting the `dragging_over` state here ensures the `DragEnter` event won't be dispatched twice.
1205                            state.dragging_over.insert(hovered_entity, hit.clone());
1206                            let drag_enter_event = PointerDragEnter {
1207                                pointer: pointer.clone(),
1208                                entity: hovered_entity,
1209                                button,
1210                                dragged: *press_target,
1211                                hit: hit.clone(),
1212                            };
1213                            commands.trigger(drag_enter_event.clone());
1214                            message_writers.drag_enter_events.write(drag_enter_event);
1215                        }
1216                    }
1217
1218                    // Emit Drag events to the entities we are dragging
1219                    for (drag_target, drag) in state.dragging.iter_mut() {
1220                        let delta = location.position - drag.latest_pos;
1221                        if delta == Vec2::ZERO {
1222                            continue; // No need to emit a Drag event if there is no movement
1223                        }
1224                        let drag_event = PointerDrag {
1225                            pointer: pointer.clone(),
1226                            entity: *drag_target,
1227                            button,
1228                            distance: location.position - drag.start_pos,
1229                            delta,
1230                        };
1231                        commands.trigger(drag_event.clone());
1232                        message_writers.drag_events.write(drag_event);
1233
1234                        // Update drag position
1235                        drag.latest_pos = location.position;
1236
1237                        // Emit corresponding DragOver to the hovered entities
1238                        for (hovered_entity, hit) in hover_map
1239                            .get(&pointer_id)
1240                            .iter()
1241                            .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
1242                            .filter(|(hovered_entity, _)| *hovered_entity != *drag_target)
1243                        {
1244                            let drag_over_event = PointerDragOver {
1245                                pointer: pointer.clone(),
1246                                entity: hovered_entity,
1247                                button,
1248                                dragged: *drag_target,
1249                                hit: hit.clone(),
1250                            };
1251                            commands.trigger(drag_over_event.clone());
1252                            message_writers.drag_over_events.write(drag_over_event);
1253                        }
1254                    }
1255                }
1256
1257                for (hovered_entity, hit) in hover_map
1258                    .get(&pointer_id)
1259                    .iter()
1260                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
1261                {
1262                    // Emit Move events to the entities we are hovering
1263                    let move_event = PointerMove {
1264                        pointer: pointer.clone(),
1265                        entity: hovered_entity,
1266                        hit: hit.clone(),
1267                        delta,
1268                    };
1269                    commands.trigger(move_event.clone());
1270                    message_writers.move_events.write(move_event);
1271                }
1272            }
1273            PointerAction::Scroll { x, y, unit, phase } => {
1274                for (hovered_entity, hit) in hover_map
1275                    .get(&pointer_id)
1276                    .iter()
1277                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
1278                {
1279                    // Emit Scroll events to the entities we are hovering
1280                    let scroll_event = PointerScroll {
1281                        pointer: pointer.clone(),
1282                        entity: hovered_entity,
1283                        unit,
1284                        x,
1285                        y,
1286                        hit: hit.clone(),
1287                        phase,
1288                    };
1289                    commands.trigger(scroll_event.clone());
1290                    message_writers.scroll_events.write(scroll_event);
1291                }
1292            }
1293            // Canceled
1294            PointerAction::Cancel => {
1295                // Emit a Cancel to the hovered entity.
1296                for (hovered_entity, hit) in hover_map
1297                    .get(&pointer_id)
1298                    .iter()
1299                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
1300                {
1301                    let cancel_event = PointerCancel {
1302                        pointer: pointer.clone(),
1303                        entity: hovered_entity,
1304                        hit,
1305                    };
1306                    commands.trigger(cancel_event.clone());
1307                    message_writers.cancel_events.write(cancel_event);
1308                }
1309                // Clear the state for the canceled pointer
1310                pointer_state.clear(pointer_id);
1311            }
1312        }
1313    }
1314}
1315
1316#[cfg(test)]
1317mod tests {
1318    use bevy_app::App;
1319    use bevy_camera::{Camera, ManualTextureViewHandle};
1320
1321    use super::*;
1322
1323    const POINTER_ID: PointerId = PointerId::Mouse;
1324    const STUB_LOCATION: Location = Location {
1325        target: NormalizedRenderTarget::TextureView(ManualTextureViewHandle(5)),
1326        position: Vec2::new(3., 4.),
1327    };
1328
1329    fn initialize_app_for_test(app: &mut App) {
1330        // Init all the resources and messages necessary to run `pointer_events`
1331        app.init_resource::<HoverMap>()
1332            .init_resource::<PreviousHoverMap>()
1333            .init_resource::<PickingSettings>()
1334            .init_resource::<PointerState>()
1335            .init_resource::<PointerMap>()
1336            .add_message::<PointerInput>()
1337            .add_message::<PointerCancel>()
1338            .add_message::<PointerClick>()
1339            .add_message::<PointerPress>()
1340            .add_message::<PointerDragDrop>()
1341            .add_message::<PointerDragEnd>()
1342            .add_message::<PointerDragEnter>()
1343            .add_message::<PointerDrag>()
1344            .add_message::<PointerDragLeave>()
1345            .add_message::<PointerDragOver>()
1346            .add_message::<PointerDragStart>()
1347            .add_message::<PointerScroll>()
1348            .add_message::<PointerMove>()
1349            .add_message::<PointerOut>()
1350            .add_message::<PointerOver>()
1351            .add_message::<PointerLeave>()
1352            .add_message::<PointerEnter>()
1353            .add_message::<PointerRelease>();
1354
1355        // Initialize the pointer map resource manually with a stub location for the mouse
1356        app.world_mut()
1357            .spawn((POINTER_ID, PointerLocation::new(STUB_LOCATION)));
1358    }
1359
1360    fn update_hover_map_with_hovered_entities(app: &mut App, camera: Entity, entities: &[Entity]) {
1361        let mut hover_map = HoverMap::default();
1362        let mut entity_map = EntityHashMap::with_capacity(entities.len());
1363        for entity in entities {
1364            entity_map.insert(
1365                *entity,
1366                HitData {
1367                    depth: 0.0,
1368                    camera,
1369                    position: None,
1370                    normal: None,
1371                    extra: None,
1372                },
1373            );
1374        }
1375        hover_map.insert(PointerId::Mouse, entity_map);
1376
1377        let previous_hover_map = app.world().resource::<HoverMap>().0.clone();
1378        app.world_mut()
1379            .insert_resource(PreviousHoverMap(previous_hover_map));
1380        app.world_mut().insert_resource(hover_map);
1381    }
1382
1383    #[test]
1384    fn enter_leave_events() {
1385        // the bool distinguishes between different *_in_bounds bool vals
1386        #[derive(Resource, Default)]
1387        struct EnterEventCounts(HashMap<(Entity, bool), usize>);
1388
1389        #[derive(Resource, Default)]
1390        struct LeaveEventCounts(HashMap<(Entity, bool), usize>);
1391
1392        fn observe_enter(event: On<PointerEnter>, mut counts: ResMut<EnterEventCounts>) {
1393            *counts
1394                .0
1395                .entry((event.entity, event.event().is_in_bounds))
1396                .or_insert(0_usize) += 1;
1397        }
1398
1399        fn observe_leave(event: On<PointerLeave>, mut counts: ResMut<LeaveEventCounts>) {
1400            *counts
1401                .0
1402                .entry((event.entity, event.event().was_in_bounds))
1403                .or_insert(0_usize) += 1;
1404        }
1405
1406        fn assert_msg_event_counts(app: &App, enter_count: usize, leave_count: usize) {
1407            let enter_messages = app.world().resource::<Messages<PointerEnter>>();
1408            let leave_messages = app.world().resource::<Messages<PointerLeave>>();
1409            assert_eq!(enter_messages.len(), enter_count);
1410            assert_eq!(leave_messages.len(), leave_count);
1411        }
1412
1413        fn assert_observer_event_counts(
1414            app: &App,
1415            entity: Entity,
1416            enter_in_bounds_counts: usize,
1417            enter_out_of_bounds_counts: usize,
1418            leave_in_bounds_counts: usize,
1419            leave_out_of_bounds_counts: usize,
1420        ) {
1421            assert_eq!(
1422                *app.world()
1423                    .resource::<EnterEventCounts>()
1424                    .0
1425                    .get(&(entity, true))
1426                    .unwrap_or(&0),
1427                enter_in_bounds_counts
1428            );
1429            assert_eq!(
1430                *app.world()
1431                    .resource::<EnterEventCounts>()
1432                    .0
1433                    .get(&(entity, false))
1434                    .unwrap_or(&0),
1435                enter_out_of_bounds_counts
1436            );
1437            assert_eq!(
1438                *app.world()
1439                    .resource::<LeaveEventCounts>()
1440                    .0
1441                    .get(&(entity, true))
1442                    .unwrap_or(&0),
1443                leave_in_bounds_counts
1444            );
1445            assert_eq!(
1446                *app.world()
1447                    .resource::<LeaveEventCounts>()
1448                    .0
1449                    .get(&(entity, false))
1450                    .unwrap_or(&0),
1451                leave_out_of_bounds_counts
1452            );
1453        }
1454
1455        let mut app = App::new();
1456        initialize_app_for_test(&mut app);
1457        app.init_resource::<EnterEventCounts>()
1458            .init_resource::<LeaveEventCounts>();
1459        let enter_messages = app.world().resource::<Messages<PointerEnter>>();
1460        let leave_messages = app.world().resource::<Messages<PointerLeave>>();
1461        assert_eq!(enter_messages.len(), 0);
1462        assert_eq!(leave_messages.len(), 0);
1463        // Setup test entities
1464        let camera = app.world_mut().spawn(Camera::default()).id();
1465        let child_one = app
1466            .world_mut()
1467            .spawn_empty()
1468            .observe(observe_enter)
1469            .observe(observe_leave)
1470            .id();
1471        let child_two = app
1472            .world_mut()
1473            .spawn_empty()
1474            .observe(observe_enter)
1475            .observe(observe_leave)
1476            .id();
1477        let parent = app
1478            .world_mut()
1479            .spawn_empty()
1480            .add_children(&[child_one, child_two])
1481            .observe(observe_enter)
1482            .observe(observe_leave)
1483            .id();
1484
1485        // FIRST: child_one is hovered over
1486        update_hover_map_with_hovered_entities(&mut app, camera, &[child_one]);
1487
1488        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1489
1490        // child_one received an in_bounds `Enter` event
1491        // The parent received an indirect `Enter` event because its child was hovered into
1492        assert_msg_event_counts(&app, 2, 0);
1493        assert_observer_event_counts(&app, parent, 0, 1, 0, 0);
1494        assert_observer_event_counts(&app, child_one, 1, 0, 0, 0);
1495        assert_observer_event_counts(&app, child_two, 0, 0, 0, 0);
1496        app.world_mut().increment_change_tick();
1497        // ---
1498
1499        // SECOND: child_one is hovered out of, child_two and parent are directly hovered over
1500        update_hover_map_with_hovered_entities(&mut app, camera, &[child_two, parent]);
1501
1502        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1503
1504        // child_one received an in_bounds `Leave` event.
1505        // child_two received an in_bounds `Enter` event.
1506        // The parent did not receive any events because it is a shared ancestor
1507        assert_msg_event_counts(&app, 3, 1);
1508        assert_observer_event_counts(&app, parent, 0, 1, 0, 0);
1509        assert_observer_event_counts(&app, child_one, 1, 0, 1, 0);
1510        assert_observer_event_counts(&app, child_two, 1, 0, 0, 0);
1511        app.world_mut().increment_change_tick();
1512        // ---
1513
1514        // THIRD: child_two is hovered out of, parent is still hovered
1515        update_hover_map_with_hovered_entities(&mut app, camera, &[parent]);
1516
1517        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1518
1519        // child_two received an in_bounds `Leave` event.
1520        assert_msg_event_counts(&app, 3, 2);
1521        assert_observer_event_counts(&app, parent, 0, 1, 0, 0);
1522        assert_observer_event_counts(&app, child_one, 1, 0, 1, 0);
1523        assert_observer_event_counts(&app, child_two, 1, 0, 1, 0);
1524        app.world_mut().increment_change_tick();
1525        // ---
1526
1527        // FOURTH: child_two is hovered back into, parent is no longer directly hovered
1528        update_hover_map_with_hovered_entities(&mut app, camera, &[child_two]);
1529
1530        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1531
1532        // child_two received an in_bounds `Enter` event
1533        // The parent did not receive an `Leave` event because its child is still hovered
1534        assert_msg_event_counts(&app, 4, 2);
1535        assert_observer_event_counts(&app, parent, 0, 1, 0, 0);
1536        assert_observer_event_counts(&app, child_one, 1, 0, 1, 0);
1537        assert_observer_event_counts(&app, child_two, 2, 0, 1, 0);
1538        app.world_mut().increment_change_tick();
1539        // ---
1540
1541        // FIFTH: child_two is hovered out of
1542        update_hover_map_with_hovered_entities(&mut app, camera, &[]);
1543
1544        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1545
1546        // child_two received one in_bounds `Leave` event
1547        // The parent received one indirect `Leave` event because the pointer is no longer hovering
1548        // any of its children
1549        assert_msg_event_counts(&app, 4, 4);
1550        assert_observer_event_counts(&app, parent, 0, 1, 0, 1);
1551        assert_observer_event_counts(&app, child_one, 1, 0, 1, 0);
1552        assert_observer_event_counts(&app, child_two, 2, 0, 2, 0);
1553        app.world_mut().increment_change_tick();
1554        // ---
1555
1556        // FINAL: parent and child_one are directly hovered into
1557        update_hover_map_with_hovered_entities(&mut app, camera, &[parent, child_one]);
1558
1559        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1560
1561        // The parent received one in_bounds `Enter` event
1562        // child_one received one in_bounds `Enter` event
1563        assert_msg_event_counts(&app, 6, 4);
1564        assert_observer_event_counts(&app, parent, 1, 1, 0, 1);
1565        assert_observer_event_counts(&app, child_one, 2, 0, 1, 0);
1566        assert_observer_event_counts(&app, child_two, 2, 0, 2, 0);
1567        app.world_mut().increment_change_tick();
1568        // ---
1569    }
1570}