diff --git a/src/app.cpp b/src/app.cpp index 67fb4c7..2340587 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -2930,10 +2930,11 @@ void App::renderImportKeyDialog() ImGui::SameLine(); if (m::TactileButton(TR("clear"))) 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. pastePreview also drives the type indicator (below) - // so the user sees how the clipboard would classify before pasting — but NOT the action button. - std::string pastePreview; + // 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 + // wrong-type / red unrecognized). Drawn via the draw list only: it verifies the key WITHOUT adding + // a layout row, since a hover-toggled row would grow the centered dialog and shift the button out + // from under the cursor (hover flicker). if (pasteHovered && import_key_input_[0] == '\0') { const char* clip = ImGui::GetClipboardText(); if (clip && clip[0]) { @@ -2941,20 +2942,25 @@ void App::renderImportKeyDialog() while (!t.empty() && (t.front()==' '||t.front()=='\t'||t.front()=='\n'||t.front()=='\r')) t.erase(t.begin()); while (!t.empty() && (t.back()==' '||t.back()=='\t'||t.back()=='\n'||t.back()=='\r')) t.pop_back(); if (!t.empty()) { - pastePreview = t; + const bool pView = services::WalletSecurityController::isViewingKey(t); + const bool pSpend = services::WalletSecurityController::isRecognizedPrivateKey(t); + const bool pOk = viewMode ? pView : pSpend; + const bool pWrong = viewMode ? pSpend : pView; + ImVec4 col = pOk ? m::SuccessVec4() + : pWrong ? ImVec4(0.8f, 0.6f, 0.0f, 1.0f) + : m::ErrorVec4(); + col.w = 0.6f; // ghost (uncommitted) preview std::string shown = import_key_reveal_ ? t : std::string(std::min(t.size(), (size_t)56), '*'); if (shown.size() > 48) shown.resize(48); // fits the field before the flush eye button const ImVec2 tp(fieldMin.x + ImGui::GetStyle().FramePadding.x, fieldMin.y + ImGui::GetStyle().FramePadding.y); - ImVec4 pc = ImGui::GetStyleColorVec4(ImGuiCol_Text); - pc.w = 0.45f; // transparent ghost, like the send tab - ImGui::GetWindowDrawList()->AddText(tp, ImGui::ColorConvertFloat4ToU32(pc), shown.c_str()); + ImGui::GetWindowDrawList()->AddText(tp, ImGui::ColorConvertFloat4ToU32(col), shown.c_str()); } } } - // Trimmed field content drives the Import/Sweep guard + scan panel; the paste preview (only while - // hovering an empty field) drives just the type indicator, so a hover can never enable the button. + // Trimmed field content drives the type indicator, the Import/Sweep guard, and the scan panel — NOT + // the hover preview, so hovering can neither shift the layout nor enable the action button. std::string keyTrim(import_key_input_); while (!keyTrim.empty() && (keyTrim.front()==' '||keyTrim.front()=='\t'||keyTrim.front()=='\n'||keyTrim.front()=='\r')) keyTrim.erase(keyTrim.begin()); while (!keyTrim.empty() && (keyTrim.back()==' '||keyTrim.back()=='\t'||keyTrim.back()=='\n'||keyTrim.back()=='\r')) keyTrim.pop_back(); @@ -2962,23 +2968,15 @@ void App::renderImportKeyDialog() bool isSpend = services::WalletSecurityController::isRecognizedPrivateKey(keyTrim); bool shielded = services::WalletSecurityController::classifyPrivateKey(keyTrim) == services::WalletSecurityController::KeyKind::Shielded; - // Each button accepts only its own key type; the Import/Sweep guard uses `recognized` (wrong-type - // redirect handling lives in the indicator's iWrongType path below). + // Each button accepts only its own key type; a key valid for the OTHER button is flagged with a + // redirect hint rather than lumped in with truly unrecognized input. bool recognized = viewMode ? isView : isSpend; + bool wrongType = viewMode ? isSpend : isView; - // Indicator key = the paste preview while hovering, else the field content. - const std::string& indKey = pastePreview.empty() ? keyTrim : pastePreview; - const bool iShielded = services::WalletSecurityController::classifyPrivateKey(indKey) - == services::WalletSecurityController::KeyKind::Shielded; - const bool iRecognized = viewMode ? services::WalletSecurityController::isViewingKey(indKey) - : services::WalletSecurityController::isRecognizedPrivateKey(indKey); - const bool iWrongType = viewMode ? services::WalletSecurityController::isRecognizedPrivateKey(indKey) - : services::WalletSecurityController::isViewingKey(indKey); - - if (!indKey.empty()) { - if (iRecognized) { + if (!keyTrim.empty()) { + if (recognized) { const char* label = viewMode ? TR("import_key_type_zview") - : iShielded ? TR("import_key_type_zspend") + : shielded ? TR("import_key_type_zspend") : TR("import_key_type_tkey"); ImGui::PushFont(m::Type().iconSmall()); ImGui::TextColored(m::SuccessVec4(), ICON_MD_CHECK_CIRCLE); @@ -2986,11 +2984,11 @@ void App::renderImportKeyDialog() ImGui::SameLine(0, 4.0f * dp); ImGui::TextColored(m::SuccessVec4(), "%s", label); } else { - const char* msg = iWrongType + const char* msg = wrongType ? (viewMode ? TR("import_viewkey_wrong_type") : TR("import_key_wrong_type")) : TR("import_key_type_unknown"); ImGui::PushFont(m::Type().iconSmall()); - ImGui::TextColored(ImVec4(0.8f, 0.6f, 0.0f, 1.0f), iWrongType ? ICON_MD_WARNING : ICON_MD_HELP); + ImGui::TextColored(ImVec4(0.8f, 0.6f, 0.0f, 1.0f), wrongType ? ICON_MD_WARNING : ICON_MD_HELP); ImGui::PopFont(); ImGui::SameLine(0, 4.0f * dp); ImGui::PushTextWrapPos(0.0f);