kimun_notes/components/dialogs/
modal_shell.rs1use ratatui::crossterm::event::{KeyCode, KeyEvent, MouseEvent};
2use ratatui::layout::Rect;
3
4use crate::components::clickable::is_press_outside;
5use crate::components::hint_row::HintRow;
6
7#[derive(Debug, Default)]
13pub struct ModalShell {
14 popup_rect: Rect,
15}
16
17impl ModalShell {
18 pub fn set(&mut self, rect: Rect) {
20 self.popup_rect = rect;
21 }
22
23 pub fn pointer_key(&self, m: &MouseEvent, hints: &[&HintRow]) -> Option<KeyEvent> {
27 if is_press_outside(m, self.popup_rect) {
28 return Some(KeyEvent::from(KeyCode::Esc));
29 }
30 hints.iter().find_map(|h| h.hit(m))
31 }
32}
33
34#[cfg(test)]
35mod tests {
36 use super::*;
37 use ratatui::crossterm::event::{KeyModifiers, MouseButton, MouseEventKind};
38
39 fn press(col: u16, row: u16) -> MouseEvent {
40 MouseEvent {
41 kind: MouseEventKind::Down(MouseButton::Left),
42 column: col,
43 row,
44 modifiers: KeyModifiers::NONE,
45 }
46 }
47
48 #[test]
49 fn outside_press_is_esc_inside_is_none() {
50 let mut shell = ModalShell::default();
51 assert_eq!(
52 shell.pointer_key(&press(0, 0), &[]),
53 None,
54 "not rendered yet"
55 );
56 shell.set(Rect::new(10, 5, 20, 8));
57 assert_eq!(
58 shell.pointer_key(&press(0, 0), &[]).map(|k| k.code),
59 Some(KeyCode::Esc)
60 );
61 assert_eq!(shell.pointer_key(&press(12, 6), &[]), None);
62 }
63
64 #[test]
65 fn hint_chip_press_yields_its_key_and_outside_wins() {
66 use ratatui::style::Style;
67 use ratatui::{Terminal, backend::TestBackend};
68 let theme = crate::settings::themes::Theme::gruvbox_dark();
69 let mut hints = HintRow::new(&[(KeyCode::Esc, "Esc", "Close")]);
70 let mut t = Terminal::new(TestBackend::new(40, 5)).unwrap();
71 t.draw(|f| hints.render(f, Rect::new(10, 2, 20, 1), Style::default(), &theme))
72 .unwrap();
73 let (col, row) =
74 crate::test_support::find_text(t.backend().buffer(), "Close").expect("chip drawn");
75 let mut shell = ModalShell::default();
76 shell.set(Rect::new(8, 1, 30, 3));
77 let key = |m| shell.pointer_key(&m, &[&hints]).map(|k| k.code);
78 assert_eq!(key(press(col, row)), Some(KeyCode::Esc), "chip key");
79 assert_eq!(key(press(0, 0)), Some(KeyCode::Esc), "outside is Esc");
80 assert_eq!(
81 key(press(col, row.saturating_sub(1))),
82 None,
83 "inside, off chip"
84 );
85 }
86}