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 <cursoragent@cursor.com>
This commit is contained in:
nrq6993-a11y
2026-10-18 18:46:03 +07:00
committed by bflatastic
co-authored by Cursor
parent bf2379f401
commit 640c87b537
3 changed files with 38 additions and 2 deletions
+6
View File
@@ -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
+19
View File
@@ -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<Vec<String>> = 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]
+13 -2
View File
@@ -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<std::sync::mpsc::Receiver<bool>>,
/// 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) {