From 640c87b537e01d53cbd39404e8914dfa3e5a4b53 Mon Sep 17 00:00:00 2001 From: nrq6993-a11y Date: Sun, 18 Oct 2026 12:04:29 +0300 Subject: [PATCH] Fix #2576: poll for fonts font managers activate while the app is in front Font services (NexusFont, Adobe Fonts) register faces with the OS without touching font-folder mtimes; only re-asking the platform lister sees them. Check every two seconds as well as on window focus, and test that path churn is detected on the system font scan. Co-authored-by: Cursor --- crates/text/src/fontdb.rs | 6 ++++++ crates/text/src/tests_sysfonts.rs | 19 +++++++++++++++++++ crates/ui-egui/src/lib.rs | 15 +++++++++++++-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/crates/text/src/fontdb.rs b/crates/text/src/fontdb.rs index 23691e9ba..3acf0fd04 100644 --- a/crates/text/src/fontdb.rs +++ b/crates/text/src/fontdb.rs @@ -1611,6 +1611,12 @@ impl FontDb { Self::new(FontPaths::Fixed(font_dirs)) } + /// Same scan paths as [`FontDb::global`] (platform lister and font folders), for tests. + #[cfg(test)] + pub(crate) fn for_system_scan_test() -> Self { + Self::new(FontPaths::System) + } + fn new(font_paths: FontPaths) -> Self { let mut faces = Vec::new(); // Bundled fonts first, then explicitly supplied craft-fonts faces: Japanese (Mincho diff --git a/crates/text/src/tests_sysfonts.rs b/crates/text/src/tests_sysfonts.rs index 49721a2e2..e2afc4172 100644 --- a/crates/text/src/tests_sysfonts.rs +++ b/crates/text/src/tests_sysfonts.rs @@ -331,6 +331,25 @@ fn service_font() -> PathBuf { std::env::temp_dir().join(format!("vc-sysfonts-{}-service", std::process::id())).join(".29457") } +/// Fonts a font service activates after the first scan are picked up on the next check (#2576). +#[test] +fn the_database_tells_when_a_font_service_adds_a_font() { + use std::sync::Mutex; + static FILES: Mutex> = Mutex::new(Vec::new()); + set_platform_font_files(|| FILES.lock().unwrap_or_else(|e| e.into_inner()).clone()); + let file = service_font(); + std::fs::create_dir_all(file.parent().unwrap()).unwrap(); + let db = FontDb::for_system_scan_test(); + assert!(!db.installed_fonts_changed()); + db.load_system_fonts(); + assert!(!db.has_family("Service Sans3")); + std::fs::write(&file, renamed_to("SourceSans3-Regular.ttf", "Service Sans3")).unwrap(); + *FILES.lock().unwrap_or_else(|e| e.into_inner()) = vec![file.to_string_lossy().into_owned()]; + assert!(db.installed_fonts_changed(), "a font a font service activated since the scan"); + db.load_system_fonts(); + assert!(db.has_family("Service Sans3")); +} + /// The fonts a font service (Adobe Fonts on Windows, through DirectWrite) loads in place are /// cataloged: named by the platform's lister, read whatever their file is called (#579). #[test] diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 834174217..1455f1f80 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -455,6 +455,8 @@ pub struct VectorcraftApp { /// The look for fonts installed or removed while the app was in the background, running /// ([`Self::refresh_installed_fonts`]): whether they were. font_check: Option>, + /// When installed fonts were last checked in line (app time, s). + font_install_check_at: f64, /// Keyboard pastes of something other than text (see [`shortcuts::PasteChord`]). pub(crate) paste_chord: shortcuts::PasteChord, /// Saves and exports running in the background (Preferences → File Handling). @@ -500,6 +502,8 @@ struct PendingScreenshot { /// Seconds between two looks at the system clipboard for [`VectorcraftApp::system_paste`]. const SYSTEM_CLIPBOARD_POLL: f64 = 0.25; +/// Seconds between looks for fonts a font manager activated while the app stays in front (#2576). +const FONT_INSTALL_POLL: f64 = 2.0; impl VectorcraftApp { pub fn new(mut session: Session, services: Services) -> Self { @@ -562,6 +566,7 @@ impl VectorcraftApp { picks: picks::Picks::default(), screen_sampler: None, font_check: None, + font_install_check_at: f64::NEG_INFINITY, paste_chord: Default::default(), background: Default::default(), opening: Default::default(), @@ -1198,6 +1203,11 @@ impl VectorcraftApp { self.system_paste_at = now; self.system_paste = wanted && self.system_clipboard_pasteable(); } + #[cfg(not(target_arch = "wasm32"))] + if !(0.0..FONT_INSTALL_POLL).contains(&(now - self.font_install_check_at)) { + self.font_install_check_at = now; + self.refresh_installed_fonts(); + } self.poll_font_check(ctx); picks::poll(self, ctx); self.write_formats_before_text_input(ctx); @@ -1292,8 +1302,9 @@ impl VectorcraftApp { place::drop_files(self, files); } - /// Fonts installed or removed while the app was in the background are listed when it comes - /// back (Refresh Font List by itself): a look at the font folders, a scan only when they changed. + /// Fonts installed or removed while the app runs are listed without Refresh Font List: a look + /// at the font folders and the platform's font list on a timer and when the window comes back, + /// a scan only when they changed. /// The look runs on another thread (asking DirectWrite for the fonts font services loaded /// meanwhile takes tens of milliseconds on Windows, #579), [`Self::poll_font_check`] scans. fn refresh_installed_fonts(&mut self) {