feat(payload-handler): wire DrawPopups + Integrations/ContextFooter/StringifyMessage
E2 fills the popup-dispatch layer of PayloadHandler:
- DrawPopups: switch-dispatch over payload types, with TODO(E3)/(E4)
markers at the deferred Draw{Player,Item,Status,Uri}Popup call sites
- Integrations: invokes registered IPC integrations (LogWindow.Plugin.Ipc
-> _ipc substitution per §4.2)
- ContextFooter: ScreenshotMode + HideChat checkboxes (Plugin.Config
static-bridge substitutions per §4.2)
- StringifyMessage: pure helper, 1:1 from v1.5.6
Adds PopupSfx const (E1 polish — needed by E5's Click for
UIGlobals.PlaySoundEffect). Removes #pragma CS0169 for _popup since
DrawPopups now writes the field; the warning no longer triggers.
E3 will fill DrawPlayerPopup + FindCharacterForPayload; E4 the
Item/Status/Uri popups; E5 the Hover/Click bodies; E6 MoveTooltip.
This commit is contained in:
@@ -1,8 +1,15 @@
|
|||||||
|
using System.Linq;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using HellionChat.Code;
|
||||||
|
using HellionChat.Resources;
|
||||||
using HellionChat.Themes;
|
using HellionChat.Themes;
|
||||||
using HellionChat.Ui.Components;
|
using HellionChat.Ui.Components;
|
||||||
using HellionChat.Ui.Windows;
|
using HellionChat.Ui.Windows;
|
||||||
|
using HellionChat.Util;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace HellionChat;
|
namespace HellionChat;
|
||||||
@@ -10,6 +17,7 @@ namespace HellionChat;
|
|||||||
internal sealed class PayloadHandler
|
internal sealed class PayloadHandler
|
||||||
{
|
{
|
||||||
private const string PopupId = "hellionchat-context-popup";
|
private const string PopupId = "hellionchat-context-popup";
|
||||||
|
private const uint PopupSfx = 1;
|
||||||
|
|
||||||
private readonly ThemeRegistry _themes;
|
private readonly ThemeRegistry _themes;
|
||||||
private readonly IpcManager _ipc;
|
private readonly IpcManager _ipc;
|
||||||
@@ -24,9 +32,7 @@ internal sealed class PayloadHandler
|
|||||||
public uint HoverCounter;
|
public uint HoverCounter;
|
||||||
public uint LastHoverCounter;
|
public uint LastHoverCounter;
|
||||||
|
|
||||||
#pragma warning disable CS0169 // used in DrawPopups (E2)
|
|
||||||
private (Chunk, Payload?)? _popup;
|
private (Chunk, Payload?)? _popup;
|
||||||
#pragma warning restore CS0169
|
|
||||||
|
|
||||||
public PayloadHandler(
|
public PayloadHandler(
|
||||||
ThemeRegistry themes,
|
ThemeRegistry themes,
|
||||||
@@ -49,7 +55,7 @@ internal sealed class PayloadHandler
|
|||||||
|
|
||||||
internal void Draw()
|
internal void Draw()
|
||||||
{
|
{
|
||||||
// TODO(E2): DrawPopups();
|
DrawPopups();
|
||||||
|
|
||||||
if (HandleTooltips && ++HoverCounter - LastHoverCounter > 1)
|
if (HandleTooltips && ++HoverCounter - LastHoverCounter > 1)
|
||||||
{
|
{
|
||||||
@@ -60,6 +66,157 @@ internal sealed class PayloadHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawPopups()
|
||||||
|
{
|
||||||
|
if (_popup == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var (chunk, payload) = _popup.Value;
|
||||||
|
|
||||||
|
using var popup = ImRaii.Popup(PopupId);
|
||||||
|
if (!popup.Success)
|
||||||
|
{
|
||||||
|
_popup = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var id = ImRaii.PushId(PopupId);
|
||||||
|
var drawn = false;
|
||||||
|
switch (payload)
|
||||||
|
{
|
||||||
|
case PlayerPayload player:
|
||||||
|
// TODO(E3): DrawPlayerPopup(chunk, player);
|
||||||
|
drawn = true;
|
||||||
|
break;
|
||||||
|
case ItemPayload item:
|
||||||
|
// TODO(E4): DrawItemPopup(item);
|
||||||
|
drawn = true;
|
||||||
|
break;
|
||||||
|
case UriPayload uri:
|
||||||
|
// TODO(E4): DrawUriPopup(uri);
|
||||||
|
drawn = true;
|
||||||
|
break;
|
||||||
|
case StatusPayload status:
|
||||||
|
// TODO(E4): DrawStatusPopup(status);
|
||||||
|
drawn = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextFooter(drawn, chunk);
|
||||||
|
Integrations(chunk, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Integrations(Chunk chunk, Payload? payload)
|
||||||
|
{
|
||||||
|
var registered = _ipc.Registered;
|
||||||
|
if (registered.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
var contentId = chunk.Message?.ContentId ?? 0;
|
||||||
|
var sender =
|
||||||
|
chunk.Message?.Sender.Select(c => c.Link).FirstOrDefault(p => p is PlayerPayload)
|
||||||
|
as PlayerPayload;
|
||||||
|
|
||||||
|
using var menu = ImRaii.Menu(Language.Context_Integrations);
|
||||||
|
if (!menu.Success)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var cursor = ImGui.GetCursorPos();
|
||||||
|
foreach (var integrationId in registered)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ipc.Invoke(
|
||||||
|
integrationId,
|
||||||
|
sender,
|
||||||
|
contentId,
|
||||||
|
payload,
|
||||||
|
chunk.Message?.SenderSource,
|
||||||
|
chunk.Message?.ContentSource
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error executing integration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor == ImGui.GetCursorPos())
|
||||||
|
{
|
||||||
|
using var pushedColor = ImRaii.PushColor(
|
||||||
|
ImGuiCol.Text,
|
||||||
|
ImGui.GetStyle().Colors[(int)ImGuiCol.TextDisabled]
|
||||||
|
);
|
||||||
|
ImGui.Text("No integrations available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ContextFooter(bool didCustomContext, Chunk chunk)
|
||||||
|
{
|
||||||
|
ImRaii.MenuDisposable menu = default;
|
||||||
|
if (didCustomContext)
|
||||||
|
{
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
// Only place these menu items in a submenu if we've already drawn
|
||||||
|
// custom context menu items based on the payload.
|
||||||
|
//
|
||||||
|
// It makes it much more convenient in the majority of cases to
|
||||||
|
// copy the message content without having to open a submenu.
|
||||||
|
menu = ImRaii.Menu(Plugin.PluginName);
|
||||||
|
if (!menu.Success)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Checkbox(Language.Context_ScreenshotMode, ref Plugin.Config.ScreenshotMode);
|
||||||
|
|
||||||
|
if (ImGui.Selectable(Language.Context_HideChat))
|
||||||
|
Plugin.Config.HideChat = true;
|
||||||
|
|
||||||
|
if (chunk.Message is { } message)
|
||||||
|
{
|
||||||
|
if (ImGui.Selectable(Language.Context_Copy))
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText(StringifyMessage(message, true));
|
||||||
|
WrapperUtil.AddNotification(Language.Context_CopySuccess, NotificationType.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only show a separate "Copy content" option if the message has
|
||||||
|
// Sender chunks, so it doesn't show for system messages.
|
||||||
|
if (message.Sender.Count > 0 && ImGui.Selectable(Language.Context_CopyContent))
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText(StringifyMessage(message));
|
||||||
|
WrapperUtil.AddNotification(
|
||||||
|
Language.Context_CopyContentSuccess,
|
||||||
|
NotificationType.Info
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var pushedColor = ImRaii.PushColor(
|
||||||
|
ImGuiCol.Text,
|
||||||
|
ImGui.GetStyle().Colors[(int)ImGuiCol.TextDisabled]
|
||||||
|
);
|
||||||
|
ImGui.TextUnformatted(message.Code.Type.Name());
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string StringifyMessage(Message? message, bool withSender = false)
|
||||||
|
{
|
||||||
|
if (message == null)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var chunks = withSender ? message.Sender.Concat(message.Content) : message.Content;
|
||||||
|
return chunks
|
||||||
|
.Where(chunk => chunk is TextChunk)
|
||||||
|
.Cast<TextChunk>()
|
||||||
|
.Select(text => text.Content)
|
||||||
|
.Aggregate(string.Concat);
|
||||||
|
}
|
||||||
|
|
||||||
internal void Hover(Payload payload) { }
|
internal void Hover(Payload payload) { }
|
||||||
|
|
||||||
internal unsafe void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) { }
|
internal unsafe void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) { }
|
||||||
|
|||||||
Reference in New Issue
Block a user