Skip to main content

carch_core/ui/
events.rs

1use crossterm::event::{KeyCode, KeyEvent, MouseEvent, MouseEventKind};
2use log::info;
3
4use super::popups::run_script::RunScriptPopup;
5use super::state::{App, AppMode, FocusedPanel};
6
7impl<'a> App<'a> {
8    pub fn handle_search_input(&mut self, key: KeyEvent) {
9        match key.code {
10            KeyCode::Esc => self.toggle_search_mode(),
11            KeyCode::Enter => {
12                if !self.search.results.is_empty() {
13                    let selected_item = self.search.results[self.search.selected_idx].clone();
14                    if self.log_mode {
15                        info!(
16                            "Selected script from search: {}/{}",
17                            selected_item.item.category, selected_item.item.name
18                        );
19                    }
20
21                    if let Some(category_idx) =
22                        self.categories.items.iter().position(|c| *c == selected_item.item.category)
23                    {
24                        self.categories.state.select(Some(category_idx));
25                        self.update_script_list();
26
27                        if let Some(script_idx) = self
28                            .scripts
29                            .items
30                            .iter()
31                            .position(|s| s.name == selected_item.item.name)
32                        {
33                            self.scripts.state.select(Some(script_idx));
34                        }
35                    }
36
37                    self.update_preview();
38                    self.toggle_search_mode();
39                    self.focused_panel = FocusedPanel::Scripts;
40                    self.mode = AppMode::Normal;
41                }
42            }
43            KeyCode::Down => {
44                if !self.search.results.is_empty() {
45                    self.search.selected_idx =
46                        (self.search.selected_idx + 1) % self.search.results.len();
47                }
48            }
49            KeyCode::Up => {
50                if !self.search.results.is_empty() {
51                    self.search.selected_idx = if self.search.selected_idx > 0 {
52                        self.search.selected_idx - 1
53                    } else {
54                        self.search.results.len() - 1
55                    };
56                }
57            }
58            KeyCode::Tab => {
59                if let Some(autocomplete) = self.search.autocomplete.take() {
60                    self.search.input = autocomplete;
61                    self.search.cursor_position = self.search.input.len();
62                    self.perform_search();
63                    self.update_autocomplete();
64                }
65            }
66            KeyCode::Char(c) => {
67                self.search.input.push(c);
68                self.search.cursor_position += 1;
69                self.perform_search();
70                self.update_autocomplete();
71                self.search.selected_idx = 0;
72            }
73            KeyCode::Backspace => {
74                if self.search.cursor_position > 0 {
75                    self.search.input.remove(self.search.cursor_position - 1);
76                    self.search.cursor_position -= 1;
77                    self.perform_search();
78                    self.update_autocomplete();
79                    self.search.selected_idx = 0;
80                }
81            }
82            KeyCode::Left => {
83                if self.search.cursor_position > 0 {
84                    self.search.cursor_position -= 1;
85                }
86                self.search.autocomplete = None;
87            }
88            KeyCode::Right => {
89                if self.search.cursor_position < self.search.input.len() {
90                    self.search.cursor_position += 1;
91                }
92                if self.search.cursor_position == self.search.input.len()
93                    && self.search.autocomplete.is_some()
94                {
95                    self.search.input = self.search.autocomplete.take().unwrap();
96                    self.search.cursor_position = self.search.input.len();
97                    self.perform_search();
98                }
99            }
100            _ => {}
101        }
102    }
103
104    pub fn handle_key_normal_mode(&mut self, key: KeyEvent) {
105        match key.code {
106            KeyCode::Char('q') => {
107                if self.multi_select.enabled {
108                    self.toggle_multi_select_mode();
109                } else {
110                    self.quit = true;
111                }
112            }
113            KeyCode::Esc => {
114                if self.multi_select.enabled {
115                    self.toggle_multi_select_mode();
116                }
117            }
118            KeyCode::Char('j') | KeyCode::Down => {
119                self.next();
120            }
121            KeyCode::Char('k') | KeyCode::Up => {
122                self.previous();
123            }
124            KeyCode::Char('h') | KeyCode::Left => {
125                self.focused_panel = FocusedPanel::Categories;
126                self.scripts.state.select(None);
127            }
128            KeyCode::Char('l') | KeyCode::Right => {
129                if self.focused_panel == FocusedPanel::Scripts {
130                    if self.scripts.state.selected().is_some()
131                        && !(self.multi_select.enabled && self.multi_select.scripts.is_empty())
132                    {
133                        self.mode = AppMode::Confirm;
134                    }
135                } else {
136                    self.focused_panel = FocusedPanel::Scripts;
137                    if !self.scripts.items.is_empty() {
138                        self.scripts.state.select(Some(0));
139                    }
140                }
141            }
142            KeyCode::Home => {
143                self.top();
144            }
145            KeyCode::End => {
146                self.bottom();
147            }
148            KeyCode::Char('/') => {
149                self.toggle_search_mode();
150            }
151            KeyCode::Char('p') => {
152                self.toggle_preview_mode();
153            }
154            KeyCode::Char('m') => {
155                self.toggle_multi_select_mode();
156            }
157            KeyCode::Char('?') => {
158                self.toggle_help_mode();
159            }
160            KeyCode::Char('d') => {
161                self.toggle_description_popup();
162            }
163            KeyCode::Char('t') => {
164                if !self.theme_locked {
165                    self.cycle_theme();
166                }
167            }
168            KeyCode::Enter => {
169                if self.focused_panel == FocusedPanel::Scripts
170                    && self.scripts.state.selected().is_some()
171                    && !(self.multi_select.enabled && self.multi_select.scripts.is_empty())
172                {
173                    self.mode = AppMode::Confirm;
174                }
175            }
176            KeyCode::Char(' ') => {
177                if self.multi_select.enabled {
178                    self.toggle_script_selection();
179                }
180            }
181            _ => {}
182        }
183    }
184
185    pub fn handle_key_preview_mode(&mut self, key: KeyEvent) {
186        match key.code {
187            KeyCode::Char('q') | KeyCode::Esc | KeyCode::Char('p') | KeyCode::Char('h') => {
188                self.mode = AppMode::Normal;
189            }
190            KeyCode::Char('j') | KeyCode::Down => {
191                self.scroll_preview_down();
192            }
193            KeyCode::Char('k') | KeyCode::Up => {
194                self.scroll_preview_up();
195            }
196            KeyCode::PageDown => {
197                self.scroll_preview_page_down();
198            }
199            KeyCode::PageUp => {
200                self.scroll_preview_page_up();
201            }
202            KeyCode::Home => {
203                self.preview.scroll = 0;
204            }
205            KeyCode::End => {
206                self.preview.scroll = self.preview.max_scroll;
207            }
208            _ => {}
209        }
210    }
211
212    pub fn handle_mouse(&mut self, event: MouseEvent) {
213        match event.kind {
214            MouseEventKind::ScrollDown => match self.mode {
215                AppMode::Normal => self.next(),
216                AppMode::Preview => {
217                    for _ in 0..2 {
218                        self.scroll_preview_down();
219                    }
220                }
221                AppMode::Search => {
222                    if !self.search.results.is_empty() {
223                        self.search.selected_idx =
224                            (self.search.selected_idx + 1) % self.search.results.len();
225                    }
226                }
227                AppMode::Confirm => {}
228                AppMode::Help => {
229                    self.help.scroll = self.help.scroll.saturating_add(2).min(self.help.max_scroll);
230                }
231                AppMode::Description => {}
232                AppMode::RunScript => {}
233                AppMode::RootWarning => {}
234            },
235            MouseEventKind::ScrollUp => match self.mode {
236                AppMode::Normal => self.previous(),
237                AppMode::Preview => {
238                    for _ in 0..2 {
239                        self.scroll_preview_up();
240                    }
241                }
242                AppMode::Search => {
243                    if !self.search.results.is_empty() {
244                        self.search.selected_idx = if self.search.selected_idx > 0 {
245                            self.search.selected_idx - 1
246                        } else {
247                            self.search.results.len() - 1
248                        };
249                    }
250                }
251                AppMode::Confirm => {}
252                AppMode::Help => {
253                    self.help.scroll = self.help.scroll.saturating_sub(2);
254                }
255                AppMode::Description => {}
256                AppMode::RunScript => {}
257                AppMode::RootWarning => {}
258            },
259            _ => {}
260        }
261    }
262
263    pub fn handle_key_confirmation_mode(&mut self, key: KeyEvent) {
264        match key.code {
265            KeyCode::Char('y') | KeyCode::Char('Y') | KeyCode::Char('l') | KeyCode::Right => {
266                if self.log_mode {
267                    if self.multi_select.enabled && !self.multi_select.scripts.is_empty() {
268                        info!(
269                            "Confirmed execution of {} scripts.",
270                            self.multi_select.scripts.len()
271                        );
272                    } else if let Some(script_path) = self.get_script_path() {
273                        info!("Confirmed execution of script: {}", script_path.display());
274                    }
275                }
276
277                if self.multi_select.enabled && !self.multi_select.scripts.is_empty() {
278                    self.script_execution_queue = self.multi_select.scripts.clone();
279                } else if let Some(script_path) = self.get_script_path() {
280                    self.script_execution_queue.push(script_path);
281                }
282
283                if !self.script_execution_queue.is_empty() {
284                    let script_path = self.script_execution_queue.remove(0);
285                    let popup = RunScriptPopup::new(script_path, self.log_mode, self.theme.clone());
286                    self.run_script_popup = Some(popup);
287                    self.mode = AppMode::RunScript;
288                } else {
289                    self.mode = AppMode::Normal;
290                }
291            }
292            KeyCode::Char('n')
293            | KeyCode::Char('N')
294            | KeyCode::Esc
295            | KeyCode::Char('h')
296            | KeyCode::Char('q')
297            | KeyCode::Left => {
298                if self.log_mode {
299                    info!("Cancelled script execution.");
300                }
301                self.mode = AppMode::Normal;
302            }
303            _ => {}
304        }
305    }
306
307    pub fn handle_key_help_mode(&mut self, key: KeyEvent) {
308        match key.code {
309            KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('?') => {
310                self.mode = AppMode::Normal;
311                self.help.scroll = 0;
312            }
313            KeyCode::Char('j') | KeyCode::Down => {
314                self.help.scroll = (self.help.scroll + 1).min(self.help.max_scroll);
315            }
316            KeyCode::Char('k') | KeyCode::Up => {
317                self.help.scroll = self.help.scroll.saturating_sub(1);
318            }
319            KeyCode::Home => {
320                self.help.scroll = 0;
321            }
322            KeyCode::End => {
323                self.help.scroll = self.help.max_scroll;
324            }
325            KeyCode::PageDown => {
326                self.help.scroll = (self.help.scroll + 10).min(self.help.max_scroll);
327            }
328            KeyCode::PageUp => {
329                self.help.scroll = self.help.scroll.saturating_sub(10);
330            }
331            _ => {}
332        }
333    }
334
335    pub fn handle_key_description_mode(&mut self, key: KeyEvent) {
336        match key.code {
337            KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('d') => {
338                self.mode = AppMode::Normal;
339                self.description.content = None;
340                self.description.scroll = 0;
341            }
342            KeyCode::Char('j') | KeyCode::Down => {
343                self.description.scroll =
344                    (self.description.scroll + 1).min(self.description.max_scroll);
345            }
346            KeyCode::Char('k') | KeyCode::Up => {
347                self.description.scroll = self.description.scroll.saturating_sub(1);
348            }
349            _ => {}
350        }
351    }
352}