use std::io;
use std::time::Duration;
use crossterm::{
event::{self, Event, KeyCode, KeyModifiers},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{backend::CrosstermBackend, Terminal};
pub type Term = Terminal<CrosstermBackend<io::Stdout>>;
pub fn init() -> anyhow::Result<Term> {
enable_raw_mode()?;
execute!(io::stdout(), EnterAlternateScreen)?;
let terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;
Ok(terminal)
}
pub fn restore() {
let _ = execute!(io::stdout(), LeaveAlternateScreen);
let _ = disable_raw_mode();
}
pub trait TuiApp {
fn draw(&mut self, frame: &mut ratatui::Frame);
fn handle_key(&mut self, key: event::KeyEvent) -> anyhow::Result<bool>;
}
pub fn run_loop(terminal: &mut Term, poll_ms: u64, app: &mut dyn TuiApp) -> anyhow::Result<()> {
let timeout = Duration::from_millis(poll_ms);
loop {
terminal.draw(|f| app.draw(f))?;
if event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
if key.modifiers.contains(KeyModifiers::CONTROL) && key.code == KeyCode::Char('c') {
break;
}
if !app.handle_key(key)? {
break;
}
}
}
}
Ok(())
}