Every <OnLoad> in the interface read text widths as 0
`seat_text_measurer`'s own doc says what it is for: *"so `GetStringWidth` right after `SetText`
measures, as the reference answers it inline (`0x79e510` → `0x772890`). Also seated before the
manifest loads, for every `<OnLoad>`."* It was not seated. `UiFontAtlas` is inserted by an
`Update` system through `Commands`, so it appears at the next command flush — and the interface
load runs on the `InWorld` state transition, which comes first. `seat_raster_seam_for_load` then
took its `else { return }` and the VM went through the whole load with **no text measurer at
all**, which `ensure_measured` answers by returning at its first line.
So for the entire UI load — all of FrameXML and every addon — `FontString:GetWidth()`,
`GetHeight()` and `GetStringWidth()` answered **0** for a string that plainly had text. Measured
on grom's install: dozens of them, including every unit-popup entry, every `StaticPopup` button
and the four radio labels this was found through.
What it costs is any `<OnLoad>` that lays something out from a label's width, which is an ordinary
thing for an addon to do. `AdvancedTradeSkillWindow2.lua:1642` puts its Name radio button at
`ATSWDifficultySortCheckboxText2:GetWidth() + 5` to the right of the Difficulty one: the stored
offset came out **5.0** where it should be 53.8, so "Difficulty" was drawn across "Category" 37 px
to its left. grom, 2026-11-05: *"its loading now, but barely functioning."* Re-running
`ATSW_OnLoad()` by hand from a live session gave 53.8 — the same code, the same frame, a measurer
away.
`ensure_font_atlas(&mut World)` is `init`'s body for a caller that holds the world, called from
`seat_raster_seam_for_load`; `init` keeps its own path for every later frame. A world that still
cannot build one (no chain, no window) now warns instead of loading the interface in silence.
Verified: the ATSW2 offset is 53.8 at load, and a walk of the whole loaded interface finds **0**
FontStrings that have text and measure 0 (dozens before). benilla-app 4243 pass / 12 fail, the
recorded baseline with an identical name set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -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 `<OnLoad>` laying out from a label's width will be wrong"
|
||||
);
|
||||
return;
|
||||
}
|
||||
let Some(atlas) = world.get_resource::<crate::ui_text::UiFontAtlas>() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -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 `<OnLoad>` 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::<UiFontAtlas>() {
|
||||
return true;
|
||||
}
|
||||
let scale_factor = {
|
||||
let mut q = world.query_filtered::<&Window, With<PrimaryWindow>>();
|
||||
match q.single(world) {
|
||||
Ok(window) => window.scale_factor(),
|
||||
Err(_) => return false,
|
||||
}
|
||||
};
|
||||
let engine = {
|
||||
let Some(world_assets) = world.get_resource::<WorldAssets>() else {
|
||||
return false;
|
||||
};
|
||||
let Some(images) = world.get_resource::<Assets<Image>>() 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.
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user