feat(tabs): give pinning a way in, and a way back out
Pinning has been complete since v1.4.7: pools, a cap of five, persistence, logout symmetry, the notification when the cap is hit. The menu that called it went away, and nothing has called it since. That is not only a missing feature, it is a dead end in saved data. A tab pinned in v1.5.6 survives every save and load and permanently occupies one of the five slots, and there has been no way anywhere to release it. Unpin is the reason this task exists. So the context menu grows a pin section for temp tabs, and the sidebar grows the marker that says which rows are pinned -- a small thumbtack in the icon's lower left, drawn from the row's own rectangle so it claims no layout of its own. The unread badge owns the upper right, and badges in this sidebar are where drawing into unreserved space caught this project last. At the cap the item is disabled rather than hidden: that is a state the user can undo by unpinning something, and the tooltip switches to say which. Five translated strings that had no caller now have one. Promote-to-permanent stays out. It was removed on purpose after a tester kept hitting it by accident. Reconnecting every method that lost its caller, without asking why it lost it, would rebuild the problem this cycle is supposed to be cleaning up.
This commit is contained in:
@@ -44,6 +44,10 @@ internal sealed class Sidebar
|
||||
internal int LastRenderedGreetedGlyphCount;
|
||||
internal int LastRenderedUnreadDotCount;
|
||||
|
||||
// Small enough to read as a marker on the icon rather than as a second icon
|
||||
// beside it.
|
||||
private const float PinGlyphScale = 0.6f;
|
||||
|
||||
// B3-4 render observability: section headers actually drawn this frame.
|
||||
// Incremented only in the real header branch; reset at Draw start.
|
||||
internal int LastDrawnSectionHeaderCount;
|
||||
@@ -348,14 +352,39 @@ internal sealed class Sidebar
|
||||
// height would misplace the glyph at any other body size.
|
||||
var iconStr = icon.ToIconString();
|
||||
var iconSize = ImGui.CalcTextSize(iconStr);
|
||||
dl.AddText(
|
||||
var iconPos =
|
||||
origin
|
||||
+ new Vector2(iconInset + contentX, MetricsMath.CenterY(RowHeight, iconSize.Y)),
|
||||
iconColor,
|
||||
iconStr
|
||||
);
|
||||
+ new Vector2(iconInset + contentX, MetricsMath.CenterY(RowHeight, iconSize.Y));
|
||||
dl.AddText(iconPos, iconColor, iconStr);
|
||||
iconRight = iconInset + contentX + iconSize.X;
|
||||
|
||||
// Pinned marker: a small thumbtack tucked into the icon's lower
|
||||
// left. Drawn from the same font push and inside the row rectangle
|
||||
// the InvisibleButton already reserved, so it claims no layout of
|
||||
// its own -- badges in this very sidebar are where "draw into
|
||||
// unreserved space" caught this project last.
|
||||
//
|
||||
// Lower left because the unread dot owns the upper right.
|
||||
if (tab.IsPinned)
|
||||
{
|
||||
var pinStr = FontAwesomeIcon.Thumbtack.ToIconString();
|
||||
var pinFontSize = ImGui.GetFontSize() * PinGlyphScale;
|
||||
var pinSize = ImGui.CalcTextSize(pinStr) * PinGlyphScale;
|
||||
dl.AddText(
|
||||
ImGui.GetFont(),
|
||||
pinFontSize,
|
||||
iconPos + new Vector2(-pinSize.X * 0.45f, iconSize.Y - pinSize.Y * 0.75f),
|
||||
_palette.Abgr(Token.AccentPrimary, colors),
|
||||
pinStr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Only for pinned rows, and only in the sidebar's own hover state --
|
||||
// this is the one place a user meets the marker without having opened
|
||||
// the menu that produced it.
|
||||
if (tab.IsPinned && surfaceHovered)
|
||||
ImGuiUtil.Tooltip(HellionStrings.PinTab_PinnedTooltip);
|
||||
|
||||
if (expanded)
|
||||
dl.AddText(origin + new Vector2(iconRight + 6f * scale, contentY), textAbgr, tab.Name);
|
||||
|
||||
@@ -97,6 +97,63 @@ internal static class TabContextMenu
|
||||
|
||||
if (ImGui.MenuItem("Pop Out"))
|
||||
pool.TryOpen(tab);
|
||||
|
||||
DrawPinControls(tab);
|
||||
}
|
||||
|
||||
// Pinning has been complete since v1.4.7 -- pools, cap, persistence, logout
|
||||
// symmetry, the notification -- and has had no way in since the menu that
|
||||
// called it was removed.
|
||||
//
|
||||
// That left a dead end in saved data, which is the real reason this is here:
|
||||
// a tab pinned in v1.5.6 survives every save and load, permanently occupying
|
||||
// one of five pool slots, with nothing anywhere to release it.
|
||||
//
|
||||
// Promote-to-permanent deliberately does not come back. It was removed on
|
||||
// purpose after a tester kept hitting it by accident, and reconnecting every
|
||||
// caller-less method without asking why it lost its caller would rebuild the
|
||||
// problem.
|
||||
private static void DrawPinControls(Tab tab)
|
||||
{
|
||||
if (!tab.IsTempTab)
|
||||
return;
|
||||
|
||||
// Instance property today, not the static the old menu reached for.
|
||||
var service = Plugin.Instance.AutoTellTabsService;
|
||||
if (service is null)
|
||||
return;
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
if (tab.IsPinned)
|
||||
{
|
||||
if (ImGui.MenuItem(HellionStrings.PinTab_MenuUnpin))
|
||||
{
|
||||
service.Unpin(tab);
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var atCap = service.PinnedTempTabCount >= AutoTellTabsService.MaxPinnedTempTabs;
|
||||
|
||||
// Disabled rather than absent: the cap is a state the user can undo by
|
||||
// unpinning something, and the tooltip below is what says so.
|
||||
if (ImGui.MenuItem(HellionStrings.PinTab_MenuPin, enabled: !atCap) && service.TryPin(tab))
|
||||
ImGui.CloseCurrentPopup();
|
||||
|
||||
if (!ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||
return;
|
||||
|
||||
ImGuiUtil.Tooltip(
|
||||
atCap
|
||||
? string.Format(
|
||||
HellionStrings.PinTab_LimitReached,
|
||||
AutoTellTabsService.MaxPinnedTempTabs
|
||||
)
|
||||
: HellionStrings.PinTab_PinTooltip
|
||||
);
|
||||
}
|
||||
|
||||
// The flush depends on Draw running once more for this tab. If it never does —
|
||||
|
||||
Reference in New Issue
Block a user