leetcode_tui_shared/
tui.rs

1use crossterm::terminal::WindowSize;
2use crossterm::{
3    cursor,
4    terminal::{EnterAlternateScreen, LeaveAlternateScreen},
5};
6use std::mem;
7use std::{
8    io::Stdout,
9    ops::{Deref, DerefMut},
10};
11
12use color_eyre::eyre::Result;
13use ratatui::{prelude::CrosstermBackend as Backend, Terminal};
14
15pub type Frame<'a> = ratatui::Frame<'a>;
16
17pub struct Term {
18    pub terminal: Terminal<Backend<Stdout>>,
19}
20
21impl Term {
22    pub fn start() -> Result<Self> {
23        let terminal = Terminal::new(Backend::new(std::io::stdout()))?;
24        let mut term = Self { terminal };
25        term.enter()?;
26        Ok(term)
27    }
28
29    pub fn enter(&mut self) -> Result<()> {
30        crossterm::terminal::enable_raw_mode()?;
31        crossterm::execute!(std::io::stdout(), EnterAlternateScreen, cursor::Hide)?;
32        Ok(())
33    }
34
35    pub fn exit(&mut self) -> Result<()> {
36        if crossterm::terminal::is_raw_mode_enabled()? {
37            self.flush()?;
38            crossterm::execute!(std::io::stdout(), LeaveAlternateScreen, cursor::Show)?;
39            crossterm::terminal::disable_raw_mode()?;
40        }
41        Ok(())
42    }
43
44    pub fn suspend(&mut self) -> Result<()> {
45        self.exit()?;
46        Ok(())
47    }
48
49    pub fn resume(&mut self) -> Result<()> {
50        self.enter()?;
51        let size = self.size()?;
52        self.resize(ratatui::layout::Rect {
53            x: 0,
54            y: 0,
55            width: size.width,
56            height: size.height,
57        })?;
58        Ok(())
59    }
60
61    pub fn size() -> WindowSize {
62        let mut size = WindowSize {
63            rows: 0,
64            columns: 0,
65            width: 0,
66            height: 0,
67        };
68        if let Ok(s) = crossterm::terminal::window_size() {
69            let _ = mem::replace(&mut size, s);
70        }
71
72        if size.rows == 0 || size.columns == 0 {
73            if let Ok(s) = crossterm::terminal::size() {
74                size.columns = s.0;
75                size.rows = s.1;
76            }
77        }
78
79        // TODO: Use `CSI 14 t` to get the actual size of the terminal
80        // if size.width == 0 || size.height == 0 {}
81
82        size
83    }
84}
85
86impl Deref for Term {
87    type Target = ratatui::Terminal<Backend<std::io::Stdout>>;
88
89    fn deref(&self) -> &Self::Target {
90        &self.terminal
91    }
92}
93
94impl DerefMut for Term {
95    fn deref_mut(&mut self) -> &mut Self::Target {
96        &mut self.terminal
97    }
98}
99
100impl Drop for Term {
101    fn drop(&mut self) {
102        self.exit().unwrap();
103    }
104}