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
21pub struct App {
23 pub dialog: Dialog,
24 pub text: String,
25 pub exit: bool,
26}
27
28fn main() {
29 let mut app = App {
31 dialog: Dialog::default(),
32 text: "Hello world!".to_string(),
33 exit: false,
34 };
35
36 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 if let Err(e) = run(&mut app, &mut terminal) {
57 eprintln!("Error running app: {e}");
58 return;
59 }
60
61 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
72fn run(
74 app: &mut App,
75 terminal: &mut Terminal<CrosstermBackend<Stdout>>,
76) -> Result<(), std::io::Error> {
77 while !app.exit {
78 terminal.draw(|frame| render(frame, app))?;
80
81 match event::read()? {
83 Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
84 if app.dialog.open {
87 app.dialog.key_action(&key_event.code);
88 if app.dialog.submitted {
89 app.text = app.dialog.submitted_input.clone();
91 }
92 } else {
94 match key_event.code {
95 KeyCode::Char('q') => app.exit = true,
96 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 let horizontal = Layout::horizontal([Constraint::Fill(1), Constraint::Length(SIDEBAR_SIZE)]);
111
112 let [left, right] = horizontal.areas(frame.area());
113
114 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 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}