diff options
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | src/screens/available_packages.rs | 38 | ||||
| -rw-r--r-- | src/screens/installed_packages.rs | 37 | ||||
| -rw-r--r-- | src/widgets/search_popup.rs | 54 |
4 files changed, 93 insertions, 41 deletions
@@ -110,7 +110,7 @@ nix develop The project is still early in development. Planned features include: - [x] Public guru ebuild - [x] Portage package search/browser - - [ ] Nicer loading screen + - [x] Nicer loading screen - [ ] Improved information in package metadata - [ ] RDEPEND - [ ] BDEPEND @@ -125,10 +125,11 @@ The project is still early in development. Planned features include: - [x] Better package USE Flag rendering - [ ] Improved dashboard statistics - [ ] Portage news reader -- [ ] Search improvements +- [x] Search improvements - [ ] Sorting options - [x] Async package metadata loading - [ ] Logging +- [ ] Reading ebuild files directly instead of using pre-computed --- diff --git a/src/screens/available_packages.rs b/src/screens/available_packages.rs index a96437b..b16eac1 100644 --- a/src/screens/available_packages.rs +++ b/src/screens/available_packages.rs @@ -7,8 +7,8 @@ use ratatui::{ Frame, crossterm::event::KeyCode, layout::{Alignment, Constraint, Layout, Rect}, - style::{Color, Style}, - text::{Line, Span, Text}, + style::Color, + text::{Line, Span}, widgets::{Block, Borders, List, ListState, Paragraph}, }; @@ -136,16 +136,16 @@ impl AvailablePackagesScreen { List::new(pkg_versions.iter().map(|v| version_to_variant(v))) .style(Color::White) .highlight_style(Theme::selected()) - .highlight_symbol("> "), // , // .block( - // Block::default() - // .borders(Borders::ALL) - // .border_style(Theme::block()) - // .title("Variants"), - // ), + .highlight_symbol("> "), ); self.selected_version = Some(pkg_versions[0].clone()); } + + fn select_package_index(&mut self, index: usize, repo: &Portage) { + self.pkg_list_state.select(Some(index)); + self.build_pkg_version_list(repo); + } } impl Default for AvailablePackagesScreen { @@ -258,6 +258,10 @@ impl Screen for AvailablePackagesScreen { let text = if self.search_popup.visible { "(esc) to quit search | (enter) to search".to_string() + } else if let Some((current, total)) = self.search_popup.match_status() { + format!( + "(q) to quit | (j) down | (k) up | (space) switch panel | (/) to search | Search {current}/{total} | (n) next | (N) previous" + ) } else { "(q) to quit | (j) down | (k) up | (space) switch panel | (/) to search".to_string() }; @@ -312,6 +316,18 @@ impl Screen for AvailablePackagesScreen { KeyCode::Char(' ') => self.cycle_list(), KeyCode::Char('/') => self.search_popup.toggle(), + KeyCode::Char('n') => { + if let Some(index) = self.search_popup.next_match() { + self.select_package_index(index, repo) + } + } + + KeyCode::Char('N') => { + if let Some(index) = self.search_popup.previous_match() { + self.select_package_index(index, repo) + } + } + _ => match self.chosen_list { CurrentList::PackageList => match key.code { KeyCode::Char('j') => { @@ -369,12 +385,6 @@ impl Screen for AvailablePackagesScreen { .style(Color::White) .highlight_style(Theme::selected()) .highlight_symbol("> "); - // .block( - // Block::default() - // .borders(Borders::ALL) - // .border_style(Theme::block()) - // .title("Available packages"), - // ); self.pkg_list = Some(list); self.loading_state = LoadingState::Complete; diff --git a/src/screens/installed_packages.rs b/src/screens/installed_packages.rs index b373cb4..24fab50 100644 --- a/src/screens/installed_packages.rs +++ b/src/screens/installed_packages.rs @@ -4,17 +4,17 @@ use crate::{ signal::{Event, Signal}, theme::Theme, widgets::{ - helpers::search_popup_rect, package_metadata::render_package_metadata, - search_popup::SearchPopup, use_flags::render_use_flags, + package_metadata::render_package_metadata, search_popup::SearchPopup, + use_flags::render_use_flags, }, }; use ratatui::{ Frame, crossterm::event::{KeyCode, KeyModifiers}, layout::{Constraint, Layout, Rect}, - style::{Color, Modifier, Style}, + style::Color, text::{Line, Span}, - widgets::{Block, Borders, Clear, List, ListState, Paragraph}, + widgets::{Block, Borders, List, ListState, Paragraph}, }; #[derive(Debug)] @@ -114,7 +114,6 @@ impl Screen for InstalledPackagesScreen { frame, split_vert[0], pkg.iuse.iter().collect(), - // TODO: Dont use clone here if possible &pkg.enabled_use_flags, ); render_package_metadata( @@ -129,19 +128,13 @@ impl Screen for InstalledPackagesScreen { let text = if self.search_popup.visible { "(esc) to quit search | (enter) to search".to_string() + } else if let Some((current, total)) = self.search_popup.match_status() { + format!( + "(q) to quit | (j) down | (k) up | (space) switch panel | (/) to search | Search {current}/{total} | (n) next | (N) previous" + ) } else { let main_key_hint = "(q) to quit | (j) down | (k) up | (f) filter | (/) to search".to_string(); - // if let (Some(current), Some(total)) = (app.current_search_index, app.search_indexes_len) - // { - // let _ = write!( - // main_key_hint, - // " | (n) for next search | (N) for previous search | Searches found: {} / {}", - // current + 1, - // total - // ); - // } - main_key_hint }; @@ -215,9 +208,17 @@ impl Screen for InstalledPackagesScreen { KeyCode::Char('k') => self.list_state.select_previous(), KeyCode::Char('/') => self.search_popup.toggle(), - // TODO: Reimplement searchable "spaces" - // KeyCode::Char('n') => todo!("Next search"), - // KeyCode::Char('N') => todo!("prev search"), + KeyCode::Char('n') => { + if let Some(index) = self.search_popup.next_match() { + self.list_state.select(Some(index)) + } + } + + KeyCode::Char('N') => { + if let Some(index) = self.search_popup.previous_match() { + self.list_state.select(Some(index)) + } + } KeyCode::Char('f') => self.cycle_filter(), _ => {} diff --git a/src/widgets/search_popup.rs b/src/widgets/search_popup.rs index 0f47b10..965e6c1 100644 --- a/src/widgets/search_popup.rs +++ b/src/widgets/search_popup.rs @@ -12,7 +12,9 @@ use crate::{gentoo::package::PackageKey, theme::Theme, widgets::helpers::search_ pub struct SearchPopup { pub(crate) visible: bool, pub(crate) textarea: TextArea<'static>, - pub(crate) result: Option<usize>, + + matches: Vec<usize>, + selected_match: Option<usize>, } impl Default for SearchPopup { @@ -20,7 +22,9 @@ impl Default for SearchPopup { let mut search_popup = Self { visible: bool::default(), textarea: TextArea::default(), - result: Option::default(), + + matches: vec![], + selected_match: None, }; search_popup.textarea.set_style(Theme::muted()); @@ -51,24 +55,60 @@ impl SearchPopup { .collect(); if indexes.is_empty() { - self.result = None; + self.matches = vec![]; + self.selected_match = None; None } else { - self.result = Some(indexes[0]); - Some(indexes[0]) + self.matches = indexes; + self.selected_match = Some(0); + return Some(self.matches[0]); + } + } + + pub fn current_match(&self) -> Option<usize> { + if let Some(index) = self.selected_match { + self.matches.get(index).copied() + } else { + None + } + } + + pub fn next_match(&mut self) -> Option<usize> { + if let Some(index) = self.selected_match { + self.selected_match = Some((index + 1) % self.matches.len()); } + + self.current_match() + } + + pub fn previous_match(&mut self) -> Option<usize> { + if let Some(index) = self.selected_match { + self.selected_match = Some((index + self.matches.len() - 1) % self.matches.len()); + } + + self.current_match() + } + + pub fn match_status(&self) -> Option<(usize, usize)> { + self.selected_match + .map(|selected| (selected + 1, self.matches.len())) } pub fn draw(&mut self, frame: &mut Frame, area: Rect) { if self.visible { + let title = match self.match_status() { + Some((current, total)) => format!("Search {current}/{total}"), + None => "Search".to_string(), + }; + let mut popup_block = Block::default() - .title("Search") + .title(title) .borders(Borders::ALL) .border_type(ratatui::widgets::BorderType::Rounded) .border_style(Theme::block()) .style(Style::default().bg(Theme::BG)); - if self.result.is_some() { + if self.selected_match.is_some() { popup_block = popup_block.border_style(Theme::success()); } else { popup_block = popup_block.border_style(Theme::error()); |
