diff --git a/HellionChat/PayloadHandler.cs b/HellionChat/PayloadHandler.cs index c8d9b2a..82143d8 100644 --- a/HellionChat/PayloadHandler.cs +++ b/HellionChat/PayloadHandler.cs @@ -15,7 +15,6 @@ using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.UI; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Component.GUI; using HellionChat.Code; using HellionChat.Resources; @@ -192,8 +191,6 @@ internal sealed class PayloadHandler if (ImGui.Selectable(Language.Context_HideChat)) Plugin.Config.HideChat = true; - DrawTokenInserts(); - if (chunk.Message is { } message) { if (ImGui.Selectable(Language.Context_Copy)) @@ -939,39 +936,4 @@ internal sealed class PayloadHandler _popup = (chunk, payload); ImGui.OpenPopup(PopupId); } - - // The game expands and at send time, so inserting the literal - // token is enough. Both entries were in the v1.5.6 chat window and went out - // with it; the strings survived, untranslated in 24 files until now. - // - // Disabled when the precondition is missing, so the token cannot be sent - // only to expand into nothing on the other side. - private void DrawTokenInserts() - { - bool flagSet; - bool itemSet; - unsafe - { - flagSet = AgentMap.Instance()->FlagMarkerCount > 0; - itemSet = AgentChatLog.Instance()->LinkedItem.ItemId != 0; - } - - using (ImRaii.Disabled(!flagSet)) - { - if (ImGui.Selectable(HellionStrings.ChatLog_Insert_MapFlag)) - AppendToInput(""); - } - - using (ImRaii.Disabled(!itemSet)) - { - if (ImGui.Selectable(HellionStrings.ChatLog_Insert_ItemLink)) - AppendToInput(""); - } - } - - private void AppendToInput(string token) - { - var current = _inputBar.PendingMessage; - _inputBar.SetPendingMessage(current + token); - } } diff --git a/HellionChat/Ui/Components/InputBar.cs b/HellionChat/Ui/Components/InputBar.cs index bd3b4f0..60bac08 100644 --- a/HellionChat/Ui/Components/InputBar.cs +++ b/HellionChat/Ui/Components/InputBar.cs @@ -5,6 +5,7 @@ using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; using HellionChat._Helpers; using HellionChat.Code; using HellionChat.GameFunctions; @@ -342,10 +343,68 @@ internal sealed class InputBar _commandHelpWindow.IsOpen = false; TrySend(activeTab); } + DrawInputContextMenu(); + _isFocused = ImGui.IsItemFocused(); _wasInputTextHovered = ImGui.IsItemHovered(); } + // Right-clicking the input field opened this in v1.5.6 and has opened + // nothing since the chat window was retired. Reported by a tester who went + // looking for the map-flag entry. + // + // Must sit immediately after the InputText call: ContextPopupItem binds to + // the last submitted item. + // + // Hiding the chat is not repeated here -- it has its own button two widgets + // to the right, and one way in is enough. + private void DrawInputContextMenu() + { + using var context = ImRaii.ContextPopupItem("##hellion-input-context"); + if (!context.Success) + return; + + // The game expands and at send time, so inserting the + // literal token is the whole implementation. Each entry is disabled + // while its precondition is missing, so the token cannot be sent only + // to expand into nothing at the other end. + bool flagSet; + bool itemSet; + unsafe + { + // Null before dereferencing: both agents can be null during a zone + // transition, which is precisely when somebody is most likely to be + // typing a flag into a party chat. + var map = AgentMap.Instance(); + var chatLog = AgentChatLog.Instance(); + flagSet = map != null && map->FlagMarkerCount > 0; + itemSet = chatLog != null && chatLog->LinkedItem.ItemId != 0; + } + + using (ImRaii.Disabled(!flagSet)) + { + if (ImGui.Selectable(HellionStrings.ChatLog_Insert_MapFlag)) + InsertToken(""); + } + + using (ImRaii.Disabled(!itemSet)) + { + if (ImGui.Selectable(HellionStrings.ChatLog_Insert_ItemLink)) + InsertToken(""); + } + } + + // Focus returns to the field and the caret lands behind the token, so the + // user can keep typing. Picking from a menu and then having to click back + // into the field is the kind of small friction that makes a feature go + // unused. + private void InsertToken(string token) + { + SetPendingMessage(_pendingMessage + token); + Activate = true; + _activatePos = _pendingMessage.Length; + } + // Dispatches across three ImGui callback events: CallbackAlways (cursor // restore after popup commit), CallbackCompletion (Tab opens the auto- // translate picker), CallbackEdit (slash-command help window sync).