basic/
basic.rs

1use std::io::{Stdout, stdout};
2
3use crossterm::{
4    event::{self, Event, KeyCode, KeyEventKind},
5    execute,
6    terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
7};
8use ratatui::{
9    Frame, Terminal,
10    backend::CrosstermBackend,
11    layout::{Constraint, Layout},
12    prelude::Stylize,
13    symbols::border,
14    text::Line,
15    widgets::{Borders, Paragraph, block::*},
16};
17use tui_dialog::{Dialog, centered_rect};
18
19pub const SIDEBAR_SIZE: u16 = 40;
20
21/// The data and state of the app.
22pub struct App {
23    pub dialog: Dialog,
24    pub text: String,
25    pub exit: bool,
26}
27
28fn main() {
29    // Initialize the app.
30    let mut app = App {
31        dialog: Dialog::default(),
32        text: "Hello world!".to_string(),
33        exit: false,
34    };
35
36    // Initialize the terminal.
37    let mut terminal = match Terminal::new(CrosstermBackend::new(stdout())) {
38        Ok(v) => v,
39        Err(e) => {
40            eprintln!("Error creating new terminal interface: {e}");
41            return;
42        }
43    };
44
45    if let Err(e) = execute!(terminal.backend_mut(), EnterAlternateScreen) {
46        eprintln!("Errow switching to alternate screen: {e}");
47        return;
48    }
49
50    if let Err(e) = enable_raw_mode() {
51        eprintln!("Error entering raw mode: {e}");
52        return;
53    }
54
55    // Run the app.
56    if let Err(e) = run(&mut app, &mut terminal) {
57        eprintln!("Error running app: {e}");
58        return;
59    }
60
61    // Restore the terminal to its original state, then exit.
62    if let Err(e) = disable_raw_mode() {
63        eprintln!("Error disabling raw mode: {e}");
64        return;
65    }
66
67    if let Err(e) = execute!(terminal.backend_mut(), LeaveAlternateScreen) {
68        eprintln!("Error leaving alternate screen: {e}");
69    }
70}
71
72/// Run the application's main loop until the user quits.
73fn run(
74    app: &mut App,
75    terminal: &mut Terminal<CrosstermBackend<Stdout>>,
76) -> Result<(), std::io::Error> {
77    while !app.exit {
78        // Redraw the frame every time.
79        terminal.draw(|frame| render(frame, app))?;
80
81        // Handle user input.
82        match event::read()? {
83            Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
84                // Pass all `key_event.code`s to a dialog if open.
85                // (The dialog handles closing itself, when the user presses `Enter` or `Esc`.)
86                if app.dialog.open {
87                    app.dialog.key_action(&key_event.code);
88                    if app.dialog.submitted {
89                        // Here is where you'd do something more significant.
90                        app.text = app.dialog.submitted_input.clone();
91                    }
92                // Otherwise handle them here.
93                } else {
94                    match key_event.code {
95                        KeyCode::Char('q') => app.exit = true,
96                        // Your app needs to open the dialog.
97                        KeyCode::Char('d') => app.dialog.open = true,
98                        _ => (),
99                    }
100                }
101            }
102            _ => (),
103        };
104    }
105    Ok(())
106}
107
108fn render(frame: &mut Frame, app: &mut App) {
109    // Layout the rectangles of the UI.
110    let horizontal = Layout::horizontal([Constraint::Fill(1), Constraint::Length(SIDEBAR_SIZE)]);
111
112    let [left, right] = horizontal.areas(frame.area());
113
114    // Left - main content.
115    let main_block = Block::default()
116        .title_top(Line::from(" tui-dialog example ").bold().centered())
117        .borders(Borders::ALL)
118        .border_set(border::THICK)
119        .padding(Padding::horizontal(1));
120
121    // Right - Controls menu
122    let controls_block = Block::default()
123        .title_top(Line::from(" Controls").centered().bold())
124        .borders(Borders::ALL)
125        .border_set(border::THICK);
126
127    let controls_content: Vec<Line<'_>> = vec![
128        vec!["d".blue(), " Show dialog".into()].into(),
129        vec!["q".blue(), " Quit".into()].into(),
130    ];
131
132    let controls = Paragraph::new(controls_content).block(controls_block);
133    frame.render_widget(controls, right);
134    frame.render_widget(Paragraph::new(app.text.clone()).block(main_block), left);
135
136    let dialog_area = centered_rect(frame.area(), 45, 5, -(SIDEBAR_SIZE as i16), 0);
137    frame.render_widget(app.dialog.title_top("Enter text:"), dialog_area);
138}