1*7b32f508SLoGin use crate::app::{App, AppResult}; 2*7b32f508SLoGin use crate::event::EventHandler; 3*7b32f508SLoGin use crate::ui; 4*7b32f508SLoGin use crossterm::event::DisableMouseCapture; 5*7b32f508SLoGin use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}; 6*7b32f508SLoGin use std::io; 7*7b32f508SLoGin use std::panic; 8*7b32f508SLoGin 9*7b32f508SLoGin use ratatui::backend::Backend; 10*7b32f508SLoGin use ratatui::Terminal; 11*7b32f508SLoGin 12*7b32f508SLoGin /// Representation of a terminal user interface. 13*7b32f508SLoGin /// 14*7b32f508SLoGin /// It is responsible for setting up the terminal, 15*7b32f508SLoGin /// initializing the interface and handling the draw events. 16*7b32f508SLoGin #[derive(Debug)] 17*7b32f508SLoGin pub struct Tui<B: Backend> { 18*7b32f508SLoGin /// Interface to the Terminal. 19*7b32f508SLoGin terminal: Terminal<B>, 20*7b32f508SLoGin /// Terminal event handler. 21*7b32f508SLoGin pub events: EventHandler, 22*7b32f508SLoGin } 23*7b32f508SLoGin 24*7b32f508SLoGin impl<B: Backend> Tui<B> { 25*7b32f508SLoGin /// Constructs a new instance of [`Tui`]. new(terminal: Terminal<B>, events: EventHandler) -> Self26*7b32f508SLoGin pub fn new(terminal: Terminal<B>, events: EventHandler) -> Self { 27*7b32f508SLoGin Self { terminal, events } 28*7b32f508SLoGin } 29*7b32f508SLoGin 30*7b32f508SLoGin /// Initializes the terminal interface. 31*7b32f508SLoGin /// 32*7b32f508SLoGin /// It enables the raw mode and sets terminal properties. init(&mut self) -> AppResult<()>33*7b32f508SLoGin pub fn init(&mut self) -> AppResult<()> { 34*7b32f508SLoGin terminal::enable_raw_mode()?; 35*7b32f508SLoGin crossterm::execute!(io::stderr(), EnterAlternateScreen, DisableMouseCapture)?; 36*7b32f508SLoGin 37*7b32f508SLoGin // Define a custom panic hook to reset the terminal properties. 38*7b32f508SLoGin // This way, you won't have your terminal messed up if an unexpected error happens. 39*7b32f508SLoGin let panic_hook = panic::take_hook(); 40*7b32f508SLoGin panic::set_hook(Box::new(move |panic| { 41*7b32f508SLoGin Self::reset().expect("failed to reset the terminal"); 42*7b32f508SLoGin panic_hook(panic); 43*7b32f508SLoGin })); 44*7b32f508SLoGin 45*7b32f508SLoGin self.terminal.hide_cursor()?; 46*7b32f508SLoGin self.terminal.clear()?; 47*7b32f508SLoGin Ok(()) 48*7b32f508SLoGin } 49*7b32f508SLoGin 50*7b32f508SLoGin /// [`Draw`] the terminal interface by [`rendering`] the widgets. 51*7b32f508SLoGin /// 52*7b32f508SLoGin /// [`Draw`]: ratatui::Terminal::draw 53*7b32f508SLoGin /// [`rendering`]: crate::ui:render draw(&mut self, app: &mut App) -> AppResult<()>54*7b32f508SLoGin pub fn draw(&mut self, app: &mut App) -> AppResult<()> { 55*7b32f508SLoGin self.terminal.draw(|frame| ui::render(app, frame))?; 56*7b32f508SLoGin Ok(()) 57*7b32f508SLoGin } 58*7b32f508SLoGin 59*7b32f508SLoGin /// Resets the terminal interface. 60*7b32f508SLoGin /// 61*7b32f508SLoGin /// This function is also used for the panic hook to revert 62*7b32f508SLoGin /// the terminal properties if unexpected errors occur. reset() -> AppResult<()>63*7b32f508SLoGin fn reset() -> AppResult<()> { 64*7b32f508SLoGin terminal::disable_raw_mode()?; 65*7b32f508SLoGin crossterm::execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?; 66*7b32f508SLoGin Ok(()) 67*7b32f508SLoGin } 68*7b32f508SLoGin 69*7b32f508SLoGin /// Exits the terminal interface. 70*7b32f508SLoGin /// 71*7b32f508SLoGin /// It disables the raw mode and reverts back the terminal properties. exit(&mut self) -> AppResult<()>72*7b32f508SLoGin pub fn exit(&mut self) -> AppResult<()> { 73*7b32f508SLoGin Self::reset()?; 74*7b32f508SLoGin self.terminal.show_cursor()?; 75*7b32f508SLoGin Ok(()) 76*7b32f508SLoGin } 77*7b32f508SLoGin } 78