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(); 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(); 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(); var secondCaptures = new List(); 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 FocusChanges { get; } = new(); public void OnShown() => ShownCount++; public void OnHidden() => HiddenCount++; public void OnDescendantFocusChanged(UiElement? focusedDescendant) => FocusChanges.Add(focusedDescendant); public void Dispose() => DisposeCount++; } }