mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-26 19:18:14 -06:00
ui: the interface prefers the platform's UI faces, rendered text keeps the print ones
Windows ships a UI variant of its Japanese and Chinese families (`Meiryo`/`Meiryo UI`, `Yu Gothic`/`Yu Gothic UI`, `MS Gothic`/`MS UI Gothic`, `Microsoft YaHei`/`Microsoft YaHei UI`, `Microsoft JhengHei`/`Microsoft JhengHei UI`) whose kana and kanji are drawn for the screen, and its own link table for `Segoe UI` picks the UI one for everything it draws. The interface should ask for it - but the same lists feed `fonts::fallback_for`, which shapes composed frames, so reordering them would change how existing projects render. The two orders are therefore separate: this leaves `crates/text/src/fonts.rs` exactly as it is and gives the interface its own candidate list in `install_script_fallbacks`, which falls back to the shared choice when the platform has no UI variant, none is installed, or it does not cover the probe. `PMingLiU` is in the interface list for its proportional spacing, not as a sibling of `MingLiU`: it is MingLiU's proportional relative. The rendered order is pinned by a test now: the Japanese list keeps `Yu Gothic` ahead of `Yu Gothic UI`, and no UI name appears in the lists a document renders with. `the_interface_asks_for_the_ platforms_ui_faces` pins the interface list and checks that every other script the install probes has none, so a script that draws correctly in any face keeps the shared order. Review fixes: the locale is mapped once (the call passes the language, the helper maps it), the new test sits after the #394 test it had split from its doc comment, and the fonts.rs test asserts instead of panicking inside a closure. Rebased on current main; #1395 has not landed yet, so there is nothing to rebase after it.
This commit is contained in:
@@ -1144,6 +1144,30 @@ mod tests {
|
||||
assert_eq!(han_order_for("ja_JP.UTF-8")[0], FallbackScript::Japanese);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_rendered_order_keeps_the_print_face_first() {
|
||||
// A composed frame renders with these lists, so they keep the platform's print order: the
|
||||
// interface prefers the platform's UI faces on its own (see `install_script_fallbacks` in
|
||||
// the ui crate), and an existing project must not render differently because a window's
|
||||
// chrome changed.
|
||||
let pos = |list: &[&str], name: &str| list.iter().position(|f| *f == name).unwrap_or(usize::MAX);
|
||||
assert!(
|
||||
pos(JAPANESE_FAMILIES, "Yu Gothic") < pos(JAPANESE_FAMILIES, "Yu Gothic UI"),
|
||||
"the print face must come before its UI sibling: {JAPANESE_FAMILIES:?}"
|
||||
);
|
||||
for name in ["Meiryo UI", "MS UI Gothic"] {
|
||||
assert!(!JAPANESE_FAMILIES.contains(&name), "{name} belongs to the interface order, not this list");
|
||||
}
|
||||
assert_eq!(SIMPLIFIED_CHINESE_FAMILIES[0], "Microsoft YaHei");
|
||||
assert_eq!(TRADITIONAL_CHINESE_FAMILIES[0], "Microsoft JhengHei");
|
||||
for name in ["Microsoft YaHei UI", "Microsoft JhengHei UI", "PMingLiU"] {
|
||||
assert!(
|
||||
!SIMPLIFIED_CHINESE_FAMILIES.contains(&name) && !TRADITIONAL_CHINESE_FAMILIES.contains(&name),
|
||||
"{name} belongs to the interface order, not these lists"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bundled_ui_fonts_cover_the_ukrainian_alphabet() {
|
||||
const ALPHABET: &str = "АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯабвгґдеєжзиіїйклмнопрстуфхцчшщьюя’«»";
|
||||
|
||||
@@ -409,7 +409,15 @@ fn install_script_fallbacks(family_fonts: &mut FontDefinitions, language: &str)
|
||||
let mut names = Vec::new();
|
||||
let mut seen = Vec::new();
|
||||
for &(probe, name) in cjk.iter().chain(SCRIPT_PROBES).chain(std::iter::once(&HANGUL)) {
|
||||
let id = fonts::fallback_for(probe, base);
|
||||
// The interface asks for the platform's UI face first; the order a composed frame
|
||||
// renders with is `fonts::fallback_for`'s and is not touched here.
|
||||
let id = interface_fallback_candidates(name, language)
|
||||
.iter()
|
||||
.find_map(|family| {
|
||||
let r = fonts::resolve(family, "Regular");
|
||||
(!r.missing && fonts::face(r.face).has_char(probe)).then_some(r.face)
|
||||
})
|
||||
.unwrap_or_else(|| fonts::fallback_for(probe, base));
|
||||
if seen.contains(&id) {
|
||||
continue;
|
||||
}
|
||||
@@ -432,6 +440,31 @@ fn install_script_fallbacks(family_fonts: &mut FontDefinitions, language: &str)
|
||||
}
|
||||
}
|
||||
|
||||
/// The faces the interface asks for before it settles on the one a composed frame renders with.
|
||||
///
|
||||
/// Windows ships a UI variant of its Japanese and Chinese families - `Meiryo`/`Meiryo UI`, `Yu
|
||||
/// Gothic`/`Yu Gothic UI`, `MS Gothic`/`MS UI Gothic`, `Microsoft YaHei`/`Microsoft YaHei UI`,
|
||||
/// `Microsoft JhengHei`/`Microsoft JhengHei UI` - and its own link table for `Segoe UI`
|
||||
/// (`FontLink\SystemLink`) picks the UI one for what it draws on screen. `PMingLiU` is the one face
|
||||
/// here that is not a sibling: it is `MingLiU`'s proportional relative, and proportional spacing is
|
||||
/// what makes the chrome read better in it.
|
||||
///
|
||||
/// Only the interface takes this order. `fonts::fallback_for` keeps the order a document renders
|
||||
/// with, so an existing project's output does not move when the chrome changes.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn interface_fallback_candidates(name: &str, language: &str) -> &'static [&'static str] {
|
||||
match (name, cjk_locale(language)) {
|
||||
// Kana, and the Han ideographs of a Japanese interface: the language asks for the Japanese
|
||||
// face, so its UI sibling is the one the platform would draw chrome with.
|
||||
("japanese-system", _) | ("chinese-system", "ja") => &["Meiryo UI", "Yu Gothic UI", "MS UI Gothic"],
|
||||
("chinese-system", "zh-hant") => &["Microsoft JhengHei UI", "PMingLiU"],
|
||||
("chinese-system", "zh-hans") => &["Microsoft YaHei UI"],
|
||||
// The other scripts have no UI variant, and their shared order already picks the face made
|
||||
// for the script - the one that draws it correctly at all.
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
/// The CJK locale the text engine should prefer for Han text: the interface language when it names
|
||||
/// one, otherwise the empty string, which leaves the operating system's locale in charge.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -530,6 +563,7 @@ pub fn apply_visuals(ctx: &egui::Context, t: &Tokens) {
|
||||
|
||||
#[cfg(all(test, not(target_arch = "wasm32")))]
|
||||
mod script_font_tests {
|
||||
use super::{SCRIPT_PROBES, interface_fallback_candidates};
|
||||
use effectcraft_text::fonts;
|
||||
|
||||
/// Whether an installed face covers `probe` (else the glyph check is skipped).
|
||||
@@ -581,6 +615,7 @@ mod script_font_tests {
|
||||
/// #394: Arabic, Hebrew, Indic and Thai names are drawn by an installed face in an English
|
||||
/// interface: the UI fonts were Inter and the CJK fallbacks only, so a layer named in Arabic
|
||||
/// showed as missing-glyph boxes.
|
||||
|
||||
#[test]
|
||||
fn installed_script_fallbacks_draw_arabic_hebrew_indic_and_thai_names() {
|
||||
// Letters only: a vowel sign or virama on its own is drawn on a dotted circle.
|
||||
@@ -595,6 +630,21 @@ mod script_font_tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_interface_asks_for_the_platforms_ui_faces() {
|
||||
// The face a window draws its own chrome with, which is not the one a composed frame
|
||||
// renders with: the two orders are deliberately separate.
|
||||
let ja = &["Meiryo UI", "Yu Gothic UI", "MS UI Gothic"][..];
|
||||
assert_eq!(interface_fallback_candidates("japanese-system", "ja"), ja);
|
||||
assert_eq!(interface_fallback_candidates("chinese-system", "ja"), ja);
|
||||
assert_eq!(interface_fallback_candidates("chinese-system", "zh-hans"), &["Microsoft YaHei UI"][..]);
|
||||
assert_eq!(interface_fallback_candidates("chinese-system", "zh-hant"), &["Microsoft JhengHei UI", "PMingLiU"][..]);
|
||||
// Every other script the install probes has no UI variant, so it keeps the shared order.
|
||||
for &(_, name) in SCRIPT_PROBES.iter() {
|
||||
assert!(interface_fallback_candidates(name, "en").is_empty(), "{name} asked for a UI face");
|
||||
}
|
||||
}
|
||||
|
||||
/// #1141: switching the interface to Simplified Chinese drew most labels as boxes. The English
|
||||
/// interface had already loaded a Japanese face for kana, and that face covers the Han probe,
|
||||
/// so it became the Chinese fallback too, without the simplified-only hanzi.
|
||||
|
||||
Reference in New Issue
Block a user