feat(payload-handler): wire Hover/Click + LeftClick/RightClick/LinkClick paths
E5 fills the hover/click handler layer (10 methods): - Hover/Click: replaces E1's empty stubs with real 1:1 bodies; Click uses unsafe for FFXIVClientStructs pointer access (UIGlobals.PlaySoundEffect via PopupSfx const from E2) - DoHover: §4.2 swap LogWindow.DefaultText → _themes.Active.Colors.TextPrimary - HoverStatus, HoverItem, HoverEventItem: 2x _chunkRenderer.DrawChunks swaps each (name + description rendering) - HoverUri: pure 1:1, no LogWindow deps - LeftClickPayload: unsafe-preserved, Plugin.GameGui static stays - ClickLinkPayload: pure 1:1, Plugin.ChatGui / Plugin.Framework statics stay - RightClickPayload: sets _popup field (per E1/E2 field-syntax convention) Adds FFXIVClientStructs.FFXIV.Client.UI using for UIGlobals; adds Action alias and DalamudPartyFinderPayload/ChatTwoPartyFinderPayload aliases required by LeftClickPayload switch arms. E6 will wire MoveTooltip. After E5, PayloadHandler is functionally complete except for the MoveTooltip AddonLifecycle wiring.
This commit is contained in:
@@ -11,6 +11,7 @@ using Dalamud.Interface.Textures.TextureWraps;
|
|||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using HellionChat.Code;
|
using HellionChat.Code;
|
||||||
using HellionChat.Resources;
|
using HellionChat.Resources;
|
||||||
using HellionChat.Themes;
|
using HellionChat.Themes;
|
||||||
@@ -19,6 +20,9 @@ using HellionChat.Ui.Windows;
|
|||||||
using HellionChat.Util;
|
using HellionChat.Util;
|
||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Action = System.Action;
|
||||||
|
using ChatTwoPartyFinderPayload = HellionChat.Util.PartyFinderPayload;
|
||||||
|
using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload;
|
||||||
|
|
||||||
namespace HellionChat;
|
namespace HellionChat;
|
||||||
|
|
||||||
@@ -580,7 +584,251 @@ internal sealed class PayloadHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Hover(Payload payload) { }
|
internal void Hover(Payload payload)
|
||||||
|
{
|
||||||
|
var hoverSize = 350f * ImGuiHelpers.GlobalScale;
|
||||||
|
|
||||||
internal unsafe void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) { }
|
switch (payload)
|
||||||
|
{
|
||||||
|
case StatusPayload status:
|
||||||
|
DoHover(() => HoverStatus(status), hoverSize);
|
||||||
|
break;
|
||||||
|
case ItemPayload item:
|
||||||
|
if (Plugin.Config.NativeItemTooltips)
|
||||||
|
{
|
||||||
|
if (!HandleTooltips || HoveredItem != item.RawItemId)
|
||||||
|
{
|
||||||
|
HandleTooltips = true;
|
||||||
|
HoveredItem = item.RawItemId;
|
||||||
|
HoverCounter = LastHoverCounter = 0;
|
||||||
|
|
||||||
|
GameFunctions.GameFunctions.OpenItemTooltip(item.RawItemId, item.Kind);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LastHoverCounter = HoverCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DoHover(() => HoverItem(item), hoverSize);
|
||||||
|
break;
|
||||||
|
case UriPayload uri:
|
||||||
|
DoHover(() => HoverUri(uri), hoverSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoHover(Action drawAction, float spacingHorizontal)
|
||||||
|
{
|
||||||
|
ImGui.SetNextWindowSize(new Vector2(spacingHorizontal, -1f));
|
||||||
|
|
||||||
|
using (ImRaii.Tooltip())
|
||||||
|
using (ImRaii.TextWrapPos(0.0f))
|
||||||
|
using (
|
||||||
|
ImRaii.PushColor(
|
||||||
|
ImGuiCol.Text,
|
||||||
|
ColourUtil.RgbaToVector4(_themes.Active.Colors.TextPrimary)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
drawAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HoverStatus(StatusPayload status)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
Plugin.TextureProvider.GetFromGameIcon(status.Status.Value.Icon).GetWrapOrDefault() is
|
||||||
|
{ } icon
|
||||||
|
)
|
||||||
|
InlineIcon(icon);
|
||||||
|
|
||||||
|
var builder = new SeStringBuilder();
|
||||||
|
var nameValue = status.Status.Value.Name.ToString();
|
||||||
|
switch (status.Status.Value.StatusCategory)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
builder.AddUiForeground($"{SeIconChar.Buff.ToIconString()}{nameValue}", 517);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
builder.AddUiForeground($"{SeIconChar.Debuff.ToIconString()}{nameValue}", 518);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
builder.AddUiForeground(nameValue, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = ChunkUtil.ToChunks(builder.BuiltString, ChunkSource.None, null);
|
||||||
|
_chunkRenderer.DrawChunks(name.ToList());
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
var desc = ChunkUtil.ToChunks(
|
||||||
|
status.Status.Value.Description.ToDalamudString(),
|
||||||
|
ChunkSource.None,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
_chunkRenderer.DrawChunks(desc.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HoverItem(ItemPayload item)
|
||||||
|
{
|
||||||
|
if (item.Kind == ItemKind.EventItem)
|
||||||
|
{
|
||||||
|
HoverEventItem(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Sheets.ItemSheet.TryGetRow(item.ItemId, out var resolvedItem))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (
|
||||||
|
Plugin
|
||||||
|
.TextureProvider.GetFromGameIcon(
|
||||||
|
new GameIconLookup(resolvedItem.Icon, item.Kind == ItemKind.Hq)
|
||||||
|
)
|
||||||
|
.GetWrapOrDefault() is
|
||||||
|
{ } icon
|
||||||
|
)
|
||||||
|
InlineIcon(icon);
|
||||||
|
|
||||||
|
var name = ChunkUtil.ToChunks(resolvedItem.Name.ToDalamudString(), ChunkSource.None, null);
|
||||||
|
_chunkRenderer.DrawChunks(name.ToList());
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
var desc = ChunkUtil.ToChunks(
|
||||||
|
resolvedItem.Description.ToDalamudString(),
|
||||||
|
ChunkSource.None,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
_chunkRenderer.DrawChunks(desc.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HoverEventItem(ItemPayload item)
|
||||||
|
{
|
||||||
|
if (!Sheets.EventItemSheet.TryGetRow(item.RawItemId, out var itemRow))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (
|
||||||
|
Plugin
|
||||||
|
.TextureProvider.GetFromGameIcon(new GameIconLookup(itemRow.Icon))
|
||||||
|
.GetWrapOrDefault() is
|
||||||
|
{ } icon
|
||||||
|
)
|
||||||
|
InlineIcon(icon);
|
||||||
|
|
||||||
|
var name = ChunkUtil.ToChunks(itemRow.Name.ToDalamudString(), ChunkSource.None, null);
|
||||||
|
_chunkRenderer.DrawChunks(name.ToList());
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
if (!Sheets.EventItemHelpSheet.TryGetRow(item.RawItemId, out var itemHelpRow))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_chunkRenderer.DrawChunks(
|
||||||
|
ChunkUtil
|
||||||
|
.ToChunks(itemHelpRow.Description.ToDalamudString(), ChunkSource.None, null)
|
||||||
|
.ToList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HoverUri(UriPayload uri)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(string.Format(Language.Context_URLDomain, uri.Uri.Authority));
|
||||||
|
ImGuiUtil.WarningText(Language.Context_URLWarning);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal unsafe void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button)
|
||||||
|
{
|
||||||
|
if (Plugin.Config.PlaySounds)
|
||||||
|
UIGlobals.PlaySoundEffect(PopupSfx);
|
||||||
|
|
||||||
|
switch (button)
|
||||||
|
{
|
||||||
|
case ImGuiMouseButton.Left:
|
||||||
|
LeftClickPayload(chunk, payload);
|
||||||
|
break;
|
||||||
|
case ImGuiMouseButton.Right:
|
||||||
|
RightClickPayload(chunk, payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void LeftClickPayload(Chunk chunk, Payload? payload)
|
||||||
|
{
|
||||||
|
switch (payload)
|
||||||
|
{
|
||||||
|
case MapLinkPayload map:
|
||||||
|
Plugin.GameGui.OpenMapWithMapLink(map);
|
||||||
|
break;
|
||||||
|
case QuestPayload quest:
|
||||||
|
GameFunctions.GameFunctions.OpenQuestLog(quest.Quest);
|
||||||
|
break;
|
||||||
|
case DalamudLinkPayload link:
|
||||||
|
ClickLinkPayload(chunk, payload, link);
|
||||||
|
break;
|
||||||
|
case DalamudPartyFinderPayload pf:
|
||||||
|
if (
|
||||||
|
pf.LinkType
|
||||||
|
== DalamudPartyFinderPayload.PartyFinderLinkType.PartyFinderNotification
|
||||||
|
)
|
||||||
|
GameFunctions.GameFunctions.OpenPartyFinder();
|
||||||
|
else
|
||||||
|
GameFunctions.GameFunctions.OpenPartyFinder(pf.ListingId);
|
||||||
|
break;
|
||||||
|
case ChatTwoPartyFinderPayload pf:
|
||||||
|
GameFunctions.GameFunctions.OpenPartyFinder(pf.Id);
|
||||||
|
break;
|
||||||
|
case AchievementPayload achievement:
|
||||||
|
GameFunctions.GameFunctions.OpenAchievement(achievement.Id);
|
||||||
|
break;
|
||||||
|
case RawPayload raw:
|
||||||
|
if (Equals(raw, ChunkUtil.PeriodicRecruitmentLink))
|
||||||
|
GameFunctions.GameFunctions.OpenPartyFinder();
|
||||||
|
break;
|
||||||
|
case UriPayload uri:
|
||||||
|
WrapperUtil.TryOpenUri(uri.Uri);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
RightClickPayload(chunk, payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClickLinkPayload(Chunk chunk, Payload payload, DalamudLinkPayload link)
|
||||||
|
{
|
||||||
|
if (chunk.GetSeString() is not { } source)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var start = source.Payloads.IndexOf(payload);
|
||||||
|
var end = source.Payloads.IndexOf(RawPayload.LinkTerminator, start == -1 ? 0 : start);
|
||||||
|
if (start == -1 || end == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var payloads = source.Payloads.Skip(start).Take(end - start + 1).ToList();
|
||||||
|
if (
|
||||||
|
!Plugin.ChatGui.RegisteredLinkHandlers.TryGetValue(
|
||||||
|
(link.Plugin, link.CommandId),
|
||||||
|
out var value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Could not find DalamudLinkHandlers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Running XivCommon SendChat instantly, without RunOnTick, leads to a game freeze, for whatever reason
|
||||||
|
Plugin.Framework.RunOnTick(() => value.Invoke(link.CommandId, new SeString(payloads)));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error executing DalamudLinkPayload handler");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RightClickPayload(Chunk chunk, Payload? payload)
|
||||||
|
{
|
||||||
|
_popup = (chunk, payload);
|
||||||
|
ImGui.OpenPopup(PopupId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user