feat(payload-handler): wire MoveTooltip (E6 completes PayloadHandler)
E6 closes out the PayloadHandler resurrection. MoveTooltip handles the cross-viewport AddonLifecycle tooltip-repositioning logic — reads MainWindow.LastViewport/LastWindowPos/LastWindowSize (from A1) to filter events and reposition the native item tooltip away from the chat window. Whole method marked `public unsafe void` per spec §4.2 (matches v1.5.6 exactly — avoids per-read unsafe-block scoping). LogWarning added on the unexpected-AddonArgs early-out branch (wires _logger into real use, prevents CS0414 unused-field warning). PayloadHandler is now feature-complete. F registers it in DI; G wires the AddonLifecycle.RegisterListener for MoveTooltip from a HostedService; H adds MessageList.AttachPayloadHandler setter-injection.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.Config;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
@@ -12,6 +15,7 @@ using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using HellionChat.Code;
|
||||
using HellionChat.Resources;
|
||||
using HellionChat.Themes;
|
||||
@@ -564,6 +568,104 @@ internal sealed class PayloadHandler
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void MoveTooltip(AddonEvent type, AddonArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"MoveTooltip received unexpected AddonArgs type: {ArgsType}",
|
||||
args?.GetType().Name ?? "<null>"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only move if the user has the "Next to Cursor" option selected
|
||||
if (
|
||||
!Plugin.GameConfig.TryGet(UiControlOption.DetailTrackingType, out uint selected)
|
||||
|| selected != 0
|
||||
)
|
||||
return;
|
||||
|
||||
if (_mainWindow.LastViewport != ImGuiHelpers.MainViewport.Handle)
|
||||
return;
|
||||
|
||||
var atk = args.Addon;
|
||||
if (atk.IsNull)
|
||||
return;
|
||||
|
||||
var atkBase = (AtkUnitBase*)atk.Address;
|
||||
if (atkBase->WindowNode == null)
|
||||
return;
|
||||
|
||||
if (!atkBase->IsVisible)
|
||||
return;
|
||||
|
||||
var component = atkBase->WindowNode->AtkResNode;
|
||||
var atkPos = new Vector2(component.ScreenX, component.ScreenY);
|
||||
var atkSize = new Vector2(
|
||||
component.GetWidth() * component.ScaleX,
|
||||
component.GetHeight() * component.GetScaleY()
|
||||
);
|
||||
|
||||
var chatRect = new MathUtil.Rectangle(
|
||||
_mainWindow.LastWindowPos,
|
||||
_mainWindow.LastWindowSize
|
||||
);
|
||||
var addonRect = new MathUtil.Rectangle(atkPos, atkSize);
|
||||
|
||||
if (!chatRect.HasOverlap(addonRect))
|
||||
return;
|
||||
|
||||
var viewportSize = ImGuiHelpers.MainViewport.Size;
|
||||
var isLeft = chatRect.SizeX < viewportSize.X / 2;
|
||||
var isTop = chatRect.SizeY < viewportSize.Y / 2;
|
||||
|
||||
var mousePos = ImGui.GetMousePos();
|
||||
|
||||
// addon spawned left of mouse cursor
|
||||
if (addonRect.X < mousePos.X)
|
||||
{
|
||||
if (isLeft)
|
||||
addonRect.X = (short)mousePos.X + 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isLeft)
|
||||
addonRect.X = Math.Max(0, (short)mousePos.X - 5 - addonRect.Width);
|
||||
}
|
||||
|
||||
if (!chatRect.HasOverlap(addonRect))
|
||||
{
|
||||
atkBase->SetPosition((short)addonRect.X, (short)addonRect.Y);
|
||||
return;
|
||||
}
|
||||
|
||||
// addon spawned above mouse cursor
|
||||
if (addonRect.Y < mousePos.Y)
|
||||
{
|
||||
if (isTop)
|
||||
addonRect.Y = (short)mousePos.Y + 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isTop)
|
||||
addonRect.Y = Math.Max(0, (short)mousePos.Y - 5 - addonRect.Height); // prevent it going below 0
|
||||
}
|
||||
|
||||
if (!chatRect.HasOverlap(addonRect))
|
||||
{
|
||||
atkBase->SetPosition((short)addonRect.X, (short)addonRect.Y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Spawning right/bottom of mouse cursor didn't solve the overlap, so we spawn it next to the chat
|
||||
var x = isLeft ? chatRect.SizeX : _mainWindow.LastWindowPos.X - atkSize.X;
|
||||
var y = Math.Clamp(chatRect.SizeY - atkSize.Y, 0, float.MaxValue);
|
||||
y -= isTop ? 0 : Plugin.Config.TooltipOffset; // offset to prevent cut-off on the bottom
|
||||
|
||||
atkBase->SetPosition((short)x, (short)y);
|
||||
}
|
||||
|
||||
private const float MaxInlineIconSize = 32f;
|
||||
|
||||
private static void InlineIcon(IDalamudTextureWrap icon)
|
||||
|
||||
Reference in New Issue
Block a user