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); - } + } }