using System.Numerics; namespace AcDream.UI.Abstractions.Tests; /// /// In-memory test double for . Records every /// widget call so tests can assert against the recorded trace without /// running an ImGui context. New widgets added to the interface must /// implement here too — that compilation error IS the TDD red signal. /// internal sealed class FakePanelRenderer : IPanelRenderer { /// Ordered list of (method, args) pairs recorded across this renderer's lifetime. public List<(string Method, object?[] Args)> Calls { get; } = new(); /// If is called, return this value. Defaults to true so panels render. public bool BeginReturns { get; set; } = true; // -- Inputs the panel under test can mutate via ref args ----------- /// Pre-set return value for the next — caller flips bool to simulate user click. public bool CheckboxNextReturn { get; set; } public bool? CheckboxNextValue { get; set; } /// Pre-set return value for the next — caller sets to simulate user click. public bool ButtonNextReturn { get; set; } /// Pre-set return value for the next . public bool ComboNextReturn { get; set; } public int? ComboNextSelectedIndex { get; set; } /// Pre-set return for . public bool SliderFloatNextReturn { get; set; } public float? SliderFloatNextValue { get; set; } /// Pre-set return for . public bool CollapsingHeaderNextReturn { get; set; } = true; /// Pre-set return for . public bool TreeNodeNextReturn { get; set; } = true; /// Pre-set return for . public bool BeginTableNextReturn { get; set; } = true; /// Pre-set return for . public bool BeginChildNextReturn { get; set; } = true; /// Pre-set return value for . public float FrameHeightWithSpacingValue { get; set; } = 24f; /// Pre-set "submitted" string for the next ; null = no submit this frame. public string? InputTextSubmitNextSubmitted { get; set; } public string? InputTextSubmitNextBufferAfter { get; set; } /// K.3: return value (default true). public bool MainMenuBarReturns { get; set; } = true; /// K.3: return value (default true). public bool MenuReturns { get; set; } = true; /// K.3: return value (default false — only true for the frame the user "clicks"). public bool MenuItemReturns { get; set; } public bool Begin(string title) { Calls.Add(("Begin", new object?[] { title })); return BeginReturns; } public void End() => Calls.Add(("End", Array.Empty())); public void Text(string text) => Calls.Add(("Text", new object?[] { text })); public void SameLine() => Calls.Add(("SameLine", Array.Empty())); public void Separator() => Calls.Add(("Separator", Array.Empty())); public void ProgressBar(float fraction, float width, string? overlay = null) => Calls.Add(("ProgressBar", new object?[] { fraction, width, overlay })); public void TextColored(Vector4 rgba, string text) => Calls.Add(("TextColored", new object?[] { rgba, text })); public bool CollapsingHeader(string label, bool defaultOpen = true) { Calls.Add(("CollapsingHeader", new object?[] { label, defaultOpen })); return CollapsingHeaderNextReturn; } public bool TreeNode(string label) { Calls.Add(("TreeNode", new object?[] { label })); return TreeNodeNextReturn; } public void TreePop() => Calls.Add(("TreePop", Array.Empty())); public bool Checkbox(string label, ref bool value) { Calls.Add(("Checkbox", new object?[] { label, value })); if (CheckboxNextValue is bool nv) value = nv; return CheckboxNextReturn; } public bool Button(string label) { Calls.Add(("Button", new object?[] { label })); return ButtonNextReturn; } public bool Combo(string label, ref int selectedIndex, string[] items) { Calls.Add(("Combo", new object?[] { label, selectedIndex, items })); if (ComboNextSelectedIndex is int idx) selectedIndex = idx; return ComboNextReturn; } public bool SliderFloat(string label, ref float value, float min, float max) { Calls.Add(("SliderFloat", new object?[] { label, value, min, max })); if (SliderFloatNextValue is float v) value = v; return SliderFloatNextReturn; } public void PlotLines( string label, float[] values, int count, int offset = 0, string? overlay = null, float? min = null, float? max = null, Vector2? size = null) => Calls.Add(("PlotLines", new object?[] { label, values, count, offset, overlay, min, max, size })); public void BeginTable(string id, int columns) => Calls.Add(("BeginTable", new object?[] { id, columns })); public void TableNextColumn() => Calls.Add(("TableNextColumn", Array.Empty())); public void EndTable() => Calls.Add(("EndTable", Array.Empty())); public bool InputTextSubmit(string label, ref string buffer, int maxLen, out string? submitted) { Calls.Add(("InputTextSubmit", new object?[] { label, buffer, maxLen })); submitted = InputTextSubmitNextSubmitted; if (submitted is not null) buffer = InputTextSubmitNextBufferAfter ?? string.Empty; return submitted is not null; } public void Spacing() => Calls.Add(("Spacing", Array.Empty())); public void Dummy(Vector2 size) => Calls.Add(("Dummy", new object?[] { size })); public void TextWrapped(string text) => Calls.Add(("TextWrapped", new object?[] { text })); public bool BeginChild(string id, Vector2 size, bool border = false) { Calls.Add(("BeginChild", new object?[] { id, size, border })); return BeginChildNextReturn; } public void EndChild() => Calls.Add(("EndChild", Array.Empty())); public float FrameHeightWithSpacing() { Calls.Add(("FrameHeightWithSpacing", Array.Empty())); return FrameHeightWithSpacingValue; } public void SetScrollHereY(float ratio) => Calls.Add(("SetScrollHereY", new object?[] { ratio })); public void SetKeyboardFocusHere() => Calls.Add(("SetKeyboardFocusHere", Array.Empty())); // -- Phase K.3 — main menu bar ----------------------------------------- public bool BeginMainMenuBar() { Calls.Add(("BeginMainMenuBar", Array.Empty())); return MainMenuBarReturns; } public void EndMainMenuBar() => Calls.Add(("EndMainMenuBar", Array.Empty())); public bool BeginMenu(string label) { Calls.Add(("BeginMenu", new object?[] { label })); return MenuReturns; } public void EndMenu() => Calls.Add(("EndMenu", Array.Empty())); public bool MenuItem(string label, string? shortcut = null) { Calls.Add(("MenuItem", new object?[] { label, shortcut })); return MenuItemReturns; } // -- Tab bar ----------------------------------------------------------- /// Pre-set return for . public bool TabBarReturns { get; set; } = true; /// The label of the tab the next /// call should report as "selected" (return true). All other tab /// items return false. Defaults to null = the FIRST tab item rendered /// is the selected one. public string? ActiveTabLabel { get; set; } private string? _firstTabSeen; public bool BeginTabBar(string id) { Calls.Add(("BeginTabBar", new object?[] { id })); _firstTabSeen = null; return TabBarReturns; } public void EndTabBar() => Calls.Add(("EndTabBar", Array.Empty())); public bool BeginTabItem(string label) { Calls.Add(("BeginTabItem", new object?[] { label })); _firstTabSeen ??= label; return ActiveTabLabel is null ? string.Equals(label, _firstTabSeen, StringComparison.Ordinal) : string.Equals(label, ActiveTabLabel, StringComparison.Ordinal); } public void EndTabItem() => Calls.Add(("EndTabItem", Array.Empty())); public void TextMultilineReadOnly(string id, string content, Vector2 size) => Calls.Add(("TextMultilineReadOnly", new object?[] { id, content, size })); }