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.Raii;
|
||||
using Dalamud.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using HellionChat.Code;
|
||||
using HellionChat.Resources;
|
||||
using HellionChat.Themes;
|
||||
@@ -19,6 +20,9 @@ using HellionChat.Ui.Windows;
|
||||
using HellionChat.Util;
|
||||
using Lumina.Excel.Sheets;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Action = System.Action;
|
||||
using ChatTwoPartyFinderPayload = HellionChat.Util.PartyFinderPayload;
|
||||
using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload;
|
||||
|
||||
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