diff --git a/HellionChat/PluginHostFactory.cs b/HellionChat/PluginHostFactory.cs index a3502b1..fd57739 100644 --- a/HellionChat/PluginHostFactory.cs +++ b/HellionChat/PluginHostFactory.cs @@ -93,6 +93,9 @@ internal static class PluginHostFactory )); services.AddSingleton(_ => new Ui.StyleEngine.TokenResolver()); + services.AddSingleton(sp => new Ui.StyleEngine.PushStack( + sp.GetRequiredService() + )); services.AddSingleton(sp => new GameFunctions.GameFunctions( sp.GetRequiredService(), diff --git a/HellionChat/Ui/StyleEngine/PushStack.cs b/HellionChat/Ui/StyleEngine/PushStack.cs new file mode 100644 index 0000000..9109505 --- /dev/null +++ b/HellionChat/Ui/StyleEngine/PushStack.cs @@ -0,0 +1,71 @@ +using System.Numerics; +using Dalamud.Bindings.ImGui; +using Dalamud.Interface.ManagedFontAtlas; +using Dalamud.Interface.Utility.Raii; +using HellionChat.Themes; +using HellionChat.Util; + +namespace HellionChat.Ui.StyleEngine; + +// Token-aware bridge over Dalamud's ImRaii. Couples semantic Token lookups +// to the already-tracked push/pop machinery so callers express style intent +// instead of raw ImGuiCol slots. Counter-symmetry and exception-safety come +// from ImRaii, not from this class. +internal sealed class PushStack +{ + private readonly TokenResolver _resolver; + + public PushStack(TokenResolver resolver) + { + _resolver = resolver; + } + + public PushScope Begin() => new(_resolver); + + // Disposable scope handed to the using-block. Each Push.* call is a thin + // delegate to ImRaii / IFontHandle.Push with the token resolve in front; + // disposes in reverse order via the captured IDisposables. + internal sealed class PushScope : IDisposable + { + private readonly List _items = new(32); + private readonly TokenResolver _resolver; + + internal PushScope(TokenResolver resolver) + { + _resolver = resolver; + } + + public PushScope Color(Token token, Theme theme) + { + var slot = TokenMap.ToImGuiCol(token); + var rgba = _resolver.Resolve(token, theme.Colors); + _items.Add(ImRaii.PushColor(slot, ColourUtil.RgbaToAbgr(rgba))); + return this; + } + + public PushScope Style(ImGuiStyleVar var, float value) + { + _items.Add(ImRaii.PushStyle(var, value)); + return this; + } + + public PushScope Style(ImGuiStyleVar var, Vector2 value) + { + _items.Add(ImRaii.PushStyle(var, value)); + return this; + } + + public PushScope Font(IFontHandle font) + { + _items.Add(font.Push()); + return this; + } + + public void Dispose() + { + for (var i = _items.Count - 1; i >= 0; i--) + _items[i].Dispose(); + _items.Clear(); + } + } +}