feat(settings): bring back three settings that had no way to reach them

All three drive real behaviour and have translated labels in all 25 language
files. None of them had a control anywhere in the UI -- they were lost in the
v1.6.0 window rewrite and nobody noticed, because the config kept working with
whatever value happened to be stored.

Language picker. This is the one that needed care: a combo alone would have
been wrong twice over. LanguageChanged is an instance method, not static, and
its parameter only matters when the override is None -- it reads the config
itself otherwise. Passing picked.Code() there yields "", so "follow Dalamud"
would have silently meant English. Startup gets this right and is copied.

More importantly the glyph ranges. Their activation used to live in
Settings.Apply, a class that has not existed since v1.6.0; the comment at
Plugin.cs:290 still points at it. Without OR-ing the required range in and
rebuilding the font atlas, switching to Korean renders empty boxes until the
plugin reloads. Four steps, in this order, and the order is forced: the culture
switch reads the config, the atlas rebuild reads the glyph ranges.

Sound toggle. Gates both the per-tab notification sounds and the UI click
sound. It even has a self-test, just no switch.

Keybind mode. Strict versus flexible modifier matching. It has Name() and
Tooltip() per value, so it was demonstrably a control in v1.5.6.

The language order is sorted once into a static: 24 of the 25 endonyms are
fixed literals, so the order does not depend on the current culture, and
recomputing it per frame would make the list jump the moment None's own label
changes.

"Show novice network" moves to Behaviour on the way past. It is a display
filter, and it was the only entry under "Notifications" -- which now holds the
sound toggle it was named for.
This commit is contained in:
2026-08-18 11:20:52 +02:00
parent 81b68a1681
commit 9a31bbba03
2 changed files with 100 additions and 5 deletions
+2 -1
View File
@@ -196,7 +196,8 @@ internal static class PluginHostFactory
sp.GetRequiredService<Ui.Components.Settings.ChatColourPicker>()
));
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.GeneralTab(
sp.GetRequiredService<Plugin>()
sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<FontManager>()
));
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.ChatTab(
sp.GetRequiredService<Plugin>()
@@ -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<LanguageOverride>()
.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<KeybindMode>();
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<bool> get, Action<bool> set)
{
var current = get();