miv_editor/
tui.rs

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