fix(chargen): Campaign CC gate round 1 re-test 2 — R3-1/R3-2 caption wrap

Batch E's UiButton.DrawBlockLabel/WrapBlockLines auto-wrapped any caption
that didn't fit its box width — decomp-wrong. UIElement_Text::
CalcJustification @0x00467260 (shared by GlyphList::Recalculate's
horizontal/vertical branches) shows retail's real per-glyph break decision
(both the width-triggered wrap AND the explicit-newline break) sits behind
ONE gate keyed on the OneLine flag; nothing in the decomp confines a
caption's wrap width to a sibling element's rect (Batch E's own ValueBox
confinement for the coexisting-value-label shape).

Live-DAT evidence: the Coordination attribute-slider label (0x100002ed)
authors OneLine=true (should never wrap); the Skills credits button's
"Available Skill Credits" caption measures 193px against its own full
231px button width (fits comfortably) — the 113px confined width Batch E
fed the wrap decision was never a real retail quantity.

Fixed: WrapBlockLines now splits ONLY on the explicit (already-normalized)
'\n' — never width-based. Strict superset of the pre-Batch-E single-line
draw for every already-correct caption; "Attribute\n Credits" still works.
The ValueBox confinement computation stays in OnDraw (still feeds the
Center-alignment tx formula) but no longer gates the wrap decision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-16 17:19:57 +02:00
parent 956b8d5b6b
commit 7d6a7898f6
2 changed files with 189 additions and 72 deletions

View file

@ -461,33 +461,65 @@ public class UiButtonTests
}
/// <summary>
/// R2-3: a single-paragraph caption with NO authored newline still
/// word-wraps when it doesn't fit the available width — the exact
/// live-DAT shape of the Skills credits button's own "Available Skill
/// Credits" caption (measured 193px in a 231px-wide button whose value
/// box starts at local x=116, i.e. only ~113px of caption width is
/// actually available once R2-2/R2-3's confinement applies).
/// R3-2 (re-test 2 correction, supersedes the retired Batch E
/// "WordWrapsToFitAvailableWidth" expectation): a single-paragraph
/// caption with NO authored newline stays ONE line even when it
/// overflows the available width — the exact live-DAT shape of the
/// Skills credits button's own "Available Skill Credits" caption
/// (measured 193px, live-DAT-probed against the button's own FULL
/// 231px width, which it fits comfortably — the 113px figure in the
/// old test was the WRONG width in the first place, since retail never
/// confines a caption's wrap width to a sibling value element's rect;
/// see <see cref="UiButton.DrawBlockLabel"/>'s own doc for the
/// GlyphList::Recalculate citation). Even forced into an artificially
/// narrow box (as here), the caption must NOT wrap — retail's
/// UIElement_Button captions only ever split on an authored <c>\n</c>.
/// </summary>
[Fact]
public void WrapBlockLines_LongSingleParagraph_WordWrapsToFitAvailableWidth()
public void WrapBlockLines_LongSingleParagraph_NeverWordWraps()
{
var lines = UiButton.WrapBlockLines(
"Available Skill Credits", BitmapMeasure, lineHeight: 24f,
boxX: 0f, boxY: 0f, boxWidth: 113f, boxHeight: 28f,
UiButton.LabelAlignment.Left, leftOffset: 3f);
Assert.True(lines.Count > 1, "a 193px caption must wrap within a 110px available width");
foreach (var line in lines)
Assert.True(BitmapMeasure(line.Text) <= 110f, $"line '{line.Text}' overflowed");
Assert.Single(lines);
Assert.Equal("Available Skill Credits", lines[0].Text);
}
/// <summary>
/// R2-2/R2-3 confinement itself, exercised through OnDraw's own gate:
/// a button with BOTH Label and a coexisting ValueBox shrinks the
/// caption's OWN drawable width to stop before the value box starts —
/// this is what the two live-DAT overlap reports (R2-2 "24dits", R2-3
/// "Credit0Credits") trace to: the caption used to draw across the
/// WHOLE button width regardless of where the value sat.
/// R3-1 (re-test 2): the Profession/chargen attribute slider name label
/// (element <c>0x100002ed</c>, e.g. "Coordination") authors <c>OneLine=
/// true</c> and a 115px-wide box — live-DAT-measured against the real
/// dat font, the caption itself is 113px wide, just 1px narrower than
/// the raw box but 1px WIDER than the box minus the class's own default
/// 3px <c>LabelOffsetX</c> (112px) — exactly the boundary the retired
/// Batch E width-check would have tripped on, wrapping a single WORD
/// (no space to break at) into a garbled two-line split. Pins that this
/// no longer happens for any box/text combination, narrow or not.
/// </summary>
[Fact]
public void WrapBlockLines_SingleWordNarrowerThanBoxButWiderThanOffsetAdjustedWidth_StaysOneLine()
{
var lines = UiButton.WrapBlockLines(
"Coordination", BitmapMeasure, lineHeight: 24f,
boxX: 0f, boxY: 0f, boxWidth: 115f, boxHeight: 24f,
UiButton.LabelAlignment.Left, leftOffset: 3f);
Assert.Single(lines);
Assert.Equal("Coordination", lines[0].Text);
}
/// <summary>
/// The ValueBox-vs-Label confinement math itself, exercised through
/// OnDraw's own computation: a button with BOTH Label and a coexisting
/// ValueBox still shrinks the caption's OWN boxWidth to stop before the
/// value box starts. As of R3-2 (re-test 2) this confined width no
/// longer changes whether or how the caption draws — a single
/// (unwrapped) line is never clipped to it (see
/// <see cref="UiButton.DrawBlockLabel"/>'s own doc) — so this test only
/// pins that the computation itself is unchanged, not that it gates
/// any rendering decision.
/// </summary>
[Fact]
public void BuildButton_OwnCaptionWithCoexistingValueBox_ConfinesLabelWidthBeforeValueBox()