feat(ui): centralize retained window lifecycle
This commit is contained in:
parent
4bb37e302e
commit
6e9e10367f
12 changed files with 778 additions and 57 deletions
195
tests/AcDream.App.Tests/UI/RetailWindowManagerTests.cs
Normal file
195
tests/AcDream.App.Tests/UI/RetailWindowManagerTests.cs
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
using System.Collections.Generic;
|
||||
using AcDream.App.UI;
|
||||
|
||||
namespace AcDream.App.Tests.UI;
|
||||
|
||||
public sealed class RetailWindowManagerTests
|
||||
{
|
||||
[Fact]
|
||||
public void Hide_ClearsRootOwnership_AndShowRestoresDefaultInput()
|
||||
{
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var frame = new UiPanel { Width = 300, Height = 150 };
|
||||
var input = new UiField { Width = 200, Height = 20 };
|
||||
frame.AddChild(input);
|
||||
root.AddChild(frame);
|
||||
root.DefaultTextInput = input;
|
||||
root.Modal = frame;
|
||||
root.SetKeyboardFocus(input);
|
||||
root.SetCapture(input);
|
||||
|
||||
var controller = new RecordingController();
|
||||
RetailWindowHandle handle = root.RegisterWindow("chat", frame, frame, controller);
|
||||
var events = new List<string>();
|
||||
handle.Hidden += _ => events.Add("hidden");
|
||||
handle.Shown += _ => events.Add("shown");
|
||||
|
||||
frame.Visible = false;
|
||||
|
||||
Assert.False(frame.Visible);
|
||||
Assert.Null(root.KeyboardFocus);
|
||||
Assert.Null(root.Captured);
|
||||
Assert.Null(root.Modal);
|
||||
Assert.Null(root.DefaultTextInput);
|
||||
Assert.Equal(1, controller.ShownCount);
|
||||
Assert.Equal(1, controller.HiddenCount);
|
||||
Assert.Equal(new[] { "hidden" }, events);
|
||||
|
||||
Assert.True(handle.Show());
|
||||
|
||||
Assert.True(frame.Visible);
|
||||
Assert.Same(input, root.DefaultTextInput);
|
||||
Assert.Equal(2, controller.ShownCount);
|
||||
Assert.Equal(new[] { "hidden", "shown" }, events);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Handle_ReportsGeometryCloseAndLockLifecycle()
|
||||
{
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var frame = new UiPanel
|
||||
{
|
||||
Left = 10,
|
||||
Top = 20,
|
||||
Width = 100,
|
||||
Height = 80,
|
||||
MinWidth = 50,
|
||||
MinHeight = 40,
|
||||
};
|
||||
root.AddChild(frame);
|
||||
RetailWindowHandle handle = root.RegisterWindow("inventory", frame);
|
||||
int moved = 0, resized = 0, hidden = 0, shown = 0, closed = 0;
|
||||
var locks = new List<bool>();
|
||||
handle.Moved += _ => moved++;
|
||||
handle.Resized += _ => resized++;
|
||||
handle.Hidden += _ => hidden++;
|
||||
handle.Shown += _ => shown++;
|
||||
handle.Closed += _ => closed++;
|
||||
handle.LockChanged += (_, locked) => locks.Add(locked);
|
||||
|
||||
Assert.True(handle.MoveTo(30, 45));
|
||||
Assert.Equal((30f, 45f), (frame.Left, frame.Top));
|
||||
Assert.Equal(1, moved);
|
||||
|
||||
Assert.True(handle.ResizeTo(160, 120));
|
||||
Assert.Equal((160f, 120f), (frame.Width, frame.Height));
|
||||
Assert.Equal(1, resized);
|
||||
|
||||
root.WindowManager.SetLocked(true);
|
||||
root.WindowManager.SetLocked(true);
|
||||
root.WindowManager.SetLocked(false);
|
||||
Assert.Equal(new[] { true, false }, locks);
|
||||
|
||||
Assert.True(handle.Close());
|
||||
Assert.True(handle.Close());
|
||||
Assert.False(handle.IsVisible);
|
||||
Assert.Equal(1, hidden);
|
||||
Assert.Equal(1, closed);
|
||||
|
||||
Assert.True(handle.Show());
|
||||
Assert.True(handle.Close());
|
||||
Assert.Equal(1, shown);
|
||||
Assert.Equal(2, hidden);
|
||||
Assert.Equal(2, closed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PointerResize_ReportsCompletionThroughTypedHandle()
|
||||
{
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var frame = new UiPanel
|
||||
{
|
||||
Left = 10,
|
||||
Top = 20,
|
||||
Width = 100,
|
||||
Height = 80,
|
||||
Resizable = true,
|
||||
};
|
||||
root.AddChild(frame);
|
||||
RetailWindowHandle handle = root.RegisterWindow("chat", frame);
|
||||
int resized = 0;
|
||||
handle.Resized += _ => resized++;
|
||||
|
||||
root.OnMouseDown(UiMouseButton.Left, 109, 60);
|
||||
root.OnMouseMove(149, 60);
|
||||
root.OnMouseUp(UiMouseButton.Left, 149, 60);
|
||||
|
||||
Assert.Equal(140f, frame.Width);
|
||||
Assert.Equal(1, resized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FocusAndCaptureTransitions_AreScopedToOwningHandle()
|
||||
{
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var first = new UiPanel { Width = 200, Height = 100 };
|
||||
var second = new UiPanel { Left = 250, Width = 200, Height = 100 };
|
||||
var firstField = new UiField { Width = 100, Height = 20 };
|
||||
var secondField = new UiField { Width = 100, Height = 20 };
|
||||
first.AddChild(firstField);
|
||||
second.AddChild(secondField);
|
||||
root.AddChild(first);
|
||||
root.AddChild(second);
|
||||
var firstController = new RecordingController();
|
||||
var secondController = new RecordingController();
|
||||
RetailWindowHandle firstHandle = root.RegisterWindow("first", first, first, firstController);
|
||||
RetailWindowHandle secondHandle = root.RegisterWindow("second", second, second, secondController);
|
||||
var firstCaptures = new List<UiElement?>();
|
||||
var secondCaptures = new List<UiElement?>();
|
||||
firstHandle.DescendantCaptureChanged += (_, element) => firstCaptures.Add(element);
|
||||
secondHandle.DescendantCaptureChanged += (_, element) => secondCaptures.Add(element);
|
||||
|
||||
root.SetKeyboardFocus(firstField);
|
||||
root.SetKeyboardFocus(secondField);
|
||||
root.SetCapture(firstField);
|
||||
root.SetCapture(secondField);
|
||||
root.ReleaseCapture();
|
||||
|
||||
Assert.Equal(new UiElement?[] { firstField, null }, firstController.FocusChanges);
|
||||
Assert.Equal(new UiElement?[] { secondField }, secondController.FocusChanges);
|
||||
Assert.Equal(new UiElement?[] { firstField, null }, firstCaptures);
|
||||
Assert.Equal(new UiElement?[] { secondField, null }, secondCaptures);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnregisterAndTreeRemoval_DisposeControllersExactlyOnce()
|
||||
{
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var first = new UiPanel { Width = 100, Height = 100 };
|
||||
var second = new UiPanel { Width = 100, Height = 100 };
|
||||
root.AddChild(first);
|
||||
root.AddChild(second);
|
||||
var firstController = new RecordingController();
|
||||
var secondController = new RecordingController();
|
||||
RetailWindowHandle firstHandle = root.RegisterWindow("first", first, first, firstController);
|
||||
RetailWindowHandle secondHandle = root.RegisterWindow("second", second, second, secondController);
|
||||
|
||||
Assert.True(root.UnregisterWindow("first"));
|
||||
Assert.False(firstHandle.IsRegistered);
|
||||
Assert.Equal(1, firstController.DisposeCount);
|
||||
Assert.False(root.ShowWindow("first"));
|
||||
|
||||
Assert.True(root.RemoveChild(second));
|
||||
Assert.False(secondHandle.IsRegistered);
|
||||
Assert.Equal(1, secondController.DisposeCount);
|
||||
Assert.False(root.ShowWindow("second"));
|
||||
|
||||
root.WindowManager.Dispose();
|
||||
Assert.Equal(1, firstController.DisposeCount);
|
||||
Assert.Equal(1, secondController.DisposeCount);
|
||||
}
|
||||
|
||||
private sealed class RecordingController : IRetainedPanelController
|
||||
{
|
||||
public int ShownCount { get; private set; }
|
||||
public int HiddenCount { get; private set; }
|
||||
public int DisposeCount { get; private set; }
|
||||
public List<UiElement?> FocusChanges { get; } = new();
|
||||
|
||||
public void OnShown() => ShownCount++;
|
||||
public void OnHidden() => HiddenCount++;
|
||||
public void OnDescendantFocusChanged(UiElement? focusedDescendant)
|
||||
=> FocusChanges.Add(focusedDescendant);
|
||||
public void Dispose() => DisposeCount++;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue