fix(popouts): the context menu never opened outside the main window
Right-clicking a name or an item inside a pop-out did nothing at all. One payload handler is shared by the main window, every pop-out and the input preview, and it holds a single popup state. The main window is registered first, so it draws first, finds no open popup in its own scope, reads that as "closed" and clears the state -- before the window that actually opened the popup gets its turn. The popup now belongs to the surface that opened it. The others leave its state alone instead of dropping it. The rule itself sits in its own helper because the handler pulls in Dalamud and cannot be loaded from a test.
This commit is contained in:
@@ -49,6 +49,7 @@ internal sealed class PayloadHandler
|
|||||||
public uint LastHoverCounter;
|
public uint LastHoverCounter;
|
||||||
|
|
||||||
private (Chunk, Payload?)? _popup;
|
private (Chunk, Payload?)? _popup;
|
||||||
|
private object? _popupOwner;
|
||||||
|
|
||||||
public PayloadHandler(
|
public PayloadHandler(
|
||||||
ThemeRegistry themes,
|
ThemeRegistry themes,
|
||||||
@@ -69,6 +70,10 @@ internal sealed class PayloadHandler
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Every chat surface shares this handler, so each one claims it before it
|
||||||
|
// renders. Without that the popup cannot tell whose click it belongs to.
|
||||||
|
internal object? ActiveSurface { get; set; }
|
||||||
|
|
||||||
internal void Draw()
|
internal void Draw()
|
||||||
{
|
{
|
||||||
DrawPopups();
|
DrawPopups();
|
||||||
@@ -84,7 +89,9 @@ internal sealed class PayloadHandler
|
|||||||
|
|
||||||
private void DrawPopups()
|
private void DrawPopups()
|
||||||
{
|
{
|
||||||
if (_popup == null)
|
// A foreign surface finds no open popup here and would otherwise read
|
||||||
|
// that as "closed" and drop the state before the owner gets to draw.
|
||||||
|
if (_popup == null || !PopupOwnership.Owns(_popupOwner, ActiveSurface))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var (chunk, payload) = _popup.Value;
|
var (chunk, payload) = _popup.Value;
|
||||||
@@ -93,6 +100,7 @@ internal sealed class PayloadHandler
|
|||||||
if (!popup.Success)
|
if (!popup.Success)
|
||||||
{
|
{
|
||||||
_popup = null;
|
_popup = null;
|
||||||
|
_popupOwner = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -934,6 +942,7 @@ internal sealed class PayloadHandler
|
|||||||
private void RightClickPayload(Chunk chunk, Payload? payload)
|
private void RightClickPayload(Chunk chunk, Payload? payload)
|
||||||
{
|
{
|
||||||
_popup = (chunk, payload);
|
_popup = (chunk, payload);
|
||||||
|
_popupOwner = ActiveSurface;
|
||||||
ImGui.OpenPopup(PopupId);
|
ImGui.OpenPopup(PopupId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,6 +158,11 @@ internal sealed class MessageList
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Claim the shared handler, so a popup opened here stays with this
|
||||||
|
// window instead of being cleared by whichever surface draws first.
|
||||||
|
if (_handler is not null)
|
||||||
|
_handler.ActiveSurface = this;
|
||||||
|
|
||||||
// No own ImRaii.Child here — MainWindow already wraps the message
|
// No own ImRaii.Child here — MainWindow already wraps the message
|
||||||
// area in one. Nesting would give the window two stacked scrolls
|
// area in one. Nesting would give the window two stacked scrolls
|
||||||
// and a runaway content-height computation.
|
// and a runaway content-height computation.
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ internal sealed class InputPreview : Window
|
|||||||
_handlerLender.ResetCounter();
|
_handlerLender.ResetCounter();
|
||||||
|
|
||||||
var handler = _handlerLender.Borrow();
|
var handler = _handlerLender.Borrow();
|
||||||
|
handler.ActiveSurface = this;
|
||||||
_chunkRenderer.DrawChunks(
|
_chunkRenderer.DrawChunks(
|
||||||
_previewMessage!.Content,
|
_previewMessage!.Content,
|
||||||
wrap: true,
|
wrap: true,
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace HellionChat.Util;
|
||||||
|
|
||||||
|
// Chat surfaces share one payload handler and all render it in the same frame.
|
||||||
|
// A popup belongs to the surface that opened it; the others must leave its
|
||||||
|
// state alone instead of reading "no popup here" as "popup was closed".
|
||||||
|
internal static class PopupOwnership
|
||||||
|
{
|
||||||
|
// Reference identity on purpose -- two surfaces can compare equal without
|
||||||
|
// being the same window.
|
||||||
|
internal static bool Owns(object? owner, object? surface) =>
|
||||||
|
owner is not null && ReferenceEquals(owner, surface);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user