use crate::tui::input;
use crate::tui::state::AppState;
use crate::tui::ui;
use crate::tui::util;
use anyhow::Result;
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
use crossterm::execute;
use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
use std::io::{stdout, Stdout};
type Tui = Terminal<CrosstermBackend<Stdout>>;
fn install_panic_hook() {
let previous = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let _ = restore_terminal_raw();
previous(info);
}));
}
fn restore_terminal_raw() -> Result<()> {
let raw = disable_raw_mode();
let screen = execute!(
stdout(),
LeaveAlternateScreen,
DisableMouseCapture,
crossterm::cursor::Show
);
raw?;
screen?;
Ok(())
}
fn restore_terminal(terminal: &mut Tui) -> Result<()> {
let raw = disable_raw_mode();
let screen = execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
);
let cursor = terminal.show_cursor();
raw?;
screen?;
cursor?;
Ok(())
}
pub fn run() -> Result<()> {
install_panic_hook();
enable_raw_mode()?;
let mut stdout = stdout();
if let Err(e) = execute!(stdout, EnterAlternateScreen, EnableMouseCapture) {
let _ = restore_terminal_raw();
return Err(e.into());
}
let backend = CrosstermBackend::new(stdout);
let mut terminal = match Terminal::new(backend) {
Ok(terminal) => terminal,
Err(e) => {
let _ = restore_terminal_raw();
return Err(e.into());
}
};
let result = run_event_loop(&mut terminal);
let restored = restore_terminal(&mut terminal);
result.and(restored)
}
fn run_event_loop(terminal: &mut Tui) -> Result<()> {
let mut app = AppState::new()?;
loop {
terminal.draw(|f| ui::draw_main_ui(f, &mut app))?;
if let Some(key) = util::next_key_press(std::time::Duration::from_millis(200))? {
if input::handle_key_event(terminal, &mut app, key)? {
return Ok(());
}
}
}
}