The anchor pass added in f911b5f runs on every element's children — including
UiRoot's children, which are the top-level WINDOWS. With the default Left|Top
anchor, ApplyAnchor reset each window's Left/Top/Width/Height back to its
captured design rect EVERY frame, so user move/resize was undone instantly ("I
can't resize or move it"). A window is user-positioned, so it must not be
anchor-managed by its parent: set UiNineSlicePanel.Anchors = None. Children
INSIDE the window still anchor to it (the bars keep stretching with width).
Regression tests: UiNineSlicePanel.Anchors == None; ApplyAnchor(None) is a no-op.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
156 lines
5.8 KiB
C#
156 lines
5.8 KiB
C#
using System.Numerics;
|
|
using AcDream.App.UI;
|
|
|
|
namespace AcDream.App.Tests.UI;
|
|
|
|
public class UiRootInputTests
|
|
{
|
|
[Fact]
|
|
public void UiNineSlicePanel_IsNotAnchorManaged_SoUserMoveResizeSticks()
|
|
{
|
|
// Regression: the per-frame anchor pass must NOT reset a window's rect,
|
|
// or move/resize get undone every frame. Windows are user-positioned.
|
|
var panel = new UiNineSlicePanel(_ => ((uint)1, 32, 32));
|
|
Assert.Equal(AnchorEdges.None, panel.Anchors);
|
|
}
|
|
|
|
[Fact]
|
|
public void ApplyAnchor_None_IsNoOp()
|
|
{
|
|
var e = new UiPanel { Left = 50, Top = 60, Width = 100, Height = 40, Anchors = AnchorEdges.None };
|
|
e.ApplyAnchor(800, 600);
|
|
Assert.Equal(50f, e.Left);
|
|
Assert.Equal(60f, e.Top);
|
|
Assert.Equal(100f, e.Width);
|
|
Assert.Equal(40f, e.Height);
|
|
}
|
|
|
|
[Fact]
|
|
public void WantsMouse_TrueOverWidget_FalseOverEmptySpace()
|
|
{
|
|
var root = new UiRoot { Width = 800, Height = 600 };
|
|
var panel = new UiPanel { Left = 10, Top = 10, Width = 100, Height = 50 };
|
|
root.AddChild(panel);
|
|
|
|
root.OnMouseMove(50, 30); // inside the panel
|
|
Assert.True(root.WantsMouse);
|
|
|
|
root.OnMouseMove(500, 400); // empty space
|
|
Assert.False(root.WantsMouse);
|
|
}
|
|
|
|
[Fact]
|
|
public void WindowDrag_RepositionsDraggablePanel_StopsOnRelease()
|
|
{
|
|
var root = new UiRoot { Width = 800, Height = 600 };
|
|
var panel = new UiPanel { Left = 10, Top = 10, Width = 100, Height = 50, Draggable = true };
|
|
root.AddChild(panel);
|
|
|
|
root.OnMouseDown(UiMouseButton.Left, 20, 20); // grab at (10,10) into the panel
|
|
root.OnMouseMove(120, 90); // drag
|
|
Assert.Equal(110f, panel.Left); // 120 - 10
|
|
Assert.Equal(80f, panel.Top); // 90 - 10
|
|
|
|
root.OnMouseUp(UiMouseButton.Left, 120, 90);
|
|
root.OnMouseMove(300, 300); // released — must not move
|
|
Assert.Equal(110f, panel.Left);
|
|
Assert.Equal(80f, panel.Top);
|
|
}
|
|
|
|
[Fact]
|
|
public void NonDraggablePanel_DoesNotMoveOnDrag()
|
|
{
|
|
var root = new UiRoot { Width = 800, Height = 600 };
|
|
var panel = new UiPanel { Left = 10, Top = 10, Width = 100, Height = 50 }; // Draggable defaults false
|
|
root.AddChild(panel);
|
|
|
|
root.OnMouseDown(UiMouseButton.Left, 20, 20);
|
|
root.OnMouseMove(120, 90);
|
|
Assert.Equal(10f, panel.Left);
|
|
Assert.Equal(10f, panel.Top);
|
|
}
|
|
|
|
[Fact]
|
|
public void ResizeRect_RightBottom_GrowsSizeOnly()
|
|
{
|
|
var (x, y, w, h) = UiRoot.ResizeRect(10, 20, 100, 50,
|
|
ResizeEdges.Right | ResizeEdges.Bottom, dx: 30, dy: 15, minW: 40, minH: 40);
|
|
Assert.Equal(10f, x); Assert.Equal(20f, y);
|
|
Assert.Equal(130f, w); Assert.Equal(65f, h);
|
|
}
|
|
|
|
[Fact]
|
|
public void ResizeRect_LeftTop_MovesOriginAndClampsToMin()
|
|
{
|
|
// Drag left edge right by 80 on a 100-wide / min-40 window: width clamps to 40,
|
|
// origin shifts so the RIGHT edge (110) stays put → x = 70.
|
|
var (x, _, w, _) = UiRoot.ResizeRect(10, 20, 100, 50,
|
|
ResizeEdges.Left, dx: 80, dy: 0, minW: 40, minH: 40);
|
|
Assert.Equal(40f, w);
|
|
Assert.Equal(70f, x);
|
|
}
|
|
|
|
[Fact]
|
|
public void HitEdges_DetectsCornerAndInteriorNone()
|
|
{
|
|
var panel = new UiPanel { Left = 100, Top = 100, Width = 200, Height = 100 };
|
|
// bottom-right corner (300,200)
|
|
Assert.Equal(ResizeEdges.Right | ResizeEdges.Bottom, UiRoot.HitEdges(panel, 300, 200, 5));
|
|
// deep interior → no edges
|
|
Assert.Equal(ResizeEdges.None, UiRoot.HitEdges(panel, 200, 150, 5));
|
|
}
|
|
|
|
[Fact]
|
|
public void EdgeDrag_ResizesPanel_InteriorDragMoves()
|
|
{
|
|
var root = new UiRoot { Width = 800, Height = 600 };
|
|
var panel = new UiPanel { Left = 100, Top = 100, Width = 200, Height = 100,
|
|
Draggable = true, Resizable = true, MinWidth = 40, MinHeight = 40 };
|
|
root.AddChild(panel);
|
|
|
|
// grab just inside the right edge (x=298, within ResizeGrip=5 of x=300) and drag right → wider, same origin
|
|
root.OnMouseDown(UiMouseButton.Left, 298, 150);
|
|
root.OnMouseMove(338, 150);
|
|
Assert.Equal(240f, panel.Width);
|
|
Assert.Equal(100f, panel.Left);
|
|
root.OnMouseUp(UiMouseButton.Left, 338, 150);
|
|
|
|
// grab the interior and drag → moves
|
|
root.OnMouseDown(UiMouseButton.Left, 200, 150);
|
|
root.OnMouseMove(220, 170);
|
|
Assert.Equal(120f, panel.Left);
|
|
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);
|
|
}
|
|
|
|
[Fact]
|
|
public void ComputeAnchoredRect_LeftRight_StretchesWidth()
|
|
{
|
|
// bar at x=8,w=200 in a 220-wide parent (right margin 12). Parent grows to 300.
|
|
var (x, _, w, _) = UiElement.ComputeAnchoredRect(
|
|
AnchorEdges.Left | AnchorEdges.Right | AnchorEdges.Top,
|
|
mL: 8, mT: 24, mR: 12, mB: 58, w0: 200, h0: 14, parentW: 300, parentH: 96);
|
|
Assert.Equal(8f, x);
|
|
Assert.Equal(280f, w); // 300 - 12 - 8
|
|
}
|
|
|
|
[Fact]
|
|
public void ComputeAnchoredRect_LeftTopOnly_KeepsFixedSizeAndOrigin()
|
|
{
|
|
var (x, y, w, h) = UiElement.ComputeAnchoredRect(
|
|
AnchorEdges.Left | AnchorEdges.Top,
|
|
mL: 8, mT: 24, mR: 12, mB: 58, w0: 200, h0: 14, parentW: 300, parentH: 96);
|
|
Assert.Equal(8f, x); Assert.Equal(24f, y);
|
|
Assert.Equal(200f, w); Assert.Equal(14f, h);
|
|
}
|
|
}
|