From dd5cbdfd5daacadde9fbbf2b42a72af2b7c9dbcb Mon Sep 17 00:00:00 2001 From: Soreepeong Date: Sun, 21 Jan 2024 00:15:12 +0900 Subject: [PATCH 1/3] IFontAtlas API9 compat: support reading GameFontHandle.ImFont during UiBuilder.After/BuildFonts --- .../IFontAtlasBuildToolkit.cs | 16 +++++++ .../Internals/DelegateFontHandle.cs | 9 ++++ .../FontAtlasFactory.BuildToolkit.cs | 30 ++++++++++++- .../Internals/GamePrebakedFontHandle.cs | 32 ++++++++++++-- .../Internals/IFontHandleSubstance.cs | 17 ++++++- Dalamud/Interface/UiBuilder.cs | 44 ++++++++++++------- Dalamud/Logging/PluginLog.cs | 3 ++ Dalamud/Utility/Api10ToDoAttribute.cs | 19 ++++++++ 8 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 Dalamud/Utility/Api10ToDoAttribute.cs diff --git a/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkit.cs b/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkit.cs index 4b016bbb2..a997c48c1 100644 --- a/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkit.cs +++ b/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkit.cs @@ -1,6 +1,8 @@ using System.Runtime.InteropServices; +using Dalamud.Interface.ManagedFontAtlas.Internals; using Dalamud.Interface.Utility; +using Dalamud.Utility; using ImGuiNET; @@ -11,6 +13,20 @@ namespace Dalamud.Interface.ManagedFontAtlas; /// public interface IFontAtlasBuildToolkit { + /// + /// Functionalities for compatibility behavior.
+ ///
+ [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] + internal interface IApi9Compat : IFontAtlasBuildToolkit + { + /// + /// Invokes , temporarily applying s.
+ ///
+ /// The action to invoke. + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] + public void FromUiBuilderObsoleteEventHandlers(Action action); + } + /// /// Gets or sets the font relevant to the call. /// diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs index f0ed09155..99067a9de 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs @@ -4,6 +4,7 @@ using System.Linq; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Logging.Internal; +using Dalamud.Utility; using ImGuiNET; @@ -144,6 +145,14 @@ internal class DelegateFontHandle : IFontHandle.IInternal /// public IFontHandleManager Manager { get; } + /// + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] + public IFontAtlasBuildToolkitPreBuild? PreBuildToolkitForApi9Compat { get; set; } + + /// + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] + public bool CreateFontOnAccess { get; set; } + /// public void Dispose() { diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs index e73ea7548..fde115c9e 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs @@ -30,7 +30,7 @@ internal sealed partial class FontAtlasFactory /// Implementations for and /// . /// - private class BuildToolkit : IFontAtlasBuildToolkitPreBuild, IFontAtlasBuildToolkitPostBuild, IDisposable + private class BuildToolkit : IFontAtlasBuildToolkit.IApi9Compat, IFontAtlasBuildToolkitPreBuild, IFontAtlasBuildToolkitPostBuild, IDisposable { private static readonly ushort FontAwesomeIconMin = (ushort)Enum.GetValues().Where(x => x > 0).Min(); @@ -107,6 +107,34 @@ internal sealed partial class FontAtlasFactory /// public void DisposeWithAtlas(Action action) => this.data.Garbage.Add(action); + /// + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] + public void FromUiBuilderObsoleteEventHandlers(Action action) + { + var previousSubstances = new IFontHandleSubstance[this.data.Substances.Count]; + for (var i = 0; i < previousSubstances.Length; i++) + { + previousSubstances[i] = this.data.Substances[i].Manager.Substance; + this.data.Substances[i].Manager.Substance = this.data.Substances[i]; + this.data.Substances[i].CreateFontOnAccess = true; + this.data.Substances[i].PreBuildToolkitForApi9Compat = this; + } + + try + { + action(); + } + finally + { + for (var i = 0; i < previousSubstances.Length; i++) + { + this.data.Substances[i].Manager.Substance = previousSubstances[i]; + this.data.Substances[i].CreateFontOnAccess = false; + this.data.Substances[i].PreBuildToolkitForApi9Compat = null; + } + } + } + /// public ImFontPtr IgnoreGlobalScale(ImFontPtr fontPtr) { diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs index 99c817a91..2686259bc 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs @@ -230,6 +230,14 @@ internal class GamePrebakedFontHandle : IFontHandle.IInternal /// public IFontHandleManager Manager => this.handleManager; + /// + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] + public IFontAtlasBuildToolkitPreBuild? PreBuildToolkitForApi9Compat { get; set; } + + /// + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] + public bool CreateFontOnAccess { get; set; } + /// public void Dispose() { @@ -285,11 +293,27 @@ internal class GamePrebakedFontHandle : IFontHandle.IInternal } } + // Use this on API 10. + // /// + // public ImFontPtr GetFontPtr(IFontHandle handle) => + // handle is GamePrebakedFontHandle ggfh + // ? this.fonts.GetValueOrDefault(ggfh.FontStyle)?.FullRangeFont ?? default + // : default; + /// - public ImFontPtr GetFontPtr(IFontHandle handle) => - handle is GamePrebakedFontHandle ggfh - ? this.fonts.GetValueOrDefault(ggfh.FontStyle)?.FullRangeFont ?? default - : default; + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] + public ImFontPtr GetFontPtr(IFontHandle handle) + { + if (handle is not GamePrebakedFontHandle ggfh) + return default; + if (this.fonts.GetValueOrDefault(ggfh.FontStyle)?.FullRangeFont is { } font) + return font; + if (!this.CreateFontOnAccess) + return default; + if (this.PreBuildToolkitForApi9Compat is not { } tk) + return default; + return this.GetOrCreateFont(ggfh.FontStyle, tk); + } /// public Exception? GetBuildException(IFontHandle handle) => diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/IFontHandleSubstance.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/IFontHandleSubstance.cs index f6c5c6591..c800c30ac 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/IFontHandleSubstance.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/IFontHandleSubstance.cs @@ -1,4 +1,6 @@ -using ImGuiNET; +using Dalamud.Utility; + +using ImGuiNET; namespace Dalamud.Interface.ManagedFontAtlas.Internals; @@ -12,6 +14,19 @@ internal interface IFontHandleSubstance : IDisposable /// IFontHandleManager Manager { get; } + /// + /// Gets or sets the relevant for this. + /// + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] + IFontAtlasBuildToolkitPreBuild? PreBuildToolkitForApi9Compat { get; set; } + + /// + /// Gets or sets a value indicating whether to create a new instance of on first + /// access, for compatibility with API 9. + /// + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] + bool CreateFontOnAccess { get; set; } + /// /// Gets the font. /// diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index a477ec09e..87e3b9032 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -104,6 +104,7 @@ public sealed class UiBuilder : IDisposable /// pointers inside this handler. /// [Obsolete($"Use {nameof(this.FontAtlas)} instead.", false)] + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] public event Action? BuildFonts; /// @@ -113,6 +114,7 @@ public sealed class UiBuilder : IDisposable /// pointers inside this handler. /// [Obsolete($"Use {nameof(this.FontAtlas)} instead.", false)] + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] public event Action? AfterBuildFonts; /// @@ -423,6 +425,7 @@ public sealed class UiBuilder : IDisposable /// Font to get. /// Handle to the game font which may or may not be available for use yet. [Obsolete($"Use {nameof(this.FontAtlas)}.{nameof(IFontAtlas.NewGameFontHandle)} instead.", false)] + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] public GameFontHandle GetGameFontHandle(GameFontStyle style) => new( (IFontHandle.IInternal)this.FontAtlas.NewGameFontHandle(style), Service.Get()); @@ -620,28 +623,37 @@ public sealed class UiBuilder : IDisposable this.hitchDetector.Stop(); } + [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] private unsafe void PrivateAtlasOnBuildStepChange(IFontAtlasBuildToolkit e) { if (e.IsAsyncBuildOperation) return; - e.OnPreBuild( - _ => - { - var prev = ImGui.GetIO().NativePtr->Fonts; - ImGui.GetIO().NativePtr->Fonts = e.NewImAtlas.NativePtr; - this.BuildFonts?.InvokeSafely(); - ImGui.GetIO().NativePtr->Fonts = prev; - }); + if (this.BuildFonts is not null) + { + e.OnPreBuild( + _ => + { + var prev = ImGui.GetIO().NativePtr->Fonts; + ImGui.GetIO().NativePtr->Fonts = e.NewImAtlas.NativePtr; + ((IFontAtlasBuildToolkit.IApi9Compat)e) + .FromUiBuilderObsoleteEventHandlers(() => this.BuildFonts?.InvokeSafely()); + ImGui.GetIO().NativePtr->Fonts = prev; + }); + } - e.OnPostBuild( - _ => - { - var prev = ImGui.GetIO().NativePtr->Fonts; - ImGui.GetIO().NativePtr->Fonts = e.NewImAtlas.NativePtr; - this.AfterBuildFonts?.InvokeSafely(); - ImGui.GetIO().NativePtr->Fonts = prev; - }); + if (this.AfterBuildFonts is not null) + { + e.OnPostBuild( + _ => + { + var prev = ImGui.GetIO().NativePtr->Fonts; + ImGui.GetIO().NativePtr->Fonts = e.NewImAtlas.NativePtr; + ((IFontAtlasBuildToolkit.IApi9Compat)e) + .FromUiBuilderObsoleteEventHandlers(() => this.AfterBuildFonts?.InvokeSafely()); + ImGui.GetIO().NativePtr->Fonts = prev; + }); + } } private void OnResizeBuffers() diff --git a/Dalamud/Logging/PluginLog.cs b/Dalamud/Logging/PluginLog.cs index decf10b4c..e3744c617 100644 --- a/Dalamud/Logging/PluginLog.cs +++ b/Dalamud/Logging/PluginLog.cs @@ -1,6 +1,8 @@ using System.Reflection; using Dalamud.Plugin.Services; +using Dalamud.Utility; + using Serilog; using Serilog.Events; @@ -14,6 +16,7 @@ namespace Dalamud.Logging; /// move over as soon as reasonably possible for performance reasons. /// [Obsolete("Static PluginLog will be removed in API 10. Developers should use IPluginLog.")] +[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] public static class PluginLog { #region "Log" prefixed Serilog style methods diff --git a/Dalamud/Utility/Api10ToDoAttribute.cs b/Dalamud/Utility/Api10ToDoAttribute.cs new file mode 100644 index 000000000..f397f8f0c --- /dev/null +++ b/Dalamud/Utility/Api10ToDoAttribute.cs @@ -0,0 +1,19 @@ +namespace Dalamud.Utility; + +/// +/// Utility class for marking something to be changed for API 10, for ease of lookup. +/// +[AttributeUsage(AttributeTargets.All, Inherited = false)] +internal sealed class Api10ToDoAttribute : Attribute +{ + /// + /// Marks that this exists purely for making API 9 plugins work. + /// + public const string DeleteCompatBehavior = "Delete. This is for making API 9 plugins work."; + + /// + /// Initializes a new instance of the class. + /// + /// The explanation. + public Api10ToDoAttribute(string what) => _ = what; +} From 8afe277c0218b8f54c5250550009edc91b9a0040 Mon Sep 17 00:00:00 2001 From: Soreepeong Date: Sun, 21 Jan 2024 00:45:46 +0900 Subject: [PATCH 2/3] Make IFontHandle.Pop return a concrete struct --- Dalamud/Interface/GameFonts/GameFontHandle.cs | 10 ++++- .../Interface/ManagedFontAtlas/IFontHandle.cs | 40 ++++++++++++++++++- .../Internals/DelegateFontHandle.cs | 3 +- .../Internals/GamePrebakedFontHandle.cs | 3 +- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/Dalamud/Interface/GameFonts/GameFontHandle.cs b/Dalamud/Interface/GameFonts/GameFontHandle.cs index 77461aa0a..d11414517 100644 --- a/Dalamud/Interface/GameFonts/GameFontHandle.cs +++ b/Dalamud/Interface/GameFonts/GameFontHandle.cs @@ -2,6 +2,7 @@ using System.Numerics; using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Interface.ManagedFontAtlas.Internals; +using Dalamud.Utility; using ImGuiNET; @@ -10,6 +11,7 @@ namespace Dalamud.Interface.GameFonts; /// /// ABI-compatible wrapper for . /// +[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] public sealed class GameFontHandle : IFontHandle { private readonly IFontHandle.IInternal fontHandle; @@ -53,8 +55,14 @@ public sealed class GameFontHandle : IFontHandle /// public void Dispose() => this.fontHandle.Dispose(); - /// + /// + /// Pushes the font. + /// + /// An that can be used to pop the font on dispose. public IDisposable Push() => this.fontHandle.Push(); + + /// + IFontHandle.FontPopper IFontHandle.Push() => this.fontHandle.Push(); /// /// Creates a new .
diff --git a/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs index 854594663..47f384c11 100644 --- a/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs @@ -1,4 +1,6 @@ -using ImGuiNET; +using Dalamud.Utility; + +using ImGuiNET; namespace Dalamud.Interface.ManagedFontAtlas; @@ -38,5 +40,39 @@ public interface IFontHandle : IDisposable /// You may not access the font once you dispose this object. ///
/// A disposable object that will call (1) on dispose. - IDisposable Push(); + /// If called outside of the main thread. + FontPopper Push(); + + /// + /// The wrapper for popping fonts. + /// + public struct FontPopper : IDisposable + { + private int count; + + /// + /// Initializes a new instance of the struct. + /// + /// The font to push. + /// Whether to push. + internal FontPopper(ImFontPtr fontPtr, bool push) + { + if (!push) + return; + + ThreadSafety.AssertMainThread(); + + this.count = 1; + ImGui.PushFont(fontPtr); + } + + /// + public void Dispose() + { + ThreadSafety.AssertMainThread(); + + while (this.count-- > 0) + ImGui.PopFont(); + } + } } diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs index 99067a9de..bde349736 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs @@ -2,7 +2,6 @@ using System.Linq; using Dalamud.Interface.Utility; -using Dalamud.Interface.Utility.Raii; using Dalamud.Logging.Internal; using Dalamud.Utility; @@ -53,7 +52,7 @@ internal class DelegateFontHandle : IFontHandle.IInternal } /// - public IDisposable Push() => ImRaii.PushFont(this.ImFont, this.Available); + public IFontHandle.FontPopper Push() => new(this.ImFont, this.Available); /// /// Manager for s. diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs index 2686259bc..feda47a8a 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs @@ -9,7 +9,6 @@ using Dalamud.Game.Text; using Dalamud.Interface.GameFonts; using Dalamud.Interface.Internal; using Dalamud.Interface.Utility; -using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; using ImGuiNET; @@ -117,7 +116,7 @@ internal class GamePrebakedFontHandle : IFontHandle.IInternal } /// - public IDisposable Push() => ImRaii.PushFont(this.ImFont, this.Available); + public IFontHandle.FontPopper Push() => new(this.ImFont, this.Available); /// /// Manager for s. From 7c1ca4001d0b4787638cd065deb25ccffe2f7e27 Mon Sep 17 00:00:00 2001 From: Soreepeong Date: Sun, 21 Jan 2024 00:47:09 +0900 Subject: [PATCH 3/3] Docs --- Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs index 47f384c11..460fd53a0 100644 --- a/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs @@ -41,6 +41,10 @@ public interface IFontHandle : IDisposable /// /// A disposable object that will call (1) on dispose. /// If called outside of the main thread. + /// + /// Only intended for use with using keywords, such as using (handle.Push()).
+ /// Should you store or transfer the return value to somewhere else, use as the type. + ///
FontPopper Push(); ///