use std::io::{Stdout, stdout};
use std::panic::PanicHookInfo;
use std::sync::Arc;
use std::time::Duration;
use anyhow::Result;
use crossterm::ExecutableCommand;
use crossterm::cursor::Show;
use crossterm::event;
use crossterm::terminal::{
EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode,
};
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;
pub mod config_view;
pub mod selection_view;
pub mod status_view;
fn restore_terminal() {
let _ = disable_raw_mode();
let _ = stdout().execute(LeaveAlternateScreen);
let _ = stdout().execute(Show);
}
pub(crate) struct Tui {
pub terminal: Terminal<CrosstermBackend<Stdout>>,
prior_hook: Arc<dyn Fn(&PanicHookInfo<'_>) + Sync + Send + 'static>,
}
impl Tui {
pub fn new() -> Result<Self> {
let prior_hook: Arc<dyn Fn(&PanicHookInfo<'_>) + Sync + Send> =
Arc::from(std::panic::take_hook());
let hook_for_panic = Arc::clone(&prior_hook);
std::panic::set_hook(Box::new(move |info| {
restore_terminal();
hook_for_panic(info);
}));
enable_raw_mode()?;
stdout().execute(EnterAlternateScreen)?;
match Terminal::new(CrosstermBackend::new(stdout())) {
Ok(terminal) => Ok(Self {
terminal,
prior_hook,
}),
Err(e) => {
restore_terminal();
std::panic::set_hook(Box::new(move |info| prior_hook(info)));
Err(e.into())
}
}
}
pub fn drain_stale_input(&self, settle: Duration) {
std::thread::sleep(settle);
while matches!(event::poll(Duration::from_millis(50)), Ok(true)) {
let _ = event::read();
}
}
}
impl Drop for Tui {
fn drop(&mut self) {
restore_terminal();
let _ = self.terminal.show_cursor();
let prior = Arc::clone(&self.prior_hook);
std::panic::set_hook(Box::new(move |info| prior(info)));
}
}