mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-31 05:27:08 -06:00
UI: rescan system fonts from the font menu; Linux scan smoke test (#1089)
Fonts installed after EffectCraft launched (dnf, GNOME Font Viewer, a drop into ~/.local/share/fonts) stayed invisible until restart: only the startup background scan ran, and the report in #1089 could not tell discovery gaps apart from a stale scan. - The font menu's filter row gains a Rescan Fonts button (Character and Properties panels share the popup) that runs the existing text.fonts {rescan} command and reports the number added in the status line. The press doesn't flip the Variable-fonts filter (the button is above the row's click area and closes the popup on top of it). - system_scan_finds_fonts now asserts, on a native fontconfig desktop (any machine with /etc/fonts, which includes the Fedora reporter's and the CI runner), that the scan lists installed families rather than silently returning only the bundled fonts: 870 faces in ~10 ms on an Arch box. Tests: ui_font_search 4 passed (new rescan test drives the real popup); effectcraft-text lib system scan green; fmt and clippy --all-targets -D warnings clean. Translations for 'Rescan Fonts' in all six catalogs.
This commit is contained in:
@@ -943,6 +943,11 @@ mod tests {
|
||||
let n = all_faces().iter().filter(|f| f.info.origin == "system").count();
|
||||
eprintln!("system fonts: {n} faces in {:?}", t.elapsed());
|
||||
assert_eq!(scan_system(), 0, "scans once");
|
||||
if cfg!(all(unix, not(target_os = "macos"), not(target_arch = "wasm32"))) && std::path::Path::new("/etc/fonts").exists() {
|
||||
// #1089: a fontconfig desktop (Fedora, Arch, Ubuntu, the CI runner): the scan must
|
||||
// list the installed system families, not just the bundled three.
|
||||
assert!(n > 20, "fontconfig system fonts missing: {n}");
|
||||
}
|
||||
if cfg!(target_os = "macos") {
|
||||
assert!(n > 20, "{n}");
|
||||
let r = resolve("Helvetica", "Bold");
|
||||
|
||||
@@ -351,6 +351,7 @@ const JAPANESE: &[(&str, &str)] = &[
|
||||
("No layer selected", "レイヤーが選択されていません"),
|
||||
("No matching effects or presets", "一致するエフェクトまたはプリセットはありません"),
|
||||
("No matching fonts", "一致するフォントはありません"),
|
||||
("Rescan Fonts", "フォントを再スキャン"),
|
||||
("Variable fonts only", "可変フォントのみ"),
|
||||
("No model credits were built into this copy.", "このビルドにはモデルのクレジットが含まれていません。"),
|
||||
("No nested compositions", "ネストされたコンポジションはありません"),
|
||||
@@ -893,6 +894,7 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
|
||||
("No layer selected", "未选择图层"),
|
||||
("No matching effects or presets", "没有匹配的效果或预设"),
|
||||
("No matching fonts", "没有匹配的字体"),
|
||||
("Rescan Fonts", "重新扫描字体"),
|
||||
("Variable fonts only", "仅可变字体"),
|
||||
("No model credits were built into this copy.", "此副本未内置模型致谢。"),
|
||||
("No nested compositions", "没有嵌套合成"),
|
||||
@@ -1626,6 +1628,7 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
|
||||
("No layer selected", "未選取圖層"),
|
||||
("No matching effects or presets", "沒有匹配的效果或預設集"),
|
||||
("No matching fonts", "沒有符合的字體"),
|
||||
("Rescan Fonts", "重新掃描字型"),
|
||||
("Variable fonts only", "僅可變字型"),
|
||||
("No model credits were built into this copy.", "此副本未內置模型致謝。"),
|
||||
("No nested compositions", "沒有嵌套合成"),
|
||||
@@ -2368,6 +2371,7 @@ const UKRAINIAN: &[(&str, &str)] = &[
|
||||
("No layer selected", "Шар не виділено"),
|
||||
("No matching effects or presets", "Не знайдено відповідних ефектів або наборів"),
|
||||
("No matching fonts", "Не знайдено відповідних шрифтів"),
|
||||
("Rescan Fonts", "Повторно сканувати шрифти"),
|
||||
("Variable fonts only", "Лише змінні шрифти"),
|
||||
("No model credits were built into this copy.", "Ця копія не містить відомостей про моделі."),
|
||||
("No nested compositions", "Немає вкладених композицій"),
|
||||
@@ -2895,6 +2899,7 @@ const PORTUGUESE: &[(&str, &str)] = &[
|
||||
("New", "Novo"),
|
||||
("New Comp from Selection", "Nova composição a partir da seleção"),
|
||||
("Replace with Precomposition", "Substituir por pré-composição"),
|
||||
("Rescan Fonts", "Reanalisar fontes"),
|
||||
("used once", "usado uma vez"),
|
||||
("used {} times", "usado {} vezes"),
|
||||
("Compositions that use this item", "Composições que usam este item"),
|
||||
@@ -3349,6 +3354,7 @@ const CZECH: &[(&str, &str)] = &[
|
||||
("New", "Nový"),
|
||||
("New Comp from Selection", "Nová kompozice z výběru"),
|
||||
("Replace with Precomposition", "Nahradit předkompozicí"),
|
||||
("Rescan Fonts", "Znovu nalézt písma"),
|
||||
("used once", "použito jednou"),
|
||||
("used {} times", "použito {}×"),
|
||||
("Compositions that use this item", "Kompozice, které tuto položku používají"),
|
||||
@@ -3926,6 +3932,7 @@ const SPANISH: &[(&str, &str)] = &[
|
||||
("No layer selected", "No hay ninguna capa seleccionada"),
|
||||
("No matching effects or presets", "No hay efectos ni ajustes preestablecidos que coincidan"),
|
||||
("No matching fonts", "No hay fuentes coincidentes"),
|
||||
("Rescan Fonts", "Volver a analizar fuentes"),
|
||||
("Variable fonts only", "Solo fuentes variables"),
|
||||
("No model credits were built into this copy.", "No se incluyeron créditos de modelos en esta copia."),
|
||||
("No nested compositions", "No hay composiciones anidadas"),
|
||||
|
||||
@@ -642,6 +642,17 @@ pub(super) fn font_picker(app: &mut EffectcraftApp, ui: &mut egui::Ui, id: egui:
|
||||
// Typing goes on into the search field.
|
||||
ui.memory_mut(|m| m.request_focus(search_id));
|
||||
}
|
||||
// #1089: fonts installed after launch (dnf, GNOME Fonts, a drop into ~/.fonts) appear
|
||||
// without restarting EffectCraft: the system scan also runs in the background at
|
||||
// startup, this re-runs it on demand.
|
||||
let resc = Rect::from_min_size(pos2(row.rect.max.x - 124.0, row.rect.min.y + 2.0), vec2(120.0, filter_h - 4.0));
|
||||
if widgets::text_button(ui, resc, tr("Rescan Fonts"), false, &t, id.with("rescan")).clicked() {
|
||||
match app.session.execute("text.fonts", json!({"rescan": true})) {
|
||||
Ok(v) => app.ui.status = format!("{} +{}", tr("Rescan Fonts"), v.get("added").and_then(serde_json::Value::as_u64).unwrap_or(0)),
|
||||
Err(e) => app.ui.status = e.to_string(),
|
||||
}
|
||||
}
|
||||
app.auto.add(&format!("{auto}.rescan"), resc, "Rescan Fonts");
|
||||
let query = state.query.trim().to_lowercase();
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
let filtered: Vec<_> = rows
|
||||
|
||||
@@ -176,3 +176,24 @@ fn font_menu_filters_variable_fonts() {
|
||||
key(&mut h, Key::Escape);
|
||||
assert_eq!(layer_doc(&h.state().session, LayerId(lid)).unwrap().font, "Inter", "filtering changes nothing");
|
||||
}
|
||||
|
||||
/// #1089: the font menu can re-run the system scan without restarting the app, and the press
|
||||
/// does not also flip the "Variable fonts only" filter.
|
||||
#[test]
|
||||
fn font_menu_rescan_button_adds_fonts_installed_later() {
|
||||
let mut s = Session::default();
|
||||
s.execute("comp.new", json!({"width": 320, "height": 180})).unwrap();
|
||||
s.execute("layer.newText", json!({"text": "Keep this text", "font": "Inter"})).unwrap();
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).build_eframe(move |_| EffectcraftApp::new(s));
|
||||
h.state_mut().show_panel(PanelKind::Character);
|
||||
h.run_steps(3);
|
||||
click(&mut h, "character.font");
|
||||
h.run_steps(3);
|
||||
assert!(h.state().auto.find("character.font.rescan").is_some(), "the filter row offers a rescan");
|
||||
click(&mut h, "character.font.rescan");
|
||||
assert!(h.state().ui.status.contains("Rescan Fonts"), "the status reports the rescan: {}", h.state().ui.status);
|
||||
// The popup stays open, the variable filter did not flip, and the list still has the bundled families.
|
||||
assert!(h.state().auto.find("character.font.search").is_some(), "the popup stays open");
|
||||
let rows = h.state().auto.query("character.font.option.");
|
||||
assert!(rows.iter().any(|r| r.label == "Inter"), "{:?}", rows.iter().map(|r| &r.label).collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user