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