feat(sidebar): pinned section, dimmed pin glyph, configurable width
Smoke-test round 3 feedback from Jin:
- Sidebar now groups tabs into three sections rendered in this order:
persistent → pinned TempTabs → unpinned TempTabs. Each TempTab
section carries its own divider header ("Angepinnt (n)" / "Aktive
Tells (n)"). Plugin.Config.Tabs order is untouched — only the
display order changes, so tabI still mirrors the real index and
LastTab/WantedTab stay consistent.
- The thumbtack glyph overlay on a pinned tab dropped from accent
colour at full alpha to TextMuted at ~47% alpha. The section header
is now the primary discoverability cue; the glyph is just a per-tab
confirmation hint.
- Sidebar width is now a Config field (default 44, range 44-160).
Slider lives in Theme & Layout under the existing Sidebar-Tab-View
toggle. The icon button inside each row stretches with the width so
a widened sidebar doesn't leave the icon floating in dead space.
This commit is contained in:
@@ -1673,6 +1673,30 @@ public sealed class ChatLogWindow : Window
|
||||
Plugin.WantedTab = null;
|
||||
}
|
||||
|
||||
// Sidebar render order: persistent tabs in their original Plugin.Config.Tabs
|
||||
// position, then pinned TempTabs, then unpinned TempTabs. Returns indices
|
||||
// into Plugin.Config.Tabs so tabI in the loop body still mirrors the real
|
||||
// list position (LastTab / WantedTab stay consistent).
|
||||
private static List<int> BuildSidebarRenderOrder()
|
||||
{
|
||||
var tabs = Plugin.Config.Tabs;
|
||||
var persistent = new List<int>(tabs.Count);
|
||||
var pinned = new List<int>();
|
||||
var unpinned = new List<int>();
|
||||
for (var i = 0; i < tabs.Count; i++)
|
||||
{
|
||||
if (TabLifecycleHelpers.IsInPinnedPool(tabs[i]))
|
||||
pinned.Add(i);
|
||||
else if (TabLifecycleHelpers.IsInUnpinnedPool(tabs[i]))
|
||||
unpinned.Add(i);
|
||||
else
|
||||
persistent.Add(i);
|
||||
}
|
||||
persistent.AddRange(pinned);
|
||||
persistent.AddRange(unpinned);
|
||||
return persistent;
|
||||
}
|
||||
|
||||
private void DrawTabSidebar()
|
||||
{
|
||||
var currentTab = -1;
|
||||
@@ -1685,7 +1709,8 @@ public sealed class ChatLogWindow : Window
|
||||
if (!tabTable.Success)
|
||||
return;
|
||||
|
||||
ImGui.TableSetupColumn("tabs", ImGuiTableColumnFlags.WidthFixed, 44f);
|
||||
var sidebarWidth = Math.Clamp(Plugin.Config.SidebarWidth, 44, 160);
|
||||
ImGui.TableSetupColumn("tabs", ImGuiTableColumnFlags.WidthFixed, sidebarWidth);
|
||||
ImGui.TableSetupColumn("chat", ImGuiTableColumnFlags.WidthStretch, 1);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
@@ -1704,23 +1729,42 @@ public sealed class ChatLogWindow : Window
|
||||
ImGui.Dummy(new Vector2(0, ImGui.GetFrameHeightWithSpacing()));
|
||||
|
||||
var previousTab = Plugin.CurrentTab;
|
||||
// Divider rendered once before the first temp tab with a live unit counter.
|
||||
// Render order: persistent → pinned TempTabs → unpinned TempTabs.
|
||||
// Underlying Plugin.Config.Tabs order is untouched (tabI mirrors
|
||||
// the real list index), only the display sequence groups by
|
||||
// section so each section can carry its own divider header.
|
||||
var renderOrder = BuildSidebarRenderOrder();
|
||||
var pinnedHeaderRendered = false;
|
||||
var tempTabHeaderRendered = false;
|
||||
var tempTabCount = Plugin.Config.Tabs.Count(t => t.IsTempTab);
|
||||
var pinnedCount = Plugin.Config.Tabs.Count(TabLifecycleHelpers.IsInPinnedPool);
|
||||
var unpinnedTempCount = Plugin.Config.Tabs.Count(
|
||||
TabLifecycleHelpers.IsInUnpinnedPool
|
||||
);
|
||||
|
||||
for (var tabI = 0; tabI < Plugin.Config.Tabs.Count; tabI++)
|
||||
foreach (var tabI in renderOrder)
|
||||
{
|
||||
var tab = Plugin.Config.Tabs[tabI];
|
||||
if (tab.PopOut)
|
||||
continue;
|
||||
|
||||
if (tab.IsTempTab && !tempTabHeaderRendered)
|
||||
if (TabLifecycleHelpers.IsInPinnedPool(tab) && !pinnedHeaderRendered)
|
||||
{
|
||||
ImGui.Separator();
|
||||
if (!Plugin.Config.AutoTellTabsCompactDisplay)
|
||||
{
|
||||
ImGui.TextDisabled(
|
||||
$"{HellionStrings.AutoTellTabs_SectionHeader} ({tempTabCount})"
|
||||
$"{HellionStrings.PinTab_SectionHeader} ({pinnedCount})"
|
||||
);
|
||||
}
|
||||
pinnedHeaderRendered = true;
|
||||
}
|
||||
else if (TabLifecycleHelpers.IsInUnpinnedPool(tab) && !tempTabHeaderRendered)
|
||||
{
|
||||
ImGui.Separator();
|
||||
if (!Plugin.Config.AutoTellTabsCompactDisplay)
|
||||
{
|
||||
ImGui.TextDisabled(
|
||||
$"{HellionStrings.AutoTellTabs_SectionHeader} ({unpinnedTempCount})"
|
||||
);
|
||||
}
|
||||
tempTabHeaderRendered = true;
|
||||
@@ -1809,9 +1853,12 @@ public sealed class ChatLogWindow : Window
|
||||
using (ImRaii.PushColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(iconColor)))
|
||||
using (Plugin.FontManager.FontAwesome.Push())
|
||||
{
|
||||
// Button stretches with the configured sidebar width so a
|
||||
// user-widened sidebar feels intentional, not a 36px icon
|
||||
// floating in empty space.
|
||||
clicked = ImGui.Button(
|
||||
$"{icon.ToIconString()}##sidebar-tab-{tabI}",
|
||||
new Vector2(36f, ImGui.GetFrameHeight())
|
||||
new Vector2(sidebarWidth - 8f, ImGui.GetFrameHeight())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1871,22 +1918,23 @@ public sealed class ChatLogWindow : Window
|
||||
);
|
||||
}
|
||||
|
||||
// Pin indicator: small thumbtack glyph top-left of the icon.
|
||||
// Sits opposite the unread dot so they never collide.
|
||||
// Pin indicator: subtle thumbtack glyph top-left of the icon.
|
||||
// Muted colour because the "Pinned" section header already
|
||||
// groups these tabs visually — this is just a per-tab
|
||||
// confirmation glyph, not the primary discoverability cue.
|
||||
if (tab.IsPinned)
|
||||
{
|
||||
var min = ImGui.GetItemRectMin();
|
||||
const float pinPadding = 2f;
|
||||
const float pinPadding = 1f;
|
||||
var pinPos = new Vector2(min.X + pinPadding, min.Y + pinPadding);
|
||||
var pinColor = theme.Colors.TextMuted;
|
||||
// Dim further so the glyph reads as a hint, not a badge.
|
||||
var pinAbgr = ColourUtil.RgbaToAbgr(pinColor) & 0x77FFFFFFu;
|
||||
using (Plugin.FontManager.FontAwesome.Push())
|
||||
{
|
||||
ImGui
|
||||
.GetWindowDrawList()
|
||||
.AddText(
|
||||
pinPos,
|
||||
ColourUtil.RgbaToAbgr(theme.Colors.Accent),
|
||||
FontAwesomeIcon.Thumbtack.ToIconString()
|
||||
);
|
||||
.AddText(pinPos, pinAbgr, FontAwesomeIcon.Thumbtack.ToIconString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user