Merge fix/762 into batch 16

This commit is contained in:
bflatastic
2026-10-18 11:16:44 +07:00
2 changed files with 66 additions and 14 deletions
+4 -13
View File
@@ -274,8 +274,9 @@ fn word_at(text: &str, i: usize) -> (usize, usize) {
(a, b)
}
/// Faces the UI adds for scripts its own fonts lack (desktop: from the system), as egui font data.
pub fn script_fallbacks(have: &dyn Fn(char) -> bool) -> Vec<(String, egui::FontData)> {
/// Faces the UI adds for scripts its own fonts lack (desktop: from the system), as egui font data;
/// `taken` as for [`crate::theme::system_fallbacks`].
pub fn script_fallbacks(have: &dyn Fn(char) -> bool, taken: &mut Vec<Arc<figcraft_text::Face>>) -> Vec<(String, egui::FontData)> {
// Hebrew, Arabic letters, Arabic presentation forms (what joined names are drawn with).
const SAMPLES: [char; 3] = ['\u{05D0}', '\u{0628}', '\u{FE91}'];
const FAMILIES: [&str; 11] = [
@@ -291,17 +292,7 @@ pub fn script_fallbacks(have: &dyn Fn(char) -> bool) -> Vec<(String, egui::FontD
"FreeSans",
"Noto Sans",
];
let mut out: Vec<(String, egui::FontData)> = Vec::new();
let mut taken: Vec<Arc<figcraft_text::Face>> = Vec::new();
for c in SAMPLES {
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), crate::theme::font_data(&face)));
taken.push(face);
}
out
crate::theme::system_fallbacks(SAMPLES, &FAMILIES, have, taken)
}
#[cfg(test)]
+62 -1
View File
@@ -273,7 +273,9 @@ pub fn install_fonts(ctx: &Context, system: bool, cjk: u8) {
.filter_map(|f| figcraft_text::find_face(f, "Regular"))
.any(|x| x.has_char(c))
};
extra.extend(crate::bidi::script_fallbacks(&embedded));
let mut taken = Vec::new();
extra.extend(crate::bidi::script_fallbacks(&embedded, &mut taken));
extra.extend(key_symbol_fallbacks(&embedded, &mut taken));
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 +290,38 @@ 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 families that have them, in order of preference.
const KEY_FAMILIES: [&str; 6] = ["Lucida Grande", "Apple Symbols", "Segoe UI Symbol", "DejaVu Sans", "Noto Sans Symbols 2", "FreeSans"];
/// System faces for the shortcut glyphs the UI's own fonts lack (egui's default face has ⌘ but
/// not ⌥ or ↵); `taken` as for [`system_fallbacks`].
fn key_symbol_fallbacks(have: &dyn Fn(char) -> bool, taken: &mut Vec<Arc<figcraft_text::Face>>) -> Vec<(String, FontData)> {
system_fallbacks(KEY_SYMBOLS.chars(), &KEY_FAMILIES, have, taken)
}
/// System faces, the first of `families` that has each, for the `samples` neither the UI's own
/// fonts (`have`) nor the faces already `taken` draw. Each face found joins `taken`, so a face that
/// several lists name is added once.
pub(crate) fn system_fallbacks(
samples: impl IntoIterator<Item = char>,
families: &[&str],
have: &dyn Fn(char) -> bool,
taken: &mut Vec<Arc<figcraft_text::Face>>,
) -> Vec<(String, FontData)> {
let mut out = Vec::new();
for c in samples {
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 +365,30 @@ pub fn doc_c32(ctx: &Context, c: figcraft_color::Color) -> Color32 {
let conv = ctx.data(|d| d.get_temp::<figcraft_color::ProfileConversion>(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();
let lacking: String = KEY_SYMBOLS.chars().filter(|c| figcraft_text::face_with_char(*c, &KEY_FAMILIES).is_none()).collect();
if !lacking.is_empty() {
eprintln!("no system face here draws {lacking}; 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:?}");
}
}
});
}
}