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}