From 18834cddae3715f9d527129d1196eb57c374b572 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Mon, 15 Jun 2026 18:41:03 +0200 Subject: [PATCH] feat(fonts): restore the font-selection UI in the appearance tab --- HellionChat/PluginHostFactory.cs | 7 +- .../Ui/Components/Settings/FontsSection.cs | 206 ++++++++++++++++++ .../Components/Settings/Tabs/AppearanceTab.cs | 7 +- 3 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 HellionChat/Ui/Components/Settings/FontsSection.cs diff --git a/HellionChat/PluginHostFactory.cs b/HellionChat/PluginHostFactory.cs index 9d53904..09186f2 100644 --- a/HellionChat/PluginHostFactory.cs +++ b/HellionChat/PluginHostFactory.cs @@ -173,11 +173,16 @@ internal static class PluginHostFactory sp.GetRequiredService(), sp.GetRequiredService>() )); + services.AddSingleton(sp => new Ui.Components.Settings.FontsSection( + sp.GetRequiredService(), + sp.GetRequiredService() + )); services.AddSingleton(sp => new Ui.Components.Settings.Tabs.AppearanceTab( sp.GetRequiredService(), sp.GetRequiredService(), sp.GetRequiredService(), - sp.GetRequiredService() + sp.GetRequiredService(), + sp.GetRequiredService() )); services.AddSingleton(sp => new Ui.Components.Settings.Tabs.GeneralTab( sp.GetRequiredService() diff --git a/HellionChat/Ui/Components/Settings/FontsSection.cs b/HellionChat/Ui/Components/Settings/FontsSection.cs new file mode 100644 index 0000000..ce6c604 --- /dev/null +++ b/HellionChat/Ui/Components/Settings/FontsSection.cs @@ -0,0 +1,206 @@ +using Dalamud; +using Dalamud.Bindings.ImGui; +using Dalamud.Interface.FontIdentifier; +using HellionChat.Resources; +using HellionChat.Util; + +namespace HellionChat.Ui.Components.Settings; + +// Restores the 1.5.6 font-selection UI (1d3b429:Appearance.cs:249-405): pick the +// bundled Hellion font vs a custom global/Japanese/italic font, sizes, and extra +// glyph ranges. v1.6.0 saves live, so any change persists and rebuilds the atlas +// at once (RebuildDelegateFonts, unconditional — a face change keeps the size, so +// the size-gated IfChanged path would miss it). +internal sealed class FontsSection +{ + private readonly Plugin _plugin; + private readonly FontManager _fontManager; + + public FontsSection(Plugin plugin, FontManager fontManager) + { + _plugin = plugin; + _fontManager = fontManager; + } + + private void Apply() + { + _plugin.SaveConfig(); + _fontManager.RebuildDelegateFonts(); + } + + public void Draw() + { + if (!ImGui.CollapsingHeader(HellionStrings.Settings_Section_Fonts)) + return; + + // Readout so the user can see which font is actually active. + var active = + Plugin.Config.UseHellionFont ? "Hellion Inter (bundled)" + : Plugin.Config.FontsEnabled + ? $"Global: {Plugin.Config.GlobalFontV2.FontId.Family.EnglishName}" + : "FFXIV game font"; + ImGui.TextDisabled($"Active: {active}"); + ImGui.Spacing(); + + if ( + ImGui.Checkbox( + HellionStrings.Theme_UseHellionFont_Name, + ref Plugin.Config.UseHellionFont + ) + ) + { + if (Plugin.Config.UseHellionFont) + Plugin.Config.FontsEnabled = false; + Apply(); + } + ImGuiUtil.HelpMarker(HellionStrings.Theme_UseHellionFont_Description); + ImGui.Spacing(); + + if (Plugin.Config.UseHellionFont) + { + DrawSizeCombo(Language.Options_FontSize_Name, ref Plugin.Config.FontSizeV2); + ImGui.Spacing(); + } + else if (ImGui.Checkbox(Language.Options_FontsEnabled, ref Plugin.Config.FontsEnabled)) + { + Apply(); + } + + var unused = false; + if (!Plugin.Config.UseHellionFont && !Plugin.Config.FontsEnabled) + { + DrawSizeCombo(Language.Options_FontSize_Name, ref Plugin.Config.FontSizeV2); + } + else if (!Plugin.Config.UseHellionFont) + { + DrawFontChooser( + Language.Options_Font_Name, + Plugin.Config.GlobalFontV2, + false, + ref unused, + spec => Plugin.Config.GlobalFontV2 = spec, + () => Plugin.Config.GlobalFontV2 = DefaultFont(DalamudAsset.NotoSansCjkRegular), + "global" + ); + ImGuiUtil.HelpMarker( + string.Format(Language.Options_Font_Description, Plugin.PluginName) + ); + ImGuiUtil.WarningText(Language.Options_Font_Warning); + ImGui.Spacing(); + + DrawFontChooser( + Language.Options_JapaneseFont_Name, + Plugin.Config.JapaneseFontV2, + false, + ref unused, + spec => Plugin.Config.JapaneseFontV2 = spec, + () => Plugin.Config.JapaneseFontV2 = DefaultFont(DalamudAsset.NotoSansCjkMedium), + "japanese", + id => !id.LocaleNames?.ContainsKey("ja-jp") ?? false, + "いろはにほへと ちりぬるを" + ); + ImGuiUtil.HelpMarker( + string.Format(Language.Options_JapaneseFont_Description, Plugin.PluginName) + ); + ImGui.Spacing(); + + DrawFontChooser( + Language.Options_ItalicFont_Name, + Plugin.Config.ItalicFontV2, + true, + ref Plugin.Config.ItalicEnabled, + spec => Plugin.Config.ItalicFontV2 = spec, + () => + { + Plugin.Config.ItalicEnabled = false; + Plugin.Config.ItalicFontV2 = DefaultFont(DalamudAsset.NotoSansCjkRegular); + }, + "italic" + ); + ImGuiUtil.HelpMarker( + string.Format(Language.Options_Italic_Description, Plugin.PluginName) + ); + ImGui.Spacing(); + } + + // ExtraGlyphRanges stays reachable regardless of the font source so the + // user can verify/override the per-language auto-activation (v1.5.3 note). + ImGui.Spacing(); + if (ImGui.CollapsingHeader(Language.Options_ExtraGlyphs_Name)) + { + ImGuiUtil.HelpMarker( + string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName) + ); + + var range = (int)Plugin.Config.ExtraGlyphRanges; + var changed = false; + foreach (var extra in Enum.GetValues()) + changed |= ImGui.CheckboxFlags(extra.Name(), ref range, (int)extra); + + if (changed) + { + Plugin.Config.ExtraGlyphRanges = (ExtraGlyphRanges)range; + Apply(); + } + } + + DrawSizeCombo(Language.Options_SymbolsFontSize_Name, ref Plugin.Config.SymbolsFontSizeV2); + ImGuiUtil.HelpMarker(Language.Options_SymbolsFontSize_Description); + ImGui.Spacing(); + } + + private void DrawSizeCombo(string label, ref float size) + { + var before = size; + ImGuiUtil.FontSizeCombo(label, ref size); + if (!size.Equals(before)) + Apply(); + } + + private void DrawFontChooser( + string label, + SingleFontSpec font, + bool checkbox, + ref bool checkboxValue, + Action set, + Action reset, + string resetId, + Predicate? exclusion = null, + string? preview = null + ) + { + var prevCheckbox = checkboxValue; + var chooser = ImGuiUtil.FontChooser( + label, + font, + checkbox, + ref checkboxValue, + exclusion, + preview + ); + if (checkbox && checkboxValue != prevCheckbox) + Apply(); + + // The chooser dialog resolves on a worker thread; marshal the result back + // onto the framework thread before touching config + the font atlas. + chooser?.ResultTask.ContinueWith(r => + { + if (r.IsCompletedSuccessfully) + Plugin.Framework.Run(() => + { + set(r.Result); + Apply(); + }); + }); + + ImGui.SameLine(); + if (ImGui.Button($"Reset##{resetId}")) + { + reset(); + Apply(); + } + } + + private static SingleFontSpec DefaultFont(DalamudAsset asset) => + new() { FontId = new DalamudAssetFontAndFamilyId(asset), SizePt = 12.75f }; +} diff --git a/HellionChat/Ui/Components/Settings/Tabs/AppearanceTab.cs b/HellionChat/Ui/Components/Settings/Tabs/AppearanceTab.cs index 1782df3..5797e79 100644 --- a/HellionChat/Ui/Components/Settings/Tabs/AppearanceTab.cs +++ b/HellionChat/Ui/Components/Settings/Tabs/AppearanceTab.cs @@ -11,18 +11,21 @@ internal sealed class AppearanceTab private readonly ColorPicker _color; private readonly LivePreviewPanel _preview; private readonly ThemeImportExportRow _importExport; + private readonly FontsSection _fonts; public AppearanceTab( ThemePicker picker, ColorPicker color, LivePreviewPanel preview, - ThemeImportExportRow importExport + ThemeImportExportRow importExport, + FontsSection fonts ) { _picker = picker; _color = color; _preview = preview; _importExport = importExport; + _fonts = fonts; } public void Draw() @@ -38,6 +41,8 @@ internal sealed class AppearanceTab ImGui.Spacing(); _importExport.Draw(); ImGui.Separator(); + _fonts.Draw(); + ImGui.Separator(); _color.Draw(); } }