Add parallel resize mode to the UiRoot retained-mode input state machine. A left-drag starting within ResizeGrip=5px of a Resizable window's edge or corner resizes it (min-size clamped); interior drags on a Draggable window still reposition it. Changes: - UiElement: Resizable, MinWidth, MinHeight properties - UiRoot: ResizeEdges flags enum; _resizeTarget state fields; FindWindow (replaces FindDraggable, matches Draggable||Resizable); HitEdges (static, internal, testable); ResizeRect (static, public, testable); OnMouseDown checks edge-grip before move; OnMouseMove resize branch precedes move; OnMouseUp clears _resizeTarget - UiNineSlicePanel: Resizable = true (retail windows are resizable) - UiRootInputTests: 4 new tests — ResizeRect_RightBottom, ResizeRect_LeftTop (min-clamp + origin shift), HitEdges_DetectsCornerAndInteriorNone, EdgeDrag_ResizesPanel_InteriorDragMoves (full integration path) Note on test coordinate: right-edge grab uses x=298 (2px inside the panel's hit-test boundary) rather than x=300 (exactly at edge, misses OnHitTest's strict `<` check). This is intentional — the grip zone extends inward from the edge boundary, so a click 2px inside correctly lands in both the hit-test rect AND the resize-grip zone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
105 lines
3.9 KiB
C#
105 lines
3.9 KiB
C#
using System.Numerics;
|
|
using AcDream.App.UI;
|
|
|
|
namespace AcDream.App.Tests.UI;
|
|
|
|
public class UiRootInputTests
|
|
{
|
|
[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);
|
|
}
|
|
}
|