1use crate::Document;
2use blitz_traits::events::{
3 BlitzPointerEvent, BlitzPointerId, DomEvent, DomEventData, EventState, Point, PointerCoords,
4 UiEvent,
5};
6use blitz_traits::node_id::NodeId;
7use std::collections::VecDeque;
8
9pub trait EventHandler {
10 fn handle_event(
11 &mut self,
12 chain: &[NodeId],
13 event: &mut DomEvent,
14 doc: &mut dyn Document,
15 event_state: &mut EventState,
16 );
17}
18
19pub struct NoopEventHandler;
20impl EventHandler for NoopEventHandler {
21 fn handle_event(
22 &mut self,
23 _chain: &[NodeId],
24 _event: &mut DomEvent,
25 _doc: &mut dyn Document,
26 _event_state: &mut EventState,
27 ) {
28 }
30}
31
32pub struct EventDriver<'doc, Handler: EventHandler> {
33 doc: &'doc mut dyn Document,
34 handler: Handler,
35 queue: VecDeque<DomEvent>,
36}
37
38impl<'doc, Handler: EventHandler> EventDriver<'doc, Handler> {
39 pub fn new(doc: &'doc mut dyn Document, handler: Handler) -> Self {
40 EventDriver {
41 doc,
42 handler,
43 queue: VecDeque::with_capacity(4),
44 }
45 }
46
47 pub fn handle_pointer_move(&mut self, event: &BlitzPointerEvent) -> Option<NodeId> {
48 let mut doc = self.doc.inner_mut();
49
50 let prev_hover_node_id = doc.hover_node_id;
51 let changed = doc.set_hover_to(event.page_x(), event.page_y());
52 let hover_node_id = doc.hover_node_id;
53
54 drop(doc);
55
56 self.dispatch_pointer_move(event, prev_hover_node_id, hover_node_id, changed)
57 }
58
59 pub fn handle_pointer_move_to_node(
62 &mut self,
63 event: &BlitzPointerEvent,
64 node_id: NodeId,
65 ) -> Option<NodeId> {
66 let mut doc = self.doc.inner_mut();
67
68 let prev_hover_node_id = doc.hover_node_id;
69 let changed = doc.set_hover_to_node(node_id, event.page_x(), event.page_y());
70 let hover_node_id = doc.hover_node_id;
71
72 drop(doc);
73
74 self.dispatch_pointer_move(event, prev_hover_node_id, hover_node_id, changed)
75 }
76
77 fn dispatch_pointer_move(
78 &mut self,
79 event: &BlitzPointerEvent,
80 prev_hover_node_id: Option<NodeId>,
81 hover_node_id: Option<NodeId>,
82 changed: bool,
83 ) -> Option<NodeId> {
84 if !changed {
85 return prev_hover_node_id;
86 }
87
88 let doc = self.doc.inner();
89 let mut old_chain = prev_hover_node_id
90 .map(|id| doc.node_chain(id))
91 .unwrap_or_default();
92 let mut new_chain = hover_node_id
93 .map(|id| doc.node_chain(id))
94 .unwrap_or_default();
95 old_chain.reverse();
96 new_chain.reverse();
97
98 let old_len = old_chain.len();
100 let new_len = new_chain.len();
101
102 let first_difference_index = old_chain
103 .iter()
104 .zip(&new_chain)
105 .position(|(old, new)| old != new)
106 .unwrap_or_else(|| old_len.min(new_len));
107
108 drop(doc);
109
110 let is_mouse = event.is_mouse();
111
112 if let Some(target) = prev_hover_node_id {
113 self.handle_dom_event(DomEvent::new(
114 target,
115 DomEventData::PointerOut(event.clone()),
116 ));
117 if is_mouse {
118 self.handle_dom_event(DomEvent::new(target, DomEventData::MouseOut(event.clone())));
119 }
120
121 for node_id in old_chain
123 .get(first_difference_index..)
124 .unwrap_or(&[])
125 .iter()
126 {
127 self.handle_dom_event(DomEvent::new(
128 *node_id,
129 DomEventData::PointerLeave(event.clone()),
130 ));
131 if is_mouse {
132 self.handle_dom_event(DomEvent::new(
133 *node_id,
134 DomEventData::MouseLeave(event.clone()),
135 ));
136 }
137 }
138 }
139
140 if let Some(target) = hover_node_id {
141 self.handle_dom_event(DomEvent::new(
142 target,
143 DomEventData::PointerOver(event.clone()),
144 ));
145
146 if is_mouse {
147 self.handle_dom_event(DomEvent::new(
148 target,
149 DomEventData::MouseOver(event.clone()),
150 ));
151 }
152
153 for node_id in new_chain
155 .get(first_difference_index..)
156 .unwrap_or(&[])
157 .iter()
158 {
159 self.handle_dom_event(DomEvent::new(
160 *node_id,
161 DomEventData::PointerEnter(event.clone()),
162 ));
163
164 if is_mouse {
165 self.handle_dom_event(DomEvent::new(
166 *node_id,
167 DomEventData::MouseEnter(event.clone()),
168 ));
169 }
170 }
171 }
172
173 hover_node_id
174 }
175
176 pub fn handle_ui_event(&mut self, event: UiEvent) {
177 self.handle_ui_event_inner(event, None);
178 }
179
180 pub fn handle_ui_event_to_node(&mut self, event: UiEvent, node_id: NodeId) {
187 self.handle_ui_event_inner(event, Some(node_id));
188 }
189
190 fn handle_ui_event_inner(&mut self, event: UiEvent, forced_target: Option<NodeId>) {
191 let doc = self.doc.inner();
192
193 let mut should_clear_hover = false;
194 let mut hover_node_id = doc.hover_node_id;
195 let focussed_node_id = doc.focus_node_id;
196 drop(doc);
197
198 match &event {
200 UiEvent::PointerMove(event) => {
201 hover_node_id = forced_target
202 .and_then(|node_id| self.handle_pointer_move_to_node(event, node_id))
203 .or_else(|| self.handle_pointer_move(event));
204 }
205 UiEvent::PointerDown(event) => {
206 hover_node_id = forced_target
207 .and_then(|node_id| self.handle_pointer_move_to_node(event, node_id))
208 .or_else(|| self.handle_pointer_move(event));
209 let mut doc = self.doc.inner_mut();
210 doc.active_node();
211 doc.set_mousedown_node_id(hover_node_id);
212 }
213 UiEvent::PointerUp(event) => {
214 hover_node_id = forced_target
215 .and_then(|node_id| self.handle_pointer_move_to_node(event, node_id))
216 .or_else(|| self.handle_pointer_move(event));
217 let mut doc = self.doc.inner_mut();
218 doc.unactive_node();
219
220 if event.is_primary && matches!(event.id, BlitzPointerId::Finger(_)) {
221 should_clear_hover = true;
222 }
223 }
224 UiEvent::PointerCancel(event) => {
225 hover_node_id = forced_target
226 .and_then(|node_id| self.handle_pointer_move_to_node(event, node_id))
227 .or_else(|| self.handle_pointer_move(event));
228 let mut doc = self.doc.inner_mut();
229 doc.unactive_node();
230 doc.set_mousedown_node_id(None);
231
232 if event.is_primary && matches!(event.id, BlitzPointerId::Finger(_)) {
233 should_clear_hover = true;
234 }
235 }
236 _ => {}
237 };
238
239 let target = match event {
240 UiEvent::PointerMove(_) => hover_node_id,
241 UiEvent::PointerUp(_) => hover_node_id,
242 UiEvent::PointerDown(_) => hover_node_id,
243 UiEvent::PointerCancel(_) => hover_node_id,
244 UiEvent::Wheel(_) => hover_node_id,
245 UiEvent::KeyUp(_) => focussed_node_id,
246 UiEvent::KeyDown(_) => focussed_node_id,
247 UiEvent::Ime(_) => focussed_node_id,
248 UiEvent::AppleStandardKeybinding(_) => focussed_node_id,
249 };
250 let Some(target) = target.or_else(|| self.doc.inner().try_root_element().map(|el| el.id))
253 else {
254 return;
255 };
256
257 match event {
258 UiEvent::PointerMove(data) => {
259 self.handle_pointer_event(
260 target,
261 data,
262 DomEventData::PointerMove,
263 Some(DomEventData::MouseMove),
264 DomEventData::TouchMove,
265 );
266 }
267 UiEvent::PointerUp(data) => {
268 self.handle_pointer_event(
269 target,
270 data,
271 DomEventData::PointerUp,
272 Some(DomEventData::MouseUp),
273 DomEventData::TouchEnd,
274 );
275 }
276 UiEvent::PointerDown(data) => {
277 self.handle_pointer_event(
278 target,
279 data,
280 DomEventData::PointerDown,
281 Some(DomEventData::MouseDown),
282 DomEventData::TouchStart,
283 );
284 }
285 UiEvent::PointerCancel(data) => {
286 self.handle_pointer_event(
289 target,
290 data,
291 DomEventData::PointerCancel,
292 None::<fn(BlitzPointerEvent) -> DomEventData>,
293 DomEventData::TouchCancel,
294 );
295 }
296 UiEvent::Wheel(data) => {
297 self.handle_dom_event(DomEvent::new(target, DomEventData::Wheel(data)))
298 }
299 UiEvent::KeyUp(data) => {
300 self.handle_dom_event(DomEvent::new(target, DomEventData::KeyUp(data)))
301 }
302 UiEvent::KeyDown(data) => {
303 self.handle_dom_event(DomEvent::new(target, DomEventData::KeyDown(data)))
304 }
305 UiEvent::Ime(data) => {
306 self.handle_dom_event(DomEvent::new(target, DomEventData::Ime(data)))
307 }
308 UiEvent::AppleStandardKeybinding(data) => {
309 let mut dom_event =
310 DomEvent::new(target, DomEventData::AppleStandardKeybinding(data));
311 self.run_default_action(&mut dom_event);
312 self.process_queue();
313 }
314 };
315
316 if should_clear_hover {
318 self.doc.inner_mut().clear_hover();
319 }
320 }
321
322 pub fn handle_dom_event(&mut self, event: DomEvent) {
323 self.queue.push_back(event);
324 self.process_queue();
325 }
326
327 fn handle_pointer_event(
328 &mut self,
329 target: NodeId,
330 data: BlitzPointerEvent,
331 make_ptr_data: impl FnOnce(BlitzPointerEvent) -> DomEventData,
332 make_mouse_data: Option<impl FnOnce(BlitzPointerEvent) -> DomEventData>,
333 make_touch_data: impl FnOnce(BlitzPointerEvent) -> DomEventData,
334 ) {
335 let mut ptr_event = DomEvent::new(target, make_ptr_data(data.clone()));
336 let mut event_state = EventState::default();
337 event_state = self.run_handler_event(&mut ptr_event, event_state);
338
339 if !event_state.is_cancelled() {
347 if data.is_mouse() {
348 if let Some(make_mouse_data) = make_mouse_data {
349 let mut mouse_event = DomEvent::new(target, make_mouse_data(data));
350 event_state = self.run_handler_event(&mut mouse_event, event_state);
351 }
352 } else if data.is_finger() || data.is_pen() {
353 let mut touch_event = DomEvent::new(target, make_touch_data(data));
354 event_state = self.run_handler_event(&mut touch_event, event_state);
355 }
356 }
357
358 if !event_state.is_cancelled() {
359 self.run_default_action(&mut ptr_event);
360 }
361 self.process_queue();
362 }
363
364 fn process_queue(&mut self) {
365 while let Some(mut event) = self.queue.pop_front() {
366 let activation = match event.data {
374 DomEventData::Click(_) => {
375 self.doc.inner_mut().run_pre_click_activation(event.target)
376 }
377 _ => None,
378 };
379
380 let event_state = self.run_handler_event(&mut event, EventState::default());
381 if event_state.is_cancelled() {
382 if let Some(activation) = activation {
383 self.doc.inner_mut().undo_pre_click_activation(activation);
384 }
385 } else {
386 self.run_default_action(&mut event);
387 }
388 }
389 }
390
391 fn adjust_element_coords(
392 &self,
393 target: NodeId,
394 coords: &PointerCoords,
395 element: &mut Point<f32>,
396 ) {
397 if let Some(rect) = self.doc.inner().get_client_bounding_rect(target) {
398 element.x = coords.client_x - rect.x as f32;
399 element.y = coords.client_y - rect.y as f32;
400 }
401 }
402
403 fn run_handler_event(
404 &mut self,
405 event: &mut DomEvent,
406 initial_event_state: EventState,
407 ) -> EventState {
408 let chain = if event.bubbles {
409 let doc = self.doc.inner();
410 doc.node_chain(event.target)
411 } else {
412 vec![event.target]
413 };
414
415 match &mut event.data {
416 DomEventData::PointerMove(data)
417 | DomEventData::PointerDown(data)
418 | DomEventData::PointerUp(data)
419 | DomEventData::PointerCancel(data)
420 | DomEventData::PointerEnter(data)
421 | DomEventData::PointerLeave(data)
422 | DomEventData::PointerOver(data)
423 | DomEventData::PointerOut(data)
424 | DomEventData::MouseMove(data)
425 | DomEventData::MouseDown(data)
426 | DomEventData::MouseUp(data)
427 | DomEventData::MouseEnter(data)
428 | DomEventData::MouseLeave(data)
429 | DomEventData::MouseOver(data)
430 | DomEventData::MouseOut(data)
431 | DomEventData::TouchStart(data)
432 | DomEventData::TouchEnd(data)
433 | DomEventData::TouchMove(data)
434 | DomEventData::TouchCancel(data)
435 | DomEventData::Click(data)
436 | DomEventData::ContextMenu(data)
437 | DomEventData::DoubleClick(data) => {
438 self.adjust_element_coords(event.target, &data.coords, &mut data.element)
439 }
440 DomEventData::Wheel(data) => {
441 self.adjust_element_coords(event.target, &data.coords, &mut data.element)
442 }
443 _ => {}
444 }
445
446 let mut event_state = initial_event_state;
447 self.handler
448 .handle_event(&chain, event, self.doc, &mut event_state);
449
450 event_state
451 }
452
453 fn run_default_action(&mut self, event: &mut DomEvent) {
454 let mut doc = self.doc.inner_mut();
455 doc.handle_dom_event(event, |new_evt| self.queue.push_back(new_evt));
456 }
457}