refactor(settings): import dialog layout polish

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) <noreply@anthropic.com>
This commit is contained in:
2026-07-13 19:59:53 -05:00
parent ffc24ee99b
commit a13190a6df
2 changed files with 35 additions and 15 deletions

View File

@@ -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

View File

@@ -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);
}