diff --git a/src/app.rs b/src/app.rs index 6b2de21..9c8a944 100644 --- a/src/app.rs +++ b/src/app.rs @@ -206,6 +206,7 @@ pub enum SetConfigFields { SetPage(MainPage), SetEventDuration(String), SetShowOnStartup(bool), + Blacklist(Editable), Modes(Editable<(String, String)>), Aliases(Editable<(String, String)>), SearchDirs(Editable), diff --git a/src/app/pages/settings.rs b/src/app/pages/settings.rs index b0bda5b..c4de28f 100644 --- a/src/app/pages/settings.rs +++ b/src/app/pages/settings.rs @@ -879,6 +879,8 @@ fn commands_tab( modes_item(config.modes.clone(), &theme), section_header_with_reset("Search Directories", ResetField::SearchDirs, theme.clone()), search_dirs_item(&theme, config.search_dirs.clone()), + section_header_with_reset("Blacklist", ResetField::SearchDirs, theme.clone()), + blacklist_item(&theme, config.blacklist.clone()), Space::new().height(10).into(), section_header_with_reset("Shell commands", ResetField::ShellCommands, theme.clone()), shell_commands_item(config.shells.clone(), theme.clone(), hotkey_capture), @@ -1116,6 +1118,57 @@ fn search_dirs_item(theme: &Theme, search_dirs: Vec) -> Element<'static, .into() } +fn blacklist_item(theme: &Theme, blacklist: Vec) -> Element<'static, Message> { + Column::from_iter([ + container( + Column::from_iter(blacklist.iter().map(|item| { + let theme_clone_2 = theme.clone(); + let blacklist_item = item.clone(); + let blacklist_item_clone = blacklist_item.clone(); + container( + Row::from_iter([ + text_input_cell(blacklist_item.clone(), &theme_clone_2, "App name") + .on_input(move |input| { + Message::SetConfig(SetConfigFields::Blacklist(Editable::Update { + old: blacklist_item_clone.clone(), + new: input, + })) + }) + .into(), + Button::new("Delete") + .on_press(Message::SetConfig(SetConfigFields::Blacklist( + Editable::Delete(blacklist_item.clone()), + ))) + .style(move |_, _| delete_button_style(&theme_clone_2)) + .into(), + ]) + .spacing(10) + .align_y(Alignment::Center), + ) + .width(Length::Fill) + .align_x(Alignment::Center) + .into() + })) + .spacing(10), + ) + .height(Length::Fill) + .width(Length::Fill) + .align_x(Alignment::Center) + .align_y(Alignment::Center) + .into(), + dir_adder_button("+", theme.to_owned()) + .on_press(Message::SetConfig(SetConfigFields::Blacklist( + Editable::Create("".to_string()), + ))) + .into(), + ]) + .spacing(10) + .height(Length::Fill) + .width(Length::Fill) + .align_x(Alignment::Center) + .into() +} + fn text_input_cell(text: String, theme: &Theme, placeholder: &str) -> TextInput<'static, Message> { text_input(placeholder, &text) .font(theme.font()) diff --git a/src/app/tile.rs b/src/app/tile.rs index 7e340d7..8141aee 100644 --- a/src/app/tile.rs +++ b/src/app/tile.rs @@ -39,6 +39,7 @@ use url::Url; use std::cell::RefCell; use std::collections::HashMap; use std::fmt::Debug; +use std::sync::Arc; use std::time::Duration; /// This is a wrapper around the sender to disable dropping @@ -58,8 +59,13 @@ struct AppIndex { impl AppIndex { /// Search for an element in the index that starts with the provided prefix - fn search_prefix<'a>(&'a self, prefix: &'a str) -> impl ParallelIterator + 'a { + fn search_prefix<'a>( + &'a self, + prefix: &'a str, + blacklist: Vec, + ) -> impl ParallelIterator + 'a { let pattern = Pattern::parse(prefix, CaseMatching::Ignore, Normalization::Smart); + let blacklist_rc = Arc::new(blacklist); self.by_name.par_iter().filter_map(move |(name, app)| { thread_local! { @@ -68,6 +74,10 @@ impl AppIndex { ); } + if blacklist_rc.contains(&name.to_lowercase()) { + return None; + } + MATCHER.with(|m| { let mut buf = Vec::new(); let haystack = Utf32Str::new(name, &mut buf); @@ -405,7 +415,7 @@ impl Tile { &AppIndex::empty() }; let results: Vec = options - .search_prefix(&query) + .search_prefix(&query, self.config.blacklist.clone()) .map(|x| x.to_owned()) .collect(); @@ -704,15 +714,15 @@ mod tests { ]); let prefix_results: Vec<_> = index - .search_prefix("sa") + .search_prefix("sa", Vec::new()) .map(|app| app.display_name.clone()) .collect(); let spaced_results: Vec<_> = index - .search_prefix("studio") + .search_prefix("studio", Vec::new()) .map(|app| app.display_name.clone()) .collect(); let hyphen_results: Vec<_> = index - .search_prefix("desktop") + .search_prefix("desktop", Vec::new()) .map(|app| app.display_name.clone()) .collect(); diff --git a/src/app/tile/elm.rs b/src/app/tile/elm.rs index dd8fab1..e57b751 100644 --- a/src/app/tile/elm.rs +++ b/src/app/tile/elm.rs @@ -166,7 +166,7 @@ pub fn view(tile: &Tile, wid: window::Id) -> Element<'_, Message> { Page::EmojiSearch => emoji_page( tile.config.theme.clone(), tile.emoji_apps - .search_prefix(&tile.query_lc) + .search_prefix(&tile.query_lc, Vec::new()) .map(|x| x.to_owned()) .collect(), tile.focus_id, diff --git a/src/app/tile/update.rs b/src/app/tile/update.rs index 74713e3..3b0ec47 100644 --- a/src/app/tile/update.rs +++ b/src/app/tile/update.rs @@ -328,9 +328,10 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task { for _ in 0..amount { let len = match tile.page { Page::ClipboardHistory => tile.clipboard_content.len() as u32, - Page::EmojiSearch => { - tile.emoji_apps.search_prefix(&tile.query_lc).count() as u32 - } // or tile.results.len() + Page::EmojiSearch => tile + .emoji_apps + .search_prefix(&tile.query_lc, Vec::new()) + .count() as u32, // or tile.results.len() _ => tile.results.len() as u32, }; @@ -933,6 +934,37 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task { final_config.aliases.remove(&old.0); final_config.aliases.insert(new.0, new.1); } + SetConfigFields::Blacklist(Editable::Create(item)) => { + if !final_config.blacklist.contains(&item.to_lowercase()) { + final_config.blacklist.push(item.to_lowercase()); + } + } + SetConfigFields::Blacklist(Editable::Delete(target)) => { + final_config.blacklist = final_config + .blacklist + .iter() + .filter_map(|item| { + if &target != item { + Some(item.to_lowercase()) + } else { + None + } + }) + .collect(); + } + SetConfigFields::Blacklist(Editable::Update { old, new }) => { + final_config.blacklist = final_config + .blacklist + .iter() + .map(|item| { + if item == &old { + new.to_lowercase() + } else { + item.to_owned() + } + }) + .collect(); + } SetConfigFields::SearchDirs(Editable::Create(dir)) => { if !final_config.search_dirs.contains(&dir) { final_config.search_dirs.push(dir); diff --git a/src/config.rs b/src/config.rs index f0cc1e3..38eec45 100644 --- a/src/config.rs +++ b/src/config.rs @@ -112,6 +112,7 @@ pub struct Config { pub modes: HashMap, pub aliases: HashMap, pub search_dirs: Vec, + pub blacklist: Vec, pub log_path: String, pub debounce_delay: u64, pub auto_update: bool, @@ -137,13 +138,14 @@ impl Default for Config { haptic_feedback: false, auto_update: true, show_trayicon: true, + blacklist: Vec::new(), window_location: Position::Default, main_page: MainPage::default(), search_dirs: vec!["~".to_string()], log_path: "/tmp/rustcast.log".to_string(), modes: HashMap::new(), aliases: HashMap::new(), - shells: vec![], + shells: Vec::new(), debounce_delay: 300, input_source_on_open: None, restore_input_source_on_close: true,