fix(chargen): Campaign CC gate round 1 re-test 3 — R4-1..R4-4

Four visual residuals from the lead's own live-client captures of
1.0.2-cc.m, all root-caused via decomp + live-DAT evidence:

- R4-1: Skills credits value overlapped mid-caption again. Root cause
  was a missing UiLayoutPolicy raw-edge reflow on UiButton's value-child
  rect (the child is base-inherited across four sibling buttons of
  differing widths, so its baked-in OriginalParentWidth diverges from
  the actual 231px-wide Skills credits button) plus an HJustify.Right
  value child mapped to Center instead of a real far-edge Right.
- R4-2: the single-sprite scrollbar thumb tiled (GL_REPEAT) instead of
  drawing once — DrawTiled was reused for a small fixed marker graphic
  whose native size is far smaller than the track-proportional thumb
  rect. New DrawThumbMarker draws exactly one native-size instance.
- R4-3: the skills info-box formula line clipped past the surrounding
  gold frame's own authored bottom edge (the pane's own raw box is 20px
  taller than the frame that visually contains it) — clamp the pane's
  Height to the frame's bottom (register AD-105, since retail's
  ShowSkillsText has no code relationship to the frame to cite).
- R4-4: the Appearance help text started mid-sentence — the box was
  never touched by its page controller, so it kept UiText's chat-style
  PreserveEndOnLayout=true default; the scroll model's wasAtEnd check is
  vacuously true on its first-ever overflow transition, pinning the
  first render to the bottom. Set PreserveEndOnLayout=false (a static
  top-oriented report, not a transcript) and wired the box's own nested
  authored scrollbar, never wired before.

App suite live-DAT env 5372/3 -> 5379/3 (+7, zero regressions). Runtime
1735/0 unchanged. Full solution 14585/4 skips/1 failure (the documented
Core.Net NakEmission full-solution-only flake, confirmed standalone-pass).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-16 19:05:23 +02:00
parent 28704db4bf
commit e6acb800cc
11 changed files with 678 additions and 12 deletions

View file

@ -260,8 +260,18 @@ public sealed class UiButton : UiElement, IUiGlobalTimeListener, IUiDatStateful
/// <see cref="ValueBox"/> — the lifted child's own authored justify.</summary>
public LabelAlignment ValueAlign { get; set; } = LabelAlignment.Center;
/// <summary>Label horizontal alignment options.</summary>
public enum LabelAlignment { Center, Left }
/// <summary>
/// Label horizontal alignment options. <see cref="Right"/> (R4-1, Campaign
/// CC gate round 1 re-test 3) is ValueLabel-only today — every value
/// child on the chargen credit-display family (0x100002f1/0x100002f3)
/// authors dat HJustify Right (raw 3/5), decomp-confirmed by
/// <c>UIElement_Text::CalcJustification @0x00467260</c>'s
/// <c>ecx_5==3||5</c> branch (<c>edi = availWidth - textWidth</c>, i.e.
/// flush to the box's own far edge) — distinct from Center's halved
/// offset. <see cref="LabelAlign"/> never authors Right today so no
/// existing switch over it needs a new arm.
/// </summary>
public enum LabelAlignment { Center, Left, Right }
public bool ToggleBehavior { get; }
public bool RolloverEnabled { get; }
@ -568,9 +578,17 @@ public sealed class UiButton : UiElement, IUiGlobalTimeListener, IUiDatStateful
float boxY = ValueBox?.Y ?? 0f;
float boxWidth = ValueBox?.Width ?? Width;
float boxHeight = ValueBox?.Height ?? Height;
float vx = ValueAlign == LabelAlignment.Left
? boxX + LabelOffsetX
: boxX + (boxWidth - vf.MeasureWidth(value)) * 0.5f;
float valueWidth = vf.MeasureWidth(value);
// R4-1: Right mirrors CalcJustification's own far-edge formula
// (box's own right edge minus the measured text width, no
// decorative inset — the decomp's Right branch adds none either,
// and this box carries no threaded marginR of its own).
float vx = ValueAlign switch
{
LabelAlignment.Left => boxX + LabelOffsetX,
LabelAlignment.Right => boxX + boxWidth - valueWidth,
_ => boxX + (boxWidth - valueWidth) * 0.5f,
};
float vy = boxY + (boxHeight - vf.LineHeight) * 0.5f;
ctx.DrawStringDat(vf, value, vx, vy, ValueColor, Outline, OutlineColor);
}