Browse / github.com/storytold/effectcraft / patch
Font menu: a name the interface fonts cannot draw shows in English
Font menu: a name the interface fonts cannot draw shows in English Font menu rows use the font's English family name when the interface fonts cannot display every character in its native name. The tooltip uses the same readable name. Selecting the row still applies the font family. The fallback works in the shared dropdown control. The regressions check both displayable and missing-glyph names, then open the Character font menu, search for a synthetic font, and select its English-family row. The branch includes `main` at `bc4b48f6`. The seven-stage `cargo xtask ci` run on Windows 11 x86-64 with rustc 1.99.0 reported 4,002 test passes, zero failures, and 32
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- dotCooCoo (imported from pull request #2453, open)
- Base
bc4b48f67deef2458e9dd3e3b0e592d5f75768a1- Size
- +69 −2 in 2 files
- Review
- pending
- Shared
- 2026-10-28
- Id
7e4b57c8868a7e3b502e90a1
How it went
No other operator has reported using it yet.
Diff
raw · crates/ui-egui/src/panels/text_panels.rs crates/ui-egui/tests/it/ui_font_search.rs
@@ -717,10 +717,11 @@ pub(super) fn font_picker(app: &mut EffectcraftApp, ui: &mut egui::Ui, id: egui: let resp = ui.add_sized([row_width, 24.0], egui::Button::selectable(index == selected, "")); resp.widget_info(|| egui::WidgetInfo::selected(egui::Role::Button, ui.is_enabled(), index == selected, &r.display)); let label_rect = Rect::from_min_size(resp.rect.min + vec2(5.0, 0.0), vec2(label_width - 5.0, resp.rect.height()));+ let name = drawn_font_name(ui.ctx(), r); ui.painter().with_clip_rect(label_rect.intersect(ui.clip_rect())).text( label_rect.left_center(), Align2::LEFT_CENTER,- &r.display,+ name, Tokens::ui(12.0), t.text, );@@ -754,7 +755,7 @@ pub(super) fn font_picker(app: &mut EffectcraftApp, ui: &mut egui::Ui, id: egui: if resp.clicked() { chosen = Some(r.family.clone()); }- resp.on_hover_text(&r.display);+ resp.on_hover_text(name); } chosen };@@ -900,6 +901,13 @@ fn align_glyph(p: &egui::Painter, r: Rect, op: &str, distribute: bool, t: &Token } } +/// The name a font menu row draws: the font's own-language name when the interface fonts have a+/// glyph for each of its characters, else its English family name. A Simplified Chinese name+/// such as 微软雅黑 can have characters that the Han face of an English interface lacks.+fn drawn_font_name<'a>(ctx: &egui::Context, row: &'a effectcraft_engine::FontRow) -> &'a str {+ if ctx.fonts_mut(|f| f.has_glyphs(&Tokens::ui(12.0), &row.display)) { &row.display } else { &row.family }+}+ /// Where a font's "Sample" outlines (`lines`, baseline at y = 0) go in `room`: `(scale, origin)`, /// a point `p` landing at `origin + p * scale`. At their natural size on `baseline` when they fit; /// fonts with large or odd metrics are scaled down (never up) and moved inside, so no preview@@ -958,4 +966,17 @@ mod tests { assert!(fit_preview(&[], room, baseline).is_none()); assert!(fit_preview(&[vec![[f32::NAN, 1.0]]], room, baseline).is_none()); }++ /// A font menu row draws the font's own-language name when the interface fonts have its+ /// glyphs, and the English family name when they do not.+ #[test]+ fn font_menu_rows_draw_a_name_the_interface_fonts_have() {+ let ctx = egui::Context::default();+ crate::theme::install(&ctx, &Tokens::for_kind(Default::default()), "en");+ ctx.run_ui(Default::default(), |_| {}).textures_delta.clear();+ let row = |display: &str| effectcraft_engine::FontRow { family: "Menu Name Test".into(), display: display.into(), recent: false, variable: false };+ // U+F1234 is in a private use plane that no interface font maps.+ assert_eq!(drawn_font_name(&ctx, &row("Menu \u{F1234}")), "Menu Name Test");+ assert_eq!(drawn_font_name(&ctx, &row("Nom du menu")), "Nom du menu");+ } }@@ -106,6 +106,52 @@ fn font_search_preserves_edited_text_and_applies_only_to_selected_characters() { assert_eq!(doc.style_at(8).font, "Inter"); } +/// A font file of just `name`: the family and Regular in English, and the family again in+/// Simplified Chinese (language 0x804).+fn font_with_native_name(family: &str, native: &str) -> Vec<u8> {+ let u16s = |v: &[u16]| -> Vec<u8> { v.iter().flat_map(|x| x.to_be_bytes()).collect() };+ let records = [(0x409, 1, family), (0x409, 2, "Regular"), (0x804, 1, native)];+ let strings: Vec<Vec<u8>> = records.iter().map(|(_, _, s)| s.encode_utf16().flat_map(|u| u.to_be_bytes()).collect()).collect();+ let mut name = u16s(&[0, records.len() as u16, 6 + 12 * records.len() as u16]);+ let mut off = 0;+ for ((lang, id, _), st) in records.iter().zip(&strings) {+ name.extend(u16s(&[3, 1, *lang, *id, st.len() as u16, off]));+ off += st.len() as u16;+ }+ strings.iter().for_each(|st| name.extend(st));+ let mut out = u16s(&[1, 0, 1, 0, 0, 0]);+ out.extend(b"name");+ out.extend([0u8; 4]);+ out.extend(28u32.to_be_bytes());+ out.extend((name.len() as u32).to_be_bytes());+ out.extend(name);+ out+}++/// A family whose own-language name has a character no interface font has is listed under its+/// English name: the harness fails on a missing glyph, and the app would draw boxes. Picking the+/// row still applies the family.+#[test]+fn font_menu_lists_a_name_the_interface_fonts_lack_in_english() {+ const FAMILY: &str = "Native Name Menu Test";+ // U+F1234 is in a private use plane that no interface font maps.+ const NATIVE: &str = "中文\u{F1234}字体";+ assert_eq!(effectcraft_engine::text::fonts::add_font_data(font_with_native_name(FAMILY, NATIVE)).len(), 1);+ assert_eq!(effectcraft_engine::text::fonts::native_families().get(FAMILY).map(String::as_str), Some(NATIVE));+ let mut s = Session::default();+ s.execute("comp.new", json!({"width": 320, "height": 180})).unwrap();+ let lid = s.execute("layer.newText", json!({"text": "Native", "font": "Inter"})).unwrap()["layer"].as_u64().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");+ type_text(&mut h, "native name menu");+ let shown: Vec<String> = h.state().auto.query("character.font.option.").iter().map(|e| e.id.clone()).collect();+ assert_eq!(shown, vec![format!("character.font.option.{FAMILY}")]);+ click(&mut h, &format!("character.font.option.{FAMILY}"));+ assert_eq!(layer_doc(&h.state().session, LayerId(lid)).unwrap().font, FAMILY);+}+ /// A font file of just `name` (the family, Regular) and an `fvar` table with one axis pinned at /// 400: a variable font as far as the font menu's filter is concerned. fn pinned_variable_font(family: &str) -> Vec<u8> {