mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Implement FontChooserDialog (#1637)
* Implement FontChooserDialog * Minor fixes * Fixes 2 * Add Reset default font button * Add failsafe * reduce uninteresting exception message * Add remarks to use AttachExtraGlyphsForDalamudLanguage * Support advanced font configuration options * fixes * Shift ui elements * more fixes * Add To(Localized)String for IFontSpec * Untie GlobalFontScale from default font size * Layout fixes * Make UiBuilder.DefaultFontSize point to user configured value * Update example for NewDelegateFontHandle * Font interfaces: write notes on not intended for plugins to implement * Update default gamma to 1.7 to match closer to prev behavior (1.4**2) * Fix console window layout
This commit is contained in:
parent
3b3823d4e6
commit
34daa73612
31 changed files with 2478 additions and 81 deletions
|
|
@ -5,6 +5,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Interface.FontIdentifier;
|
||||
using Dalamud.Interface.Internal.Windows.PluginInstaller;
|
||||
using Dalamud.Interface.Style;
|
||||
using Dalamud.IoC.Internal;
|
||||
|
|
@ -145,7 +146,13 @@ internal sealed class DalamudConfiguration : IServiceType, IDisposable
|
|||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use AXIS fonts from the game.
|
||||
/// </summary>
|
||||
public bool UseAxisFontsFromGame { get; set; } = false;
|
||||
[Obsolete($"See {nameof(DefaultFontSpec)}")]
|
||||
public bool UseAxisFontsFromGame { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default font spec.
|
||||
/// </summary>
|
||||
public IFontSpec? DefaultFontSpec { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gamma value to apply for Dalamud fonts. Do not use.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
using Dalamud.Storage.Assets;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a font from Dalamud assets.
|
||||
/// </summary>
|
||||
public sealed class DalamudAssetFontAndFamilyId : IFontFamilyId, IFontId
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DalamudAssetFontAndFamilyId"/> class.
|
||||
/// </summary>
|
||||
/// <param name="asset">The font asset.</param>
|
||||
public DalamudAssetFontAndFamilyId(DalamudAsset asset)
|
||||
{
|
||||
if (asset.GetPurpose() != DalamudAssetPurpose.Font)
|
||||
throw new ArgumentOutOfRangeException(nameof(asset), asset, "The specified asset is not a font asset.");
|
||||
this.Asset = asset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font asset.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public DalamudAsset Asset { get; init; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public string EnglishName => $"Dalamud: {this.Asset}";
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public IReadOnlyDictionary<string, string>? LocaleNames => null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public IReadOnlyList<IFontId> Fonts => new List<IFontId> { this }.AsReadOnly();
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public IFontFamilyId Family => this;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public int Weight => (int)DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_NORMAL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public int Stretch => (int)DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_NORMAL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public int Style => (int)DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL;
|
||||
|
||||
public static bool operator ==(DalamudAssetFontAndFamilyId? left, DalamudAssetFontAndFamilyId? right) =>
|
||||
Equals(left, right);
|
||||
|
||||
public static bool operator !=(DalamudAssetFontAndFamilyId? left, DalamudAssetFontAndFamilyId? right) =>
|
||||
!Equals(left, right);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj) => obj is DalamudAssetFontAndFamilyId other && this.Equals(other);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode() => (int)this.Asset;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"{nameof(DalamudAssetFontAndFamilyId)}:{this.Asset}";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int FindBestMatch(int weight, int stretch, int style) => 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ImFontPtr AddToBuildToolkit(IFontAtlasBuildToolkitPreBuild tk, in SafeFontConfig config) =>
|
||||
tk.AddDalamudAssetFont(this.Asset, config);
|
||||
|
||||
private bool Equals(DalamudAssetFontAndFamilyId other) => this.Asset == other.Asset;
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the default Dalamud font.
|
||||
/// </summary>
|
||||
public sealed class DalamudDefaultFontAndFamilyId : IFontId, IFontFamilyId
|
||||
{
|
||||
/// <summary>
|
||||
/// The shared instance of <see cref="DalamudDefaultFontAndFamilyId"/>.
|
||||
/// </summary>
|
||||
public static readonly DalamudDefaultFontAndFamilyId Instance = new();
|
||||
|
||||
private DalamudDefaultFontAndFamilyId()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public string EnglishName => "(Default)";
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public IReadOnlyDictionary<string, string>? LocaleNames => null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public IFontFamilyId Family => this;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public int Weight => (int)DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_NORMAL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public int Stretch => (int)DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_NORMAL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public int Style => (int)DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public IReadOnlyList<IFontId> Fonts => new List<IFontId> { this }.AsReadOnly();
|
||||
|
||||
public static bool operator ==(DalamudDefaultFontAndFamilyId? left, DalamudDefaultFontAndFamilyId? right) =>
|
||||
left is null == right is null;
|
||||
|
||||
public static bool operator !=(DalamudDefaultFontAndFamilyId? left, DalamudDefaultFontAndFamilyId? right) =>
|
||||
left is null != right is null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj) => obj is DalamudDefaultFontAndFamilyId;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode() => 12345678;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => nameof(DalamudDefaultFontAndFamilyId);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ImFontPtr AddToBuildToolkit(IFontAtlasBuildToolkitPreBuild tk, in SafeFontConfig config)
|
||||
=> tk.AddDalamudDefaultFont(config.SizePx, config.GlyphRanges);
|
||||
// TODO: mergeFont
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int FindBestMatch(int weight, int stretch, int style) => 0;
|
||||
}
|
||||
81
Dalamud/Interface/FontIdentifier/GameFontAndFamilyId.cs
Normal file
81
Dalamud/Interface/FontIdentifier/GameFontAndFamilyId.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a font from the game.
|
||||
/// </summary>
|
||||
public sealed class GameFontAndFamilyId : IFontId, IFontFamilyId
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GameFontAndFamilyId"/> class.
|
||||
/// </summary>
|
||||
/// <param name="family">The game font family.</param>
|
||||
public GameFontAndFamilyId(GameFontFamily family) => this.GameFontFamily = family;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the game font family.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public GameFontFamily GameFontFamily { get; init; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public string EnglishName => $"Game: {Enum.GetName(this.GameFontFamily) ?? throw new NotSupportedException()}";
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public IReadOnlyDictionary<string, string>? LocaleNames => null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public IFontFamilyId Family => this;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public int Weight => (int)DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_NORMAL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public int Stretch => (int)DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_NORMAL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public int Style => (int)DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public IReadOnlyList<IFontId> Fonts => new List<IFontId> { this }.AsReadOnly();
|
||||
|
||||
public static bool operator ==(GameFontAndFamilyId? left, GameFontAndFamilyId? right) => Equals(left, right);
|
||||
|
||||
public static bool operator !=(GameFontAndFamilyId? left, GameFontAndFamilyId? right) => !Equals(left, right);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj) =>
|
||||
ReferenceEquals(this, obj) || (obj is GameFontAndFamilyId other && this.Equals(other));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode() => (int)this.GameFontFamily;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int FindBestMatch(int weight, int stretch, int style) => 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"{nameof(GameFontAndFamilyId)}:{this.GameFontFamily}";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ImFontPtr AddToBuildToolkit(IFontAtlasBuildToolkitPreBuild tk, in SafeFontConfig config) =>
|
||||
tk.AddGameGlyphs(new(this.GameFontFamily, config.SizePx), config.GlyphRanges, config.MergeFont);
|
||||
|
||||
private bool Equals(GameFontAndFamilyId other) => this.GameFontFamily == other.GameFontFamily;
|
||||
}
|
||||
102
Dalamud/Interface/FontIdentifier/IFontFamilyId.cs
Normal file
102
Dalamud/Interface/FontIdentifier/IFontFamilyId.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a font family identifier.<br />
|
||||
/// Not intended for plugins to implement.
|
||||
/// </summary>
|
||||
public interface IFontFamilyId : IObjectWithLocalizableName
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the list of fonts under this family.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
IReadOnlyList<IFontId> Fonts { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Finds the index of the font inside <see cref="Fonts"/> that best matches the given parameters.
|
||||
/// </summary>
|
||||
/// <param name="weight">The weight of the font.</param>
|
||||
/// <param name="stretch">The stretch of the font.</param>
|
||||
/// <param name="style">The style of the font.</param>
|
||||
/// <returns>The index of the font. Guaranteed to be a valid index.</returns>
|
||||
int FindBestMatch(int weight, int stretch, int style);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of Dalamud-provided fonts.
|
||||
/// </summary>
|
||||
/// <returns>The list of fonts.</returns>
|
||||
public static List<IFontFamilyId> ListDalamudFonts() =>
|
||||
new()
|
||||
{
|
||||
new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansJpMedium),
|
||||
new DalamudAssetFontAndFamilyId(DalamudAsset.InconsolataRegular),
|
||||
new DalamudAssetFontAndFamilyId(DalamudAsset.FontAwesomeFreeSolid),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of Game-provided fonts.
|
||||
/// </summary>
|
||||
/// <returns>The list of fonts.</returns>
|
||||
public static List<IFontFamilyId> ListGameFonts() => new()
|
||||
{
|
||||
new GameFontAndFamilyId(GameFontFamily.Axis),
|
||||
new GameFontAndFamilyId(GameFontFamily.Jupiter),
|
||||
new GameFontAndFamilyId(GameFontFamily.JupiterNumeric),
|
||||
new GameFontAndFamilyId(GameFontFamily.Meidinger),
|
||||
new GameFontAndFamilyId(GameFontFamily.MiedingerMid),
|
||||
new GameFontAndFamilyId(GameFontFamily.TrumpGothic),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of System-provided fonts.
|
||||
/// </summary>
|
||||
/// <param name="refresh">If <c>true</c>, try to refresh the list.</param>
|
||||
/// <returns>The list of fonts.</returns>
|
||||
public static unsafe List<IFontFamilyId> ListSystemFonts(bool refresh)
|
||||
{
|
||||
using var dwf = default(ComPtr<IDWriteFactory>);
|
||||
fixed (Guid* piid = &IID.IID_IDWriteFactory)
|
||||
{
|
||||
DirectX.DWriteCreateFactory(
|
||||
DWRITE_FACTORY_TYPE.DWRITE_FACTORY_TYPE_SHARED,
|
||||
piid,
|
||||
(IUnknown**)dwf.GetAddressOf()).ThrowOnError();
|
||||
}
|
||||
|
||||
using var sfc = default(ComPtr<IDWriteFontCollection>);
|
||||
dwf.Get()->GetSystemFontCollection(sfc.GetAddressOf(), refresh).ThrowOnError();
|
||||
|
||||
var count = (int)sfc.Get()->GetFontFamilyCount();
|
||||
var result = new List<IFontFamilyId>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
using var ff = default(ComPtr<IDWriteFontFamily>);
|
||||
if (sfc.Get()->GetFontFamily((uint)i, ff.GetAddressOf()).FAILED)
|
||||
{
|
||||
// Ignore errors, if any
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result.Add(SystemFontFamilyId.FromDWriteFamily(ff));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
40
Dalamud/Interface/FontIdentifier/IFontId.cs
Normal file
40
Dalamud/Interface/FontIdentifier/IFontId.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a font identifier.<br />
|
||||
/// Not intended for plugins to implement.
|
||||
/// </summary>
|
||||
public interface IFontId : IObjectWithLocalizableName
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the associated font family.
|
||||
/// </summary>
|
||||
IFontFamilyId Family { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font weight, ranging from 1 to 999.
|
||||
/// </summary>
|
||||
int Weight { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font stretch, ranging from 1 to 9.
|
||||
/// </summary>
|
||||
int Stretch { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font style. Treat as an opaque value.
|
||||
/// </summary>
|
||||
int Style { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds this font to the given font build toolkit.
|
||||
/// </summary>
|
||||
/// <param name="tk">The font build toolkit.</param>
|
||||
/// <param name="config">The font configuration. Some parameters may be ignored.</param>
|
||||
/// <returns>The added font.</returns>
|
||||
ImFontPtr AddToBuildToolkit(IFontAtlasBuildToolkitPreBuild tk, in SafeFontConfig config);
|
||||
}
|
||||
50
Dalamud/Interface/FontIdentifier/IFontSpec.cs
Normal file
50
Dalamud/Interface/FontIdentifier/IFontSpec.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a user's choice of font(s).<br />
|
||||
/// Not intended for plugins to implement.
|
||||
/// </summary>
|
||||
public interface IFontSpec
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the font size in pixels.
|
||||
/// </summary>
|
||||
float SizePx { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font size in points.
|
||||
/// </summary>
|
||||
float SizePt { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the line height in pixels.
|
||||
/// </summary>
|
||||
float LineHeightPx { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a font handle corresponding to this font specification.
|
||||
/// </summary>
|
||||
/// <param name="atlas">The atlas to bind this font handle to.</param>
|
||||
/// <param name="callback">Optional callback to be called after creating the font handle.</param>
|
||||
/// <returns>The new font handle.</returns>
|
||||
IFontHandle CreateFontHandle(IFontAtlas atlas, FontAtlasBuildStepDelegate? callback = null);
|
||||
|
||||
/// <summary>
|
||||
/// Adds this font to the given font build toolkit.
|
||||
/// </summary>
|
||||
/// <param name="tk">The font build toolkit.</param>
|
||||
/// <param name="mergeFont">The font to merge to.</param>
|
||||
/// <returns>The added font.</returns>
|
||||
ImFontPtr AddToBuildToolkit(IFontAtlasBuildToolkitPreBuild tk, ImFontPtr mergeFont = default);
|
||||
|
||||
/// <summary>
|
||||
/// Represents this font specification, preferrably in the requested locale.
|
||||
/// </summary>
|
||||
/// <param name="localeCode">The locale code. Must be in lowercase(invariant).</param>
|
||||
/// <returns>The value.</returns>
|
||||
string ToLocalizedString(string localeCode);
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Utility;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an object with localizable names.
|
||||
/// </summary>
|
||||
public interface IObjectWithLocalizableName
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name, preferrably in English.
|
||||
/// </summary>
|
||||
string EnglishName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names per locales.
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<string, string>? LocaleNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name in the requested locale if available; otherwise, <see cref="EnglishName"/>.
|
||||
/// </summary>
|
||||
/// <param name="localeCode">The locale code. Must be in lowercase(invariant).</param>
|
||||
/// <returns>The value.</returns>
|
||||
string GetLocalizedName(string localeCode)
|
||||
{
|
||||
if (this.LocaleNames is null)
|
||||
return this.EnglishName;
|
||||
if (this.LocaleNames.TryGetValue(localeCode, out var v))
|
||||
return v;
|
||||
foreach (var (a, b) in this.LocaleNames)
|
||||
{
|
||||
if (a.StartsWith(localeCode))
|
||||
return b;
|
||||
}
|
||||
|
||||
return this.EnglishName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves all names per locales.
|
||||
/// </summary>
|
||||
/// <param name="fn">The names.</param>
|
||||
/// <returns>A new dictionary mapping from locale code to localized names.</returns>
|
||||
internal static unsafe IReadOnlyDictionary<string, string> GetLocaleNames(IDWriteLocalizedStrings* fn)
|
||||
{
|
||||
var count = fn->GetCount();
|
||||
var maxStrLen = 0u;
|
||||
for (var i = 0u; i < count; i++)
|
||||
{
|
||||
var length = 0u;
|
||||
fn->GetStringLength(i, &length).ThrowOnError();
|
||||
maxStrLen = Math.Max(maxStrLen, length);
|
||||
fn->GetLocaleNameLength(i, &length).ThrowOnError();
|
||||
maxStrLen = Math.Max(maxStrLen, length);
|
||||
}
|
||||
|
||||
maxStrLen++;
|
||||
var buf = stackalloc char[(int)maxStrLen];
|
||||
var result = new Dictionary<string, string>((int)count);
|
||||
for (var i = 0u; i < count; i++)
|
||||
{
|
||||
fn->GetLocaleName(i, (ushort*)buf, maxStrLen).ThrowOnError();
|
||||
var key = new string(buf);
|
||||
fn->GetString(i, (ushort*)buf, maxStrLen).ThrowOnError();
|
||||
var value = new string(buf);
|
||||
result[key.ToLowerInvariant()] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
155
Dalamud/Interface/FontIdentifier/SingleFontSpec.cs
Normal file
155
Dalamud/Interface/FontIdentifier/SingleFontSpec.cs
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
using System.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
using Dalamud.Interface.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a user's choice of a single font.
|
||||
/// </summary>
|
||||
[SuppressMessage(
|
||||
"StyleCop.CSharp.OrderingRules",
|
||||
"SA1206:Declaration keywords should follow order",
|
||||
Justification = "public required")]
|
||||
public record SingleFontSpec : IFontSpec
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the font id.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public required IFontId FontId { get; init; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonProperty]
|
||||
public float SizePx { get; init; } = 16;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public float SizePt
|
||||
{
|
||||
get => (this.SizePx * 3) / 4;
|
||||
init => this.SizePx = (value * 4) / 3;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public float LineHeightPx => MathF.Round(this.SizePx * this.LineHeight);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the line height ratio to the font size.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public float LineHeight { get; init; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the glyph offset in pixels.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public Vector2 GlyphOffset { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the letter spacing in pixels.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public float LetterSpacing { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the glyph ranges.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public ushort[]? GlyphRanges { get; init; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ToLocalizedString(string localeCode)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(this.FontId.Family.GetLocalizedName(localeCode));
|
||||
sb.Append($"({this.FontId.GetLocalizedName(localeCode)}, {this.SizePt}pt");
|
||||
if (Math.Abs(this.LineHeight - 1f) > 0.000001f)
|
||||
sb.Append($", LH={this.LineHeight:0.##}");
|
||||
if (this.GlyphOffset != default)
|
||||
sb.Append($", O={this.GlyphOffset.X:0.##},{this.GlyphOffset.Y:0.##}");
|
||||
if (this.LetterSpacing != 0f)
|
||||
sb.Append($", LS={this.LetterSpacing:0.##}");
|
||||
sb.Append(')');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => this.ToLocalizedString("en");
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IFontHandle CreateFontHandle(IFontAtlas atlas, FontAtlasBuildStepDelegate? callback = null) =>
|
||||
atlas.NewDelegateFontHandle(tk =>
|
||||
{
|
||||
tk.OnPreBuild(e => e.Font = this.AddToBuildToolkit(e));
|
||||
callback?.Invoke(tk);
|
||||
});
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ImFontPtr AddToBuildToolkit(IFontAtlasBuildToolkitPreBuild tk, ImFontPtr mergeFont = default)
|
||||
{
|
||||
var font = this.FontId.AddToBuildToolkit(
|
||||
tk,
|
||||
new()
|
||||
{
|
||||
SizePx = this.SizePx,
|
||||
GlyphRanges = this.GlyphRanges,
|
||||
MergeFont = mergeFont,
|
||||
});
|
||||
|
||||
tk.RegisterPostBuild(
|
||||
() =>
|
||||
{
|
||||
var roundUnit = tk.IsGlobalScaleIgnored(font) ? 1 : 1 / tk.Scale;
|
||||
var newAscent = MathF.Round((font.Ascent * this.LineHeight) / roundUnit) * roundUnit;
|
||||
var newFontSize = MathF.Round((font.FontSize * this.LineHeight) / roundUnit) * roundUnit;
|
||||
var shiftDown = MathF.Round((newFontSize - font.FontSize) / 2f / roundUnit) * roundUnit;
|
||||
|
||||
font.Ascent = newAscent;
|
||||
font.FontSize = newFontSize;
|
||||
font.Descent = newFontSize - font.Ascent;
|
||||
|
||||
var lookup = new BitArray(ushort.MaxValue + 1, this.GlyphRanges is null);
|
||||
if (this.GlyphRanges is not null)
|
||||
{
|
||||
for (var i = 0; i < this.GlyphRanges.Length && this.GlyphRanges[i] != 0; i += 2)
|
||||
{
|
||||
var to = (int)this.GlyphRanges[i + 1];
|
||||
for (var j = this.GlyphRanges[i]; j <= to; j++)
|
||||
lookup[j] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// `/ roundUnit` = `* scale`
|
||||
var dax = MathF.Round(this.LetterSpacing / roundUnit / roundUnit) * roundUnit;
|
||||
var dxy0 = this.GlyphOffset / roundUnit;
|
||||
|
||||
dxy0 /= roundUnit;
|
||||
dxy0.X = MathF.Round(dxy0.X);
|
||||
dxy0.Y = MathF.Round(dxy0.Y);
|
||||
dxy0 *= roundUnit;
|
||||
|
||||
dxy0.Y += shiftDown;
|
||||
var dxy = new Vector4(dxy0, dxy0.X, dxy0.Y);
|
||||
foreach (ref var glyphReal in font.GlyphsWrapped().DataSpan)
|
||||
{
|
||||
if (!lookup[glyphReal.Codepoint])
|
||||
continue;
|
||||
|
||||
glyphReal.XY += dxy;
|
||||
glyphReal.AdvanceX += dax;
|
||||
}
|
||||
});
|
||||
|
||||
return font;
|
||||
}
|
||||
}
|
||||
181
Dalamud/Interface/FontIdentifier/SystemFontFamilyId.cs
Normal file
181
Dalamud/Interface/FontIdentifier/SystemFontFamilyId.cs
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using Dalamud.Utility;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a font from system.
|
||||
/// </summary>
|
||||
public sealed class SystemFontFamilyId : IFontFamilyId
|
||||
{
|
||||
[JsonIgnore]
|
||||
private IReadOnlyList<IFontId>? fontsLazy;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemFontFamilyId"/> class.
|
||||
/// </summary>
|
||||
/// <param name="englishName">The font name in English.</param>
|
||||
/// <param name="localeNames">The localized font name for display purposes.</param>
|
||||
[JsonConstructor]
|
||||
internal SystemFontFamilyId(string englishName, IReadOnlyDictionary<string, string> localeNames)
|
||||
{
|
||||
this.EnglishName = englishName;
|
||||
this.LocaleNames = localeNames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemFontFamilyId"/> class.
|
||||
/// </summary>
|
||||
/// <param name="localeNames">The localized font name for display purposes.</param>
|
||||
internal SystemFontFamilyId(IReadOnlyDictionary<string, string> localeNames)
|
||||
{
|
||||
if (localeNames.TryGetValue("en-us", out var name))
|
||||
this.EnglishName = name;
|
||||
else if (localeNames.TryGetValue("en", out name))
|
||||
this.EnglishName = name;
|
||||
else
|
||||
this.EnglishName = localeNames.Values.First();
|
||||
this.LocaleNames = localeNames;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonProperty]
|
||||
public string EnglishName { get; init; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonProperty]
|
||||
public IReadOnlyDictionary<string, string>? LocaleNames { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public IReadOnlyList<IFontId> Fonts => this.fontsLazy ??= this.GetFonts();
|
||||
|
||||
public static bool operator ==(SystemFontFamilyId? left, SystemFontFamilyId? right) => Equals(left, right);
|
||||
|
||||
public static bool operator !=(SystemFontFamilyId? left, SystemFontFamilyId? right) => !Equals(left, right);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int FindBestMatch(int weight, int stretch, int style)
|
||||
{
|
||||
using var matchingFont = default(ComPtr<IDWriteFont>);
|
||||
|
||||
var candidates = this.Fonts.ToList();
|
||||
var minGap = int.MaxValue;
|
||||
foreach (var c in candidates)
|
||||
minGap = Math.Min(minGap, Math.Abs(c.Weight - weight));
|
||||
candidates.RemoveAll(c => Math.Abs(c.Weight - weight) != minGap);
|
||||
|
||||
minGap = int.MaxValue;
|
||||
foreach (var c in candidates)
|
||||
minGap = Math.Min(minGap, Math.Abs(c.Stretch - stretch));
|
||||
candidates.RemoveAll(c => Math.Abs(c.Stretch - stretch) != minGap);
|
||||
|
||||
if (candidates.Any(x => x.Style == style))
|
||||
candidates.RemoveAll(x => x.Style != style);
|
||||
else if (candidates.Any(x => x.Style == (int)DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL))
|
||||
candidates.RemoveAll(x => x.Style != (int)DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL);
|
||||
|
||||
if (!candidates.Any())
|
||||
return 0;
|
||||
|
||||
for (var i = 0; i < this.Fonts.Count; i++)
|
||||
{
|
||||
if (Equals(this.Fonts[i], candidates[0]))
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"{nameof(SystemFontFamilyId)}:{this.EnglishName}";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj) =>
|
||||
ReferenceEquals(this, obj) || (obj is SystemFontFamilyId other && this.Equals(other));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode() => this.EnglishName.GetHashCode();
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="SystemFontFamilyId"/> from an <see cref="IDWriteFontFamily"/>.
|
||||
/// </summary>
|
||||
/// <param name="family">The family.</param>
|
||||
/// <returns>The new instance.</returns>
|
||||
internal static unsafe SystemFontFamilyId FromDWriteFamily(ComPtr<IDWriteFontFamily> family)
|
||||
{
|
||||
using var fn = default(ComPtr<IDWriteLocalizedStrings>);
|
||||
family.Get()->GetFamilyNames(fn.GetAddressOf()).ThrowOnError();
|
||||
return new(IObjectWithLocalizableName.GetLocaleNames(fn));
|
||||
}
|
||||
|
||||
private unsafe IReadOnlyList<IFontId> GetFonts()
|
||||
{
|
||||
using var dwf = default(ComPtr<IDWriteFactory>);
|
||||
fixed (Guid* piid = &IID.IID_IDWriteFactory)
|
||||
{
|
||||
DirectX.DWriteCreateFactory(
|
||||
DWRITE_FACTORY_TYPE.DWRITE_FACTORY_TYPE_SHARED,
|
||||
piid,
|
||||
(IUnknown**)dwf.GetAddressOf()).ThrowOnError();
|
||||
}
|
||||
|
||||
using var sfc = default(ComPtr<IDWriteFontCollection>);
|
||||
dwf.Get()->GetSystemFontCollection(sfc.GetAddressOf(), false).ThrowOnError();
|
||||
|
||||
var familyIndex = 0u;
|
||||
BOOL exists = false;
|
||||
fixed (void* pName = this.EnglishName)
|
||||
sfc.Get()->FindFamilyName((ushort*)pName, &familyIndex, &exists).ThrowOnError();
|
||||
if (!exists)
|
||||
throw new FileNotFoundException($"Font \"{this.EnglishName}\" not found.");
|
||||
|
||||
using var family = default(ComPtr<IDWriteFontFamily>);
|
||||
sfc.Get()->GetFontFamily(familyIndex, family.GetAddressOf()).ThrowOnError();
|
||||
|
||||
var fontCount = (int)family.Get()->GetFontCount();
|
||||
var fonts = new List<IFontId>(fontCount);
|
||||
for (var i = 0; i < fontCount; i++)
|
||||
{
|
||||
using var font = default(ComPtr<IDWriteFont>);
|
||||
if (family.Get()->GetFont((uint)i, font.GetAddressOf()).FAILED)
|
||||
{
|
||||
// Ignore errors, if any
|
||||
continue;
|
||||
}
|
||||
|
||||
if (font.Get()->GetSimulations() != DWRITE_FONT_SIMULATIONS.DWRITE_FONT_SIMULATIONS_NONE)
|
||||
{
|
||||
// No simulation support
|
||||
continue;
|
||||
}
|
||||
|
||||
fonts.Add(new SystemFontId(this, font));
|
||||
}
|
||||
|
||||
fonts.Sort(
|
||||
(a, b) =>
|
||||
{
|
||||
var comp = a.Weight.CompareTo(b.Weight);
|
||||
if (comp != 0)
|
||||
return comp;
|
||||
|
||||
comp = a.Stretch.CompareTo(b.Stretch);
|
||||
if (comp != 0)
|
||||
return comp;
|
||||
|
||||
return a.Style.CompareTo(b.Style);
|
||||
});
|
||||
return fonts;
|
||||
}
|
||||
|
||||
private bool Equals(SystemFontFamilyId other) => this.EnglishName == other.EnglishName;
|
||||
}
|
||||
163
Dalamud/Interface/FontIdentifier/SystemFontId.cs
Normal file
163
Dalamud/Interface/FontIdentifier/SystemFontId.cs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a font installed in the system.
|
||||
/// </summary>
|
||||
public sealed class SystemFontId : IFontId
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemFontId"/> class.
|
||||
/// </summary>
|
||||
/// <param name="family">The parent font family.</param>
|
||||
/// <param name="font">The font.</param>
|
||||
internal unsafe SystemFontId(SystemFontFamilyId family, ComPtr<IDWriteFont> font)
|
||||
{
|
||||
this.Family = family;
|
||||
this.Weight = (int)font.Get()->GetWeight();
|
||||
this.Stretch = (int)font.Get()->GetStretch();
|
||||
this.Style = (int)font.Get()->GetStyle();
|
||||
|
||||
using var fn = default(ComPtr<IDWriteLocalizedStrings>);
|
||||
font.Get()->GetFaceNames(fn.GetAddressOf()).ThrowOnError();
|
||||
this.LocaleNames = IObjectWithLocalizableName.GetLocaleNames(fn);
|
||||
if (this.LocaleNames.TryGetValue("en-us", out var name))
|
||||
this.EnglishName = name;
|
||||
else if (this.LocaleNames.TryGetValue("en", out name))
|
||||
this.EnglishName = name;
|
||||
else
|
||||
this.EnglishName = this.LocaleNames.Values.First();
|
||||
}
|
||||
|
||||
[JsonConstructor]
|
||||
private SystemFontId(string englishName, IReadOnlyDictionary<string, string> localeNames, IFontFamilyId family)
|
||||
{
|
||||
this.EnglishName = englishName;
|
||||
this.LocaleNames = localeNames;
|
||||
this.Family = family;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonProperty]
|
||||
public string EnglishName { get; init; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonProperty]
|
||||
public IReadOnlyDictionary<string, string>? LocaleNames { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonProperty]
|
||||
public IFontFamilyId Family { get; init; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonProperty]
|
||||
public int Weight { get; init; } = (int)DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_NORMAL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonProperty]
|
||||
public int Stretch { get; init; } = (int)DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_NORMAL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonProperty]
|
||||
public int Style { get; init; } = (int)DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL;
|
||||
|
||||
public static bool operator ==(SystemFontId? left, SystemFontId? right) => Equals(left, right);
|
||||
|
||||
public static bool operator !=(SystemFontId? left, SystemFontId? right) => !Equals(left, right);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj) =>
|
||||
ReferenceEquals(this, obj) || (obj is SystemFontId other && this.Equals(other));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode() => HashCode.Combine(this.Family, this.Weight, this.Stretch, this.Style);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() =>
|
||||
$"{nameof(SystemFontId)}:{this.Weight}:{this.Stretch}:{this.Style}:{this.Family}";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ImFontPtr AddToBuildToolkit(IFontAtlasBuildToolkitPreBuild tk, in SafeFontConfig config)
|
||||
{
|
||||
var (path, index) = this.GetFileAndIndex();
|
||||
return tk.AddFontFromFile(path, config with { FontNo = index });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file containing this font, and the font index within.
|
||||
/// </summary>
|
||||
/// <returns>The path and index.</returns>
|
||||
public unsafe (string Path, int Index) GetFileAndIndex()
|
||||
{
|
||||
using var dwf = default(ComPtr<IDWriteFactory>);
|
||||
fixed (Guid* piid = &IID.IID_IDWriteFactory)
|
||||
{
|
||||
DirectX.DWriteCreateFactory(
|
||||
DWRITE_FACTORY_TYPE.DWRITE_FACTORY_TYPE_SHARED,
|
||||
piid,
|
||||
(IUnknown**)dwf.GetAddressOf()).ThrowOnError();
|
||||
}
|
||||
|
||||
using var sfc = default(ComPtr<IDWriteFontCollection>);
|
||||
dwf.Get()->GetSystemFontCollection(sfc.GetAddressOf(), false).ThrowOnError();
|
||||
|
||||
var familyIndex = 0u;
|
||||
BOOL exists = false;
|
||||
fixed (void* name = this.Family.EnglishName)
|
||||
sfc.Get()->FindFamilyName((ushort*)name, &familyIndex, &exists).ThrowOnError();
|
||||
if (!exists)
|
||||
throw new FileNotFoundException($"Font \"{this.Family.EnglishName}\" not found.");
|
||||
|
||||
using var family = default(ComPtr<IDWriteFontFamily>);
|
||||
sfc.Get()->GetFontFamily(familyIndex, family.GetAddressOf()).ThrowOnError();
|
||||
|
||||
using var font = default(ComPtr<IDWriteFont>);
|
||||
family.Get()->GetFirstMatchingFont(
|
||||
(DWRITE_FONT_WEIGHT)this.Weight,
|
||||
(DWRITE_FONT_STRETCH)this.Stretch,
|
||||
(DWRITE_FONT_STYLE)this.Style,
|
||||
font.GetAddressOf()).ThrowOnError();
|
||||
|
||||
using var fface = default(ComPtr<IDWriteFontFace>);
|
||||
font.Get()->CreateFontFace(fface.GetAddressOf()).ThrowOnError();
|
||||
var fileCount = 0;
|
||||
fface.Get()->GetFiles((uint*)&fileCount, null).ThrowOnError();
|
||||
if (fileCount != 1)
|
||||
throw new NotSupportedException();
|
||||
|
||||
using var ffile = default(ComPtr<IDWriteFontFile>);
|
||||
fface.Get()->GetFiles((uint*)&fileCount, ffile.GetAddressOf()).ThrowOnError();
|
||||
void* refKey;
|
||||
var refKeySize = 0u;
|
||||
ffile.Get()->GetReferenceKey(&refKey, &refKeySize).ThrowOnError();
|
||||
|
||||
using var floader = default(ComPtr<IDWriteFontFileLoader>);
|
||||
ffile.Get()->GetLoader(floader.GetAddressOf()).ThrowOnError();
|
||||
|
||||
using var flocal = default(ComPtr<IDWriteLocalFontFileLoader>);
|
||||
floader.As(&flocal).ThrowOnError();
|
||||
|
||||
var pathSize = 0u;
|
||||
flocal.Get()->GetFilePathLengthFromKey(refKey, refKeySize, &pathSize).ThrowOnError();
|
||||
|
||||
var path = stackalloc char[(int)pathSize + 1];
|
||||
flocal.Get()->GetFilePathFromKey(refKey, refKeySize, (ushort*)path, pathSize + 1).ThrowOnError();
|
||||
return (new(path, 0, (int)pathSize), (int)fface.Get()->GetIndex());
|
||||
}
|
||||
|
||||
private bool Equals(SystemFontId other) => this.Family.Equals(other.Family) && this.Weight == other.Weight &&
|
||||
this.Stretch == other.Stretch && this.Style == other.Style;
|
||||
}
|
||||
1117
Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs
Normal file
1117
Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -705,13 +705,13 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
using (this.dalamudAtlas.SuppressAutoRebuild())
|
||||
{
|
||||
this.DefaultFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
|
||||
e => e.OnPreBuild(tk => tk.AddDalamudDefaultFont(DefaultFontSizePx)));
|
||||
e => e.OnPreBuild(tk => tk.AddDalamudDefaultFont(-1)));
|
||||
this.IconFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
|
||||
e => e.OnPreBuild(
|
||||
tk => tk.AddFontAwesomeIconFont(
|
||||
new()
|
||||
{
|
||||
SizePx = DefaultFontSizePx,
|
||||
SizePx = Service<FontAtlasFactory>.Get().DefaultFontSpec.SizePx,
|
||||
GlyphMinAdvanceX = DefaultFontSizePx,
|
||||
GlyphMaxAdvanceX = DefaultFontSizePx,
|
||||
})));
|
||||
|
|
@ -719,7 +719,10 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
e => e.OnPreBuild(
|
||||
tk => tk.AddDalamudAssetFont(
|
||||
DalamudAsset.InconsolataRegular,
|
||||
new() { SizePx = DefaultFontSizePx })));
|
||||
new()
|
||||
{
|
||||
SizePx = Service<FontAtlasFactory>.Get().DefaultFontSpec.SizePx,
|
||||
})));
|
||||
this.dalamudAtlas.BuildStepChange += e => e.OnPostBuild(
|
||||
tk =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -152,8 +152,11 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
ImGui.SetCursorPosX(ImGui.GetContentRegionMax().X / 2.0f - ImGui.CalcTextSize(regexErrorString).X / 2.0f);
|
||||
ImGui.TextColored(ImGuiColors.DalamudRed, regexErrorString);
|
||||
}
|
||||
|
||||
ImGui.BeginChild("scrolling", new Vector2(0, ImGui.GetFrameHeightWithSpacing() - 55 * ImGuiHelpers.GlobalScale), false, ImGuiWindowFlags.AlwaysHorizontalScrollbar | ImGuiWindowFlags.AlwaysVerticalScrollbar);
|
||||
|
||||
var sendButtonSize = ImGui.CalcTextSize("Send") +
|
||||
((new Vector2(16, 0) + (ImGui.GetStyle().FramePadding * 2)) * ImGuiHelpers.GlobalScale);
|
||||
var scrollingHeight = ImGui.GetContentRegionAvail().Y - sendButtonSize.Y;
|
||||
ImGui.BeginChild("scrolling", new Vector2(0, scrollingHeight), false, ImGuiWindowFlags.AlwaysHorizontalScrollbar | ImGuiWindowFlags.AlwaysVerticalScrollbar);
|
||||
|
||||
if (this.clearLog) this.Clear();
|
||||
|
||||
|
|
@ -173,9 +176,10 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
var childDrawList = ImGui.GetWindowDrawList();
|
||||
var childSize = ImGui.GetWindowSize();
|
||||
|
||||
var cursorDiv = ImGuiHelpers.GlobalScale * 93;
|
||||
var cursorLogLevel = ImGuiHelpers.GlobalScale * 100;
|
||||
var cursorLogLine = ImGuiHelpers.GlobalScale * 135;
|
||||
var cursorDiv = ImGui.CalcTextSize("00:00:00.000 ").X;
|
||||
var cursorLogLevel = ImGui.CalcTextSize("00:00:00.000 | ").X;
|
||||
var dividerOffset = ImGui.CalcTextSize("00:00:00.000 | AAA ").X + (ImGui.CalcTextSize(" ").X / 2);
|
||||
var cursorLogLine = ImGui.CalcTextSize("00:00:00.000 | AAA | ").X;
|
||||
|
||||
lock (this.renderLock)
|
||||
{
|
||||
|
|
@ -242,8 +246,7 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
}
|
||||
|
||||
// Draw dividing line
|
||||
var offset = ImGuiHelpers.GlobalScale * 127;
|
||||
childDrawList.AddLine(new Vector2(childPos.X + offset, childPos.Y), new Vector2(childPos.X + offset, childPos.Y + childSize.Y), 0x4FFFFFFF, 1.0f);
|
||||
childDrawList.AddLine(new Vector2(childPos.X + dividerOffset, childPos.Y), new Vector2(childPos.X + dividerOffset, childPos.Y + childSize.Y), 0x4FFFFFFF, 1.0f);
|
||||
|
||||
ImGui.EndChild();
|
||||
|
||||
|
|
@ -261,7 +264,7 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - (80.0f * ImGuiHelpers.GlobalScale) - (ImGui.GetStyle().ItemSpacing.X * ImGuiHelpers.GlobalScale));
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - sendButtonSize.X - (ImGui.GetStyle().ItemSpacing.X * ImGuiHelpers.GlobalScale));
|
||||
|
||||
var getFocus = false;
|
||||
unsafe
|
||||
|
|
@ -280,7 +283,7 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
|
||||
if (hadColor) ImGui.PopStyleColor();
|
||||
|
||||
if (ImGui.Button("Send", ImGuiHelpers.ScaledVector2(80.0f, 23.0f)))
|
||||
if (ImGui.Button("Send", sendButtonSize))
|
||||
{
|
||||
this.ProcessCommand();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ using System.Numerics;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Interface.FontIdentifier;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.ImGuiFontChooserDialog;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
using Dalamud.Interface.ManagedFontAtlas.Internals;
|
||||
using Dalamud.Interface.Utility;
|
||||
|
|
@ -24,6 +27,8 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
|
|||
{
|
||||
private ImVectorWrapper<byte> testStringBuffer;
|
||||
private IFontAtlas? privateAtlas;
|
||||
private SingleFontSpec fontSpec = new() { FontId = DalamudDefaultFontAndFamilyId.Instance };
|
||||
private IFontHandle? fontDialogHandle;
|
||||
private IReadOnlyDictionary<GameFontFamily, (GameFontStyle Size, Lazy<IFontHandle> Handle)[]>? fontHandles;
|
||||
private bool useGlobalScale;
|
||||
private bool useWordWrap;
|
||||
|
|
@ -111,29 +116,32 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
|
|||
if (ImGui.Button("Test Lock"))
|
||||
Task.Run(this.TestLock);
|
||||
|
||||
fixed (byte* labelPtr = "Test Input"u8)
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Choose Editor Font"))
|
||||
{
|
||||
if (ImGuiNative.igInputTextMultiline(
|
||||
labelPtr,
|
||||
this.testStringBuffer.Data,
|
||||
(uint)this.testStringBuffer.Capacity,
|
||||
new(ImGui.GetContentRegionAvail().X, 32 * ImGuiHelpers.GlobalScale),
|
||||
0,
|
||||
null,
|
||||
null) != 0)
|
||||
{
|
||||
var len = this.testStringBuffer.StorageSpan.IndexOf((byte)0);
|
||||
if (len + 4 >= this.testStringBuffer.Capacity)
|
||||
this.testStringBuffer.EnsureCapacityExponential(len + 4);
|
||||
if (len < this.testStringBuffer.Capacity)
|
||||
{
|
||||
this.testStringBuffer.LengthUnsafe = len;
|
||||
this.testStringBuffer.StorageSpan[len] = default;
|
||||
}
|
||||
var fcd = new SingleFontChooserDialog(
|
||||
Service<FontAtlasFactory>.Get().CreateFontAtlas(
|
||||
$"{nameof(GamePrebakedFontsTestWidget)}:EditorFont",
|
||||
FontAtlasAutoRebuildMode.Async));
|
||||
fcd.SelectedFont = this.fontSpec;
|
||||
fcd.IgnorePreviewGlobalScale = !this.useGlobalScale;
|
||||
Service<InterfaceManager>.Get().Draw += fcd.Draw;
|
||||
fcd.ResultTask.ContinueWith(
|
||||
r => Service<Framework>.Get().RunOnFrameworkThread(
|
||||
() =>
|
||||
{
|
||||
Service<InterfaceManager>.Get().Draw -= fcd.Draw;
|
||||
fcd.Dispose();
|
||||
|
||||
if (this.useMinimumBuild)
|
||||
_ = this.privateAtlas?.BuildFontsAsync();
|
||||
}
|
||||
_ = r.Exception;
|
||||
if (!r.IsCompletedSuccessfully)
|
||||
return;
|
||||
|
||||
this.fontSpec = r.Result;
|
||||
Log.Information("Selected font: {font}", this.fontSpec);
|
||||
this.fontDialogHandle?.Dispose();
|
||||
this.fontDialogHandle = null;
|
||||
}));
|
||||
}
|
||||
|
||||
this.privateAtlas ??=
|
||||
|
|
@ -141,6 +149,41 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
|
|||
nameof(GamePrebakedFontsTestWidget),
|
||||
FontAtlasAutoRebuildMode.Async,
|
||||
this.useGlobalScale);
|
||||
this.fontDialogHandle ??= this.fontSpec.CreateFontHandle(this.privateAtlas);
|
||||
|
||||
fixed (byte* labelPtr = "Test Input"u8)
|
||||
{
|
||||
if (!this.useGlobalScale)
|
||||
ImGuiNative.igSetWindowFontScale(1 / ImGuiHelpers.GlobalScale);
|
||||
using (this.fontDialogHandle.Push())
|
||||
{
|
||||
if (ImGuiNative.igInputTextMultiline(
|
||||
labelPtr,
|
||||
this.testStringBuffer.Data,
|
||||
(uint)this.testStringBuffer.Capacity,
|
||||
new(ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeight() * 3),
|
||||
0,
|
||||
null,
|
||||
null) != 0)
|
||||
{
|
||||
var len = this.testStringBuffer.StorageSpan.IndexOf((byte)0);
|
||||
if (len + 4 >= this.testStringBuffer.Capacity)
|
||||
this.testStringBuffer.EnsureCapacityExponential(len + 4);
|
||||
if (len < this.testStringBuffer.Capacity)
|
||||
{
|
||||
this.testStringBuffer.LengthUnsafe = len;
|
||||
this.testStringBuffer.StorageSpan[len] = default;
|
||||
}
|
||||
|
||||
if (this.useMinimumBuild)
|
||||
_ = this.privateAtlas?.BuildFontsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.useGlobalScale)
|
||||
ImGuiNative.igSetWindowFontScale(1);
|
||||
}
|
||||
|
||||
this.fontHandles ??=
|
||||
Enum.GetValues<GameFontFamilyAndSize>()
|
||||
.Where(x => x.GetAttribute<GameFontFamilyAndSizeAttribute>() is not null)
|
||||
|
|
@ -227,6 +270,8 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
|
|||
this.fontHandles?.Values.SelectMany(x => x.Where(y => y.Handle.IsValueCreated).Select(y => y.Handle.Value))
|
||||
.AggregateToDisposable().Dispose();
|
||||
this.fontHandles = null;
|
||||
this.fontDialogHandle?.Dispose();
|
||||
this.fontDialogHandle = null;
|
||||
this.privateAtlas?.Dispose();
|
||||
this.privateAtlas = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,11 +68,11 @@ internal class SettingsWindow : Window
|
|||
var interfaceManager = Service<InterfaceManager>.Get();
|
||||
var fontAtlasFactory = Service<FontAtlasFactory>.Get();
|
||||
|
||||
var rebuildFont = fontAtlasFactory.UseAxis != configuration.UseAxisFontsFromGame;
|
||||
var rebuildFont = !Equals(fontAtlasFactory.DefaultFontSpec, configuration.DefaultFontSpec);
|
||||
rebuildFont |= !Equals(ImGui.GetIO().FontGlobalScale, configuration.GlobalUiScale);
|
||||
|
||||
ImGui.GetIO().FontGlobalScale = configuration.GlobalUiScale;
|
||||
fontAtlasFactory.UseAxisOverride = null;
|
||||
fontAtlasFactory.DefaultFontSpecOverride = null;
|
||||
|
||||
if (rebuildFont)
|
||||
interfaceManager.RebuildFonts();
|
||||
|
|
|
|||
|
|
@ -5,9 +5,14 @@ using System.Text;
|
|||
|
||||
using CheapLoc;
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.FontIdentifier;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.ImGuiFontChooserDialog;
|
||||
using Dalamud.Interface.Internal.Windows.PluginInstaller;
|
||||
using Dalamud.Interface.Internal.Windows.Settings.Widgets;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
using Dalamud.Interface.ManagedFontAtlas.Internals;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -21,31 +26,19 @@ public class SettingsTabLook : SettingsTab
|
|||
{
|
||||
private static readonly (string, float)[] GlobalUiScalePresets =
|
||||
{
|
||||
("9.6pt##DalamudSettingsGlobalUiScaleReset96", 9.6f / InterfaceManager.DefaultFontSizePt),
|
||||
("12pt##DalamudSettingsGlobalUiScaleReset12", 12f / InterfaceManager.DefaultFontSizePt),
|
||||
("14pt##DalamudSettingsGlobalUiScaleReset14", 14f / InterfaceManager.DefaultFontSizePt),
|
||||
("18pt##DalamudSettingsGlobalUiScaleReset18", 18f / InterfaceManager.DefaultFontSizePt),
|
||||
("24pt##DalamudSettingsGlobalUiScaleReset24", 24f / InterfaceManager.DefaultFontSizePt),
|
||||
("36pt##DalamudSettingsGlobalUiScaleReset36", 36f / InterfaceManager.DefaultFontSizePt),
|
||||
("80%##DalamudSettingsGlobalUiScaleReset96", 0.8f),
|
||||
("100%##DalamudSettingsGlobalUiScaleReset12", 1f),
|
||||
("117%##DalamudSettingsGlobalUiScaleReset14", 14 / 12f),
|
||||
("150%##DalamudSettingsGlobalUiScaleReset18", 1.5f),
|
||||
("200%##DalamudSettingsGlobalUiScaleReset24", 2f),
|
||||
("300%##DalamudSettingsGlobalUiScaleReset36", 3f),
|
||||
};
|
||||
|
||||
private float globalUiScale;
|
||||
private IFontSpec defaultFontSpec = null!;
|
||||
|
||||
public override SettingsEntry[] Entries { get; } =
|
||||
{
|
||||
new GapSettingsEntry(5),
|
||||
|
||||
new SettingsEntry<bool>(
|
||||
Loc.Localize("DalamudSettingToggleAxisFonts", "Use AXIS fonts as default Dalamud font"),
|
||||
Loc.Localize("DalamudSettingToggleUiAxisFontsHint", "Use AXIS fonts (the game's main UI fonts) as default Dalamud font."),
|
||||
c => c.UseAxisFontsFromGame,
|
||||
(v, c) => c.UseAxisFontsFromGame = v,
|
||||
v =>
|
||||
{
|
||||
Service<FontAtlasFactory>.Get().UseAxisOverride = v;
|
||||
Service<InterfaceManager>.Get().RebuildFonts();
|
||||
}),
|
||||
|
||||
new GapSettingsEntry(5, true),
|
||||
|
||||
new ButtonSettingsEntry(
|
||||
|
|
@ -178,10 +171,10 @@ public class SettingsTabLook : SettingsTab
|
|||
}
|
||||
}
|
||||
|
||||
var globalUiScaleInPt = 12f * this.globalUiScale;
|
||||
if (ImGui.DragFloat("##DalamudSettingsGlobalUiScaleDrag", ref globalUiScaleInPt, 0.1f, 9.6f, 36f, "%.1fpt", ImGuiSliderFlags.AlwaysClamp))
|
||||
var globalUiScaleInPct = 100f * this.globalUiScale;
|
||||
if (ImGui.DragFloat("##DalamudSettingsGlobalUiScaleDrag", ref globalUiScaleInPct, 1f, 80f, 300f, "%.0f%%", ImGuiSliderFlags.AlwaysClamp))
|
||||
{
|
||||
this.globalUiScale = globalUiScaleInPt / 12f;
|
||||
this.globalUiScale = globalUiScaleInPct / 100f;
|
||||
ImGui.GetIO().FontGlobalScale = this.globalUiScale;
|
||||
interfaceManager.RebuildFonts();
|
||||
}
|
||||
|
|
@ -201,12 +194,53 @@ public class SettingsTabLook : SettingsTab
|
|||
}
|
||||
}
|
||||
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
|
||||
if (ImGui.Button(Loc.Localize("DalamudSettingChooseDefaultFont", "Choose Default Font")))
|
||||
{
|
||||
var faf = Service<FontAtlasFactory>.Get();
|
||||
var fcd = new SingleFontChooserDialog(
|
||||
faf.CreateFontAtlas($"{nameof(SettingsTabLook)}:Default", FontAtlasAutoRebuildMode.Async));
|
||||
fcd.SelectedFont = (SingleFontSpec)this.defaultFontSpec;
|
||||
fcd.FontFamilyExcludeFilter = x => x is DalamudDefaultFontAndFamilyId;
|
||||
interfaceManager.Draw += fcd.Draw;
|
||||
fcd.ResultTask.ContinueWith(
|
||||
r => Service<Framework>.Get().RunOnFrameworkThread(
|
||||
() =>
|
||||
{
|
||||
interfaceManager.Draw -= fcd.Draw;
|
||||
fcd.Dispose();
|
||||
|
||||
_ = r.Exception;
|
||||
if (!r.IsCompletedSuccessfully)
|
||||
return;
|
||||
|
||||
faf.DefaultFontSpecOverride = this.defaultFontSpec = r.Result;
|
||||
interfaceManager.RebuildFonts();
|
||||
}));
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
using (interfaceManager.MonoFontHandle?.Push())
|
||||
{
|
||||
if (ImGui.Button(Loc.Localize("DalamudSettingResetDefaultFont", "Reset Default Font")))
|
||||
{
|
||||
var faf = Service<FontAtlasFactory>.Get();
|
||||
faf.DefaultFontSpecOverride =
|
||||
this.defaultFontSpec =
|
||||
new SingleFontSpec { FontId = new GameFontAndFamilyId(GameFontFamily.Axis) };
|
||||
interfaceManager.RebuildFonts();
|
||||
}
|
||||
}
|
||||
|
||||
base.Draw();
|
||||
}
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
this.globalUiScale = Service<DalamudConfiguration>.Get().GlobalUiScale;
|
||||
this.defaultFontSpec = Service<FontAtlasFactory>.Get().DefaultFontSpec;
|
||||
|
||||
base.Load();
|
||||
}
|
||||
|
|
@ -214,6 +248,7 @@ public class SettingsTabLook : SettingsTab
|
|||
public override void Save()
|
||||
{
|
||||
Service<DalamudConfiguration>.Get().GlobalUiScale = this.globalUiScale;
|
||||
Service<DalamudConfiguration>.Get().DefaultFontSpec = this.defaultFontSpec;
|
||||
|
||||
base.Save();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ using ImGuiNET;
|
|||
namespace Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for <see cref="ImFontAtlasPtr"/>.
|
||||
/// Wrapper for <see cref="ImFontAtlasPtr"/>.<br />
|
||||
/// Not intended for plugins to implement.
|
||||
/// </summary>
|
||||
public interface IFontAtlas : IDisposable
|
||||
{
|
||||
|
|
@ -93,11 +94,15 @@ public interface IFontAtlas : IDisposable
|
|||
/// </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>
|
||||
/// <remarks>
|
||||
/// Consider calling <see cref="IFontAtlasBuildToolkitPreBuild.AttachExtraGlyphsForDalamudLanguage"/> to support
|
||||
/// glyphs that are not supplied by the game by default; this mostly affects Chinese and Korean language users.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <b>On initialization</b>:
|
||||
/// <code>
|
||||
/// this.fontHandle = atlas.NewDelegateFontHandle(e => e.OnPreBuild(tk => {
|
||||
/// var config = new SafeFontConfig { SizePx = 16 };
|
||||
/// var config = new SafeFontConfig { SizePx = UiBuilder.DefaultFontSizePx };
|
||||
/// config.MergeFont = tk.AddFontFromFile(@"C:\Windows\Fonts\comic.ttf", config);
|
||||
/// tk.AddGameSymbol(config);
|
||||
/// tk.AddExtraGlyphsForDalamudLanguage(config);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ using ImGuiNET;
|
|||
namespace Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
/// <summary>
|
||||
/// Common stuff for <see cref="IFontAtlasBuildToolkitPreBuild"/> and <see cref="IFontAtlasBuildToolkitPostBuild"/>.
|
||||
/// Common stuff for <see cref="IFontAtlasBuildToolkitPreBuild"/> and <see cref="IFontAtlasBuildToolkitPostBuild"/>.<br />
|
||||
/// Not intended for plugins to implement.
|
||||
/// </summary>
|
||||
public interface IFontAtlasBuildToolkit
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ using ImGuiNET;
|
|||
namespace Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
/// <summary>
|
||||
/// Toolkit for use when the build state is <see cref="FontAtlasBuildStep.PostBuild"/>.
|
||||
/// Toolkit for use when the build state is <see cref="FontAtlasBuildStep.PostBuild"/>.<br />
|
||||
/// Not intended for plugins to implement.
|
||||
/// </summary>
|
||||
public interface IFontAtlasBuildToolkitPostBuild : IFontAtlasBuildToolkit
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Interface.FontIdentifier;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Utility;
|
||||
|
||||
|
|
@ -10,6 +11,7 @@ namespace Dalamud.Interface.ManagedFontAtlas;
|
|||
|
||||
/// <summary>
|
||||
/// Toolkit for use when the build state is <see cref="FontAtlasBuildStep.PreBuild"/>.<br />
|
||||
/// Not intended for plugins to implement.<br />
|
||||
/// <br />
|
||||
/// After <see cref="FontAtlasBuildStepDelegate"/> returns,
|
||||
/// either <see cref="IFontAtlasBuildToolkit.Font"/> must be set,
|
||||
|
|
@ -52,6 +54,12 @@ public interface IFontAtlasBuildToolkitPreBuild : IFontAtlasBuildToolkit
|
|||
/// <returns>True if ignored.</returns>
|
||||
bool IsGlobalScaleIgnored(ImFontPtr fontPtr);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a function to be run after build.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to run.</param>
|
||||
void RegisterPostBuild(Action action);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a font from memory region allocated using <see cref="ImGuiHelpers.AllocateMemory"/>.<br />
|
||||
/// <b>It WILL crash if you try to use a memory pointer allocated in some other way.</b><br />
|
||||
|
|
@ -134,7 +142,12 @@ public interface IFontAtlasBuildToolkitPreBuild : IFontAtlasBuildToolkit
|
|||
/// As this involves adding multiple fonts, calling this function will set <see cref="IFontAtlasBuildToolkit.Font"/>
|
||||
/// as the return value of this function, if it was empty before.
|
||||
/// </summary>
|
||||
/// <param name="sizePx">Font size in pixels.</param>
|
||||
/// <param name="sizePx">
|
||||
/// Font size in pixels.
|
||||
/// If a negative value is supplied,
|
||||
/// (<see cref="UiBuilder.DefaultFontSpec"/>.<see cref="IFontSpec.SizePx"/> * <paramref name="sizePx"/>) will be
|
||||
/// used as the font size. Specify -1 to use the default font size.
|
||||
/// </param>
|
||||
/// <param name="glyphRanges">The glyph ranges. Use <see cref="FontAtlasBuildToolkitUtilities"/>.ToGlyphRange to build.</param>
|
||||
/// <returns>A font returned from <see cref="ImFontAtlasPtr.AddFont"/>.</returns>
|
||||
ImFontPtr AddDalamudDefaultFont(float sizePx, ushort[]? glyphRanges = null);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ using ImGuiNET;
|
|||
namespace Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a reference counting handle for fonts.
|
||||
/// Represents a reference counting handle for fonts.<br />
|
||||
/// Not intended for plugins to implement.
|
||||
/// </summary>
|
||||
public interface IFontHandle : IDisposable
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ namespace Dalamud.Interface.ManagedFontAtlas;
|
|||
|
||||
/// <summary>
|
||||
/// The wrapper for <see cref="ImFontPtr"/>, guaranteeing that the associated data will be available as long as
|
||||
/// this struct is not disposed.
|
||||
/// this struct is not disposed.<br />
|
||||
/// Not intended for plugins to implement.
|
||||
/// </summary>
|
||||
public interface ILockedImFont : IDisposable
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
|
|||
using System.Text.Unicode;
|
||||
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Interface.FontIdentifier;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Utility;
|
||||
|
|
@ -42,6 +43,7 @@ internal sealed partial class FontAtlasFactory
|
|||
private readonly GamePrebakedFontHandle.HandleSubstance gameFontHandleSubstance;
|
||||
private readonly FontAtlasFactory factory;
|
||||
private readonly FontAtlasBuiltData data;
|
||||
private readonly List<Action> registeredPostBuildActions = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BuildToolkit"/> class.
|
||||
|
|
@ -162,6 +164,9 @@ internal sealed partial class FontAtlasFactory
|
|||
/// <inheritdoc/>
|
||||
public int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError) =>
|
||||
this.data.AddNewTexture(textureWrap, disposeOnError);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RegisterPostBuild(Action action) => this.registeredPostBuildActions.Add(action);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public unsafe ImFontPtr AddFontFromImGuiHeapAllocatedMemory(
|
||||
|
|
@ -314,18 +319,32 @@ internal sealed partial class FontAtlasFactory
|
|||
/// <inheritdoc/>
|
||||
public ImFontPtr AddDalamudDefaultFont(float sizePx, ushort[]? glyphRanges)
|
||||
{
|
||||
ImFontPtr font;
|
||||
ImFontPtr font = default;
|
||||
glyphRanges ??= this.factory.DefaultGlyphRanges;
|
||||
if (this.factory.UseAxis)
|
||||
|
||||
var dfid = this.factory.DefaultFontSpec;
|
||||
if (sizePx < 0f)
|
||||
sizePx *= -dfid.SizePx;
|
||||
|
||||
if (dfid is SingleFontSpec sfs)
|
||||
{
|
||||
font = this.AddGameGlyphs(new(GameFontFamily.Axis, sizePx), glyphRanges, default);
|
||||
if (sfs.FontId is DalamudDefaultFontAndFamilyId)
|
||||
{
|
||||
// invalid; calling sfs.AddToBuildToolkit calls this function, causing infinite recursion
|
||||
}
|
||||
else
|
||||
{
|
||||
sfs = sfs with { SizePx = sizePx };
|
||||
font = sfs.AddToBuildToolkit(this);
|
||||
if (sfs.FontId is not GameFontAndFamilyId { GameFontFamily: GameFontFamily.Axis })
|
||||
this.AddGameSymbol(new() { SizePx = sizePx, MergeFont = font });
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (font.IsNull())
|
||||
{
|
||||
font = this.AddDalamudAssetFont(
|
||||
DalamudAsset.NotoSansJpMedium,
|
||||
new() { SizePx = sizePx, GlyphRanges = glyphRanges });
|
||||
this.AddGameSymbol(new() { SizePx = sizePx, MergeFont = font });
|
||||
// fall back to AXIS fonts
|
||||
font = this.AddGameGlyphs(new(GameFontFamily.Axis, sizePx), glyphRanges, default);
|
||||
}
|
||||
|
||||
this.AttachExtraGlyphsForDalamudLanguage(new() { SizePx = sizePx, MergeFont = font });
|
||||
|
|
@ -531,6 +550,13 @@ internal sealed partial class FontAtlasFactory
|
|||
substance.OnPostBuild(this);
|
||||
}
|
||||
|
||||
public void PostBuildCallbacks()
|
||||
{
|
||||
foreach (var ac in this.registeredPostBuildActions)
|
||||
ac.InvokeSafely();
|
||||
this.registeredPostBuildActions.Clear();
|
||||
}
|
||||
|
||||
public unsafe void UploadTextures()
|
||||
{
|
||||
var buf = Array.Empty<byte>();
|
||||
|
|
|
|||
|
|
@ -658,7 +658,7 @@ internal sealed partial class FontAtlasFactory
|
|||
toolkit = res.CreateToolkit(this.factory, isAsync);
|
||||
|
||||
// PreBuildSubstances deals with toolkit.Add... function family. Do this first.
|
||||
var defaultFont = toolkit.AddDalamudDefaultFont(InterfaceManager.DefaultFontSizePx, null);
|
||||
var defaultFont = toolkit.AddDalamudDefaultFont(-1, null);
|
||||
|
||||
this.BuildStepChange?.Invoke(toolkit);
|
||||
toolkit.PreBuildSubstances();
|
||||
|
|
@ -679,6 +679,7 @@ internal sealed partial class FontAtlasFactory
|
|||
|
||||
toolkit.PostBuild();
|
||||
toolkit.PostBuildSubstances();
|
||||
toolkit.PostBuildCallbacks();
|
||||
this.BuildStepChange?.Invoke(toolkit);
|
||||
|
||||
foreach (var font in toolkit.Fonts)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
|||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Interface.FontIdentifier;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Storage.Assets;
|
||||
|
|
@ -108,14 +109,29 @@ internal sealed partial class FontAtlasFactory
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to override configuration for UseAxis.
|
||||
/// Gets or sets a value indicating whether to override configuration for <see cref="DefaultFontSpec"/>.
|
||||
/// </summary>
|
||||
public bool? UseAxisOverride { get; set; } = null;
|
||||
public IFontSpec? DefaultFontSpecOverride { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether to use AXIS fonts.
|
||||
/// Gets the default font ID.
|
||||
/// </summary>
|
||||
public bool UseAxis => this.UseAxisOverride ?? Service<DalamudConfiguration>.Get().UseAxisFontsFromGame;
|
||||
public IFontSpec DefaultFontSpec =>
|
||||
this.DefaultFontSpecOverride
|
||||
?? Service<DalamudConfiguration>.Get().DefaultFontSpec
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
?? (Service<DalamudConfiguration>.Get().UseAxisFontsFromGame
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
? new()
|
||||
{
|
||||
FontId = new GameFontAndFamilyId(GameFontFamily.Axis),
|
||||
SizePx = InterfaceManager.DefaultFontSizePx,
|
||||
}
|
||||
: new SingleFontSpec
|
||||
{
|
||||
FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansJpMedium),
|
||||
SizePx = InterfaceManager.DefaultFontSizePx + 1,
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Gets the service instance of <see cref="Framework"/>.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public struct SafeFontConfig
|
|||
this.PixelSnapH = true;
|
||||
this.GlyphMaxAdvanceX = float.MaxValue;
|
||||
this.RasterizerMultiply = 1f;
|
||||
this.RasterizerGamma = 1.4f;
|
||||
this.RasterizerGamma = 1.7f;
|
||||
this.EllipsisChar = unchecked((char)-1);
|
||||
this.Raw.FontDataOwnedByAtlas = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Dalamud.Game;
|
|||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Interface.FontIdentifier;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Internal.ManagedAsserts;
|
||||
|
|
@ -173,12 +174,12 @@ public sealed class UiBuilder : IDisposable
|
|||
/// <summary>
|
||||
/// Gets the default Dalamud font size in points.
|
||||
/// </summary>
|
||||
public static float DefaultFontSizePt => InterfaceManager.DefaultFontSizePt;
|
||||
public static float DefaultFontSizePt => Service<FontAtlasFactory>.Get().DefaultFontSpec.SizePt;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default Dalamud font size in pixels.
|
||||
/// </summary>
|
||||
public static float DefaultFontSizePx => InterfaceManager.DefaultFontSizePx;
|
||||
public static float DefaultFontSizePx => Service<FontAtlasFactory>.Get().DefaultFontSpec.SizePx;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default Dalamud font - supporting all game languages and icons.<br />
|
||||
|
|
@ -198,6 +199,11 @@ public sealed class UiBuilder : IDisposable
|
|||
/// </summary>
|
||||
public static ImFontPtr MonoFont => InterfaceManager.MonoFont;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default font specifications.
|
||||
/// </summary>
|
||||
public IFontSpec DefaultFontSpec => Service<FontAtlasFactory>.Get().DefaultFontSpec;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the handle to the default Dalamud font - supporting all game languages and icons.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using System.Numerics;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.Unicode;
|
||||
|
||||
using Dalamud.Configuration.Internal;
|
||||
|
|
@ -543,6 +544,24 @@ public static class ImGuiHelpers
|
|||
var pageIndex = unchecked((ushort)(codepoint / 4096));
|
||||
font.NativePtr->Used4kPagesMap[pageIndex >> 3] |= unchecked((byte)(1 << (pageIndex & 7)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the text for a text input, during the callback.
|
||||
/// </summary>
|
||||
/// <param name="data">The callback data.</param>
|
||||
/// <param name="s">The new text.</param>
|
||||
internal static unsafe void SetTextFromCallback(ImGuiInputTextCallbackData* data, string s)
|
||||
{
|
||||
if (data->BufTextLen != 0)
|
||||
ImGuiNative.ImGuiInputTextCallbackData_DeleteChars(data, 0, data->BufTextLen);
|
||||
|
||||
var len = Encoding.UTF8.GetByteCount(s);
|
||||
var buf = len < 1024 ? stackalloc byte[len] : new byte[len];
|
||||
Encoding.UTF8.GetBytes(s, buf);
|
||||
fixed (byte* pBuf = buf)
|
||||
ImGuiNative.ImGuiInputTextCallbackData_InsertChars(data, 0, pBuf, pBuf + len);
|
||||
ImGuiNative.ImGuiInputTextCallbackData_SelectAll(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the corresponding ImGui viewport ID for the given window handle.
|
||||
|
|
|
|||
|
|
@ -97,4 +97,76 @@ internal static class ArrayExtensions
|
|||
/// <returns><paramref name="array"/> casted as a <see cref="IReadOnlyCollection{T}"/> if it is one; otherwise the result of <see cref="Enumerable.ToArray{TSource}"/>.</returns>
|
||||
public static IReadOnlyCollection<T> AsReadOnlyCollection<T>(this IEnumerable<T> array) =>
|
||||
array as IReadOnlyCollection<T> ?? array.ToArray();
|
||||
|
||||
/// <inheritdoc cref="List{T}.FindIndex(System.Predicate{T})"/>
|
||||
public static int FindIndex<T>(this IReadOnlyList<T> list, Predicate<T> match)
|
||||
=> list.FindIndex(0, list.Count, match);
|
||||
|
||||
/// <inheritdoc cref="List{T}.FindIndex(int,System.Predicate{T})"/>
|
||||
public static int FindIndex<T>(this IReadOnlyList<T> list, int startIndex, Predicate<T> match)
|
||||
=> list.FindIndex(startIndex, list.Count - startIndex, match);
|
||||
|
||||
/// <inheritdoc cref="List{T}.FindIndex(int,int,System.Predicate{T})"/>
|
||||
public static int FindIndex<T>(this IReadOnlyList<T> list, int startIndex, int count, Predicate<T> match)
|
||||
{
|
||||
if ((uint)startIndex > (uint)list.Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex, null);
|
||||
|
||||
if (count < 0 || startIndex > list.Count - count)
|
||||
throw new ArgumentOutOfRangeException(nameof(count), count, null);
|
||||
|
||||
if (match == null)
|
||||
throw new ArgumentNullException(nameof(match));
|
||||
|
||||
var endIndex = startIndex + count;
|
||||
for (var i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
if (match(list[i])) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="List{T}.FindLastIndex(System.Predicate{T})"/>
|
||||
public static int FindLastIndex<T>(this IReadOnlyList<T> list, Predicate<T> match)
|
||||
=> list.FindLastIndex(list.Count - 1, list.Count, match);
|
||||
|
||||
/// <inheritdoc cref="List{T}.FindLastIndex(int,System.Predicate{T})"/>
|
||||
public static int FindLastIndex<T>(this IReadOnlyList<T> list, int startIndex, Predicate<T> match)
|
||||
=> list.FindLastIndex(startIndex, startIndex + 1, match);
|
||||
|
||||
/// <inheritdoc cref="List{T}.FindLastIndex(int,int,System.Predicate{T})"/>
|
||||
public static int FindLastIndex<T>(this IReadOnlyList<T> list, int startIndex, int count, Predicate<T> match)
|
||||
{
|
||||
if (match == null)
|
||||
throw new ArgumentNullException(nameof(match));
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
// Special case for 0 length List
|
||||
if (startIndex != -1)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure we're not out of range
|
||||
if ((uint)startIndex >= (uint)list.Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex, null);
|
||||
}
|
||||
|
||||
// 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
|
||||
if (count < 0 || startIndex - count + 1 < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count), count, null);
|
||||
|
||||
var endIndex = startIndex - count;
|
||||
for (var i = startIndex; i > endIndex; i--)
|
||||
{
|
||||
if (match(list[i]))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ using Dalamud.Logging.Internal;
|
|||
using ImGuiNET;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Serilog;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
|
||||
namespace Dalamud.Utility;
|
||||
|
|
@ -684,6 +687,16 @@ public static class Util
|
|||
return names.ElementAt(rng.Next(0, names.Count() - 1)).Singular.RawString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws a corresponding exception if <see cref="HRESULT.FAILED"/> is true.
|
||||
/// </summary>
|
||||
/// <param name="hr">The result value.</param>
|
||||
internal static void ThrowOnError(this HRESULT hr)
|
||||
{
|
||||
if (hr.FAILED)
|
||||
Marshal.ThrowExceptionForHR(hr.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Print formatted GameObject Information to ImGui.
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue