diff --git a/ChatTwo/Configuration.cs b/ChatTwo/Configuration.cs index 2afa0c7..139282a 100755 --- a/ChatTwo/Configuration.cs +++ b/ChatTwo/Configuration.cs @@ -1,12 +1,33 @@ using System.Collections; using ChatTwo.Code; +using ChatTwo.GameFunctions.Types; using ChatTwo.Resources; using ChatTwo.Ui; using Dalamud.Configuration; +using Dalamud.Game.ClientState.Keys; using ImGuiNET; namespace ChatTwo; +[Serializable] +internal class ConfigKeyBind +{ + public ModifierFlag Modifier; + public VirtualKey Key; + + public override string ToString() + { + var modString = ""; + if (Modifier.HasFlag(ModifierFlag.Ctrl)) + modString += Language.Keybind_Modifier_Ctrl + " + "; + if (Modifier.HasFlag(ModifierFlag.Shift)) + modString += Language.Keybind_Modifier_Shift + " + "; + if (Modifier.HasFlag(ModifierFlag.Alt)) + modString += Language.Keybind_Modifier_Alt + " + "; + return modString+Key.GetFancyName(); + } +} + [Serializable] internal class Configuration : IPluginConfiguration { @@ -68,6 +89,9 @@ internal class Configuration : IPluginConfiguration public bool OverrideStyle; public string? ChosenStyle; + public ConfigKeyBind? ChatTabForward = null; + public ConfigKeyBind? ChatTabBackward = null; + internal void UpdateFrom(Configuration other, bool backToOriginal) { if (backToOriginal) @@ -123,6 +147,8 @@ internal class Configuration : IPluginConfiguration Tabs = other.Tabs.Select(t => t.Clone()).ToList(); OverrideStyle = other.OverrideStyle; ChosenStyle = other.ChosenStyle; + ChatTabForward = other.ChatTabForward; + ChatTabBackward = other.ChatTabBackward; } } diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs index 902fd99..d36b307 100755 --- a/ChatTwo/GameFunctions/Chat.cs +++ b/ChatTwo/GameFunctions/Chat.cs @@ -266,6 +266,36 @@ internal sealed unsafe class Chat : IDisposable modifierState |= modifier; } + bool KeyPressed(VirtualKey key, ModifierFlag modifier) + { + if (!Plugin.KeyState.IsVirtualKeyValid(key)) + return false; + + var modifierPressed = Plugin.Config.KeybindMode switch + { + KeybindMode.Strict => modifier == modifierState, + KeybindMode.Flexible => modifierState.HasFlag(modifier), + _ => false, + }; + + return modifierPressed && Plugin.KeyState[key]; + } + + // Test for custom keybinds for changing chat tabs before checking + // vanilla keybinds. + if (Plugin.Config.ChatTabBackward != null && KeyPressed(Plugin.Config.ChatTabBackward.Key, Plugin.Config.ChatTabBackward.Modifier)) + { + Plugin.KeyState[Plugin.Config.ChatTabBackward.Key] = false; + Plugin.ChatLogWindow.ChangeTabDelta(-1); + return; + } + if (Plugin.Config.ChatTabForward != null && KeyPressed(Plugin.Config.ChatTabForward.Key, Plugin.Config.ChatTabForward.Modifier)) + { + Plugin.KeyState[Plugin.Config.ChatTabForward.Key] = false; + Plugin.ChatLogWindow.ChangeTabDelta(1); + return; + } + var turnedOff = new Dictionary(); foreach (var toIntercept in KeybindsToIntercept.Keys) { @@ -281,20 +311,7 @@ internal sealed unsafe class Chat : IDisposable void Intercept(VirtualKey key, ModifierFlag modifier) { - if (!Plugin.KeyState.IsVirtualKeyValid(key)) - return; - - var modifierPressed = Plugin.Config.KeybindMode switch - { - KeybindMode.Strict => modifier == modifierState, - KeybindMode.Flexible => modifierState.HasFlag(modifier), - _ => false, - }; - - if (!modifierPressed) - return; - - if (!Plugin.KeyState[key]) + if (!KeyPressed(key, modifier)) return; var bits = BitOperations.PopCount((uint) modifier); diff --git a/ChatTwo/GameFunctions/Types/ModifierFlag.cs b/ChatTwo/GameFunctions/Types/ModifierFlag.cs index 4fc9382..395055e 100755 --- a/ChatTwo/GameFunctions/Types/ModifierFlag.cs +++ b/ChatTwo/GameFunctions/Types/ModifierFlag.cs @@ -3,6 +3,7 @@ namespace ChatTwo.GameFunctions.Types; [Flags] internal enum ModifierFlag { + None = 0, Shift = 1 << 0, Ctrl = 1 << 1, Alt = 1 << 2, diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index 606f01a..def1633 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -1715,6 +1715,51 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to ESC to clear. + /// + internal static string Keybind_EscToClear { + get { + return ResourceManager.GetString("Keybind_EscToClear", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Alt. + /// + internal static string Keybind_Modifier_Alt { + get { + return ResourceManager.GetString("Keybind_Modifier_Alt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ctrl. + /// + internal static string Keybind_Modifier_Ctrl { + get { + return ResourceManager.GetString("Keybind_Modifier_Ctrl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shift. + /// + internal static string Keybind_Modifier_Shift { + get { + return ResourceManager.GetString("Keybind_Modifier_Shift", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to none set. + /// + internal static string Keybind_None { + get { + return ResourceManager.GetString("Keybind_None", resourceCulture); + } + } + /// /// Looks up a localized string similar to Flexible. /// @@ -1940,6 +1985,24 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Cycle chat tab backwards keybind. + /// + internal static string Options_ChatTabBackwardKeybind_Name { + get { + return ResourceManager.GetString("Options_ChatTabBackwardKeybind_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cycle chat tab forwards keybind. + /// + internal static string Options_ChatTabForwardKeybind_Name { + get { + return ResourceManager.GetString("Options_ChatTabForwardKeybind_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to Clear the message history database. /// diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index ec6eb53..9018653 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -514,6 +514,27 @@ Styles + + Cycle chat tab forwards keybind + + + Cycle chat tab backwards keybind + + + none set + + + ESC to clear + + + Ctrl + + + Alt + + + Shift + Ctrl + {0} diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index 86991d2..c0d925f 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -41,6 +41,7 @@ public sealed class ChatLogWindow : Window internal Vector4 DefaultText { get; set; } + internal int? WantedTab { get; set; } internal Tab? CurrentTab { get @@ -348,6 +349,34 @@ public sealed class ChatLogWindow : Window if (ImGui.GetIO().KeyShift) modifierState |= ModifierFlag.Shift; + bool KeyPressed(VirtualKey vk, ModifierFlag modifier) + { + if (!vk.TryToImGui(out var key)) + return false; + + var modifierPressed = Plugin.Config.KeybindMode switch + { + KeybindMode.Strict => modifier == modifierState, + KeybindMode.Flexible => modifierState.HasFlag(modifier), + _ => false, + }; + + return ImGui.IsKeyPressed(key) && modifierPressed && (modifier != 0 || !modifiersOnly); + } + + // Test for custom keybinds for changing chat tabs before checking + // vanilla keybinds. + if (Plugin.Config.ChatTabBackward != null && KeyPressed(Plugin.Config.ChatTabBackward.Key, Plugin.Config.ChatTabBackward.Modifier)) + { + Plugin.ChatLogWindow.ChangeTabDelta(-1); + return; + } + if (Plugin.Config.ChatTabForward != null && KeyPressed(Plugin.Config.ChatTabForward.Key, Plugin.Config.ChatTabForward.Modifier)) + { + Plugin.ChatLogWindow.ChangeTabDelta(1); + return; + } + var turnedOff = new Dictionary(); foreach (var (toIntercept, keybind) in Plugin.Functions.Chat.Keybinds) { @@ -356,17 +385,7 @@ public sealed class ChatLogWindow : Window void Intercept(VirtualKey vk, ModifierFlag modifier) { - if (!vk.TryToImGui(out var key)) - return; - - var modifierPressed = Plugin.Config.KeybindMode switch - { - KeybindMode.Strict => modifier == modifierState, - KeybindMode.Flexible => modifierState.HasFlag(modifier), - _ => false, - }; - - if (!ImGui.IsKeyPressed(key) || !modifierPressed || modifier == 0 && modifiersOnly) + if (!KeyPressed(vk, modifier)) return; var bits = BitOperations.PopCount((uint) modifier); @@ -395,6 +414,16 @@ public sealed class ChatLogWindow : Window } } + internal void ChangeTab(int index) => WantedTab = index; + + internal void ChangeTabDelta(int offset) + { + var newIndex = (LastTab + offset) % Plugin.Config.Tabs.Count; + while (newIndex < 0) + newIndex += Plugin.Config.Tabs.Count; + ChangeTab(newIndex); + } + private void TabChannelSwitch(Tab tab) { // Save the previous channel to restore it later @@ -1158,7 +1187,10 @@ public sealed class ChatLogWindow : Window continue; var unread = tabI == LastTab || tab.UnreadMode == UnreadMode.None || tab.Unread == 0 ? "" : $" ({tab.Unread})"; - using var tabItem = ImRaii.TabItem($"{tab.Name}{unread}###log-tab-{tabI}"); + var flags = ImGuiTabItemFlags.None; + if (WantedTab == tabI) + flags |= ImGuiTabItemFlags.SetSelected; + using var tabItem = ImRaii.TabItem($"{tab.Name}{unread}###log-tab-{tabI}", flags); DrawTabContextMenu(tab, tabI); if (!tabItem.Success) @@ -1174,6 +1206,7 @@ public sealed class ChatLogWindow : Window DrawMessageLog(tab, PayloadHandler, GetRemainingHeightForMessageLog(), switchedTab); } + WantedTab = null; return currentTab; } @@ -1203,10 +1236,10 @@ public sealed class ChatLogWindow : Window continue; var unread = tabI == LastTab || tab.UnreadMode == UnreadMode.None || tab.Unread == 0 ? "" : $" ({tab.Unread})"; - var clicked = ImGui.Selectable($"{tab.Name}{unread}###log-tab-{tabI}", LastTab == tabI); + var clicked = ImGui.Selectable($"{tab.Name}{unread}###log-tab-{tabI}", LastTab == tabI || WantedTab == tabI); DrawTabContextMenu(tab, tabI); - if (!clicked) + if (!clicked && WantedTab != tabI) continue; currentTab = tabI; @@ -1229,6 +1262,7 @@ public sealed class ChatLogWindow : Window if (currentTab > -1) DrawMessageLog(Plugin.Config.Tabs[currentTab], PayloadHandler, childHeight, switchedTab); + WantedTab = null; return currentTab; } diff --git a/ChatTwo/Ui/SettingsTabs/ChatLog.cs b/ChatTwo/Ui/SettingsTabs/ChatLog.cs index 2948a5b..4f4a305 100644 --- a/ChatTwo/Ui/SettingsTabs/ChatLog.cs +++ b/ChatTwo/Ui/SettingsTabs/ChatLog.cs @@ -72,6 +72,18 @@ internal sealed class ChatLog : ISettingsTab ImGui.Separator(); ImGui.Spacing(); + ImGui.TextUnformatted(Language.Options_ChatTabForwardKeybind_Name); + ImGui.SetNextItemWidth(-1); + ImGuiUtil.KeybindInput("ChatTabForwardKeybind", ref Mutable.ChatTabForward); + + ImGui.TextUnformatted(Language.Options_ChatTabBackwardKeybind_Name); + ImGui.SetNextItemWidth(-1); + ImGuiUtil.KeybindInput("ChatTabBackwardKeybind", ref Mutable.ChatTabBackward); + + ImGui.Spacing(); + ImGui.Separator(); + ImGui.Spacing(); + ImGui.TextUnformatted(Language.Options_AdjustPosition_Name); var pos = Plugin.ChatLogWindow.LastWindowPos; ImGui.SetNextItemWidth(-1); diff --git a/ChatTwo/Util/ImGuiUtil.cs b/ChatTwo/Util/ImGuiUtil.cs index 83aab61..4937854 100755 --- a/ChatTwo/Util/ImGuiUtil.cs +++ b/ChatTwo/Util/ImGuiUtil.cs @@ -1,5 +1,7 @@ using System.Numerics; using System.Text; +using ChatTwo.GameFunctions.Types; +using ChatTwo.Resources; using Dalamud.Game.ClientState.Keys; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Interface; @@ -304,6 +306,74 @@ internal static class ImGuiUtil } } + internal static bool KeybindInput(string id, ref ConfigKeyBind? keybind) + { + var idUint = ImGui.GetID(id); + using var pushedId = ImRaii.PushId(id); + if (ImGui.GetStateStorage().GetBool(idUint)) + { + var io = ImGui.GetIO(); + var currentMods = ModifierFlag.None; + var modString = ""; + if (io.KeyCtrl) + { + currentMods |= ModifierFlag.Ctrl; + modString += Language.Keybind_Modifier_Ctrl + " + "; + } + if (io.KeyShift) + { + currentMods |= ModifierFlag.Shift; + modString += Language.Keybind_Modifier_Shift + " + "; + } + if (io.KeyAlt) + { + currentMods |= ModifierFlag.Alt; + modString += Language.Keybind_Modifier_Alt + " + "; + } + + var text = $"{modString}... ({Language.Keybind_EscToClear})"; + using (ImRaii.PushColor(ImGuiCol.TextSelectedBg, Vector4.Zero)) + { + ImGui.SetKeyboardFocusHere(); + ImGui.InputText(id + "##keybind", ref text, 0, ImGuiInputTextFlags.ReadOnly); + } + + if (ImGui.IsKeyPressed(ImGuiKey.Escape)) + { + keybind = null; + ImGui.GetStateStorage().SetBool(idUint, false); + return false; + } + + foreach (var vk in Enum.GetValues(typeof(VirtualKey)).Cast()) + { + if (vk is VirtualKey.NO_KEY or VirtualKey.CONTROL or VirtualKey.LCONTROL or VirtualKey.RCONTROL or VirtualKey.SHIFT or VirtualKey.LSHIFT or VirtualKey.RSHIFT or VirtualKey.MENU or VirtualKey.LMENU or VirtualKey.RMENU) + continue; + + if (!TryToImGui(vk, out var imKey) || !ImGui.IsKeyPressed(imKey)) + continue; + + keybind = new ConfigKeyBind + { + Modifier = currentMods, + Key = vk + }; + ImGui.GetStateStorage().SetBool(idUint, false); + return true; + } + } + else + { + var text = $"({Language.Keybind_None})"; + if (keybind != null) + text = keybind.ToString(); + if (ImGui.Button(text, new Vector2(-1, 0))) + ImGui.GetStateStorage().SetBool(idUint, true); + } + + return false; + } + public static void DrawArrows(ref int selected, int min, int max, float spacing, int id = 0) { // Prevents changing values from triggering EndDisable