// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "qr_popup_dialog.h" #include "../../app.h" #include "../../util/i18n.h" #include "../widgets/qr_code.h" #include "../widgets/copy_field.h" #include "../schema/ui_schema.h" #include "../material/draw_helpers.h" #include "../layout.h" #include "../theme.h" #include "imgui.h" namespace dragonx { namespace ui { // Static member initialization bool QRPopupDialog::s_open = false; std::string QRPopupDialog::s_address; std::string QRPopupDialog::s_label; uintptr_t QRPopupDialog::s_qr_texture = 0; int QRPopupDialog::s_qr_width = 0; int QRPopupDialog::s_qr_height = 0; void QRPopupDialog::show(const std::string& address, const std::string& label) { // Cleanup previous texture if (s_qr_texture != 0) { FreeQRTexture(s_qr_texture); s_qr_texture = 0; } s_open = true; s_address = address; s_label = label; // Generate new QR texture s_qr_texture = GenerateQRTexture(address.c_str(), &s_qr_width, &s_qr_height); } bool QRPopupDialog::isOpen() { return s_open; } void QRPopupDialog::close() { s_open = false; if (s_qr_texture != 0) { FreeQRTexture(s_qr_texture); s_qr_texture = 0; } } void QRPopupDialog::render(App* app) { (void)app; // Unused for now if (!s_open) return; auto& S = schema::UI(); auto qr = S.drawElement("dialogs.qr-popup", "qr-code"); auto actionBtn = S.button("dialogs.qr-popup", "action-button"); // Match the key-export modal: 85% of the window width (divide out the dpiScale that // BeginOverlayDialog re-applies, so the final card is exactly 85% at any font scale). const float cardW = (0.85f * ImGui::GetMainViewport()->Size.x) / Layout::dpiScale(); material::OverlayDialogSpec ov; ov.title = TR("qr_title"); ov.p_open = &s_open; ov.style = material::OverlayStyle::BlurFloat; ov.cardWidth = cardW; ov.idSuffix = "qrpopup"; if (material::BeginOverlayDialog(ov)) { // Label if present if (!s_label.empty()) { ImGui::TextWrapped("%s", s_label.c_str()); ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); } // Center the QR code (responsive — larger, to suit the wider modal). float window_width = ImGui::GetWindowWidth(); float qr_size = qr.size > 0 ? (float)qr.size : 280.0f; const float responsive = window_width * 0.5f; if (responsive > qr_size) qr_size = responsive; if (qr_size > 420.0f) qr_size = 420.0f; float padding = (window_width - qr_size) / 2.0f; if (padding < 0.0f) padding = 0.0f; ImGui::SetCursorPosX(padding); // Render QR code if (s_qr_texture != 0) { ImGui::Image((ImTextureID)s_qr_texture, ImVec2(qr_size, qr_size)); } else { // Fallback: show error ImGui::BeginChild("QRPlaceholder", ImVec2(qr_size, qr_size), true); ImGui::TextWrapped("%s", TR("qr_failed")); ImGui::EndChild(); } ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); // Address — click-to-copy field (same formatting as the key-export dialog). ImGui::Text("%s", TR("address_label")); widgets::AddressCopyField("##QRAddress", s_address); ImGui::Spacing(); // Buttons — size each to its label (so "Copy address" never clips), then center the pair. ImFont* btnFont = S.resolveFont(actionBtn.font); ImGui::PushFont(btnFont); const float btnPad = ImGui::GetStyle().FramePadding.x * 2.0f + 24.0f; float w_copy = ImGui::CalcTextSize(TR("copy_address")).x + btnPad; float w_close = ImGui::CalcTextSize(TR("close")).x + btnPad; ImGui::PopFont(); if (w_copy < actionBtn.width) w_copy = actionBtn.width; if (w_close < actionBtn.width) w_close = actionBtn.width; const float total_width = w_copy + w_close + ImGui::GetStyle().ItemSpacing.x; ImGui::SetCursorPosX((window_width - total_width) / 2.0f); if (material::TactileButton(TR("copy_address"), ImVec2(w_copy, 0), btnFont)) { ImGui::SetClipboardText(s_address.c_str()); } ImGui::SameLine(); if (material::TactileButton(TR("close"), ImVec2(w_close, 0), btnFont)) { close(); } material::EndOverlayDialog(); } // Handle window close button if (!s_open && s_qr_texture != 0) { FreeQRTexture(s_qr_texture); s_qr_texture = 0; } } } // namespace ui } // namespace dragonx