1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
pub trait AssetManager: Send + Sync {
/// Request an image asset. Returns the current state (Loading, Ready, or Error).
fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>>;
/// Pre-load an image into the cache.
fn preload_image(&self, url: &str);
}
/// The phase of a touch or gesture event in its lifecycle.
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum TouchPhase {
/// The touch/gesture has just begun.
Began,
/// The touch/gesture is moving.
Moved,
/// The touch/gesture has ended normally.
Ended,
/// The touch/gesture was cancelled (e.g., by the system).
Cancelled,
}
/// User input event types
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Event {
PointerDown {
x: f32,
y: f32,
button: u32,
proximity_field: f32,
tilt: Option<f32>,
azimuth: Option<f32>,
pressure: Option<f32>,
barrel_rotation: Option<f32>,
pointer_precision: f32,
},
PointerUp {
x: f32,
y: f32,
button: u32,
tilt: Option<f32>,
azimuth: Option<f32>,
pressure: Option<f32>,
barrel_rotation: Option<f32>,
pointer_precision: f32,
},
PointerMove {
x: f32,
y: f32,
proximity_field: f32,
tilt: Option<f32>,
azimuth: Option<f32>,
pressure: Option<f32>,
barrel_rotation: Option<f32>,
pointer_precision: f32,
},
PointerClick {
x: f32,
y: f32,
button: u32,
tilt: Option<f32>,
azimuth: Option<f32>,
pressure: Option<f32>,
barrel_rotation: Option<f32>,
pointer_precision: f32,
},
PointerEnter,
PointerLeave,
/// Mouse wheel / trackpad scroll event.
/// `delta_x` is the horizontal scroll amount, `delta_y` is the vertical scroll amount (positive = scroll down).
PointerWheel {
x: f32,
y: f32,
delta_x: f32,
delta_y: f32,
pointer_precision: f32,
},
/// Double-click event (rapid successive clicks).
PointerDoubleClick {
x: f32,
y: f32,
button: u32,
pointer_precision: f32,
},
/// Drag-and-drop: drag started (pointer moved while button held past threshold).
DragStart {
x: f32,
y: f32,
button: u32,
pointer_precision: f32,
},
/// Drag-and-drop: drag in progress.
DragMove {
x: f32,
y: f32,
pointer_precision: f32,
},
/// Drag-and-drop: drag ended (pointer released).
DragEnd {
x: f32,
y: f32,
pointer_precision: f32,
},
KeyDown {
key: String,
modifiers: KeyModifiers,
},
KeyUp {
key: String,
modifiers: KeyModifiers,
},
/// Focus gained by a node.
FocusIn,
/// Focus lost by a node.
FocusOut,
/// Clipboard copy event.
Copy,
/// Clipboard cut event.
Cut,
/// Clipboard paste event with the pasted text content.
Paste(String),
/// Input Method Editor event (e.g. CJK character composition)
Ime(String),
/// Touch began at the given position.
TouchStart {
x: f32,
y: f32,
touch_id: u64,
},
/// Touch moved to a new position.
TouchMove {
x: f32,
y: f32,
touch_id: u64,
},
/// Touch ended at the given position.
TouchEnd {
x: f32,
y: f32,
touch_id: u64,
},
/// Touch cancelled.
TouchCancel {
touch_id: u64,
},
/// Multi-touch pinch gesture.
/// `center` is the gesture anchor point in device-independent pixels.
/// `scale` is the relative pinch scale (>1 = expand, <1 = contract).
/// `velocity` is the instantaneous velocity of the pinch.
/// `phase` indicates the current phase of the gesture lifecycle.
GesturePinch {
center: [f32; 2],
scale: f32,
velocity: f32,
phase: TouchPhase,
},
/// Multi-touch swipe/pan gesture.
/// `direction` is the normalized direction vector [dx, dy].
/// `velocity` is the instantaneous velocity of the swipe.
/// `phase` indicates the current phase of the gesture lifecycle.
GestureSwipe {
direction: [f32; 2],
velocity: f32,
phase: TouchPhase,
},
/// Drag-and-drop: external file dropped onto window.
FileDrop {
x: f32,
y: f32,
path: String,
},
/// Gamepad connected.
GamepadConnected {
/// Gamepad device ID.
id: u64,
/// Human-readable name.
name: String,
},
/// Gamepad disconnected.
GamepadDisconnected {
/// Gamepad device ID.
id: u64,
},
/// Gamepad button pressed or released.
GamepadButton {
/// Gamepad device ID.
id: u64,
/// Button index.
button: u32,
/// Pressure in [0.0, 1.0].
pressure: f32,
},
/// Gamepad axis moved.
GamepadAxis {
/// Gamepad device ID.
id: u64,
/// Axis index.
axis: u32,
/// Axis value in [-1.0, 1.0].
value: f32,
},
}
impl Event {
/// Returns the input pointer precision value in physical pixels if applicable.
///
/// WHY: Used to scale hit-testing bounding boxes for proximity matching.
/// CONTRACT: Mouse pointer inputs return low precision values (close to 0.0px),
/// whereas touch inputs return larger values (e.g., 150.0px) for finger emulation.
pub fn pointer_precision(&self) -> f32 {
match self {
Self::PointerDown {
pointer_precision, ..
}
| Self::PointerUp {
pointer_precision, ..
}
| Self::PointerMove {
pointer_precision, ..
}
| Self::PointerClick {
pointer_precision, ..
}
| Self::PointerWheel {
pointer_precision, ..
}
| Self::PointerDoubleClick {
pointer_precision, ..
}
| Self::DragStart {
pointer_precision, ..
}
| Self::DragMove {
pointer_precision, ..
}
| Self::DragEnd {
pointer_precision, ..
} => *pointer_precision,
_ => 0.0,
}
}
/// Returns the canonical string name of the event for lookup in handler maps.
pub fn name(&self) -> &'static str {
match self {
Self::PointerDown { .. } => "pointerdown",
Self::PointerUp { .. } => "pointerup",
Self::PointerMove { .. } => "pointermove",
Self::PointerClick { .. } => "pointerclick",
Self::PointerEnter => "pointerenter",
Self::PointerLeave => "pointerleave",
Self::PointerWheel { .. } => "pointerwheel",
Self::PointerDoubleClick { .. } => "pointerdoubleclick",
Self::DragStart { .. } => "dragstart",
Self::DragMove { .. } => "dragmove",
Self::DragEnd { .. } => "dragend",
Self::KeyDown { .. } => "keydown",
Self::KeyUp { .. } => "keyup",
Self::FocusIn => "focusin",
Self::FocusOut => "focusout",
Self::Copy => "copy",
Self::Cut => "cut",
Self::Paste(_) => "paste",
Self::Ime(_) => "ime",
Self::TouchStart { .. } => "touchstart",
Self::TouchMove { .. } => "touchmove",
Self::TouchEnd { .. } => "touchend",
Self::TouchCancel { .. } => "touchcancel",
Self::GesturePinch { .. } => "gesturepinch",
Self::GestureSwipe { .. } => "gestureswipe",
Self::FileDrop { .. } => "filedrop",
Self::GamepadConnected { .. } => "gamepadconnected",
Self::GamepadDisconnected { .. } => "gamepaddisconnected",
Self::GamepadButton { .. } => "gamepadbutton",
Self::GamepadAxis { .. } => "gamepadaxis",
}
}
}
/// Response from an event handler
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventResponse {
Handled,
Ignored,
}
// =========================================================================
use crate::*;
use std::sync::Arc;
// P1-40: EventPhase -- documents event propagation phases
// =========================================================================
//
// The CVKG event system follows the standard capture/target/bubble
// model used by the W3C DOM Event spec. When an event fires, it
// propagates through 3 phases:
//
// 1. Capture: the event travels from the root down to the
// target's parent. Listeners registered for the capture
// phase fire first.
// 2. Target: the event reaches the target node itself. Listeners
// on the target fire (regardless of capture/bubble).
// 3. Bubble: the event travels back up from the target's
// parent to the root. Listeners registered for the bubble
// phase fire last.
//
// Cancellation: any handler can call Event::stop_propagation()
// to prevent the event from continuing to the next phase or
// the next node. This affects only the current event instance.
//
// Example: a click on a button inside a panel:
// - panel's capture handler fires
// - button's capture handler fires
// - button's target handler fires
// - button's bubble handler fires
// - panel's bubble handler fires
//
// Use this enum when registering listeners to specify which
// phase to listen for.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EventPhase {
/// Event is traveling from the root toward the target.
Capture,
/// Event has reached the target node.
Target,
/// Event is traveling from the target back toward the root.
Bubble,
}
impl EventPhase {
/// All phases in propagation order.
pub const ALL: [EventPhase; 3] = [EventPhase::Capture, EventPhase::Target, EventPhase::Bubble];
}