Phase K final commit. Settings panel with click-to-rebind UX on top of the K.1+K.2 input architecture, plus the roadmap / ISSUES / memory updates that retire Phase K. InputDispatcher gains BeginCapture / CancelCapture / IsCapturing / SetBindings — modal capture suppresses normal action firing for the next chord. Esc cancels (returns sentinel default chord); modifier-only keys don't complete capture; non-modifier key down with current modifier mask completes. IPanelRenderer + ImGuiPanelRenderer + FakePanelRenderer gain BeginMainMenuBar / EndMainMenuBar / BeginMenu / EndMenu / MenuItem primitives. SettingsVM owns a draft copy of KeyBindings with explicit Save / Cancel / Reset semantics. Click-to-rebind enters dispatcher capture mode; on chord captured, conflict-detect against draft (excluding the action being rebound itself); surface a ConflictPrompt when the chord collides; ResolveConflict(replace=true|false) commits or reverts. ResetActionToDefault restores a single action to RetailDefaults(); ResetAllToDefaults rebuilds the entire draft. Save invokes the onSave callback (which writes JSON + swaps the live dispatcher's bindings). SettingsPanel renders 8 retail-keymap-categorized CollapsingHeader sections (Movement, Postures, Camera, Combat, UI panels, Chat, Hotbar, Emotes). Per action: name + current binding(s) summary + "Rebind"/"Reset" buttons. Conflict prompt at the top when pending. Save / Cancel / "Reset all to retail defaults" at the top. GameWindow registers SettingsPanel + wires F11 → ToggleOptionsPanel → IsVisible toggle, plus a top-of-frame ImGui MainMenuBar with View → Settings/Vitals/Chat/Debug entries (calls ImGui directly — the abstraction methods exist for backend portability but the host doesn't own a menu-bar surface). Tests: +37 across InputDispatcherCaptureTests (7), IPanelRendererMainMenuBarTests (9), SettingsVMTests (13), SettingsPanelTests (8). Solution total 1220 green. Roadmap (docs/plans/2026-04-11-roadmap.md) appends Phase K shipped section after Phase J with K.1a–K.3 commit SHAs. ISSUES.md files Phase L deferred work as #L.1–#L.8 (hotbar UI, spellbook favorites, combat-mode dispatch, F-key panels, floating chat windows, UI layout save/load, joystick bindings, plugin input subscription) and adds #21–#25 to Recently closed. project_input_pipeline.md updated to shipped state. CLAUDE.md gets an input-pipeline reference. Closes Phase K. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
238 lines
9.6 KiB
C#
238 lines
9.6 KiB
C#
using System.Numerics;
|
|
|
|
namespace AcDream.UI.Abstractions;
|
|
|
|
/// <summary>
|
|
/// Drawing primitives exposed to panels. The <b>only</b> API panels use to
|
|
/// emit pixels. The ImGui backend maps these straight onto ImGui calls; the
|
|
/// later custom retail-look backend will map the same primitives onto its
|
|
/// own retained-mode toolkit using retail dat-sourced fonts / sprites.
|
|
///
|
|
/// <para>
|
|
/// Keep this interface small and retail-friendly. If a widget requires a
|
|
/// feature the custom backend couldn't express with dat assets, don't add
|
|
/// it — find a different widget shape that both backends can satisfy.
|
|
/// </para>
|
|
/// </summary>
|
|
public interface IPanelRenderer
|
|
{
|
|
/// <summary>
|
|
/// Begin a top-level window. Matches retail's root <c>UiPanel</c> +
|
|
/// ImGui's <c>Begin</c>. Returns <c>false</c> if the window is collapsed
|
|
/// — the caller must still call <see cref="End"/> to balance.
|
|
/// </summary>
|
|
bool Begin(string title);
|
|
|
|
/// <summary>Close the most recent <see cref="Begin"/>.</summary>
|
|
void End();
|
|
|
|
/// <summary>Draw a single line of text. No formatting / markdown.</summary>
|
|
void Text(string text);
|
|
|
|
/// <summary>Keep the next widget on the same line as the previous one.</summary>
|
|
void SameLine();
|
|
|
|
/// <summary>Horizontal rule separator.</summary>
|
|
void Separator();
|
|
|
|
/// <summary>
|
|
/// A filled progress bar.
|
|
/// <paramref name="fraction"/> is clamped by the backend to [0, 1].
|
|
/// <paramref name="width"/> is the pixel width of the full bar.
|
|
/// <paramref name="overlay"/> is optional text (e.g. <c>"54%"</c>) rendered on top.
|
|
/// </summary>
|
|
void ProgressBar(float fraction, float width, string? overlay = null);
|
|
|
|
// -- Phase I.1 widget extensions ----------------------------------
|
|
|
|
/// <summary>
|
|
/// Draw a single line of text in the supplied RGBA color.
|
|
/// Used for combat-event color-coding (damage red, heal green) and
|
|
/// any other surface where a glance distinguishes severity.
|
|
/// </summary>
|
|
void TextColored(Vector4 rgba, string text);
|
|
|
|
/// <summary>
|
|
/// Open / close section grouping inside a window. Returns
|
|
/// <c>true</c> when the section is currently expanded — only render
|
|
/// the section's contents in that branch.
|
|
/// </summary>
|
|
/// <param name="defaultOpen">If the user has never toggled the
|
|
/// header before, start in this state.</param>
|
|
bool CollapsingHeader(string label, bool defaultOpen = true);
|
|
|
|
/// <summary>
|
|
/// Push an expandable nested node. Pair every successful
|
|
/// <c>true</c> return with a matching <see cref="TreePop"/>;
|
|
/// when this returns <c>false</c> do NOT call <see cref="TreePop"/>.
|
|
/// </summary>
|
|
bool TreeNode(string label);
|
|
|
|
/// <summary>Pop a previously-pushed <see cref="TreeNode"/>.</summary>
|
|
void TreePop();
|
|
|
|
/// <summary>
|
|
/// A boolean toggle. Returns <c>true</c> on the frame the user
|
|
/// flipped the box (and updates <paramref name="value"/> in place);
|
|
/// returns <c>false</c> the rest of the time.
|
|
/// </summary>
|
|
bool Checkbox(string label, ref bool value);
|
|
|
|
/// <summary>
|
|
/// A clickable button. Returns <c>true</c> on the single frame the
|
|
/// user clicked it; <c>false</c> every other frame.
|
|
/// </summary>
|
|
bool Button(string label);
|
|
|
|
/// <summary>
|
|
/// A drop-down selector. Returns <c>true</c> on the frame the user
|
|
/// changed the selection (and updates <paramref name="selectedIndex"/>
|
|
/// to the new value); <c>false</c> otherwise.
|
|
/// </summary>
|
|
bool Combo(string label, ref int selectedIndex, string[] items);
|
|
|
|
/// <summary>
|
|
/// A horizontal slider clamped to <paramref name="min"/> /
|
|
/// <paramref name="max"/>. Returns <c>true</c> on frames where the
|
|
/// user dragged the value (and updates <paramref name="value"/>);
|
|
/// <c>false</c> when idle.
|
|
/// </summary>
|
|
bool SliderFloat(string label, ref float value, float min, float max);
|
|
|
|
/// <summary>
|
|
/// Time-series line graph (e.g. fps history). The active window is
|
|
/// <paramref name="values"/>[<paramref name="offset"/>..
|
|
/// <paramref name="offset"/>+<paramref name="count"/>] interpreted
|
|
/// as a ring buffer.
|
|
/// </summary>
|
|
/// <param name="label">Header text shown above the plot.</param>
|
|
/// <param name="values">Sample buffer (kept by the caller).</param>
|
|
/// <param name="count">Number of valid samples in the ring.</param>
|
|
/// <param name="offset">Index of the oldest sample in
|
|
/// <paramref name="values"/>.</param>
|
|
/// <param name="overlay">Optional centered overlay text (e.g.
|
|
/// <c>"60 fps"</c>).</param>
|
|
/// <param name="min">Optional fixed lower bound; null = autoscale.</param>
|
|
/// <param name="max">Optional fixed upper bound; null = autoscale.</param>
|
|
/// <param name="size">Optional pixel size; null = backend default.</param>
|
|
void PlotLines(
|
|
string label,
|
|
float[] values,
|
|
int count,
|
|
int offset = 0,
|
|
string? overlay = null,
|
|
float? min = null,
|
|
float? max = null,
|
|
Vector2? size = null);
|
|
|
|
/// <summary>
|
|
/// Begin a multi-column table. Pair every call with <see cref="EndTable"/>.
|
|
/// Inside the table, advance to each cell with
|
|
/// <see cref="TableNextColumn"/> before emitting widgets for it.
|
|
/// </summary>
|
|
void BeginTable(string id, int columns);
|
|
|
|
/// <summary>Move to the next cell of the active table.</summary>
|
|
void TableNextColumn();
|
|
|
|
/// <summary>Close the most recent <see cref="BeginTable"/>.</summary>
|
|
void EndTable();
|
|
|
|
/// <summary>
|
|
/// A single-line text input that submits on Enter. Returns
|
|
/// <c>true</c> on the frame the user pressed Enter; in that case
|
|
/// <paramref name="submitted"/> is the value entered and the
|
|
/// implementation clears <paramref name="buffer"/> for the next
|
|
/// frame. On every other frame returns <c>false</c> and
|
|
/// <paramref name="submitted"/> is null; the implementation may
|
|
/// still mutate <paramref name="buffer"/> as the user types.
|
|
/// </summary>
|
|
/// <param name="maxLen">Maximum characters the buffer may grow to.</param>
|
|
bool InputTextSubmit(string label, ref string buffer, int maxLen, out string? submitted);
|
|
|
|
/// <summary>Insert a small vertical gap.</summary>
|
|
void Spacing();
|
|
|
|
/// <summary>Reserve invisible space of the given size, useful for
|
|
/// layout-only padding.</summary>
|
|
void Dummy(Vector2 size);
|
|
|
|
/// <summary>
|
|
/// Draw text that wraps at the current content region width.
|
|
/// Use for descriptions, error messages, anything multi-line.
|
|
/// </summary>
|
|
void TextWrapped(string text);
|
|
|
|
// -- Phase J Tier 3 — scrollable child region for chat-style layouts --
|
|
|
|
/// <summary>
|
|
/// Open a scrollable nested region inside the current window.
|
|
/// <paramref name="size"/> follows ImGui semantics: 0 means "fill
|
|
/// available", a positive value is absolute pixels, a negative
|
|
/// value means "fill available minus this amount". Pass
|
|
/// <c>(0, -footerHeight)</c> to reserve space at the bottom for
|
|
/// fixed elements (separator + input field) so the inner content
|
|
/// scrolls but the footer stays put across window resizes.
|
|
/// </summary>
|
|
bool BeginChild(string id, Vector2 size, bool border = false);
|
|
|
|
/// <summary>Close the most recent <see cref="BeginChild"/>.</summary>
|
|
void EndChild();
|
|
|
|
/// <summary>
|
|
/// Approximate single-line widget height including ImGui's frame
|
|
/// padding + item spacing. Panels use this to compute footer
|
|
/// reservations for <see cref="BeginChild"/> (e.g. one input
|
|
/// field + spacing). Backend-friendly because it returns a
|
|
/// scalar that the custom retail-look toolkit can also expose.
|
|
/// </summary>
|
|
float FrameHeightWithSpacing();
|
|
|
|
/// <summary>
|
|
/// Scroll the current scroll region so that the given fractional
|
|
/// vertical position is visible, where 0 = top and 1 = bottom.
|
|
/// Typical usage: call with <c>1.0f</c> at the bottom of a
|
|
/// chat-tail render block to keep the latest line visible when
|
|
/// new entries arrive. No-op when no new content was emitted —
|
|
/// callers are expected to skip the call when they don't want
|
|
/// to force a scroll.
|
|
/// </summary>
|
|
void SetScrollHereY(float ratio);
|
|
|
|
/// <summary>
|
|
/// Request keyboard focus for the NEXT widget rendered. Used by
|
|
/// <c>ChatPanel</c> when Tab fires <c>ToggleChatEntry</c> — the
|
|
/// chat input gets focused programmatically so the user can begin
|
|
/// typing without clicking the field.
|
|
/// </summary>
|
|
void SetKeyboardFocusHere();
|
|
|
|
// -- Phase K.3 — top-of-screen main menu bar -------------------------
|
|
|
|
/// <summary>
|
|
/// Open the top-of-screen main menu bar. Returns true if the bar is
|
|
/// visible (top-level menus go inside that branch). Always pair with
|
|
/// <see cref="EndMainMenuBar"/> when the call returned true.
|
|
/// </summary>
|
|
bool BeginMainMenuBar();
|
|
|
|
/// <summary>Close the menu bar opened by <see cref="BeginMainMenuBar"/>.</summary>
|
|
void EndMainMenuBar();
|
|
|
|
/// <summary>
|
|
/// Open a top-level menu within a menu bar. Returns true if the menu
|
|
/// is open — the caller emits <see cref="MenuItem"/> entries inside
|
|
/// that branch, then calls <see cref="EndMenu"/>.
|
|
/// </summary>
|
|
bool BeginMenu(string label);
|
|
|
|
/// <summary>Close the menu opened by <see cref="BeginMenu"/>.</summary>
|
|
void EndMenu();
|
|
|
|
/// <summary>
|
|
/// A clickable menu item with optional shortcut hint (e.g.
|
|
/// <c>"F11"</c>) drawn right-aligned. Returns true on the single
|
|
/// frame the user clicks the item; false otherwise.
|
|
/// </summary>
|
|
bool MenuItem(string label, string? shortcut = null);
|
|
}
|