Update docs and exposed API

This commit is contained in:
Soreepeong 2023-12-10 14:39:22 +09:00
parent 7eb4bf8ab4
commit e7c7cdaa29
6 changed files with 128 additions and 95 deletions

View file

@ -699,35 +699,38 @@ internal class InterfaceManager : IDisposable, IServiceType
{
this.dalamudAtlas = fontAtlasFactory
.CreateFontAtlas(nameof(InterfaceManager), FontAtlasAutoRebuildMode.Disable);
this.defaultFontHandle = (IFontHandle.IInternal)this.dalamudAtlas.NewDelegateFontHandle(
e => e.OnPreBuild(tk => tk.AddDalamudDefaultFont(DefaultFontSizePx)));
this.iconFontHandle = (IFontHandle.IInternal)this.dalamudAtlas.NewDelegateFontHandle(
e => e.OnPreBuild(
tk => tk.AddFontAwesomeIconFont(
new()
{
SizePx = DefaultFontSizePx,
GlyphMinAdvanceX = DefaultFontSizePx,
GlyphMaxAdvanceX = DefaultFontSizePx,
})));
this.monoFontHandle = (IFontHandle.IInternal)this.dalamudAtlas.NewDelegateFontHandle(
e => e.OnPreBuild(
tk => tk.AddDalamudAssetFont(
DalamudAsset.InconsolataRegular,
new() { SizePx = DefaultFontSizePx })));
this.dalamudAtlas.BuildStepChange += e => e.OnPostPromotion(
tk =>
{
// Note: the first call of this function is done outside the main thread; this is expected.
// Do not use DefaultFont, IconFont, and MonoFont.
// Use font handles directly.
using (this.dalamudAtlas.SuppressAutoRebuild())
{
this.defaultFontHandle = (IFontHandle.IInternal)this.dalamudAtlas.NewDelegateFontHandle(
e => e.OnPreBuild(tk => tk.AddDalamudDefaultFont(DefaultFontSizePx)));
this.iconFontHandle = (IFontHandle.IInternal)this.dalamudAtlas.NewDelegateFontHandle(
e => e.OnPreBuild(
tk => tk.AddFontAwesomeIconFont(
new()
{
SizePx = DefaultFontSizePx,
GlyphMinAdvanceX = DefaultFontSizePx,
GlyphMaxAdvanceX = DefaultFontSizePx,
})));
this.monoFontHandle = (IFontHandle.IInternal)this.dalamudAtlas.NewDelegateFontHandle(
e => e.OnPreBuild(
tk => tk.AddDalamudAssetFont(
DalamudAsset.InconsolataRegular,
new() { SizePx = DefaultFontSizePx })));
this.dalamudAtlas.BuildStepChange += e => e.OnPostPromotion(
tk =>
{
// Note: the first call of this function is done outside the main thread; this is expected.
// Do not use DefaultFont, IconFont, and MonoFont.
// Use font handles directly.
// Fill missing glyphs in MonoFont from DefaultFont
tk.CopyGlyphsAcrossFonts(this.defaultFontHandle.ImFont, this.monoFontHandle.ImFont, true);
// Fill missing glyphs in MonoFont from DefaultFont
tk.CopyGlyphsAcrossFonts(this.defaultFontHandle.ImFont, this.monoFontHandle.ImFont, true);
// Broadcast to auto-rebuilding instances
this.AfterBuildFonts?.Invoke();
});
// Broadcast to auto-rebuilding instances
this.AfterBuildFonts?.Invoke();
});
}
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
_ = this.dalamudAtlas.BuildFontsAsync(false);

View file

@ -1,7 +1,6 @@
using System.Threading.Tasks;
using Dalamud.Interface.GameFonts;
using Dalamud.Interface.ManagedFontAtlas.Internals;
using ImGuiNET;
@ -54,25 +53,73 @@ public interface IFontAtlas : IDisposable
/// </summary>
bool IsGlobalScaled { get; }
/// <inheritdoc cref="GamePrebakedFontHandle.HandleManager.NewFontHandle"/>
/// <summary>
/// Suppresses automatically rebuilding fonts for the scope.
/// </summary>
/// <returns>An instance of <see cref="IDisposable"/> that will release the suppression.</returns>
/// <remarks>
/// Use when you will be creating multiple new handles, and want rebuild to trigger only when you're done doing so.
/// This function will effectively do nothing, if <see cref="AutoRebuildMode"/> is set to
/// <see cref="FontAtlasAutoRebuildMode.Disable"/>.
/// </remarks>
/// <example>
/// <code>
/// using (atlas.SuppressBuild()) {
/// this.font1 = atlas.NewGameFontHandle(...);
/// this.font2 = atlas.NewDelegateFontHandle(...);
/// }
/// </code>
/// </example>
public IDisposable SuppressAutoRebuild();
/// <summary>
/// Creates a new <see cref="IFontHandle"/> from game's built-in fonts.
/// </summary>
/// <param name="style">Font to use.</param>
/// <returns>Handle to a font that may or may not be ready yet.</returns>
public IFontHandle NewGameFontHandle(GameFontStyle style);
/// <inheritdoc cref="DelegateFontHandle.HandleManager.NewFontHandle"/>
/// <summary>
/// Creates a new IFontHandle using your own callbacks.
/// </summary>
/// <param name="buildStepDelegate">Callback for <see cref="IFontAtlas.BuildStepChange"/>.</param>
/// <returns>Handle to a font that may or may not be ready yet.</returns>
/// <example>
/// <b>On initialization</b>:
/// <code>
/// this.fontHandle = atlas.NewDelegateFontHandle(e => e.OnPreBuild(tk => {
/// var config = new SafeFontConfig { SizePx = 16 };
/// config.MergeFont = tk.AddFontFromFile(@"C:\Windows\Fonts\comic.ttf", config);
/// tk.AddGameSymbol(config);
/// tk.AddExtraGlyphsForDalamudLanguage(config);
/// // optionally do the following if you have to add more than one font here,
/// // to specify which font added during this delegate is the final font to use.
/// tk.Font = config.MergeFont;
/// }));
/// // or
/// this.fontHandle = atlas.NewDelegateFontHandle(e => e.OnPreBuild(tk => tk.AddDalamudDefaultFont(36)));
/// </code>
/// <br />
/// <b>On use</b>:
/// <code>
/// using (this.fontHandle.Push())
/// ImGui.TextUnformatted("Example");
/// </code>
/// </example>
public IFontHandle NewDelegateFontHandle(FontAtlasBuildStepDelegate buildStepDelegate);
/// <inheritdoc cref="IFontHandleManager.FreeFontHandle"/>
public void FreeFontHandle(IFontHandle handle);
/// <summary>
/// Queues rebuilding fonts, on the main thread.<br />
/// Note that <see cref="BuildTask"/> would not necessarily get changed from calling this function.
/// </summary>
/// <exception cref="InvalidOperationException">If <see cref="AutoRebuildMode"/> is <see cref="FontAtlasAutoRebuildMode.Async"/>.</exception>
void BuildFontsOnNextFrame();
/// <summary>
/// Rebuilds fonts immediately, on the current thread.<br />
/// Even the callback for <see cref="FontAtlasBuildStep.PostPromotion"/> will be called on the same thread.
/// </summary>
/// <exception cref="InvalidOperationException">If <see cref="AutoRebuildMode"/> is <see cref="FontAtlasAutoRebuildMode.Async"/>.</exception>
void BuildFontsImmediately();
/// <summary>
@ -80,5 +127,6 @@ public interface IFontAtlas : IDisposable
/// </summary>
/// <param name="callPostPromotionOnMainThread">Call <see cref="FontAtlasBuildStep.PostPromotion"/> on the main thread.</param>
/// <returns>The task.</returns>
/// <exception cref="InvalidOperationException">If <see cref="AutoRebuildMode"/> is <see cref="FontAtlasAutoRebuildMode.OnNewFrame"/>.</exception>
Task BuildFontsAsync(bool callPostPromotionOnMainThread = true);
}

