diff --git a/tui/src/float.rs b/tui/src/float.rs index 3df63ae9..56676f9b 100644 --- a/tui/src/float.rs +++ b/tui/src/float.rs @@ -50,15 +50,7 @@ impl Float { pub fn draw(&mut self, frame: &mut Frame, parent_area: Rect) { let popup_area = self.floating_window(parent_area); - - let content_area = Rect { - x: popup_area.x, - y: popup_area.y, - width: popup_area.width, - height: popup_area.height, - }; - - self.content.draw(frame, content_area); + self.content.draw(frame, popup_area); } // Returns true if the floating window is finished. diff --git a/tui/src/hint.rs b/tui/src/hint.rs index 31bbef40..034406db 100644 --- a/tui/src/hint.rs +++ b/tui/src/hint.rs @@ -20,6 +20,15 @@ pub struct Shortcut { pub desc: &'static str, } +fn add_spacing(list: Vec>) -> Line { + list.into_iter() + .flat_map(|mut s| { + s.push(Span::default().content(" ")); + s + }) + .collect() +} + pub fn span_vec_len(span_vec: &[Span]) -> usize { span_vec.iter().rfold(0, |init, s| init + s.width()) } @@ -29,15 +38,14 @@ impl ShortcutList { .title(self.scope_name) .borders(Borders::all()); let inner_area = area.inner(Margin::new(1, 1)); - let mut shortcut_list: Vec> = self.hints.iter().map(|h| h.to_spans()).collect(); + let shortcut_spans: Vec> = self.hints.iter().map(|h| h.to_spans()).collect(); - let mut lines = vec![Line::default(); SHORTCUT_LINES]; - let mut idx = 0; + let mut lines: Vec = Vec::with_capacity(SHORTCUT_LINES); - while idx < SHORTCUT_LINES - 1 { - let split_idx = shortcut_list + let shortcut_list = (0..SHORTCUT_LINES - 1).fold(shortcut_spans, |mut acc, _| { + let split_idx = acc .iter() - .scan(0usize, |total_len, s| { + .scan(0_usize, |total_len, s| { *total_len += span_vec_len(s); if *total_len > inner_area.width as usize { None @@ -47,25 +55,13 @@ impl ShortcutList { } }) .count(); - let new_shortcut_list = shortcut_list.split_off(split_idx); - let line: Vec<_> = shortcut_list - .into_iter() - .flat_map(|mut s| { - s.push(Span::default().content(" ")); - s - }) - .collect(); - shortcut_list = new_shortcut_list; - lines[idx] = line.into(); - idx += 1; - } - lines[idx] = shortcut_list - .into_iter() - .flat_map(|mut s| { - s.push(Span::default().content(" ")); - s - }) - .collect(); + + let new_shortcut_list = acc.split_off(split_idx); + lines.push(add_spacing(acc)); + + new_shortcut_list + }); + lines.push(add_spacing(shortcut_list)); let p = Paragraph::new(lines).block(block); frame.render_widget(p, area); diff --git a/tui/src/state.rs b/tui/src/state.rs index 856a614a..f3e1c574 100644 --- a/tui/src/state.rs +++ b/tui/src/state.rs @@ -17,6 +17,9 @@ use ratatui::{ Frame, }; +const MIN_WIDTH: u16 = 77; +const MIN_HEIGHT: u16 = 19; + pub struct AppState { /// Selected theme theme: Theme, @@ -72,16 +75,14 @@ impl AppState { } pub fn draw(&mut self, frame: &mut Frame) { let terminal_size = frame.area(); - let min_width = 77; // Minimum width threshold - let min_height = 19; // Minimum height threshold - if terminal_size.width < min_width || terminal_size.height < min_height { + if terminal_size.width < MIN_WIDTH || terminal_size.height < MIN_HEIGHT { let size_warning_message = format!( "Terminal size too small:\nWidth = {} Height = {}\n\nMinimum size:\nWidth = {} Height = {}", terminal_size.width, terminal_size.height, - min_width, - min_height, + MIN_WIDTH, + MIN_HEIGHT, ); let warning_paragraph = Paragraph::new(size_warning_message.clone()) @@ -230,6 +231,12 @@ impl AppState { }, )); + let style = if let Focus::List = self.focus { + Style::default().reversed() + } else { + Style::new() + }; + // Create the list widget with items let list = List::new(items) .highlight_style(if let Focus::List = self.focus { @@ -259,12 +266,12 @@ impl AppState { pub fn handle_key(&mut self, key: &KeyEvent) -> bool { // This should be defined first to allow closing // the application even when not drawable ( If terminal is small ) - if matches!(self.focus, Focus::TabList | Focus::List) { - if key.code == KeyCode::Char('q') - || key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL) - { - return false; - } + // Exit on 'q' or 'Ctrl-c' input + if matches!(self.focus, Focus::TabList | Focus::List) + && (key.code == KeyCode::Char('q') + || key.modifiers.contains(KeyModifiers::CONTROL) && key.code == KeyCode::Char('c')) + { + return false; } // If UI is not drawable returning true will mark as the key handled @@ -303,20 +310,11 @@ impl AppState { self.focus = Focus::List; } } - Focus::Search => match self.filter.handle_key(key) { SearchAction::Exit => self.exit_search(), SearchAction::Update => self.update_items(), _ => {} }, - - _ if key.code == KeyCode::Char('q') - || key.code == KeyCode::Char('c') - && key.modifiers.contains(KeyModifiers::CONTROL) => - { - return false; - } - Focus::TabList => match key.code { KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => self.focus = Focus::List, @@ -337,7 +335,6 @@ impl AppState { KeyCode::Char('T') => self.theme.prev(), _ => {} }, - Focus::List if key.kind != KeyEventKind::Release => match key.code { KeyCode::Char('j') | KeyCode::Down => self.selection.select_next(), KeyCode::Char('k') | KeyCode::Up => self.selection.select_previous(), @@ -358,7 +355,6 @@ impl AppState { KeyCode::Char(' ') if self.multi_select => self.toggle_selection(), _ => {} }, - _ => (), }; true @@ -410,7 +406,7 @@ impl AppState { self.selection.select(Some(0)); self.update_items(); } - pub fn get_selected_command(&self) -> Option { + fn get_selected_node(&self) -> Option<&ListNode> { let mut selected_index = self.selection.selected().unwrap_or(0); if !self.at_root() && selected_index == 0 { @@ -422,27 +418,17 @@ impl AppState { if let Some(item) = self.filter.item_list().get(selected_index) { if !item.has_children { - return Some(item.node.command.clone()); + return Some(&item.node); } } None } - fn get_selected_description(&mut self) -> Option { - let mut selected_index = self.selection.selected().unwrap_or(0); - - if !self.at_root() && selected_index == 0 { - return None; - } - if !self.at_root() { - selected_index = selected_index.saturating_sub(1); - } - - if let Some(item) = self.filter.item_list().get(selected_index) { - if !item.has_children { - return Some(item.node.description.clone()); - } - } - None + pub fn get_selected_command(&self) -> Option { + self.get_selected_node().map(|node| node.command.clone()) + } + fn get_selected_description(&self) -> Option { + self.get_selected_node() + .map(|node| node.description.clone()) } pub fn go_to_selected_dir(&mut self) { let mut selected_index = self.selection.selected().unwrap_or(0); @@ -475,29 +461,14 @@ impl AppState { selected_index = selected_index.saturating_sub(1); } - if let Some(item) = self.filter.item_list().get(selected_index) { - item.has_children - } else { - false - } + self.filter + .item_list() + .get(selected_index) + .map_or(false, |item| item.has_children) } pub fn selected_item_is_cmd(&self) -> bool { - let mut selected_index = self.selection.selected().unwrap_or(0); - - if !self.at_root() && selected_index == 0 { - return false; - } - - if !self.at_root() { - selected_index = selected_index.saturating_sub(1); - } - - if let Some(item) = self.filter.item_list().get(selected_index) { - !item.has_children - } else { - false - } + !self.selected_item_is_dir() } pub fn selected_item_is_up_dir(&self) -> bool { let selected_index = self.selection.selected().unwrap_or(0);