From 0b9d617021a816303bcc06c484556f19c2d783b0 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 29 Dec 2021 21:53:44 -0500 Subject: [PATCH] feat: partially handle some payloads --- ChatTwo/Chunk.cs | 4 +- ChatTwo/Code/InputChannel.cs | 2 +- ChatTwo/Code/InputChannelExt.cs | 2 +- ChatTwo/Configuration.cs | 4 +- ChatTwo/GameFunctions.cs | 55 ++++++++- ChatTwo/Message.cs | 2 +- ChatTwo/PayloadHandler.cs | 190 ++++++++++++++++++++++++++++++++ ChatTwo/Plugin.cs | 14 ++- ChatTwo/PluginUi.cs | 6 +- ChatTwo/Ui/ChatLog.cs | 60 +++++----- ChatTwo/Ui/Settings.cs | 4 + ChatTwo/Ui/UiComponent.cs | 2 +- ChatTwo/Util/ChunkUtil.cs | 9 +- ChatTwo/Util/ImGuiUtil.cs | 37 ++++++- 14 files changed, 341 insertions(+), 50 deletions(-) create mode 100755 ChatTwo/PayloadHandler.cs diff --git a/ChatTwo/Chunk.cs b/ChatTwo/Chunk.cs index 72134a1..fd14a56 100755 --- a/ChatTwo/Chunk.cs +++ b/ChatTwo/Chunk.cs @@ -4,7 +4,7 @@ using Dalamud.Game.Text.SeStringHandling; namespace ChatTwo; internal abstract class Chunk { - internal Payload? Link; + internal Payload? Link { get; set; } protected Chunk(Payload? link) { this.Link = link; @@ -32,7 +32,7 @@ internal class TextChunk : Chunk { } internal class IconChunk : Chunk { - internal BitmapFontIcon Icon; + internal BitmapFontIcon Icon { get; set; } public IconChunk(Payload? link, BitmapFontIcon icon) : base(link) { this.Icon = icon; diff --git a/ChatTwo/Code/InputChannel.cs b/ChatTwo/Code/InputChannel.cs index 63c36e7..e57915c 100755 --- a/ChatTwo/Code/InputChannel.cs +++ b/ChatTwo/Code/InputChannel.cs @@ -1,4 +1,4 @@ -namespace ChatTwo.Code; +namespace ChatTwo.Code; internal enum InputChannel : uint { Tell = 0, diff --git a/ChatTwo/Code/InputChannelExt.cs b/ChatTwo/Code/InputChannelExt.cs index 899539e..d313b17 100755 --- a/ChatTwo/Code/InputChannelExt.cs +++ b/ChatTwo/Code/InputChannelExt.cs @@ -1,4 +1,4 @@ -namespace ChatTwo.Code; +namespace ChatTwo.Code; internal static class InputChannelExt { internal static ChatType ToChatType(this InputChannel input) { diff --git a/ChatTwo/Configuration.cs b/ChatTwo/Configuration.cs index 4e273f2..375bc5b 100755 --- a/ChatTwo/Configuration.cs +++ b/ChatTwo/Configuration.cs @@ -1,13 +1,14 @@ using ChatTwo.Code; using Dalamud.Configuration; -namespace ChatTwo; +namespace ChatTwo; [Serializable] internal class Configuration : IPluginConfiguration { public int Version { get; set; } = 1; public bool HideChat = true; + public bool NativeItemTooltips = true; public float FontSize = 17f; public Dictionary ChatColours = new(); public List Tabs = new(); @@ -43,6 +44,7 @@ internal class Tab { if (this.Messages.Count > 1000) { this.Messages.RemoveAt(0); } + this.MessagesMutex.ReleaseMutex(); this.Unread += 1; diff --git a/ChatTwo/GameFunctions.cs b/ChatTwo/GameFunctions.cs index 27ec42a..1ca63dc 100755 --- a/ChatTwo/GameFunctions.cs +++ b/ChatTwo/GameFunctions.cs @@ -102,6 +102,59 @@ internal unsafe class GameFunctions : IDisposable { return (*flags & (1 << 22)) == 0; } + internal void OpenItemTooltip(uint id) { + var atkStage = AtkStage.GetSingleton(); + var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemDetail); + var addon = atkStage->RaptureAtkUnitManager->GetAddonByName("ItemDetail"); + if (agent == null || addon == null) { + // atkStage ain't gonna be null or we have bigger problems + return; + } + + var agentPtr = (IntPtr) agent; + + // addresses mentioned here are 6.01 + // see the call near the end of AgentItemDetail.Update + // offsets valid as of 6.01 + + // 8BFC49: sets some shit + *(uint*) (agentPtr + 0x20) = 22; + // 8C04C8: switch goes down to default, which is what we want + *(byte*) (agentPtr + 0x118) = 1; + // 8BFCF6: item id when hovering over item in chat + *(uint*) (agentPtr + 0x11C) = id; + // 8BFCE4: always 0 when hovering over item in chat + *(uint*) (agentPtr + 0x120) = 0; + // 8C0B55: skips a check to do with inventory + *(byte*) (agentPtr + 0x128) &= 0xEF; + // 8BFC7C: when set to 1, lets everything continue (one frame) + *(byte*) (agentPtr + 0x146) = 1; + // 8BFC89: skips early return + *(byte*) (agentPtr + 0x14A) = 0; + + // this just probably needs to be set + agent->AddonId = (uint) addon->ID; + + // vcall from E8 ?? ?? ?? ?? 0F B7 C0 48 83 C4 60 + var vf5 = (delegate**) ((IntPtr) addon->VTable + 40); + // E8872D: lets vf5 actually run + *(byte*) ((IntPtr) atkStage + 0x2B4) |= 2; + (*vf5)(addon, 0, 15); + } + + internal void CloseItemTooltip() { + // hide addon first to prevent the "addon close" sound + var addon = AtkStage.GetSingleton()->RaptureAtkUnitManager->GetAddonByName("ItemDetail"); + if (addon != null) { + addon->Hide(true); + } + + var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemDetail); + if (agent != null) { + agent->Hide(); + } + } + private byte ChatLogRefreshDetour(IntPtr log, ushort eventId, AtkValue* value) { if (eventId == 0x31 && value != null && value->UInt is 0x05 or 0x0C) { string? eventInput = null; @@ -144,7 +197,7 @@ internal unsafe class GameFunctions : IDisposable { } var channel = *(uint*) (shellModule + 0xFD0); - + // var channel = *(uint*) (agent + 0x40); if (channel is 17 or 18) { channel = 0; diff --git a/ChatTwo/Message.cs b/ChatTwo/Message.cs index bf074ab..17cf241 100755 --- a/ChatTwo/Message.cs +++ b/ChatTwo/Message.cs @@ -1,6 +1,6 @@ using ChatTwo.Code; -namespace ChatTwo; +namespace ChatTwo; internal class Message { internal DateTime Date { get; } diff --git a/ChatTwo/PayloadHandler.cs b/ChatTwo/PayloadHandler.cs new file mode 100755 index 0000000..37d3531 --- /dev/null +++ b/ChatTwo/PayloadHandler.cs @@ -0,0 +1,190 @@ +using System.Numerics; +using ChatTwo.Ui; +using ChatTwo.Util; +using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; +using Dalamud.Logging; +using Dalamud.Utility; +using ImGuiNET; + +namespace ChatTwo; + +internal sealed class PayloadHandler { + private PluginUi Ui { get; } + private ChatLog Log { get; } + + private HashSet Popups { get; set; } = new(); + + private uint _hoveredItem; + private uint _hoverCounter; + private uint _lastHoverCounter; + + internal PayloadHandler(PluginUi ui, ChatLog log) { + this.Ui = ui; + this.Log = log; + } + + internal void Draw() { + var newPopups = new HashSet(); + foreach (var player in this.Popups) { + var id = PopupId(player); + if (!ImGui.BeginPopup(id)) { + continue; + } + + newPopups.Add(player); + ImGui.PushID(id); + + this.DrawPlayerPopup(player); + + ImGui.PopID(); + ImGui.EndPopup(); + } + + this.Popups = newPopups; + + if (++this._hoverCounter - this._lastHoverCounter > 1) { + this.Ui.Plugin.Functions.CloseItemTooltip(); + this._hoveredItem = 0; + this._hoverCounter = this._lastHoverCounter = 0; + } + } + + internal void Click(Payload payload, ImGuiMouseButton button) { + PluginLog.Log($"clicked {payload} with {button}"); + + switch (button) { + case ImGuiMouseButton.Left: + this.LeftClickPayload(payload); + break; + case ImGuiMouseButton.Right: + this.RightClickPayload(payload); + break; + } + } + + internal void Hover(Payload payload) { + switch (payload) { + case StatusPayload status: { + this.DoHover(() => this.HoverStatus(status), 250f); + break; + } + case ItemPayload item: { + if (this.Ui.Plugin.Config.NativeItemTooltips) { + this.Ui.Plugin.Functions.OpenItemTooltip(item.ItemId); + + if (this._hoveredItem != item.ItemId) { + this._hoveredItem = item.ItemId; + this._hoverCounter = this._lastHoverCounter = 0; + } else { + this._lastHoverCounter = this._hoverCounter; + } + + break; + } + + this.DoHover(() => this.HoverItem(item), 250f); + break; + } + } + } + + private void DoHover(Action inside, float width) { + ImGui.SetNextWindowSize(new Vector2(width, -1f)); + + ImGui.BeginTooltip(); + ImGui.PushTextWrapPos(); + + ImGui.PushStyleColor(ImGuiCol.Text, this.Ui.DefaultText); + try { + inside(); + } finally { + ImGui.PopStyleColor(); + ImGui.PopTextWrapPos(); + ImGui.EndTooltip(); + } + } + + private void HoverStatus(StatusPayload status) { + var name = ChunkUtil.ToChunks(status.Status.Name.ToDalamudString(), null); + this.Log.DrawChunks(name.ToList()); + ImGui.Separator(); + + var desc = ChunkUtil.ToChunks(status.Status.Description.ToDalamudString(), null); + this.Log.DrawChunks(desc.ToList()); + } + + private void HoverItem(ItemPayload item) { + var name = ChunkUtil.ToChunks(item.Item.Name.ToDalamudString(), null); + this.Log.DrawChunks(name.ToList()); + ImGui.Separator(); + + var desc = ChunkUtil.ToChunks(item.Item.Description.ToDalamudString(), null); + this.Log.DrawChunks(desc.ToList()); + } + + private void LeftClickPayload(Payload payload) { + switch (payload) { + case MapLinkPayload map: { + this.Ui.Plugin.GameGui.OpenMapWithMapLink(map); + break; + } + case QuestPayload quest: { + this.Ui.Plugin.Common.Functions.Journal.OpenQuest(quest.Quest); + break; + } + case DalamudLinkPayload link: { + break; + } + } + } + + private void RightClickPayload(Payload payload) { + switch (payload) { + case PlayerPayload player: { + this.Popups.Add(player); + ImGui.OpenPopup(PopupId(player)); + break; + } + } + } + + private void DrawPlayerPopup(PlayerPayload player) { + var name = player.PlayerName; + if (player.World.IsPublic) { + name += $"{player.World.Name}"; + } + + ImGui.TextUnformatted(name); + ImGui.Separator(); + + if (player.World.IsPublic && ImGui.Selectable("Send Tell")) { + this.Log.Chat = $"/tell {player.PlayerName}@{player.World.Name} "; + this.Log.Activate = true; + } + + if (ImGui.Selectable("Target")) { + foreach (var obj in this.Ui.Plugin.ObjectTable) { + if (obj is not PlayerCharacter character) { + continue; + } + + if (character.Name.TextValue != player.PlayerName) { + continue; + } + + if (player.World.IsPublic && character.HomeWorld.Id != player.World.RowId) { + continue; + } + + this.Ui.Plugin.TargetManager.SetTarget(obj); + break; + } + } + } + + private static string PopupId(PlayerPayload player) { + return $"###player-{player.PlayerName}@{player.World}"; + } +} diff --git a/ChatTwo/Plugin.cs b/ChatTwo/Plugin.cs index 8192629..bedcb6a 100755 --- a/ChatTwo/Plugin.cs +++ b/ChatTwo/Plugin.cs @@ -1,6 +1,7 @@ using Dalamud.Data; using Dalamud.Game; using Dalamud.Game.ClientState; +using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Command; using Dalamud.Game.Gui; using Dalamud.IoC; @@ -21,7 +22,7 @@ public sealed class Plugin : IDalamudPlugin { [PluginService] internal ClientState ClientState { get; init; } - + [PluginService] internal CommandManager CommandManager { get; init; } @@ -31,9 +32,18 @@ public sealed class Plugin : IDalamudPlugin { [PluginService] internal Framework Framework { get; init; } + [PluginService] + internal GameGui GameGui { get; init; } + + [PluginService] + internal ObjectTable ObjectTable { get; init; } + [PluginService] internal SigScanner SigScanner { get; init; } + [PluginService] + internal TargetManager TargetManager { get; init; } + internal Configuration Config { get; } internal XivCommonBase Common { get; } internal GameFunctions Functions { get; } @@ -78,7 +88,7 @@ public sealed class Plugin : IDalamudPlugin { if (!this.Config.HideChat) { return; } - + foreach (var name in ChatAddonNames) { if (GameFunctions.IsAddonInteractable(name)) { GameFunctions.SetAddonInteractable(name, false); diff --git a/ChatTwo/PluginUi.cs b/ChatTwo/PluginUi.cs index dadb2c7..97fac2f 100755 --- a/ChatTwo/PluginUi.cs +++ b/ChatTwo/PluginUi.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using System.Numerics; +using System.Runtime.InteropServices; using ChatTwo.Ui; using Dalamud.Interface; using Dalamud.Logging; @@ -10,6 +11,7 @@ internal sealed class PluginUi : IDisposable { internal Plugin Plugin { get; } internal ImFontPtr? RegularFont { get; private set; } internal ImFontPtr? ItalicFont { get; private set; } + internal Vector4 DefaultText { get; private set; } private List Components { get; } private ImFontConfigPtr _fontCfg; @@ -104,6 +106,8 @@ internal sealed class PluginUi : IDisposable { } private void Draw() { + this.DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text]; + var font = this.RegularFont.HasValue; if (font) { diff --git a/ChatTwo/Ui/ChatLog.cs b/ChatTwo/Ui/ChatLog.cs index 52d4f75..2ae8b4f 100755 --- a/ChatTwo/Ui/ChatLog.cs +++ b/ChatTwo/Ui/ChatLog.cs @@ -9,15 +9,18 @@ namespace ChatTwo.Ui; internal sealed class ChatLog : IUiComponent { private PluginUi Ui { get; } - private bool _activate; - private string _chat = string.Empty; + internal bool Activate; + internal string Chat = string.Empty; private readonly TextureWrap? _fontIcon; private readonly List _inputBacklog = new(); private int _inputBacklogIdx = -1; private int _lastTab; + private PayloadHandler PayloadHandler { get; } + internal ChatLog(PluginUi ui) { this.Ui = ui; + this.PayloadHandler = new PayloadHandler(this.Ui, this); this._fontIcon = this.Ui.Plugin.DataManager.GetImGuiTexture("common/font/fonticon_ps5.tex"); @@ -30,9 +33,9 @@ internal sealed class ChatLog : IUiComponent { } private void ChatActivated(string? input) { - this._activate = true; - if (input != null && !this._chat.Contains(input)) { - this._chat += input; + this.Activate = true; + if (input != null && !this.Chat.Contains(input)) { + this.Chat += input; } } @@ -80,7 +83,7 @@ internal sealed class ChatLog : IUiComponent { // int numMessages; try { tab.MessagesMutex.WaitOne(); - + for (var i = 0; i < tab.Messages.Count; i++) { // numDrawn += 1; var message = tab.Messages[i]; @@ -94,11 +97,11 @@ internal sealed class ChatLog : IUiComponent { } if (message.Sender.Count > 0) { - this.DrawChunks(message.Sender); + this.DrawChunks(message.Sender, this.PayloadHandler); ImGui.SameLine(); } - this.DrawChunks(message.Content); + this.DrawChunks(message.Content, this.PayloadHandler); // drawnHeight += ImGui.GetCursorPosY() - lastPos; // lastPos = ImGui.GetCursorPosY(); @@ -115,7 +118,7 @@ internal sealed class ChatLog : IUiComponent { } // PluginLog.Log($"numDrawn: {numDrawn}"); - + if (switchedTab || ImGui.GetScrollY() >= ImGui.GetScrollMaxY()) { // PluginLog.Log($"drawnHeight: {drawnHeight}"); // var itemPosY = clipper.StartPosY + drawnHeight; @@ -125,6 +128,8 @@ internal sealed class ChatLog : IUiComponent { } } + this.PayloadHandler.Draw(); + ImGui.EndChild(); ImGui.EndTabItem(); @@ -134,7 +139,7 @@ internal sealed class ChatLog : IUiComponent { ImGui.EndTabBar(); } - if (this._activate) { + if (this.Activate) { ImGui.SetKeyboardFocusHere(); } @@ -148,23 +153,23 @@ internal sealed class ChatLog : IUiComponent { if (inputColour != null) { ImGui.PushStyleColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(inputColour.Value)); } - + ImGui.SetNextItemWidth(-1); const ImGuiInputTextFlags inputFlags = ImGuiInputTextFlags.EnterReturnsTrue | ImGuiInputTextFlags.CallbackAlways | ImGuiInputTextFlags.CallbackHistory; - if (ImGui.InputText("##chat2-input", ref this._chat, 500, inputFlags, this.Callback)) { - if (!string.IsNullOrWhiteSpace(this._chat)) { - var trimmed = this._chat.Trim(); + if (ImGui.InputText("##chat2-input", ref this.Chat, 500, inputFlags, this.Callback)) { + if (!string.IsNullOrWhiteSpace(this.Chat)) { + var trimmed = this.Chat.Trim(); this.AddBacklog(trimmed); this._inputBacklogIdx = -1; this.Ui.Plugin.Common.Functions.Chat.SendMessage(trimmed); } - this._chat = string.Empty; + this.Chat = string.Empty; } - + if (inputColour != null) { ImGui.PopStyleColor(); } @@ -175,9 +180,9 @@ internal sealed class ChatLog : IUiComponent { private unsafe int Callback(ImGuiInputTextCallbackData* data) { var ptr = new ImGuiInputTextCallbackDataPtr(data); - if (this._activate) { - this._activate = false; - data->CursorPos = this._chat.Length; + if (this.Activate) { + this.Activate = false; + data->CursorPos = this.Chat.Length; data->SelectionStart = data->SelectionEnd = data->CursorPos; } @@ -192,12 +197,12 @@ internal sealed class ChatLog : IUiComponent { switch (this._inputBacklogIdx) { case -1: var offset = 0; - - if (!string.IsNullOrWhiteSpace(this._chat)) { - this.AddBacklog(this._chat); + + if (!string.IsNullOrWhiteSpace(this.Chat)) { + this.AddBacklog(this.Chat); offset = 1; } - + this._inputBacklogIdx = this._inputBacklog.Count - 1 - offset; break; case > 0: @@ -229,9 +234,9 @@ internal sealed class ChatLog : IUiComponent { return 0; } - private void DrawChunks(IReadOnlyList chunks) { + internal void DrawChunks(IReadOnlyList chunks, PayloadHandler? handler = null) { for (var i = 0; i < chunks.Count; i++) { - this.DrawChunk(chunks[i]); + this.DrawChunk(chunks[i], handler); if (i < chunks.Count - 1) { ImGui.SameLine(); @@ -239,7 +244,7 @@ internal sealed class ChatLog : IUiComponent { } } - private void DrawChunk(Chunk chunk) { + private void DrawChunk(Chunk chunk, PayloadHandler? handler = null) { if (chunk is IconChunk icon && this._fontIcon != null) { var bounds = IconUtil.GetBounds((byte) icon.Icon); if (bounds != null) { @@ -251,6 +256,7 @@ internal sealed class ChatLog : IUiComponent { var uv0 = new Vector2(bounds.Value.X, bounds.Value.Y - 2) / texSize; var uv1 = new Vector2(bounds.Value.X + bounds.Value.Z, bounds.Value.Y - 2 + bounds.Value.W) / texSize; ImGui.Image(this._fontIcon.ImGuiHandle, size, uv0, uv1); + ImGuiUtil.PostPayload(chunk.Link, handler); } return; @@ -277,7 +283,7 @@ internal sealed class ChatLog : IUiComponent { ImGui.PushFont(this.Ui.ItalicFont.Value); } - ImGuiUtil.WrapText(text.Content); + ImGuiUtil.WrapText(text.Content, chunk.Link, handler); if (text.Italic && this.Ui.ItalicFont.HasValue) { ImGui.PopFont(); diff --git a/ChatTwo/Ui/Settings.cs b/ChatTwo/Ui/Settings.cs index e45d5ed..12269fd 100755 --- a/ChatTwo/Ui/Settings.cs +++ b/ChatTwo/Ui/Settings.cs @@ -12,6 +12,7 @@ internal sealed class Settings : IUiComponent { private bool _visible; private bool _hideChat; + private bool _nativeItemTooltips; private float _fontSize; private Dictionary _chatColours = new(); private List _tabs = new(); @@ -34,6 +35,7 @@ internal sealed class Settings : IUiComponent { private void Initialise() { var config = this.Ui.Plugin.Config; this._hideChat = config.HideChat; + this._nativeItemTooltips = config.NativeItemTooltips; this._fontSize = config.FontSize; this._chatColours = config.ChatColours.ToDictionary(entry => entry.Key, entry => entry.Value); this._tabs = config.Tabs.Select(tab => tab.Clone()).ToList(); @@ -60,6 +62,7 @@ internal sealed class Settings : IUiComponent { - ImGui.CalcTextSize("A").Y; if (ImGui.BeginChild("##chat2-settings", new Vector2(-1, height))) { ImGui.Checkbox("Hide chat", ref this._hideChat); + ImGui.Checkbox("Show native item tooltips", ref this._nativeItemTooltips); ImGui.DragFloat("Font size", ref this._fontSize, .5f, 12f, 36f); if (ImGui.TreeNodeEx("Chat colours")) { @@ -163,6 +166,7 @@ internal sealed class Settings : IUiComponent { var fontSizeChanged = Math.Abs(this._fontSize - this.Ui.Plugin.Config.FontSize) > float.Epsilon; config.HideChat = this._hideChat; + config.NativeItemTooltips = this._nativeItemTooltips; config.FontSize = this._fontSize; config.ChatColours = this._chatColours; config.Tabs = this._tabs; diff --git a/ChatTwo/Ui/UiComponent.cs b/ChatTwo/Ui/UiComponent.cs index 38d3067..93a7499 100755 --- a/ChatTwo/Ui/UiComponent.cs +++ b/ChatTwo/Ui/UiComponent.cs @@ -1,4 +1,4 @@ -namespace ChatTwo.Ui; +namespace ChatTwo.Ui; internal interface IUiComponent : IDisposable { void Draw(); diff --git a/ChatTwo/Util/ChunkUtil.cs b/ChatTwo/Util/ChunkUtil.cs index 16ca2d9..7c40c23 100755 --- a/ChatTwo/Util/ChunkUtil.cs +++ b/ChatTwo/Util/ChunkUtil.cs @@ -1,7 +1,6 @@ using ChatTwo.Code; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; -using Dalamud.Logging; namespace ChatTwo.Util; @@ -23,13 +22,7 @@ internal static class ChunkUtil { }); } - PluginLog.Log(""); foreach (var payload in msg.Payloads) { - PluginLog.Log(payload.Type.ToString()); - if (payload.Type == PayloadType.Unknown) { - PluginLog.Log(payload.Encode().Select(b => b.ToString("x2")).Aggregate(string.Concat)); - } - switch (payload.Type) { case PayloadType.EmphasisItalic: var newStatus = ((EmphasisItalicPayload) payload).IsEnabled; @@ -75,7 +68,7 @@ internal static class ChunkUtil { if (rawPayload.Data[1] == 0x13) { foreground.Pop(); glow.Pop(); - } else if (rawPayload == RawPayload.LinkTerminator) { + } else if (Equals(rawPayload, RawPayload.LinkTerminator)) { link = null; } diff --git a/ChatTwo/Util/ImGuiUtil.cs b/ChatTwo/Util/ImGuiUtil.cs index 0b5ece3..eb23b09 100755 --- a/ChatTwo/Util/ImGuiUtil.cs +++ b/ChatTwo/Util/ImGuiUtil.cs @@ -1,10 +1,39 @@ using System.Text; +using Dalamud.Game.Text.SeStringHandling; using ImGuiNET; -namespace ChatTwo.Util; +namespace ChatTwo.Util; internal static class ImGuiUtil { - internal static unsafe void WrapText(string csText) { + private static readonly ImGuiMouseButton[] Buttons = { + ImGuiMouseButton.Left, + ImGuiMouseButton.Middle, + ImGuiMouseButton.Right, + }; + + internal static void PostPayload(Payload? payload, PayloadHandler? handler) { + if (payload != null && ImGui.IsItemHovered()) { + ImGui.SetMouseCursor(ImGuiMouseCursor.Hand); + handler?.Hover(payload); + } + + if (payload == null || handler == null) { + return; + } + + foreach (var button in Buttons) { + if (ImGui.IsItemClicked(button)) { + handler.Click(payload, button); + } + } + } + + internal static unsafe void WrapText(string csText, Payload? payload, PayloadHandler? handler) { + void Text(byte* text, byte* textEnd) { + ImGuiNative.igTextUnformatted(text, textEnd); + PostPayload(payload, handler); + } + foreach (var part in csText.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)) { var bytes = Encoding.UTF8.GetBytes(part); fixed (byte* rawText = bytes) { @@ -23,7 +52,7 @@ internal static class ImGuiUtil { return; } - ImGuiNative.igTextUnformatted(text, endPrevLine); + Text(text, endPrevLine); widthLeft = ImGui.GetContentRegionAvail().X; while (endPrevLine < textEnd) { @@ -37,7 +66,7 @@ internal static class ImGuiUtil { break; } - ImGuiNative.igTextUnformatted(text, endPrevLine); + Text(text, endPrevLine); } } }