Expand the typed client-command boundary across travel, character queries, local UI and layout controls, AFK and consent, emotes, friends, squelch and filters, and fill-components. Preserve retail packet layouts and queue ownership, import the confirmation dialog, and keep authoritative social state in Core. Co-Authored-By: Codex <codex@openai.com>
160 lines
5.5 KiB
C#
160 lines
5.5 KiB
C#
using AcDream.App.UI;
|
|
using AcDream.UI.Abstractions.Panels.Settings;
|
|
|
|
namespace AcDream.App.Tests.UI;
|
|
|
|
public sealed class RetailWindowLayoutPersistenceTests : IDisposable
|
|
{
|
|
private readonly string _directory = Path.Combine(
|
|
Path.GetTempPath(), "acdream-window-layout-tests-" + Guid.NewGuid().ToString("N"));
|
|
private string PathName => Path.Combine(_directory, "settings.json");
|
|
|
|
[Fact]
|
|
public void CompletedInteractions_SaveCompleteCharacterResolutionLayout()
|
|
{
|
|
var store = new SettingsStore(PathName);
|
|
var root = new UiRoot { Width = 800, Height = 600 };
|
|
RetailWindowHandle handle = Mount(root, "chat");
|
|
using var persistence = new RetailWindowLayoutPersistence(
|
|
root.WindowManager, store, () => "Alice", () => (800, 600));
|
|
|
|
handle.MoveTo(44f, 55f);
|
|
handle.ResizeTo(320f, 180f);
|
|
handle.Hide();
|
|
|
|
UiWindowLayout saved = Assert.IsType<UiWindowLayout>(
|
|
store.LoadWindowLayout("Alice", "800x600", "chat", default));
|
|
Assert.Equal((44f, 55f, 320f, 180f), (saved.X, saved.Y, saved.Width, saved.Height));
|
|
Assert.False(saved.Visible);
|
|
}
|
|
|
|
[Fact]
|
|
public void Restore_ClampsBoundsAndReappliesPanelStateAndVisibility()
|
|
{
|
|
var store = new SettingsStore(PathName);
|
|
store.SaveWindowLayout(
|
|
"Alice",
|
|
"640x480",
|
|
"chat",
|
|
new UiWindowLayout(900f, 700f, 500f, 300f, false, false, true));
|
|
|
|
var root = new UiRoot { Width = 640, Height = 480 };
|
|
var state = new FakeWindowState();
|
|
RetailWindowHandle handle = Mount(root, "chat", state);
|
|
using var persistence = new RetailWindowLayoutPersistence(
|
|
root.WindowManager, store, () => "Alice", () => (640, 480));
|
|
|
|
persistence.RestoreAll();
|
|
|
|
Assert.Equal((500f, 300f), (handle.Width, handle.Height));
|
|
Assert.Equal((140f, 180f), (handle.Left, handle.Top));
|
|
Assert.False(handle.IsVisible);
|
|
Assert.True(state.Restored.Maximized);
|
|
}
|
|
|
|
[Fact]
|
|
public void DefaultPreLoginKey_NeverOverwritesCharacterLayouts()
|
|
{
|
|
var store = new SettingsStore(PathName);
|
|
var root = new UiRoot { Width = 800, Height = 600 };
|
|
RetailWindowHandle handle = Mount(root, "radar");
|
|
using var persistence = new RetailWindowLayoutPersistence(
|
|
root.WindowManager, store, () => "default", () => (800, 600));
|
|
|
|
handle.MoveTo(123f, 234f);
|
|
|
|
Assert.Null(store.LoadWindowLayout("default", "800x600", "radar", default));
|
|
}
|
|
|
|
[Fact]
|
|
public void Restore_StateManagedWindow_PreservesAuthoritativeVisibility()
|
|
{
|
|
var store = new SettingsStore(PathName);
|
|
store.SaveWindowLayout(
|
|
"Alice",
|
|
"800x600",
|
|
"combat",
|
|
new UiWindowLayout(44f, 55f, 200f, 100f, true, false, false));
|
|
|
|
var root = new UiRoot { Width = 800, Height = 600 };
|
|
RetailWindowHandle handle = Mount(root, "combat");
|
|
handle.Hide();
|
|
using var persistence = new RetailWindowLayoutPersistence(
|
|
root.WindowManager,
|
|
store,
|
|
() => "Alice",
|
|
() => (800, 600),
|
|
stateManagedVisibilityWindows: ["combat"]);
|
|
|
|
persistence.RestoreAll();
|
|
|
|
Assert.Equal((44f, 55f), (handle.Left, handle.Top));
|
|
Assert.False(handle.IsVisible);
|
|
|
|
// Combat mode owns subsequent show/hide transitions as well; they must
|
|
// not turn into saved user visibility preferences.
|
|
handle.Show();
|
|
UiWindowLayout saved = Assert.IsType<UiWindowLayout>(
|
|
store.LoadWindowLayout("Alice", "800x600", "combat", default));
|
|
Assert.False(saved.Visible);
|
|
}
|
|
|
|
[Fact]
|
|
public void NamedProfile_SaveAndRestore_AppliesEveryMountedWindow()
|
|
{
|
|
var store = new SettingsStore(PathName);
|
|
var root = new UiRoot { Width = 800, Height = 600 };
|
|
RetailWindowHandle chat = Mount(root, "chat");
|
|
RetailWindowHandle radar = Mount(root, "radar");
|
|
using var persistence = new RetailWindowLayoutPersistence(
|
|
root.WindowManager, store, () => "Alice", () => (800, 600));
|
|
|
|
chat.MoveTo(44f, 55f);
|
|
radar.MoveTo(300f, 120f);
|
|
radar.Hide();
|
|
persistence.SaveNamed("hunting");
|
|
|
|
chat.MoveTo(1f, 2f);
|
|
radar.MoveTo(3f, 4f);
|
|
radar.Show();
|
|
persistence.RestoreNamed("hunting");
|
|
|
|
Assert.Equal((44f, 55f), (chat.Left, chat.Top));
|
|
Assert.Equal((300f, 120f), (radar.Left, radar.Top));
|
|
Assert.False(radar.IsVisible);
|
|
}
|
|
|
|
private static RetailWindowHandle Mount(
|
|
UiRoot root,
|
|
string name,
|
|
IRetainedWindowStateController? state = null)
|
|
{
|
|
var frame = new UiPanel
|
|
{
|
|
Left = 10f,
|
|
Top = 20f,
|
|
Width = 200f,
|
|
Height = 100f,
|
|
MinWidth = 100f,
|
|
MinHeight = 80f,
|
|
MaxWidth = 600f,
|
|
MaxHeight = 400f,
|
|
Draggable = true,
|
|
Resizable = true,
|
|
};
|
|
root.AddChild(frame);
|
|
return root.RegisterWindow(name, frame, stateController: state);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (Directory.Exists(_directory)) Directory.Delete(_directory, recursive: true);
|
|
}
|
|
|
|
private sealed class FakeWindowState : IRetainedWindowStateController
|
|
{
|
|
public RetainedWindowState Restored { get; private set; }
|
|
public RetainedWindowState CaptureWindowState() => Restored;
|
|
public void RestoreWindowState(RetainedWindowState state) => Restored = state;
|
|
}
|
|
}
|