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:
42
src/app.cpp
42
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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user