264 lines
9.7 KiB
C#
264 lines
9.7 KiB
C#
using AcDream.App.UI;
|
|
using AcDream.App.UI.Layout;
|
|
using AcDream.Core.Chat;
|
|
using AcDream.UI.Abstractions;
|
|
using AcDream.UI.Abstractions.Panels.Chat;
|
|
|
|
namespace AcDream.App.Tests.UI.Layout;
|
|
|
|
/// <summary>
|
|
/// Dat-free conformance tests for the committed chat_21000006.json golden fixture.
|
|
/// Verifies that LayoutImporter.ImportInfos correctly resolves the BaseElement /
|
|
/// BaseLayoutId inheritance chain for the chat window (LayoutDesc 0x21000006).
|
|
/// </summary>
|
|
public class ChatLayoutConformanceTests
|
|
{
|
|
private static (uint, int, int) NoTex(uint _) => (0, 0, 0);
|
|
|
|
private static ElementInfo? Find(ElementInfo n, uint id)
|
|
{
|
|
if (n.Id == id) return n;
|
|
foreach (var c in n.Children)
|
|
{
|
|
var f = Find(c, id);
|
|
if (f is not null) return f;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
[Fact]
|
|
public void ChatFixture_ResolvesKnownElements()
|
|
{
|
|
var root = FixtureLoader.LoadChatInfos();
|
|
Assert.NotNull(Find(root, 0x10000011u)); // transcript
|
|
Assert.NotNull(Find(root, 0x10000016u)); // input
|
|
Assert.NotNull(Find(root, 0x10000012u)); // scrollbar track
|
|
Assert.NotNull(Find(root, 0x10000014u)); // channel menu
|
|
Assert.NotNull(Find(root, 0x10000019u)); // send button
|
|
Assert.NotNull(Find(root, 0x1000046Fu)); // max/min button
|
|
}
|
|
|
|
[Fact]
|
|
public void ChatFixture_ResolvedTypes_MatchRetailRegistry()
|
|
{
|
|
var root = FixtureLoader.LoadChatInfos();
|
|
Assert.Equal(6u, Find(root, 0x10000014u)!.Type); // Menu
|
|
Assert.Equal(11u, Find(root, 0x10000012u)!.Type); // Scrollbar
|
|
Assert.Equal(1u, Find(root, 0x10000019u)!.Type); // Button (Send)
|
|
Assert.Equal(1u, Find(root, 0x1000046Fu)!.Type); // Button (Max/Min)
|
|
Assert.Equal(12u, Find(root, 0x10000011u)!.Type); // Text/style-prototype (transcript)
|
|
Assert.Equal(12u, Find(root, 0x10000016u)!.Type); // Text/style-prototype (input)
|
|
}
|
|
|
|
[Fact]
|
|
public void ChatFixture_BuildsSelectableTranscriptAndEditableInputInPlace()
|
|
{
|
|
var layout = FixtureLoader.LoadChat();
|
|
|
|
var transcript = Assert.IsType<UiText>(layout.FindElement(0x10000011u));
|
|
var input = Assert.IsType<UiField>(layout.FindElement(0x10000016u));
|
|
|
|
Assert.True(transcript.Selectable);
|
|
Assert.True(input.Selectable);
|
|
Assert.True(input.OneLine);
|
|
Assert.Equal(0x10000016u, input.DatElementId);
|
|
}
|
|
|
|
[Fact]
|
|
public void ChatFixture_ScrollbarImportsInheritedMediaRoles()
|
|
{
|
|
var layout = FixtureLoader.LoadChat();
|
|
var scrollbar = Assert.IsType<UiScrollbar>(layout.FindElement(0x10000012u));
|
|
|
|
Assert.Equal(0x06004C5Fu, scrollbar.TrackSprite);
|
|
Assert.Equal(0x06004C60u, scrollbar.ThumbTopSprite);
|
|
Assert.Equal(0x06004C63u, scrollbar.ThumbSprite);
|
|
Assert.Equal(0x06004C66u, scrollbar.ThumbBotSprite);
|
|
Assert.Equal(0x06004C69u, scrollbar.UpSprite);
|
|
Assert.Equal(0x06004C6Cu, scrollbar.DownSprite);
|
|
}
|
|
|
|
[Fact]
|
|
public void ChatFixture_WindowRootCarriesRetailHeightConstraints()
|
|
{
|
|
var root = FixtureLoader.LoadChatInfos();
|
|
var window = Find(root, 0x1000000Eu)!;
|
|
|
|
Assert.True(window.TryGetEffectiveInteger(0x3Eu, out int minHeight));
|
|
Assert.True(window.TryGetEffectiveInteger(0x3Cu, out int maxHeight));
|
|
Assert.Equal(100, minHeight);
|
|
Assert.Equal(360, maxHeight);
|
|
}
|
|
|
|
[Fact]
|
|
public void ChatFixture_CroppedDockedRoot_ReflowsContentAcrossMountedWidth()
|
|
{
|
|
var infos = FixtureLoader.LoadChatInfos();
|
|
var layout = LayoutImporter.Build(infos, NoTex, null);
|
|
var controller = ChatWindowController.Bind(
|
|
infos,
|
|
layout,
|
|
new ChatVM(new ChatLog()),
|
|
() => NullCommandBus.Instance,
|
|
null,
|
|
null,
|
|
NoTex);
|
|
Assert.NotNull(controller);
|
|
|
|
var root = controller!.Root;
|
|
root.Width = 490;
|
|
root.Height = 100;
|
|
root.RebaseChildLayoutBaselines();
|
|
root.Width = 615;
|
|
|
|
var transcriptPanel = layout.FindElement(0x10000010u)!;
|
|
var inputBar = layout.FindElement(0x10000013u)!;
|
|
transcriptPanel.ApplyAnchor(root.Width, root.Height);
|
|
inputBar.ApplyAnchor(root.Width, root.Height);
|
|
|
|
Assert.Equal(615f, transcriptPanel.Width);
|
|
Assert.Equal(615f, inputBar.Width);
|
|
}
|
|
|
|
[Fact]
|
|
public void ChatFixture_ChannelCaptionWidth_SurvivesImportedLayoutPass()
|
|
{
|
|
var infos = FixtureLoader.LoadChatInfos();
|
|
var layout = LayoutImporter.Build(infos, NoTex, null);
|
|
var controller = ChatWindowController.Bind(
|
|
infos,
|
|
layout,
|
|
new ChatVM(new ChatLog()),
|
|
() => NullCommandBus.Instance,
|
|
null,
|
|
null,
|
|
NoTex);
|
|
Assert.NotNull(controller);
|
|
|
|
controller!.Menu.OnSelect!.Invoke(ChatChannelKind.General);
|
|
float fittedWidth = controller.Menu.Width;
|
|
controller.Menu.ApplyAnchor(controller.Menu.Parent!.Width, controller.Menu.Parent.Height);
|
|
|
|
Assert.True(fittedWidth > 46f);
|
|
Assert.Equal(fittedWidth, controller.Menu.Width);
|
|
Assert.Equal(controller.Menu.Left + fittedWidth, controller.Input.Left);
|
|
}
|
|
|
|
[Fact]
|
|
public void ChatMaximize_ResizesOuterFrameByHalfParent_AndRestores()
|
|
{
|
|
var infos = FixtureLoader.LoadChatInfos();
|
|
var layout = LayoutImporter.Build(infos, NoTex, null);
|
|
var controller = ChatWindowController.Bind(
|
|
infos,
|
|
layout,
|
|
new ChatVM(new ChatLog()),
|
|
() => NullCommandBus.Instance,
|
|
null,
|
|
null,
|
|
NoTex)!;
|
|
var root = new UiRoot { Width = 800, Height = 600 };
|
|
RetailWindowHandle handle = RetailWindowFrame.Mount(
|
|
root,
|
|
controller.Root,
|
|
NoTex,
|
|
new RetailWindowFrame.Options
|
|
{
|
|
WindowName = WindowNames.Chat,
|
|
Chrome = RetailWindowChrome.NineSlice,
|
|
Left = 12f,
|
|
Top = 390f,
|
|
ContentWidth = 490f,
|
|
ContentHeight = controller.Root.Height,
|
|
RebaseContentLayout = true,
|
|
DatConstraintSource = controller.DatWindowInfo,
|
|
MinWidth = 200f,
|
|
});
|
|
controller.AttachWindow(handle);
|
|
var maxMin = Assert.IsType<UiButton>(layout.FindElement(0x1000046Fu));
|
|
|
|
maxMin.OnClick!();
|
|
|
|
Assert.True(controller.IsMaximized);
|
|
Assert.Equal(130f, handle.Top);
|
|
Assert.Equal(370f, handle.Height); // DAT max 360 + 2*5px shared chrome
|
|
Assert.Equal(500f, handle.Top + handle.Height); // lower edge stays fixed while growing upward
|
|
Assert.Equal(RetailUiStateIds.Maximized, maxMin.ActiveRetailStateId);
|
|
controller.Root.ApplyAnchor(handle.Width, handle.Height);
|
|
Assert.Equal(360f, controller.Root.Height);
|
|
|
|
maxMin.OnClick!();
|
|
|
|
Assert.False(controller.IsMaximized);
|
|
Assert.Equal(390f, handle.Top);
|
|
Assert.Equal(110f, handle.Height);
|
|
Assert.Equal(RetailUiStateIds.Minimized, maxMin.ActiveRetailStateId);
|
|
}
|
|
|
|
[Fact]
|
|
public void MountedChatScrollbar_ButtonsAndThumbDragDriveTranscriptModel()
|
|
{
|
|
var infos = FixtureLoader.LoadChatInfos();
|
|
var layout = LayoutImporter.Build(infos, NoTex, null);
|
|
var controller = ChatWindowController.Bind(
|
|
infos,
|
|
layout,
|
|
new ChatVM(new ChatLog()),
|
|
() => NullCommandBus.Instance,
|
|
null,
|
|
null,
|
|
NoTex)!;
|
|
var root = new UiRoot { Width = 800, Height = 600 };
|
|
RetailWindowHandle handle = RetailWindowFrame.Mount(
|
|
root,
|
|
controller.Root,
|
|
NoTex,
|
|
new RetailWindowFrame.Options
|
|
{
|
|
WindowName = WindowNames.Chat,
|
|
Chrome = RetailWindowChrome.NineSlice,
|
|
Left = 10f,
|
|
Top = 440f,
|
|
ContentWidth = 490f,
|
|
ContentHeight = controller.Root.Height,
|
|
RebaseContentLayout = true,
|
|
DatConstraintSource = controller.DatWindowInfo,
|
|
MinWidth = 200f,
|
|
});
|
|
controller.AttachWindow(handle);
|
|
|
|
UiScrollbar bar = controller.Scrollbar;
|
|
UiScrollable scroll = controller.Transcript.Scroll;
|
|
scroll.LineHeight = 16;
|
|
scroll.SetExtents(contentHeight: 400, viewHeight: 50, preserveEnd: true);
|
|
Assert.Equal(350, scroll.ScrollY);
|
|
|
|
var screen = bar.ScreenPosition;
|
|
int centerX = (int)(screen.X + bar.Width / 2f);
|
|
|
|
// Top/decrement button.
|
|
int upY = (int)(screen.Y + 8f);
|
|
root.OnMouseDown(UiMouseButton.Left, centerX, upY);
|
|
root.OnMouseUp(UiMouseButton.Left, centerX, upY);
|
|
Assert.Equal(334, scroll.ScrollY);
|
|
|
|
// Re-layout must retain the position chosen through the scrollbar.
|
|
scroll.SetExtents(contentHeight: 400, viewHeight: 50, preserveEnd: true);
|
|
Assert.Equal(334, scroll.ScrollY);
|
|
|
|
// Drag the thumb toward the top of the track.
|
|
float trackTop = 16f;
|
|
float trackLength = bar.Height - 32f;
|
|
var (thumbY, thumbHeight) = UiScrollbar.ThumbRect(scroll, trackTop, trackLength);
|
|
int dragStartY = (int)(screen.Y + thumbY + thumbHeight / 2f);
|
|
int dragEndY = (int)(screen.Y + trackTop + thumbHeight / 2f);
|
|
root.OnMouseDown(UiMouseButton.Left, centerX, dragStartY);
|
|
root.OnMouseMove(centerX, dragEndY);
|
|
root.OnMouseUp(UiMouseButton.Left, centerX, dragEndY);
|
|
|
|
Assert.True(scroll.ScrollY < 334);
|
|
int draggedPosition = scroll.ScrollY;
|
|
scroll.SetExtents(contentHeight: 400, viewHeight: 50, preserveEnd: true);
|
|
Assert.Equal(draggedPosition, scroll.ScrollY);
|
|
}
|
|
}
|