using System.Numerics; using AcDream.UI.Abstractions; using ImGuiNET; namespace AcDream.UI.ImGui; /// /// implemented as thin wrappers around /// ImGui.NET calls. This is the ONLY place where ImGuiNET types appear /// outside of bootstrap plumbing — panels that need a feature must /// extend the abstraction here, not by importing ImGuiNET in panel /// files. /// public sealed class ImGuiPanelRenderer : IPanelRenderer { /// public bool Begin(string title) => ImGuiNET.ImGui.Begin(title); /// public void End() => ImGuiNET.ImGui.End(); /// public void Text(string text) => ImGuiNET.ImGui.TextUnformatted(text); /// public void SameLine() => ImGuiNET.ImGui.SameLine(); /// public void Separator() => ImGuiNET.ImGui.Separator(); /// public void ProgressBar(float fraction, float width, string? overlay = null) { // Clamp defensively; ImGui clamps internally but the abstraction // contract promises to handle out-of-range values. if (fraction < 0f) fraction = 0f; else if (fraction > 1f) fraction = 1f; var size = new Vector2(width, 0f); // height 0 → ImGui picks based on font ImGuiNET.ImGui.ProgressBar(fraction, size, overlay ?? string.Empty); } // -- Phase I.1 widget extensions --------------------------------- /// public void TextColored(Vector4 rgba, string text) => ImGuiNET.ImGui.TextColored(rgba, text); /// public bool CollapsingHeader(string label, bool defaultOpen = true) => ImGuiNET.ImGui.CollapsingHeader( label, defaultOpen ? ImGuiTreeNodeFlags.DefaultOpen : ImGuiTreeNodeFlags.None); /// public bool TreeNode(string label) => ImGuiNET.ImGui.TreeNode(label); /// public void TreePop() => ImGuiNET.ImGui.TreePop(); /// public bool Checkbox(string label, ref bool value) => ImGuiNET.ImGui.Checkbox(label, ref value); /// public bool Button(string label) => ImGuiNET.ImGui.Button(label); /// public bool Combo(string label, ref int selectedIndex, string[] items) => ImGuiNET.ImGui.Combo(label, ref selectedIndex, items, items.Length); /// public bool SliderFloat(string label, ref float value, float min, float max) => ImGuiNET.ImGui.SliderFloat(label, ref value, min, max); /// public void PlotLines( string label, float[] values, int count, int offset = 0, string? overlay = null, float? min = null, float? max = null, Vector2? size = null) { // ImGui.NET 1.91.6.1's PlotLines binding takes `ref float values` // (pointer-to-first-element semantics) plus a separate values_count // and values_offset. The "no fixed bound" / "default size" sentinels // are float.MaxValue and Vector2.Zero respectively — we pass those // when the caller leaves the optional args null. if (count <= 0 || values.Length == 0) { // Nothing to plot — emit the label so layout doesn't shift but // skip the native call (ref to values[0] would NRE on empty). ImGuiNET.ImGui.TextUnformatted(label); return; } float scaleMin = min ?? float.MaxValue; float scaleMax = max ?? float.MaxValue; Vector2 graphSize = size ?? Vector2.Zero; ImGuiNET.ImGui.PlotLines( label, ref values[0], count, offset, overlay ?? string.Empty, scaleMin, scaleMax, graphSize); } /// public void BeginTable(string id, int columns) => ImGuiNET.ImGui.BeginTable(id, columns); /// public void TableNextColumn() => ImGuiNET.ImGui.TableNextColumn(); /// public void EndTable() => ImGuiNET.ImGui.EndTable(); /// public bool InputTextSubmit(string label, ref string buffer, int maxLen, out string? submitted) { // EnterReturnsTrue: the call returns true on the frame the user // pressed Enter. On every other frame ImGui still mutates `buffer` // as the user types; we just don't surface a submit. bool entered = ImGuiNET.ImGui.InputText( label, ref buffer, (uint)maxLen, ImGuiInputTextFlags.EnterReturnsTrue); if (entered) { submitted = buffer; buffer = string.Empty; // contract: clear for next frame return true; } submitted = null; return false; } /// public void Spacing() => ImGuiNET.ImGui.Spacing(); /// public void Dummy(Vector2 size) => ImGuiNET.ImGui.Dummy(size); /// public void TextWrapped(string text) => ImGuiNET.ImGui.TextWrapped(text); }