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 [`Pointer<E>`] events.
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<Pointer<Over>>| {
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: [`Over`], [`Move`], and [`Out`].
34//! + Clicking and pressing: [`Press`], [`Release`], and [`Click`].
35//! + Dragging and dropping: [`DragStart`], [`Drag`], [`DragEnd`], [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].
36//!
37//! When received by an observer, these events will always be wrapped by the [`Pointer`] type, which contains
38//! general metadata about the pointer event.
39
40use core::{fmt::Debug, time::Duration};
41
42use bevy_camera::NormalizedRenderTarget;
43use bevy_ecs::{prelude::*, query::QueryData, system::SystemParam, traversal::Traversal};
44use bevy_input::mouse::MouseScrollUnit;
45use bevy_math::Vec2;
46use bevy_platform::collections::HashMap;
47use bevy_platform::time::Instant;
48use bevy_reflect::prelude::*;
49use bevy_window::Window;
50use tracing::debug;
51
52use crate::{
53 backend::{prelude::PointerLocation, HitData},
54 hover::{HoverMap, PreviousHoverMap},
55 pointer::{Location, PointerAction, PointerButton, PointerId, PointerInput, PointerMap},
56};
57
58/// Stores the common data needed for all pointer events.
59///
60/// The documentation for the [`pointer_events`] explains the events this module exposes and
61/// the order in which they fire.
62#[derive(Message, EntityEvent, Clone, PartialEq, Debug, Reflect, Component)]
63#[entity_event(propagate = PointerTraversal, auto_propagate)]
64#[reflect(Component, Debug, Clone)]
65pub struct Pointer<E: Debug + Clone + Reflect> {
66 /// The entity this pointer event happened for.
67 pub entity: Entity,
68 /// The pointer that triggered this event
69 pub pointer_id: PointerId,
70 /// The location of the pointer during this event
71 pub pointer_location: Location,
72 /// Additional event-specific data. [`DragDrop`] for example, has an additional field to describe
73 /// the `Entity` that is being dropped on the target.
74 pub event: E,
75}
76
77/// A traversal query (i.e. it implements [`Traversal`]) intended for use with [`Pointer`] events.
78///
79/// This will always traverse to the parent, if the entity being visited has one. Otherwise, it
80/// propagates to the pointer's window and stops there.
81#[derive(QueryData)]
82pub struct PointerTraversal {
83 child_of: Option<&'static ChildOf>,
84 window: Option<&'static Window>,
85}
86
87impl<E> Traversal<Pointer<E>> for PointerTraversal
88where
89 E: Debug + Clone + Reflect,
90{
91 fn traverse(item: Self::Item<'_, '_>, pointer: &Pointer<E>) -> Option<Entity> {
92 let PointerTraversalItem { child_of, window } = item;
93
94 // Send event to parent, if it has one.
95 if let Some(child_of) = child_of {
96 return Some(child_of.parent());
97 };
98
99 // Otherwise, send it to the window entity (unless this is a window entity).
100 if window.is_none()
101 && let NormalizedRenderTarget::Window(window_ref) = pointer.pointer_location.target
102 {
103 return Some(window_ref.entity());
104 }
105
106 None
107 }
108}
109
110impl<E: Debug + Clone + Reflect> core::fmt::Display for Pointer<E> {
111 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112 f.write_fmt(format_args!(
113 "{:?}, {:.1?}, {:.1?}",
114 self.pointer_id, self.pointer_location.position, self.event
115 ))
116 }
117}
118
119impl<E: Debug + Clone + Reflect> core::ops::Deref for Pointer<E> {
120 type Target = E;
121
122 fn deref(&self) -> &Self::Target {
123 &self.event
124 }
125}
126
127impl<E: Debug + Clone + Reflect> Pointer<E> {
128 /// Construct a new `Pointer<E>` event.
129 pub fn new(id: PointerId, location: Location, event: E, entity: Entity) -> Self {
130 Self {
131 pointer_id: id,
132 pointer_location: location,
133 event,
134 entity,
135 }
136 }
137}
138
139/// Fires when a pointer is canceled, and its current interaction state is dropped.
140#[derive(Clone, PartialEq, Debug, Reflect)]
141#[reflect(Clone, PartialEq)]
142pub struct Cancel {
143 /// Information about the picking intersection.
144 pub hit: HitData,
145}
146
147/// Fires when a pointer crosses into the bounds of the [target entity](EntityEvent::event_target).
148#[derive(Clone, PartialEq, Debug, Reflect)]
149#[reflect(Clone, PartialEq)]
150pub struct Over {
151 /// Information about the picking intersection.
152 pub hit: HitData,
153}
154
155/// Fires when a pointer crosses out of the bounds of the [target entity](EntityEvent::event_target).
156#[derive(Clone, PartialEq, Debug, Reflect)]
157#[reflect(Clone, PartialEq)]
158pub struct Out {
159 /// Information about the latest prior picking intersection.
160 pub hit: HitData,
161}
162
163/// Fires when a pointer button is pressed over the [target entity](EntityEvent::event_target).
164#[derive(Clone, PartialEq, Debug, Reflect)]
165#[reflect(Clone, PartialEq)]
166pub struct Press {
167 /// Pointer button pressed to trigger this event.
168 pub button: PointerButton,
169 /// Information about the picking intersection.
170 pub hit: HitData,
171}
172
173/// Fires when a pointer button is released over the [target entity](EntityEvent::event_target).
174#[derive(Clone, PartialEq, Debug, Reflect)]
175#[reflect(Clone, PartialEq)]
176pub struct Release {
177 /// Pointer button lifted to trigger this event.
178 pub button: PointerButton,
179 /// Information about the picking intersection.
180 pub hit: HitData,
181}
182
183/// Fires when a pointer sends a pointer pressed event followed by a pointer released event, with the same
184/// [target entity](EntityEvent::event_target) for both events.
185#[derive(Clone, PartialEq, Debug, Reflect)]
186#[reflect(Clone, PartialEq)]
187pub struct Click {
188 /// Pointer button pressed and lifted to trigger this event.
189 pub button: PointerButton,
190 /// Information about the picking intersection.
191 pub hit: HitData,
192 /// Duration between the pointer pressed and lifted for this click
193 pub duration: Duration,
194}
195
196/// Fires while a pointer is moving over the [target entity](EntityEvent::event_target).
197#[derive(Clone, PartialEq, Debug, Reflect)]
198#[reflect(Clone, PartialEq)]
199pub struct Move {
200 /// Information about the picking intersection.
201 pub hit: HitData,
202 /// The change in position since the last move event.
203 ///
204 /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
205 /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
206 /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
207 /// world-space.
208 pub delta: Vec2,
209}
210
211/// Fires when the [target entity](EntityEvent::event_target) receives a pointer pressed event followed by a pointer move event.
212#[derive(Clone, PartialEq, Debug, Reflect)]
213#[reflect(Clone, PartialEq)]
214pub struct DragStart {
215 /// Pointer button pressed and moved to trigger this event.
216 pub button: PointerButton,
217 /// Information about the picking intersection.
218 pub hit: HitData,
219}
220
221/// Fires while the [target entity](EntityEvent::event_target) is being dragged.
222#[derive(Clone, PartialEq, Debug, Reflect)]
223#[reflect(Clone, PartialEq)]
224pub struct Drag {
225 /// Pointer button pressed and moved to trigger this event.
226 pub button: PointerButton,
227 /// The total distance vector of a drag, measured from drag start to the current position.
228 ///
229 /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
230 /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
231 /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
232 /// world-space.
233 pub distance: Vec2,
234 /// The change in position since the last drag event.
235 ///
236 /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
237 /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
238 /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
239 /// world-space.
240 pub delta: Vec2,
241}
242
243/// Fires when a pointer is dragging the [target entity](EntityEvent::event_target) and a pointer released event is received.
244#[derive(Clone, PartialEq, Debug, Reflect)]
245#[reflect(Clone, PartialEq)]
246pub struct DragEnd {
247 /// Pointer button pressed, moved, and released to trigger this event.
248 pub button: PointerButton,
249 /// The vector of drag movement measured from start to final pointer position.
250 ///
251 /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
252 /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
253 /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
254 /// world-space.
255 pub distance: Vec2,
256}
257
258/// Fires when a pointer dragging the `dragged` entity enters the [target entity](EntityEvent::event_target).
259#[derive(Clone, PartialEq, Debug, Reflect)]
260#[reflect(Clone, PartialEq)]
261pub struct DragEnter {
262 /// Pointer button pressed to enter drag.
263 pub button: PointerButton,
264 /// The entity that was being dragged when the pointer entered the [target entity](EntityEvent::event_target).
265 pub dragged: Entity,
266 /// Information about the picking intersection.
267 pub hit: HitData,
268}
269
270/// Fires while the `dragged` entity is being dragged over the [target entity](EntityEvent::event_target).
271#[derive(Clone, PartialEq, Debug, Reflect)]
272#[reflect(Clone, PartialEq)]
273pub struct DragOver {
274 /// Pointer button pressed while dragging over.
275 pub button: PointerButton,
276 /// The entity that was being dragged when the pointer was over the [target entity](EntityEvent::event_target).
277 pub dragged: Entity,
278 /// Information about the picking intersection.
279 pub hit: HitData,
280}
281
282/// Fires when a pointer dragging the `dragged` entity leaves the [target entity](EntityEvent::event_target).
283#[derive(Clone, PartialEq, Debug, Reflect)]
284#[reflect(Clone, PartialEq)]
285pub struct DragLeave {
286 /// Pointer button pressed while leaving drag.
287 pub button: PointerButton,
288 /// The entity that was being dragged when the pointer left the [target entity](EntityEvent::event_target).
289 pub dragged: Entity,
290 /// Information about the latest prior picking intersection.
291 pub hit: HitData,
292}
293
294/// Fires when a pointer drops the `dropped` entity onto the [target entity](EntityEvent::event_target).
295#[derive(Clone, PartialEq, Debug, Reflect)]
296#[reflect(Clone, PartialEq)]
297pub struct DragDrop {
298 /// Pointer button released to drop.
299 pub button: PointerButton,
300 /// The entity that was dropped onto the [target entity](EntityEvent::event_target).
301 pub dropped: Entity,
302 /// Information about the picking intersection.
303 pub hit: HitData,
304}
305
306/// Dragging state.
307#[derive(Clone, PartialEq, Debug, Reflect)]
308#[reflect(Clone, PartialEq)]
309pub struct DragEntry {
310 /// The position of the pointer at drag start.
311 ///
312 /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
313 /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
314 /// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or
315 /// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to
316 /// convert from screen-space to world-space.
317 pub start_pos: Vec2,
318 /// The latest position of the pointer during this drag, used to compute deltas.
319 ///
320 /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
321 /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
322 /// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or
323 /// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to
324 /// convert from screen-space to world-space.
325 pub latest_pos: Vec2,
326}
327
328/// Fires while a pointer is scrolling over the [target entity](EntityEvent::event_target).
329#[derive(Clone, PartialEq, Debug, Reflect)]
330#[reflect(Clone, PartialEq)]
331pub struct Scroll {
332 /// The mouse scroll unit.
333 pub unit: MouseScrollUnit,
334 /// The horizontal scroll value.
335 pub x: f32,
336 /// The vertical scroll value.
337 pub y: f32,
338 /// Information about the picking intersection.
339 pub hit: HitData,
340}
341
342/// An entry in the cache that drives the `pointer_events` system, storing additional data
343/// about pointer button presses.
344#[derive(Debug, Clone, Default)]
345pub struct PointerButtonState {
346 /// Stores the press location and start time for each button currently being pressed by the pointer.
347 pub pressing: HashMap<Entity, (Location, Instant, HitData)>,
348 /// Stores the starting and current locations for each entity currently being dragged by the pointer.
349 pub dragging: HashMap<Entity, DragEntry>,
350 /// Stores the hit data for each entity currently being dragged over by the pointer.
351 pub dragging_over: HashMap<Entity, HitData>,
352}
353
354/// State for all pointers.
355#[derive(Debug, Clone, Default, Resource)]
356pub struct PointerState {
357 /// Pressing and dragging state, organized by pointer and button.
358 pub pointer_buttons: HashMap<(PointerId, PointerButton), PointerButtonState>,
359}
360
361impl PointerState {
362 /// Retrieves the current state for a specific pointer and button, if it has been created.
363 pub fn get(&self, pointer_id: PointerId, button: PointerButton) -> Option<&PointerButtonState> {
364 self.pointer_buttons.get(&(pointer_id, button))
365 }
366
367 /// Provides write access to the state of a pointer and button, creating it if it does not yet exist.
368 pub fn get_mut(
369 &mut self,
370 pointer_id: PointerId,
371 button: PointerButton,
372 ) -> &mut PointerButtonState {
373 self.pointer_buttons
374 .entry((pointer_id, button))
375 .or_default()
376 }
377
378 /// Clears all the data associated with all of the buttons on a pointer. Does not free the underlying memory.
379 pub fn clear(&mut self, pointer_id: PointerId) {
380 for button in PointerButton::iter() {
381 if let Some(state) = self.pointer_buttons.get_mut(&(pointer_id, button)) {
382 state.pressing.clear();
383 state.dragging.clear();
384 state.dragging_over.clear();
385 }
386 }
387 }
388}
389
390/// A helper system param for accessing the picking event writers.
391#[derive(SystemParam)]
392pub struct PickingMessageWriters<'w> {
393 cancel_events: MessageWriter<'w, Pointer<Cancel>>,
394 click_events: MessageWriter<'w, Pointer<Click>>,
395 pressed_events: MessageWriter<'w, Pointer<Press>>,
396 drag_drop_events: MessageWriter<'w, Pointer<DragDrop>>,
397 drag_end_events: MessageWriter<'w, Pointer<DragEnd>>,
398 drag_enter_events: MessageWriter<'w, Pointer<DragEnter>>,
399 drag_events: MessageWriter<'w, Pointer<Drag>>,
400 drag_leave_events: MessageWriter<'w, Pointer<DragLeave>>,
401 drag_over_events: MessageWriter<'w, Pointer<DragOver>>,
402 drag_start_events: MessageWriter<'w, Pointer<DragStart>>,
403 scroll_events: MessageWriter<'w, Pointer<Scroll>>,
404 move_events: MessageWriter<'w, Pointer<Move>>,
405 out_events: MessageWriter<'w, Pointer<Out>>,
406 over_events: MessageWriter<'w, Pointer<Over>>,
407 released_events: MessageWriter<'w, Pointer<Release>>,
408}
409
410/// Dispatches interaction events to the target entities.
411///
412/// Within a single frame, events are dispatched in the following order:
413/// + [`Out`] → [`DragLeave`].
414/// + [`DragEnter`] → [`Over`].
415/// + Any number of any of the following:
416/// + For each movement: [`DragStart`] → [`Drag`] → [`DragOver`] → [`Move`].
417/// + For each button press: [`Press`] or [`Click`] → [`Release`] → [`DragDrop`] → [`DragEnd`] → [`DragLeave`].
418/// + For each pointer cancellation: [`Cancel`].
419///
420/// Additionally, across multiple frames, the following are also strictly
421/// ordered by the interaction state machine:
422/// + When a pointer moves over the target:
423/// [`Over`], [`Move`], [`Out`].
424/// + When a pointer presses buttons on the target:
425/// [`Press`], [`Click`], [`Release`].
426/// + When a pointer drags the target:
427/// [`DragStart`], [`Drag`], [`DragEnd`].
428/// + When a pointer drags something over the target:
429/// [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].
430/// + When a pointer is canceled:
431/// No other events will follow the [`Cancel`] event for that pointer.
432///
433/// Two events -- [`Over`] and [`Out`] -- are driven only by the [`HoverMap`].
434/// The rest rely on additional data from the [`PointerInput`] event stream. To
435/// receive these events for a custom pointer, you must add [`PointerInput`]
436/// events.
437///
438/// When the pointer goes from hovering entity A to entity B, entity A will
439/// receive [`Out`] and then entity B will receive [`Over`]. No entity will ever
440/// receive both an [`Over`] and and a [`Out`] event during the same frame.
441///
442/// When we account for event bubbling, this is no longer true. When the hovering focus shifts
443/// between children, parent entities may receive redundant [`Out`] → [`Over`] pairs.
444/// In the context of UI, this is especially problematic. Additional hierarchy-aware
445/// events will be added in a future release.
446///
447/// Both [`Click`] and [`Release`] target the entity hovered in the *previous frame*,
448/// rather than the current frame. This is because touch pointers hover nothing
449/// on the frame they are released. The end effect is that these two events can
450/// be received sequentially after an [`Out`] event (but always on the same frame
451/// as the [`Out`] event).
452///
453/// Note: Though it is common for the [`PointerInput`] stream may contain
454/// multiple pointer movements and presses each frame, the hover state is
455/// determined only by the pointer's *final position*. Since the hover state
456/// ultimately determines which entities receive events, this may mean that an
457/// entity can receive events from before or after it was actually hovered.
458pub fn pointer_events(
459 // Input
460 mut input_events: MessageReader<PointerInput>,
461 // ECS State
462 pointers: Query<&PointerLocation>,
463 pointer_map: Res<PointerMap>,
464 hover_map: Res<HoverMap>,
465 previous_hover_map: Res<PreviousHoverMap>,
466 mut pointer_state: ResMut<PointerState>,
467 // Output
468 mut commands: Commands,
469 mut message_writers: PickingMessageWriters,
470) {
471 // Setup utilities
472 let now = Instant::now();
473 let pointer_location = |pointer_id: PointerId| {
474 pointer_map
475 .get_entity(pointer_id)
476 .and_then(|entity| pointers.get(entity).ok())
477 .and_then(|pointer| pointer.location.clone())
478 };
479
480 // If the entity was hovered by a specific pointer last frame...
481 for (pointer_id, hovered_entity, hit) in previous_hover_map
482 .iter()
483 .flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
484 {
485 // ...but is now not being hovered by that same pointer...
486 if !hover_map
487 .get(&pointer_id)
488 .iter()
489 .any(|e| e.contains_key(&hovered_entity))
490 {
491 let Some(location) = pointer_location(pointer_id) else {
492 debug!(
493 "Unable to get location for pointer {:?} during pointer out",
494 pointer_id
495 );
496 continue;
497 };
498
499 // Always send Out events
500 let out_event = Pointer::new(
501 pointer_id,
502 location.clone(),
503 Out { hit: hit.clone() },
504 hovered_entity,
505 );
506 commands.trigger(out_event.clone());
507 message_writers.out_events.write(out_event);
508
509 // Possibly send DragLeave events
510 for button in PointerButton::iter() {
511 let state = pointer_state.get_mut(pointer_id, button);
512 state.dragging_over.remove(&hovered_entity);
513 for drag_target in state.dragging.keys() {
514 let drag_leave_event = Pointer::new(
515 pointer_id,
516 location.clone(),
517 DragLeave {
518 button,
519 dragged: *drag_target,
520 hit: hit.clone(),
521 },
522 hovered_entity,
523 );
524 commands.trigger(drag_leave_event.clone());
525 message_writers.drag_leave_events.write(drag_leave_event);
526 }
527 }
528 }
529 }
530
531 // If the entity is hovered...
532 for (pointer_id, hovered_entity, hit) in hover_map
533 .iter()
534 .flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
535 {
536 // ...but was not hovered last frame...
537 if !previous_hover_map
538 .get(&pointer_id)
539 .iter()
540 .any(|e| e.contains_key(&hovered_entity))
541 {
542 let Some(location) = pointer_location(pointer_id) else {
543 debug!(
544 "Unable to get location for pointer {:?} during pointer over",
545 pointer_id
546 );
547 continue;
548 };
549
550 // Possibly send DragEnter events
551 for button in PointerButton::iter() {
552 let state = pointer_state.get_mut(pointer_id, button);
553
554 for drag_target in state.dragging.keys() {
555 state.dragging_over.insert(hovered_entity, hit.clone());
556 let drag_enter_event = Pointer::new(
557 pointer_id,
558 location.clone(),
559 DragEnter {
560 button,
561 dragged: *drag_target,
562 hit: hit.clone(),
563 },
564 hovered_entity,
565 );
566 commands.trigger(drag_enter_event.clone());
567 message_writers.drag_enter_events.write(drag_enter_event);
568 }
569 }
570
571 // Always send Over events
572 let over_event = Pointer::new(
573 pointer_id,
574 location.clone(),
575 Over { hit: hit.clone() },
576 hovered_entity,
577 );
578 commands.trigger(over_event.clone());
579 message_writers.over_events.write(over_event);
580 }
581 }
582
583 // Dispatch input events...
584 for PointerInput {
585 pointer_id,
586 location,
587 action,
588 } in input_events.read().cloned()
589 {
590 match action {
591 PointerAction::Press(button) => {
592 let state = pointer_state.get_mut(pointer_id, button);
593
594 // If it's a press, emit a Pressed event and mark the hovered entities as pressed
595 for (hovered_entity, hit) in hover_map
596 .get(&pointer_id)
597 .iter()
598 .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
599 {
600 let pressed_event = Pointer::new(
601 pointer_id,
602 location.clone(),
603 Press {
604 button,
605 hit: hit.clone(),
606 },
607 hovered_entity,
608 );
609 commands.trigger(pressed_event.clone());
610 message_writers.pressed_events.write(pressed_event);
611 // Also insert the press into the state
612 state
613 .pressing
614 .insert(hovered_entity, (location.clone(), now, hit));
615 }
616 }
617 PointerAction::Release(button) => {
618 let state = pointer_state.get_mut(pointer_id, button);
619
620 // Emit Click and Up events on all the previously hovered entities.
621 for (hovered_entity, hit) in previous_hover_map
622 .get(&pointer_id)
623 .iter()
624 .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
625 {
626 // If this pointer previously pressed the hovered entity, emit a Click event
627 if let Some((_, press_instant, _)) = state.pressing.get(&hovered_entity) {
628 let click_event = Pointer::new(
629 pointer_id,
630 location.clone(),
631 Click {
632 button,
633 hit: hit.clone(),
634 duration: now - *press_instant,
635 },
636 hovered_entity,
637 );
638 commands.trigger(click_event.clone());
639 message_writers.click_events.write(click_event);
640 }
641 // Always send the Release event
642 let released_event = Pointer::new(
643 pointer_id,
644 location.clone(),
645 Release {
646 button,
647 hit: hit.clone(),
648 },
649 hovered_entity,
650 );
651 commands.trigger(released_event.clone());
652 message_writers.released_events.write(released_event);
653 }
654
655 // Then emit the drop events.
656 for (drag_target, drag) in state.dragging.drain() {
657 // Emit DragDrop
658 for (dragged_over, hit) in state.dragging_over.iter() {
659 let drag_drop_event = Pointer::new(
660 pointer_id,
661 location.clone(),
662 DragDrop {
663 button,
664 dropped: drag_target,
665 hit: hit.clone(),
666 },
667 *dragged_over,
668 );
669 commands.trigger(drag_drop_event.clone());
670 message_writers.drag_drop_events.write(drag_drop_event);
671 }
672 // Emit DragEnd
673 let drag_end_event = Pointer::new(
674 pointer_id,
675 location.clone(),
676 DragEnd {
677 button,
678 distance: drag.latest_pos - drag.start_pos,
679 },
680 drag_target,
681 );
682 commands.trigger(drag_end_event.clone());
683 message_writers.drag_end_events.write(drag_end_event);
684 // Emit DragLeave
685 for (dragged_over, hit) in state.dragging_over.iter() {
686 let drag_leave_event = Pointer::new(
687 pointer_id,
688 location.clone(),
689 DragLeave {
690 button,
691 dragged: drag_target,
692 hit: hit.clone(),
693 },
694 *dragged_over,
695 );
696 commands.trigger(drag_leave_event.clone());
697 message_writers.drag_leave_events.write(drag_leave_event);
698 }
699 }
700
701 // Finally, we can clear the state of everything relating to presses or drags.
702 state.pressing.clear();
703 state.dragging.clear();
704 state.dragging_over.clear();
705 }
706 // Moved
707 PointerAction::Move { delta } => {
708 if delta == Vec2::ZERO {
709 continue; // If delta is zero, the following events will not be triggered.
710 }
711 // Triggers during movement even if not over an entity
712 for button in PointerButton::iter() {
713 let state = pointer_state.get_mut(pointer_id, button);
714
715 // Emit DragEntry and DragStart the first time we move while pressing an entity
716 for (press_target, (location, _, hit)) in state.pressing.iter() {
717 if state.dragging.contains_key(press_target) {
718 continue; // This entity is already logged as being dragged
719 }
720 state.dragging.insert(
721 *press_target,
722 DragEntry {
723 start_pos: location.position,
724 latest_pos: location.position,
725 },
726 );
727 let drag_start_event = Pointer::new(
728 pointer_id,
729 location.clone(),
730 DragStart {
731 button,
732 hit: hit.clone(),
733 },
734 *press_target,
735 );
736 commands.trigger(drag_start_event.clone());
737 message_writers.drag_start_events.write(drag_start_event);
738 }
739
740 // Emit Drag events to the entities we are dragging
741 for (drag_target, drag) in state.dragging.iter_mut() {
742 let delta = location.position - drag.latest_pos;
743 if delta == Vec2::ZERO {
744 continue; // No need to emit a Drag event if there is no movement
745 }
746 let drag_event = Pointer::new(
747 pointer_id,
748 location.clone(),
749 Drag {
750 button,
751 distance: location.position - drag.start_pos,
752 delta,
753 },
754 *drag_target,
755 );
756 commands.trigger(drag_event.clone());
757 message_writers.drag_events.write(drag_event);
758
759 // Update drag position
760 drag.latest_pos = location.position;
761
762 // Emit corresponding DragOver to the hovered entities
763 for (hovered_entity, hit) in hover_map
764 .get(&pointer_id)
765 .iter()
766 .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
767 .filter(|(hovered_entity, _)| *hovered_entity != *drag_target)
768 {
769 let drag_over_event = Pointer::new(
770 pointer_id,
771 location.clone(),
772 DragOver {
773 button,
774 dragged: *drag_target,
775 hit: hit.clone(),
776 },
777 hovered_entity,
778 );
779 commands.trigger(drag_over_event.clone());
780 message_writers.drag_over_events.write(drag_over_event);
781 }
782 }
783 }
784
785 for (hovered_entity, hit) in hover_map
786 .get(&pointer_id)
787 .iter()
788 .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
789 {
790 // Emit Move events to the entities we are hovering
791 let move_event = Pointer::new(
792 pointer_id,
793 location.clone(),
794 Move {
795 hit: hit.clone(),
796 delta,
797 },
798 hovered_entity,
799 );
800 commands.trigger(move_event.clone());
801 message_writers.move_events.write(move_event);
802 }
803 }
804 PointerAction::Scroll { x, y, unit } => {
805 for (hovered_entity, hit) in hover_map
806 .get(&pointer_id)
807 .iter()
808 .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
809 {
810 // Emit Scroll events to the entities we are hovering
811 let scroll_event = Pointer::new(
812 pointer_id,
813 location.clone(),
814 Scroll {
815 unit,
816 x,
817 y,
818 hit: hit.clone(),
819 },
820 hovered_entity,
821 );
822 commands.trigger(scroll_event.clone());
823 message_writers.scroll_events.write(scroll_event);
824 }
825 }
826 // Canceled
827 PointerAction::Cancel => {
828 // Emit a Cancel to the hovered entity.
829 for (hovered_entity, hit) in hover_map
830 .get(&pointer_id)
831 .iter()
832 .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
833 {
834 let cancel_event =
835 Pointer::new(pointer_id, location.clone(), Cancel { hit }, hovered_entity);
836 commands.trigger(cancel_event.clone());
837 message_writers.cancel_events.write(cancel_event);
838 }
839 // Clear the state for the canceled pointer
840 pointer_state.clear(pointer_id);
841 }
842 }
843 }
844}