diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index f72f53777..0e28c1025 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -25,10 +25,15 @@ using Dalamud.Interface.Utility;
using Dalamud.Interface.Windowing;
using Dalamud.Utility;
using Dalamud.Utility.Timing;
+
using ImGuiNET;
+
using ImGuiScene;
+
using PInvoke;
+
using Serilog;
+
using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
@@ -131,6 +136,13 @@ internal class InterfaceManager : IInternalDisposableService
public static ImFontPtr IconFont =>
WhenFontsReady().IconFontHandle!.LockUntilPostFrame().OrElse(ImGui.GetIO().FontDefault);
+ ///
+ /// Gets an included FontAwesome icon font with fixed width.
+ /// Accessing this static property outside of the main thread is dangerous and not supported.
+ ///
+ public static ImFontPtr IconFontFixedWidth =>
+ WhenFontsReady().IconFontFixedWidthHandle!.LockUntilPostFrame().OrElse(ImGui.GetIO().FontDefault);
+
///
/// Gets an included monospaced font.
/// Accessing this static property outside of the main thread is dangerous and not supported.
@@ -148,6 +160,11 @@ internal class InterfaceManager : IInternalDisposableService
///
public FontHandle? IconFontHandle { get; private set; }
+ ///
+ /// Gets the icon font handle with fixed width.
+ ///
+ public FontHandle? IconFontFixedWidthHandle { get; private set; }
+
///
/// Gets the mono font handle.
///
@@ -402,7 +419,7 @@ internal class InterfaceManager : IInternalDisposableService
});
}
}
-
+
// no sampler for now because the ImGui implementation we copied doesn't allow for changing it
return new DalamudTextureWrap(new D3DTextureWrap(resView, width, height));
}
@@ -498,7 +515,7 @@ internal class InterfaceManager : IInternalDisposableService
atlas.BuildTask.GetAwaiter().GetResult();
return im;
}
-
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void RenderImGui(RawDX11Scene scene)
{
@@ -732,6 +749,13 @@ internal class InterfaceManager : IInternalDisposableService
GlyphMinAdvanceX = DefaultFontSizePx,
GlyphMaxAdvanceX = DefaultFontSizePx,
})));
+ this.IconFontFixedWidthHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
+ e => e.OnPreBuild(tk => tk.AddDalamudAssetFont(
+ DalamudAsset.FontAwesomeFreeSolid,
+ new()
+ {
+ GlyphRanges = new ushort[] { 0x20 },
+ })));
this.MonoFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
e => e.OnPreBuild(
tk => tk.AddDalamudAssetFont(
@@ -748,6 +772,13 @@ internal class InterfaceManager : IInternalDisposableService
tk.GetFont(this.DefaultFontHandle),
tk.GetFont(this.MonoFontHandle),
missingOnly: true);
+
+ // Fill missing glyphs in IconFontFixedWidth with IconFont and fit ratio
+ tk.CopyGlyphsAcrossFonts(
+ tk.GetFont(this.IconFontHandle),
+ tk.GetFont(this.IconFontFixedWidthHandle),
+ missingOnly: true);
+ tk.FitRatio(tk.GetFont(this.IconFontFixedWidthHandle));
});
this.DefaultFontHandle.ImFontChanged += (_, font) =>
{
diff --git a/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs b/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs
index 827187063..2df0deae6 100644
--- a/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs
+++ b/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs
@@ -1,4 +1,4 @@
-using Dalamud.Interface.Internal;
+using Dalamud.Interface.Internal;
using Dalamud.Utility;
using ImGuiNET;
@@ -27,6 +27,13 @@ public interface IFontAtlasBuildToolkitPostBuild : IFontAtlasBuildToolkit
/// The texture index.
int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError);
+ ///
+ /// Fits a font to a fixed 1:1 ratio adjusting glyph positions horizontally and vertically to fit within font size boundaries.
+ ///
+ /// The font to fit.
+ /// Whether to call target.BuildLookupTable().
+ void FitRatio(ImFontPtr font, bool rebuildLookupTable = true);
+
///
/// Copies glyphs across fonts, in a safer way.
/// If the font does not belong to the current atlas, this function is a no-op.
diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs
index 34d28ccbd..d05e5a2e7 100644
--- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs
+++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs
@@ -1,4 +1,4 @@
-using System.Buffers;
+using System.Buffers;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -166,7 +166,7 @@ internal sealed partial class FontAtlasFactory
///
public int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError) =>
this.data.AddNewTexture(textureWrap, disposeOnError);
-
+
///
public void RegisterPostBuild(Action action) => this.registeredPostBuildActions.Add(action);
@@ -391,12 +391,10 @@ internal sealed partial class FontAtlasFactory
});
case DalamudAsset.LodestoneGameSymbol when !this.factory.HasGameSymbolsFontFile:
- {
return this.AddGameGlyphs(
new(GameFontFamily.Axis, fontConfig.SizePx),
fontConfig.GlyphRanges,
fontConfig.MergeFont);
- }
default:
return this.factory.AddFont(
@@ -858,5 +856,30 @@ internal sealed partial class FontAtlasFactory
}
}
}
+
+ ///
+ public void FitRatio(ImFontPtr font, bool rebuildLookupTable = true)
+ {
+ var nsize = font.FontSize;
+ var glyphs = font.GlyphsWrapped();
+ foreach (ref var glyph in glyphs.DataSpan)
+ {
+ var ratio = 1f;
+ if (glyph.X1 - glyph.X0 > nsize)
+ ratio = Math.Max(ratio, (glyph.X1 - glyph.X0) / nsize);
+ if (glyph.Y1 - glyph.Y0 > nsize)
+ ratio = Math.Max(ratio, (glyph.Y1 - glyph.Y0) / nsize);
+ var w = MathF.Round((glyph.X1 - glyph.X0) / ratio, MidpointRounding.ToZero);
+ var h = MathF.Round((glyph.Y1 - glyph.Y0) / ratio, MidpointRounding.AwayFromZero);
+ glyph.X0 = MathF.Round((nsize - w) / 2f, MidpointRounding.ToZero);
+ glyph.Y0 = MathF.Round((nsize - h) / 2f, MidpointRounding.AwayFromZero);
+ glyph.X1 = glyph.X0 + w;
+ glyph.Y1 = glyph.Y0 + h;
+ glyph.AdvanceX = nsize;
+ }
+
+ if (rebuildLookupTable)
+ this.BuildLookupTable(font);
+ }
}
}
diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs
index b80fe0b82..9440b89e6 100644
--- a/Dalamud/Interface/UiBuilder.cs
+++ b/Dalamud/Interface/UiBuilder.cs
@@ -21,9 +21,13 @@ using Dalamud.Plugin;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
+
using ImGuiNET;
+
using ImGuiScene;
+
using Serilog;
+
using SharpDX.Direct3D11;
namespace Dalamud.Interface;
@@ -53,6 +57,7 @@ public sealed class UiBuilder : IDisposable
private IFontHandle? defaultFontHandle;
private IFontHandle? iconFontHandle;
private IFontHandle? monoFontHandle;
+ private IFontHandle? iconFontFixedWidthHandle;
///
/// Initializes a new instance of the class and registers it.
@@ -106,7 +111,7 @@ public sealed class UiBuilder : IDisposable
/// Event that is fired when the plugin should open its configuration interface.
///
public event Action OpenConfigUi;
-
+
///
/// Event that is fired when the plugin should open its main interface.
///
@@ -251,6 +256,16 @@ public sealed class UiBuilder : IDisposable
this.InterfaceManagerWithScene?.IconFontHandle
?? throw new InvalidOperationException("Scene is not yet ready.")));
+ ///
+ /// Gets the default Dalamud icon font based on FontAwesome 5 free solid with a fixed width and vertically centered glyphs.
+ ///
+ public IFontHandle IconFontFixedWidthHandle =>
+ this.iconFontFixedWidthHandle ??=
+ this.scopedFinalizer.Add(
+ new FontHandleWrapper(
+ this.InterfaceManagerWithScene?.IconFontFixedWidthHandle
+ ?? throw new InvalidOperationException("Scene is not yet ready.")));
+
///
/// Gets the default Dalamud monospaced font based on Inconsolata Regular.
///
@@ -266,7 +281,7 @@ public sealed class UiBuilder : IDisposable
/// new() { SizePx = UiBuilder.DefaultFontSizePx })));
///
///
- public IFontHandle MonoFontHandle =>
+ public IFontHandle MonoFontHandle =>
this.monoFontHandle ??=
this.scopedFinalizer.Add(
new FontHandleWrapper(
@@ -630,7 +645,7 @@ public sealed class UiBuilder : IDisposable
{
this.OpenConfigUi?.InvokeSafely();
}
-
+
///
/// Open the registered configuration UI, if it exists.
///
@@ -838,5 +853,5 @@ public sealed class UiBuilder : IDisposable
private void WrappedOnImFontChanged(IFontHandle obj, ILockedImFont lockedFont) =>
this.ImFontChanged?.Invoke(obj, lockedFont);
- }
+ }
}