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:
@@ -137,6 +137,12 @@ struct PfEditState {
|
||||
};
|
||||
static PfEditState s_pfEdit;
|
||||
|
||||
// Two-stage delete arm/confirm for the master-list per-row delete icon (mirrors the
|
||||
// contacts tab's s_confirm_delete_idx): first click on a row's trash arms it (icon turns
|
||||
// red); a second click on the SAME row within the loop confirms the erase. Hovering/clicking
|
||||
// a different row, or clicking off the icons, disarms. -1 = nothing armed.
|
||||
static int s_pfConfirmDelIdx = -1;
|
||||
|
||||
// The pure price-series math lives in data/market_series.h; the selected chart range is
|
||||
// s_mkt.chartInterval (0=Live 1=1H 2=1D 3=1W 4=1M, session-scoped, default 1M).
|
||||
|
||||
@@ -837,6 +843,7 @@ static void RenderPortfolioEditor(App* app)
|
||||
float rowH = 46.0f * dp;
|
||||
float delSlot = 36.0f * dp; // reserved trailing space for the delete icon + margin
|
||||
int clickedSel = -999, delRow = -1; // deferred: don't mutate `entries` mid-loop
|
||||
bool armedThisFrame = false; // an arm-click also fires the row Selectable; don't let it disarm
|
||||
for (int vi = 0; vi < (int)vis.size(); vi++) {
|
||||
int i = vis[vi];
|
||||
ImGui::PushID(i);
|
||||
@@ -877,6 +884,7 @@ static void RenderPortfolioEditor(App* app)
|
||||
// Per-row delete icon (larger, inset from the edge). Hit-tested manually so it is
|
||||
// NOT an overlapping ImGui item over the row Selectable (which asserted on hover).
|
||||
if (rowHov || selRow) {
|
||||
bool armed = (s_pfConfirmDelIdx == i); // this row is arm-confirmed
|
||||
ImFont* delFont = Type().iconMed();
|
||||
float ds = delFont->LegacySize;
|
||||
ImVec2 dc(rmx.x - Layout::spacingMd() - ds * 0.5f, rmn.y + rowH * 0.5f);
|
||||
@@ -884,11 +892,20 @@ static void RenderPortfolioEditor(App* app)
|
||||
bool dhov = ImGui::IsMouseHoveringRect(dmn, dmx);
|
||||
if (dhov) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) delRow = i;
|
||||
// Hovering a *different* row's trash disarms the previously armed one.
|
||||
if (!armed && s_pfConfirmDelIdx >= 0) s_pfConfirmDelIdx = -1;
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
if (armed) delRow = i; // 2nd click on the armed row erases
|
||||
else { s_pfConfirmDelIdx = i; // 1st click arms this row...
|
||||
armedThisFrame = true; } // ...and must survive the row-select this same click triggers
|
||||
}
|
||||
}
|
||||
ImVec2 isz = delFont->CalcTextSizeA(ds, FLT_MAX, 0, ICON_MD_DELETE_OUTLINE);
|
||||
// A filled trash glyph while armed (vs outline) reinforces the recolor.
|
||||
const char* delGlyph = armed ? ICON_MD_DELETE : ICON_MD_DELETE_OUTLINE;
|
||||
ImU32 delCol = armed ? ReadableError() : (dhov ? Error() : OnSurfaceMedium());
|
||||
ImVec2 isz = delFont->CalcTextSizeA(ds, FLT_MAX, 0, delGlyph);
|
||||
mdl->AddText(delFont, ds, ImVec2(dc.x - isz.x * 0.5f, dc.y - isz.y * 0.5f),
|
||||
dhov ? Error() : OnSurfaceMedium(), ICON_MD_DELETE_OUTLINE);
|
||||
delCol, delGlyph);
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
@@ -905,10 +922,16 @@ static void RenderPortfolioEditor(App* app)
|
||||
else if (delRow == s_pfEdit.sel) s_pfEdit.sel = std::min(delRow, (int)es.size() - 1);
|
||||
PortfolioBeginEdit(app, s_pfEdit.sel);
|
||||
}
|
||||
} else if (clickedSel != -999) {
|
||||
s_pfConfirmDelIdx = -1; // erase done — storage indices shifted, drop the arm
|
||||
} else if (clickedSel != -999 && !armedThisFrame) {
|
||||
pfCommitIfNeeded(app); // auto-save the current group before switching
|
||||
PortfolioBeginEdit(app, clickedSel);
|
||||
s_pfConfirmDelIdx = -1; // switching rows disarms any pending delete
|
||||
}
|
||||
// A left-click that hit no row/action (empty list space) disarms a pending delete.
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
|
||||
!ImGui::IsAnyItemHovered())
|
||||
s_pfConfirmDelIdx = -1;
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
Reference in New Issue
Block a user