feat(main-window): wire Lender + handler.Draw() (A2, MessageList popups)

A2 completes the deferred Lender-cycle from A1 and addresses the
handler.Draw() gap identified in I code-quality-review:

- MainWindow ctor takes Lender<PayloadHandler> as new param (DI-reg
  extended in PluginHostFactory); _handlerLender.ResetCounter() called
  at top of Draw() as primary pool-reset path (InputPreview has the
  secondary defensive fallback for MainWindow-closed edge case)

- MessageList.DrawHandlerPopups() new passthrough method
  (=> _handler?.Draw()) provides the per-frame popup-tick that
  PayloadHandler needs to render the right-click context popup;
  MainWindow.Draw() calls it after the message-list body renders

Without this fix, right-clicking a player/item/status in the chat log
would silently fail to open a popup (handler.Draw() never fired for the
MessageList's _handler). Phase 3 smoke steps 3/4/5 unblocked.

Polish-Sweep + Smoke-Gate are the last cycle-tasks.
This commit is contained in:
2026-05-27 23:29:18 +02:00
parent cba6a16f8e
commit d1bfddd9b8
3 changed files with 15 additions and 2 deletions
+9 -1
View File
@@ -2,6 +2,7 @@ using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using HellionChat.Util;
namespace HellionChat.Ui.Windows;
@@ -26,6 +27,7 @@ internal sealed class MainWindow : Window
private readonly Components.MessageList _messages;
private readonly Components.InputBar _input;
private readonly Components.StatusBar _status;
private readonly Lender<PayloadHandler> _handlerLender;
private Tab? _activeTab;
@@ -38,7 +40,8 @@ internal sealed class MainWindow : Window
Components.Sidebar sidebar,
Components.MessageList messages,
Components.InputBar input,
Components.StatusBar status
Components.StatusBar status,
Lender<PayloadHandler> handlerLender
)
: base($"{Plugin.PluginName}###hellion-main")
{
@@ -47,6 +50,7 @@ internal sealed class MainWindow : Window
_messages = messages;
_input = input;
_status = status;
_handlerLender = handlerLender;
Size = new Vector2(DefaultWidth, DefaultHeight);
SizeCondition = ImGuiCond.FirstUseEver;
@@ -92,6 +96,9 @@ internal sealed class MainWindow : Window
LastViewport = ImGui.GetWindowViewport().Handle;
}
// Primary pool-reset path; InputPreview has a defensive fallback for the MainWindow-closed edge case.
_handlerLender.ResetCounter();
// First-frame seed: the active tab defaults to the first persisted
// tab so the message list isn't empty on a clean session.
if (_activeTab is null && Plugin.Config.Tabs.Count > 0)
@@ -105,6 +112,7 @@ internal sealed class MainWindow : Window
DrawBody();
}
_messages.DrawHandlerPopups();
_status.Draw(_activeTab);
}