Detailed changes
@@ -449,28 +449,28 @@ impl FontPickerDelegate {
) -> Self {
let font_family_cache = FontFamilyCache::global(cx);
- let fonts: Vec<SharedString> = font_family_cache
- .list_font_families(cx)
- .into_iter()
- .collect();
-
+ let fonts = font_family_cache
+ .try_list_font_families()
+ .unwrap_or_else(|| vec![current_font.clone()]);
let selected_index = fonts
.iter()
.position(|font| *font == current_font)
.unwrap_or(0);
+ let filtered_fonts = fonts
+ .iter()
+ .enumerate()
+ .map(|(index, font)| StringMatch {
+ candidate_id: index,
+ string: font.to_string(),
+ positions: Vec::new(),
+ score: 0.0,
+ })
+ .collect();
+
Self {
- fonts: fonts.clone(),
- filtered_fonts: fonts
- .iter()
- .enumerate()
- .map(|(index, font)| StringMatch {
- candidate_id: index,
- string: font.to_string(),
- positions: Vec::new(),
- score: 0.0,
- })
- .collect(),
+ fonts,
+ filtered_fonts,
selected_index,
current_font,
on_font_changed: Arc::new(on_font_changed),
@@ -242,12 +242,25 @@ struct Onboarding {
impl Onboarding {
fn new(workspace: &Workspace, cx: &mut App) -> Entity<Self> {
- cx.new(|cx| Self {
- workspace: workspace.weak_handle(),
- focus_handle: cx.focus_handle(),
- selected_page: SelectedPage::Basics,
- user_store: workspace.user_store().clone(),
- _settings_subscription: cx.observe_global::<SettingsStore>(move |_, cx| cx.notify()),
+ let font_family_cache = theme::FontFamilyCache::global(cx);
+
+ cx.new(|cx| {
+ cx.spawn(async move |this, cx| {
+ font_family_cache.prefetch(cx).await;
+ this.update(cx, |_, cx| {
+ cx.notify();
+ })
+ })
+ .detach();
+
+ Self {
+ workspace: workspace.weak_handle(),
+ focus_handle: cx.focus_handle(),
+ selected_page: SelectedPage::Basics,
+ user_store: workspace.user_store().clone(),
+ _settings_subscription: cx
+ .observe_global::<SettingsStore>(move |_, cx| cx.notify()),
+ }
})
}
@@ -78,6 +78,7 @@ impl SettingsValue<serde_json::Value> {
let fs = <dyn Fs>::global(cx);
let rx = settings_store.update_settings_file_at_path(fs.clone(), path.as_slice(), value);
+
let path = path.clone();
cx.background_spawn(async move {
rx.await?
@@ -16,7 +16,7 @@ struct FontFamilyCacheState {
/// so we do it once and then use the cached values each render.
#[derive(Default)]
pub struct FontFamilyCache {
- state: RwLock<FontFamilyCacheState>,
+ state: Arc<RwLock<FontFamilyCacheState>>,
}
#[derive(Default)]
@@ -52,4 +52,44 @@ impl FontFamilyCache {
lock.font_families.clone()
}
+
+ /// Returns the list of font families if they have been loaded
+ pub fn try_list_font_families(&self) -> Option<Vec<SharedString>> {
+ self.state
+ .try_read()
+ .filter(|state| state.loaded_at.is_some())
+ .map(|state| state.font_families.clone())
+ }
+
+ /// Prefetch all font names in the background
+ pub async fn prefetch(&self, cx: &gpui::AsyncApp) {
+ if self
+ .state
+ .try_read()
+ .is_none_or(|state| state.loaded_at.is_some())
+ {
+ return;
+ }
+
+ let Ok(text_system) = cx.update(|cx| App::text_system(cx).clone()) else {
+ return;
+ };
+
+ let state = self.state.clone();
+
+ cx.background_executor()
+ .spawn(async move {
+ // We take this lock in the background executor to ensure that synchronous calls to `list_font_families` are blocked while we are prefetching,
+ // while not blocking the main thread and risking deadlocks
+ let mut lock = state.write();
+ let all_font_names = text_system
+ .all_font_names()
+ .into_iter()
+ .map(SharedString::from)
+ .collect();
+ lock.font_families = all_font_names;
+ lock.loaded_at = Some(Instant::now());
+ })
+ .await;
+ }
}