Create AGENTS.md as a thin bridge to CLAUDE.md, fix isolated dat-layout previews by normalizing panel roots to the studio canvas, add a --mockup mode that mounts production-backed UI windows into one UiHost, and thread the per-element dat-font resolver through GameWindow's live imports. Verified with dotnet test and headless studio screenshots for inventory and the mockup desktop.
This commit is contained in:
parent
ca94b479bf
commit
9444a328fa
8 changed files with 405 additions and 44 deletions
|
|
@ -18,7 +18,7 @@ namespace AcDream.App.Studio;
|
|||
/// (<see cref="RenderBootstrap"/>) and previews a single UI panel
|
||||
/// identified by a <see cref="LayoutSource"/>.
|
||||
///
|
||||
/// Usage: dotnet run -- ui-studio [dat-dir] [--layout 0xNNNN] [--markup path]
|
||||
/// Usage: dotnet run -- ui-studio [dat-dir] [--layout 0xNNNN] [--markup path] [--mockup]
|
||||
///
|
||||
/// Task 3 adds an ImGui IDE on top of the panel FBO:
|
||||
/// <list type="bullet">
|
||||
|
|
@ -124,6 +124,9 @@ public sealed class StudioWindow : IDisposable
|
|||
|
||||
_stack = RenderBootstrap.Create(gl, _dats, new RenderBootstrapOptions(quality));
|
||||
|
||||
if (_opts.Mockup)
|
||||
MockupDesktop.Load(_dats, _stack);
|
||||
|
||||
// Load the panel described by options and add it to the UI tree.
|
||||
// Task 4b: --dump <slug> uses DumpLayout (static retail mockup, no controllers).
|
||||
// All other modes use LayoutSource + FixtureProvider (production path).
|
||||
|
|
@ -141,8 +144,8 @@ public sealed class StudioWindow : IDisposable
|
|||
_dumpSlugs = UiDumpModel.ListSlugs(_dumpFile)
|
||||
.OrderBy(s => s, StringComparer.OrdinalIgnoreCase).ToList();
|
||||
|
||||
UiElement? root;
|
||||
if (_opts.DumpSlug is not null)
|
||||
UiElement? root = null;
|
||||
if (!_opts.Mockup && _opts.DumpSlug is not null)
|
||||
{
|
||||
if (_dumpFile is null)
|
||||
{
|
||||
|
|
@ -160,11 +163,13 @@ public sealed class StudioWindow : IDisposable
|
|||
_currentSlug = _opts.DumpSlug;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!_opts.Mockup)
|
||||
{
|
||||
root = _source.Load(_opts);
|
||||
if (root is null)
|
||||
Console.Error.WriteLine($"[studio] panel load failed: {_source.LastError}");
|
||||
else
|
||||
NormalizeSinglePanelRoot(root);
|
||||
}
|
||||
|
||||
_panelRoot = root;
|
||||
|
|
@ -189,12 +194,18 @@ public sealed class StudioWindow : IDisposable
|
|||
if (_opts.ScreenshotPath is null)
|
||||
{
|
||||
var input = _window!.CreateInput();
|
||||
// Wire KEYBOARD input into UiHost (keyboard coords are not spatial, so no remapping needed).
|
||||
// Do NOT wire mouse here — raw Silk window coords would be offset by the canvas sub-window's
|
||||
// position (tree pane width + ImGui chrome) and land in the wrong panel-local location.
|
||||
// Mouse is forwarded manually from DrawCanvas with correct panel-local mapping below.
|
||||
// Mockup mode draws UiHost directly to the window, so raw Silk mouse coordinates
|
||||
// already match UI pixels. Inspector mode draws UiHost inside an ImGui canvas, so
|
||||
// mouse is forwarded manually from DrawCanvas with canvas-local remapping.
|
||||
if (_opts.Mockup)
|
||||
foreach (var mouse in input.Mice)
|
||||
_stack.UiHost.WireMouse(mouse);
|
||||
foreach (var kb in input.Keyboards)
|
||||
_stack.UiHost.WireKeyboard(kb);
|
||||
|
||||
if (_opts.Mockup)
|
||||
return;
|
||||
|
||||
_imgui = new AcDream.UI.ImGui.ImGuiBootstrapper(gl, _window!, input);
|
||||
_inspector = new StudioInspector();
|
||||
}
|
||||
|
|
@ -215,7 +226,7 @@ public sealed class StudioWindow : IDisposable
|
|||
// Pick render size from the loaded root's bounds (clamped to sane limits).
|
||||
// Fall back to 1280×720 when the root has no explicit size.
|
||||
int w = 1280, h = 720;
|
||||
if (_panelRoot is not null)
|
||||
if (!_opts.Mockup && _panelRoot is not null)
|
||||
{
|
||||
float rw = _panelRoot.Width;
|
||||
float rh = _panelRoot.Height;
|
||||
|
|
@ -264,6 +275,20 @@ public sealed class StudioWindow : IDisposable
|
|||
}
|
||||
|
||||
// ── INTERACTIVE PATH ──────────────────────────────────────────────────────
|
||||
if (_opts.Mockup)
|
||||
{
|
||||
var mockupGl = _stack.Gl;
|
||||
int mockupW = _window!.Size.X;
|
||||
int mockupH = _window!.Size.Y;
|
||||
_stack.UiHost.Tick(dt);
|
||||
mockupGl.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
|
||||
mockupGl.Viewport(0, 0, (uint)mockupW, (uint)mockupH);
|
||||
mockupGl.ClearColor(0.08f, 0.08f, 0.08f, 1f);
|
||||
mockupGl.Clear(ColorBufferBit | DepthBufferBit);
|
||||
_stack.UiHost.Draw(new Vector2(mockupW, mockupH));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_imgui is null || _inspector is null) return;
|
||||
|
||||
var gl = _stack.Gl;
|
||||
|
|
@ -428,6 +453,18 @@ public sealed class StudioWindow : IDisposable
|
|||
_inspector.Selected = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Studio dat-layout previews show one panel in isolation, not at its live-game
|
||||
/// screen position. Some top-level layouts (inventory: x=500,y=138) otherwise
|
||||
/// draw entirely outside the root-sized screenshot FBO.
|
||||
/// </summary>
|
||||
internal static void NormalizeSinglePanelRoot(UiElement root)
|
||||
{
|
||||
root.Left = 0f;
|
||||
root.Top = 0f;
|
||||
root.Anchors = AnchorEdges.None;
|
||||
}
|
||||
|
||||
private void OnClosing()
|
||||
{
|
||||
_imgui?.Dispose();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue