fix(D.2b): correct edge-anchor mapping (RightEdge==1=stretch) + enable vitals horizontal resize
ToAnchors was inverted vs retail UIElement::UpdateForParentSizeChange @0x00462640: stretch is RightEdge==1 (not ==2/==4), LeftEdge==2 = track-right. Verified against all 19 vitals fixture pieces. Enables Resizable/ResizeX on the importer vitals root (the prior 'dat is fixed-size' conclusion was wrong). At-rest render unchanged (anchors only fire on resize). Added a 160->200 resize conformance test. Also fixed DatWidgetFactoryTests.RectAndAnchors_SetFromElementInfo which encoded the old inverted model (Right=2 expecting Right anchor; corrected to Right=1). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
825536a2bd
commit
8aa643f3e0
6 changed files with 174 additions and 105 deletions
|
|
@ -1810,16 +1810,21 @@ public sealed class GameWindow : IDisposable
|
|||
healthText: () => (_vitalsVm!.HealthCurrent, _vitalsVm.HealthMax) is (uint c, uint m) ? $"{c}/{m}" : "",
|
||||
staminaText: () => (_vitalsVm!.StaminaCurrent, _vitalsVm.StaminaMax) is (uint c, uint m) ? $"{c}/{m}" : "",
|
||||
manaText: () => (_vitalsVm!.ManaCurrent, _vitalsVm.ManaMax) is (uint c, uint m) ? $"{c}/{m}" : "");
|
||||
// Top-level window: user-positioned (Anchors.None so the per-frame anchor
|
||||
// pass doesn't reset it) + movable, like the retired hand-authored panel.
|
||||
// Resize is left off — the dat stacked-vitals layout (0x2100006C) is
|
||||
// fixed-size (chrome edges near-pinned); faithful grip/dragbar-driven
|
||||
// resize is the Plan-2 window manager.
|
||||
// Top-level retail window: user-positioned (Anchors.None so the per-frame
|
||||
// anchor pass doesn't reset it), movable, and horizontally resizable like
|
||||
// retail. On a width change the dat edge-anchors reflow the pieces
|
||||
// (UIElement::UpdateForParentSizeChange @0x00462640): top/bottom edges +
|
||||
// the three bars stretch, corners stay 5px, the right edge/corners track
|
||||
// the right side. Vertical resize is off (the layout has no vertical stretch).
|
||||
var vitalsRoot = imported.Root;
|
||||
vitalsRoot.Left = 10; vitalsRoot.Top = 30;
|
||||
vitalsRoot.ClickThrough = false;
|
||||
vitalsRoot.Anchors = AcDream.App.UI.AnchorEdges.None;
|
||||
vitalsRoot.Draggable = true;
|
||||
vitalsRoot.Resizable = true;
|
||||
vitalsRoot.ResizeX = true;
|
||||
vitalsRoot.ResizeY = false;
|
||||
vitalsRoot.MinWidth = 40f;
|
||||
_uiHost.Root.AddChild(vitalsRoot);
|
||||
Console.WriteLine("[D.2b] retail UI active — vitals window from LayoutDesc importer (0x2100006C).");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,42 +68,23 @@ public sealed class ElementInfo
|
|||
/// </summary>
|
||||
public static class ElementReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps the four raw edge-anchor flag values from <c>ElementDesc</c> to the
|
||||
/// <see cref="AnchorEdges"/> bit-flag used by the UI layout engine.
|
||||
///
|
||||
/// <para>
|
||||
/// The dat stores one <c>uint</c> per edge with these semantics (§4 of the
|
||||
/// LayoutDesc format reference, 2026-06-15):
|
||||
/// <list type="bullet">
|
||||
/// <item><description>0 = no anchor (prototype-only elements — zero-size style stores)</description></item>
|
||||
/// <item><description>1 = pinned to the <em>near</em> edge (left for LeftEdge, top for TopEdge)</description></item>
|
||||
/// <item><description>2 = pinned to the <em>far</em> edge (right for RightEdge, bottom for BottomEdge)</description></item>
|
||||
/// <item><description>3 = floating / centered between both far edges (maps to neither Left nor Right)</description></item>
|
||||
/// <item><description>4 = stretch: pinned to BOTH near AND far edges simultaneously (element stretches with parent)</description></item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Default when no flags resolve: <c>Left | Top</c> (pin top-left, fixed size).
|
||||
/// This matches elements whose all-zero edge flags indicate a no-reflow prototype.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <summary>Edge-anchor flags → AnchorEdges, per retail UIElement::UpdateForParentSizeChange
|
||||
/// @0x00462640. The far-axis fields drive stretch: RightEdge==1 ⇒ the right edge tracks the
|
||||
/// parent's right edge (stretch); LeftEdge==2 ⇒ a fixed-width element's left tracks the right
|
||||
/// edge (it moves right). ==4 (not present in the vitals layout) = both-sides stretch; ==3 =
|
||||
/// centered (no edge anchor → falls back to pin-top-left). This is the INVERSE of the earlier
|
||||
/// format-doc §4 reading, which was wrong (it made every piece fixed-width).</summary>
|
||||
/// <param name="left">LeftEdge dat field value (0–4).</param>
|
||||
/// <param name="top">TopEdge dat field value (0–4).</param>
|
||||
/// <param name="right">RightEdge dat field value (0–4).</param>
|
||||
/// <param name="bottom">BottomEdge dat field value (0–4).</param>
|
||||
public static AnchorEdges ToAnchors(uint left, uint top, uint right, uint bottom)
|
||||
{
|
||||
// 1 = near-pin, 2 = far-pin, 3 = both-far (floating center), 4 = stretch (both sides).
|
||||
// Only 1 and 4 contribute the NEAR (Left/Top) anchor.
|
||||
// Only 2 and 4 contribute the FAR (Right/Bottom) anchor.
|
||||
// Value 3 contributes neither (floating center is handled by the UI engine differently).
|
||||
var a = AnchorEdges.None;
|
||||
if (left == 1 || left == 4) a |= AnchorEdges.Left;
|
||||
if (top == 1 || top == 4) a |= AnchorEdges.Top;
|
||||
if (right == 2 || right == 4) a |= AnchorEdges.Right;
|
||||
if (bottom == 2 || bottom == 4) a |= AnchorEdges.Bottom;
|
||||
if (left == 1 || left == 4) a |= AnchorEdges.Left;
|
||||
if (right == 1 || right == 4 || left == 2) a |= AnchorEdges.Right;
|
||||
if (top == 1 || top == 4) a |= AnchorEdges.Top;
|
||||
if (bottom == 1 || bottom == 4 || top == 2) a |= AnchorEdges.Bottom;
|
||||
if (a == AnchorEdges.None) a = AnchorEdges.Left | AnchorEdges.Top; // default: pin top-left
|
||||
return a;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue