From af91b8432a72f47a4a42592645c6de0696fe685c Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 14 Jun 2026 18:51:56 +0200 Subject: [PATCH] feat(D.2b): per-window resize-axis lock; vitals window is X-only (retail) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ResizeX/ResizeY bool properties to UiElement (both true by default). HitEdges() in UiRoot masks out locked axes after edge detection, so a locked edge falls through to window-move behaviour — matching retail, where the vitals bar height is fixed and only widens. MarkupDocument.Build() parses an optional resize="x|y|both|none" attribute on ; vitals.xml gets resize="x" to enforce the horizontal-only constraint in all instances of the panel. Two new tests: HitEdges_RespectsResizeAxisLock (UiRootInputTests) and Build_ResizeAttrX_SetsHorizontalOnly (MarkupDocumentTests). 11/11 green. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/AcDream.App/UI/MarkupDocument.cs | 8 ++++++++ src/AcDream.App/UI/UiElement.cs | 5 +++++ src/AcDream.App/UI/UiRoot.cs | 2 ++ src/AcDream.App/UI/assets/vitals.xml | 2 +- tests/AcDream.App.Tests/UI/MarkupDocumentTests.cs | 9 +++++++++ tests/AcDream.App.Tests/UI/UiRootInputTests.cs | 10 ++++++++++ 6 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/AcDream.App/UI/MarkupDocument.cs b/src/AcDream.App/UI/MarkupDocument.cs index d4b0cb42..e27cd294 100644 --- a/src/AcDream.App/UI/MarkupDocument.cs +++ b/src/AcDream.App/UI/MarkupDocument.cs @@ -34,6 +34,14 @@ public static class MarkupDocument Height = F(root, "h"), }; + // Optional per-window resize-axis lock: resize="x" | "y" | "both" | "none". + string? resize = (string?)root.Attribute("resize"); + if (resize is not null) + { + panel.ResizeX = resize is "x" or "both"; + panel.ResizeY = resize is "y" or "both"; + } + string? title = (string?)root.Attribute("title"); if (!string.IsNullOrEmpty(title)) { diff --git a/src/AcDream.App/UI/UiElement.cs b/src/AcDream.App/UI/UiElement.cs index 30c4b260..48e1955b 100644 --- a/src/AcDream.App/UI/UiElement.cs +++ b/src/AcDream.App/UI/UiElement.cs @@ -101,6 +101,11 @@ public abstract class UiElement public float MinWidth { get; set; } = 40f; public float MinHeight { get; set; } = 40f; + /// Allow horizontal (width) resize. Ignored unless . + public bool ResizeX { get; set; } = true; + /// Allow vertical (height) resize. Ignored unless . + public bool ResizeY { get; set; } = true; + // ── Tree structure ────────────────────────────────────────────────── public UiElement? Parent { get; private set; } diff --git a/src/AcDream.App/UI/UiRoot.cs b/src/AcDream.App/UI/UiRoot.cs index 1b72ec9f..6f836253 100644 --- a/src/AcDream.App/UI/UiRoot.cs +++ b/src/AcDream.App/UI/UiRoot.cs @@ -542,6 +542,8 @@ public sealed class UiRoot : UiElement if (System.Math.Abs(x - r) <= grip) e |= ResizeEdges.Right; if (System.Math.Abs(y - t) <= grip) e |= ResizeEdges.Top; if (System.Math.Abs(y - b) <= grip) e |= ResizeEdges.Bottom; + if (!w.ResizeX) e &= ~(ResizeEdges.Left | ResizeEdges.Right); + if (!w.ResizeY) e &= ~(ResizeEdges.Top | ResizeEdges.Bottom); return e; } diff --git a/src/AcDream.App/UI/assets/vitals.xml b/src/AcDream.App/UI/assets/vitals.xml index 868926d4..83d59c3b 100644 --- a/src/AcDream.App/UI/assets/vitals.xml +++ b/src/AcDream.App/UI/assets/vitals.xml @@ -1,4 +1,4 @@ - + diff --git a/tests/AcDream.App.Tests/UI/MarkupDocumentTests.cs b/tests/AcDream.App.Tests/UI/MarkupDocumentTests.cs index 8ba52d27..5e76ab95 100644 --- a/tests/AcDream.App.Tests/UI/MarkupDocumentTests.cs +++ b/tests/AcDream.App.Tests/UI/MarkupDocumentTests.cs @@ -47,4 +47,13 @@ public class MarkupDocumentTests Assert.Null(meter.Fill()); Assert.Null(meter.Label()); } + + [Fact] + public void Build_ResizeAttrX_SetsHorizontalOnly() + { + const string xml = ""; + var panel = MarkupDocument.Build(xml, new object(), _ => ((uint)1, 32, 32)); + Assert.True(panel.ResizeX); + Assert.False(panel.ResizeY); + } } diff --git a/tests/AcDream.App.Tests/UI/UiRootInputTests.cs b/tests/AcDream.App.Tests/UI/UiRootInputTests.cs index 6ea9e317..9ba7dae5 100644 --- a/tests/AcDream.App.Tests/UI/UiRootInputTests.cs +++ b/tests/AcDream.App.Tests/UI/UiRootInputTests.cs @@ -102,4 +102,14 @@ public class UiRootInputTests Assert.Equal(120f, panel.Top); root.OnMouseUp(UiMouseButton.Left, 220, 170); } + + [Fact] + public void HitEdges_RespectsResizeAxisLock() + { + var panel = new UiPanel { Left = 100, Top = 100, Width = 200, Height = 100, ResizeY = false }; + // right edge still detected (X allowed) + Assert.True((UiRoot.HitEdges(panel, 300, 150, 5) & ResizeEdges.Right) != 0); + // bottom edge masked out (Y locked) + Assert.True((UiRoot.HitEdges(panel, 200, 200, 5) & ResizeEdges.Bottom) == 0); + } }