refactor(settings): rebuild the per-tab panel into sub-sections
This commit is contained in:
@@ -497,4 +497,12 @@ internal class HellionStrings
|
||||
internal static string Settings_Section_Hide => Get(nameof(Settings_Section_Hide));
|
||||
internal static string Settings_Section_InactivityHide => Get(nameof(Settings_Section_InactivityHide));
|
||||
internal static string Settings_Section_Frame => Get(nameof(Settings_Section_Frame));
|
||||
|
||||
// v1.5.6: Tabs tab per-tab-item sub-section titles (R6)
|
||||
internal static string Settings_Section_Tab_Channels => Get(nameof(Settings_Section_Tab_Channels));
|
||||
internal static string Settings_Section_Tab_Display => Get(nameof(Settings_Section_Tab_Display));
|
||||
internal static string Settings_Section_Tab_Notification => Get(nameof(Settings_Section_Tab_Notification));
|
||||
internal static string Settings_Section_Tab_Input => Get(nameof(Settings_Section_Tab_Input));
|
||||
internal static string Settings_Section_Tab_PopOut => Get(nameof(Settings_Section_Tab_PopOut));
|
||||
internal static string Settings_Section_Tab_Volume_AllTabsHint => Get(nameof(Settings_Section_Tab_Volume_AllTabsHint));
|
||||
}
|
||||
|
||||
@@ -1158,4 +1158,24 @@
|
||||
<data name="Settings_Section_Frame" xml:space="preserve">
|
||||
<value>Frame</value>
|
||||
</data>
|
||||
|
||||
<!-- v1.5.6: Tabs tab per-tab-item sub-section titles (R6) -->
|
||||
<data name="Settings_Section_Tab_Channels" xml:space="preserve">
|
||||
<value>Channels</value>
|
||||
</data>
|
||||
<data name="Settings_Section_Tab_Display" xml:space="preserve">
|
||||
<value>Display</value>
|
||||
</data>
|
||||
<data name="Settings_Section_Tab_Notification" xml:space="preserve">
|
||||
<value>Notification</value>
|
||||
</data>
|
||||
<data name="Settings_Section_Tab_Input" xml:space="preserve">
|
||||
<value>Input</value>
|
||||
</data>
|
||||
<data name="Settings_Section_Tab_PopOut" xml:space="preserve">
|
||||
<value>Pop-out window</value>
|
||||
</data>
|
||||
<data name="Settings_Section_Tab_Volume_AllTabsHint" xml:space="preserve">
|
||||
<value>This volume applies to all tabs.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -72,6 +72,13 @@ internal sealed class Tabs : ISettingsTab
|
||||
{
|
||||
var tab = Mutable.Tabs[i];
|
||||
|
||||
// Sub-sections (Channels/Display/Notification/Input/Pop-out) are inlined into
|
||||
// this loop body rather than extracted to helpers, because each one closes over
|
||||
// the per-iteration `i` and `tab` state. Extraction would mean passing both
|
||||
// into every helper without meaningful encapsulation gain.
|
||||
|
||||
// ToOpen controls which tab-item TreeNode is open (e.g. after add/move).
|
||||
// This is the outer level — not touched by sectionJustEntered.
|
||||
if (doOpens)
|
||||
ImGui.SetNextItemOpen(i == ToOpen);
|
||||
|
||||
@@ -117,6 +124,7 @@ internal sealed class Tabs : ISettingsTab
|
||||
ToOpen = i + 1;
|
||||
}
|
||||
|
||||
// Name and Icon are always visible — no sub-section collapse for these.
|
||||
ImGui.InputText(
|
||||
Language.Options_Tabs_Name,
|
||||
ref tab.Name,
|
||||
@@ -165,7 +173,73 @@ internal sealed class Tabs : ISettingsTab
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Spacing();
|
||||
|
||||
// ── Sub-section: Channels ─────────────────────────────────────────
|
||||
// First because it answers "what does this tab collect?" — most important.
|
||||
if (sectionJustEntered) ImGui.SetNextItemOpen(false);
|
||||
using (var secChannels = ImRaii.TreeNode(HellionStrings.Settings_Section_Tab_Channels + $"##sec-channels-{i}"))
|
||||
{
|
||||
if (secChannels.Success)
|
||||
{
|
||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
||||
ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, tab.SelectedChannels);
|
||||
ImGuiUtil.ExtraChatSelector(
|
||||
Language.Options_Tabs_ExtraChatChannels,
|
||||
ref tab.ExtraChatAll,
|
||||
tab.ExtraChatChannels
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Spacing();
|
||||
|
||||
// ── Sub-section: Display ──────────────────────────────────────────
|
||||
if (sectionJustEntered) ImGui.SetNextItemOpen(false);
|
||||
using (var secDisplay = ImRaii.TreeNode(HellionStrings.Settings_Section_Tab_Display + $"##sec-display-{i}"))
|
||||
{
|
||||
if (secDisplay.Success)
|
||||
{
|
||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
||||
|
||||
ImGui.Checkbox(Language.Options_Tabs_ShowTimestamps, ref tab.DisplayTimestamp);
|
||||
|
||||
using (
|
||||
var combo = ImGuiUtil.BeginComboVertical(
|
||||
Language.Options_Tabs_UnreadMode,
|
||||
tab.UnreadMode.Name()
|
||||
)
|
||||
)
|
||||
{
|
||||
if (combo.Success)
|
||||
{
|
||||
foreach (var mode in Enum.GetValues<UnreadMode>())
|
||||
{
|
||||
if (ImGui.Selectable(mode.Name(), tab.UnreadMode == mode))
|
||||
tab.UnreadMode = mode;
|
||||
|
||||
if (mode.Tooltip() is { } tooltip && ImGui.IsItemHovered())
|
||||
ImGuiUtil.Tooltip(tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only relevant when the global hide-when-inactive is on.
|
||||
if (Mutable.HideWhenInactive)
|
||||
ImGui.Checkbox(Language.Options_Tabs_InactivityBehaviour, ref tab.UnhideOnActivity);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Spacing();
|
||||
|
||||
// ── Sub-section: Notification ─────────────────────────────────────
|
||||
if (sectionJustEntered) ImGui.SetNextItemOpen(false);
|
||||
using (var secNotif = ImRaii.TreeNode(HellionStrings.Settings_Section_Tab_Notification + $"##sec-notif-{i}"))
|
||||
{
|
||||
if (secNotif.Success)
|
||||
{
|
||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
||||
|
||||
ImGui.Checkbox(
|
||||
HellionStrings.Tabs_NotificationSound_Enable_Name,
|
||||
ref tab.EnableNotificationSound
|
||||
@@ -173,7 +247,7 @@ internal sealed class Tabs : ISettingsTab
|
||||
ImGuiUtil.HelpMarker(HellionStrings.Tabs_NotificationSound_Description);
|
||||
if (tab.EnableNotificationSound)
|
||||
{
|
||||
using var indent = ImRaii.PushIndent(10.0f);
|
||||
using var notifIndent = ImRaii.PushIndent(10.0f);
|
||||
// Build a readable preview label for the currently selected sound.
|
||||
var soundPreview =
|
||||
tab.NotificationSoundId <= 16
|
||||
@@ -238,89 +312,47 @@ internal sealed class Tabs : ISettingsTab
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui.Checkbox(Language.Options_Tabs_PopOut, ref tab.PopOut);
|
||||
if (tab.PopOut)
|
||||
{
|
||||
using var _ = ImRaii.PushIndent(10.0f);
|
||||
ImGui.Checkbox(
|
||||
Language.Options_Tabs_IndependentOpacity,
|
||||
ref tab.IndependentOpacity
|
||||
);
|
||||
if (tab.IndependentOpacity)
|
||||
ImGuiUtil.DragFloatVertical(
|
||||
Language.Options_Tabs_Opacity,
|
||||
ref tab.Opacity,
|
||||
0.25f,
|
||||
|
||||
// Volume is stored as a 0-1 float but shown as 0-100%.
|
||||
// Same field as General → Sound; shown here for convenience.
|
||||
// DragFloatVertical derives its widget ID from the label text and exposes no
|
||||
// override. We inline the equivalent (text label + SetNextItemWidth + DragFloat)
|
||||
// to keep an explicit ##tab-volume-{i} ID, which reads more clearly than relying
|
||||
// on the surrounding PushId("tab-{i}") scope to disambiguate identical labels.
|
||||
// Volume is global (Mutable.CustomSoundVolume) and applies to every tab's
|
||||
// notification sound, so it is shown unconditionally — not gated by the
|
||||
// per-tab EnableNotificationSound toggle.
|
||||
ImGui.TextUnformatted(HellionStrings.Settings_General_CustomSoundVolume_Name);
|
||||
ImGui.SetNextItemWidth(-1);
|
||||
var customSoundVolumePercent = Mutable.CustomSoundVolume * 100f;
|
||||
if (
|
||||
ImGui.DragFloat(
|
||||
$"##tab-volume-{i}",
|
||||
ref customSoundVolumePercent,
|
||||
1f,
|
||||
0f,
|
||||
100f,
|
||||
$"{tab.Opacity:N2}%%",
|
||||
$"{customSoundVolumePercent:N0}%%",
|
||||
ImGuiSliderFlags.AlwaysClamp
|
||||
);
|
||||
|
||||
ImGui.Checkbox(Language.Options_Tabs_IndependentHide, ref tab.IndependentHide);
|
||||
if (tab.IndependentHide)
|
||||
{
|
||||
using var __ = ImRaii.PushIndent(10.0f);
|
||||
ImGuiUtil.OptionCheckbox(
|
||||
ref tab.HideDuringCutscenes,
|
||||
Language.Options_HideDuringCutscenes_Name
|
||||
);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGuiUtil.OptionCheckbox(
|
||||
ref tab.HideWhenNotLoggedIn,
|
||||
Language.Options_HideWhenNotLoggedIn_Name
|
||||
);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGuiUtil.OptionCheckbox(
|
||||
ref tab.HideWhenUiHidden,
|
||||
Language.Options_HideWhenUiHidden_Name
|
||||
);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGuiUtil.OptionCheckbox(
|
||||
ref tab.HideInLoadingScreens,
|
||||
Language.Options_HideInLoadingScreens_Name
|
||||
);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGuiUtil.OptionCheckbox(
|
||||
ref tab.HideInBattle,
|
||||
Language.Options_HideInBattle_Name
|
||||
);
|
||||
ImGui.Spacing();
|
||||
}
|
||||
|
||||
ImGuiUtil.OptionCheckbox(ref tab.CanMove, Language.Popout_CanMove_Name);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGuiUtil.OptionCheckbox(ref tab.CanResize, Language.Popout_CanResize_Name);
|
||||
ImGui.Spacing();
|
||||
}
|
||||
|
||||
using (
|
||||
var combo = ImGuiUtil.BeginComboVertical(
|
||||
Language.Options_Tabs_UnreadMode,
|
||||
tab.UnreadMode.Name()
|
||||
)
|
||||
)
|
||||
{
|
||||
if (combo.Success)
|
||||
{
|
||||
foreach (var mode in Enum.GetValues<UnreadMode>())
|
||||
{
|
||||
if (ImGui.Selectable(mode.Name(), tab.UnreadMode == mode))
|
||||
tab.UnreadMode = mode;
|
||||
|
||||
if (mode.Tooltip() is { } tooltip && ImGui.IsItemHovered())
|
||||
ImGuiUtil.Tooltip(tooltip);
|
||||
Mutable.CustomSoundVolume = customSoundVolumePercent / 100f;
|
||||
}
|
||||
// Applies globally — same value as in General → Sound.
|
||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_General_CustomSoundVolume_Description + "\n\n" + HellionStrings.Settings_Section_Tab_Volume_AllTabsHint);
|
||||
}
|
||||
}
|
||||
|
||||
if (Mutable.HideWhenInactive)
|
||||
ImGui.Checkbox(Language.Options_Tabs_InactivityBehaviour, ref tab.UnhideOnActivity);
|
||||
ImGui.Spacing();
|
||||
|
||||
// ── Sub-section: Input ────────────────────────────────────────────
|
||||
if (sectionJustEntered) ImGui.SetNextItemOpen(false);
|
||||
using (var secInput = ImRaii.TreeNode(HellionStrings.Settings_Section_Tab_Input + $"##sec-input-{i}"))
|
||||
{
|
||||
if (secInput.Success)
|
||||
{
|
||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
||||
|
||||
ImGui.Checkbox(Language.Options_Tabs_NoInput, ref tab.InputDisabled);
|
||||
if (!tab.InputDisabled)
|
||||
@@ -436,13 +468,81 @@ internal sealed class Tabs : ISettingsTab
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, tab.SelectedChannels);
|
||||
ImGuiUtil.ExtraChatSelector(
|
||||
Language.Options_Tabs_ExtraChatChannels,
|
||||
ref tab.ExtraChatAll,
|
||||
tab.ExtraChatChannels
|
||||
ImGui.Spacing();
|
||||
|
||||
// ── Sub-section: Pop-out window ───────────────────────────────────
|
||||
if (sectionJustEntered) ImGui.SetNextItemOpen(false);
|
||||
using (var secPopOut = ImRaii.TreeNode(HellionStrings.Settings_Section_Tab_PopOut + $"##sec-popout-{i}"))
|
||||
{
|
||||
if (secPopOut.Success)
|
||||
{
|
||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
||||
|
||||
ImGui.Checkbox(Language.Options_Tabs_PopOut, ref tab.PopOut);
|
||||
if (tab.PopOut)
|
||||
{
|
||||
using var _ = ImRaii.PushIndent(10.0f);
|
||||
ImGui.Checkbox(
|
||||
Language.Options_Tabs_IndependentOpacity,
|
||||
ref tab.IndependentOpacity
|
||||
);
|
||||
if (tab.IndependentOpacity)
|
||||
ImGuiUtil.DragFloatVertical(
|
||||
Language.Options_Tabs_Opacity,
|
||||
ref tab.Opacity,
|
||||
0.25f,
|
||||
0f,
|
||||
100f,
|
||||
$"{tab.Opacity:N2}%%",
|
||||
ImGuiSliderFlags.AlwaysClamp
|
||||
);
|
||||
|
||||
ImGui.Checkbox(Language.Options_Tabs_IndependentHide, ref tab.IndependentHide);
|
||||
if (tab.IndependentHide)
|
||||
{
|
||||
using var __ = ImRaii.PushIndent(10.0f);
|
||||
ImGuiUtil.OptionCheckbox(
|
||||
ref tab.HideDuringCutscenes,
|
||||
Language.Options_HideDuringCutscenes_Name
|
||||
);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGuiUtil.OptionCheckbox(
|
||||
ref tab.HideWhenNotLoggedIn,
|
||||
Language.Options_HideWhenNotLoggedIn_Name
|
||||
);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGuiUtil.OptionCheckbox(
|
||||
ref tab.HideWhenUiHidden,
|
||||
Language.Options_HideWhenUiHidden_Name
|
||||
);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGuiUtil.OptionCheckbox(
|
||||
ref tab.HideInLoadingScreens,
|
||||
Language.Options_HideInLoadingScreens_Name
|
||||
);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGuiUtil.OptionCheckbox(
|
||||
ref tab.HideInBattle,
|
||||
Language.Options_HideInBattle_Name
|
||||
);
|
||||
ImGui.Spacing();
|
||||
}
|
||||
|
||||
ImGuiUtil.OptionCheckbox(ref tab.CanMove, Language.Popout_CanMove_Name);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGuiUtil.OptionCheckbox(ref tab.CanResize, Language.Popout_CanResize_Name);
|
||||
ImGui.Spacing();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemove > -1)
|
||||
|
||||
Reference in New Issue
Block a user