Files
ObsidianDragon/src/ui/windows/qr_popup_dialog.cpp
DanS 04a5078133 feat(receive): migrate QR + Request-payment modals to the reference design
Bring the two Receive-tab modals onto the settings-modal reference language
(presentation-only):
- struct-form BeginOverlayDialog(OverlayDialogSpec{BlurFloat, cardWidth, idSuffix})
  replacing the legacy positional overload (idSuffix qrpopup / requestpay)
- StyledButton -> TactileButton (2 + 3 sites)
- drop Request-payment's description divider

No logic touched: the QR texture lifecycle, payment-URI builder, address combo,
and clipboard/notification calls are unchanged. The "DRGX" ticker stays hardcoded
per the existing codebase convention (7 sites, no currency TR key).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 16:20:02 -05:00

146 lines
4.6 KiB
C++

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