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