feat(autotell): wire temp-tab pop-outs to the channel-popout pool
This commit is contained in:
@@ -218,7 +218,7 @@ internal sealed class AutoTellTabsService : IDisposable
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tab? FindTempTab(string name, uint world)
|
internal static Tab? FindTempTab(string name, uint world)
|
||||||
{
|
{
|
||||||
var byTarget = Plugin.Config.Tabs.FirstOrDefault(t =>
|
var byTarget = Plugin.Config.Tabs.FirstOrDefault(t =>
|
||||||
t.IsTempTab
|
t.IsTempTab
|
||||||
@@ -256,21 +256,20 @@ internal sealed class AutoTellTabsService : IDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop-out-window cleanup is offline while the channel-popout pool
|
|
||||||
// is rebuilt — Tab.PopOut still flips on/off, the visible window
|
|
||||||
// disappears once the new pool comes online.
|
|
||||||
|
|
||||||
var dropped = victim.Tab;
|
var dropped = victim.Tab;
|
||||||
Plugin.Config.Tabs.RemoveAt(victim.Index);
|
Plugin.Config.Tabs.RemoveAt(victim.Index);
|
||||||
|
|
||||||
// Re-anchor the UI selection if it pointed at the dropped tab. This runs on
|
// Re-anchor the UI selection if it pointed at the dropped tab, and close any
|
||||||
// the PendingMessage worker thread and the repair mutates the re-seeded
|
// pop-out window the dropped tab owned. Both run on the PendingMessage worker
|
||||||
// tab's channel via OnTabActivated, so marshal it onto the framework thread
|
// thread and touch window state the Draw path reads (OnTabActivated re-seed +
|
||||||
// to serialize with Draw (reference_dalamud_framework_thread) — otherwise a
|
// the pool's Unbind), so marshal onto the framework thread to serialize with
|
||||||
// half-applied strip could race the input bar's send-routing read.
|
// Draw (reference_dalamud_framework_thread). TryClose is idempotent: a tab that
|
||||||
|
// was never popped is a silent no-op.
|
||||||
Plugin.Framework.RunOnFrameworkThread(() =>
|
Plugin.Framework.RunOnFrameworkThread(() =>
|
||||||
_plugin.MainWindow?.ResetActiveTabIfRemoved(dropped)
|
{
|
||||||
);
|
_plugin.ChannelPopoutPool.TryClose(dropped.Identifier);
|
||||||
|
_plugin.MainWindow?.ResetActiveTabIfRemoved(dropped);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SpawnTempTab((string Name, uint World) partner, Message currentMessage)
|
private void SpawnTempTab((string Name, uint World) partner, Message currentMessage)
|
||||||
@@ -282,13 +281,28 @@ internal sealed class AutoTellTabsService : IDisposable
|
|||||||
|
|
||||||
tab.AddMessage(currentMessage, unread: true);
|
tab.AddMessage(currentMessage, unread: true);
|
||||||
|
|
||||||
// Open as pop-out if configured (set before Tabs.Add for next render-tick)
|
// Open as pop-out if configured (flag set before Tabs.Add for the next render-tick).
|
||||||
if (Plugin.Config.AutoTellTabsOpenAsPopout)
|
if (Plugin.Config.AutoTellTabsOpenAsPopout)
|
||||||
{
|
{
|
||||||
tab.PopOut = true;
|
tab.PopOut = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugin.Config.Tabs.Add(tab);
|
Plugin.Config.Tabs.Add(tab);
|
||||||
|
|
||||||
|
// Actually open the pop-out window for the flagged tab — without this the
|
||||||
|
// flag was dead (a PopOut tab with no window). SpawnTempTab runs on the
|
||||||
|
// PendingMessage worker thread under _tempTabsLock; TryOpen does
|
||||||
|
// OnTabActivated + Bind (window state Draw reads), so marshal onto the
|
||||||
|
// framework thread. If the pool is full, drop the flag so it never claims a
|
||||||
|
// window it didn't get (flag/window parity).
|
||||||
|
if (tab.PopOut)
|
||||||
|
{
|
||||||
|
Plugin.Framework.RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
|
if (!_plugin.ChannelPopoutPool.TryOpen(tab))
|
||||||
|
tab.PopOut = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tab BuildTempTab(string playerName, uint worldRowId)
|
private static Tab BuildTempTab(string playerName, uint worldRowId)
|
||||||
@@ -427,8 +441,12 @@ internal sealed class AutoTellTabsService : IDisposable
|
|||||||
.Config.Tabs.Where(t => TabLifecycleHelpers.IsInUnpinnedPool(t) && t.PopOut)
|
.Config.Tabs.Where(t => TabLifecycleHelpers.IsInUnpinnedPool(t) && t.PopOut)
|
||||||
.Select(t => t.Identifier)
|
.Select(t => t.Identifier)
|
||||||
.ToList();
|
.ToList();
|
||||||
// Pop-out-window cleanup is offline; see Disconnect path above.
|
|
||||||
_ = poppedTempTabIds;
|
// Close each popped temp tab's window before the tabs leave the list.
|
||||||
|
// Logout is a framework-thread event (serialized with Draw), so no
|
||||||
|
// marshalling is needed here, unlike the worker-thread eviction path.
|
||||||
|
foreach (var id in poppedTempTabIds)
|
||||||
|
_plugin.ChannelPopoutPool.TryClose(id);
|
||||||
|
|
||||||
Plugin.Config.Tabs.RemoveAll(TabLifecycleHelpers.IsInUnpinnedPool);
|
Plugin.Config.Tabs.RemoveAll(TabLifecycleHelpers.IsInUnpinnedPool);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user