199 lines · 5.8 KB · Rust
RawDownload
use std::sync::Arc; use fuzzy::{StringMatch, StringMatchCandidate};use gpui::{AnyElement, App, Context, DismissEvent, SharedString, Task, Window};use picker::{Picker, PickerDelegate};use theme::ThemeRegistry;use ui::{ListItem, ListItemSpacing, prelude::*}; type IconThemePicker = Picker<IconThemePickerDelegate>; pub struct IconThemePickerDelegate {    icon_themes: Vec<SharedString>,    filtered_themes: Vec<StringMatch>,    selected_index: usize,    current_theme: SharedString,    on_theme_changed: Arc<dyn Fn(SharedString, &mut Window, &mut App) + 'static>,} impl IconThemePickerDelegate {    fn new(        current_theme: SharedString,        on_theme_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,        cx: &mut Context<IconThemePicker>,    ) -> Self {        let theme_registry = ThemeRegistry::global(cx);         let icon_themes: Vec<SharedString> = theme_registry            .list_icon_themes()            .into_iter()            .map(|theme_meta| theme_meta.name)            .collect();         let selected_index = icon_themes            .iter()            .position(|icon_theme| *icon_theme == current_theme)            .unwrap_or(0);         let filtered_themes = icon_themes            .iter()            .enumerate()            .map(|(index, theme)| StringMatch {                candidate_id: index,                string: theme.to_string(),                positions: Vec::new(),                score: 0.0,            })            .collect();         Self {            icon_themes,            filtered_themes,            selected_index,            current_theme,            on_theme_changed: Arc::new(on_theme_changed),        }    }} impl PickerDelegate for IconThemePickerDelegate {    type ListItem = AnyElement;     fn name() -> &'static str {        "icon theme picker"    }     fn match_count(&self) -> usize {        self.filtered_themes.len()    }     fn selected_index(&self) -> usize {        self.selected_index    }     fn set_selected_index(        &mut self,        index: usize,        _window: &mut Window,        cx: &mut Context<IconThemePicker>,    ) {        self.selected_index = index.min(self.filtered_themes.len().saturating_sub(1));        cx.notify();    }     fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {        "Search icon themes…".into()    }     fn update_matches(        &mut self,        query: String,        _window: &mut Window,        cx: &mut Context<IconThemePicker>,    ) -> Task<()> {        let icon_themes = self.icon_themes.clone();        let current_theme = self.current_theme.clone();         let matches: Vec<StringMatch> = if query.is_empty() {            icon_themes                .iter()                .enumerate()                .map(|(index, theme)| StringMatch {                    candidate_id: index,                    string: theme.to_string(),                    positions: Vec::new(),                    score: 0.0,                })                .collect()        } else {            let _candidates: Vec<StringMatchCandidate> = icon_themes                .iter()                .enumerate()                .map(|(id, theme)| StringMatchCandidate::new(id, theme.as_ref()))                .collect();             icon_themes                .iter()                .enumerate()                .filter(|(_, theme)| theme.to_lowercase().contains(&query.to_lowercase()))                .map(|(index, theme)| StringMatch {                    candidate_id: index,                    string: theme.to_string(),                    positions: Vec::new(),                    score: 0.0,                })                .collect()        };         let selected_index = if query.is_empty() {            icon_themes                .iter()                .position(|theme| *theme == current_theme)                .unwrap_or(0)        } else {            matches                .iter()                .position(|m| icon_themes[m.candidate_id] == current_theme)                .unwrap_or(0)        };         self.filtered_themes = matches;        self.selected_index = selected_index;        cx.notify();         Task::ready(())    }     fn confirm(        &mut self,        _secondary: bool,        window: &mut Window,        cx: &mut Context<IconThemePicker>,    ) {        if let Some(theme_match) = self.filtered_themes.get(self.selected_index) {            let theme = theme_match.string.clone();            (self.on_theme_changed)(theme.into(), window, cx);        }    }     fn dismissed(&mut self, window: &mut Window, cx: &mut Context<IconThemePicker>) {        cx.defer_in(window, |picker, window, cx| {            picker.set_query("", window, cx);        });        cx.emit(DismissEvent);    }     fn render_match(        &self,        index: usize,        selected: bool,        _window: &mut Window,        _cx: &mut Context<IconThemePicker>,    ) -> Option<Self::ListItem> {        let theme_match = self.filtered_themes.get(index)?;         Some(            ListItem::new(index)                .inset(true)                .spacing(ListItemSpacing::Sparse)                .toggle_state(selected)                .child(Label::new(theme_match.string.clone()))                .into_any_element(),        )    }} pub fn icon_theme_picker(    current_theme: SharedString,    on_theme_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,    window: &mut Window,    cx: &mut Context<IconThemePicker>,) -> IconThemePicker {    let delegate = IconThemePickerDelegate::new(current_theme, on_theme_changed, cx);     Picker::uniform_list(delegate, window, cx)        .show_scrollbar(true)        .initial_width(rems_from_px(210.))        .max_height(rems(18.))        .popover()}