// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #pragma once #include "../material/material.h" #include "../../embedded/IconsMaterialDesign.h" #include "imgui.h" #include #include #include namespace dragonx { namespace ui { namespace screens { using namespace material; // ============================================================================ // Receive Screen // ============================================================================ // Display receiving addresses with QR codes /** * @brief Wallet address for receiving */ struct WalletAddress { std::string address; std::string label; std::string type; // "shielded" or "transparent" double balance; bool isDefault; }; /** * @brief Receive screen with address cards */ class ReceiveScreen { public: void setAddresses(const std::vector& addresses) { m_addresses = addresses; } void setOnCopyAddress(std::function callback) { m_onCopyAddress = callback; } void setOnNewAddress(std::function callback) { m_onNewAddress = callback; } void setOnShowQR(std::function callback) { m_onShowQR = callback; } void setOnEditLabel(std::function callback) { m_onEditLabel = callback; } void render(); private: void renderAddressCard(const WalletAddress& addr, int index); void renderNewAddressButton(); void renderQRCodePopup(); std::vector m_addresses; std::function m_onCopyAddress; std::function m_onNewAddress; std::function m_onShowQR; std::function m_onEditLabel; std::string m_selectedQRAddress; bool m_showQRPopup = false; int m_selectedTab = 0; // 0 = shielded, 1 = transparent }; // ============================================================================ // Implementation // ============================================================================ inline void ReceiveScreen::render() { // Title Typography::instance().text(TypeStyle::H5, "Receive DRGX"); ImGui::Dummy(ImVec2(0, spacing::dp(2))); // Tab selection TabSpec tabSpec; tabSpec.variant = TabVariant::Fixed; if (BeginTabs("receive_tabs", tabSpec)) { if (Tab(ICON_MD_SHIELD " Shielded", m_selectedTab == 0)) { m_selectedTab = 0; } if (Tab(ICON_MD_DESCRIPTION " Transparent", m_selectedTab == 1)) { m_selectedTab = 1; } EndTabs(); } ImGui::Dummy(ImVec2(0, spacing::dp(2))); // Filter addresses by type std::string filterType = m_selectedTab == 0 ? "shielded" : "transparent"; // Display filtered addresses int visibleCount = 0; for (size_t i = 0; i < m_addresses.size(); i++) { if (m_addresses[i].type == filterType) { renderAddressCard(m_addresses[i], static_cast(i)); ImGui::Dummy(ImVec2(0, spacing::dp(2))); visibleCount++; } } // Empty state if (visibleCount == 0) { CardSpec emptySpec; emptySpec.elevation = 1; emptySpec.padding = spacing::dp(4); if (BeginCard(emptySpec)) { float availWidth = ImGui::GetContentRegionAvail().x; // Center text const char* emptyText = m_selectedTab == 0 ? "No shielded addresses yet" : "No transparent addresses yet"; ImVec2 textSize = ImGui::CalcTextSize(emptyText); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availWidth - textSize.x) * 0.5f); Typography::instance().textColored(TypeStyle::Body1, OnSurfaceMedium(), emptyText); EndCard(); } ImGui::Dummy(ImVec2(0, spacing::dp(2))); } // New address button renderNewAddressButton(); // QR popup if (m_showQRPopup) { renderQRCodePopup(); } } inline void ReceiveScreen::renderAddressCard(const WalletAddress& addr, int index) { CardSpec cardSpec; cardSpec.elevation = addr.isDefault ? 3 : 1; cardSpec.padding = spacing::dp(2); char cardId[32]; snprintf(cardId, sizeof(cardId), "addr_card_%d", index); ImGui::PushID(cardId); if (BeginCard(cardSpec)) { // Header row: Label + Default badge ImGui::BeginGroup(); { if (!addr.label.empty()) { Typography::instance().text(TypeStyle::Subtitle1, addr.label.c_str()); } else { Typography::instance().textColored(TypeStyle::Subtitle1, OnSurfaceMedium(), "Unnamed Address"); } if (addr.isDefault) { ImGui::SameLine(); ChipSpec chipSpec; chipSpec.variant = ChipVariant::Filled; chipSpec.color = Primary(); Chip("DEFAULT", chipSpec); } } ImGui::EndGroup(); ImGui::Dummy(ImVec2(0, spacing::dp(1))); // Address (truncated with copy) ImGui::BeginGroup(); { // Show truncated address std::string displayAddr = addr.address; if (displayAddr.length() > 40) { displayAddr = displayAddr.substr(0, 20) + "..." + displayAddr.substr(displayAddr.length() - 16); } Typography::instance().textColored(TypeStyle::Body2, OnSurfaceMedium(), displayAddr.c_str()); } ImGui::EndGroup(); ImGui::Dummy(ImVec2(0, spacing::dp(1))); // Balance if (addr.balance > 0) { char balanceStr[64]; snprintf(balanceStr, sizeof(balanceStr), "Balance: %.8f DRGX", addr.balance); Typography::instance().textColored(TypeStyle::Caption, OnSurfaceMedium(), balanceStr); ImGui::Dummy(ImVec2(0, spacing::dp(1))); } // Action buttons ImGui::BeginGroup(); { if (OutlinedButton(ICON_MD_CONTENT_COPY " COPY")) { if (m_onCopyAddress) m_onCopyAddress(addr.address); } ImGui::SameLine(0, spacing::dp(1)); if (OutlinedButton(ICON_MD_QR_CODE " QR")) { m_selectedQRAddress = addr.address; m_showQRPopup = true; } ImGui::SameLine(0, spacing::dp(1)); if (TextButton(ICON_MD_EDIT " EDIT")) { // Would trigger edit label dialog if (m_onEditLabel) m_onEditLabel(addr.address, addr.label); } } ImGui::EndGroup(); EndCard(); } ImGui::PopID(); } inline void ReceiveScreen::renderNewAddressButton() { // Floating action button style float buttonWidth = 200.0f; float availWidth = ImGui::GetContentRegionAvail().x; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availWidth - buttonWidth) * 0.5f); if (ContainedButton("+ NEW ADDRESS")) { if (m_onNewAddress) { m_onNewAddress(m_selectedTab == 0); // true for shielded } } } inline void ReceiveScreen::renderQRCodePopup() { DialogSpec dialogSpec; dialogSpec.title = "QR Code"; dialogSpec.maxWidth = 400.0f; DialogResult result = BeginDialog("qr_popup", dialogSpec); if (result.isOpen) { // QR code placeholder (actual QR generation would be separate) float qrSize = 250.0f; float availWidth = ImGui::GetContentRegionAvail().x; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availWidth - qrSize) * 0.5f); // QR placeholder box ImVec2 pos = ImGui::GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled( pos, ImVec2(pos.x + qrSize, pos.y + qrSize), ImGui::GetColorU32(Surface()) ); ImGui::GetWindowDrawList()->AddRect( pos, ImVec2(pos.x + qrSize, pos.y + qrSize), ImGui::GetColorU32(Divider()), 4.0f ); // Center text in QR box const char* qrText = "QR CODE"; ImVec2 textSize = ImGui::CalcTextSize(qrText); ImGui::SetCursorScreenPos(ImVec2( pos.x + (qrSize - textSize.x) * 0.5f, pos.y + (qrSize - textSize.y) * 0.5f )); Typography::instance().textColored(TypeStyle::Body1, OnSurfaceMedium(), qrText); ImGui::Dummy(ImVec2(qrSize, qrSize + spacing::dp(2))); // Address below QR ImGui::TextWrapped("%s", m_selectedQRAddress.c_str()); ImGui::Dummy(ImVec2(0, spacing::dp(2))); // Actions float btnWidth = 100.0f; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availWidth - btnWidth * 2 - spacing::dp(2)) * 0.5f); if (OutlinedButton("COPY")) { if (m_onCopyAddress) m_onCopyAddress(m_selectedQRAddress); } ImGui::SameLine(0, spacing::dp(2)); if (ContainedButton("CLOSE")) { m_showQRPopup = false; } EndDialog(); } if (result.dismissed) { m_showQRPopup = false; } } } // namespace screens } // namespace ui } // namespace dragonx