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:
@@ -40,6 +40,7 @@ static bool s_show_edit_dialog = false;
|
||||
static bool s_show_contacts_settings = false; // Contacts customization modal (gear button)
|
||||
static bool s_focus_edit_field = false; // focus the first field the frame the add/edit dialog opens
|
||||
static int s_confirm_delete_idx = -1; // armed storage index; a 2nd Delete confirms
|
||||
static int s_confirm_avatar_del_idx = -1; // armed avatar-library index; a 2nd badge click confirms the (irreversible) file delete
|
||||
static char s_edit_label[128] = "";
|
||||
static char s_edit_address[512] = "";
|
||||
static char s_edit_notes[512] = "";
|
||||
@@ -555,7 +556,7 @@ void RenderContactsTab(App* app)
|
||||
sdl->AddRectFilled(ImVec2(cMin.x + 2.0f * dp, cMin.y + 2.0f * dp),
|
||||
ImVec2(cMax.x - 2.0f * dp, cMax.y - 2.0f * dp),
|
||||
material::WithAlpha(material::Primary(), 210), (segH - 4.0f * dp) * 0.5f);
|
||||
ImU32 fg = active ? IM_COL32(255, 255, 255, 255) : (hov ? material::OnSurface() : material::OnSurfaceMedium());
|
||||
ImU32 fg = active ? material::OnPrimary() : (hov ? material::OnSurface() : material::OnSurfaceMedium());
|
||||
float igW = segIcoF->CalcTextSizeA(segIcoF->LegacySize, FLT_MAX, 0, segIco[i]).x;
|
||||
float lbW = btnFont->CalcTextSizeA(btnFont->LegacySize, FLT_MAX, 0, segLbl[i]).x;
|
||||
float gapI = 5.0f * dp;
|
||||
@@ -747,12 +748,16 @@ void RenderContactsTab(App* app)
|
||||
float dr = std::max(7.0f * dp, cell * 0.15f);
|
||||
ImVec2 dcc(mx.x - dr - 3.0f * dp, mn.y + dr + 3.0f * dp);
|
||||
bool dhov = ImGui::IsMouseHoveringRect(ImVec2(dcc.x-dr, dcc.y-dr), ImVec2(dcc.x+dr, dcc.y+dr));
|
||||
const int libIdx = n - 1;
|
||||
const bool delArmed = (s_confirm_avatar_del_idx == libIdx);
|
||||
ImGui::PushID(n);
|
||||
bool thumbClicked = ImGui::InvisibleButton("##avthumb", ImVec2(cell, cell));
|
||||
bool thumbHov = ImGui::IsItemHovered();
|
||||
ImGui::PopID();
|
||||
if (thumbHov || sel || hov) { // draw the delete badge
|
||||
gdl->AddCircleFilled(dcc, dr, dhov ? material::ReadableError() : IM_COL32(0, 0, 0, 175));
|
||||
if (thumbHov || sel || hov || delArmed) { // draw the delete badge (persist while armed)
|
||||
gdl->AddCircleFilled(dcc, dr, (dhov || delArmed) ? material::ReadableError() : IM_COL32(0, 0, 0, 175));
|
||||
// While armed, ring the badge so the "click again to delete" state is unmistakable.
|
||||
if (delArmed) gdl->AddCircle(dcc, dr + 1.5f * dp, material::ReadableError(), 0, 1.5f * dp);
|
||||
ImFont* xf = material::Type().iconSmall();
|
||||
float xsz = dr * 1.35f;
|
||||
ImVec2 xs = xf->CalcTextSizeA(xsz, FLT_MAX, 0, ICON_MD_CLOSE);
|
||||
@@ -760,11 +765,18 @@ void RenderContactsTab(App* app)
|
||||
}
|
||||
if (dhov) { // badge takes priority over selecting the thumbnail
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
material::Tooltip("%s", TR("delete"));
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) pendingDelete = n - 1;
|
||||
// Two-stage confirm: the first click arms this badge; a second click on the SAME
|
||||
// badge deletes the image file (fs::remove is irreversible — no undo).
|
||||
material::Tooltip("%s", delArmed ? TR("address_book_confirm_delete") : TR("delete"));
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
if (delArmed) { pendingDelete = libIdx; s_confirm_avatar_del_idx = -1; }
|
||||
else s_confirm_avatar_del_idx = libIdx; // arm; requires a 2nd deliberate click
|
||||
}
|
||||
} else {
|
||||
if (thumbHov) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (thumbClicked) s_edit_avatar = "img:" + path;
|
||||
// Any click that lands off this badge (selecting the thumbnail or elsewhere)
|
||||
// disarms it, so a stale armed badge can't be confirmed by an unrelated click.
|
||||
if (thumbClicked) { s_edit_avatar = "img:" + path; s_confirm_avatar_del_idx = -1; }
|
||||
}
|
||||
}
|
||||
col = (col + 1) % cols;
|
||||
|
||||
Reference in New Issue
Block a user