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);
+ }
}