Skip to repository content187 lines · 5.5 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T03:35:58.123Z 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
font_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::FontFamilyCache;
7use ui::{ListItem, ListItemSpacing, prelude::*};
8
9type FontPicker = Picker<FontPickerDelegate>;
10
11pub struct FontPickerDelegate {
12 fonts: Vec<SharedString>,
13 filtered_fonts: Vec<StringMatch>,
14 selected_index: usize,
15 current_font: SharedString,
16 on_font_changed: Arc<dyn Fn(SharedString, &mut Window, &mut App) + 'static>,
17}
18
19impl FontPickerDelegate {
20 fn new(
21 current_font: SharedString,
22 on_font_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
23 cx: &mut Context<FontPicker>,
24 ) -> Self {
25 let font_family_cache = FontFamilyCache::global(cx);
26
27 let fonts = font_family_cache
28 .try_list_font_families()
29 .unwrap_or_else(|| vec![current_font.clone()]);
30 let selected_index = fonts
31 .iter()
32 .position(|font| *font == current_font)
33 .unwrap_or(0);
34
35 let filtered_fonts = fonts
36 .iter()
37 .enumerate()
38 .map(|(index, font)| StringMatch {
39 candidate_id: index,
40 string: font.to_string(),
41 positions: Vec::new(),
42 score: 0.0,
43 })
44 .collect();
45
46 Self {
47 fonts,
48 filtered_fonts,
49 selected_index,
50 current_font,
51 on_font_changed: Arc::new(on_font_changed),
52 }
53 }
54}
55
56impl PickerDelegate for FontPickerDelegate {
57 type ListItem = AnyElement;
58
59 fn name() -> &'static str {
60 "font picker"
61 }
62
63 fn match_count(&self) -> usize {
64 self.filtered_fonts.len()
65 }
66
67 fn selected_index(&self) -> usize {
68 self.selected_index
69 }
70
71 fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<FontPicker>) {
72 self.selected_index = ix.min(self.filtered_fonts.len().saturating_sub(1));
73 cx.notify();
74 }
75
76 fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
77 "Search fonts…".into()
78 }
79
80 fn update_matches(
81 &mut self,
82 query: String,
83 _window: &mut Window,
84 cx: &mut Context<FontPicker>,
85 ) -> Task<()> {
86 let fonts = self.fonts.clone();
87 let current_font = self.current_font.clone();
88
89 let matches: Vec<StringMatch> = if query.is_empty() {
90 fonts
91 .iter()
92 .enumerate()
93 .map(|(index, font)| StringMatch {
94 candidate_id: index,
95 string: font.to_string(),
96 positions: Vec::new(),
97 score: 0.0,
98 })
99 .collect()
100 } else {
101 let _candidates: Vec<StringMatchCandidate> = fonts
102 .iter()
103 .enumerate()
104 .map(|(id, font)| StringMatchCandidate::new(id, font.as_ref()))
105 .collect();
106
107 fonts
108 .iter()
109 .enumerate()
110 .filter(|(_, font)| font.to_lowercase().contains(&query.to_lowercase()))
111 .map(|(index, font)| StringMatch {
112 candidate_id: index,
113 string: font.to_string(),
114 positions: Vec::new(),
115 score: 0.0,
116 })
117 .collect()
118 };
119
120 let selected_index = if query.is_empty() {
121 fonts
122 .iter()
123 .position(|font| *font == current_font)
124 .unwrap_or(0)
125 } else {
126 matches
127 .iter()
128 .position(|m| fonts[m.candidate_id] == current_font)
129 .unwrap_or(0)
130 };
131
132 self.filtered_fonts = matches;
133 self.selected_index = selected_index;
134 cx.notify();
135
136 Task::ready(())
137 }
138
139 fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<FontPicker>) {
140 if let Some(font_match) = self.filtered_fonts.get(self.selected_index) {
141 let font = font_match.string.clone();
142 (self.on_font_changed)(font.into(), window, cx);
143 }
144 }
145
146 fn dismissed(&mut self, window: &mut Window, cx: &mut Context<FontPicker>) {
147 cx.defer_in(window, |picker, window, cx| {
148 picker.set_query("", window, cx);
149 });
150 cx.emit(DismissEvent);
151 }
152
153 fn render_match(
154 &self,
155 ix: usize,
156 selected: bool,
157 _window: &mut Window,
158 _cx: &mut Context<FontPicker>,
159 ) -> Option<Self::ListItem> {
160 let font_match = self.filtered_fonts.get(ix)?;
161
162 Some(
163 ListItem::new(ix)
164 .inset(true)
165 .spacing(ListItemSpacing::Sparse)
166 .toggle_state(selected)
167 .child(Label::new(font_match.string.clone()))
168 .into_any_element(),
169 )
170 }
171}
172
173pub fn font_picker(
174 current_font: SharedString,
175 on_font_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
176 window: &mut Window,
177 cx: &mut Context<FontPicker>,
178) -> FontPicker {
179 let delegate = FontPickerDelegate::new(current_font, on_font_changed, cx);
180
181 Picker::uniform_list(delegate, window, cx)
182 .show_scrollbar(true)
183 .initial_width(rems_from_px(210.))
184 .max_height(rems(18.))
185 .popover()
186}
187