diff --git a/HellionChat/Ui/Components/Settings/Tabs/ChatTab.cs b/HellionChat/Ui/Components/Settings/Tabs/ChatTab.cs index 3081efb..68fdf4e 100644 --- a/HellionChat/Ui/Components/Settings/Tabs/ChatTab.cs +++ b/HellionChat/Ui/Components/Settings/Tabs/ChatTab.cs @@ -1,5 +1,4 @@ using Dalamud.Bindings.ImGui; -using HellionChat.Code; using HellionChat.Resources; using HellionChat.Util; diff --git a/HellionChat/Ui/Components/Settings/Tabs/GeneralTab.cs b/HellionChat/Ui/Components/Settings/Tabs/GeneralTab.cs index feab47d..36b15de 100644 --- a/HellionChat/Ui/Components/Settings/Tabs/GeneralTab.cs +++ b/HellionChat/Ui/Components/Settings/Tabs/GeneralTab.cs @@ -9,9 +9,10 @@ internal sealed class GeneralTab private readonly Plugin _plugin; private readonly FontManager _fonts; - // 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. + // Sorted once: the 25 endonyms are fixed literals, so the order never + // changes, and rebuilding it per frame costs an Enum.GetValues plus the + // whole LINQ chain. None is pinned to the front rather than sorted, so its + // localised label never affects the ordering. private static readonly LanguageOverride[] LanguageOrder = BuildLanguageOrder(); public GeneralTab(Plugin plugin, FontManager fonts) @@ -71,13 +72,11 @@ internal sealed class GeneralTab if (ImGui.CollapsingHeader("Notifications", ImGuiTreeNodeFlags.DefaultOpen)) { DrawToggle( - "Play sounds", + Language.Options_PlaySounds_Name, () => Plugin.Config.PlaySounds, v => Plugin.Config.PlaySounds = v ); - ImGuiUtil.HelpMarker( - "Gates both the per-tab notification sounds and the UI click sound." - ); + ImGuiUtil.HelpMarker(Language.Options_PlaySounds_Description); } if (ImGui.CollapsingHeader("Volumes", ImGuiTreeNodeFlags.DefaultOpen)) @@ -113,11 +112,19 @@ internal sealed class GeneralTab ImGuiUtil.HelpMarker( string.Format(Language.Options_Language_Description, Plugin.PluginName) + + "\n\nSwitching rebuilds the font atlas, so the chat window goes blank" + + " for a moment. Scripts a language needs stay enabled afterwards;" + + " clear them under Appearance -> Fonts -> Extra glyphs." ); } private void ApplyLanguage(LanguageOverride picked) { + // Combo only reports real changes, but a rebuild is expensive enough + // that a future caller should not be able to trigger a no-op one. + if (picked == Plugin.Config.LanguageOverride) + return; + Plugin.Config.LanguageOverride = picked; var required = picked.RequiredGlyphRanges(); @@ -133,7 +140,17 @@ internal sealed class GeneralTab _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. + // the OR above. + // + // This runs inside Plugin.Draw's open font push (Plugin.cs:1105) and + // disposes the very handle that is on the ImGui font stack. It is safe + // because Dalamud keeps the ImFont alive under a per-frame lock until + // the frame ends -- not merely because we are on the draw thread, which + // is what the old comment claimed. + // + // The rebuild is asynchronous: FontsReady goes false until the atlas is + // done, and every chat component returns early meanwhile. For CJK that + // is visible as a blank chat window for a moment. _fonts.RebuildDelegateFonts(); } @@ -147,7 +164,7 @@ internal sealed class GeneralTab labels[i] = values[i].Name(); ImGui.SetNextItemWidth(200f); - if (ImGui.Combo("Modifier matching", ref selected, labels, labels.Length)) + if (ImGui.Combo(Language.Options_KeybindMode_Name, ref selected, labels, labels.Length)) { if (selected >= 0 && selected < values.Length) { diff --git a/HellionChat/Ui/Windows/SettingsWindow.cs b/HellionChat/Ui/Windows/SettingsWindow.cs index b7b8537..543b2e1 100644 --- a/HellionChat/Ui/Windows/SettingsWindow.cs +++ b/HellionChat/Ui/Windows/SettingsWindow.cs @@ -75,6 +75,16 @@ internal sealed class SettingsWindow : Window DisableWindowSounds = true; } + // The title is baked in at construction, so it kept the language the plugin + // started in even after the user switched. Everything else re-reads its + // strings per draw; this was the one frozen string. + public override void PreDraw() + { + var wanted = $"{Language.Settings_Title.Format(Plugin.PluginName)}###chat2-settings"; + if (!string.Equals(WindowName, wanted, StringComparison.Ordinal)) + WindowName = wanted; + } + public override void Draw() { _sidebar.Draw();