Skip to main content

itools_tui/components/
input.rs

1//! 文本框组件
2
3use crate::{event::Event, layout::Rect, render::Frame, style::Style};
4use crossterm::event::{KeyCode, KeyEvent};
5
6/// 文本框组件
7pub struct Input {
8    /// 输入内容
9    value: String,
10    /// 提示文本
11    placeholder: Option<String>,
12    /// 文本框样式
13    style: Style,
14    /// 光标样式
15    cursor_style: Style,
16    /// 光标位置
17    cursor_pos: usize,
18    /// 是否获得焦点
19    focused: bool,
20    /// 输入回调
21    on_change: Option<Box<dyn Fn(&str)>>,
22}
23
24impl Input {
25    /// 创建新的文本框
26    pub fn new() -> Self {
27        Self {
28            value: String::new(),
29            placeholder: None,
30            style: Style::new(),
31            cursor_style: Style::new().reversed(),
32            cursor_pos: 0,
33            focused: false,
34            on_change: None,
35        }
36    }
37
38    /// 设置初始值
39    pub fn value(mut self, value: &str) -> Self {
40        self.value = value.to_string();
41        self.cursor_pos = self.value.len();
42        self
43    }
44
45    /// 设置提示文本
46    pub fn placeholder(mut self, placeholder: &str) -> Self {
47        self.placeholder = Some(placeholder.to_string());
48        self
49    }
50
51    /// 设置样式
52    pub fn style(mut self, style: Style) -> Self {
53        self.style = style;
54        self
55    }
56
57    /// 设置光标样式
58    pub fn cursor_style(mut self, style: Style) -> Self {
59        self.cursor_style = style;
60        self
61    }
62
63    /// 设置是否获得焦点
64    pub fn focused(mut self, focused: bool) -> Self {
65        self.focused = focused;
66        self
67    }
68
69    /// 设置输入回调
70    pub fn on_change<F: Fn(&str) + 'static>(mut self, f: F) -> Self {
71        self.on_change = Some(Box::new(f));
72        self
73    }
74
75    /// 获取输入值
76    pub fn get_value(&self) -> &str {
77        &self.value
78    }
79
80    /// 处理键盘事件
81    fn handle_key(&mut self, key: KeyEvent) {
82        match key.code {
83            KeyCode::Backspace => {
84                if self.cursor_pos > 0 {
85                    self.value.remove(self.cursor_pos - 1);
86                    self.cursor_pos -= 1;
87                    if let Some(callback) = &self.on_change {
88                        callback(&self.value);
89                    }
90                }
91            }
92            KeyCode::Delete => {
93                if self.cursor_pos < self.value.len() {
94                    self.value.remove(self.cursor_pos);
95                    if let Some(callback) = &self.on_change {
96                        callback(&self.value);
97                    }
98                }
99            }
100            KeyCode::Left => {
101                if self.cursor_pos > 0 {
102                    self.cursor_pos -= 1;
103                }
104            }
105            KeyCode::Right => {
106                if self.cursor_pos < self.value.len() {
107                    self.cursor_pos += 1;
108                }
109            }
110            KeyCode::Home => {
111                self.cursor_pos = 0;
112            }
113            KeyCode::End => {
114                self.cursor_pos = self.value.len();
115            }
116            KeyCode::Char(c) => {
117                self.value.insert(self.cursor_pos, c);
118                self.cursor_pos += 1;
119                if let Some(callback) = &self.on_change {
120                    callback(&self.value);
121                }
122            }
123            _ => {}
124        }
125    }
126}
127
128impl super::Component for Input {
129    fn render(&self, frame: &mut Frame, area: Rect) {
130        let text =
131            if self.value.is_empty() && self.placeholder.is_some() { self.placeholder.as_ref().unwrap() } else { &self.value };
132
133        frame.render_input(text, self.cursor_pos, area, self.style.clone(), self.cursor_style.clone(), self.focused);
134    }
135
136    fn handle_event(&mut self, event: &Event) -> bool {
137        if !self.focused {
138            return false;
139        }
140
141        match event {
142            Event::Key(key_event) => {
143                self.handle_key(*key_event);
144                true
145            }
146            _ => false,
147        }
148    }
149}