feat(D.2b): Character window — value captions + tab bar sprites + footer legibility

Gap 1 — Header captions:
- 0x1000023A ("Character Level") above the level value: bound via LabelLeft().
- 0x10000234 ("Total Experience (XP):") left of total XP: bound via LabelLeft().
- 0x10000237/0x10000238 (XP-to-level label + value) are children of the UiMeter
  (0x10000236, ConsumesDatChildren=true) and cannot be bound — documented in
  code comments; XP meter fill still bound via Fill=XpFraction.

Gap 2 — Tab bar sprites:
- Tab group elements 0x10000228/229/538 are Type-12 UIElement_Text in the dat
  (ConsumesDatChildren=true), so the three button children (left-cap, center,
  right-cap) are consumed at import and absent from the widget tree. Old
  SetTabState/SetButtonStateRecursive found no UiButton children to set.
- Fix: AddTabSprites() injects three UiText sprite-children per group using
  the known RenderSurface ids confirmed from the retail UI layout dump:
  Open (active)   0x06005D92/0x06005D94/0x06005D96
  Closed (inactive) 0x06005D93/0x06005D95/0x06005D97
  Source: dump nodes 0x10000439/0x100000E9/0x10000215 in layout 0x2100002E,
  state_id 11=Closed, 12=Open per gmTabUI::SetActive(bool).

Gap 3 — Footer legibility:
- The shared footer child ids (0x1000024E etc.) appear in THREE footer-state
  groups (A/B/C). ImportedLayout._byId stores the LAST duplicate = narrower
  State B/C copies (145px labels). Fix: hide State B/C groups (footerB/footerC
  Visible=false), walk State A container (0x10000240) positionally to bind the
  wider State A labels (195px). FooterLine1Label now reads "Skill Credits
  Available:" and FooterLine2Label reads "Unassigned Experience:" at full width.

Tests: 3 old tab-state tests (SetButtonStateRecursive expectation) replaced by
4 new sprite-injection tests + 2 caption-binding tests. Full suite: 676 pass,
0 fail (was 673 pass after 3 failures).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-25 22:17:40 +02:00
parent defbde1f86
commit 99291595bb
3 changed files with 297 additions and 85 deletions

View file

@ -46,6 +46,21 @@ Copy this block when adding a new issue:
---
## #149 — Studio: inventory window (0x21000023) renders all-black in preview
**Status:** OPEN
**Severity:** MEDIUM
**Filed:** 2026-06-25
**Component:** ui (studio)
**Description:** `AcDream.App ui-studio "<datdir>" --layout 0x21000023 --screenshot out.png` renders a flat ~1.2 KB dark-canvas image — the inventory frame / grid / paperdoll don't draw. No exception, no console output. Other studio panels render fine (vitals 0x2100006C, character 0x2100002E, toolbar 0x21000016); the inventory works in the live game (F12). Matters for the planned multi-window "full mockup" studio mode.
**Root cause / status:** Pre-existing (identical at commits 9021600 and eccacc5; NOT the CascadeHeight change). The binding does not throw. Likely the studio host/render-stack omits state the inventory's whole-window Draggable frame + sub-window mounts (paperdoll / gmBackpackUI / gm3DItemsUI) need, or a sprite-resolve / z-order gap specific to the studio host. The 3-D viewports may legitimately need game-only RTT/mesh state (OK to leave empty) but the FRAME should still draw.
**Files:** `src/AcDream.App/Studio/FixtureProvider.cs` (case 0x21000023u), `src/AcDream.App/Studio/StudioWindow.cs`, `src/AcDream.App/Studio/PanelFbo.cs`.
**Investigation start:** dump the imported tree (rects / Visible / media) vs a working panel; confirm the frame backdrop sprite resolves via `RenderStack.ResolveChrome`; compare `StudioWindow`'s UiHost build vs `GameWindow`'s inventory path.
## #148 — Status-bar backpack icon should toggle the inventory window (stateful open/closed)
**Status:** OPEN