use crate::app::{App, AppResult};
use crate::event::EventHandler;
use crate::ui;
use anyhow::Context;
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
#[cfg(not(target_os = "windows"))]
use crossterm::event::{
KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
};
use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::Backend;
use ratatui::Terminal;
use std::io;
use std::panic;
#[derive(Debug)]
pub struct Tui<B: Backend> {
terminal: Terminal<B>,
pub events: EventHandler,
}
impl<B: Backend> Tui<B>
where
B::Error: std::error::Error + Send + Sync + 'static,
{
pub fn new(terminal: Terminal<B>, events: EventHandler) -> Self {
Self { terminal, events }
}
pub fn init(&mut self) -> AppResult<()> {
terminal::enable_raw_mode().context("Could not enable raw mode")?;
#[cfg(not(target_os = "windows"))]
crossterm::execute!(
io::stderr(),
EnterAlternateScreen,
EnableMouseCapture,
PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES)
)
.context("Could not initialize terminal, error in `crossterm::execute!`")?;
#[cfg(target_os = "windows")]
crossterm::execute!(io::stderr(), EnterAlternateScreen, EnableMouseCapture)
.context("Could not initialize terminal, error in `crossterm::execute!`")?;
let panic_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic| {
Self::reset().expect("failed to reset the terminal");
panic_hook(panic);
}));
self.terminal
.hide_cursor()
.context("Error when hiding terminal cursor")?;
self.terminal.clear().context("Could not clear terminal")?;
Ok(())
}
pub fn draw(&mut self, app: &mut App) -> AppResult<()> {
self.terminal
.draw(|frame| ui::render(frame, app))
.context("Failed to render the user interface")?;
Ok(())
}
fn reset() -> AppResult<()> {
terminal::disable_raw_mode().context("Failed to disable raw mode")?;
#[cfg(not(target_os = "windows"))]
crossterm::execute!(
io::stderr(),
LeaveAlternateScreen,
DisableMouseCapture,
PopKeyboardEnhancementFlags
)
.context("Failed resetting terminal, error during `crossterm::execute!`")?;
#[cfg(target_os = "windows")]
crossterm::execute!(io::stderr(), LeaveAlternateScreen, DisableMouseCapture)
.context("Failed resetting terminal, error during `crossterm::execute!`")?;
Ok(())
}
pub fn exit(&mut self) -> AppResult<()> {
Self::reset().context("Failed to reset terminal")?;
self.terminal
.show_cursor()
.context("Failed to show cursor")?;
Ok(())
}
}