diff --git a/crates/benilla-app/src/ui_script/lifecycle.rs b/crates/benilla-app/src/ui_script/lifecycle.rs index 6152014f..5e5a84fb 100644 --- a/crates/benilla-app/src/ui_script/lifecycle.rs +++ b/crates/benilla-app/src/ui_script/lifecycle.rs @@ -738,6 +738,16 @@ fn seat_raster_seam_for_load(world: &mut World, script: &mut UiScript) { // out yet. script.set_screen_size(w / s, h / s); } + // Built here if it is not built yet: the atlas's own `init` is an `Update` system inserting + // through `Commands`, and this runs on the `InWorld` transition, which comes first — see + // `ui_text::engine::gpu::ensure_font_atlas` for what a missing measurer costs the load. + if !crate::ui_text::ensure_font_atlas(world) { + warn!( + "ui_script: no font atlas at world entry — every GetStringWidth during this UI load \ + will answer 0, and any `` laying out from a label's width will be wrong" + ); + return; + } let Some(atlas) = world.get_resource::() else { return; }; diff --git a/crates/benilla-app/src/ui_text/engine/gpu.rs b/crates/benilla-app/src/ui_text/engine/gpu.rs index 48c08dbf..bea41308 100644 --- a/crates/benilla-app/src/ui_text/engine/gpu.rs +++ b/crates/benilla-app/src/ui_text/engine/gpu.rs @@ -77,6 +77,50 @@ fn init( }); } +/// [`init`] for a caller that holds the world, which is how the UI load reaches it. +/// +/// [`init`] is an `Update` system that inserts through `Commands`, so the resource appears at the +/// next command flush — and the interface load runs on the `InWorld` state transition, which can +/// and does come first. The cost of missing it is not cosmetic: with no atlas there is no text +/// measurer, so for the whole of the UI load **every `GetStringWidth` and every FontString's +/// `GetWidth` answers 0**, where the reference measures inline and always knows (`0x79e510` → +/// `0x772890`). Every `` in FrameXML and in every addon that lays something out from a +/// label's width reads a zero — `AdvancedTradeSkillWindow2.lua:1642` puts its Name radio button +/// `ATSWDifficultySortCheckboxText2:GetWidth() + 5` to the right of the Difficulty one and so +/// stacked the two labels on top of each other, 5 px apart instead of 53.8. +/// +/// Returns whether an atlas is in the world afterwards. +pub(crate) fn ensure_font_atlas(world: &mut World) -> bool { + if world.contains_resource::() { + return true; + } + let scale_factor = { + let mut q = world.query_filtered::<&Window, With>(); + match q.single(world) { + Ok(window) => window.scale_factor(), + Err(_) => return false, + } + }; + let engine = { + let Some(world_assets) = world.get_resource::() else { + return false; + }; + let Some(images) = world.get_resource::>() else { + return false; + }; + match TextEngine::load(world_assets, images, scale_factor) { + Some(engine) => engine, + None => return false, + } + }; + world.insert_resource(UiFontAtlas { + engine: Arc::new(Mutex::new(engine)), + generation: 0, + ellipsis: crate::ui_text::EllipsisMemo::default(), + }); + true +} + /// The frame boundary: follow the window's DPI, carry out a pending reset, create the sheet's /// texture once, and hand this frame's cells to the render world. A reset waits for this boundary /// so no UV moves while quads that use it are still being pushed. diff --git a/crates/benilla-app/src/ui_text/engine/mod.rs b/crates/benilla-app/src/ui_text/engine/mod.rs index 62ac6098..a41eeb84 100644 --- a/crates/benilla-app/src/ui_text/engine/mod.rs +++ b/crates/benilla-app/src/ui_text/engine/mod.rs @@ -37,7 +37,7 @@ use cosmic_text::{ use benilla_assets::{LockRecover, WorldAssets}; use faces::{hhea_ascent_ratio, register_font, CLIENT_FONTS}; -pub(crate) use gpu::UiTextPlugin; +pub(crate) use gpu::{ensure_font_atlas, UiTextPlugin}; use super::outline::outlined_cell; use super::pack::{Cell, Sheet}; diff --git a/crates/benilla-app/src/ui_text/mod.rs b/crates/benilla-app/src/ui_text/mod.rs index a5c21c9a..b3747707 100644 --- a/crates/benilla-app/src/ui_text/mod.rs +++ b/crates/benilla-app/src/ui_text/mod.rs @@ -20,7 +20,7 @@ mod measurer; mod outline; mod pack; -pub(crate) use engine::{UiFontAtlas, UiTextPlugin}; +pub(crate) use engine::{ensure_font_atlas, UiFontAtlas, UiTextPlugin}; use benilla_ui::script::Outline; use benilla_ui::widget::RegionHandle;