use std::io::{self, Stdout};
use std::panic;
use anyhow::Result;
use crossterm::cursor::{SetCursorStyle, Show};
use crossterm::event::{
DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture,
};
use crossterm::terminal::{
EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode,
};
use crossterm::{execute, queue};
use ratatui::backend::CrosstermBackend;
use ratatui::{Frame, Terminal};
type Backend = CrosstermBackend<Stdout>;
pub struct Tui {
terminal: Terminal<Backend>,
}
impl Tui {
pub fn new(mouse: bool) -> Result<Self> {
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableBracketedPaste)?;
if mouse {
execute!(stdout, EnableMouseCapture)?;
}
let terminal = Terminal::new(CrosstermBackend::new(stdout))?;
Ok(Self { terminal })
}
pub fn draw<F: FnOnce(&mut Frame)>(&mut self, render: F) -> Result<()> {
self.terminal.draw(render)?;
Ok(())
}
pub fn clear(&mut self) -> Result<()> {
self.terminal.clear()?;
Ok(())
}
pub fn set_cursor_shape(&mut self, bar: bool) -> Result<()> {
let style = if bar {
SetCursorStyle::SteadyBar
} else {
SetCursorStyle::SteadyBlock
};
queue!(io::stdout(), style)?;
Ok(())
}
}
impl Drop for Tui {
fn drop(&mut self) {
let _ = restore();
}
}
pub fn restore() -> io::Result<()> {
execute!(
io::stdout(),
DisableMouseCapture,
DisableBracketedPaste,
LeaveAlternateScreen,
SetCursorStyle::DefaultUserShape,
Show
)?;
disable_raw_mode()
}
pub fn install_panic_hook() {
let previous = panic::take_hook();
panic::set_hook(Box::new(move |info| {
let _ = restore();
previous(info);
}));
}