Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_picking/src/events.rs
9367 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 [`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
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(Message, EntityEvent, Clone, PartialEq, Debug, Reflect, Component)]
63
#[entity_event(propagate = PointerTraversal, auto_propagate)]
64
#[reflect(Component, Debug, Clone)]
65
pub 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)]
82
pub struct PointerTraversal {
83
child_of: Option<&'static ChildOf>,
84
window: Option<&'static Window>,
85
}
86
87
impl<E> Traversal<Pointer<E>> for PointerTraversal
88
where
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
110
impl<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
119
impl<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
127
impl<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)]
142
pub 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)]
150
pub 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)]
158
pub 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)]
166
pub 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)]
176
pub 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)]
187
pub 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)]
199
pub 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)]
214
pub 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)]
224
pub 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)]
246
pub 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)]
261
pub 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)]
273
pub 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)]
285
pub 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)]
297
pub 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)]
309
pub 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)]
331
pub 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)]
345
pub 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
impl PointerButtonState {
355
/// Clears all press and drag data tracked for this button on its pointer.
356
pub fn clear(&mut self) {
357
self.pressing.clear();
358
self.dragging.clear();
359
self.dragging_over.clear();
360
}
361
}
362
363
/// State for all pointers.
364
#[derive(Debug, Clone, Default, Resource)]
365
pub struct PointerState {
366
/// Pressing and dragging state, organized by pointer and button.
367
pub pointer_buttons: HashMap<(PointerId, PointerButton), PointerButtonState>,
368
}
369
370
impl PointerState {
371
/// Retrieves the current state for a specific pointer and button, if it has been created.
372
pub fn get(&self, pointer_id: PointerId, button: PointerButton) -> Option<&PointerButtonState> {
373
self.pointer_buttons.get(&(pointer_id, button))
374
}
375
376
/// Provides write access to the state of a pointer and button, creating it if it does not yet exist.
377
pub fn get_mut(
378
&mut self,
379
pointer_id: PointerId,
380
button: PointerButton,
381
) -> &mut PointerButtonState {
382
self.pointer_buttons
383
.entry((pointer_id, button))
384
.or_default()
385
}
386
387
/// Clears all the data associated with all of the buttons on a pointer. Does not free the underlying memory.
388
pub fn clear(&mut self, pointer_id: PointerId) {
389
for button in PointerButton::iter() {
390
if let Some(state) = self.pointer_buttons.get_mut(&(pointer_id, button)) {
391
state.clear();
392
}
393
}
394
}
395
}
396
397
/// A helper system param for accessing the picking event writers.
398
#[derive(SystemParam)]
399
pub struct PickingMessageWriters<'w> {
400
cancel_events: MessageWriter<'w, Pointer<Cancel>>,
401
click_events: MessageWriter<'w, Pointer<Click>>,
402
pressed_events: MessageWriter<'w, Pointer<Press>>,
403
drag_drop_events: MessageWriter<'w, Pointer<DragDrop>>,
404
drag_end_events: MessageWriter<'w, Pointer<DragEnd>>,
405
drag_enter_events: MessageWriter<'w, Pointer<DragEnter>>,
406
drag_events: MessageWriter<'w, Pointer<Drag>>,
407
drag_leave_events: MessageWriter<'w, Pointer<DragLeave>>,
408
drag_over_events: MessageWriter<'w, Pointer<DragOver>>,
409
drag_start_events: MessageWriter<'w, Pointer<DragStart>>,
410
scroll_events: MessageWriter<'w, Pointer<Scroll>>,
411
move_events: MessageWriter<'w, Pointer<Move>>,
412
out_events: MessageWriter<'w, Pointer<Out>>,
413
over_events: MessageWriter<'w, Pointer<Over>>,
414
released_events: MessageWriter<'w, Pointer<Release>>,
415
}
416
417
/// Dispatches interaction events to the target entities.
418
///
419
/// Within a single frame, events are dispatched in the following order:
420
/// + [`Out`] → [`DragLeave`].
421
/// + [`DragEnter`] → [`Over`].
422
/// + Any number of any of the following:
423
/// + For each movement: [`DragStart`] → [`Drag`] → [`DragOver`] → [`Move`].
424
/// + For each button press: [`Press`] or [`Click`] → [`Release`] → [`DragDrop`] → [`DragEnd`] → [`DragLeave`].
425
/// + For each pointer cancellation: [`Cancel`].
426
///
427
/// Additionally, across multiple frames, the following are also strictly
428
/// ordered by the interaction state machine:
429
/// + When a pointer moves over the target:
430
/// [`Over`], [`Move`], [`Out`].
431
/// + When a pointer presses buttons on the target:
432
/// [`Press`], [`Click`], [`Release`].
433
/// + When a pointer drags the target:
434
/// [`DragStart`], [`Drag`], [`DragEnd`].
435
/// + When a pointer drags something over the target:
436
/// [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].
437
/// + When a pointer is canceled:
438
/// No other events will follow the [`Cancel`] event for that pointer.
439
///
440
/// Two events -- [`Over`] and [`Out`] -- are driven only by the [`HoverMap`].
441
/// The rest rely on additional data from the [`PointerInput`] event stream. To
442
/// receive these events for a custom pointer, you must add [`PointerInput`]
443
/// events.
444
///
445
/// When the pointer goes from hovering entity A to entity B, entity A will
446
/// receive [`Out`] and then entity B will receive [`Over`]. No entity will ever
447
/// receive both an [`Over`] and an [`Out`] event during the same frame.
448
///
449
/// When we account for event bubbling, this is no longer true. When the hovering focus shifts
450
/// between children, parent entities may receive redundant [`Out`] → [`Over`] pairs.
451
/// In the context of UI, this is especially problematic. Additional hierarchy-aware
452
/// events will be added in a future release.
453
///
454
/// Both [`Click`] and [`Release`] target the entity hovered in the *previous frame*,
455
/// rather than the current frame. This is because touch pointers hover nothing
456
/// on the frame they are released. The end effect is that these two events can
457
/// be received sequentially after an [`Out`] event (but always on the same frame
458
/// as the [`Out`] event).
459
///
460
/// Note: Though it is common for the [`PointerInput`] stream may contain
461
/// multiple pointer movements and presses each frame, the hover state is
462
/// determined only by the pointer's *final position*. Since the hover state
463
/// ultimately determines which entities receive events, this may mean that an
464
/// entity can receive events from before or after it was actually hovered.
465
pub fn pointer_events(
466
// Input
467
mut input_events: MessageReader<PointerInput>,
468
// ECS State
469
pointers: Query<&PointerLocation>,
470
pointer_map: Res<PointerMap>,
471
hover_map: Res<HoverMap>,
472
previous_hover_map: Res<PreviousHoverMap>,
473
mut pointer_state: ResMut<PointerState>,
474
// Output
475
mut commands: Commands,
476
mut message_writers: PickingMessageWriters,
477
) {
478
// Setup utilities
479
let now = Instant::now();
480
let pointer_location = |pointer_id: PointerId| {
481
pointer_map
482
.get_entity(pointer_id)
483
.and_then(|entity| pointers.get(entity).ok())
484
.and_then(|pointer| pointer.location.clone())
485
};
486
487
// If the entity was hovered by a specific pointer last frame...
488
for (pointer_id, hovered_entity, hit) in previous_hover_map
489
.iter()
490
.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
491
{
492
// ...but is now not being hovered by that same pointer...
493
if !hover_map
494
.get(&pointer_id)
495
.iter()
496
.any(|e| e.contains_key(&hovered_entity))
497
{
498
let Some(location) = pointer_location(pointer_id) else {
499
debug!(
500
"Unable to get location for pointer {:?} during pointer out",
501
pointer_id
502
);
503
continue;
504
};
505
506
// Always send Out events
507
let out_event = Pointer::new(
508
pointer_id,
509
location.clone(),
510
Out { hit: hit.clone() },
511
hovered_entity,
512
);
513
commands.trigger(out_event.clone());
514
message_writers.out_events.write(out_event);
515
516
// Possibly send DragLeave events
517
for button in PointerButton::iter() {
518
let state = pointer_state.get_mut(pointer_id, button);
519
state.dragging_over.remove(&hovered_entity);
520
for drag_target in state.dragging.keys() {
521
let drag_leave_event = Pointer::new(
522
pointer_id,
523
location.clone(),
524
DragLeave {
525
button,
526
dragged: *drag_target,
527
hit: hit.clone(),
528
},
529
hovered_entity,
530
);
531
commands.trigger(drag_leave_event.clone());
532
message_writers.drag_leave_events.write(drag_leave_event);
533
}
534
}
535
}
536
}
537
538
// Iterate all currently hovered entities for each pointer
539
for (pointer_id, hovered_entity, hit) in hover_map
540
.iter()
541
.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
542
{
543
// Continue if the pointer does not have a valid location.
544
let Some(location) = pointer_location(pointer_id) else {
545
debug!(
546
"Unable to get location for pointer {:?} during pointer over",
547
pointer_id
548
);
549
continue;
550
};
551
552
// For each button update its `dragging_over` state and possibly emit DragEnter events.
553
for button in PointerButton::iter() {
554
let state = pointer_state.get_mut(pointer_id, button);
555
556
// Only update the `dragging_over` state if there is at least one entity being dragged.
557
// Only emit DragEnter events for this `hovered_entity`, if it had no previous `dragging_over` state.
558
if !state.dragging.is_empty()
559
&& state
560
.dragging_over
561
.insert(hovered_entity, hit.clone())
562
.is_none()
563
{
564
for drag_target in state.dragging.keys() {
565
let drag_enter_event = Pointer::new(
566
pointer_id,
567
location.clone(),
568
DragEnter {
569
button,
570
dragged: *drag_target,
571
hit: hit.clone(),
572
},
573
hovered_entity,
574
);
575
commands.trigger(drag_enter_event.clone());
576
message_writers.drag_enter_events.write(drag_enter_event);
577
}
578
}
579
}
580
581
// Emit an Over event if the `hovered_entity` was not hovered by the same pointer the previous frame.
582
if !previous_hover_map
583
.get(&pointer_id)
584
.iter()
585
.any(|e| e.contains_key(&hovered_entity))
586
{
587
let over_event = Pointer::new(
588
pointer_id,
589
location.clone(),
590
Over { hit: hit.clone() },
591
hovered_entity,
592
);
593
commands.trigger(over_event.clone());
594
message_writers.over_events.write(over_event);
595
}
596
}
597
598
// Dispatch input events...
599
for PointerInput {
600
pointer_id,
601
location,
602
action,
603
} in input_events.read().cloned()
604
{
605
match action {
606
PointerAction::Press(button) => {
607
let state = pointer_state.get_mut(pointer_id, button);
608
609
// If it's a press, emit a Pressed event and mark the hovered entities as pressed
610
for (hovered_entity, hit) in hover_map
611
.get(&pointer_id)
612
.iter()
613
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
614
{
615
let pressed_event = Pointer::new(
616
pointer_id,
617
location.clone(),
618
Press {
619
button,
620
hit: hit.clone(),
621
},
622
hovered_entity,
623
);
624
commands.trigger(pressed_event.clone());
625
message_writers.pressed_events.write(pressed_event);
626
// Also insert the press into the state
627
state
628
.pressing
629
.insert(hovered_entity, (location.clone(), now, hit));
630
}
631
}
632
PointerAction::Release(button) => {
633
let state = pointer_state.get_mut(pointer_id, button);
634
635
// Emit Click and Release events on all the previously hovered entities.
636
for (hovered_entity, hit) in previous_hover_map
637
.get(&pointer_id)
638
.iter()
639
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
640
{
641
// If this pointer previously pressed the hovered entity, emit a Click event
642
if let Some((_, press_instant, _)) = state.pressing.get(&hovered_entity) {
643
let click_event = Pointer::new(
644
pointer_id,
645
location.clone(),
646
Click {
647
button,
648
hit: hit.clone(),
649
duration: now - *press_instant,
650
},
651
hovered_entity,
652
);
653
commands.trigger(click_event.clone());
654
message_writers.click_events.write(click_event);
655
}
656
// Always send the Release event
657
let released_event = Pointer::new(
658
pointer_id,
659
location.clone(),
660
Release {
661
button,
662
hit: hit.clone(),
663
},
664
hovered_entity,
665
);
666
commands.trigger(released_event.clone());
667
message_writers.released_events.write(released_event);
668
}
669
670
// Then emit the drop events.
671
for (drag_target, drag) in state.dragging.drain() {
672
// Emit DragDrop
673
for (dragged_over, hit) in state.dragging_over.iter() {
674
let drag_drop_event = Pointer::new(
675
pointer_id,
676
location.clone(),
677
DragDrop {
678
button,
679
dropped: drag_target,
680
hit: hit.clone(),
681
},
682
*dragged_over,
683
);
684
commands.trigger(drag_drop_event.clone());
685
message_writers.drag_drop_events.write(drag_drop_event);
686
}
687
// Emit DragEnd
688
let drag_end_event = Pointer::new(
689
pointer_id,
690
location.clone(),
691
DragEnd {
692
button,
693
distance: drag.latest_pos - drag.start_pos,
694
},
695
drag_target,
696
);
697
commands.trigger(drag_end_event.clone());
698
message_writers.drag_end_events.write(drag_end_event);
699
// Emit DragLeave
700
for (dragged_over, hit) in state.dragging_over.iter() {
701
let drag_leave_event = Pointer::new(
702
pointer_id,
703
location.clone(),
704
DragLeave {
705
button,
706
dragged: drag_target,
707
hit: hit.clone(),
708
},
709
*dragged_over,
710
);
711
commands.trigger(drag_leave_event.clone());
712
message_writers.drag_leave_events.write(drag_leave_event);
713
}
714
}
715
716
// Finally, we can clear the state of everything relating to presses or drags.
717
state.clear();
718
}
719
// Moved
720
PointerAction::Move { delta } => {
721
if delta == Vec2::ZERO {
722
continue; // If delta is zero, the following events will not be triggered.
723
}
724
// Triggers during movement even if not over an entity
725
for button in PointerButton::iter() {
726
let state = pointer_state.get_mut(pointer_id, button);
727
728
// Emit DragEntry and DragStart the first time we move while pressing an entity
729
for (press_target, (location, _, hit)) in state.pressing.iter() {
730
if state.dragging.contains_key(press_target) {
731
continue; // This entity is already logged as being dragged
732
}
733
state.dragging.insert(
734
*press_target,
735
DragEntry {
736
start_pos: location.position,
737
latest_pos: location.position,
738
},
739
);
740
let drag_start_event = Pointer::new(
741
pointer_id,
742
location.clone(),
743
DragStart {
744
button,
745
hit: hit.clone(),
746
},
747
*press_target,
748
);
749
750
commands.trigger(drag_start_event.clone());
751
message_writers.drag_start_events.write(drag_start_event);
752
753
// Insert dragging over state and emit DragEnter for hovered entities.
754
for (hovered_entity, hit) in hover_map
755
.get(&pointer_id)
756
.iter()
757
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
758
.filter(|(hovered_entity, _)| *hovered_entity != *press_target)
759
{
760
// Inserting the `dragging_over` state here ensures the `DragEnter` event won't be dispatched twice.
761
state.dragging_over.insert(hovered_entity, hit.clone());
762
let drag_enter_event = Pointer::new(
763
pointer_id,
764
location.clone(),
765
DragEnter {
766
button,
767
dragged: *press_target,
768
hit: hit.clone(),
769
},
770
hovered_entity,
771
);
772
commands.trigger(drag_enter_event.clone());
773
message_writers.drag_enter_events.write(drag_enter_event);
774
}
775
}
776
777
// Emit Drag events to the entities we are dragging
778
for (drag_target, drag) in state.dragging.iter_mut() {
779
let delta = location.position - drag.latest_pos;
780
if delta == Vec2::ZERO {
781
continue; // No need to emit a Drag event if there is no movement
782
}
783
let drag_event = Pointer::new(
784
pointer_id,
785
location.clone(),
786
Drag {
787
button,
788
distance: location.position - drag.start_pos,
789
delta,
790
},
791
*drag_target,
792
);
793
commands.trigger(drag_event.clone());
794
message_writers.drag_events.write(drag_event);
795
796
// Update drag position
797
drag.latest_pos = location.position;
798
799
// Emit corresponding DragOver to the hovered entities
800
for (hovered_entity, hit) in hover_map
801
.get(&pointer_id)
802
.iter()
803
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
804
.filter(|(hovered_entity, _)| *hovered_entity != *drag_target)
805
{
806
let drag_over_event = Pointer::new(
807
pointer_id,
808
location.clone(),
809
DragOver {
810
button,
811
dragged: *drag_target,
812
hit: hit.clone(),
813
},
814
hovered_entity,
815
);
816
commands.trigger(drag_over_event.clone());
817
message_writers.drag_over_events.write(drag_over_event);
818
}
819
}
820
}
821
822
for (hovered_entity, hit) in hover_map
823
.get(&pointer_id)
824
.iter()
825
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
826
{
827
// Emit Move events to the entities we are hovering
828
let move_event = Pointer::new(
829
pointer_id,
830
location.clone(),
831
Move {
832
hit: hit.clone(),
833
delta,
834
},
835
hovered_entity,
836
);
837
commands.trigger(move_event.clone());
838
message_writers.move_events.write(move_event);
839
}
840
}
841
PointerAction::Scroll { x, y, unit } => {
842
for (hovered_entity, hit) in hover_map
843
.get(&pointer_id)
844
.iter()
845
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
846
{
847
// Emit Scroll events to the entities we are hovering
848
let scroll_event = Pointer::new(
849
pointer_id,
850
location.clone(),
851
Scroll {
852
unit,
853
x,
854
y,
855
hit: hit.clone(),
856
},
857
hovered_entity,
858
);
859
commands.trigger(scroll_event.clone());
860
message_writers.scroll_events.write(scroll_event);
861
}
862
}
863
// Canceled
864
PointerAction::Cancel => {
865
// Emit a Cancel to the hovered entity.
866
for (hovered_entity, hit) in hover_map
867
.get(&pointer_id)
868
.iter()
869
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
870
{
871
let cancel_event =
872
Pointer::new(pointer_id, location.clone(), Cancel { hit }, hovered_entity);
873
commands.trigger(cancel_event.clone());
874
message_writers.cancel_events.write(cancel_event);
875
}
876
// Clear the state for the canceled pointer
877
pointer_state.clear(pointer_id);
878
}
879
}
880
}
881
}
882
883