Files
HellionChat/HellionChat/Ui/Components/TopTabBar.cs
T
JonKazama-Hellion 5dfe8e3b49 fix(unread): restore the tab unread badge and fix the post-F2 unread decision
The v1.8.x sidebar/top-bar rebuild never re-rendered the unread dot, so inactive tabs showed no badge even though the counter was tracked. Draw it again top-right of the tab icon in both Sidebar and TopTabBar, gated on !active && UnreadMode != None && Unread > 0, and zero the active tab's counter every frame (1.5.6 convention) so the dot only ever shows on tabs you are not looking at.

The unread decision moves to MessageManager.ShouldCountUnread and snapshots the active tab + whether it shows the message once before the loop: Unseen suppresses unread on an inactive tab only when the active (real, post-F2) tab also shows that message. Adds SidebarUnreadDotStep (render) and UnreadDecisionStep (decision) self-tests (step count 32 -> 34).
2026-06-13 18:49:27 +02:00

68 lines
2.2 KiB
C#

using System.Numerics;
using Dalamud.Bindings.ImGui;
using HellionChat.Util;
namespace HellionChat.Ui.Components;
// Horizontal tab strip — the alternative MainWindow layout to the Sidebar.
// Selection drives the same shared EnsureCurrentChannel path; pop-out is the
// same pool.TryOpen affordance as the sidebar (right-click context menu).
internal sealed class TopTabBar
{
private readonly Windows.ChannelPopoutPool _pool;
public TopTabBar(Windows.ChannelPopoutPool pool)
{
_pool = pool;
}
public void Draw(IList<Tab> tabs, ref Tab? activeTab)
{
for (var i = 0; i < tabs.Count; i++)
{
var tab = tabs[i];
if (i > 0)
ImGui.SameLine();
var selected = ReferenceEquals(tab, activeTab);
if (
ImGui.Selectable(
$"{tab.Name}###hellion_toptab_{i}",
selected,
ImGuiSelectableFlags.None,
new Vector2(0, 0)
)
)
{
var previous = activeTab;
activeTab = tab;
TabLifecycleHelpers.OnTabActivated(tab, previous);
}
// 1.5.6-parity unread dot at the item's top-right. Gate on the
// POST-click selection (not the frame-start 'selected') so clicking a
// tab suppresses its dot the same frame, like the sidebar. The active
// tab is also zeroed every frame (MainWindow.Draw).
if (
!ReferenceEquals(tab, activeTab)
&& tab.UnreadMode != UnreadMode.None
&& tab.Unread > 0
)
{
var max = ImGui.GetItemRectMax();
var min = ImGui.GetItemRectMin();
var danger = ColourUtil.RgbaToAbgr(
Plugin.Instance.ThemeRegistry.Active.Colors.StatusDanger
);
ImGui
.GetWindowDrawList()
.AddCircleFilled(new Vector2(max.X - 4f, min.Y + 4f), 3.5f, danger, 12);
}
TabContextMenu.Draw(tab, $"toptab_ctx_{i}", _pool);
}
ImGui.Separator();
}
}