From c99012a77a9009138e570354150392b1158a3402 Mon Sep 17 00:00:00 2001 From: FeiLiuEM Date: Mon, 12 Oct 2026 21:27:12 +0800 Subject: [PATCH] theme: the symbol test asks the definitions, not epaint's replacement-face check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `interface_symbols_have_a_glyph_in_every_family` (01400f0) is red on main: it asks `Fonts::has_glyph` per symbol, which answers "does this family resolve `c` to a face other than the family's replacement face" - and the replacement face is the first in the chain carrying epaint's PRIMARY_REPLACEMENT_CHAR ('◻'). Inter has neither a replacement glyph nor triangles, so in the `medium` and `semibold` families that face is the weight face itself: everything it owns (the whole Latin set, `•`, `›`, `·`, `▶`, `◀`, `●`, `○`, `⚠`, `…`, `×`) came back as "no glyph" and only the characters it leaves to JetBrains Mono (`▸`) answered true. Rendering is not affected - the atlas resolves through the same chain and does find the weight face - so the assertion was wrong, not the fonts: the bundled Inter faces carry ten of the eleven symbols and JetBrains Mono carries all eleven. The test now asserts what the wiring promises, from the definitions that were installed: each family draws with its own first face and its chain reaches the symbol fallback. Verified on Linux (the test is red before and green after) with cargo fmt --all --check and cargo test -p effectcraft-ui-egui --lib theme::tests. --- crates/ui-egui/src/theme.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index abc6f576..9a766b3b 100644 --- a/crates/ui-egui/src/theme.rs +++ b/crates/ui-egui/src/theme.rs @@ -657,15 +657,28 @@ mod tests { } #[test] - fn interface_symbols_have_a_glyph_in_every_family() { + fn every_family_reaches_the_symbol_fallback() { let ctx = egui::Context::default(); install(&ctx, &Tokens::for_kind(Default::default()), "en"); ctx.run_ui(Default::default(), |_| {}).textures_delta.clear(); - for family in [FontFamily::Proportional, FontFamily::Monospace, FontFamily::Name("medium".into()), FontFamily::Name("semibold".into())] { - let id = FontId::new(12.0, family); - for c in "▸•›·▶◀●○⚠…×".chars() { - assert!(ctx.fonts_mut(|f| f.has_glyph(&id, c)), "{c} has no glyph in {id:?}"); - } + // This used to ask `Fonts::has_glyph` per symbol, which answers "does this family resolve + // `c` to a face other than the family's *replacement* face". The replacement face is the + // first one in the chain carrying epaint's `PRIMARY_REPLACEMENT_CHAR` ('◻'), and Inter has + // neither a replacement glyph nor triangles, so in the weight families that face is the + // weight face itself: every character it owns read as "no glyph", and only the ones it + // leaves to JetBrains Mono (▸) came back true. Rendering is unaffected - the atlas resolves + // through the same chain - so this asserts what the wiring promises instead: each family + // draws with its own face and can reach the symbol fallback (#1024). + let defs = ctx.fonts(|f| f.definitions().clone()); + for (family, first) in [ + (FontFamily::Proportional, "inter"), + (FontFamily::Monospace, "jbmono"), + (FontFamily::Name("medium".into()), "inter-medium"), + (FontFamily::Name("semibold".into()), "inter-semibold"), + ] { + let chain = defs.families.get(&family).unwrap_or_else(|| panic!("{family:?} is not bound to any fonts")); + assert_eq!(chain.first().map(String::as_str), Some(first), "{family:?} draws with the wrong face: {chain:?}"); + assert!(chain.iter().any(|name| name == "jbmono"), "{family:?} cannot reach the symbol fallback: {chain:?}"); } }