Skip to main content

itools_tui/components/
select_menu.rs

1//! 选择菜单组件
2
3use crate::{event::Event, layout::Rect, render::Frame, style::Style};
4use crossterm::event::KeyCode;
5
6/// 选择菜单项
7pub struct SelectItem {
8    /// 项文本
9    text: String,
10    /// 项样式
11    style: Style,
12    /// 选中样式
13    selected_style: Style,
14}
15
16impl SelectItem {
17    /// 创建新的选择菜单项
18    pub fn new(text: &str) -> Self {
19        Self { text: text.to_string(), style: Style::new(), selected_style: Style::new().reversed() }
20    }
21
22    /// 设置项样式
23    pub fn style(mut self, style: Style) -> Self {
24        self.style = style;
25        self
26    }
27
28    /// 设置选中样式
29    pub fn selected_style(mut self, style: Style) -> Self {
30        self.selected_style = style;
31        self
32    }
33
34    /// 获取项文本
35    pub fn text(&self) -> &str {
36        &self.text
37    }
38
39    /// 获取项样式
40    pub fn get_style(&self) -> &Style {
41        &self.style
42    }
43
44    /// 获取选中样式
45    pub fn get_selected_style(&self) -> &Style {
46        &self.selected_style
47    }
48}
49
50/// 选择菜单组件
51pub struct SelectMenu {
52    /// 选择提示
53    prompt: String,
54    /// 选择项
55    items: Vec<SelectItem>,
56    /// 选中索引
57    selected: usize,
58    /// 菜单样式
59    style: Style,
60    /// 选中回调
61    on_select: Option<Box<dyn Fn(usize)>>,
62    /// 取消回调
63    on_cancel: Option<Box<dyn Fn()>>,
64}
65
66impl SelectMenu {
67    /// 创建新的选择菜单
68    pub fn new(prompt: &str, items: Vec<SelectItem>) -> Self {
69        Self { prompt: prompt.to_string(), items, selected: 0, style: Style::new(), on_select: None, on_cancel: None }
70    }
71
72    /// 设置样式
73    pub fn style(mut self, style: Style) -> Self {
74        self.style = style;
75        self
76    }
77
78    /// 设置选中回调
79    pub fn on_select<F: Fn(usize) + 'static>(mut self, f: F) -> Self {
80        self.on_select = Some(Box::new(f));
81        self
82    }
83
84    /// 设置取消回调
85    pub fn on_cancel<F: Fn() + 'static>(mut self, f: F) -> Self {
86        self.on_cancel = Some(Box::new(f));
87        self
88    }
89
90    /// 获取选中项索引
91    pub fn get_selected(&self) -> usize {
92        self.selected
93    }
94
95    /// 处理键盘事件
96    fn handle_key(&mut self, key: KeyCode) -> bool {
97        match key {
98            KeyCode::Up => {
99                if self.selected > 0 {
100                    self.selected -= 1;
101                }
102                true
103            }
104            KeyCode::Down => {
105                if self.selected < self.items.len() - 1 {
106                    self.selected += 1;
107                }
108                true
109            }
110            KeyCode::Enter => {
111                if let Some(callback) = &self.on_select {
112                    callback(self.selected);
113                }
114                true
115            }
116            KeyCode::Esc => {
117                if let Some(callback) = &self.on_cancel {
118                    callback();
119                }
120                true
121            }
122            _ => false,
123        }
124    }
125}
126
127impl super::Component for SelectMenu {
128    fn render(&self, frame: &mut Frame, area: Rect) {
129        frame.render_select_menu(&self.prompt, &self.items, self.selected, area, self.style.clone());
130    }
131
132    fn handle_event(&mut self, event: &Event) -> bool {
133        match event {
134            Event::Key(key_event) => self.handle_key(key_event.code),
135            _ => false,
136        }
137    }
138}