feat(ui): implement UI/UX audit — i18n, HiDPI, theme, destructive-action & overflow fixes
Implements all 26 confirmed UI/UX audit findings plus the 4 dashboard/timeline tile labels. Verified across full-node + Lite + Windows builds (ctest green) and an adversarial diff review (one market-tab delete regression caught + fixed). i18n coverage: - Balance hero/quick-actions/toasts + dashboard & timeline tiles (Total Balance, Shielded, Transparent, Quick Send, Quick Receive, Click to open, Market) - Send: Review Send / Cancel / Paste / view-only tooltip / memo byte counter - Settings: Lite lifecycle errors, plaintext-RPC security warning, 8 toasts - Mining pool-payout tooltip - Resolve daemon_update_title double-assignment collision (new daemon_update_prompt_title) - 23 new keys translated into all 8 locales; CJK subset font rebuilt HiDPI: DPI-scale the Send amount bar, mining thread-tile clamp bounds, receive loading skeleton, and sidebar notification-badge insets. Light theme: theme-aware material::SurfaceOverlay() for balance bar tracks and row-hover highlights; contacts active-pill foreground uses OnPrimary(). Destructive actions: arm/confirm for portfolio-group delete, saved pool/worker remove, and avatar-image delete. Interaction/overflow: route balance star/eye buttons and the console fold-toggle through popup-safe guards; clip peer addr/subver; truncate the balance custom label; measure Settings tool-button widths; mining stepper disabled-state feedback; +/- stepper buttons restyled to match the thread tiles. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -83,7 +83,7 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
||||
// over-estimates the grid width and lets the thread cells overflow the card (e.g. at 150%).
|
||||
float estControlsW = availWidth - std::min(schema::UI().drawElement("tabs.mining", "button-max-width-clamp").size * dp, miningBtnMaxW) - miningBtnGap;
|
||||
float innerW = estControlsW - pad * 2;
|
||||
float cellSz = std::clamp(schema::UI().drawElement("tabs.mining", "cell-size").size * vs, schema::UI().drawElement("tabs.mining", "cell-min-size").size, schema::UI().drawElement("tabs.mining", "cell-max-size").sizeOr(42.0f));
|
||||
float cellSz = std::clamp(schema::UI().drawElement("tabs.mining", "cell-size").size * vs, schema::UI().drawElement("tabs.mining", "cell-min-size").size * dp, schema::UI().drawElement("tabs.mining", "cell-max-size").sizeOr(42.0f) * dp);
|
||||
float cellGap = std::max(schema::UI().drawElement("tabs.mining", "cell-gap-min").size, cellSz * schema::UI().drawElement("tabs.mining", "cell-gap-ratio").size);
|
||||
int cols = std::max(1, std::min(nOpts, (int)(innerW / (cellSz + cellGap))));
|
||||
int rows = (nOpts + cols - 1) / cols;
|
||||
@@ -162,14 +162,35 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
||||
const float sy = curY;
|
||||
ImVec2 savedCur = ImGui::GetCursorScreenPos();
|
||||
|
||||
material::IconButtonStyle sideStyle;
|
||||
sideStyle.color = OnSurfaceMedium();
|
||||
sideStyle.hoverBg = StateHover();
|
||||
// -/+ buttons drawn in the same style as the thread tiles (rounded rect + border + centered
|
||||
// glyph, same fill/border/rounding). Hit-tested with a real InvisibleButton so they stay
|
||||
// popup-safe.
|
||||
const float stepRound = schema::UI().drawElement("tabs.mining", "cell-rounding").size;
|
||||
// atBound: the stepper can't move further (already at 1 for "-" or max for "+"). When
|
||||
// at the bound the control is a no-op, so render it disabled: dim glyph/border, no hand
|
||||
// cursor, no tooltip — and swallow the click so it reads as inert.
|
||||
auto rectStepBtn = [&](const char* id, const char* glyph, float x, const char* tip, bool atBound) -> bool {
|
||||
ImGui::SetCursorScreenPos(ImVec2(x, sy));
|
||||
ImGui::InvisibleButton(id, ImVec2(sideW, fieldH));
|
||||
const bool hov = ImGui::IsItemHovered() && !atBound;
|
||||
const bool clk = ImGui::IsItemClicked() && !atBound;
|
||||
const ImVec2 mn(x, sy), mx(x + sideW, sy + fieldH);
|
||||
dl->AddRectFilled(mn, mx, hov ? WithAlpha(OnSurface(), 25) : WithAlpha(OnSurface(), 8), stepRound);
|
||||
dl->AddRect(mn, mx, WithAlpha(OnSurface(), atBound ? 15 : (hov ? 80 : 35)), stepRound);
|
||||
const ImVec2 gsz = stepFont->CalcTextSizeA(stepFont->LegacySize, FLT_MAX, 0, glyph);
|
||||
dl->AddText(stepFont, stepFont->LegacySize,
|
||||
ImVec2(x + (sideW - gsz.x) * 0.5f, sy + (fieldH - gsz.y) * 0.5f),
|
||||
WithAlpha(OnSurface(), atBound ? 30 : (hov ? 160 : 80)), glyph);
|
||||
if (hov) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (tip && tip[0]) material::Tooltip("%s", tip);
|
||||
}
|
||||
return clk;
|
||||
};
|
||||
|
||||
// "-" button
|
||||
sideStyle.tooltip = TR("mining_threads_minus_tooltip");
|
||||
ImGui::SetCursorScreenPos(ImVec2(sx, sy));
|
||||
if (material::IconButton("##ThreadMinus", ICON_MD_REMOVE, stepFont, ImVec2(sideW, fieldH), sideStyle))
|
||||
// "-" button (disabled at 1 thread)
|
||||
if (rectStepBtn("##ThreadMinus", ICON_MD_REMOVE, sx, TR("mining_threads_minus_tooltip"),
|
||||
s_selected_threads <= 1))
|
||||
applyThreads(s_selected_threads - 1);
|
||||
sx += sideW + g;
|
||||
|
||||
@@ -188,10 +209,9 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
||||
ImGui::PopStyleVar();
|
||||
sx += fieldW + g;
|
||||
|
||||
// "+" button
|
||||
sideStyle.tooltip = TR("mining_threads_plus_tooltip");
|
||||
ImGui::SetCursorScreenPos(ImVec2(sx, sy));
|
||||
if (material::IconButton("##ThreadPlus", ICON_MD_ADD, stepFont, ImVec2(sideW, fieldH), sideStyle))
|
||||
// "+" button (disabled at max_threads)
|
||||
if (rectStepBtn("##ThreadPlus", ICON_MD_ADD, sx, TR("mining_threads_plus_tooltip"),
|
||||
s_selected_threads >= max_threads))
|
||||
applyThreads(s_selected_threads + 1);
|
||||
|
||||
ImGui::SetCursorScreenPos(savedCur);
|
||||
@@ -738,15 +758,9 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
||||
dl->AddRect(cMin, cMax, WithAlpha(Primary(), (int)(160 + 60 * glow)), rounding, 0, schema::UI().drawElement("tabs.mining", "active-cell-border-thickness").size);
|
||||
} else if (active) {
|
||||
// Active but not mining: solid primary fill
|
||||
ImU32 pri = Primary();
|
||||
int priR = (pri >> 0) & 0xFF;
|
||||
int priG = (pri >> 8) & 0xFF;
|
||||
int priB = (pri >> 16) & 0xFF;
|
||||
ImU32 fillCol = hovered
|
||||
? IM_COL32(priR, priG, priB, 220)
|
||||
: IM_COL32(priR, priG, priB, 180);
|
||||
ImU32 fillCol = material::WithAlpha(Primary(), hovered ? 220 : 180);
|
||||
dl->AddRectFilled(cMin, cMax, fillCol, rounding);
|
||||
dl->AddRect(cMin, cMax, IM_COL32(priR, priG, priB, 255), rounding, 0, schema::UI().drawElement("tabs.mining", "cell-border-thickness").size);
|
||||
dl->AddRect(cMin, cMax, material::WithAlpha(Primary(), 255), rounding, 0, schema::UI().drawElement("tabs.mining", "cell-border-thickness").size);
|
||||
} else {
|
||||
// Inactive: dim outline
|
||||
ImU32 fillCol = hovered
|
||||
@@ -938,7 +952,7 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
||||
else if (poolBlockedBySolo)
|
||||
material::Tooltip("%s", TR("mining_stop_solo_for_pool"));
|
||||
else if (poolNeedsPayout)
|
||||
material::Tooltip("%s", "Enter a payout address first (generate a Z address)");
|
||||
material::Tooltip("%s", TR("mining_pool_needs_payout_tooltip"));
|
||||
else
|
||||
material::Tooltip("%s", isMiningActive ? TR("stop_mining") : TR("start_mining"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user