1use std::{cell::Cell, collections::HashMap, rc::Rc};
14
15use crossterm::event::Event;
16use ratatui::layout::Rect;
17
18#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
20pub enum EventResult {
21 #[default]
23 Ignored,
24 Consumed,
26}
27
28#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
30pub enum EventPriority {
31 Low = 0,
32 #[default]
33 Normal = 1,
34 High = 2,
35}
36
37#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
42pub struct LayerId(u64);
43
44#[derive(Clone, Copy, PartialEq, Eq, Debug)]
46pub struct InputLayer {
47 pub(crate) id: LayerId,
48}
49
50#[derive(Clone, Copy, PartialEq, Eq)]
52pub enum EventScope {
53 Current,
56 Layer(InputLayer),
58 Global,
60}
61
62#[derive(Clone, Copy, Default)]
64pub struct EventOptions {
65 pub hit_test: bool,
68}
69
70#[derive(Clone, Copy)]
73pub(crate) struct CurrentLayer(pub(crate) LayerId);
74
75struct LayerEntry {
77 id: LayerId,
78 blocks_lower: bool,
80}
81
82struct HandlerEntry {
84 layer: Option<LayerId>,
86 priority: EventPriority,
87 order: usize,
89 options: EventOptions,
90 area: Rc<Cell<Rect>>,
92 f: Box<dyn FnMut(Event) -> EventResult>,
93}
94
95#[derive(Default)]
97pub(crate) struct InputRuntime {
98 layers: Vec<LayerEntry>,
99 handlers: Vec<HandlerEntry>,
100 next_layer_id: u64,
101 root_layer: Option<LayerId>,
102}
103
104impl InputRuntime {
105 pub(crate) fn begin_frame(&mut self) {
107 self.layers.clear();
108 self.handlers.clear();
109 let root = self.mint_layer_id();
110 self.root_layer = Some(root);
111 self.layers.push(LayerEntry {
112 id: root,
113 blocks_lower: false,
114 });
115 }
116
117 pub(crate) fn root_layer(&self) -> LayerId {
119 self.root_layer
120 .expect("`begin_frame` was not called before `root_layer`")
121 }
122
123 fn mint_layer_id(&mut self) -> LayerId {
124 let id = LayerId(self.next_layer_id);
125 self.next_layer_id = self.next_layer_id.wrapping_add(1);
126 id
127 }
128
129 pub(crate) fn push_layer(&mut self, open: bool, blocks_lower: bool) -> InputLayer {
134 let id = self.mint_layer_id();
135 if open {
136 self.layers.push(LayerEntry { id, blocks_lower });
137 }
138 InputLayer { id }
139 }
140
141 pub(crate) fn register_handler(
143 &mut self,
144 layer: Option<LayerId>,
145 priority: EventPriority,
146 options: EventOptions,
147 area: Rc<Cell<Rect>>,
148 f: Box<dyn FnMut(Event) -> EventResult>,
149 ) {
150 let order = self.handlers.len();
151 self.handlers.push(HandlerEntry {
152 layer,
153 priority,
154 order,
155 options,
156 area,
157 f,
158 });
159 }
160
161 pub(crate) fn dispatch(&mut self, event: Event) {
168 let cut = self
170 .layers
171 .iter()
172 .rposition(|e| e.blocks_lower)
173 .unwrap_or(0);
174 let active: HashMap<LayerId, usize> = self.layers[cut..]
176 .iter()
177 .enumerate()
178 .map(|(off, e)| (e.id, cut + off))
179 .collect();
180
181 let mut handlers = std::mem::take(&mut self.handlers);
183
184 let mut global_idx: Vec<usize> = (0..handlers.len())
186 .filter(|&i| handlers[i].layer.is_none())
187 .collect();
188 global_idx.sort_by(|&a, &b| {
189 handlers[b]
190 .priority
191 .cmp(&handlers[a].priority)
192 .then(handlers[a].order.cmp(&handlers[b].order))
193 });
194 if Self::run_handlers(&mut handlers, &global_idx, &event) {
195 return;
196 }
197
198 let mut layer_idx: Vec<usize> = (0..handlers.len())
200 .filter(|&i| handlers[i].layer.is_some_and(|l| active.contains_key(&l)))
201 .collect();
202 layer_idx.sort_by(|&a, &b| {
203 let za = active[&handlers[a].layer.unwrap()];
204 let zb = active[&handlers[b].layer.unwrap()];
205 zb.cmp(&za) .then(handlers[b].priority.cmp(&handlers[a].priority)) .then(handlers[a].order.cmp(&handlers[b].order)) });
209 Self::run_handlers(&mut handlers, &layer_idx, &event);
210 }
212
213 fn run_handlers(handlers: &mut [HandlerEntry], order: &[usize], event: &Event) -> bool {
216 for &i in order {
217 if Self::call_handler(&mut handlers[i], event) == EventResult::Consumed {
218 return true;
219 }
220 }
221 false
222 }
223
224 fn call_handler(h: &mut HandlerEntry, event: &Event) -> EventResult {
227 if h.options.hit_test
228 && let Event::Mouse(m) = event
229 {
230 let a = h.area.get();
231 let hit = m.column >= a.x
232 && m.column < a.x.saturating_add(a.width)
233 && m.row >= a.y
234 && m.row < a.y.saturating_add(a.height);
235 if !hit {
236 return EventResult::Ignored;
237 }
238 }
239 (h.f)(event.clone())
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246 use crossterm::event::{
247 KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
248 };
249 use std::cell::RefCell;
250
251 type Log = Rc<RefCell<Vec<&'static str>>>;
252
253 fn key() -> Event {
254 Event::Key(KeyEvent::new(KeyCode::Char('x'), KeyModifiers::NONE))
255 }
256
257 fn mouse_at(col: u16, row: u16) -> Event {
258 Event::Mouse(MouseEvent {
259 kind: MouseEventKind::Down(MouseButton::Left),
260 column: col,
261 row,
262 modifiers: KeyModifiers::NONE,
263 })
264 }
265
266 fn full_area() -> Rc<Cell<Rect>> {
267 Rc::new(Cell::new(Rect::new(0, 0, 100, 100)))
268 }
269
270 fn handler(
271 log: &Log,
272 tag: &'static str,
273 result: EventResult,
274 ) -> Box<dyn FnMut(Event) -> EventResult> {
275 let log = log.clone();
276 Box::new(move |_| {
277 log.borrow_mut().push(tag);
278 result
279 })
280 }
281
282 fn opts(hit_test: bool) -> EventOptions {
283 EventOptions { hit_test }
284 }
285
286 #[test]
288 fn blocks_lower_truncates_background() {
289 let log: Log = Default::default();
290 let mut rt = InputRuntime::default();
291 rt.begin_frame();
292 let root = rt.root_layer();
293 rt.register_handler(
294 Some(root),
295 EventPriority::Normal,
296 opts(false),
297 full_area(),
298 handler(&log, "bg", EventResult::Ignored),
299 );
300 let modal = rt.push_layer(true, true);
301 rt.register_handler(
302 Some(modal.id),
303 EventPriority::Normal,
304 opts(false),
305 full_area(),
306 handler(&log, "modal", EventResult::Ignored),
307 );
308 rt.dispatch(key());
309 assert_eq!(*log.borrow(), ["modal"]);
310 }
311
312 #[test]
314 fn nested_blocks_lower_activates_only_top() {
315 let log: Log = Default::default();
316 let mut rt = InputRuntime::default();
317 rt.begin_frame();
318 let root = rt.root_layer();
319 rt.register_handler(
320 Some(root),
321 EventPriority::Normal,
322 opts(false),
323 full_area(),
324 handler(&log, "root", EventResult::Ignored),
325 );
326 let l1 = rt.push_layer(true, true);
327 rt.register_handler(
328 Some(l1.id),
329 EventPriority::Normal,
330 opts(false),
331 full_area(),
332 handler(&log, "l1", EventResult::Ignored),
333 );
334 let l2 = rt.push_layer(true, true);
335 rt.register_handler(
336 Some(l2.id),
337 EventPriority::Normal,
338 opts(false),
339 full_area(),
340 handler(&log, "l2", EventResult::Ignored),
341 );
342 rt.dispatch(key());
343 assert_eq!(*log.borrow(), ["l2"]);
344 }
345
346 #[test]
348 fn non_blocking_layers_above_blocker_remain_active() {
349 let log: Log = Default::default();
350 let mut rt = InputRuntime::default();
351 rt.begin_frame();
352 let root = rt.root_layer();
353 rt.register_handler(
354 Some(root),
355 EventPriority::Normal,
356 opts(false),
357 full_area(),
358 handler(&log, "root", EventResult::Ignored),
359 );
360 let modal = rt.push_layer(true, true);
361 rt.register_handler(
362 Some(modal.id),
363 EventPriority::Normal,
364 opts(false),
365 full_area(),
366 handler(&log, "modal", EventResult::Ignored),
367 );
368 let toast = rt.push_layer(true, false);
369 rt.register_handler(
370 Some(toast.id),
371 EventPriority::Normal,
372 opts(false),
373 full_area(),
374 handler(&log, "toast", EventResult::Ignored),
375 );
376
377 rt.dispatch(key());
378 assert_eq!(*log.borrow(), ["toast", "modal"]);
379 }
380
381 #[test]
383 fn consumed_stops_subsequent() {
384 let log: Log = Default::default();
385 let mut rt = InputRuntime::default();
386 rt.begin_frame();
387 let root = rt.root_layer();
388 rt.register_handler(
389 Some(root),
390 EventPriority::Normal,
391 opts(false),
392 full_area(),
393 handler(&log, "first", EventResult::Consumed),
394 );
395 rt.register_handler(
396 Some(root),
397 EventPriority::Normal,
398 opts(false),
399 full_area(),
400 handler(&log, "second", EventResult::Ignored),
401 );
402 rt.dispatch(key());
403 assert_eq!(*log.borrow(), ["first"]);
404 }
405
406 #[test]
408 fn ignored_continues_propagation() {
409 let log: Log = Default::default();
410 let mut rt = InputRuntime::default();
411 rt.begin_frame();
412 let root = rt.root_layer();
413 rt.register_handler(
414 Some(root),
415 EventPriority::Normal,
416 opts(false),
417 full_area(),
418 handler(&log, "first", EventResult::Ignored),
419 );
420 rt.register_handler(
421 Some(root),
422 EventPriority::Normal,
423 opts(false),
424 full_area(),
425 handler(&log, "second", EventResult::Ignored),
426 );
427 rt.dispatch(key());
428 assert_eq!(*log.borrow(), ["first", "second"]);
429 }
430
431 #[test]
433 fn layer_z_order_beats_priority() {
434 let log: Log = Default::default();
435 let mut rt = InputRuntime::default();
436 rt.begin_frame();
437 let root = rt.root_layer();
438 rt.register_handler(
439 Some(root),
440 EventPriority::High,
441 opts(false),
442 full_area(),
443 handler(&log, "bg_high", EventResult::Ignored),
444 );
445 let top = rt.push_layer(true, false); rt.register_handler(
447 Some(top.id),
448 EventPriority::Normal,
449 opts(false),
450 full_area(),
451 handler(&log, "top_normal", EventResult::Ignored),
452 );
453 rt.dispatch(key());
454 assert_eq!(*log.borrow(), ["top_normal", "bg_high"]);
455 }
456
457 #[test]
459 fn global_phase_first_and_can_consume() {
460 let log: Log = Default::default();
461 let mut rt = InputRuntime::default();
462 rt.begin_frame();
463 let root = rt.root_layer();
464 rt.register_handler(
465 None,
466 EventPriority::Normal,
467 opts(false),
468 full_area(),
469 handler(&log, "global", EventResult::Consumed),
470 );
471 rt.register_handler(
472 Some(root),
473 EventPriority::High,
474 opts(false),
475 full_area(),
476 handler(&log, "layer", EventResult::Ignored),
477 );
478 rt.dispatch(key());
479 assert_eq!(*log.borrow(), ["global"]);
480 }
481
482 #[test]
484 fn global_ignored_does_not_truncate() {
485 let log: Log = Default::default();
486 let mut rt = InputRuntime::default();
487 rt.begin_frame();
488 let root = rt.root_layer();
489 rt.register_handler(
490 None,
491 EventPriority::Normal,
492 opts(false),
493 full_area(),
494 handler(&log, "global", EventResult::Ignored),
495 );
496 rt.register_handler(
497 Some(root),
498 EventPriority::Normal,
499 opts(false),
500 full_area(),
501 handler(&log, "layer", EventResult::Ignored),
502 );
503 rt.dispatch(key());
504 assert_eq!(*log.borrow(), ["global", "layer"]);
505 }
506
507 #[test]
509 fn inactive_layer_handler_skipped() {
510 let log: Log = Default::default();
511 let mut rt = InputRuntime::default();
512 rt.begin_frame();
513 let inactive = rt.push_layer(false, true); rt.register_handler(
515 Some(inactive.id),
516 EventPriority::Normal,
517 opts(false),
518 full_area(),
519 handler(&log, "inactive", EventResult::Ignored),
520 );
521 rt.dispatch(key());
522 assert!(log.borrow().is_empty());
523 }
524
525 #[test]
527 fn hit_test_skips_outside_area() {
528 let log: Log = Default::default();
529 let mut rt = InputRuntime::default();
530 rt.begin_frame();
531 let root = rt.root_layer();
532 let area = Rc::new(Cell::new(Rect::new(0, 0, 10, 10)));
533 rt.register_handler(
534 Some(root),
535 EventPriority::Normal,
536 opts(true),
537 area,
538 handler(&log, "hit", EventResult::Consumed),
539 );
540 rt.dispatch(mouse_at(50, 50)); assert!(log.borrow().is_empty());
542
543 rt.begin_frame(); let root2 = rt.root_layer();
545 let area2 = Rc::new(Cell::new(Rect::new(0, 0, 10, 10)));
546 rt.register_handler(
547 Some(root2),
548 EventPriority::Normal,
549 opts(true),
550 area2,
551 handler(&log, "hit", EventResult::Consumed),
552 );
553 rt.dispatch(mouse_at(5, 5)); assert_eq!(*log.borrow(), ["hit"]);
555 }
556}