From b84ee179d474da8024dd55a707a40cb1f8e19a97 Mon Sep 17 00:00:00 2001 From: JimKnoxx Date: Sat, 17 Oct 2026 17:05:49 +0200 Subject: [PATCH] M8: draw the macOS shortcut glyphs the UI font lacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Menus and tooltips showed boxes for ⌃ ⌥ ↵ ⌫ ⇥ ⇞ ⇟ (e.g. Present ⌥⌘↵): egui's default face has ⌘ and ⇧ but not these. The desktop app now adds a system face that has them after the UI fonts. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/theme.rs | 47 +++++++++++++++++++++++++++++++++++++ docs/ui-parity.md | 1 + 2 files changed, 48 insertions(+) diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index 080bce43..933ebece 100644 --- a/crates/ui-egui/src/theme.rs +++ b/crates/ui-egui/src/theme.rs @@ -274,6 +274,7 @@ pub fn install_fonts(ctx: &Context, system: bool, cjk: u8) { .any(|x| x.has_char(c)) }; extra.extend(crate::bidi::script_fallbacks(&embedded)); + extra.extend(key_symbol_fallbacks(&embedded)); extra.extend(crate::cjk::faces(cjk, lang, &embedded).iter().map(|f| (format!("system-{}-{}", f.family, f.style), font_data(f)))); } extra.extend(craft(true)); @@ -288,6 +289,26 @@ pub fn install_fonts(ctx: &Context, system: bool, cjk: u8) { ctx.set_fonts(fonts); } +/// The glyphs macOS shortcuts are written with ([`figcraft_engine::keymap::display`]). +const KEY_SYMBOLS: &str = "⌃⌥⇧⌘⌫↵⇥⇞⇟←→↑↓"; + +/// System faces for the shortcut glyphs the UI's own fonts lack (egui's default face has ⌘ but +/// not ⌥ or ↵). +fn key_symbol_fallbacks(have: &dyn Fn(char) -> bool) -> Vec<(String, FontData)> { + const FAMILIES: [&str; 6] = ["Lucida Grande", "Apple Symbols", "Segoe UI Symbol", "DejaVu Sans", "Noto Sans Symbols 2", "FreeSans"]; + let mut out = Vec::new(); + let mut taken: Vec> = Vec::new(); + for c in KEY_SYMBOLS.chars() { + if have(c) || taken.iter().any(|f| f.has_char(c)) { + continue; + } + let Some(face) = figcraft_text::face_with_char(c, &FAMILIES) else { continue }; + out.push((format!("system-{}-{}", face.family, face.style), font_data(&face))); + taken.push(face); + } + out +} + /// egui font data over a face's bytes, without copying them (CJK faces are 10–20 MB). Font /// database faces are never dropped, so a system face's shared bytes are kept for good: leaking /// one more reference to them costs a pointer and gives egui the `'static` slice it needs. @@ -331,3 +352,29 @@ pub fn doc_c32(ctx: &Context, c: figcraft_color::Color) -> Color32 { let conv = ctx.data(|d| d.get_temp::(screen_conversion_id())); c32(conv.map_or(c, |k| k.apply(c))) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ui_font_draws_every_mac_shortcut_glyph() { + figcraft_text::scan_system_fonts(); + if key_symbol_fallbacks(&|_| false).is_empty() { + eprintln!("no system face with shortcut glyphs; skipping"); + return; + } + let ctx = Context::default(); + install_fonts(&ctx, true, 0); + let input = + egui::RawInput { screen_rect: Some(egui::Rect::from_min_size(egui::pos2(0.0, 0.0), egui::vec2(100.0, 100.0))), ..Default::default() }; + ctx.run_ui(input, |_| {}).textures_delta.clear(); + ctx.fonts_mut(|f| { + for c in KEY_SYMBOLS.chars() { + for font in [regular(11.0), semibold(11.0)] { + assert!(f.has_glyphs(&font, &c.to_string()), "UI font draws {c:?}"); + } + } + }); + } +} diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 0cccf008..a28749bf 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -172,6 +172,7 @@ in the table. `crates/ui-egui/src/shortcut_keys_tests.rs` drives the keys a look | Date | Change | Summary | |---|---|---| +| 2026-10-17 | patch | macOS shortcut keycaps draw ⌃ ⌥ ↵ ⌫ ⇥ ⇞ ⇟ in menus and tooltips (they were boxes): the desktop app adds a system face with them (Lucida Grande, Apple Symbols, Segoe UI Symbol, DejaVu Sans…) after the UI fonts. Checked in the app on macOS. The web build on a Mac still shows boxes (no system fonts there). Summary and hours unchanged | | 2026-10-17 | minor | The community batches #733, #735, #736, #729 and #753: submenus open after a short hover, keep their place along a safe triangle and reopen closed (main and context menus); the Actions palette's tabs, ranked search, keyboard highlight and recents; menu items that need a value ask for it, File ▸ Rename edits in place; Shift+I and Resources open Assets; a draggable Pages/Layers divider; Shift-drag moves the selection; a double-click near a thin stroke, merging dropped points and Flatten keeping open paths in vector editing; live font preview and the selection highlight after line breaks; the colour picker on a new fill or stroke; Paste over and Paste properties from the keyboard; Selection colors (partial: no library styles or variables there) and smart-selection spacing fields and canvas handles (partial: no reorder rings), both also checked by their author on macOS; the keyboard shortcuts panel closes on the home screen; Adwaita window controls on Wayland compositors without server decorations (checked by its author on GNOME; KDE not run). Checked in the app on Windows (submenus by their author), not side by side with Figma. Summary and hours unchanged | | 2026-10-15 | minor | Hex fields take CSS colour names (`red` → `FF0000`): fills, strokes, effects, grids, variables, backgrounds and the colour picker | | 2026-10-15 | minor | Typography: drag the line-height, letter-spacing and paragraph-spacing icons to scrub their values; shared numeric-field rates, resize cursor and one Undo step per gesture (#373). |