diff --git a/HellionChat/PluginHostFactory.cs b/HellionChat/PluginHostFactory.cs index 5960b90..f67d683 100644 --- a/HellionChat/PluginHostFactory.cs +++ b/HellionChat/PluginHostFactory.cs @@ -196,7 +196,8 @@ internal static class PluginHostFactory sp.GetRequiredService() )); services.AddSingleton(sp => new Ui.Components.Settings.Tabs.GeneralTab( - sp.GetRequiredService() + sp.GetRequiredService(), + sp.GetRequiredService() )); services.AddSingleton(sp => new Ui.Components.Settings.Tabs.ChatTab( sp.GetRequiredService() diff --git a/HellionChat/Ui/Components/Settings/Tabs/GeneralTab.cs b/HellionChat/Ui/Components/Settings/Tabs/GeneralTab.cs index e82f597..feab47d 100644 --- a/HellionChat/Ui/Components/Settings/Tabs/GeneralTab.cs +++ b/HellionChat/Ui/Components/Settings/Tabs/GeneralTab.cs @@ -1,4 +1,5 @@ using Dalamud.Bindings.ImGui; +using HellionChat.Resources; using HellionChat.Util; namespace HellionChat.Ui.Components.Settings.Tabs; @@ -6,10 +7,25 @@ namespace HellionChat.Ui.Components.Settings.Tabs; internal sealed class GeneralTab { private readonly Plugin _plugin; + private readonly FontManager _fonts; - public GeneralTab(Plugin plugin) + // Endonyms are fixed literals for 24 of the 25 entries, so the order is + // language-independent and worth sorting once. Recomputing per frame would + // also make the list jump the moment None's own label changes culture. + private static readonly LanguageOverride[] LanguageOrder = BuildLanguageOrder(); + + public GeneralTab(Plugin plugin, FontManager fonts) { _plugin = plugin; + _fonts = fonts; + } + + private static LanguageOverride[] BuildLanguageOrder() + { + var all = Enum.GetValues() + .Where(l => l != LanguageOverride.None) + .OrderBy(l => l.Name(), StringComparer.CurrentCulture); + return new[] { LanguageOverride.None }.Concat(all).ToArray(); } public void Draw() @@ -26,6 +42,12 @@ internal sealed class GeneralTab () => Plugin.Config.PrintChangelog, v => Plugin.Config.PrintChangelog = v ); + DrawToggle( + "Show novice network", + () => Plugin.Config.ShowNoviceNetwork, + v => Plugin.Config.ShowNoviceNetwork = v + ); + DrawLanguagePicker(); } if (ImGui.CollapsingHeader("Keybinds", ImGuiTreeNodeFlags.DefaultOpen)) @@ -43,14 +65,18 @@ internal sealed class GeneralTab () => Plugin.Config.ChatTabBackward, v => Plugin.Config.ChatTabBackward = v ); + DrawKeybindModePicker(); } if (ImGui.CollapsingHeader("Notifications", ImGuiTreeNodeFlags.DefaultOpen)) { DrawToggle( - "Show novice network", - () => Plugin.Config.ShowNoviceNetwork, - v => Plugin.Config.ShowNoviceNetwork = v + "Play sounds", + () => Plugin.Config.PlaySounds, + v => Plugin.Config.PlaySounds = v + ); + ImGuiUtil.HelpMarker( + "Gates both the per-tab notification sounds and the UI click sound." ); } @@ -66,6 +92,74 @@ internal sealed class GeneralTab } } + // Four steps, all of them required. The glyph-range activation used to live + // in Settings.Apply, which has not existed since the v1.6.0 rewrite -- see + // the comment at Plugin.cs:290. Without steps 2 and 4 a switch to Korean + // renders empty boxes until the plugin reloads. + private void DrawLanguagePicker() + { + var current = Plugin.Config.LanguageOverride; + var selected = Array.IndexOf(LanguageOrder, current); + var labels = new string[LanguageOrder.Length]; + for (var i = 0; i < LanguageOrder.Length; i++) + labels[i] = LanguageOrder[i].Name(); + + ImGui.SetNextItemWidth(200f); + if (ImGui.Combo(Language.Options_Language_Name, ref selected, labels, labels.Length)) + { + if (selected >= 0 && selected < LanguageOrder.Length) + ApplyLanguage(LanguageOrder[selected]); + } + + ImGuiUtil.HelpMarker( + string.Format(Language.Options_Language_Description, Plugin.PluginName) + ); + } + + private void ApplyLanguage(LanguageOverride picked) + { + Plugin.Config.LanguageOverride = picked; + + var required = picked.RequiredGlyphRanges(); + if (required != 0 && !Plugin.Config.ExtraGlyphRanges.HasFlag(required)) + Plugin.Config.ExtraGlyphRanges |= required; + + _plugin.SaveConfig(); + + // Instance method, and the argument only matters when the override is + // None: LanguageChanged reads the config itself and falls back to the + // parameter only in that case. Passing picked.Code() there yields "", + // so "follow Dalamud" would silently mean English. + _plugin.LanguageChanged(Plugin.Interface.UiLanguage); + + // Reads Config.ExtraGlyphRanges via SetUpRanges, so it has to come after + // the OR above. Same thread as every font push, no marshal needed. + _fonts.RebuildDelegateFonts(); + } + + private void DrawKeybindModePicker() + { + var values = Enum.GetValues(); + var current = Plugin.Config.KeybindMode; + var selected = Array.IndexOf(values, current); + var labels = new string[values.Length]; + for (var i = 0; i < values.Length; i++) + labels[i] = values[i].Name(); + + ImGui.SetNextItemWidth(200f); + if (ImGui.Combo("Modifier matching", ref selected, labels, labels.Length)) + { + if (selected >= 0 && selected < values.Length) + { + Plugin.Config.KeybindMode = values[selected]; + _plugin.SaveConfig(); + } + } + + if (Plugin.Config.KeybindMode.Tooltip() is { } tip) + ImGuiUtil.HelpMarker(tip); + } + private void DrawToggle(string label, Func get, Action set) { var current = get();