tui_components/components/
input.rs

1use crate::{Component, Event, Spannable};
2use crossterm::event::KeyCode;
3use tui::buffer::Buffer;
4use tui::layout::Rect;
5use tui::style::Style;
6use tui::text::{Span, Spans};
7use tui::widgets::{Paragraph, Widget};
8
9#[derive(Debug, Default, Clone)]
10// todo: add cursor
11pub struct Input {
12    pub value: String,
13    pub error: Option<String>,
14    pub focused: bool,
15    text_style: Style,
16    editing_style: Style,
17    error_style: Style,
18}
19
20impl Input {
21    pub fn text_style(mut self, style: Style) -> Self {
22        self.text_style = style;
23        self
24    }
25
26    pub fn editing_style(mut self, style: Style) -> Self {
27        self.editing_style = style;
28        self
29    }
30
31    pub fn error_style(mut self, style: Style) -> Self {
32        self.error_style = style;
33        self
34    }
35}
36
37#[derive(Debug)]
38pub enum InputResponse {
39    None,
40    Edited { deletion: bool },
41    Submit,
42    Cancel,
43}
44
45impl Component for Input {
46    type Response = InputResponse;
47    type DrawResponse = ();
48
49    fn handle_event(&mut self, event: Event) -> Self::Response {
50        if let Event::Key(key_event) = event {
51            match key_event.code {
52                KeyCode::Char(c) => {
53                    self.value.push(c);
54                    InputResponse::Edited { deletion: false }
55                }
56                KeyCode::Backspace => {
57                    self.value.pop();
58                    InputResponse::Edited { deletion: true }
59                }
60                KeyCode::Enter => InputResponse::Submit,
61                KeyCode::Esc => InputResponse::Cancel,
62                _ => InputResponse::None,
63            }
64        } else {
65            InputResponse::None
66        }
67    }
68
69    fn draw(&mut self, rect: Rect, buf: &mut Buffer) {
70        let p = Paragraph::new(self.get_spans());
71        p.render(rect, buf);
72    }
73}
74
75impl Spannable for Input {
76    fn get_spans<'a, 'b>(&'a self) -> Spans<'b> {
77        let mut spans = Spans::default();
78        if self.focused {
79            spans.0.push(Span::raw("> "));
80            spans
81                .0
82                .push(Span::styled(self.value.clone(), self.editing_style));
83            if let Some(e) = &self.error {
84                spans
85                    .0
86                    .push(Span::styled(format!(" {}", e), self.error_style));
87            }
88        } else {
89            let style = if self.error.is_some() {
90                self.error_style
91            } else {
92                self.text_style
93            };
94            spans.0.push(Span::styled(self.value.clone(), style));
95        }
96        spans
97    }
98}