fix(ui): prevent text/button cutoff and scale hand-drawn geometry at HiDPI

Findings from a UI-cutoff audit — each is a spot where an in-tree helper
(truncateMiddle / TruncateToWidth / measured button width / the *dpiScale/*hs
factors) was bypassed:

- notifications: the toast-pill height/padding/icon-gap were raw logical px while
  the icon/text drawn inside are DPI-baked, so they clipped the pill at HiDPI.
  Scale the geometry by dpiScale (not the already-scaled glyph metrics).
- settings: in the two-column NODE & SECURITY layout the data-directory path could
  overrun into the Daemon-binary column (shared draw list, no clip rect between
  them). Middle-ellipsize it to the column width; the full path stays in the
  tooltip + click-to-open + copy.
- send: the "Confirm & Send" button width came straight from the schema and was
  never measured against the label, clipping the Russian translation on the
  pre-broadcast dialog. Size to max(schema width, measured label + padding).
- balance: the recent-tx address-column offset missed the `* hs` DPI factor its
  sibling (amount-right-margin) uses, overlapping the type label at HiDPI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-10 22:11:07 -05:00
co-authored by Claude Opus 4.8
parent 393f3d147e
commit 001e85ac1a
4 changed files with 35 additions and 12 deletions
+11 -8
View File
@@ -32,19 +32,22 @@ void Notifications::render()
return v >= 0 ? v : fb;
};
// Status bar geometry
float sbHeight = S.window("components.status-bar").height;
if (sbHeight <= 0.0f) sbHeight = 30.0f;
// Status bar geometry. These are logical-px schema values; the icon/text drawn into the pill
// are DPI-baked, so scale the box by dpiScale to match the (also DPI-scaled) rendered status bar
// and keep the icon/text inside the pill at HiDPI.
const float dp = Layout::dpiScale();
float sbHeight = S.window("components.status-bar").height * dp;
if (sbHeight <= 0.0f) sbHeight = 30.0f * dp;
ImGuiViewport* viewport = ImGui::GetMainViewport();
float viewBottom = viewport->WorkPos.y + viewport->WorkSize.y;
float viewCenterX = viewport->WorkPos.x + viewport->WorkSize.x * 0.5f;
// Toast pill sizing — fit inside status bar with margin
float pillMarginY = nde("pill-margin-y", 3.0f);
float pillMarginY = nde("pill-margin-y", 3.0f) * dp;
float pillHeight = sbHeight - pillMarginY * 2.0f;
float pillPadX = nde("padding-x", 12.0f);
float pillRounding = nde("pill-rounding", 12.0f);
float pillPadX = nde("padding-x", 12.0f) * dp;
float pillRounding = nde("pill-rounding", 12.0f) * dp;
// Get accent color based on type — resolved from theme palette
ImVec4 accent_color, text_color;
@@ -89,7 +92,7 @@ void Notifications::render()
ImFont* textFont = material::Type().caption();
ImFont* iconFont = material::Type().iconSmall();
float iconW = iconFont ? iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0.0f, icon).x : 0.0f;
float iconGap = 4.0f;
float iconGap = 4.0f * dp;
float msgW = textFont ? textFont->CalcTextSizeA(textFont->LegacySize, FLT_MAX, 0.0f, notif.message.c_str()).x : 100.0f;
float pillWidth = pillPadX + iconW + iconGap + msgW + pillPadX;
// Clamp to reasonable bounds
@@ -122,7 +125,7 @@ void Notifications::render()
// Progress bar at bottom of pill (accent-colored), clipped to pill rounded
// corners. Draw a full-pill-size rounded rect and clip it to just the
// bottom-left progress strip so both bottom corners are respected.
float progH = nde("progress-bar-height", 2.0f);
float progH = nde("progress-bar-height", 2.0f) * dp;
float progW = pillWidth * (1.0f - progress);
if (progW > 0.0f) {
ImVec2 clipMin(pillX, pMax.y - progH);