Commit graph

2 commits

Author SHA1 Message Date
Erik
47eb2d575b fix(plugin-ui): shelf children must not anchor — the per-draw anchor pass was undoing collapse/reflow geometry
EVIDENCE (live UI probe dump at 1280x720, build 2ebcc0164): after collapsing
the plugin shelf, PluginSidePanel rect=(1240,233,24,28) was correct, but
ShelfGripPanel rect=(1240,233,20,18) and the toggle rect=(1260,233,16,18)
stayed UNCHANGED from the expanded geometry. The toggle then sat outside the
24px shelf and the ancestor clip removed it, so the owner saw the tab with
no "<".

ROOT CAUSE: UiElement.ApplyAnchor (src/AcDream.App/UI/UiElement.cs ~829-856)
runs for every child on every draw (called at :699). For any child whose
Anchors != AnchorEdges.None, it captures the Left/Top/Width/Height margins
ONCE (_anchorCaptured) on the first draw and re-applies that snapshot every
subsequent draw, overwriting whatever PluginSidePanel.LayoutChrome/Reflow had
just set. The grip, the toggle, and each PluginShelfButton entry were
constructed with the default Anchors (Left|Top), so their first-draw
geometry froze. The shelf itself already used AnchorEdges.None for exactly
this reason. Unit tests never caught it because UiRoot.Tick does not draw —
the anchor snapshot only exists after a real Draw pass, and the prior
draw-level toggle tests only ever drew once, before any collapse.

FIX: set Anchors = AnchorEdges.None on _grip, _toggle (PluginSidePanel
constructor) and each PluginShelfButton entry (PluginSidePanel.Add) — the
shelf is the sole layout owner of these children and anchoring is the wrong
mechanism for them, not a per-reflow patch via ResetAnchorCapture().
PluginMinimizeButton is untouched (it is a child of the plugin window and
deliberately anchors Top|Right).

TESTS (tests/AcDream.App.Tests/UI/PluginSidePanelToggleGlyphClipTests.cs):
- Collapse_AfterADraw_RepositionsGripAndToggle_NotFrozenAtExpandedGeometry:
  draws the shelf, collapses via a real UiRoot press/release, draws again,
  and asserts the grip/toggle geometry actually reflects the collapsed
  Width/Height rather than the frozen expanded snapshot. Failed-first
  (pre-fix) at line 364 with "Expected: 8, Actual: 20" (grip.Width frozen at
  the pre-collapse value instead of the new collapsed Width - ToggleWidth).
- MultiColumnReflow_AfterADraw_EveryRemainingButtonMatchesAFreshSinglePassLayout:
  12 entries, draw, unregister one window (a real removal), draw again, and
  compares every surviving button's geometry against an independent
  reference shelf built directly with the same final 11-entry set. Failed-
  first with "Expected: 16, Actual: 48" (a surviving button's Left frozen at
  its stale 12-entry column/row instead of the fresh 11-entry reflow).

Both tests use font: null (bitmap fallback) so they run in every CI lane
without an installed retail DAT, unlike the Lane=InstalledDat tests above.

VERIFY: dotnet build (Release) green for src/AcDream.App and the test
project. Targeted filter (PluginSidePanel|Markup|UiRootInput): 130/130
passed. Full tests/AcDream.App.Tests suite: 7353 passed / 97 skipped / 36
failed - matching the stated baseline (7351/97/36) plus the two new tests;
the 36 failures are the pre-existing environment-gated set (installed-DAT
version mismatch, Linux-only waiter, Lane=Manual live-mount probes) and are
unrelated to this change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 16:04:04 +02:00
Erik
2ebcc01640 fix(plugin-ui): size the shelf grip from the DAT font and make the collapsed tab findable
Owner report: "I dont see the < after I minimize the window" — after
collapsing the plugin shelf with the > toggle, the < (expand) glyph was not
visible.

