Files
ObsidianDragon/src/ui/screens/receive_screen.h
DanS 3aee55b49c ObsidianDragon - DragonX ImGui Wallet
Full-node GUI wallet for DragonX cryptocurrency.
Built with Dear ImGui, SDL3, and OpenGL3/DX11.

Features:
- Send/receive shielded and transparent transactions
- Autoshield with merged transaction display
- Built-in CPU mining (xmrig)
- Peer management and network monitoring
- Wallet encryption with PIN lock
- QR code generation for receive addresses
- Transaction history with pagination
- Console for direct RPC commands
- Cross-platform (Linux, Windows)
2026-02-27 00:26:01 -06:00

319 lines
9.6 KiB
C++

// 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 <string>
#include <vector>
#include <functional>
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<WalletAddress>& addresses) {
m_addresses = addresses;
}
void setOnCopyAddress(std::function<void(const std::string&)> callback) {
m_onCopyAddress = callback;
}
void setOnNewAddress(std::function<void(bool shielded)> callback) {
m_onNewAddress = callback;
}
void setOnShowQR(std::function<void(const std::string&)> callback) {
m_onShowQR = callback;
}
void setOnEditLabel(std::function<void(const std::string&, const std::string&)> callback) {
m_onEditLabel = callback;
}
void render();
private:
void renderAddressCard(const WalletAddress& addr, int index);
void renderNewAddressButton();
void renderQRCodePopup();
std::vector<WalletAddress> m_addresses;
std::function<void(const std::string&)> m_onCopyAddress;
std::function<void(bool shielded)> m_onNewAddress;
std::function<void(const std::string&)> m_onShowQR;
std::function<void(const std::string&, const std::string&)> 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<int>(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