Skip to repository content200 lines · 5.9 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T03:36:11.814Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
icon_theme_picker.rs
1use std::sync::Arc;
2
3use fuzzy::{StringMatch, StringMatchCandidate};
4use gpui::{AnyElement, App, Context, DismissEvent, SharedString, Task, Window};
5use picker::{Picker, PickerDelegate};
6use theme::ThemeRegistry;
7use ui::{ListItem, ListItemSpacing, prelude::*};
8
9type IconThemePicker = Picker<IconThemePickerDelegate>;
10
11pub struct IconThemePickerDelegate {
12 icon_themes: Vec<SharedString>,
13 filtered_themes: Vec<StringMatch>,
14 selected_index: usize,
15 current_theme: SharedString,
16 on_theme_changed: Arc<dyn Fn(SharedString, &mut Window, &mut App) + 'static>,
17}
18
19impl IconThemePickerDelegate {
20 fn new(
21 current_theme: SharedString,
22 on_theme_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
23 cx: &mut Context<IconThemePicker>,
24 ) -> Self {
25 let theme_registry = ThemeRegistry::global(cx);
26
27 let icon_themes: Vec<SharedString> = theme_registry
28 .list_icon_themes()
29 .into_iter()
30 .map(|theme_meta| theme_meta.name)
31 .collect();
32
33 let selected_index = icon_themes
34 .iter()
35 .position(|icon_theme| *icon_theme == current_theme)
36 .unwrap_or(0);
37
38 let filtered_themes = icon_themes
39 .iter()
40 .enumerate()
41 .map(|(index, theme)| StringMatch {
42 candidate_id: index,
43 string: theme.to_string(),
44 positions: Vec::new(),
45 score: 0.0,
46 })
47 .collect();
48
49 Self {
50 icon_themes,
51 filtered_themes,
52 selected_index,
53 current_theme,
54 on_theme_changed: Arc::new(on_theme_changed),
55 }
56 }
57}
58
59impl PickerDelegate for IconThemePickerDelegate {
60 type ListItem = AnyElement;
61
62 fn name() -> &'static str {
63 "icon theme picker"
64 }
65
66 fn match_count(&self) -> usize {
67 self.filtered_themes.len()
68 }
69
70 fn selected_index(&self) -> usize {
71 self.selected_index
72 }
73
74 fn set_selected_index(
75 &mut self,
76 index: usize,
77 _window: &mut Window,
78 cx: &mut Context<IconThemePicker>,
79 ) {
80 self.selected_index = index.min(self.filtered_themes.len().saturating_sub(1));
81 cx.notify();
82 }
83
84 fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
85 "Search icon themes…".into()
86 }
87
88 fn update_matches(
89 &mut self,
90 query: String,
91 _window: &mut Window,
92 cx: &mut Context<IconThemePicker>,
93 ) -> Task<()> {
94 let icon_themes = self.icon_themes.clone();
95 let current_theme = self.current_theme.clone();
96
97 let matches: Vec<StringMatch> = if query.is_empty() {
98 icon_themes
99 .iter()
100 .enumerate()
101 .map(|(index, theme)| StringMatch {
102 candidate_id: index,
103 string: theme.to_string(),
104 positions: Vec::new(),
105 score: 0.0,
106 })
107 .collect()
108 } else {
109 let _candidates: Vec<StringMatchCandidate> = icon_themes
110 .iter()
111 .enumerate()
112 .map(|(id, theme)| StringMatchCandidate::new(id, theme.as_ref()))
113 .collect();
114
115 icon_themes
116 .iter()
117 .enumerate()
118 .filter(|(_, theme)| theme.to_lowercase().contains(&query.to_lowercase()))
119 .map(|(index, theme)| StringMatch {
120 candidate_id: index,
121 string: theme.to_string(),
122 positions: Vec::new(),
123 score: 0.0,
124 })
125 .collect()
126 };
127
128 let selected_index = if query.is_empty() {
129 icon_themes
130 .iter()
131 .position(|theme| *theme == current_theme)
132 .unwrap_or(0)
133 } else {
134 matches
135 .iter()
136 .position(|m| icon_themes[m.candidate_id] == current_theme)
137 .unwrap_or(0)
138 };
139
140 self.filtered_themes = matches;
141 self.selected_index = selected_index;
142 cx.notify();
143
144 Task::ready(())
145 }
146
147 fn confirm(
148 &mut self,
149 _secondary: bool,
150 window: &mut Window,
151 cx: &mut Context<IconThemePicker>,
152 ) {
153 if let Some(theme_match) = self.filtered_themes.get(self.selected_index) {
154 let theme = theme_match.string.clone();
155 (self.on_theme_changed)(theme.into(), window, cx);
156 }
157 }
158
159 fn dismissed(&mut self, window: &mut Window, cx: &mut Context<IconThemePicker>) {
160 cx.defer_in(window, |picker, window, cx| {
161 picker.set_query("", window, cx);
162 });
163 cx.emit(DismissEvent);
164 }
165
166 fn render_match(
167 &self,
168 index: usize,
169 selected: bool,
170 _window: &mut Window,
171 _cx: &mut Context<IconThemePicker>,
172 ) -> Option<Self::ListItem> {
173 let theme_match = self.filtered_themes.get(index)?;
174
175 Some(
176 ListItem::new(index)
177 .inset(true)
178 .spacing(ListItemSpacing::Sparse)
179 .toggle_state(selected)
180 .child(Label::new(theme_match.string.clone()))
181 .into_any_element(),
182 )
183 }
184}
185
186pub fn icon_theme_picker(
187 current_theme: SharedString,
188 on_theme_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
189 window: &mut Window,
190 cx: &mut Context<IconThemePicker>,
191) -> IconThemePicker {
192 let delegate = IconThemePickerDelegate::new(current_theme, on_theme_changed, cx);
193
194 Picker::uniform_list(delegate, window, cx)
195 .show_scrollbar(true)
196 .initial_width(rems_from_px(210.))
197 .max_height(rems(18.))
198 .popover()
199}
200