mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-30 04:37:29 -06:00
Merge pull request #2884 from nrq6993-a11y/stream/vc-issue-2576-font-poll
Fix #2576: poll for fonts that font managers activate while the app is in front
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -332,18 +332,30 @@ fn service_font() -> PathBuf {
|
||||
}
|
||||
|
||||
/// 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).
|
||||
/// cataloged: named by the platform's lister, read whatever their file is called (#579), and one it
|
||||
/// activates after a scan is picked up by the next check (#2576). One test: the lister is set once
|
||||
/// per process, so two tests changing what it lists would race.
|
||||
#[test]
|
||||
fn fonts_the_platform_lists_outside_the_font_folders_are_cataloged() {
|
||||
use std::sync::Mutex;
|
||||
static FILES: Mutex<Vec<String>> = Mutex::new(Vec::new());
|
||||
let list = |files: Vec<String>| *FILES.lock().unwrap_or_else(|e| e.into_inner()) = files;
|
||||
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 _ = std::fs::remove_file(&file);
|
||||
// Nothing listed at the first scan; then a font service activates a font.
|
||||
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();
|
||||
// The lister's files outside the font folders are scanned with them, each once (a collection
|
||||
// lists its file once per face).
|
||||
fn lister() -> Vec<String> {
|
||||
vec![service_font().to_string_lossy().into_owned(); 2]
|
||||
}
|
||||
set_platform_font_files(lister);
|
||||
// A collection lists its file once per face.
|
||||
list(vec![file.to_string_lossy().into_owned(); 2]);
|
||||
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 lister's files outside the font folders are scanned with them, each once.
|
||||
let dirs = system_font_dirs();
|
||||
assert_eq!(dirs.iter().filter(|d| **d == file).count(), 1, "{dirs:?}");
|
||||
// A file named by itself is read without a font's extension, one in a folder isn't.
|
||||
|
||||
+31
-12
@@ -452,9 +452,12 @@ pub struct VectorcraftApp {
|
||||
pub(crate) picks: picks::Picks,
|
||||
/// The screen color sampler shown and not answered yet, and where its color goes.
|
||||
pub(crate) screen_sampler: Option<screen_color::Waiting>,
|
||||
/// 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>>,
|
||||
/// The look for fonts installed or removed meanwhile, running
|
||||
/// ([`Self::refresh_installed_fonts`]): whether they were, and whether to wake the app for
|
||||
/// the answer.
|
||||
font_check: Option<(std::sync::mpsc::Receiver<bool>, 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 +503,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 +567,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 +1204,12 @@ 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;
|
||||
// Read on the next frame the app draws anyway: an idle app isn't woken for it.
|
||||
self.refresh_installed_fonts(false);
|
||||
}
|
||||
self.poll_font_check(ctx);
|
||||
picks::poll(self, ctx);
|
||||
self.write_formats_before_text_input(ctx);
|
||||
@@ -1292,12 +1304,15 @@ 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) {
|
||||
if self.font_check.is_some() {
|
||||
/// meanwhile takes tens of milliseconds on Windows, #579), [`Self::poll_font_check`] scans;
|
||||
/// `wake` wakes the app for the answer.
|
||||
fn refresh_installed_fonts(&mut self, wake: bool) {
|
||||
if let Some((_, w)) = &mut self.font_check {
|
||||
*w |= wake;
|
||||
return;
|
||||
}
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
@@ -1310,12 +1325,12 @@ impl VectorcraftApp {
|
||||
if spawned.is_none() {
|
||||
look();
|
||||
}
|
||||
self.font_check = Some(rx);
|
||||
self.font_check = Some((rx, wake));
|
||||
}
|
||||
|
||||
/// Rescan the fonts once [`Self::refresh_installed_fonts`]'s look says they changed.
|
||||
fn poll_font_check(&mut self, ctx: &egui::Context) {
|
||||
let Some(check) = &self.font_check else { return };
|
||||
let Some((check, wake)) = &self.font_check else { return };
|
||||
match check.try_recv() {
|
||||
Ok(changed) => {
|
||||
self.font_check = None;
|
||||
@@ -1324,7 +1339,11 @@ impl VectorcraftApp {
|
||||
let _ = self.run("text.rescanFonts", json!({}));
|
||||
}
|
||||
}
|
||||
Err(std::sync::mpsc::TryRecvError::Empty) => ctx.request_repaint_after(std::time::Duration::from_millis(50)),
|
||||
Err(std::sync::mpsc::TryRecvError::Empty) => {
|
||||
if *wake {
|
||||
ctx.request_repaint_after(std::time::Duration::from_millis(50));
|
||||
}
|
||||
}
|
||||
Err(std::sync::mpsc::TryRecvError::Disconnected) => self.font_check = None,
|
||||
}
|
||||
}
|
||||
@@ -1346,7 +1365,7 @@ impl VectorcraftApp {
|
||||
match e {
|
||||
egui::Event::ModifiersChanged(m) => self.host_modifiers = *m,
|
||||
egui::Event::WindowFocused(false) => self.host_modifiers = egui::Modifiers::NONE,
|
||||
egui::Event::WindowFocused(true) => self.refresh_installed_fonts(),
|
||||
egui::Event::WindowFocused(true) => self.refresh_installed_fonts(true),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user