View file

@ -88,11 +88,7 @@ internal class DelegateFontHandle : IFontHandle.IInternal
}
}
/// <summary>
/// Creates a new IFontHandle using your own callbacks.
/// </summary>
/// <param name="buildStepDelegate">Callback for <see cref="IFontAtlas.BuildStepChange"/>.</param>
/// <returns>Handle to a font that may or may not be ready yet.</returns>
/// <inheritdoc cref="IFontAtlas.NewDelegateFontHandle"/>
public IFontHandle NewFontHandle(FontAtlasBuildStepDelegate buildStepDelegate)
{
var key = new DelegateFontHandle(this, buildStepDelegate);

View file

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;
@ -203,6 +204,9 @@ internal sealed partial class FontAtlasFactory
private Task<FontAtlasBuiltData> buildTask = EmptyTask;
private FontAtlasBuiltData builtData;
private int buildSuppressionCounter;
private bool buildSuppressionSuppressed;
private int buildIndex;
private bool buildQueued;
private bool disposed = false;
@ -356,6 +360,19 @@ internal sealed partial class FontAtlasFactory
GC.SuppressFinalize(this);
}
/// <inheritdoc/>
public IDisposable SuppressAutoRebuild()
{
this.buildSuppressionCounter++;
return Disposable.Create(
() =>
{
this.buildSuppressionCounter--;
if (this.buildSuppressionSuppressed)
this.OnRebuildRecommend();
});
}
/// <inheritdoc/>
public IFontHandle NewGameFontHandle(GameFontStyle style) => this.gameFontHandleManager.NewFontHandle(style);
@ -363,15 +380,6 @@ internal sealed partial class FontAtlasFactory
public IFontHandle NewDelegateFontHandle(FontAtlasBuildStepDelegate buildStepDelegate) =>
this.delegateFontHandleManager.NewFontHandle(buildStepDelegate);
/// <inheritdoc/>
public void FreeFontHandle(IFontHandle handle)
{
foreach (var manager in this.fontHandleManagers)
{
manager.FreeFontHandle(handle);
}
}
/// <inheritdoc/>
public void BuildFontsOnNextFrame()
{
@ -688,6 +696,13 @@ internal sealed partial class FontAtlasFactory
if (this.disposed)
return;
if (this.buildSuppressionCounter > 0)
{
this.buildSuppressionSuppressed = true;
return;
}
this.buildSuppressionSuppressed = false;
this.factory.Framework.RunOnFrameworkThread(
() =>
{

View file

@ -157,11 +157,7 @@ internal class GamePrebakedFontHandle : IFontHandle.IInternal
this.Substance = null;
}
/// <summary>
/// Creates a new <see cref="IFontHandle"/> from game's built-in fonts.
/// </summary>
/// <param name="style">Font to use.</param>
/// <returns>Handle to a font that may or may not be ready yet.</returns>
/// <inheritdoc cref="IFontAtlas.NewGameFontHandle"/>
public IFontHandle NewFontHandle(GameFontStyle style)
{
var handle = new GamePrebakedFontHandle(this, style);

View file

@ -37,7 +37,6 @@ public sealed class UiBuilder : IDisposable
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
private readonly DisposeSafety.ScopedFinalizer scopedFinalizer = new();
private readonly IFontAtlas privateAtlas;
private bool hasErrorWindow = false;
private bool lastFrameUiHideState = false;
@ -61,14 +60,14 @@ public sealed class UiBuilder : IDisposable
this.interfaceManager.ResizeBuffers += this.OnResizeBuffers;
this.scopedFinalizer.Add(() => this.interfaceManager.ResizeBuffers -= this.OnResizeBuffers);
this.privateAtlas =
this.FontAtlas =
this.scopedFinalizer
.Add(
Service<FontAtlasFactory>
.Get()
.CreateFontAtlas(namespaceName, FontAtlasAutoRebuildMode.Disable));
this.privateAtlas.BuildStepChange += this.PrivateAtlasOnBuildStepChange;
this.privateAtlas.RebuildRecommend += this.RebuildFonts;
this.FontAtlas.BuildStepChange += this.PrivateAtlasOnBuildStepChange;
this.FontAtlas.RebuildRecommend += this.RebuildFonts;
}
catch
{
@ -104,7 +103,7 @@ public sealed class UiBuilder : IDisposable
/// (at any time), so you should both reload your custom fonts and restore those
/// pointers inside this handler.
/// </summary>
[Obsolete($"Use {nameof(NewDelegateFontHandle)} instead.", false)]
[Obsolete($"Use {nameof(this.FontAtlas)} instead.", false)]
public event Action? BuildFonts;
/// <summary>
@ -113,7 +112,7 @@ public sealed class UiBuilder : IDisposable
/// (at any time), so you should both reload your custom fonts and restore those
/// pointers inside this handler.
/// </summary>
[Obsolete($"Use {nameof(NewDelegateFontHandle)} instead.", false)]
[Obsolete($"Use {nameof(this.FontAtlas)} instead.", false)]
public event Action? AfterBuildFonts;
/// <summary>
@ -145,7 +144,7 @@ public sealed class UiBuilder : IDisposable
/// <remarks>
/// A font handle corresponding to this font can be obtained with:
/// <code>
/// uiBuilderOrFontAtlas.NewDelegateFontHandle(
/// fontAtlas.NewDelegateFontHandle(
/// e => e.OnPreBuild(
/// tk => tk.AddDalamudDefaultFont(UiBuilder.DefaultFontSizePt)));
/// </code>
@ -159,7 +158,7 @@ public sealed class UiBuilder : IDisposable
/// <remarks>
/// A font handle corresponding to this font can be obtained with:
/// <code>
/// uiBuilderOrFontAtlas.NewDelegateFontHandle(
/// fontAtlas.NewDelegateFontHandle(
/// e => e.OnPreBuild(
/// tk => tk.AddFontAwesomeIconFont(new() { SizePt = UiBuilder.DefaultFontSizePt })));
/// </code>
@ -173,7 +172,7 @@ public sealed class UiBuilder : IDisposable
/// <remarks>
/// A font handle corresponding to this font can be obtained with:
/// <code>
/// uiBuilderOrFontAtlas.NewDelegateFontHandle(
/// fontAtlas.NewDelegateFontHandle(
/// e => e.OnPreBuild(
/// tk => tk.AddDalamudAssetFont(
/// DalamudAsset.InconsolataRegular,
@ -251,6 +250,11 @@ public sealed class UiBuilder : IDisposable
/// </summary>
public bool UiPrepared => Service<InterfaceManager.InterfaceManagerWithScene>.GetNullable() != null;
/// <summary>
/// Gets the plugin-private font atlas.
/// </summary>
public IFontAtlas FontAtlas { get; }
/// <summary>
/// Gets or sets a value indicating whether statistics about UI draw time should be collected.
/// </summary>
@ -418,40 +422,11 @@ public sealed class UiBuilder : IDisposable
/// </summary>
/// <param name="style">Font to get.</param>
/// <returns>Handle to the game font which may or may not be available for use yet.</returns>
[Obsolete($"Use {nameof(NewGameFontHandle)} instead.", false)]
[Obsolete($"Use {nameof(this.FontAtlas)}.{nameof(IFontAtlas.NewGameFontHandle)} instead.", false)]
public GameFontHandle GetGameFontHandle(GameFontStyle style) => new(
(IFontHandle.IInternal)this.NewGameFontHandle(style),
(IFontHandle.IInternal)this.FontAtlas.NewGameFontHandle(style),
Service<FontAtlasFactory>.Get());
/// <inheritdoc cref="IFontAtlas.NewGameFontHandle"/>
public IFontHandle NewGameFontHandle(GameFontStyle style) => this.privateAtlas.NewGameFontHandle(style);
/// <inheritdoc cref="IFontAtlas.NewDelegateFontHandle"/>
/// <example>
/// <b>On initialization</b>:
/// <code>
/// this.fontHandle = uiBuilder.NewDelegateFontHandle(e => e.OnPreBuild(tk => {
/// var config = new SafeFontConfig { SizePx = 16 };
/// config.MergeFont = tk.AddFontFromFile(@"C:\Windows\Fonts\comic.ttf", config);
/// tk.AddGameSymbol(config);
/// tk.AddExtraGlyphsForDalamudLanguage(config);
/// // optionally do the following if you have to add more than one font here,
/// // to specify which font added during this delegate is the final font to use.
/// tk.Font = config.MergeFont;
/// }));
/// // or
/// this.fontHandle = uiBuilder.NewDelegateFontHandle(e =&gt; e.OnPreBuild(tk =&gt; tk.AddDalamudDefaultFont(36)));
/// </code>
/// <br />
/// <b>On use</b>:
/// <code>
/// using (this.fontHandle.Push())
/// ImGui.TextUnformatted("Example");
/// </code>
/// </example>
public IFontHandle NewDelegateFontHandle(FontAtlasBuildStepDelegate buildStepDelegate) =>
this.privateAtlas.NewDelegateFontHandle(buildStepDelegate);
/// <summary>
/// Call this to queue a rebuild of the font atlas.<br/>
/// This will invoke any <see cref="BuildFonts"/> and <see cref="AfterBuildFonts"/> handlers and ensure that any
@ -461,9 +436,9 @@ public sealed class UiBuilder : IDisposable
{
Log.Verbose("[FONT] {0} plugin is initiating FONT REBUILD", this.namespaceName);
if (this.AfterBuildFonts is null && this.BuildFonts is null)
this.privateAtlas.BuildFontsAsync();
this.FontAtlas.BuildFontsAsync();
else
this.privateAtlas.BuildFontsOnNextFrame();
this.FontAtlas.BuildFontsOnNextFrame();
}
/// <summary>
@ -579,7 +554,7 @@ public sealed class UiBuilder : IDisposable
}
// just in case, if something goes wrong, prevent drawing; otherwise it probably will crash.
if (!this.privateAtlas.BuildTask.IsCompletedSuccessfully
if (!this.FontAtlas.BuildTask.IsCompletedSuccessfully
&& (this.BuildFonts is not null || this.AfterBuildFonts is not null))
{
return;