From a13190a6df6fe71aaa34d2bbf178687739652337 Mon Sep 17 00:00:00 2001 From: DanS Date: Mon, 13 Jul 2026 19:59:53 -0500 Subject: [PATCH] refactor(settings): import dialog layout polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several layout refinements to the Import Private/Viewing Key dialog: - Widen the dialog to 640px so the one-line warning under the heading no longer wraps. - Vertically center DialogWarningHeader's label with the (taller) warning icon instead of top-aligning it — a consistent fix for every warning header. - Add a small gap between the key input's text and the flush eye toggle. - Center + slightly enlarge the Paste / Clear buttons. - Center the Import/Sweep + Close action buttons. - Scan-height control: put the numeric input first with the slider below it, and drop the redundant "0 = rescan from the start" hint (0 is the field default; the transparent-key note is kept). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app.cpp | 42 ++++++++++++++++++++++------------ src/ui/material/draw_helpers.h | 8 ++++++- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index 158f2ad..d8922ce 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -2775,8 +2775,15 @@ static void RenderScanHeightControl(char* buf, size_t bufSize, int tipHeight, bo ImGui::BeginDisabled(!enabled); + // Numeric entry first — the slider sits below it. + ImGui::SetNextItemWidth(availW); + ImGui::InputText("##importscanheight", buf, bufSize, ImGuiInputTextFlags_CharsDecimal); + cur = buf[0] ? std::atoi(buf) : 0; // reflect a just-typed value in the slider below + if (cur < 0) cur = 0; + if (tipHeight > 0) { - const float barW = availW; // slider spans the panel interior, stacked above the field + ImGui::Spacing(); + const float barW = availW; // slider spans the panel interior, below the field const float barH = ImGui::GetFrameHeight(); // match the numeric field's height (already dp-scaled) const float barRound = barH * 0.5f; const float thumbR = barH * 0.5f; @@ -2828,10 +2835,6 @@ static void RenderScanHeightControl(char* buf, size_t bufSize, int tipHeight, bo } } - // Exact numeric entry — full-width beneath the slider. - ImGui::SetNextItemWidth(availW); - ImGui::InputText("##importscanheight", buf, bufSize, ImGuiInputTextFlags_CharsDecimal); - ImGui::EndDisabled(); } @@ -2845,7 +2848,7 @@ void App::renderImportKeyDialog() ov.title = viewMode ? TR("import_viewkey_title") : TR("import_key_title"); ov.p_open = &show_import_key_; ov.style = m::OverlayStyle::BlurFloat; - ov.cardWidth = 520.0f; + ov.cardWidth = 640.0f; // wide enough that the one-line warning under the heading doesn't wrap ov.idSuffix = "importkey"; if (!m::BeginOverlayDialog(ov)) return; @@ -2902,21 +2905,26 @@ void App::renderImportKeyDialog() // revealed) — like the send tab — so the user can verify before pasting. const float fieldH = ImGui::GetFrameHeight(); const ImVec2 iconSz(fieldH, fieldH); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - fieldH); + const float eyeGap = ui::Layout::spacingSm(); // breathing room between the text and the eye button + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - fieldH - eyeGap); ImGuiInputTextFlags flags = import_key_reveal_ ? 0 : ImGuiInputTextFlags_Password; ImGui::InputText("##importkey", import_key_input_, sizeof(import_key_input_), flags); const ImVec2 fieldMin = ImGui::GetItemRectMin(); // anchor for the paste-preview overlay - ImGui::SameLine(0, 0); + ImGui::SameLine(0, eyeGap); m::IconButtonStyle eyeSt; eyeSt.hoverBg = m::WithAlpha(m::OnSurface(), 30); - eyeSt.bgRounding = 4.0f * dp; // match the input frame's corners (it sits flush at the field's edge) + eyeSt.bgRounding = 4.0f * dp; eyeSt.tooltip = TR("import_key_reveal_tip"); if (m::IconButton("##revealkey", import_key_reveal_ ? ICON_MD_VISIBILITY_OFF : ICON_MD_VISIBILITY, m::Type().iconSmall(), iconSz, eyeSt)) import_key_reveal_ = !import_key_reveal_; - // Paste / Clear — labelled buttons. - const bool pasteClicked = m::TactileButton(TR("paste")); + // Paste / Clear — centered, slightly larger labelled buttons. + const float actBtnW = 104.0f * dp; + const float actTotal = actBtnW * 2.0f + ImGui::GetStyle().ItemSpacing.x; + const float actOff = (ImGui::GetContentRegionAvail().x - actTotal) * 0.5f; + if (actOff > 0.0f) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + actOff); + const bool pasteClicked = m::TactileButton(TR("paste"), ImVec2(actBtnW, 0)); const bool pasteHovered = ImGui::IsItemHovered(); if (pasteClicked) { const char* clipboard = ImGui::GetClipboardText(); @@ -2928,7 +2936,7 @@ void App::renderImportKeyDialog() } } ImGui::SameLine(); - if (m::TactileButton(TR("clear"))) sodium_memzero(import_key_input_, sizeof(import_key_input_)); + if (m::TactileButton(TR("clear"), ImVec2(actBtnW, 0))) sodium_memzero(import_key_input_, sizeof(import_key_input_)); // While Paste is hovered and the field is empty, preview the trimmed clipboard INSIDE the field — // masked with '*' unless the key is revealed — coloured by how it would classify (green ok / amber @@ -3079,8 +3087,10 @@ void App::renderImportKeyDialog() RenderScanHeightControl(import_key_scan_height_, sizeof(import_key_scan_height_), tip, !transparentKey, dp, availW); - ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(m::OnSurfaceDisabled()), "%s", - transparentKey ? TR("import_scan_transparent") : TR("import_scan_hint")); + // Only note why the control is inert for a transparent key; the generic "0 = rescan from the + // start" hint was dropped as self-evident (0 is the field's default). + if (transparentKey) + ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(m::OnSurfaceDisabled()), "%s", TR("import_scan_transparent")); ImGui::Unindent(padX); ImGui::Dummy(ImVec2(0, padY)); @@ -3150,6 +3160,10 @@ void App::renderImportKeyDialog() // Sweep needs a spending key AND a destination (a picked address when not using a fresh one). const bool destReady = !sweepMode || sweep_dest_mode_ == 0 || sweep_dest_pick_[0] != '\0'; const bool canAct = recognized && state_.connected && !running && destReady; + // Center the action + Close buttons in the (wider) dialog. + const float footTotal = btnW * 2.0f + ImGui::GetStyle().ItemSpacing.x; + const float footOff = (ImGui::GetContentRegionAvail().x - footTotal) * 0.5f; + if (footOff > 0.0f) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + footOff); ImGui::BeginDisabled(!canAct); if (m::TactileButton(sweepMode ? TR("sweep_button") : TR("import_key_import"), ImVec2(btnW, 0))) { int startHeight = 0; // 0 = full rescan. Shielded imports (z_importkey) honor it; transparent diff --git a/src/ui/material/draw_helpers.h b/src/ui/material/draw_helpers.h index 82f4dda..c1f193a 100644 --- a/src/ui/material/draw_helpers.h +++ b/src/ui/material/draw_helpers.h @@ -1666,10 +1666,16 @@ inline void BeginOverlayDialogFooter(float totalActionWidth, bool drawSeparator // TextColored(label). The label text is passed in (already translated). inline void DialogWarningHeader(const char* warningLabel, const ImVec4& col = WarningVec4()) { - ImGui::PushFont(Type().iconLarge()); + ImFont* iconF = Type().iconLarge(); + const float rowTop = ImGui::GetCursorPosY(); + const float iconH = iconF->LegacySize; // already dp-scaled + const float textH = ImGui::GetFontSize(); + ImGui::PushFont(iconF); ImGui::TextColored(col, ICON_MD_WARNING); ImGui::PopFont(); ImGui::SameLine(); + // Vertically center the label with the taller warning glyph (stays within the icon's row height). + if (iconH > textH) ImGui::SetCursorPosY(rowTop + (iconH - textH) * 0.5f); ImGui::TextColored(col, "%s", warningLabel); }