// bibiman - a TUI for managing BibLaTeX databases // Copyright (C) 2024 lukeflo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . ///// use crate::frontend::app::App; use crate::frontend::event::EventHandler; use color_eyre::eyre::Result; use crossterm::event::{DisableMouseCapture, EnableMouseCapture}; use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}; use ratatui::backend::{Backend, CrosstermBackend}; // use ratatui::backend::CrosstermBackend as Backend; use ratatui::Terminal; use std::io::{self, stdout, Stdout}; use std::panic; use std::{ ops::{Deref, DerefMut}, time::Duration, }; // pub type IO = std::io::{{crossterm_io | title_case}}; // pub fn io() -> IO { // std::io::{{crossterm_io}}() // } /// Representation of a terminal user interface. /// /// It is responsible for setting up the terminal, /// initializing the interface and handling the draw events. #[derive(Debug)] pub struct Tui { /// Interface to the Terminal. terminal: ratatui::Terminal>, /// Terminal event handler. pub events: EventHandler, } impl Tui { /// Constructs a new instance of [`Tui`]. pub fn new() -> Result { let backend = CrosstermBackend::new(stdout()); let terminal = Terminal::new(backend)?; let events = EventHandler::new(250); Ok(Self { terminal, events }) } /// Initializes the terminal interface. /// /// It enables the raw mode and sets terminal properties. pub fn init(&mut self) -> Result<()> { terminal::enable_raw_mode()?; crossterm::execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture)?; // Define a custom panic hook to reset the terminal properties. // This way, you won't have your terminal messed up if an unexpected error happens. 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()?; self.terminal.clear()?; Ok(()) } /// [`Draw`] the terminal interface by [`rendering`] the widgets. /// /// [`Draw`]: ratatui::Terminal::draw /// [`rendering`]: crate::ui::render pub fn draw(&mut self, app: &mut App) -> Result<()> { // self.terminal.draw(|frame| ui::render(app, frame))?; self.terminal .draw(|frame| frame.render_widget(app, frame.area()))?; Ok(()) } /// Resets the terminal interface. /// /// This function is also used for the panic hook to revert /// the terminal properties if unexpected errors occur. fn reset() -> Result<()> { terminal::disable_raw_mode()?; crossterm::execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?; Ok(()) } /// Exits the terminal interface. /// /// It disables the raw mode and reverts back the terminal properties. pub fn exit(&mut self) -> Result<()> { Self::reset()?; self.terminal.show_cursor()?; Ok(()) } }