From 74c497922e32fb611ad8503a9543f666be5f3d7b Mon Sep 17 00:00:00 2001 From: DanS Date: Sun, 18 Oct 2026 11:39:45 -0500 Subject: [PATCH] fix(hidpi): scale security-dialog buttons, market caption, QR inset by dpiScale HiDPI geometry debt from the audit: 15 security-dialog buttons used a raw 40px height (undersized vs the scaled label at 1.5x), the market "Updated" caption used a raw SameLine(0,12), and the QR popup's 28px overlay-inset budget wasn't scaled (wrapping the address at HiDPI). All now go through ui::Layout::dpiScale(). Co-Authored-By: Claude Opus 4.8 --- src/app_security.cpp | 30 +++++++++++++++--------------- src/ui/windows/market_tab.cpp | 2 +- src/ui/windows/qr_popup_dialog.cpp | 4 +++- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/app_security.cpp b/src/app_security.cpp index 17d0145..cbe6b12 100644 --- a/src/app_security.cpp +++ b/src/app_security.cpp @@ -1521,7 +1521,7 @@ void App::renderEncryptWalletDialog() { float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; ImGui::BeginDisabled(!valid || encrypt_in_progress_); - if (ui::material::TactileButton(TR("settings_encrypt_wallet"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("settings_encrypt_wallet"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { std::string pass(encrypt_pass_buf_); enc_dlg_saved_passphrase_ = pass; memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_)); @@ -1533,7 +1533,7 @@ void App::renderEncryptWalletDialog() { ImGui::EndDisabled(); ImGui::SameLine(); - if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_)); memset(encrypt_confirm_buf_, 0, sizeof(encrypt_confirm_buf_)); show_encrypt_dialog_ = false; @@ -1612,7 +1612,7 @@ void App::renderEncryptWalletDialog() { float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; ImGui::BeginDisabled(!pinValid || !hasPassphrase || pin_in_progress_); - if (ui::material::TactileButton(TR("settings_set_pin"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("settings_set_pin"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { pin_in_progress_ = true; enc_dlg_pin_status_.clear(); std::string savedPass = enc_dlg_saved_passphrase_; @@ -1655,7 +1655,7 @@ void App::renderEncryptWalletDialog() { ImGui::EndDisabled(); ImGui::SameLine(); - if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { if (!enc_dlg_saved_passphrase_.empty()) { util::SecureVault::secureZero(&enc_dlg_saved_passphrase_[0], enc_dlg_saved_passphrase_.size()); @@ -1730,7 +1730,7 @@ void App::renderEncryptWalletDialog() { float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; // Not while a PIN operation runs: PIN setup could verify and seal the OLD passphrase. ImGui::BeginDisabled(!valid || encrypt_in_progress_ || pin_in_progress_); - if (ui::material::TactileButton(TR("change_pass_title"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("change_pass_title"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { changePassphrase(std::string(change_old_pass_buf_), std::string(change_new_pass_buf_)); } @@ -1739,7 +1739,7 @@ void App::renderEncryptWalletDialog() { ImGui::SameLine(); // Cancel does what Esc/close does — dismiss without applying. Buffers are wiped by // the !show_change_passphrase_ cleanup block below. - if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { show_change_passphrase_ = false; } EndOverlayDialog(); @@ -1808,7 +1808,7 @@ void App::renderDecryptWalletDialog() { float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; ImGui::BeginDisabled(!valid || decryptState.inProgress); // Decrypting stops and restarts the node: not while it is busy (refuseIfNodeBusy() only on a click). - if ((ui::material::TactileButton(TR("settings_remove_encryption"), ImVec2(btnW, 40)) || (enterPressed && valid)) && + if ((ui::material::TactileButton(TR("settings_remove_encryption"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale())) || (enterPressed && valid)) && !refuseIfNodeBusy()) { std::string passphrase(decrypt_pass_buf_); memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_)); @@ -2017,7 +2017,7 @@ void App::renderDecryptWalletDialog() { ImGui::EndDisabled(); ImGui::SameLine(); - if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_)); show_decrypt_dialog_ = false; } @@ -2132,7 +2132,7 @@ void App::renderDecryptWalletDialog() { ImGui::TextWrapped("%s", TR("decrypt_success_desc")); ImGui::Spacing(); - if (ui::material::TactileButton(TR("close"), ImVec2(-1, 40))) { + if (ui::material::TactileButton(TR("close"), ImVec2(-1, 40.0f * ui::Layout::dpiScale()))) { show_decrypt_dialog_ = false; } @@ -2149,11 +2149,11 @@ void App::renderDecryptWalletDialog() { ImGui::Spacing(); float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; - if (ui::material::TactileButton(TR("try_again"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("try_again"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { wallet_security_workflow_.reset(); } ImGui::SameLine(); - if (ui::material::TactileButton(TR("close"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("close"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { show_decrypt_dialog_ = false; } } @@ -2219,7 +2219,7 @@ void App::renderPinDialogs() { float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; // Not while a passphrase change runs: it could land after the old passphrase is verified. ImGui::BeginDisabled(!valid || pin_in_progress_ || encrypt_in_progress_); - if (ui::material::TactileButton(TR("settings_set_pin"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("settings_set_pin"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { pin_in_progress_ = true; pin_status_ = TR("sec_verifying_passphrase"); @@ -2292,7 +2292,7 @@ void App::renderPinDialogs() { ImGui::SameLine(); // Cancel does what Esc/close does — dismiss without applying. Buffers are wiped by // the !show_pin_setup_ cleanup block below. - if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40))) { + if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40.0f * ui::Layout::dpiScale()))) { show_pin_setup_ = false; } EndOverlayDialog(); @@ -2346,7 +2346,7 @@ void App::renderPinDialogs() { strcmp(pin_buf_, pin_confirm_buf_) == 0; ImGui::BeginDisabled(!valid || pin_in_progress_); - if (ui::material::TactileButton(TR("settings_change_pin"), ImVec2(-1, 40))) { + if (ui::material::TactileButton(TR("settings_change_pin"), ImVec2(-1, 40.0f * ui::Layout::dpiScale()))) { pin_in_progress_ = true; pin_status_ = TR("sec_changing_pin"); std::string oldPin(pin_old_buf_); @@ -2411,7 +2411,7 @@ void App::renderPinDialogs() { ImGui::Spacing(); bool valid = strlen(pin_old_buf_) >= 4; ImGui::BeginDisabled(!valid || pin_in_progress_); - if (ui::material::TactileButton(TR("settings_remove_pin"), ImVec2(-1, 40))) { + if (ui::material::TactileButton(TR("settings_remove_pin"), ImVec2(-1, 40.0f * ui::Layout::dpiScale()))) { pin_in_progress_ = true; pin_status_ = TR("sec_verifying_pin"); std::string oldPin(pin_old_buf_); diff --git a/src/ui/windows/market_tab.cpp b/src/ui/windows/market_tab.cpp index a62fc67..8063724 100644 --- a/src/ui/windows/market_tab.cpp +++ b/src/ui/windows/market_tab.cpp @@ -1431,7 +1431,7 @@ static void mktDrawPairSelector(App* app, const std::vector& ImGui::Dummy(ImVec2(0, Layout::spacingSm())); Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("market_attribution")); if (!market.last_updated.empty()) { - ImGui::SameLine(0, 12); + ImGui::SameLine(0, 12.0f * Layout::dpiScale()); snprintf(buf, sizeof(buf), TR("market_updated"), market.last_updated.c_str()); Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), buf); } diff --git a/src/ui/windows/qr_popup_dialog.cpp b/src/ui/windows/qr_popup_dialog.cpp index 04f988e..f6126f7 100644 --- a/src/ui/windows/qr_popup_dialog.cpp +++ b/src/ui/windows/qr_popup_dialog.cpp @@ -74,7 +74,9 @@ void QRPopupDialog::render(App* app) const float addrBoxW = ImGui::CalcTextSize(chunkedAddr.c_str()).x + addrPadX * 2.0f + 2.0f; // The BlurFloat overlay card insets content by 28px per side (draw_helpers BeginOverlayDialog); add // that (+ a little slack) so the address box fits on one line under its label instead of wrapping. - const float wantW = addrBoxW + 28.0f * 2.0f + 12.0f; + // Scale the overlay inset (BeginOverlayDialog insets 28 LOGICAL px/side) into the same scaled space + // as addrBoxW before the /dp conversion below, so the address box doesn't wrap at HiDPI. + const float wantW = addrBoxW + (28.0f * 2.0f + 12.0f) * dp; const float maxW = 0.85f * ImGui::GetMainViewport()->Size.x; const float cardW = (wantW < maxW ? wantW : maxW) / dp; material::OverlayDialogSpec ov;