Root cause, established with a real-DAT draw probe (font 0x40000000) before
changing anything: '<' and '>' share IDENTICAL glyph metrics (OffsetY=4,
Width=5, Height=7, VerticalOffsetBefore=5), so there is no per-glyph
asymmetry to explain "I see one but not the other." Against the pre-fix 12px
grip band, the toggle's FILL glyph plane measured fully INSIDE the band in
both states (local y=[3,10] of [0,12]) — the "16px line box overhangs a 12px
band" theory alone does not erase the glyph, so a bare clip fix would not
have addressed the report. What IS true: the border-inflated OUTLINE
(background/shadow) plane, drawn first per retail's UIElement_Text::DrawSelf,
spans y=[-1,14] before clipping and was cropped by the band's self-clip
(UiElement.ClipsChildren) to exactly [0,12] — a real but minor defect. The
actual explanation for the report is discoverability: the collapsed shelf
shrank to a bare 24x12 near-black sliver at the screen edge, several times
smaller than any other clickable affordance in the UI — easy to overlook even
though its pixels were, in fact, being drawn.

Fix (src/AcDream.App/UI/PluginSidePanel.cs):
- ExpandedGripBandHeight (new internal property) derives the EXPANDED
  grip/toggle band from the real font metrics — max(12, font.LineHeight + 2)
  — so neither the fill nor the border-inflated outline plane can clip for
  any font; the 12px constant remains only as the bitmap-font fallback.
  Threaded through the ctor's initial Height, OnTick's row-wrap height calc,
  LayoutChrome, and Reflow's entry-Top/expanded-Height math.
- The COLLAPSED tab is now ButtonExtent (28px) tall instead of the 12px grip
  band — the same size as an ordinary entry button — with the toggle glyph
  filling and centering in the taller band. This is the actual fix for the
  report: the collapsed affordance is now button-sized and findable, not a
  bug-for-bug-identical-but-larger clip fix.

Tests:
- tests/AcDream.App.Tests/UI/PluginSidePanelToggleGlyphClipTests.cs (new,
  Lane=InstalledDat): loads the real DAT font, builds a live shelf, and
  proves via TextRenderer.DebugSpriteSegmentVerts that the toggle's ink (fill
  + outline) is fully contained in its own clip band in BOTH the expanded
  ('>') and real-click-collapsed ('<') states, plus that the collapsed tab is
  button-sized. Verified failing against the pre-fix code (git stash of just
  this file) with concrete numbers: computed unclipped span [-1,14] does not
  fit inside the 12px band; collapsed height measured 12 (not button-sized).
  Passes after the fix.
- tests/AcDream.App.Tests/UI/PluginSidePanelTests.cs: added
  CollapseThenExpand_WhileStillDocked_ReturnsToTheIdenticalLeftAndTop (the
  dock/anchor invariant survives the collapsed-height change), and derived
  Drag_StartingOnShelfPadding_DoesNotMoveTheShelf's press-below-the-grip-band
  Y coordinate from the new ExpandedGripBandHeight accessor instead of a
  re-hard-coded literal.

Verification: dotnet build (App + tests) green. Filtered run
(PluginSidePanel|UiDatFont|Markup|UiRootInput) 131/131 passed, 0 skipped —
the InstalledDat lane tests actually ran (DAT dir resolved). Full
AcDream.App.Tests suite: 7334 passed / 97 skipped / 36 failed (baseline was
7331/97/36) — the 3 new tests are the only delta; the 36 failed test names
are byte-identical to the pre-existing set (cathedral collision installed-dat
gates, alpha-flush conformance, layout live-mount probes, Linux frame-pacing,
credential resolver — all unrelated to this change).

No retail-divergence register change needed: IA-27 already covers the
plugin shelf's non-retail collapse-toggle glyphs/behavior in general; this
is a bug fix within that already-declared deviation, not a new one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 15:16:03 +02:00