feat(payload-handler): wire DrawItem/EventItem/Status/Uri popups + InlineIcon

E4 fills the remaining popup-body methods:

- DrawItemPopup: shows item-name, icon (via InlineIcon), and description
  via _chunkRenderer.DrawChunks; dispatches to DrawEventItemPopup when
  payload.Kind == ItemKind.EventItem (per v1.5.6 internal split)
- DrawEventItemPopup: same shape, Sheets.EventItemSheet/EventItemHelpSheet
  reads, _chunkRenderer.DrawChunks for description rendering
- DrawStatusPopup: status-name + description via _chunkRenderer.DrawChunks;
  "Link" action appends " <status>" via _inputBar.AppendPending (per §4.2)
- DrawUriPopup: open-in-browser + copy-link selectables (no LogWindow deps)
- InlineIcon: pure static helper for popup-icon rendering

Replaces E2's TODO(E4) markers in DrawPopups' Item/Status/Uri switch-cases
with real calls + drawn=true. E5 will wire Hover/Click bodies; E6
MoveTooltip.
This commit is contained in:
2026-05-27 13:20:59 +02:00
parent abc0617d58
commit 6da8ac93fe
+171 -3
View File
@@ -1,16 +1,23 @@
using System.Linq;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.Textures;
using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility;
using HellionChat.Code;
using HellionChat.Resources;
using HellionChat.Themes;
using HellionChat.Ui.Components;
using HellionChat.Ui.Windows;
using HellionChat.Util;
using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
namespace HellionChat;
@@ -90,15 +97,15 @@ internal sealed class PayloadHandler
drawn = true;
break;
case ItemPayload item:
// TODO(E4): DrawItemPopup(item);
DrawItemPopup(item);
drawn = true;
break;
case UriPayload uri:
// TODO(E4): DrawUriPopup(uri);
DrawUriPopup(uri);
drawn = true;
break;
case StatusPayload status:
// TODO(E4): DrawStatusPopup(status);
DrawStatusPopup(status);
drawn = true;
break;
}
@@ -412,6 +419,167 @@ internal sealed class PayloadHandler
return null;
}
private void DrawItemPopup(ItemPayload payload)
{
if (payload.Kind == ItemKind.EventItem)
{
DrawEventItemPopup(payload);
return;
}
if (!Sheets.ItemSheet.TryGetRow(payload.ItemId, out var itemRow))
return;
var hq = payload.Kind == ItemKind.Hq;
if (
Plugin
.TextureProvider.GetFromGameIcon(new GameIconLookup(itemRow.Icon, hq))
.GetWrapOrDefault() is
{ } icon
)
InlineIcon(icon);
var name = itemRow.Name.ToDalamudString();
if (hq)
name.Payloads.Add(new TextPayload(" "));
else if (payload.Kind == ItemKind.Collectible)
name.Payloads.Add(new TextPayload(" "));
_chunkRenderer.DrawChunks(ChunkUtil.ToChunks(name, ChunkSource.None, null).ToList(), false);
ImGui.Separator();
var realItemId = payload.RawItemId;
if (itemRow.EquipSlotCategory.RowId != 0)
{
if (ImGui.Selectable(Language.Context_TryOn))
GameFunctions.Context.TryOn(realItemId, 0);
if (ImGui.Selectable(Language.Context_ItemComparison))
GameFunctions.Context.OpenItemComparison(realItemId);
}
if (itemRow.ItemSearchCategory.Value.Category == 3)
if (ImGui.Selectable(Language.Context_SearchRecipes))
GameFunctions.Context.SearchForRecipesUsingItem(payload.ItemId);
if (ImGui.Selectable(Language.Context_SearchForItem))
GameFunctions.Context.SearchForItem(realItemId);
if (ImGui.Selectable(Language.Context_Link))
GameFunctions.Context.LinkItem(realItemId);
if (ImGui.Selectable(Language.Context_CopyItemName))
ImGui.SetClipboardText(name.TextValue);
}
private void DrawEventItemPopup(ItemPayload payload)
{
if (payload.Kind != ItemKind.EventItem)
return;
if (!Sheets.EventItemSheet.HasRow(payload.ItemId))
return;
var item = Sheets.EventItemSheet.GetRow(payload.ItemId);
if (
Plugin
.TextureProvider.GetFromGameIcon(new GameIconLookup(item.Icon))
.GetWrapOrDefault() is
{ } icon
)
InlineIcon(icon);
_chunkRenderer.DrawChunks(
ChunkUtil.ToChunks(item.Name.ToDalamudString(), ChunkSource.None, null).ToList(),
false
);
ImGui.Separator();
var realItemId = payload.RawItemId;
if (ImGui.Selectable(Language.Context_Link))
GameFunctions.Context.LinkItem(realItemId);
if (ImGui.Selectable(Language.Context_CopyItemName))
ImGui.SetClipboardText(item.Name.ToString());
}
private void DrawStatusPopup(StatusPayload status)
{
if (
Plugin
.TextureProvider.GetFromGameIcon(new GameIconLookup(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;
}
_chunkRenderer.DrawChunks(
ChunkUtil.ToChunks(builder.BuiltString, ChunkSource.None, null).ToList(),
false
);
ImGui.Separator();
if (ImGui.Selectable(Language.Context_Link))
{
GameFunctions.Context.LinkStatus(status.Status.RowId);
_inputBar.AppendPending(" <status>");
}
}
private void DrawUriPopup(UriPayload uri)
{
ImGui.TextUnformatted(string.Format(Language.Context_URLDomain, uri.Uri.Authority));
ImGuiUtil.WarningText(Language.Context_URLWarning, false);
ImGui.Separator();
if (ImGui.Selectable(Language.Context_OpenInBrowser))
WrapperUtil.TryOpenUri(uri.Uri);
if (ImGui.Selectable(Language.Context_CopyLink))
{
ImGui.SetClipboardText(uri.Uri.ToString());
WrapperUtil.AddNotification(
Language.Context_CopyLinkNotification,
NotificationType.Info
);
}
}
private const float MaxInlineIconSize = 32f;
private static void InlineIcon(IDalamudTextureWrap icon)
{
if (icon.Size.X <= 0 || icon.Size.Y <= 0)
return;
var width = (float)icon.Size.X;
var height = (float)icon.Size.Y;
var scale = Math.Min(1f, Math.Min(MaxInlineIconSize / width, MaxInlineIconSize / height));
var size = ImGuiHelpers.ScaledVector2(width * scale, height * scale);
var cursor = ImGui.GetCursorPos();
ImGui.Image(icon.Handle, size);
ImGui.SameLine();
ImGui.SetCursorPos(
cursor + new Vector2(size.X + 4, size.Y - ImGui.GetTextLineHeightWithSpacing())
);
}
internal void Hover(Payload payload) { }
internal unsafe void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) { }