fix(ui): stop sidebar badges from displacing button text

The Chat/History nav buttons centered their icon+label in a region that
shrank when a badge (unread count / mining dot) was present, and the
"has badge" test read the LIVE count — so the text jumped sideways the
moment a count toggled (e.g. a new chat message arrived). Reserve badge
clearance by whether the page CAN show a badge (constant per item), center
in the full button width regardless, and cap the label with symmetric
clearance so a long label still can't run under the corner badge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-01 22:54:36 -05:00
parent d27f387d6d
commit a60a2f8e39

View File

@@ -679,15 +679,18 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
ImU32 textCol = selected ? Primary() : (pageNeedsUnlock ? OnSurfaceDisabled() : OnSurfaceMedium());
if (showLabels) {
// Reserve room for a badge (if this item will draw one) so the
// label centers in the space to the left of it instead of
// running underneath the badge circle.
bool itemHasBadge =
(item.page == NavPage::History && status.unconfirmedTxCount > 0) ||
(item.page == NavPage::Mining && status.miningActive) ||
(item.page == NavPage::Chat && status.chatUnreadCount > 0);
// The badge is a fixed top-right corner overlay, so it must NOT move
// the icon+label — otherwise the text jumps sideways the moment a live
// count toggles the badge on/off. Reserve clearance from whether the
// page CAN show a badge (constant per item), never from the current
// count, and keep the icon+label centered in the FULL button width so
// the text position and size stay identical with or without a badge.
bool itemBadgeCapable =
item.page == NavPage::History ||
item.page == NavPage::Mining ||
item.page == NavPage::Chat;
float badgeReserve = 0.0f;
if (itemHasBadge) {
if (itemBadgeCapable) {
bool dotOnlyReserve = (item.page == NavPage::Mining);
float badgeRReserve = dotOnlyReserve ? badgeRadiusDot : badgeRadiusNumber;
float badgeInsetXReserve = sde("badge-inset-x", 6.0f);
@@ -697,14 +700,17 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
ImFont* font = selected ? Type().subtitle2() : Type().body2();
float lblFsz = ScaledFontSize(font);
float btnW = indMax.x - indMin.x;
float maxLabelW = btnW - iconS * 2.0f - iconLabelGap - Layout::spacingXs() * 2 - badgeReserve;
// Clearance is symmetric (2x) because the group stays centered in the
// full width: reserving on both sides keeps the label's right edge clear
// of the right-side corner badge without shifting the center off-axis.
float maxLabelW = btnW - iconS * 2.0f - iconLabelGap - Layout::spacingXs() * 2 - badgeReserve * 2.0f;
ImVec2 labelSz = font->CalcTextSizeA(lblFsz, 1000.0f, 0.0f, NavLabel(item));
if (labelSz.x > maxLabelW && maxLabelW > 0) {
lblFsz *= maxLabelW / labelSz.x;
labelSz = font->CalcTextSizeA(lblFsz, 1000.0f, 0.0f, NavLabel(item));
}
float totalW = iconS * 2.0f + iconLabelGap + labelSz.x;
float btnCX = (indMin.x + indMax.x - badgeReserve) * 0.5f;
float btnCX = (indMin.x + indMax.x) * 0.5f;
float startX = btnCX - totalW * 0.5f;
DrawNavIcon(dl, item.page, startX + iconS, iconCY, iconS, textCol);