feat(ui): D.2b — toolbar collapse-to-one-row (bottom-edge snap resize hides/shows row 2)

UiElement.MaxHeight + ResizableEdges mask; UiCollapsibleFrame snaps height to the nearer
of {collapsed,expanded} and toggles row-2 visibility; GameWindow computes the two heights
from the layout + top-anchors the content. Amends IA-17. UiNineSlicePanel unsealed to
allow subclassing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-20 18:33:35 +02:00
parent e58be3f030
commit f74e017509
8 changed files with 159 additions and 15 deletions

View file

@ -2044,7 +2044,7 @@ public sealed class GameWindow : IDisposable
// thickness on every side, giving an outer window of 310×132.
const int toolbarBorder = AcDream.App.UI.RetailChromeSprites.Border;
float toolbarContentW = 300f, toolbarContentH = toolbarRoot.Height;
var toolbarFrame = new AcDream.App.UI.UiNineSlicePanel(ResolveChrome)
var toolbarFrame = new AcDream.App.UI.UiCollapsibleFrame(ResolveChrome)
{
Left = 10, Top = 300,
Width = toolbarContentW + 2 * toolbarBorder,
@ -2057,14 +2057,48 @@ public sealed class GameWindow : IDisposable
toolbarRoot.Top = toolbarBorder;
toolbarRoot.Width = toolbarContentW;
toolbarRoot.Height = toolbarContentH;
// Anchor content to all four edges so it reflows if the frame is resized.
// Anchor content to Left|Top|Right only — drop Bottom so the dat content
// keeps its full height when the frame collapses; row 2 hides via Visible,
// not reflow. (Width is fixed so the horizontal anchors are inert but harmless.)
toolbarRoot.Anchors = AcDream.App.UI.AnchorEdges.Left | AcDream.App.UI.AnchorEdges.Top
| AcDream.App.UI.AnchorEdges.Right | AcDream.App.UI.AnchorEdges.Bottom;
| AcDream.App.UI.AnchorEdges.Right;
// The frame is the draggable window; the content itself is not.
toolbarRoot.ClickThrough = false;
toolbarRoot.Draggable = false;
toolbarRoot.Resizable = false;
toolbarFrame.AddChild(toolbarRoot);
// Collapse-to-one-row: the frame's bottom edge snaps between a one-row (row 2 hidden)
// and two-row height. CollapsedHeight is computed from the layout (just above row 2),
// so there's no magic constant. Bottom-edge only; default expanded.
uint[] row2Ids =
{
0x100006B7u, 0x100006B8u, 0x100006B9u, 0x100006BAu, 0x100006BBu,
0x100006BCu, 0x100006BDu, 0x100006BEu, 0x100006BFu,
};
var toolbarRow2 = new System.Collections.Generic.List<AcDream.App.UI.UiElement>();
float minRow2Top = float.MaxValue;
foreach (var id in row2Ids)
if (toolbarLayout.FindElement(id) is { } e2)
{
toolbarRow2.Add(e2);
if (e2.Top < minRow2Top) minRow2Top = e2.Top;
}
if (toolbarRow2.Count > 0)
{
float expandedH = toolbarContentH + 2 * toolbarBorder; // today's full height
float collapsedH = minRow2Top + 2 * toolbarBorder; // just above row 2
toolbarFrame.CollapsedHeight = collapsedH;
toolbarFrame.ExpandedHeight = expandedH;
toolbarFrame.SecondRow = toolbarRow2;
toolbarFrame.Resizable = true;
toolbarFrame.ResizableEdges = AcDream.App.UI.ResizeEdges.Bottom; // bottom edge only
toolbarFrame.MinHeight = collapsedH;
toolbarFrame.MaxHeight = expandedH;
// Height stays expandedH (already set at construction) = default expanded.
Console.WriteLine($"[D.2b] toolbar collapse: collapsed={collapsedH:0} expanded={expandedH:0} (row2Top={minRow2Top:0}).");
}
_uiHost.Root.AddChild(toolbarFrame);
Console.WriteLine("[D.5.1] retail toolbar window from LayoutDesc importer (0x21000016).");