From 56f6855da22c0670991624c9004bf975404145ac Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Thu, 18 Jul 2024 01:35:59 +1000 Subject: [PATCH] feat: autohide improvements - Adds new setting "Enable inactivity hide during battle" (default: true) which determines whether autohide should apply during battle (thanks @aurieh) - Adds new setting "Chat channels considered for activity" which allows customizing which channels incoming messages must match to "bump" the inactivity timer - Adds new per-tab setting "Unhide the chat window on activity" to configure whether it will be considered for "bumping" the inactivity timer when receiving messages that match the new channel filter. Note that the foreground tab is currently always considered. - Extends autohide code to apply to poped-out tabs as well. Each popout window has its own inactivity timer, but focusing the main window will restore all popped out windows. Co-authored-by: Auri --- ChatTwo/Configuration.cs | 40 ++++++------- ChatTwo/GameFunctions/KeybindManager.cs | 6 +- ChatTwo/Message.cs | 11 ++++ ChatTwo/Resources/Language.Designer.cs | 74 ++++++++++++++++++++++- ChatTwo/Resources/Language.resx | 26 +++++++- ChatTwo/Ui/ChatLogWindow.cs | 19 +++--- ChatTwo/Ui/Popout.cs | 21 ++++++- ChatTwo/Ui/SettingsTabs/Display.cs | 48 ++++++++++++++- ChatTwo/Ui/SettingsTabs/Tabs.cs | 80 ++----------------------- ChatTwo/Util/ImGuiUtil.cs | 75 +++++++++++++++++++++++ ChatTwo/Util/TabsUtil.cs | 8 +++ 11 files changed, 296 insertions(+), 112 deletions(-) diff --git a/ChatTwo/Configuration.cs b/ChatTwo/Configuration.cs index 139282a..3c66b4f 100755 --- a/ChatTwo/Configuration.cs +++ b/ChatTwo/Configuration.cs @@ -3,6 +3,7 @@ using ChatTwo.Code; using ChatTwo.GameFunctions.Types; using ChatTwo.Resources; using ChatTwo.Ui; +using ChatTwo.Util; using Dalamud.Configuration; using Dalamud.Game.ClientState.Keys; using ImGuiNET; @@ -43,6 +44,10 @@ internal class Configuration : IPluginConfiguration public bool HideInBattle; public bool HideWhenInactive; public int InactivityHideTimeout = 10; + public bool InactivityHideActiveDuringBattle = true; + public Dictionary InactivityHideChannels = TabsUtil.AllChannels(); + public bool InactivityHideExtraChatAll = true; + public HashSet InactivityHideExtraChatChannels = []; public bool ShowHideButton = true; public bool NativeItemTooltips = true; public bool PrettierTimestamps = true; @@ -106,6 +111,10 @@ internal class Configuration : IPluginConfiguration HideInBattle = other.HideInBattle; HideWhenInactive = other.HideWhenInactive; InactivityHideTimeout = other.InactivityHideTimeout; + InactivityHideActiveDuringBattle = other.InactivityHideActiveDuringBattle; + InactivityHideChannels = other.InactivityHideChannels.ToDictionary(entry => entry.Key, entry => entry.Value); + InactivityHideExtraChatAll = other.InactivityHideExtraChatAll; + InactivityHideExtraChatChannels = other.InactivityHideExtraChatChannels.ToHashSet(); ShowHideButton = other.ShowHideButton; NativeItemTooltips = other.NativeItemTooltips; PrettierTimestamps = other.PrettierTimestamps; @@ -188,6 +197,7 @@ internal class Tab public HashSet ExtraChatChannels = []; public UnreadMode UnreadMode = UnreadMode.Unseen; + public bool UnhideOnActivity; public bool DisplayTimestamp = true; public InputChannel? Channel; public bool PopOut; @@ -199,7 +209,7 @@ internal class Tab public uint Unread; [NonSerialized] - public long LastMessageTime; + public long LastActivity; [NonSerialized] public MessageList Messages = new(); @@ -210,31 +220,20 @@ internal class Tab [NonSerialized] public Guid Identifier = Guid.NewGuid(); - internal bool Matches(Message message) - { - if (message.ExtraChatChannel != Guid.Empty) - return ExtraChatAll || ExtraChatChannels.Contains(message.ExtraChatChannel); - - return message.Code.Type.IsGm() - || ChatCodes.TryGetValue(message.Code.Type, out var sources) - && (message.Code.Source is 0 or (ChatSource) 1 - || sources.HasFlag(message.Code.Source)); - } + internal bool Matches(Message message) => message.Matches(ChatCodes, ExtraChatAll, ExtraChatChannels); internal void AddMessage(Message message, bool unread = true) { Messages.AddPrune(message, MessageManager.MessageDisplayLimit); - if (unread) - { - Unread += 1; - LastMessageTime = Environment.TickCount64; - } + if (!unread) + return; + Unread += 1; + + if (message.Matches(Plugin.Config.InactivityHideChannels, Plugin.Config.InactivityHideExtraChatAll, Plugin.Config.InactivityHideExtraChatChannels)) + LastActivity = Environment.TickCount64; } - internal void Clear() - { - Messages.Clear(); - } + internal void Clear() => Messages.Clear(); internal Tab Clone() { @@ -245,6 +244,7 @@ internal class Tab ExtraChatAll = ExtraChatAll, ExtraChatChannels = ExtraChatChannels.ToHashSet(), UnreadMode = UnreadMode, + UnhideOnActivity = UnhideOnActivity, DisplayTimestamp = DisplayTimestamp, Channel = Channel, PopOut = PopOut, diff --git a/ChatTwo/GameFunctions/KeybindManager.cs b/ChatTwo/GameFunctions/KeybindManager.cs index 811921c..ecc3a95 100644 --- a/ChatTwo/GameFunctions/KeybindManager.cs +++ b/ChatTwo/GameFunctions/KeybindManager.cs @@ -10,8 +10,6 @@ using FFXIVClientStructs.FFXIV.Client.UI; using ImGuiNET; using ModifierFlag = ChatTwo.GameFunctions.Types.ModifierFlag; -using ModifierFlag = ChatTwo.GameFunctions.Types.ModifierFlag; - namespace ChatTwo.GameFunctions; internal enum KeyboardSource { @@ -95,8 +93,8 @@ internal unsafe class KeybindManager : IDisposable { // List of keys that can be used as a part of keybinds while the chat is // focused WITHOUT modifiers. All other keys can only be used if their - // configured keybind contains modifiers. This allows for using e.g. F11 to - // change chat channel while typing. + // configured keybind contains modifiers (except only SHIFT). This allows + // for using e.g. F11 to change chat channel while typing. private static readonly IReadOnlyCollection ModifierlessChatKeys = new[] { // VirtualKey.NO_KEY, diff --git a/ChatTwo/Message.cs b/ChatTwo/Message.cs index d517da0..499f19d 100755 --- a/ChatTwo/Message.cs +++ b/ChatTwo/Message.cs @@ -124,6 +124,17 @@ internal partial class Message return new Message(0, 0, code, [], content, new SeString(), new SeString()); } + internal bool Matches(Dictionary channels, bool allExtraChatChannels, HashSet extraChatChannels) + { + if (ExtraChatChannel != Guid.Empty) + return allExtraChatChannels || extraChatChannels.Contains(ExtraChatChannel); + + return Code.Type.IsGm() + || channels.TryGetValue(Code.Type, out var sources) + && (Code.Source is 0 or (ChatSource) 1 + || sources.HasFlag(Code.Source)); + } + private int GenerateHash() { return SortCode.GetHashCode() diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index def1633..d1336b9 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -2481,7 +2481,7 @@ namespace ChatTwo.Resources { } /// - /// Looks up a localized string similar to Hide the chat after a configurable period of inactivity. The current tab and any tabs with unread indicators enabled are considered for activity.. + /// Looks up a localized string similar to Hide the chat after a configurable period of inactivity. The current tab and any other tabs with the setting enabled are considered for activity.. /// internal static string Options_HideWhenInactive_Description { get { @@ -2534,6 +2534,69 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to When disabled, the chat log will stay active during battle.. + /// + internal static string Options_InactivityHideActiveDuringBattle_Description { + get { + return ResourceManager.GetString("Options_InactivityHideActiveDuringBattle_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable inactivity hide during battle. + /// + internal static string Options_InactivityHideActiveDuringBattle_Name { + get { + return ResourceManager.GetString("Options_InactivityHideActiveDuringBattle_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select All. + /// + internal static string Options_InactivityHideChannels_All_Label { + get { + return ResourceManager.GetString("Options_InactivityHideChannels_All_Label", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hold Ctrl+Shift to click.. + /// + internal static string Options_InactivityHideChannels_Button_Tooltip { + get { + return ResourceManager.GetString("Options_InactivityHideChannels_Button_Tooltip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Which chat channels should be considered for activity. Other channels will not restore the chat regardless of which tab they occur in.. + /// + internal static string Options_InactivityHideChannels_Description { + get { + return ResourceManager.GetString("Options_InactivityHideChannels_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Chat channels considered for activity. + /// + internal static string Options_InactivityHideChannels_Name { + get { + return ResourceManager.GetString("Options_InactivityHideChannels_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unselect All. + /// + internal static string Options_InactivityHideChannels_None_Label { + get { + return ResourceManager.GetString("Options_InactivityHideChannels_None_Label", resourceCulture); + } + } + /// /// Looks up a localized string similar to How long to wait (in seconds) before considering the chat log inactive.. /// @@ -3137,6 +3200,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Unhide the chat window on activity. + /// + internal static string Options_Tabs_InactivityBehaviour { + get { + return ResourceManager.GetString("Options_Tabs_InactivityBehaviour", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use different opacity than main window. /// diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index 9018653..4a457e4 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -220,6 +220,9 @@ Unread mode + + Unhide the chat window on activity + <None> @@ -410,7 +413,7 @@ Hide when inactive - Hide the chat after a configurable period of inactivity. The current tab and any tabs with unread indicators enabled are considered for activity. + Hide the chat after a configurable period of inactivity. The current tab and any other tabs with the setting enabled are considered for activity. Inactivity timeout @@ -418,6 +421,27 @@ How long to wait (in seconds) before considering the chat log inactive. + + Enable inactivity hide during battle + + + When disabled, the chat log will stay active during battle. + + + Chat channels considered for activity + + + Which chat channels should be considered for activity. Other channels will not restore the chat regardless of which tab they occur in. + + + Select All + + + Unselect All + + + Hold Ctrl+Shift to click. + Keybind mode diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index 4e5b48f..344b7a9 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -87,7 +87,7 @@ public sealed class ChatLogWindow : Window private bool PlayedClosingSound = true; private long FrameTime; // set every frame - private long LastActivityTime = Environment.TickCount64; + internal long LastActivityTime = Environment.TickCount64; private readonly ExcelSheet WorldSheet; private readonly ExcelSheet LogFilterSheet; @@ -348,7 +348,10 @@ public sealed class ChatLogWindow : Window return height; } - internal void ChangeTab(int index) => WantedTab = index; + internal void ChangeTab(int index) { + WantedTab = index; + LastActivityTime = FrameTime; + } internal void ChangeTabDelta(int offset) { @@ -373,7 +376,7 @@ public sealed class ChatLogWindow : Window SetChannel(tab.Channel ?? tab.PreviousChannel); } - private static bool InBattle => Plugin.Condition[ConditionFlag.InCombat]; + internal static bool InBattle => Plugin.Condition[ConditionFlag.InCombat]; private static bool GposeActive => Plugin.Condition[ConditionFlag.WatchingCutscene]; private static bool CutsceneActive => Plugin.Condition[ConditionFlag.OccupiedInCutSceneEvent] || Plugin.Condition[ConditionFlag.WatchingCutscene78]; @@ -448,7 +451,8 @@ public sealed class ChatLogWindow : Window FrameTime = Environment.TickCount64; if (IsHidden) return false; - if (!Plugin.Config.HideWhenInactive || Activate) + + if (!Plugin.Config.HideWhenInactive || (!Plugin.Config.InactivityHideActiveDuringBattle && InBattle) || Activate) { LastActivityTime = FrameTime; return true; @@ -456,11 +460,10 @@ public sealed class ChatLogWindow : Window var currentTab = CurrentTab; // local to avoid calling the getter repeatedly var lastActivityTime = Plugin.Config.Tabs - .Where(tab => tab.UnreadMode is not UnreadMode.None || tab == currentTab) - .Select(tab => tab.LastMessageTime) - .DefaultIfEmpty(0) + .Where(tab => !tab.PopOut && (tab.UnhideOnActivity || tab == currentTab)) + .Select(tab => tab.LastActivity) + .Append(LastActivityTime) .Max(); - lastActivityTime = Math.Max(lastActivityTime, LastActivityTime); return FrameTime - lastActivityTime <= 1000 * Plugin.Config.InactivityHideTimeout; } diff --git a/ChatTwo/Ui/Popout.cs b/ChatTwo/Ui/Popout.cs index 18a9e18..b713c06 100644 --- a/ChatTwo/Ui/Popout.cs +++ b/ChatTwo/Ui/Popout.cs @@ -12,6 +12,9 @@ internal class Popout : Window private readonly Tab Tab; private readonly int Idx; + private long FrameTime; // set every frame + private long LastActivityTime = Environment.TickCount64; + public Popout(ChatLogWindow chatLogWindow, Tab tab, int idx) : base($"{tab.Name}##popout") { ChatLogWindow = chatLogWindow; @@ -34,7 +37,20 @@ internal class Popout : Window public override bool DrawConditions() { - return !ChatLogWindow.IsHidden; + FrameTime = Environment.TickCount64; + if (ChatLogWindow.IsHidden) + return false; + + if (!Plugin.Config.HideWhenInactive || (!Plugin.Config.InactivityHideActiveDuringBattle && ChatLogWindow.InBattle) || !Tab.UnhideOnActivity) + { + LastActivityTime = FrameTime; + return true; + } + + // Activity in the tab, this popout window, or the main chat log window. + var lastActivityTime = Math.Max(Tab.LastActivity, LastActivityTime); + lastActivityTime = Math.Max(lastActivityTime, ChatLogWindow.LastActivityTime); + return FrameTime - lastActivityTime <= 1000 * Plugin.Config.InactivityHideTimeout; } public override void PreDraw() @@ -65,6 +81,9 @@ internal class Popout : Window var handler = ChatLogWindow.HandlerLender.Borrow(); ChatLogWindow.DrawMessageLog(Tab, handler, ImGui.GetContentRegionAvail().Y, false); + + if (ImGui.IsWindowHovered(ImGuiHoveredFlags.ChildWindows)) + LastActivityTime = FrameTime; } public override void PostDraw() diff --git a/ChatTwo/Ui/SettingsTabs/Display.cs b/ChatTwo/Ui/SettingsTabs/Display.cs index e3d773f..66d9844 100755 --- a/ChatTwo/Ui/SettingsTabs/Display.cs +++ b/ChatTwo/Ui/SettingsTabs/Display.cs @@ -1,5 +1,6 @@ using ChatTwo.Resources; using ChatTwo.Util; +using Dalamud.Interface.Utility.Raii; using ImGuiNET; namespace ChatTwo.Ui.SettingsTabs; @@ -37,27 +38,70 @@ internal sealed class Display : ISettingsTab ImGuiUtil.OptionCheckbox(ref Mutable.HideInBattle, Language.Options_HideInBattle_Name, Language.Options_HideInBattle_Description); ImGui.Spacing(); + ImGui.Separator(); + ImGui.Spacing(); + ImGuiUtil.OptionCheckbox(ref Mutable.HideWhenInactive, Language.Options_HideWhenInactive_Name, Language.Options_HideWhenInactive_Description); ImGui.Spacing(); if (Mutable.HideWhenInactive) { + using var _ = ImRaii.PushIndent(); ImGuiUtil.InputIntVertical(Language.Options_InactivityHideTimeout_Name, Language.Options_InactivityHideTimeout_Description, ref Mutable.InactivityHideTimeout, 1, 10); // Enforce a minimum of 2 seconds to avoid people soft locking // themselves. Mutable.InactivityHideTimeout = Math.Max(2, Mutable.InactivityHideTimeout); ImGui.Spacing(); + + // This setting conflicts with HideInBattle, so it's disabled. + using (ImRaii.Disabled(Mutable.HideInBattle)) + { + ImGuiUtil.OptionCheckbox(ref Mutable.InactivityHideActiveDuringBattle, + Language.Options_InactivityHideActiveDuringBattle_Name, + Language.Options_InactivityHideActiveDuringBattle_Description); + ImGui.Spacing(); + } + + using var channelTree = ImRaii.TreeNode(Language.Options_InactivityHideChannels_Name); + if (channelTree.Success) + { + if (ImGuiUtil.CtrlShiftButton(Language.Options_InactivityHideChannels_All_Label, + Language.Options_InactivityHideChannels_Button_Tooltip)) + { + Mutable.InactivityHideChannels = TabsUtil.AllChannels(); + Mutable.InactivityHideExtraChatAll = true; + Mutable.InactivityHideExtraChatChannels = []; + } + + ImGui.SameLine(); + if (ImGuiUtil.CtrlShiftButton(Language.Options_InactivityHideChannels_None_Label, + Language.Options_InactivityHideChannels_Button_Tooltip)) + { + Mutable.InactivityHideChannels = new(); + Mutable.InactivityHideExtraChatAll = false; + Mutable.InactivityHideExtraChatChannels = []; + } + + ImGui.Spacing(); + + ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, Mutable.InactivityHideChannels); + ImGuiUtil.ExtraChatSelector(Language.Options_Tabs_ExtraChatChannels, + ref Mutable.InactivityHideExtraChatAll, Mutable.InactivityHideExtraChatChannels); + } + ImGui.Spacing(); } + ImGui.Separator(); + ImGui.Spacing(); + ImGuiUtil.OptionCheckbox(ref Mutable.PrettierTimestamps, Language.Options_PrettierTimestamps_Name, Language.Options_PrettierTimestamps_Description); if (Mutable.PrettierTimestamps) { - ImGui.TreePush(); + using var _ = ImRaii.PushIndent(); ImGuiUtil.OptionCheckbox(ref Mutable.MoreCompactPretty, Language.Options_MoreCompactPretty_Name, Language.Options_MoreCompactPretty_Description); ImGuiUtil.OptionCheckbox(ref Mutable.HideSameTimestamps, Language.Options_HideSameTimestamps_Name, Language.Options_HideSameTimestamps_Description); - ImGui.TreePop(); } ImGui.Spacing(); diff --git a/ChatTwo/Ui/SettingsTabs/Tabs.cs b/ChatTwo/Ui/SettingsTabs/Tabs.cs index be98190..8c99b34 100755 --- a/ChatTwo/Ui/SettingsTabs/Tabs.cs +++ b/ChatTwo/Ui/SettingsTabs/Tabs.cs @@ -108,6 +108,9 @@ internal sealed class Tabs : ISettingsTab } } + if (Mutable.HideWhenInactive) + ImGui.Checkbox(Language.Options_Tabs_InactivityBehaviour, ref tab.UnhideOnActivity); + ImGui.Checkbox(Language.Options_Tabs_NoInput, ref tab.InputDisabled); if (!tab.InputDisabled) { @@ -124,81 +127,8 @@ internal sealed class Tabs : ISettingsTab } } - using (var channelNode = ImRaii.TreeNode(Language.Options_Tabs_Channels)) - { - if (channelNode) - { - foreach (var (header, types) in ChatTypeExt.SortOrder) - { - using var headerNode = ImRaii.TreeNode(header + $"##{i}"); - if (!headerNode.Success) - continue; - - foreach (var type in types) - { - if (type.IsGm()) - continue; - - var enabled = tab.ChatCodes.ContainsKey(type); - if (ImGui.Checkbox($"##{type.Name()}-{i}", ref enabled)) - { - if (enabled) - tab.ChatCodes[type] = ChatSourceExt.All; - else - tab.ChatCodes.Remove(type); - } - - ImGui.SameLine(); - - if (!type.HasSource()) - { - ImGui.TextUnformatted(type.Name()); - continue; - } - - using var typeNode = ImRaii.TreeNode($"{type.Name()}##{i}"); - if (!typeNode.Success) - continue; - - tab.ChatCodes.TryGetValue(type, out var sourcesEnum); - var sources = (uint) sourcesEnum; - - foreach (var source in Enum.GetValues()) - if (ImGui.CheckboxFlags(source.Name(), ref sources, (uint) source)) - tab.ChatCodes[type] = (ChatSource) sources; - } - } - } - } - - - if (Plugin.ExtraChat.ChannelNames.Count <= 0) - continue; - - using var extraTree = ImRaii.TreeNode(Language.Options_Tabs_ExtraChatChannels); - if (!extraTree.Success) - continue; - - ImGui.Checkbox(Language.Options_Tabs_ExtraChatAll, ref tab.ExtraChatAll); - ImGui.Separator(); - - if (tab.ExtraChatAll) - ImGui.BeginDisabled(); - - foreach (var (id, name) in Plugin.ExtraChat.ChannelNames) - { - var enabled = tab.ExtraChatChannels.Contains(id); - if (!ImGui.Checkbox($"{name}##ec-{id}", ref enabled)) - continue; - - if (enabled) - tab.ExtraChatChannels.Add(id); - else - tab.ExtraChatChannels.Remove(id); - } - - if (tab.ExtraChatAll) - ImGui.EndDisabled(); + ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, tab.ChatCodes); + ImGuiUtil.ExtraChatSelector(Language.Options_Tabs_ExtraChatChannels, ref tab.ExtraChatAll, tab.ExtraChatChannels); } if (toRemove > -1) diff --git a/ChatTwo/Util/ImGuiUtil.cs b/ChatTwo/Util/ImGuiUtil.cs index 4937854..f26fd24 100755 --- a/ChatTwo/Util/ImGuiUtil.cs +++ b/ChatTwo/Util/ImGuiUtil.cs @@ -1,5 +1,6 @@ using System.Numerics; using System.Text; +using ChatTwo.Code; using ChatTwo.GameFunctions.Types; using ChatTwo.Resources; using Dalamud.Game.ClientState.Keys; @@ -549,6 +550,80 @@ internal static class ImGuiUtil return new EndUnconditionally(ImGui.PopTextWrapPos, true); } + public static void ChannelSelector(string headerText, Dictionary chatCodes) + { + using var channelNode = ImRaii.TreeNode(headerText); + if (!channelNode) + return; + + foreach (var (header, types) in ChatTypeExt.SortOrder) + { + using var headerNode = ImRaii.TreeNode(header); + if (!headerNode.Success) + continue; + + foreach (var type in types) + { + if (type.IsGm()) + continue; + + var enabled = chatCodes.ContainsKey(type); + if (ImGui.Checkbox($"##{type.Name()}", ref enabled)) + { + if (enabled) + chatCodes[type] = ChatSourceExt.All; + else + chatCodes.Remove(type); + } + + ImGui.SameLine(); + + if (!type.HasSource()) + { + ImGui.TextUnformatted(type.Name()); + continue; + } + + using var typeNode = ImRaii.TreeNode($"{type.Name()}"); + if (!typeNode.Success) + continue; + + chatCodes.TryGetValue(type, out var sourcesEnum); + var sources = (uint)sourcesEnum; + + foreach (var source in Enum.GetValues()) + if (ImGui.CheckboxFlags(source.Name(), ref sources, (uint)source)) + chatCodes[type] = (ChatSource)sources; + } + } + } + + public static void ExtraChatSelector(string headerText, ref bool all, HashSet extraChatChannels) + { + if (Plugin.ExtraChat.ChannelNames.Count <= 0) + return; + + using var extraTree = ImRaii.TreeNode(headerText); + if (!extraTree.Success) + return; + + ImGui.Checkbox(Language.Options_Tabs_ExtraChatAll, ref all); + ImGui.Separator(); + + using var _ = ImRaii.Disabled(all); + foreach (var (id, name) in Plugin.ExtraChat.ChannelNames) + { + var enabled = extraChatChannels.Contains(id); + if (!ImGui.Checkbox($"{name}##ec-{id}", ref enabled)) + continue; + + if (enabled) + extraChatChannels.Add(id); + else + extraChatChannels.Remove(id); + } + } + // Used to avoid pops if condition is false for Push. private static void Nop() { } } diff --git a/ChatTwo/Util/TabsUtil.cs b/ChatTwo/Util/TabsUtil.cs index 1f9cb2c..7749938 100755 --- a/ChatTwo/Util/TabsUtil.cs +++ b/ChatTwo/Util/TabsUtil.cs @@ -4,6 +4,14 @@ using ChatTwo.Resources; namespace ChatTwo.Util; internal static class TabsUtil { + internal static Dictionary AllChannels() + { + var channels = new Dictionary(); + foreach (var chatType in Enum.GetValues()) + channels[chatType] = ChatSourceExt.All; + return channels; + } + internal static Tab VanillaGeneral => new() { Name = Language.Tabs_Presets_General, ChatCodes = new Dictionary {