feat(studio): add mockup desktop controls
This commit is contained in:
parent
9444a328fa
commit
4cc565badc
3 changed files with 255 additions and 23 deletions
|
|
@ -5,6 +5,7 @@ using AcDream.Core.Chat;
|
|||
using AcDream.UI.Abstractions;
|
||||
using AcDream.UI.Abstractions.Panels.Chat;
|
||||
using DatReaderWriter;
|
||||
using System.Numerics;
|
||||
|
||||
namespace AcDream.App.Studio;
|
||||
|
||||
|
|
@ -19,24 +20,32 @@ internal static class MockupDesktop
|
|||
public static void Load(DatCollection dats, RenderStack stack)
|
||||
{
|
||||
var objects = SampleData.BuildObjectTable();
|
||||
var windows = new List<MockupWindow>();
|
||||
|
||||
MountVitals(dats, stack);
|
||||
MountToolbar(dats, stack, objects);
|
||||
MountCharacter(dats, stack, objects);
|
||||
MountInventory(dats, stack, objects);
|
||||
MountChat(dats, stack);
|
||||
if (MountVitals(dats, stack, objects) is { } vitals)
|
||||
RegisterWindow(stack, windows, WindowNames.Vitals, "Vitals", vitals, zOrder: 10);
|
||||
if (MountToolbar(dats, stack, objects) is { } toolbar)
|
||||
RegisterWindow(stack, windows, WindowNames.Toolbar, "Toolbar", toolbar, zOrder: 20);
|
||||
if (MountCharacter(dats, stack, objects) is { } character)
|
||||
RegisterWindow(stack, windows, WindowNames.Character, "Character", character, zOrder: 30);
|
||||
if (MountInventory(dats, stack, objects) is { } inventory)
|
||||
RegisterWindow(stack, windows, WindowNames.Inventory, "Inventory", inventory, zOrder: 40);
|
||||
if (MountChat(dats, stack) is { } chat)
|
||||
RegisterWindow(stack, windows, WindowNames.Chat, "Chat", chat, zOrder: 50);
|
||||
|
||||
MountControls(stack, windows);
|
||||
}
|
||||
|
||||
private static ImportedLayout? Import(DatCollection dats, uint layoutId, RenderStack stack)
|
||||
=> LayoutImporter.Import(dats, layoutId, stack.ResolveChrome, stack.VitalsDatFont,
|
||||
fontResolve: stack.ResolveDatFont);
|
||||
|
||||
private static void MountVitals(DatCollection dats, RenderStack stack)
|
||||
private static UiElement? MountVitals(DatCollection dats, RenderStack stack, AcDream.Core.Items.ClientObjectTable objects)
|
||||
{
|
||||
var layout = Import(dats, 0x2100006Cu, stack);
|
||||
if (layout is null) return;
|
||||
if (layout is null) return null;
|
||||
|
||||
FixtureProvider.Populate(0x2100006Cu, layout, stack, SampleData.BuildObjectTable(), dats);
|
||||
FixtureProvider.Populate(0x2100006Cu, layout, stack, objects, dats);
|
||||
|
||||
var root = layout.Root;
|
||||
root.Left = 12f;
|
||||
|
|
@ -48,13 +57,13 @@ internal static class MockupDesktop
|
|||
root.ResizeX = true;
|
||||
root.ResizeY = false;
|
||||
root.MinWidth = 40f;
|
||||
stack.UiHost.Root.AddChild(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
private static void MountToolbar(DatCollection dats, RenderStack stack, AcDream.Core.Items.ClientObjectTable objects)
|
||||
private static UiElement? MountToolbar(DatCollection dats, RenderStack stack, AcDream.Core.Items.ClientObjectTable objects)
|
||||
{
|
||||
var layout = Import(dats, 0x21000016u, stack);
|
||||
if (layout is null) return;
|
||||
if (layout is null) return null;
|
||||
|
||||
FixtureProvider.Populate(0x21000016u, layout, stack, objects, dats);
|
||||
|
||||
|
|
@ -82,7 +91,7 @@ internal static class MockupDesktop
|
|||
frame.AddChild(toolbarRoot);
|
||||
|
||||
ConfigureToolbarCollapse(layout, frame, contentH, border);
|
||||
stack.UiHost.Root.AddChild(frame);
|
||||
return frame;
|
||||
}
|
||||
|
||||
private static void ConfigureToolbarCollapse(ImportedLayout layout, UiCollapsibleFrame frame, float contentH, int border)
|
||||
|
|
@ -117,10 +126,10 @@ internal static class MockupDesktop
|
|||
frame.MaxHeight = expandedH;
|
||||
}
|
||||
|
||||
private static void MountCharacter(DatCollection dats, RenderStack stack, AcDream.Core.Items.ClientObjectTable objects)
|
||||
private static UiElement? MountCharacter(DatCollection dats, RenderStack stack, AcDream.Core.Items.ClientObjectTable objects)
|
||||
{
|
||||
var layout = Import(dats, 0x2100002Eu, stack);
|
||||
if (layout is null) return;
|
||||
if (layout is null) return null;
|
||||
|
||||
FixtureProvider.Populate(0x2100002Eu, layout, stack, objects, dats);
|
||||
|
||||
|
|
@ -133,13 +142,13 @@ internal static class MockupDesktop
|
|||
root.Resizable = true;
|
||||
root.MinWidth = root.Width;
|
||||
root.MinHeight = root.Height;
|
||||
stack.UiHost.Root.AddChild(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
private static void MountInventory(DatCollection dats, RenderStack stack, AcDream.Core.Items.ClientObjectTable objects)
|
||||
private static UiElement? MountInventory(DatCollection dats, RenderStack stack, AcDream.Core.Items.ClientObjectTable objects)
|
||||
{
|
||||
var layout = Import(dats, 0x21000023u, stack);
|
||||
if (layout is null) return;
|
||||
if (layout is null) return null;
|
||||
|
||||
FixtureProvider.Populate(0x21000023u, layout, stack, objects, dats);
|
||||
|
||||
|
|
@ -179,8 +188,7 @@ internal static class MockupDesktop
|
|||
PinTopLeft(layout, 0x100001CDu);
|
||||
PinTopLeft(layout, 0x100001CEu);
|
||||
|
||||
stack.UiHost.Root.AddChild(frame);
|
||||
stack.UiHost.RegisterWindow(WindowNames.Inventory, frame);
|
||||
return frame;
|
||||
}
|
||||
|
||||
private static void StretchV(ImportedLayout layout, uint id)
|
||||
|
|
@ -195,10 +203,10 @@ internal static class MockupDesktop
|
|||
element.Anchors = AnchorEdges.Left | AnchorEdges.Top;
|
||||
}
|
||||
|
||||
private static void MountChat(DatCollection dats, RenderStack stack)
|
||||
private static UiElement? MountChat(DatCollection dats, RenderStack stack)
|
||||
{
|
||||
var rootInfo = LayoutImporter.ImportInfos(dats, ChatWindowController.LayoutId);
|
||||
if (rootInfo is null) return;
|
||||
if (rootInfo is null) return null;
|
||||
|
||||
var layout = LayoutImporter.Build(rootInfo, stack.ResolveChrome, stack.VitalsDatFont,
|
||||
fontResolve: stack.ResolveDatFont);
|
||||
|
|
@ -219,7 +227,7 @@ internal static class MockupDesktop
|
|||
stack.VitalsDatFont,
|
||||
debugFont: null,
|
||||
stack.ResolveChrome);
|
||||
if (controller is null) return;
|
||||
if (controller is null) return null;
|
||||
|
||||
const int border = RetailChromeSprites.Border;
|
||||
var chatRoot = controller.Root;
|
||||
|
|
@ -245,7 +253,221 @@ internal static class MockupDesktop
|
|||
chatRoot.Resizable = false;
|
||||
frame.AddChild(chatRoot);
|
||||
|
||||
stack.UiHost.Root.AddChild(frame);
|
||||
stack.UiHost.Root.DefaultTextInput = controller.Input;
|
||||
return frame;
|
||||
}
|
||||
|
||||
private static void RegisterWindow(RenderStack stack, List<MockupWindow> windows,
|
||||
string name, string title, UiElement element, int zOrder)
|
||||
{
|
||||
element.ZOrder = zOrder;
|
||||
stack.UiHost.Root.AddChild(element);
|
||||
stack.UiHost.RegisterWindow(name, element);
|
||||
windows.Add(new MockupWindow(
|
||||
name,
|
||||
title,
|
||||
element,
|
||||
new MockupRect(element.Left, element.Top, element.Width, element.Height),
|
||||
zOrder));
|
||||
}
|
||||
|
||||
private static void MountControls(RenderStack stack, IReadOnlyList<MockupWindow> windows)
|
||||
{
|
||||
if (windows.Count == 0) return;
|
||||
|
||||
var font = stack.VitalsDatFont;
|
||||
var panel = new MockupControlPanel
|
||||
{
|
||||
Name = "mockup-controls",
|
||||
Left = 1010f,
|
||||
Top = 430f,
|
||||
Width = 258f,
|
||||
Height = 242f,
|
||||
Anchors = AnchorEdges.None,
|
||||
Draggable = true,
|
||||
Resizable = false,
|
||||
BackgroundColor = new Vector4(0.03f, 0.035f, 0.04f, 0.82f),
|
||||
BorderColor = new Vector4(0.55f, 0.45f, 0.27f, 0.95f),
|
||||
BorderThickness = 1f,
|
||||
ZOrder = 1_000,
|
||||
};
|
||||
|
||||
panel.AddChild(new MockupLabel(font)
|
||||
{
|
||||
Left = 12f,
|
||||
Top = 10f,
|
||||
Width = 220f,
|
||||
Height = 18f,
|
||||
Text = "Mockup",
|
||||
TextColor = new Vector4(0.95f, 0.86f, 0.62f, 1f),
|
||||
});
|
||||
|
||||
var toggles = new List<(MockupWindow Window, MockupButton Button)>();
|
||||
panel.AddChild(MakeButton(font, "Arrange", 12f, 34f, 108f, 24f, () =>
|
||||
{
|
||||
Arrange(windows, showAll: false);
|
||||
RefreshToggles(toggles);
|
||||
}));
|
||||
panel.AddChild(MakeButton(font, "Reset", 132f, 34f, 108f, 24f, () =>
|
||||
{
|
||||
Arrange(windows, showAll: true);
|
||||
RefreshToggles(toggles);
|
||||
}));
|
||||
|
||||
float y = 70f;
|
||||
foreach (var window in windows)
|
||||
{
|
||||
var button = MakeButton(font, "", 12f, y, 228f, 24f, () =>
|
||||
{
|
||||
stack.UiHost.ToggleWindow(window.Name);
|
||||
RefreshToggles(toggles);
|
||||
});
|
||||
toggles.Add((window, button));
|
||||
panel.AddChild(button);
|
||||
y += 30f;
|
||||
}
|
||||
|
||||
RefreshToggles(toggles);
|
||||
stack.UiHost.Root.AddChild(panel);
|
||||
}
|
||||
|
||||
private static MockupButton MakeButton(UiDatFont? font, string text,
|
||||
float left, float top, float width, float height, Action onClick)
|
||||
{
|
||||
var button = new MockupButton(font)
|
||||
{
|
||||
Left = left,
|
||||
Top = top,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Text = text,
|
||||
};
|
||||
button.Click += onClick;
|
||||
return button;
|
||||
}
|
||||
|
||||
private static void Arrange(IReadOnlyList<MockupWindow> windows, bool showAll)
|
||||
{
|
||||
foreach (var window in windows)
|
||||
{
|
||||
var element = window.Element;
|
||||
element.Left = window.DefaultRect.Left;
|
||||
element.Top = window.DefaultRect.Top;
|
||||
element.Width = window.DefaultRect.Width;
|
||||
element.Height = window.DefaultRect.Height;
|
||||
element.ZOrder = window.DefaultZOrder;
|
||||
if (showAll)
|
||||
element.Visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RefreshToggles(IReadOnlyList<(MockupWindow Window, MockupButton Button)> toggles)
|
||||
{
|
||||
foreach (var (window, button) in toggles)
|
||||
{
|
||||
bool visible = window.Element.Visible;
|
||||
button.Text = visible ? $"{window.Title} on" : $"{window.Title} off";
|
||||
button.TextColor = visible
|
||||
? new Vector4(0.96f, 0.93f, 0.82f, 1f)
|
||||
: new Vector4(0.62f, 0.62f, 0.62f, 1f);
|
||||
button.BackgroundColor = visible
|
||||
? new Vector4(0.14f, 0.13f, 0.10f, 0.94f)
|
||||
: new Vector4(0.06f, 0.065f, 0.07f, 0.82f);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record MockupWindow(
|
||||
string Name,
|
||||
string Title,
|
||||
UiElement Element,
|
||||
MockupRect DefaultRect,
|
||||
int DefaultZOrder);
|
||||
|
||||
private readonly record struct MockupRect(float Left, float Top, float Width, float Height);
|
||||
|
||||
private sealed class MockupControlPanel : UiPanel
|
||||
{
|
||||
protected override void OnDraw(UiRenderContext ctx)
|
||||
{
|
||||
if (BackgroundColor.W > 0f)
|
||||
ctx.DrawFill(0f, 0f, Width, Height, BackgroundColor);
|
||||
if (BorderColor.W > 0f && BorderThickness > 0f)
|
||||
ctx.DrawRectOutline(0f, 0f, Width, Height, BorderColor, BorderThickness);
|
||||
}
|
||||
|
||||
protected override void OnTick(double deltaSeconds)
|
||||
{
|
||||
base.OnTick(deltaSeconds);
|
||||
if (Parent is null) return;
|
||||
|
||||
int top = ZOrder;
|
||||
foreach (var sibling in Parent.Children)
|
||||
{
|
||||
if (!ReferenceEquals(sibling, this))
|
||||
top = Math.Max(top, sibling.ZOrder + 1);
|
||||
}
|
||||
ZOrder = top;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MockupLabel : UiElement
|
||||
{
|
||||
private readonly UiDatFont? _font;
|
||||
|
||||
public string Text { get; set; } = string.Empty;
|
||||
public Vector4 TextColor { get; set; } = Vector4.One;
|
||||
|
||||
public MockupLabel(UiDatFont? font)
|
||||
{
|
||||
_font = font;
|
||||
ClickThrough = true;
|
||||
}
|
||||
|
||||
protected override void OnDraw(UiRenderContext ctx)
|
||||
{
|
||||
if (_font is null || Text.Length == 0) return;
|
||||
ctx.DrawStringDat(_font, Text, 0f, 0f, TextColor);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MockupButton : UiPanel
|
||||
{
|
||||
private readonly UiDatFont? _font;
|
||||
|
||||
public event Action? Click;
|
||||
public string Text { get; set; } = string.Empty;
|
||||
public Vector4 TextColor { get; set; } = new(0.96f, 0.93f, 0.82f, 1f);
|
||||
|
||||
public MockupButton(UiDatFont? font)
|
||||
{
|
||||
_font = font;
|
||||
BackgroundColor = new Vector4(0.14f, 0.13f, 0.10f, 0.94f);
|
||||
BorderColor = new Vector4(0.45f, 0.38f, 0.24f, 1f);
|
||||
BorderThickness = 1f;
|
||||
}
|
||||
|
||||
public override bool HandlesClick => true;
|
||||
|
||||
public override bool OnEvent(in UiEvent e)
|
||||
{
|
||||
if (e.Type != UiEventType.Click || !Enabled) return false;
|
||||
Click?.Invoke();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnDraw(UiRenderContext ctx)
|
||||
{
|
||||
if (BackgroundColor.W > 0f)
|
||||
ctx.DrawFill(0f, 0f, Width, Height, BackgroundColor);
|
||||
if (BorderColor.W > 0f && BorderThickness > 0f)
|
||||
ctx.DrawRectOutline(0f, 0f, Width, Height, BorderColor, BorderThickness);
|
||||
|
||||
if (_font is null || Text.Length == 0) return;
|
||||
|
||||
float textW = _font.MeasureWidth(Text);
|
||||
float tx = MathF.Max(4f, (Width - textW) * 0.5f);
|
||||
float ty = (Height - _font.LineHeight) * 0.5f;
|
||||
ctx.DrawStringDat(_font, Text, tx, ty, TextColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,12 @@ public sealed class UiHost : System.IDisposable
|
|||
/// <summary>Register a top-level window for Show/Hide/Toggle. See <see cref="UiRoot.RegisterWindow"/>.</summary>
|
||||
public void RegisterWindow(string name, UiElement window) => Root.RegisterWindow(name, window);
|
||||
|
||||
/// <summary>Show a registered window; returns false if the name is unknown.</summary>
|
||||
public bool ShowWindow(string name) => Root.ShowWindow(name);
|
||||
|
||||
/// <summary>Hide a registered window; returns false if the name is unknown.</summary>
|
||||
public bool HideWindow(string name) => Root.HideWindow(name);
|
||||
|
||||
/// <summary>Toggle a registered window's visibility; returns the new IsVisible.</summary>
|
||||
public bool ToggleWindow(string name) => Root.ToggleWindow(name);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,5 +4,9 @@ namespace AcDream.App.UI;
|
|||
/// mount, the window registry, and the toggle keybind all agree on one literal.</summary>
|
||||
public static class WindowNames
|
||||
{
|
||||
public const string Vitals = "vitals";
|
||||
public const string Toolbar = "toolbar";
|
||||
public const string Character = "character";
|
||||
public const string Inventory = "inventory";
|
||||
public const string Chat = "chat";
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue