1use std::io;
4
5use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
6use crossterm::execute;
7use crossterm::terminal::{
8 disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
9};
10use ratatui::backend::CrosstermBackend;
11use ratatui::Terminal;
12
13use crate::filter::{FilterApp, Outcome};
14
15pub fn run_tui(mut app: FilterApp) -> io::Result<(FilterApp, Outcome)> {
20 let mut stdout = io::stdout();
21 enable_raw_mode()?;
22 execute!(stdout, EnterAlternateScreen)?;
23
24 let backend = CrosstermBackend::new(stdout);
25 let mut terminal = Terminal::new(backend)?;
26
27 let result = event_loop(&mut terminal, &mut app);
28
29 disable_raw_mode()?;
30 execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
31
32 result?;
33 let outcome = app.outcome;
34 Ok((app, outcome))
35}
36
37fn event_loop(
38 terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
39 app: &mut FilterApp,
40) -> io::Result<()> {
41 while !app.should_quit {
42 terminal.draw(|frame| crate::filter::ui::render(frame, app))?;
43 match crossterm::event::read()? {
44 Event::Key(key) => handle_key(app, key),
45 Event::Resize(_, _) => {}
46 _ => {}
47 }
48 }
49 Ok(())
50}
51
52pub fn handle_key(app: &mut FilterApp, key: KeyEvent) {
53 match key.code {
54 KeyCode::Esc => {
55 app.outcome = Outcome::Discard;
56 app.should_quit = true;
57 }
58 KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
59 app.outcome = Outcome::Discard;
60 app.should_quit = true;
61 }
62 KeyCode::Enter => {
63 app.outcome = Outcome::Emit;
64 app.should_quit = true;
65 }
66 KeyCode::Up => app.select_prev(),
67 KeyCode::Down => app.select_next(),
68 KeyCode::Char('i') if key.modifiers.contains(KeyModifiers::ALT) => {
69 app.toggle_case_insensitive();
70 }
71 KeyCode::Char('v') if key.modifiers.contains(KeyModifiers::ALT) => {
72 app.toggle_invert();
73 }
74 KeyCode::Backspace => {
75 app.pattern_editor.delete_back();
76 app.recompute();
77 }
78 KeyCode::Left => app.pattern_editor.move_left(),
79 KeyCode::Right => app.pattern_editor.move_right(),
80 KeyCode::Home => app.pattern_editor.move_home(),
81 KeyCode::End => app.pattern_editor.move_end(),
82 KeyCode::Char(c) if !key.modifiers.contains(KeyModifiers::CONTROL) => {
83 app.pattern_editor.insert_char(c);
84 app.recompute();
85 }
86 _ => {}
87 }
88}