fix(ui): cut-off/clipping — recent-tx collisions, updater note wrap, request-payment URI, console filter, icon-grid scroll

Fixes the cut-off/clipping bugs from the layout audit (all visible at the default
1280/1024 window sizes):

- Receive "Recent Received" rows: the amount collided with the relative-time
  ("+15.7500 DRGX14 days ago") and the type label touched the address at narrow
  widths. Use the shared short time format (formatTimeAgoShort, matching Overview),
  chain the amount's right edge off the measured time width, and start the address
  after the measured type-label width — so neither pair can collide.
- Daemon & xmrig updater verify-note: drawn unwrapped and clipped at the card's right
  edge; wrap it (PushTextWrapPos) within the already-reserved height.
- Request Payment: the three footer buttons shared one fixed width (clipping "Copy
  Full Address"); size each to its own label. The Payment URI overflowed a plain
  field; render it in a bordered read-only box (bounded, un-chunked).
- Console: the filter input shrank below its own placeholder (gone entirely at 1024);
  give it a min width >= the placeholder and drop the "N lines" count when the row
  can't fit both.
- Address-label "Choose Icon" grid: had NoScrollbar hiding most of the catalog with
  no cue; give it a real scrollbar.
- Overview "Recent Transactions": drop the 4th row at 1024 (it clipped off-screen) by
  capping to rows that fully fit the reserved height.
- Sidebar: reserve the unread-badge width in the nav-label centering so History/Chat
  labels no longer collide with their badge.

Verified at 1024 and 1280 across full-node + Lite + Windows (ctest green) and an
adversarial diff review (clean). Skipped the legacy settings_window overlay footer
(dead code / removal candidate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-19 10:11:55 -05:00
co-authored by Claude Opus 4.8
parent 9205addf55
commit a598217975
8 changed files with 128 additions and 53 deletions
+23 -27
View File
@@ -324,20 +324,9 @@ static void RenderAddressDropdown(App* app, float width) {
}
// ============================================================================
// Helpers: timeAgo / DrawRecvIcon (local copies — originals are static in send_tab)
// Helpers: DrawRecvIcon (local copy — original is static in send_tab).
// Relative time uses the shared util::formatTimeAgoShort ("14d ago"), matching Overview/Send.
// ============================================================================
static std::string recvTimeAgo(int64_t timestamp) {
if (timestamp <= 0) return "";
int64_t now = (int64_t)std::time(nullptr);
int64_t diff = now - timestamp;
if (diff < 0) diff = 0;
char buf[32];
if (diff < 60) { snprintf(buf, sizeof(buf), TR("time_seconds_ago"), (long long)diff); return buf; }
if (diff < 3600) { snprintf(buf, sizeof(buf), TR("time_minutes_ago"), (long long)(diff / 60)); return buf; }
if (diff < 86400) { snprintf(buf, sizeof(buf), TR("time_hours_ago"), (long long)(diff / 3600)); return buf; }
snprintf(buf, sizeof(buf), TR("time_days_ago"), (long long)(diff / 86400)); return buf;
}
static void DrawRecvIcon(ImDrawList* dl, float cx, float cy, float s, ImU32 col) {
dl->AddTriangleFilled(
ImVec2(cx, cy + s),
@@ -404,30 +393,37 @@ static void RenderRecentReceived(const AddressInfo& /* addr */,
rowDL->AddText(capFont, capFont->LegacySize,
ImVec2(txX, rowPos.y + 2.0f * dp), OnSurfaceMedium(), typeText);
// Address (second line)
float addrX = txX + S.drawElement("tabs.balance", "recent-tx-addr-offset").sizeOr(65.0f) * hs;
// Address — start it AFTER the measured type-label width (not a fixed offset that shrinks below
// the label at narrow widths), mirroring Overview's recent-tx list. The schema offset is a floor.
float typeW = capFont->CalcTextSizeA(capFont->LegacySize, 10000.0f, 0.0f, typeText).x;
float addrX = txX + std::max(S.drawElement("tabs.balance", "recent-tx-addr-offset").sizeOr(65.0f) * hs,
typeW + Layout::spacingSm());
std::string addrDisplay = util::truncateMiddle(tx.address,
(int)S.drawElement("tabs.balance", "recent-tx-addr-trunc").sizeOr(20.0f));
rowDL->AddText(capFont, capFont->LegacySize,
ImVec2(addrX, rowPos.y + 2.0f * dp), OnSurfaceDisabled(), addrDisplay.c_str());
// Amount (right-aligned, first line)
// Time ago — short "14d ago" form (shared helper, matches Overview/Send). Measure it
// first so the amount can chain its right edge off this width and never overlap.
std::string ago = util::formatTimeAgoShort(tx.timestamp);
ImVec2 agoSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000.0f, 0.0f, ago.c_str());
float rightEdge = rowPos.x + ImGui::GetContentRegionAvail().x;
float agoMargin = S.drawElement("tabs.balance", "recent-tx-time-margin").sizeOr(4.0f) * hs;
rowDL->AddText(capFont, capFont->LegacySize,
ImVec2(rightEdge - agoSz.x - agoMargin, rowPos.y + 2.0f * dp),
OnSurfaceDisabled(), ago.c_str());
// Amount (right-aligned, first line) — anchored to the LEFT of the time-ago text
// (measured width + a gap) so the two columns can never collide, whatever the strings.
snprintf(buf, sizeof(buf), "+%.4f %s", std::abs(tx.amount), DRAGONX_TICKER);
ImVec2 amtSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000.0f, 0.0f, buf);
float rightEdge = rowPos.x + ImGui::GetContentRegionAvail().x;
float amtX = rightEdge - amtSz.x - std::max(S.drawElement("tabs.balance", "amount-right-min-margin").size,
S.drawElement("tabs.balance", "amount-right-margin").size * hs);
float amtGap = std::max(S.drawElement("tabs.balance", "amount-right-min-margin").size,
S.drawElement("tabs.balance", "amount-right-margin").size * hs);
float amtRightEdge = rightEdge - agoSz.x - agoMargin - amtGap;
float amtX = amtRightEdge - amtSz.x;
rowDL->AddText(capFont, capFont->LegacySize,
ImVec2(amtX, rowPos.y + 2.0f * dp), recvCol, buf);
// Time ago
std::string ago = recvTimeAgo(tx.timestamp);
ImVec2 agoSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000.0f, 0.0f, ago.c_str());
rowDL->AddText(capFont, capFont->LegacySize,
ImVec2(rightEdge - agoSz.x - S.drawElement("tabs.balance", "recent-tx-time-margin").sizeOr(4.0f) * hs,
rowPos.y + 2.0f * dp),
OnSurfaceDisabled(), ago.c_str());
// Clickable row — hover highlight + navigate to History
float rowW = ImGui::GetContentRegionAvail().x;
ImVec2 rowEnd(rowPos.x + rowW, rowPos.y + rowH);