diff --git a/Dalamud/Interface/Utility/Raii/Disposables/PlotAlignedDisposable.cs b/Dalamud/Interface/Utility/Raii/Disposables/PlotAlignedDisposable.cs
new file mode 100644
index 000000000..760b0ff05
--- /dev/null
+++ b/Dalamud/Interface/Utility/Raii/Disposables/PlotAlignedDisposable.cs
@@ -0,0 +1,73 @@
+// ReSharper disable once CheckNamespace
+
+using Dalamud.Bindings.ImPlot;
+
+namespace Dalamud.Interface.Utility.Raii;
+
+public static partial class ImRaii
+{
+ /// A wrapper around ImGui tables.
+ public ref struct PlotAlignedDisposable : IDisposable
+ {
+ /// Whether creating the table succeeded. This needs to be checked before calling any of the member methods.
+ public readonly bool Success;
+
+ /// Whether the table is already ended.
+ public bool Alive { get; private set; }
+
+ /// Initializes a new instance of the struct.
+ /// The ID of the plot as text.
+ /// Whether the plot should be vertical.
+ internal PlotAlignedDisposable(string groupId, bool vertical = true)
+ {
+ this.Success = ImPlot.BeginAlignedPlots(groupId, vertical);
+ this.Alive = true;
+ }
+
+ ///
+ internal PlotAlignedDisposable(ReadOnlySpan groupId, bool vertical = true)
+ {
+ this.Success = ImPlot.BeginAlignedPlots(groupId, vertical);
+ this.Alive = true;
+ }
+
+ /// Conversion to bool.
+ public static implicit operator bool(PlotAlignedDisposable value)
+ => value.Success;
+
+ /// Conversion to bool.
+ public static bool operator true(PlotAlignedDisposable i)
+ => i.Success;
+
+ /// Conversion to bool.
+ public static bool operator false(PlotAlignedDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on NOT operators.
+ public static bool operator !(PlotAlignedDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on AND operators.
+ public static bool operator &(PlotAlignedDisposable i, bool value)
+ => i.Success && value;
+
+ /// Conversion to bool on OR operators.
+ public static bool operator |(PlotAlignedDisposable i, bool value)
+ => i.Success || value;
+
+ /// End the Table on leaving scope.
+ public void Dispose()
+ {
+ if (!this.Alive)
+ return;
+
+ if (this.Success)
+ ImPlot.EndAlignedPlots();
+ this.Alive = false;
+ }
+
+ /// End a Table without using an IDisposable.
+ public static void EndUnsafe()
+ => ImPlot.EndAlignedPlots();
+ }
+}
diff --git a/Dalamud/Interface/Utility/Raii/Disposables/PlotColorDisposable.cs b/Dalamud/Interface/Utility/Raii/Disposables/PlotColorDisposable.cs
new file mode 100644
index 000000000..da7a7e789
--- /dev/null
+++ b/Dalamud/Interface/Utility/Raii/Disposables/PlotColorDisposable.cs
@@ -0,0 +1,124 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using Dalamud.Bindings.ImGui;
+using Dalamud.Bindings.ImPlot;
+
+// ReSharper disable once CheckNamespace
+namespace Dalamud.Interface.Utility.Raii;
+
+public static partial class ImRaii
+{
+ public sealed class PlotColorDisposable : IDisposable
+ {
+ internal static readonly List<(ImPlotCol Type, uint BackupColor)> Stack = [];
+
+ /// Gets the number of colors currently pushed using this disposable.
+ public int Count { get; private set; }
+
+ /// Push a color to the color stack.
+ /// The type of color to change.
+ /// The color to change it to.
+ /// If this is false, the color is not pushed.
+ /// A disposable object that can be used to push further colors and pops those colors after leaving scope. Use with using.
+ /// If you need to keep colors pushed longer than the current scope, use without using and use .
+ public PlotColorDisposable Push(ImPlotCol type, uint color, bool condition = true)
+ => condition ? this.InternalPush(type, color) : this;
+
+ /// Push a color to the color stack.
+ /// The type of color to change.
+ /// The color to change it to.
+ /// /// If this is false, the color is not pushed.
+ /// A disposable object that can be used to push further colors and pops those colors after leaving scope. Use with using.
+ /// If you need to keep colors pushed longer than the current scope, use without using and use .
+ public PlotColorDisposable Push(ImPlotCol type, Vector4 color, bool condition = true)
+ => condition ? this.InternalPush(type, color) : this;
+
+ /// Push a color to the color stack.
+ /// The type of color to change.
+ /// The color to change it to. If this is null, no color will be set.
+ /// /// If this is false, the color is not pushed.
+ /// A disposable object that can be used to push further colors and pops those colors after leaving scope. Use with using.
+ /// If you need to keep colors pushed longer than the current scope, use without using and use .
+ public PlotColorDisposable Push(ImPlotCol type, Vector4? color, bool condition = true)
+ {
+ if (!color.HasValue)
+ return this;
+
+ return condition ? this.InternalPush(type, color.Value) : this;
+ }
+
+ private PlotColorDisposable InternalPush(ImPlotCol type, uint color)
+ {
+ Stack.Add((type, GetColorU32(type)));
+ ImPlot.PushStyleColor(type, color);
+ ++this.Count;
+ return this;
+ }
+
+ private PlotColorDisposable InternalPush(ImPlotCol type, Vector4 color)
+ {
+ Stack.Add((type, GetColorU32(type)));
+ ImPlot.PushStyleColor(type, color);
+ ++this.Count;
+ return this;
+ }
+
+ /// Reverts all pushed colors to their previous values temporarily.
+ /// A disposable object that can be used to push further colors and pops those colors after leaving scope. Use with using.
+ /// If you need to keep colors pushed longer than the current scope, use without using and use .
+ public static PlotColorDisposable PlotDefaultColors()
+ {
+ var ret = new PlotColorDisposable();
+ var reverseStack = Stack.GroupBy(p => p.Type).Select(p => (p.Key, p.First().BackupColor)).ToArray();
+ foreach (var (idx, val) in reverseStack)
+ ret.Push(idx, val);
+ return ret;
+ }
+
+ /// Push the default value, i.e. the value as if nothing was ever pushed to this, of a color to the color stack.
+ /// The type of color to return to its default value.
+ /// A disposable object that can be used to push further colors and pops those colors after leaving scope. Use with using.
+ /// If you need to keep colors pushed longer than the current scope, use without using and use .
+ public PlotColorDisposable PlotPushDefault(ImPlotCol type)
+ {
+ foreach (var styleMod in Stack.Where(m => m.Type == type))
+ return this.Push(type, styleMod.BackupColor);
+
+ return this;
+ }
+
+ /// Pop a number of colors.
+ /// The number of colors to pop. This is clamped to the number of colors pushed by this object.
+ public PlotColorDisposable Pop(int num = 1)
+ {
+ num = Math.Min(num, this.Count);
+ if (num > 0)
+ {
+ this.Count -= num;
+ ImPlot.PopStyleColor(num);
+ Stack.RemoveRange(Stack.Count - num, num);
+ }
+
+ return this;
+ }
+
+ /// Pop all pushed colors.
+ public void Dispose()
+ {
+ this.Pop(this.Count);
+ this.Count = 0;
+ }
+
+ /// Pop a number of colors.
+ /// The number of colors to pop. The number is not checked against the color stack.
+ /// Avoid using this function, and colors across scopes, as much as possible.
+ public static void PopUnsafe(int num = 1)
+ => ImPlot.PopStyleColor(num);
+
+ // Reimplementation of https://github.com/ocornut/imgui/blob/868facff9ded2d61425c67deeba354eb24275bd1/imgui.cpp#L3035
+ // for ImPlot
+ private static uint GetColorU32(ImPlotCol idx)
+ => ImGui.GetColorU32(ImPlot.GetStyle().Colors[(int)idx]);
+ }
+}
diff --git a/Dalamud/Interface/Utility/Raii/Disposables/PlotDisposable.cs b/Dalamud/Interface/Utility/Raii/Disposables/PlotDisposable.cs
new file mode 100644
index 000000000..a2917ca80
--- /dev/null
+++ b/Dalamud/Interface/Utility/Raii/Disposables/PlotDisposable.cs
@@ -0,0 +1,75 @@
+// ReSharper disable once CheckNamespace
+
+using System.Numerics;
+using Dalamud.Bindings.ImPlot;
+
+namespace Dalamud.Interface.Utility.Raii;
+
+public static partial class ImRaii
+{
+ /// A wrapper around ImGui tables.
+ public ref struct PlotDisposable : IDisposable
+ {
+ /// Whether creating the table succeeded. This needs to be checked before calling any of the member methods.
+ public readonly bool Success;
+
+ /// Whether the table is already ended.
+ public bool Alive { get; private set; }
+
+ /// Initializes a new instance of the struct.
+ /// The ID of the plot as text.
+ /// The desired size of the plot.
+ /// Additional flags for the plot.
+ internal PlotDisposable(string titleId, Vector2 size, ImPlotFlags flags)
+ {
+ this.Success = ImPlot.BeginPlot(titleId, size, flags);
+ this.Alive = true;
+ }
+
+ ///
+ internal PlotDisposable(ReadOnlySpan titleId, Vector2 size, ImPlotFlags flags)
+ {
+ this.Success = ImPlot.BeginPlot(titleId, size, flags);
+ this.Alive = true;
+ }
+
+ /// Conversion to bool.
+ public static implicit operator bool(PlotDisposable value)
+ => value.Success;
+
+ /// Conversion to bool.
+ public static bool operator true(PlotDisposable i)
+ => i.Success;
+
+ /// Conversion to bool.
+ public static bool operator false(PlotDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on NOT operators.
+ public static bool operator !(PlotDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on AND operators.
+ public static bool operator &(PlotDisposable i, bool value)
+ => i.Success && value;
+
+ /// Conversion to bool on OR operators.
+ public static bool operator |(PlotDisposable i, bool value)
+ => i.Success || value;
+
+ /// End the Table on leaving scope.
+ public void Dispose()
+ {
+ if (!this.Alive)
+ return;
+
+ if (this.Success)
+ ImPlot.EndPlot();
+ this.Alive = false;
+ }
+
+ /// End a Table without using an IDisposable.
+ public static void EndUnsafe()
+ => ImPlot.EndPlot();
+ }
+}
diff --git a/Dalamud/Interface/Utility/Raii/Disposables/PlotDragDropSourceDisposable.cs b/Dalamud/Interface/Utility/Raii/Disposables/PlotDragDropSourceDisposable.cs
new file mode 100644
index 000000000..cb0be3dea
--- /dev/null
+++ b/Dalamud/Interface/Utility/Raii/Disposables/PlotDragDropSourceDisposable.cs
@@ -0,0 +1,93 @@
+// ReSharper disable once CheckNamespace
+
+using Dalamud.Bindings.ImGui;
+using Dalamud.Bindings.ImPlot;
+
+namespace Dalamud.Interface.Utility.Raii;
+
+public static partial class ImRaii
+{
+ /// A wrapper around ImGui tables.
+ public ref struct PlotDragDropSourceDisposable : IDisposable
+ {
+ /// Whether creating the table succeeded. This needs to be checked before calling any of the member methods.
+ public readonly bool Success;
+
+ /// Whether the table is already ended.
+ public bool Alive { get; private set; }
+
+ /// Initializes a new instance of the struct.
+ /// The ID of the plot as text.
+ /// Additional flags to control the drag and drop behaviour.
+ internal PlotDragDropSourceDisposable(string labelId, ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
+ {
+ this.Success = ImPlot.BeginDragDropSourceItem(labelId, flags);
+ this.Alive = true;
+ }
+
+ ///
+ internal PlotDragDropSourceDisposable(ReadOnlySpan labelId, ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
+ {
+ this.Success = ImPlot.BeginDragDropSourceItem(labelId, flags);
+ this.Alive = true;
+ }
+
+ /// Initialize a new instance of the struct with SourceAxis.
+ /// The axis to drag.
+ /// Additional flags to control the drag and drop behaviour.
+ /// A disposable object that indicates whether the source is active. Use with using.
+ internal static PlotDragDropSourceDisposable AxisPlot(ImAxis axis, ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
+ => new(ImPlot.BeginDragDropSourceAxis(axis, flags));
+
+ /// Initialize a new instance of the struct with SourcePlot.
+ /// Additional flags to control the drag and drop behaviour.
+ /// A disposable object that indicates whether the source is active. Use with using.
+ internal static PlotDragDropSourceDisposable SourcePlot(ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
+ => new(ImPlot.BeginDragDropSourcePlot(flags));
+
+ private PlotDragDropSourceDisposable(bool success)
+ {
+ this.Success = success;
+ this.Alive = true;
+ }
+
+ /// Conversion to bool.
+ public static implicit operator bool(PlotDragDropSourceDisposable value)
+ => value.Success;
+
+ /// Conversion to bool.
+ public static bool operator true(PlotDragDropSourceDisposable i)
+ => i.Success;
+
+ /// Conversion to bool.
+ public static bool operator false(PlotDragDropSourceDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on NOT operators.
+ public static bool operator !(PlotDragDropSourceDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on AND operators.
+ public static bool operator &(PlotDragDropSourceDisposable i, bool value)
+ => i.Success && value;
+
+ /// Conversion to bool on OR operators.
+ public static bool operator |(PlotDragDropSourceDisposable i, bool value)
+ => i.Success || value;
+
+ /// End the Table on leaving scope.
+ public void Dispose()
+ {
+ if (!this.Alive)
+ return;
+
+ if (this.Success)
+ ImPlot.EndDragDropSource();
+ this.Alive = false;
+ }
+
+ /// End a Table without using an IDisposable.
+ public static void EndUnsafe()
+ => ImPlot.EndDragDropSource();
+ }
+}
diff --git a/Dalamud/Interface/Utility/Raii/Disposables/PlotDragDropTargetDisposable.cs b/Dalamud/Interface/Utility/Raii/Disposables/PlotDragDropTargetDisposable.cs
new file mode 100644
index 000000000..361c847f7
--- /dev/null
+++ b/Dalamud/Interface/Utility/Raii/Disposables/PlotDragDropTargetDisposable.cs
@@ -0,0 +1,83 @@
+// ReSharper disable once CheckNamespace
+
+using Dalamud.Bindings.ImGui;
+using Dalamud.Bindings.ImPlot;
+
+namespace Dalamud.Interface.Utility.Raii;
+
+public static partial class ImRaii
+{
+ /// A wrapper around ImGui tables.
+ public ref struct PlotDragDropTargetDisposable : IDisposable
+ {
+ /// Whether creating the table succeeded. This needs to be checked before calling any of the member methods.
+ public readonly bool Success;
+
+ /// Whether the table is already ended.
+ public bool Alive { get; private set; }
+
+ /// Initialize a new instance of the struct with SourceAxis.
+ /// The axis to drag.
+ /// Additional flags to control the drag and drop behaviour.
+ /// A disposable object that indicates whether the source is active. Use with using.
+ internal static PlotDragDropTargetDisposable AxisPlot(ImAxis axis)
+ => new(ImPlot.BeginDragDropTargetAxis(axis));
+
+ /// Initialize a new instance of the struct with SourcePlot.
+ /// Additional flags to control the drag and drop behaviour.
+ /// A disposable object that indicates whether the source is active. Use with using.
+ internal static PlotDragDropTargetDisposable LegendPlot()
+ => new(ImPlot.BeginDragDropTargetLegend());
+
+ /// Initialize a new instance of the struct with SourcePlot.
+ /// Additional flags to control the drag and drop behaviour.
+ /// A disposable object that indicates whether the source is active. Use with using.
+ internal static PlotDragDropTargetDisposable SourcePlot()
+ => new(ImPlot.BeginDragDropTargetPlot());
+
+ private PlotDragDropTargetDisposable(bool success)
+ {
+ this.Success = success;
+ this.Alive = true;
+ }
+
+ /// Conversion to bool.
+ public static implicit operator bool(PlotDragDropTargetDisposable value)
+ => value.Success;
+
+ /// Conversion to bool.
+ public static bool operator true(PlotDragDropTargetDisposable i)
+ => i.Success;
+
+ /// Conversion to bool.
+ public static bool operator false(PlotDragDropTargetDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on NOT operators.
+ public static bool operator !(PlotDragDropTargetDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on AND operators.
+ public static bool operator &(PlotDragDropTargetDisposable i, bool value)
+ => i.Success && value;
+
+ /// Conversion to bool on OR operators.
+ public static bool operator |(PlotDragDropTargetDisposable i, bool value)
+ => i.Success || value;
+
+ /// End the Table on leaving scope.
+ public void Dispose()
+ {
+ if (!this.Alive)
+ return;
+
+ if (this.Success)
+ ImPlot.EndDragDropTarget();
+ this.Alive = false;
+ }
+
+ /// End a Table without using an IDisposable.
+ public static void EndUnsafe()
+ => ImPlot.EndDragDropTarget();
+ }
+}
diff --git a/Dalamud/Interface/Utility/Raii/Disposables/PlotLegendDisposable.cs b/Dalamud/Interface/Utility/Raii/Disposables/PlotLegendDisposable.cs
new file mode 100644
index 000000000..65eba1c26
--- /dev/null
+++ b/Dalamud/Interface/Utility/Raii/Disposables/PlotLegendDisposable.cs
@@ -0,0 +1,74 @@
+// ReSharper disable once CheckNamespace
+
+using Dalamud.Bindings.ImGui;
+using Dalamud.Bindings.ImPlot;
+
+namespace Dalamud.Interface.Utility.Raii;
+
+public static partial class ImRaii
+{
+ /// A wrapper around ImGui tables.
+ public ref struct PlotLegendDisposable : IDisposable
+ {
+ /// Whether creating the table succeeded. This needs to be checked before calling any of the member methods.
+ public readonly bool Success;
+
+ /// Whether the table is already ended.
+ public bool Alive { get; private set; }
+
+ /// Initializes a new instance of the struct.
+ /// The ID of the plot as text.
+ /// The mouse button to use for opening the legend popup.
+ internal PlotLegendDisposable(string labelId, ImGuiMouseButton mouseButton = ImGuiMouseButton.Right)
+ {
+ this.Success = ImPlot.BeginLegendPopup(labelId, mouseButton);
+ this.Alive = true;
+ }
+
+ ///
+ internal PlotLegendDisposable(ReadOnlySpan labelId, ImGuiMouseButton mouseButton = ImGuiMouseButton.Right)
+ {
+ this.Success = ImPlot.BeginLegendPopup(labelId, mouseButton);
+ this.Alive = true;
+ }
+
+ /// Conversion to bool.
+ public static implicit operator bool(PlotLegendDisposable value)
+ => value.Success;
+
+ /// Conversion to bool.
+ public static bool operator true(PlotLegendDisposable i)
+ => i.Success;
+
+ /// Conversion to bool.
+ public static bool operator false(PlotLegendDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on NOT operators.
+ public static bool operator !(PlotLegendDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on AND operators.
+ public static bool operator &(PlotLegendDisposable i, bool value)
+ => i.Success && value;
+
+ /// Conversion to bool on OR operators.
+ public static bool operator |(PlotLegendDisposable i, bool value)
+ => i.Success || value;
+
+ /// End the Table on leaving scope.
+ public void Dispose()
+ {
+ if (!this.Alive)
+ return;
+
+ if (this.Success)
+ ImPlot.EndLegendPopup();
+ this.Alive = false;
+ }
+
+ /// End a Table without using an IDisposable.
+ public static void EndUnsafe()
+ => ImPlot.EndLegendPopup();
+ }
+}
diff --git a/Dalamud/Interface/Utility/Raii/Disposables/PlotStyleDisposable.cs b/Dalamud/Interface/Utility/Raii/Disposables/PlotStyleDisposable.cs
new file mode 100644
index 000000000..c2f9d6960
--- /dev/null
+++ b/Dalamud/Interface/Utility/Raii/Disposables/PlotStyleDisposable.cs
@@ -0,0 +1,188 @@
+// ReSharper disable once CheckNamespace
+
+using System.Numerics;
+using System.Collections.Generic;
+using System.Linq;
+
+using Dalamud.Bindings.ImGui;
+using Dalamud.Bindings.ImPlot;
+
+namespace Dalamud.Interface.Utility.Raii;
+
+public static partial class ImRaii
+{
+ /// A wrapper around style pushing.
+ public sealed class PlotStyleDisposable : IDisposable
+ {
+ internal static readonly List<(ImPlotStyleVar Type, Vector2 Value)> Stack = [];
+
+ /// The number of styles currently pushed using this disposable.
+ public int Count { get; private set; }
+
+ /// Push a style variable to the style stack.
+ /// The type of style variable to change.
+ /// The value to change it to.
+ /// If this is false, the style is not pushed.
+ /// A disposable object that can be used to push further style variables and pops those style variables after leaving scope. Use with using.
+ /// If you need to keep styles pushed longer than the current scope, use without using and use .
+ public PlotStyleDisposable Push(ImPlotStyleVar type, float value, bool condition)
+ => condition ? this.Push(type, value) : this;
+
+ ///
+ public PlotStyleDisposable Push(ImPlotStyleVar type, Vector2 value, bool condition)
+ => condition ? this.Push(type, value) : this;
+
+ public PlotStyleDisposable Push(ImPlotStyleVar type, float value)
+ {
+ CheckStyleIdx(type, typeof(float));
+ Stack.Add((type, GetStyle(type)));
+
+ ImPlot.PushStyleVar(type, value);
+ ++this.Count;
+ return this;
+ }
+
+ ///
+ public PlotStyleDisposable Push(ImPlotStyleVar type, Vector2 value)
+ {
+ CheckStyleIdx(type, typeof(Vector2));
+ Stack.Add((type, GetStyle(type)));
+
+ ImPlot.PushStyleVar(type, value);
+ ++this.Count;
+ return this;
+ }
+
+ /// Push styles that revert all current style changes made temporarily.
+ public static PlotStyleDisposable PlotDefaultStyle()
+ {
+ var ret = new PlotStyleDisposable();
+ var reverseStack = Stack.GroupBy(p => p.Type).Select(p => (p.Key, p.First().Value)).ToArray();
+ foreach (var (idx, val) in reverseStack)
+ {
+ if (idx == ImPlotStyleVar.Marker)
+ ret.Push(idx, (int)val.X);
+ else if (float.IsNaN(val.Y))
+ ret.Push(idx, val.X);
+ else
+ ret.Push(idx, val);
+ }
+
+ return ret;
+ }
+
+ /// Push the default value, i.e. the value as if nothing was ever pushed to this, of a style variable to the style stack.
+ /// The type of style variable to return to its default value.
+ /// A disposable object that can be used to push further style variables and pops those style variables after leaving scope. Use with using.
+ /// If you need to keep styles pushed longer than the current scope, use without using and use .
+ public PlotStyleDisposable PlotPushDefault(ImPlotStyleVar type)
+ {
+ foreach (var styleMod in Stack.Where(m => m.Type == type))
+ return Push(type, styleMod.Value);
+
+ return this;
+ }
+
+ /// Pop a number of style variables.
+ /// The number of style variables to pop. This is clamped to the number of style variables pushed by this object.
+ public PlotStyleDisposable Pop(int num = 1)
+ {
+ num = Math.Min(num, this.Count);
+ if (num > 0)
+ {
+ this.Count -= num;
+ ImPlot.PopStyleVar(num);
+ Stack.RemoveRange(Stack.Count - num, num);
+ }
+
+ return this;
+ }
+
+ /// Pop all pushed styles.
+ public void Dispose()
+ {
+ ImPlot.PopStyleVar(this.Count);
+ this.Count = 0;
+ }
+
+ /// Pop a number of style variables.
+ /// The number of style variables to pop. The number is not checked against the style stack.
+ /// Avoid using this function, and styles across scopes, as much as possible.
+ public static void PopUnsafe(int num = 1)
+ => ImPlot.PopStyleVar(num);
+
+ private static void CheckStyleIdx(ImPlotStyleVar idx, Type type)
+ {
+ var shouldThrow = idx switch
+ {
+ ImPlotStyleVar.LineWeight => type != typeof(float),
+ ImPlotStyleVar.Marker => type != typeof(int),
+ ImPlotStyleVar.MarkerSize => type != typeof(float),
+ ImPlotStyleVar.MarkerWeight => type != typeof(float),
+ ImPlotStyleVar.FillAlpha => type != typeof(float),
+ ImPlotStyleVar.ErrorBarSize => type != typeof(float),
+ ImPlotStyleVar.ErrorBarWeight => type != typeof(float),
+ // ImPlotStyleVar.DigitalBitHeight => type != typeof(float),
+ // ImPlotStyleVar.DigitalBitGap => type != typeof(float),
+ // ImPlotStyleVar.PlotBorderSize => type != typeof(float),
+ ImPlotStyleVar.MinorAlpha => type != typeof(float),
+ ImPlotStyleVar.MajorTickLen => type != typeof(Vector2),
+ ImPlotStyleVar.MinorTickLen => type != typeof(Vector2),
+ ImPlotStyleVar.MajorTickSize => type != typeof(Vector2),
+ ImPlotStyleVar.MinorTickSize => type != typeof(Vector2),
+ ImPlotStyleVar.MajorGridSize => type != typeof(Vector2),
+ ImPlotStyleVar.MinorGridSize => type != typeof(Vector2),
+ // ImPlotStyleVar.PlotPadding => type != typeof(Vector2),
+ ImPlotStyleVar.LabelPadding => type != typeof(Vector2),
+ ImPlotStyleVar.LegendPadding => type != typeof(Vector2),
+ ImPlotStyleVar.LegendInnerPadding => type != typeof(Vector2),
+ ImPlotStyleVar.LegendSpacing => type != typeof(Vector2),
+ ImPlotStyleVar.MousePosPadding => type != typeof(Vector2),
+ ImPlotStyleVar.AnnotationPadding => type != typeof(Vector2),
+ ImPlotStyleVar.FitPadding => type != typeof(Vector2),
+ // ImPlotStyleVar.PlotDefaultSize => type != typeof(Vector2),
+ // ImPlotStyleVar.PlotMinSize => type != typeof(Vector2),
+ _ => throw new ArgumentOutOfRangeException(nameof(idx), idx, null),
+ };
+
+ if (shouldThrow)
+ throw new ArgumentException($"Unable to push {type} to {idx}.");
+ }
+
+ public static Vector2 GetStyle(ImPlotStyleVar idx)
+ {
+ var style = ImPlot.GetStyle();
+ return idx switch
+ {
+ ImPlotStyleVar.LineWeight => new Vector2(style.LineWeight, float.NaN),
+ ImPlotStyleVar.Marker => new Vector2(style.Marker, float.NaN),
+ ImPlotStyleVar.MarkerSize => new Vector2(style.MarkerSize, float.NaN),
+ ImPlotStyleVar.MarkerWeight => new Vector2(style.MarkerWeight, float.NaN),
+ ImPlotStyleVar.FillAlpha => new Vector2(style.FillAlpha, float.NaN),
+ ImPlotStyleVar.ErrorBarSize => new Vector2(style.ErrorBarSize, float.NaN),
+ ImPlotStyleVar.ErrorBarWeight => new Vector2(style.ErrorBarWeight, float.NaN),
+ // ImPlotStyleVar.DigitalBitHeight => new Vector2(style.DigitalBitHeight, float.NaN),
+ // ImPlotStyleVar.DigitalBitGap => new Vector2(style.DigitalBitGap, float.NaN),
+ // ImPlotStyleVar.PlotBorderSize => new Vector2(style.PlotBorderSize, float.NaN),
+ ImPlotStyleVar.MinorAlpha => new Vector2(style.MinorAlpha, float.NaN),
+ ImPlotStyleVar.MajorTickLen => style.MajorTickLen,
+ ImPlotStyleVar.MinorTickLen => style.MinorTickLen,
+ ImPlotStyleVar.MajorTickSize => style.MajorTickSize,
+ ImPlotStyleVar.MinorTickSize => style.MinorTickSize,
+ ImPlotStyleVar.MajorGridSize => style.MajorGridSize,
+ ImPlotStyleVar.MinorGridSize => style.MinorGridSize,
+ // ImPlotStyleVar.PlotPadding => style.PlotPadding,
+ ImPlotStyleVar.LabelPadding => style.LabelPadding,
+ ImPlotStyleVar.LegendPadding => style.LegendPadding,
+ ImPlotStyleVar.LegendInnerPadding => style.LegendInnerPadding,
+ ImPlotStyleVar.LegendSpacing => style.LegendSpacing,
+ ImPlotStyleVar.MousePosPadding => style.MousePosPadding,
+ ImPlotStyleVar.AnnotationPadding => style.AnnotationPadding,
+ ImPlotStyleVar.FitPadding => style.FitPadding,
+ // ImPlotStyleVar.PlotDefaultSize => style.PlotDefaultSize,
+ // ImPlotStyleVar.PlotMinSize => style.PlotMinSize,
+ _ => throw new ArgumentOutOfRangeException(nameof(idx), idx, null),
+ };
+ }
+ }
+}
diff --git a/Dalamud/Interface/Utility/Raii/Disposables/PlotSubDisposable.cs b/Dalamud/Interface/Utility/Raii/Disposables/PlotSubDisposable.cs
new file mode 100644
index 000000000..5232e625e
--- /dev/null
+++ b/Dalamud/Interface/Utility/Raii/Disposables/PlotSubDisposable.cs
@@ -0,0 +1,99 @@
+// ReSharper disable once CheckNamespace
+
+using System.Numerics;
+
+using Dalamud.Bindings.ImPlot;
+
+namespace Dalamud.Interface.Utility.Raii;
+
+public static partial class ImRaii
+{
+ /// A wrapper around ImGui tables.
+ public ref struct PlotSubDisposable : IDisposable
+ {
+ /// Whether creating the table succeeded. This needs to be checked before calling any of the member methods.
+ public readonly bool Success;
+
+ /// Whether the table is already ended.
+ public bool Alive { get; private set; }
+
+ /// Initializes a new instance of the struct.
+ /// The ID of the plot as text.
+ /// The number of rows in the plot.
+ /// The number of columns in the plot.
+ /// The desired size of the plot.
+ /// Additional flags for the plot.
+ internal PlotSubDisposable(string titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags = ImPlotSubplotFlags.None)
+ {
+ this.Success = ImPlot.BeginSubplots(titleId, rows, cols, size, flags);
+ this.Alive = true;
+ }
+
+ ///
+ internal PlotSubDisposable(ReadOnlySpan titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags = ImPlotSubplotFlags.None)
+ {
+ this.Success = ImPlot.BeginSubplots(titleId, rows, cols, size, flags);
+ this.Alive = true;
+ }
+
+ /// Initializes a new instance of the struct.
+ /// The ID of the plot as text.
+ /// The number of rows in the plot.
+ /// The number of columns in the plot.
+ /// The desired size of the plot.
+ /// Additional flags for the plot.
+ /// The row ratios for the plot.
+ /// The column ratios for the plot.
+ internal PlotSubDisposable(string titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags, ref float rowRatios, ref float colRatios)
+ {
+ this.Success = ImPlot.BeginSubplots(titleId, rows, cols, size, flags, ref rowRatios, ref colRatios);
+ this.Alive = true;
+ }
+
+ ///
+ internal PlotSubDisposable(ReadOnlySpan titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags, ref float rowRatios, ref float colRatios)
+ {
+ this.Success = ImPlot.BeginSubplots(titleId, rows, cols, size, flags, ref rowRatios, ref colRatios);
+ this.Alive = true;
+ }
+
+ /// Conversion to bool.
+ public static implicit operator bool(PlotSubDisposable value)
+ => value.Success;
+
+ /// Conversion to bool.
+ public static bool operator true(PlotSubDisposable i)
+ => i.Success;
+
+ /// Conversion to bool.
+ public static bool operator false(PlotSubDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on NOT operators.
+ public static bool operator !(PlotSubDisposable i)
+ => !i.Success;
+
+ /// Conversion to bool on AND operators.
+ public static bool operator &(PlotSubDisposable i, bool value)
+ => i.Success && value;
+
+ /// Conversion to bool on OR operators.
+ public static bool operator |(PlotSubDisposable i, bool value)
+ => i.Success || value;
+
+ /// End the Table on leaving scope.
+ public void Dispose()
+ {
+ if (!this.Alive)
+ return;
+
+ if (this.Success)
+ ImPlot.EndSubplots();
+ this.Alive = false;
+ }
+
+ /// End a Table without using an IDisposable.
+ public static void EndUnsafe()
+ => ImPlot.EndSubplots();
+ }
+}
diff --git a/Dalamud/Interface/Utility/Raii/EndObjects.cs b/Dalamud/Interface/Utility/Raii/EndObjects.cs
index 1d3ce1dad..bce02ed51 100644
--- a/Dalamud/Interface/Utility/Raii/EndObjects.cs
+++ b/Dalamud/Interface/Utility/Raii/EndObjects.cs
@@ -207,109 +207,4 @@ public static partial class ImRaii
return new EndUnconditionally(Widget.EndFramedGroup, true);
}
*/
-
- // Exported interface for RAII.
- public interface IEndObject : IDisposable
- {
- public bool Success { get; }
-
- public static bool operator true(IEndObject i)
- => i.Success;
-
- public static bool operator false(IEndObject i)
- => !i.Success;
-
- public static bool operator !(IEndObject i)
- => !i.Success;
-
- public static bool operator &(IEndObject i, bool value)
- => i.Success && value;
-
- public static bool operator |(IEndObject i, bool value)
- => i.Success || value;
- }
-
- // Use end-function regardless of success.
- // Used by Child, Group and Tooltip.
- public ref struct EndUnconditionally : IEndObject
- {
- private Action EndAction { get; }
-
- public bool Success { get; }
-
- public bool Disposed { get; private set; }
-
- public EndUnconditionally(Action endAction, bool success)
- {
- this.EndAction = endAction;
- this.Success = success;
- this.Disposed = false;
- }
-
- public void Dispose()
- {
- if (this.Disposed)
- return;
-
- this.EndAction();
- this.Disposed = true;
- }
-
- public static bool operator true(EndUnconditionally i)
- => i.Success;
-
- public static bool operator false(EndUnconditionally i)
- => !i.Success;
-
- public static bool operator !(EndUnconditionally i)
- => !i.Success;
-
- public static bool operator &(EndUnconditionally i, bool value)
- => i.Success && value;
-
- public static bool operator |(EndUnconditionally i, bool value)
- => i.Success || value;
- }
-
- // Use end-function only on success.
- public ref struct EndConditionally : IEndObject
- {
- public EndConditionally(Action endAction, bool success)
- {
- this.EndAction = endAction;
- this.Success = success;
- this.Disposed = false;
- }
-
- public bool Success { get; }
-
- public bool Disposed { get; private set; }
-
- private Action EndAction { get; }
-
- public void Dispose()
- {
- if (this.Disposed)
- return;
-
- if (this.Success)
- this.EndAction();
- this.Disposed = true;
- }
-
- public static bool operator true(EndConditionally i)
- => i.Success;
-
- public static bool operator false(EndConditionally i)
- => !i.Success;
-
- public static bool operator !(EndConditionally i)
- => !i.Success;
-
- public static bool operator &(EndConditionally i, bool value)
- => i.Success && value;
-
- public static bool operator |(EndConditionally i, bool value)
- => i.Success || value;
- }
}
diff --git a/Dalamud/Interface/Utility/Raii/Plot.cs b/Dalamud/Interface/Utility/Raii/Plot.cs
index 807b2c38d..b5c7e6a03 100644
--- a/Dalamud/Interface/Utility/Raii/Plot.cs
+++ b/Dalamud/Interface/Utility/Raii/Plot.cs
@@ -1,5 +1,3 @@
-using System.Collections.Generic;
-using System.Linq;
using System.Numerics;
using Dalamud.Bindings.ImGui;
@@ -7,293 +5,92 @@ using Dalamud.Bindings.ImPlot;
namespace Dalamud.Interface.Utility.Raii;
-// TODO Convert to new syntax
-
// All previous files, but only for ImPlot specific functions.
public static partial class ImRaii
{
#region EndObjects
- public static EndConditionally Plot(string titleId, Vector2 size, ImPlotFlags flags)
- => new EndConditionally(ImPlot.EndPlot, ImPlot.BeginPlot(titleId, size, flags));
+ public static PlotDisposable Plot(string titleId, Vector2 size, ImPlotFlags flags)
+ => new(titleId, size, flags);
- public static EndConditionally Plot(ReadOnlySpan titleId, Vector2 size, ImPlotFlags flags)
- => new EndConditionally(ImPlot.EndPlot, ImPlot.BeginPlot(titleId, size, flags));
+ public static PlotDisposable Plot(ReadOnlySpan titleId, Vector2 size, ImPlotFlags flags)
+ => new(titleId, size, flags);
- public static EndConditionally AlignedPlots(string groupId, bool vertical = true)
- => new EndConditionally(ImPlot.EndAlignedPlots, ImPlot.BeginAlignedPlots(groupId, vertical));
+ public static PlotAlignedDisposable AlignedPlots(string groupId, bool vertical = true)
+ => new(groupId, vertical);
- public static EndConditionally AlignedPlots(ReadOnlySpan groupId, bool vertical = true)
- => new EndConditionally(ImPlot.EndAlignedPlots, ImPlot.BeginAlignedPlots(groupId, vertical));
+ public static PlotAlignedDisposable AlignedPlots(ReadOnlySpan groupId, bool vertical = true)
+ => new(groupId, vertical);
- public static EndConditionally LegendPopup(string labelId, ImGuiMouseButton mouseButton = ImGuiMouseButton.Right)
- => new EndConditionally(ImPlot.EndLegendPopup, ImPlot.BeginLegendPopup(labelId, mouseButton));
+ public static PlotLegendDisposable LegendPopup(string labelId, ImGuiMouseButton mouseButton = ImGuiMouseButton.Right)
+ => new(labelId, mouseButton);
- public static EndConditionally LegendPopup(ReadOnlySpan labelId, ImGuiMouseButton mouseButton = ImGuiMouseButton.Right)
- => new EndConditionally(ImPlot.EndLegendPopup, ImPlot.BeginLegendPopup(labelId, mouseButton));
+ public static PlotLegendDisposable LegendPopup(ReadOnlySpan labelId, ImGuiMouseButton mouseButton = ImGuiMouseButton.Right)
+ => new(labelId, mouseButton);
- public static EndConditionally Subplots(string titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags = ImPlotSubplotFlags.None)
- => new EndConditionally(ImPlot.EndSubplots, ImPlot.BeginSubplots(titleId, rows, cols, size, flags));
+ public static PlotSubDisposable Subplots(string titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags = ImPlotSubplotFlags.None)
+ => new(titleId, rows, cols, size, flags);
- public static EndConditionally Subplots(ReadOnlySpan titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags = ImPlotSubplotFlags.None)
- => new EndConditionally(ImPlot.EndSubplots, ImPlot.BeginSubplots(titleId, rows, cols, size, flags));
+ public static PlotSubDisposable Subplots(ReadOnlySpan titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags = ImPlotSubplotFlags.None)
+ => new(titleId, rows, cols, size, flags);
- public static EndConditionally Subplots(string titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags, ref float rowRatios, ref float colRatios)
- => new EndConditionally(ImPlot.EndSubplots, ImPlot.BeginSubplots(titleId, rows, cols, size, flags, ref rowRatios, ref colRatios));
+ public static PlotSubDisposable Subplots(string titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags, ref float rowRatios, ref float colRatios)
+ => new(titleId, rows, cols, size, flags, ref rowRatios, ref colRatios);
- public static EndConditionally Subplots(ReadOnlySpan titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags, ref float rowRatios, ref float colRatios)
- => new EndConditionally(ImPlot.EndSubplots, ImPlot.BeginSubplots(titleId, rows, cols, size, flags, ref rowRatios, ref colRatios));
+ public static PlotSubDisposable Subplots(ReadOnlySpan titleId, int rows, int cols, Vector2 size, ImPlotSubplotFlags flags, ref float rowRatios, ref float colRatios)
+ => new(titleId, rows, cols, size, flags, ref rowRatios, ref colRatios);
- public static EndConditionally DragDropSourceAxis(ImAxis axis, ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
- => new EndConditionally(ImPlot.EndDragDropSource, ImPlot.BeginDragDropSourceAxis(axis, flags));
+ public static PlotDragDropSourceDisposable DragDropSourceItem(string labelId, ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
+ => new(labelId, flags);
- public static EndConditionally DragDropSourceItem(string labelId, ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
- => new EndConditionally(ImPlot.EndDragDropSource, ImPlot.BeginDragDropSourceItem(labelId, flags));
+ public static PlotDragDropSourceDisposable DragDropSourceItem(ReadOnlySpan labelId, ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
+ => new(labelId, flags);
- public static EndConditionally DragDropSourceItem(ReadOnlySpan labelId, ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
- => new EndConditionally(ImPlot.EndDragDropSource, ImPlot.BeginDragDropSourceItem(labelId, flags));
+ public static PlotDragDropSourceDisposable DragDropSourceAxis(ImAxis axis, ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
+ => PlotDragDropSourceDisposable.AxisPlot(axis, flags);
- public static EndConditionally DragDropSourcePlot(ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
- => new EndConditionally(ImPlot.EndDragDropSource, ImPlot.BeginDragDropSourcePlot(flags));
+ public static PlotDragDropSourceDisposable DragDropSourcePlot(ImGuiDragDropFlags flags = ImGuiDragDropFlags.None)
+ => PlotDragDropSourceDisposable.SourcePlot(flags);
- public static EndConditionally DragDropTargetAxis(ImAxis axis)
- => new EndConditionally(ImPlot.EndDragDropTarget, ImPlot.BeginDragDropTargetAxis(axis));
+ public static PlotDragDropTargetDisposable DragDropTargetAxis(ImAxis axis)
+ => PlotDragDropTargetDisposable.AxisPlot(axis);
- public static EndConditionally DragDropTargetLegend()
- => new EndConditionally(ImPlot.EndDragDropTarget, ImPlot.BeginDragDropTargetLegend());
+ public static PlotDragDropTargetDisposable DragDropTargetLegend()
+ => PlotDragDropTargetDisposable.LegendPlot();
- public static EndConditionally DragDropTargetPlot()
- => new EndConditionally(ImPlot.EndDragDropTarget, ImPlot.BeginDragDropTargetPlot());
+ public static PlotDragDropTargetDisposable DragDropTargetPlot()
+ => PlotDragDropTargetDisposable.SourcePlot();
#endregion EndObjects
#region Style
- public static PlotStyle PushStyle(ImPlotStyleVar idx, int value, bool condition = true)
- => new PlotStyle().Push(idx, value, condition);
+ public static PlotStyleDisposable PushStyle(ImPlotStyleVar idx, int value, bool condition = true)
+ => new PlotStyleDisposable().Push(idx, value, condition);
- public static PlotStyle PushStyle(ImPlotStyleVar idx, float value, bool condition = true)
- => new PlotStyle().Push(idx, value, condition);
+ public static PlotStyleDisposable PushStyle(ImPlotStyleVar idx, float value, bool condition = true)
+ => new PlotStyleDisposable().Push(idx, value, condition);
- public static PlotStyle PushStyle(ImPlotStyleVar idx, Vector2 value, bool condition = true)
- => new PlotStyle().Push(idx, value, condition);
+ public static PlotStyleDisposable PushStyle(ImPlotStyleVar idx, Vector2 value, bool condition = true)
+ => new PlotStyleDisposable().Push(idx, value, condition);
// Push styles that revert all current plot style changes made temporarily.
- public static PlotStyle DefaultPlotStyle()
- {
- var ret = new PlotStyle();
- var reverseStack = PlotStyle.Stack.GroupBy(p => p.Item1).Select(p => (p.Key, p.First().Item2)).ToArray();
- foreach (var (idx, val) in reverseStack)
- {
- if (idx == ImPlotStyleVar.Marker)
- ret.Push(idx, (int)val.X);
- else if (float.IsNaN(val.Y))
- ret.Push(idx, val.X);
- else
- ret.Push(idx, val);
- }
-
- return ret;
- }
-
- public sealed class PlotStyle : IDisposable
- {
- internal static readonly List<(ImPlotStyleVar, Vector2)> Stack = [];
-
- private int count;
-
- private static void CheckStyleIdx(ImPlotStyleVar idx, Type type)
- {
- var shouldThrow = idx switch
- {
- ImPlotStyleVar.LineWeight => type != typeof(float),
- ImPlotStyleVar.Marker => type != typeof(int),
- ImPlotStyleVar.MarkerSize => type != typeof(float),
- ImPlotStyleVar.MarkerWeight => type != typeof(float),
- ImPlotStyleVar.FillAlpha => type != typeof(float),
- ImPlotStyleVar.ErrorBarSize => type != typeof(float),
- ImPlotStyleVar.ErrorBarWeight => type != typeof(float),
- // ImPlotStyleVar.DigitalBitHeight => type != typeof(float),
- // ImPlotStyleVar.DigitalBitGap => type != typeof(float),
- // ImPlotStyleVar.PlotBorderSize => type != typeof(float),
- ImPlotStyleVar.MinorAlpha => type != typeof(float),
- ImPlotStyleVar.MajorTickLen => type != typeof(Vector2),
- ImPlotStyleVar.MinorTickLen => type != typeof(Vector2),
- ImPlotStyleVar.MajorTickSize => type != typeof(Vector2),
- ImPlotStyleVar.MinorTickSize => type != typeof(Vector2),
- ImPlotStyleVar.MajorGridSize => type != typeof(Vector2),
- ImPlotStyleVar.MinorGridSize => type != typeof(Vector2),
- // ImPlotStyleVar.PlotPadding => type != typeof(Vector2),
- ImPlotStyleVar.LabelPadding => type != typeof(Vector2),
- ImPlotStyleVar.LegendPadding => type != typeof(Vector2),
- ImPlotStyleVar.LegendInnerPadding => type != typeof(Vector2),
- ImPlotStyleVar.LegendSpacing => type != typeof(Vector2),
- ImPlotStyleVar.MousePosPadding => type != typeof(Vector2),
- ImPlotStyleVar.AnnotationPadding => type != typeof(Vector2),
- ImPlotStyleVar.FitPadding => type != typeof(Vector2),
- // ImPlotStyleVar.PlotDefaultSize => type != typeof(Vector2),
- // ImPlotStyleVar.PlotMinSize => type != typeof(Vector2),
- _ => throw new ArgumentOutOfRangeException(nameof(idx), idx, null),
- };
-
- if (shouldThrow)
- throw new ArgumentException($"Unable to push {type} to {idx}.");
- }
-
- public static Vector2 GetStyle(ImPlotStyleVar idx)
- {
- var style = ImPlot.GetStyle();
- return idx switch
- {
- ImPlotStyleVar.LineWeight => new Vector2(style.LineWeight, float.NaN),
- ImPlotStyleVar.Marker => new Vector2(style.Marker, float.NaN),
- ImPlotStyleVar.MarkerSize => new Vector2(style.MarkerSize, float.NaN),
- ImPlotStyleVar.MarkerWeight => new Vector2(style.MarkerWeight, float.NaN),
- ImPlotStyleVar.FillAlpha => new Vector2(style.FillAlpha, float.NaN),
- ImPlotStyleVar.ErrorBarSize => new Vector2(style.ErrorBarSize, float.NaN),
- ImPlotStyleVar.ErrorBarWeight => new Vector2(style.ErrorBarWeight, float.NaN),
- // ImPlotStyleVar.DigitalBitHeight => new Vector2(style.DigitalBitHeight, float.NaN),
- // ImPlotStyleVar.DigitalBitGap => new Vector2(style.DigitalBitGap, float.NaN),
- // ImPlotStyleVar.PlotBorderSize => new Vector2(style.PlotBorderSize, float.NaN),
- ImPlotStyleVar.MinorAlpha => new Vector2(style.MinorAlpha, float.NaN),
- ImPlotStyleVar.MajorTickLen => style.MajorTickLen,
- ImPlotStyleVar.MinorTickLen => style.MinorTickLen,
- ImPlotStyleVar.MajorTickSize => style.MajorTickSize,
- ImPlotStyleVar.MinorTickSize => style.MinorTickSize,
- ImPlotStyleVar.MajorGridSize => style.MajorGridSize,
- ImPlotStyleVar.MinorGridSize => style.MinorGridSize,
- // ImPlotStyleVar.PlotPadding => style.PlotPadding,
- ImPlotStyleVar.LabelPadding => style.LabelPadding,
- ImPlotStyleVar.LegendPadding => style.LegendPadding,
- ImPlotStyleVar.LegendInnerPadding => style.LegendInnerPadding,
- ImPlotStyleVar.LegendSpacing => style.LegendSpacing,
- ImPlotStyleVar.MousePosPadding => style.MousePosPadding,
- ImPlotStyleVar.AnnotationPadding => style.AnnotationPadding,
- ImPlotStyleVar.FitPadding => style.FitPadding,
- // ImPlotStyleVar.PlotDefaultSize => style.PlotDefaultSize,
- // ImPlotStyleVar.PlotMinSize => style.PlotMinSize,
- _ => throw new ArgumentOutOfRangeException(nameof(idx), idx, null),
- };
- }
-
- public PlotStyle Push(ImPlotStyleVar idx, int value, bool condition = true)
- {
- if (!condition)
- return this;
-
- // Should be accurate for +/- 2^24 markers, which is fine, because the only valid range
- // for markers is [-1, 9].
- CheckStyleIdx(idx, typeof(int));
- Stack.Add((idx, GetStyle(idx)));
- ImPlot.PushStyleVar(idx, value);
- ++this.count;
-
- return this;
- }
-
- public PlotStyle Push(ImPlotStyleVar idx, float value, bool condition = true)
- {
- if (!condition)
- return this;
-
- CheckStyleIdx(idx, typeof(float));
- Stack.Add((idx, GetStyle(idx)));
- ImPlot.PushStyleVar(idx, value);
- ++this.count;
-
- return this;
- }
-
- public PlotStyle Push(ImPlotStyleVar idx, Vector2 value, bool condition = true)
- {
- if (!condition)
- return this;
-
- CheckStyleIdx(idx, typeof(Vector2));
- Stack.Add((idx, GetStyle(idx)));
- ImPlot.PushStyleVar(idx, value);
- ++this.count;
-
- return this;
- }
-
- public void Pop(int num = 1)
- {
- num = Math.Min(num, this.count);
- this.count -= num;
- ImPlot.PopStyleVar(num);
- Stack.RemoveRange(Stack.Count - num, num);
- }
-
- public void Dispose()
- => this.Pop(this.count);
- }
+ public static PlotStyleDisposable DefaultPlotStyle()
+ => PlotStyleDisposable.PlotDefaultStyle();
#endregion Style
#region Color
- public static PlotColor PushColor(ImPlotCol idx, uint color, bool condition = true)
- => new PlotColor().Push(idx, color, condition);
+ public static PlotColorDisposable PushColor(ImPlotCol idx, uint color, bool condition = true)
+ => new PlotColorDisposable().Push(idx, color, condition);
- public static PlotColor PushColor(ImPlotCol idx, Vector4 color, bool condition = true)
- => new PlotColor().Push(idx, color, condition);
+ public static PlotColorDisposable PushColor(ImPlotCol idx, Vector4 color, bool condition = true)
+ => new PlotColorDisposable().Push(idx, color, condition);
// Push colors that revert all current color changes made temporarily.
- public static PlotColor DefaultPlotColors()
- {
- var ret = new PlotColor();
- var reverseStack = PlotColor.Stack.GroupBy(p => p.Item1).Select(p => (p.Key, p.First().Item2)).ToArray();
- foreach (var (idx, val) in reverseStack)
- ret.Push(idx, val);
- return ret;
- }
-
- public sealed class PlotColor : IDisposable
- {
- internal static readonly List<(ImPlotCol, uint)> Stack = [];
- private int count;
-
- // Reimplementation of https://github.com/ocornut/imgui/blob/868facff9ded2d61425c67deeba354eb24275bd1/imgui.cpp#L3035
- // for ImPlot
- private static uint GetColorU32(ImPlotCol idx)
- => ImGui.GetColorU32(ImPlot.GetStyle().Colors[(int)idx]);
-
- public PlotColor Push(ImPlotCol idx, uint color, bool condition = true)
- {
- if (condition)
- {
- Stack.Add((idx, GetColorU32(idx)));
- ImPlot.PushStyleColor(idx, color);
- ++this.count;
- }
-
- return this;
- }
-
- public PlotColor Push(ImPlotCol idx, Vector4 color, bool condition = true)
- {
- if (condition)
- {
- Stack.Add((idx, GetColorU32(idx)));
- ImPlot.PushStyleColor(idx, color);
- ++this.count;
- }
-
- return this;
- }
-
- public void Pop(int num = 1)
- {
- num = Math.Min(num, this.count);
- this.count -= num;
- ImPlot.PopStyleColor(num);
- Stack.RemoveRange(Stack.Count - num, num);
- }
-
- public void Dispose()
- => this.Pop(this.count);
- }
+ public static PlotColorDisposable DefaultPlotColors()
+ => PlotColorDisposable.PlotDefaultColors();
#endregion Color
}