chatty_rs/app/ui/
input_box.rs1use crate::models::Event;
2use ratatui::{
3 Frame,
4 layout::Rect,
5 style::{Color, Style, Stylize},
6 text::Line,
7 widgets::{Block, BorderType, Borders, Clear, Padding, Widget},
8};
9use tui_textarea::{CursorMove, TextArea};
10
11pub struct InputBox<'a> {
12 showing: bool,
13 text: String,
14 input: TextArea<'a>,
15
16 title: String,
17 placeholder: String,
18}
19
20impl<'a> InputBox<'a> {
21 pub fn with_title(mut self, title: &str) -> InputBox<'a> {
22 self.set_title(title);
23 self
24 }
25
26 pub fn with_placeholder(mut self, placeholder: &str) -> InputBox<'a> {
27 self.set_placeholder(placeholder);
28 self
29 }
30
31 pub fn set_title(&mut self, title: &str) {
32 if !title.is_empty() {
33 self.title = title.to_string();
34 }
35 }
36
37 pub fn set_placeholder(&mut self, placeholder: &str) {
38 if !placeholder.is_empty() {
39 self.placeholder = placeholder.to_string();
40 }
41 }
42
43 pub fn showing(&self) -> bool {
44 self.showing
45 }
46
47 pub fn open(&mut self, text: impl Into<String>) {
48 self.text = text.into();
49 self.input = self.build_input();
50 self.showing = true;
51 }
52
53 pub fn close(&mut self) -> Option<String> {
54 if self.showing {
55 self.showing = false;
56 let text = self.input.lines().join("\n");
57 return Some(text);
58 }
59 None
60 }
61
62 pub fn render(&mut self, f: &mut Frame, area: Rect) {
63 if !self.showing {
64 return;
65 }
66
67 f.render_widget(Clear, area);
68 self.input.render(area, f.buffer_mut());
69 }
70
71 pub fn handle_key_event(&mut self, event: &Event) {
72 match event {
73 Event::KeyboardCharInput(input) => {
74 self.input.input(input.clone());
75 }
76 Event::KeyboardPaste(text) => {
77 self.input.set_yank_text(text.replace('\r', "\n"));
78 self.input.paste();
79 }
80 _ => {}
81 }
82 }
83
84 fn build_input(&self) -> TextArea<'a> {
85 let mut text_area = TextArea::new(vec![self.text.clone()]);
86 let block = Block::default()
87 .title(Line::from(self.title.clone()).bold())
88 .borders(Borders::ALL)
89 .border_type(BorderType::Rounded)
90 .border_style(Style::default().fg(Color::LightMagenta))
91 .padding(Padding::symmetric(1, 0));
92 text_area.set_block(block);
93 text_area.set_placeholder_text(&self.placeholder);
94 text_area.move_cursor(CursorMove::End);
95 text_area
96 }
97}
98
99impl Default for InputBox<'_> {
100 fn default() -> Self {
101 Self {
102 showing: false,
103 text: String::new(),
104 input: TextArea::default(),
105 title: "Input".to_string(),
106 placeholder: "Type here...".to_string(),
107 }
108 }
109}
110
111pub fn build_area(area: Rect, width: u16) -> Rect {
112 let x = area.x + (area.width - width) / 2;
113 let y = area.y + (area.height - (area.height as f32 * 0.7).ceil() as u16) / 2;
114 Rect::new(x, y, width, 3)
115}