sim_restaurant/tui/
terminal.rs

1// Copyright (c) 2026 Graphcore Ltd. All rights reserved.
2
3use std::{io, panic};
4
5use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
6use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen};
7use ratatui::Terminal;
8use ratatui::backend::Backend;
9
10use crate::tui::event::AppResult;
11
12#[derive(Debug)]
13pub struct Tui<B: Backend> {
14    terminal: Terminal<B>,
15}
16
17impl<B: Backend> Tui<B> {
18    pub fn new(terminal: Terminal<B>) -> Self {
19        Self { terminal }
20    }
21
22    pub fn init(&mut self) -> AppResult<()>
23    where
24        <B as Backend>::Error: 'static,
25    {
26        terminal::enable_raw_mode()?;
27        crossterm::execute!(io::stderr(), EnterAlternateScreen, EnableMouseCapture)?;
28
29        let panic_hook = panic::take_hook();
30        panic::set_hook(Box::new(move |panic| {
31            Self::reset().expect("failed to reset the terminal");
32            panic_hook(panic);
33        }));
34
35        self.terminal.hide_cursor()?;
36        self.terminal.clear()?;
37        Ok(())
38    }
39
40    pub fn draw(&mut self, app: &mut crate::tui::app::App) -> AppResult<()>
41    where
42        <B as Backend>::Error: 'static,
43    {
44        self.terminal
45            .draw(|frame| crate::tui::ui::render(app, frame))?;
46        Ok(())
47    }
48
49    fn reset() -> AppResult<()> {
50        terminal::disable_raw_mode()?;
51        crossterm::execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
52        Ok(())
53    }
54
55    pub fn exit(&mut self) -> AppResult<()>
56    where
57        <B as Backend>::Error: 'static,
58    {
59        Self::reset()?;
60        self.terminal.show_cursor()?;
61        Ok(())
62    }
63}