acdream/src/AcDream.UI.Abstractions/IPanelHost.cs
Erik 8c64ad2eeb feat(ui): AcDream.UI.Abstractions layer — IPanel / IPanelRenderer / VitalsVM
Adds the backend-agnostic UI contract layer called for by the 2026-04-24
staged UI strategy (docs/plans/2026-04-24-ui-framework.md). This is the
stable layer both the Phase D.2a Hexa.NET.ImGui backend and the later
D.2b custom retail-look backend implement.

New module `src/AcDream.UI.Abstractions/`:

  * IPanel        — a drawable panel (id/title/visible/Render)
  * IPanelHost    — owns the panel list, drives per-frame dispatch
  * IPanelRenderer — drawing primitives (Begin/End/Text/SameLine/
                    Separator/ProgressBar). Kept small + retail-friendly
                    on purpose — if a widget can't be expressed with
                    dat-sourced sprites+fonts later, don't add it here.
  * ICommandBus   — user-intent publisher; NullCommandBus is D.2a default
  * PanelContext  — per-frame record struct (DeltaSeconds + Commands)
  * Panels/Vitals/
      VitalsVM   — reads CombatState.GetHealthPercent for the local
                   player. Stamina/Mana return null in D.2a; they await
                   a LocalPlayerState cache of PlayerDescription (0x0013)
                   which is filed as a follow-up issue.
      VitalsPanel — first real panel. HP bar always drawn; Stam/Mana
                    appear automatically when the VM returns non-null.

Invariant documented in IPanel's XML doc: no `using Hexa.NET.ImGui` in
panel files, ever. If a widget needs something IPanelRenderer can't
express, the interface grows; panels never reach through.

References AcDream.Core for CombatState. Zero runtime/UI dependencies
— this project compiles headless, perfect for unit testing the
ViewModels.

No visible change yet. Next commits: (2) tests, (3) ImGui backend,
(4) GameWindow hookup + visible panel behind ACDREAM_DEVTOOLS=1.
2026-04-25 00:24:11 +02:00

36 lines
1.3 KiB
C#

namespace AcDream.UI.Abstractions;
/// <summary>
/// Owns the set of live <see cref="IPanel"/>s and drives per-frame draw
/// dispatch. The backend (Hexa.NET.ImGui in D.2a, custom in D.2b) implements
/// this; <c>GameWindow</c> creates one at startup and registers panels.
///
/// <para>
/// <b>Does not</b> call <c>ImGui.NewFrame</c> / <c>ImGui.Render</c> — those
/// belong to the caller so GL-state ownership is unambiguous. Caller pattern:
/// </para>
///
/// <code>
/// // per frame, render thread
/// inputBridge.BeginFrame(size, dt);
/// ImGui.NewFrame();
/// panelHost.RenderAll(ctx);
/// ImGui.Render();
/// ImGuiImplOpenGL3.RenderDrawData(ImGui.GetDrawData());
/// </code>
/// </summary>
public interface IPanelHost
{
/// <summary>Register a panel for per-frame rendering. Idempotent by <see cref="IPanel.Id"/>.</summary>
void Register(IPanel panel);
/// <summary>Remove the panel with the matching id. No-op if not present.</summary>
void Unregister(string panelId);
/// <summary>
/// Iterate every visible panel and call <see cref="IPanel.Render"/>. Call
/// order within a frame is the registration order; panels with
/// <see cref="IPanel.IsVisible"/> set to <c>false</c> are skipped entirely.
/// </summary>
void RenderAll(PanelContext ctx);
}