diff --git a/src/app.rs b/src/app.rs index d9acd0d..2eef601 100644 --- a/src/app.rs +++ b/src/app.rs @@ -26,10 +26,10 @@ pub enum Page { #[allow(dead_code)] #[derive(Debug, Clone)] pub enum ArrowKey { - ArrowUp, - ArrowDown, - ArrowLeft, - ArrowRight, + Up, + Down, + Left, + Right, } #[derive(Debug, Clone)] diff --git a/src/app/tile.rs b/src/app/tile.rs index 5562593..cad8dbf 100644 --- a/src/app/tile.rs +++ b/src/app/tile.rs @@ -2,11 +2,10 @@ mod elm; mod update; -use crate::app::apps::{App, AppCommand}; +use crate::app::apps::App; use crate::app::tile::elm::default_app_paths; use crate::app::{ArrowKey, Message, Move, Page}; use crate::clipboard::ClipBoardContentType; -use crate::commands::Function; use crate::config::Config; use crate::utils::open_settings; @@ -28,6 +27,7 @@ use objc2_app_kit::NSRunningApplication; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use tray_icon::TrayIcon; +use std::collections::BTreeMap; use std::fs; use std::path::PathBuf; use std::time::Duration; @@ -41,6 +41,29 @@ impl Drop for ExtSender { fn drop(&mut self) {} } +#[derive(Clone, Debug)] +struct AppIndex { + by_name: BTreeMap, +} + +impl AppIndex { + fn search_prefix<'a>(&'a self, prefix: &'a str) -> impl Iterator + 'a { + self.by_name + .range(prefix.to_string()..) // start at prefix + .take_while(move |(k, _)| k.starts_with(prefix)) + .map(|(_, v)| v) + } + + pub fn from_apps(options: Vec) -> Self { + let mut bmap = BTreeMap::new(); + for app in options { + bmap.insert(app.name_lc.clone(), app); + } + + AppIndex { by_name: bmap } + } +} + /// This is the base window, and its a "Tile" /// Its fields are: /// - Theme ([`iced::Theme`]) @@ -62,9 +85,8 @@ pub struct Tile { focus_id: u32, query: String, query_lc: String, - prev_query_lc: String, results: Vec, - options: Vec, + options: AppIndex, visible: bool, focused: bool, frontmost: Option>, @@ -124,10 +146,10 @@ impl Tile { return Some(Message::KeyPressed(65598)); } keyboard::Key::Named(Named::ArrowUp) => { - return Some(Message::ChangeFocus(ArrowKey::ArrowUp)); + return Some(Message::ChangeFocus(ArrowKey::Up)); } keyboard::Key::Named(Named::ArrowDown) => { - return Some(Message::ChangeFocus(ArrowKey::ArrowDown)); + return Some(Message::ChangeFocus(ArrowKey::Down)); } keyboard::Key::Character(chr) => { if modifiers.command() && chr.to_string().to_lowercase() == "r" { @@ -173,37 +195,14 @@ impl Tile { /// should be separated out to make it easier to test. This function is called by the `update` /// function to handle the search query changed event. pub fn handle_search_query_changed(&mut self) { - let filter_vec: &Vec = if self.query_lc.starts_with(&self.prev_query_lc) { - self.prev_query_lc = self.query_lc.to_owned(); - &self.results - } else { - &self.options - }; - let query = self.query_lc.clone(); - - let mut exact: Vec = filter_vec - .par_iter() - .filter(|x| match &x.open_command { - &AppCommand::Function(Function::RunShellCommand(_, _)) => x - .name_lc - .starts_with(query.split_once(" ").unwrap_or((&query, "")).0), - _ => x.name_lc == query, - }) - .cloned() - .collect(); - - let mut prefix: Vec = filter_vec - .par_iter() - .filter(|x| match x.open_command { - AppCommand::Function(Function::RunShellCommand(_, _)) => false, - _ => x.name_lc != query && x.name_lc.starts_with(&query), - }) - .cloned() + let results: Vec = self + .options + .search_prefix(&query) + .map(|x| x.to_owned()) .collect(); - exact.append(&mut prefix); - self.results = exact; + self.results = results; } /// Gets the frontmost application to focus later. diff --git a/src/app/tile/elm.rs b/src/app/tile/elm.rs index 0c5366e..9cd5dcb 100644 --- a/src/app/tile/elm.rs +++ b/src/app/tile/elm.rs @@ -16,6 +16,7 @@ use rayon::{ }; use crate::app::apps::AppCommand; +use crate::app::tile::AppIndex; use crate::config::Theme; use crate::{ app::{Message, Page, apps::App, default_settings, tile::Tile}, @@ -59,12 +60,12 @@ pub fn new(hotkey: HotKey, config: &Config) -> (Tile, Task) { options.extend(config.shells.iter().map(|x| x.to_app())); options.extend(App::basic_apps()); options.par_sort_by_key(|x| x.name.len()); + let options = AppIndex::from_apps(options); ( Tile { query: String::new(), query_lc: String::new(), - prev_query_lc: String::new(), focus_id: 0, results: vec![], options, @@ -119,11 +120,12 @@ pub fn view(tile: &Tile, wid: window::Id) -> Element<'_, Message> { let results = match tile.page { Page::Main => { let mut search_results = Column::new(); - let mut i = 0_u32; - for result in &tile.results { - search_results = - search_results.push(result.render(&tile.config.theme, i, tile.focus_id)); - i += 1; + for (i, result) in tile.results.iter().enumerate() { + search_results = search_results.push(result.render( + &tile.config.theme, + i as u32, + tile.focus_id, + )); } search_results } diff --git a/src/app/tile/update.rs b/src/app/tile/update.rs index 2770007..b6ea380 100644 --- a/src/app/tile/update.rs +++ b/src/app/tile/update.rs @@ -23,6 +23,7 @@ use crate::app::apps::App; use crate::app::apps::AppCommand; use crate::app::default_settings; use crate::app::menubar::menu_icon; +use crate::app::tile::AppIndex; use crate::app::tile::elm::default_app_paths; use crate::calculator::Expression; use crate::commands::Function; @@ -190,7 +191,7 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task { height: ((max_elem * 55) + DEFAULT_WINDOW_HEIGHT as usize) as f32, }, ), - Task::done(Message::ChangeFocus(ArrowKey::ArrowLeft)), + Task::done(Message::ChangeFocus(ArrowKey::Left)), ]) } else if tile.page == Page::ClipboardHistory { let element_count = min(tile.clipboard_content.len(), 5); @@ -216,8 +217,8 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task { let u32_len = tile.results.len() as u32; if u32_len > 0 { match key { - ArrowKey::ArrowDown => tile.focus_id = (tile.focus_id + 1) % u32_len, - ArrowKey::ArrowUp => tile.focus_id = (tile.focus_id + u32_len - 1) % u32_len, + ArrowKey::Down => tile.focus_id = (tile.focus_id + 1) % u32_len, + ArrowKey::Up => tile.focus_id = (tile.focus_id + u32_len - 1) % u32_len, _ => {} } @@ -263,8 +264,7 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task { tile.theme = new_config.theme.to_owned().into(); tile.config = new_config; - tile.options = new_options; - + tile.options = AppIndex::from_apps(new_options); Task::none() }