demo/
demo.rs

1use std::io::stdout;
2use crossterm::{terminal::{EnterAlternateScreen, enable_raw_mode, disable_raw_mode, LeaveAlternateScreen}, execute, event::{DisableMouseCapture, self, KeyEventKind, KeyEvent, KeyCode}, ExecutableCommand};
3use ratatui::{Terminal, prelude::{CrosstermBackend, Backend, Layout, Direction, Rect}, Frame, widgets::{Block, Borders}};
4use ratatui::layout::Constraint;
5use tui_textbox::{TextboxState, Textbox};
6
7pub struct App {
8    pub textbox_state: TextboxState,
9    pub textbox2_state: TextboxState,
10    pub focused_textbox: u8
11}
12
13impl App {
14    pub fn ui(&mut self, f: &mut Frame) {
15        let size = f.area();
16
17        let vertical_chunks = Layout::default()
18            .direction(Direction::Vertical)
19            .constraints([Constraint::Length(3), Constraint::Length(3), Constraint::Percentage(100)])
20            .split(size);
21
22        let block1 = Block::default().borders(Borders::ALL).title(format!("Use <TAB> to switch active textbox, <ESC> to exit demo"));
23        f.render_widget(block1, vertical_chunks[0]);
24
25        let textbox = Textbox::default()
26            .render_cursor(self.focused_textbox == 0);
27        let textbox_rect = Rect {
28            x: 1,
29            y: 1,
30            width: 15,
31            height: 1,
32        };
33        f.render_stateful_widget(textbox, textbox_rect, &mut self.textbox_state);
34
35        let block2 = Block::default().borders(Borders::ALL);
36        f.render_widget(block2, vertical_chunks[1]);
37
38        let textbox2 = Textbox::default()
39            .fg(ratatui::style::Color::Yellow)
40            .bg(ratatui::style::Color::Green)
41            .hint_color(ratatui::style::Color::LightGreen)
42            .cursor_color(ratatui::style::Color::DarkGray)
43            .render_cursor(self.focused_textbox == 1);
44        f.render_stateful_widget(textbox2, vertical_chunks[1].inner(ratatui::layout::Margin::new(1, 1)), &mut self.textbox2_state);
45    }
46
47    fn handle_events(&mut self, key: KeyEvent) -> std::io::Result<bool> {
48        match (key.code, key.modifiers) {
49            (KeyCode::Esc, _) => return Ok(true),
50            (KeyCode::Tab, _) => { self.focused_textbox = (self.focused_textbox + 1) % 2;  },
51            (key_code, key_modifiers) => {
52                match self.focused_textbox {
53                    0 => self.textbox_state.handle_events(key_code, key_modifiers),
54                    1 => self.textbox2_state.handle_events(key_code, key_modifiers),
55                    _ => {}
56                };
57            }
58        }
59        Ok(false)
60    }
61}
62
63pub fn main() -> std::io::Result<()> {
64
65    let mut app = App {
66        textbox_state: Default::default(),
67        textbox2_state: Default::default(),
68        focused_textbox: 0
69    };
70
71    stdout().execute(EnterAlternateScreen)?;
72    enable_raw_mode()?;
73    let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
74    terminal.clear()?;
75    let _res = run_app(&mut terminal, &mut app);
76
77    disable_raw_mode()?;
78    execute!(
79        terminal.backend_mut(),
80        LeaveAlternateScreen,
81        DisableMouseCapture
82        )?;
83    terminal.show_cursor()?;
84
85    Ok(())
86}
87
88
89pub fn run_app<B: Backend>(
90    terminal: &mut Terminal<B>,
91    app: &mut App,
92) -> std::io::Result<bool> {
93    loop {
94        terminal.draw(|f| app.ui(f))?;
95
96        if event::poll(std::time::Duration::from_millis(16))? {
97            if let event::Event::Key(key) = event::read()? {
98                if key.kind == KeyEventKind::Press {
99                    if let Ok(true) = app.handle_events(key) {
100                        return Ok(true);
101                    }
102                }
103            }
104        }
105    }
106}