// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #pragma once #include "../material/material.h" #include "../../config/version.h" #include "../../embedded/IconsMaterialDesign.h" #include "imgui.h" #include #include #include #include namespace dragonx { namespace ui { namespace screens { using namespace material; // ============================================================================ // Settings Screen // ============================================================================ // Application settings with Material list items /** * @brief Setting category */ enum class SettingsCategory { Wallet, Display, Network, Privacy, Advanced }; /** * @brief Settings data */ struct SettingsData { // Wallet bool autoShield = true; double defaultFee = DRAGONX_DEFAULT_FEE; // Display std::string theme = "dark"; std::string language = "en"; std::string fiatCurrency = "USD"; bool showBalanceInFiat = true; bool hideEmptyAddresses = true; // Network std::string proxyAddress; int proxyPort = 9050; bool useTor = false; int maxConnections = 125; // Privacy bool rememberAddresses = true; bool autoLock = true; int autoLockTimeout = 5; // minutes // Advanced int confirmations = 10; std::string dataDir; bool debugMode = false; }; /** * @brief Settings screen with Material list */ class SettingsScreen { public: void setSettings(const SettingsData& settings) { m_settings = settings; } const SettingsData& getSettings() const { return m_settings; } void setOnSettingsChanged(std::function callback) { m_onSettingsChanged = callback; } void setOnBackupWallet(std::function callback) { m_onBackupWallet = callback; } void setOnExportKeys(std::function callback) { m_onExportKeys = callback; } void setOnRescan(std::function callback) { m_onRescan = callback; } void render(); private: void renderWalletSettings(); void renderDisplaySettings(); void renderNetworkSettings(); void renderPrivacySettings(); void renderAdvancedSettings(); void renderAboutSection(); void notifySettingsChanged(); SettingsData m_settings; std::function m_onSettingsChanged; std::function m_onBackupWallet; std::function m_onExportKeys; std::function m_onRescan; SettingsCategory m_selectedCategory = SettingsCategory::Wallet; }; // ============================================================================ // Implementation // ============================================================================ inline void SettingsScreen::render() { // Title Typography::instance().text(TypeStyle::H5, "Settings"); ImGui::Dummy(ImVec2(0, spacing::dp(2))); // Category tabs TabSpec tabSpec; tabSpec.variant = TabVariant::Scrollable; if (BeginTabs("settings_tabs", tabSpec)) { if (Tab(ICON_MD_ACCOUNT_BALANCE_WALLET " Wallet", m_selectedCategory == SettingsCategory::Wallet)) { m_selectedCategory = SettingsCategory::Wallet; } if (Tab(ICON_MD_PALETTE " Display", m_selectedCategory == SettingsCategory::Display)) { m_selectedCategory = SettingsCategory::Display; } if (Tab(ICON_MD_PUBLIC " Network", m_selectedCategory == SettingsCategory::Network)) { m_selectedCategory = SettingsCategory::Network; } if (Tab(ICON_MD_LOCK " Privacy", m_selectedCategory == SettingsCategory::Privacy)) { m_selectedCategory = SettingsCategory::Privacy; } if (Tab(ICON_MD_SETTINGS " Advanced", m_selectedCategory == SettingsCategory::Advanced)) { m_selectedCategory = SettingsCategory::Advanced; } EndTabs(); } ImGui::Dummy(ImVec2(0, spacing::dp(2))); // Settings content switch (m_selectedCategory) { case SettingsCategory::Wallet: renderWalletSettings(); break; case SettingsCategory::Display: renderDisplaySettings(); break; case SettingsCategory::Network: renderNetworkSettings(); break; case SettingsCategory::Privacy: renderPrivacySettings(); break; case SettingsCategory::Advanced: renderAdvancedSettings(); break; } ImGui::Dummy(ImVec2(0, spacing::dp(3))); // About section (always visible) renderAboutSection(); } inline void SettingsScreen::renderWalletSettings() { CardSpec cardSpec; cardSpec.elevation = 1; cardSpec.padding = 0; if (BeginCard(cardSpec)) { BeginList("wallet_settings", false); // Auto-shield toggle { ListItemSpec itemSpec; itemSpec.primaryText = "Auto-Shield Transparent Funds"; itemSpec.secondaryText = "Automatically move transparent balance to shielded"; itemSpec.dividerBelow = true; ImGui::PushID("auto_shield"); ListItem(itemSpec); // Toggle positioned at end ImGui::SameLine(ImGui::GetContentRegionAvail().x - 60); if (Switch("##toggle", m_settings.autoShield)) { notifySettingsChanged(); } ImGui::PopID(); } // Default fee { ListItemSpec itemSpec; itemSpec.primaryText = "Default Transaction Fee"; char feeStr[32]; snprintf(feeStr, sizeof(feeStr), "%.8f DRGX", m_settings.defaultFee); itemSpec.secondaryText = feeStr; itemSpec.trailingIcon = ">"; itemSpec.dividerBelow = true; ListItem(itemSpec); } // Backup wallet { ListItemSpec itemSpec; itemSpec.leadingIcon = ICON_MD_SAVE; itemSpec.primaryText = "Backup Wallet"; itemSpec.secondaryText = "Export wallet backup file"; itemSpec.trailingIcon = ">"; itemSpec.dividerBelow = true; if (ListItem(itemSpec)) { if (m_onBackupWallet) m_onBackupWallet(); } } // Export keys { ListItemSpec itemSpec; itemSpec.leadingIcon = ICON_MD_KEY; itemSpec.primaryText = "Export Private Keys"; itemSpec.secondaryText = "Export viewing and spending keys"; itemSpec.trailingIcon = ">"; if (ListItem(itemSpec)) { if (m_onExportKeys) m_onExportKeys(); } } EndList(); EndCard(); } } inline void SettingsScreen::renderDisplaySettings() { CardSpec cardSpec; cardSpec.elevation = 1; cardSpec.padding = 0; if (BeginCard(cardSpec)) { BeginList("display_settings", false); // Theme selection { ListItemSpec itemSpec; itemSpec.primaryText = "Theme"; itemSpec.secondaryText = m_settings.theme == "dark" ? "Dark" : "Light"; itemSpec.trailingIcon = ">"; itemSpec.dividerBelow = true; ListItem(itemSpec); } // Language { ListItemSpec itemSpec; itemSpec.primaryText = "Language"; itemSpec.secondaryText = "English"; // Would map from code itemSpec.trailingIcon = ">"; itemSpec.dividerBelow = true; ListItem(itemSpec); } // Fiat currency { ListItemSpec itemSpec; itemSpec.primaryText = "Fiat Currency"; itemSpec.secondaryText = m_settings.fiatCurrency.c_str(); itemSpec.trailingIcon = ">"; itemSpec.dividerBelow = true; ListItem(itemSpec); } // Show balance in fiat toggle { ListItemSpec itemSpec; itemSpec.primaryText = "Show Balance in Fiat"; itemSpec.secondaryText = "Display fiat equivalent on home screen"; itemSpec.dividerBelow = true; ImGui::PushID("show_fiat"); ListItem(itemSpec); ImGui::SameLine(ImGui::GetContentRegionAvail().x - 60); if (Switch("##toggle", m_settings.showBalanceInFiat)) { notifySettingsChanged(); } ImGui::PopID(); } // Hide empty addresses { ListItemSpec itemSpec; itemSpec.primaryText = "Hide Empty Addresses"; itemSpec.secondaryText = "Don't show addresses with zero balance"; ImGui::PushID("hide_empty"); ListItem(itemSpec); ImGui::SameLine(ImGui::GetContentRegionAvail().x - 60); if (Switch("##toggle", m_settings.hideEmptyAddresses)) { notifySettingsChanged(); } ImGui::PopID(); } EndList(); EndCard(); } } inline void SettingsScreen::renderNetworkSettings() { CardSpec cardSpec; cardSpec.elevation = 1; cardSpec.padding = 0; if (BeginCard(cardSpec)) { BeginList("network_settings", false); // Use Tor { ListItemSpec itemSpec; itemSpec.leadingIcon = "🧅"; itemSpec.primaryText = "Use Tor Network"; itemSpec.secondaryText = "Route connections through Tor for privacy"; itemSpec.dividerBelow = true; ImGui::PushID("use_tor"); ListItem(itemSpec); ImGui::SameLine(ImGui::GetContentRegionAvail().x - 60); if (Switch("##toggle", m_settings.useTor)) { notifySettingsChanged(); } ImGui::PopID(); } // Proxy settings (shown if Tor enabled) if (m_settings.useTor) { ListItemSpec itemSpec; itemSpec.primaryText = "Proxy Address"; char proxyStr[128]; snprintf(proxyStr, sizeof(proxyStr), "%s:%d", m_settings.proxyAddress.empty() ? "127.0.0.1" : m_settings.proxyAddress.c_str(), m_settings.proxyPort); itemSpec.secondaryText = proxyStr; itemSpec.trailingIcon = ">"; itemSpec.dividerBelow = true; ListItem(itemSpec); } // Max connections { ListItemSpec itemSpec; itemSpec.primaryText = "Maximum Connections"; char connStr[32]; snprintf(connStr, sizeof(connStr), "%d peers", m_settings.maxConnections); itemSpec.secondaryText = connStr; itemSpec.trailingIcon = ">"; ListItem(itemSpec); } EndList(); EndCard(); } } inline void SettingsScreen::renderPrivacySettings() { CardSpec cardSpec; cardSpec.elevation = 1; cardSpec.padding = 0; if (BeginCard(cardSpec)) { BeginList("privacy_settings", false); // Remember addresses { ListItemSpec itemSpec; itemSpec.primaryText = "Remember Addresses"; itemSpec.secondaryText = "Save recently used addresses"; itemSpec.dividerBelow = true; ImGui::PushID("remember_addr"); ListItem(itemSpec); ImGui::SameLine(ImGui::GetContentRegionAvail().x - 60); if (Switch("##toggle", m_settings.rememberAddresses)) { notifySettingsChanged(); } ImGui::PopID(); } // Auto-lock { ListItemSpec itemSpec; itemSpec.primaryText = "Auto-Lock Wallet"; itemSpec.secondaryText = "Lock wallet after inactivity"; itemSpec.dividerBelow = true; ImGui::PushID("auto_lock"); ListItem(itemSpec); ImGui::SameLine(ImGui::GetContentRegionAvail().x - 60); if (Switch("##toggle", m_settings.autoLock)) { notifySettingsChanged(); } ImGui::PopID(); } // Lock timeout (if auto-lock enabled) if (m_settings.autoLock) { ListItemSpec itemSpec; itemSpec.primaryText = "Lock Timeout"; char timeoutStr[32]; snprintf(timeoutStr, sizeof(timeoutStr), "%d minutes", m_settings.autoLockTimeout); itemSpec.secondaryText = timeoutStr; itemSpec.trailingIcon = ">"; ListItem(itemSpec); } EndList(); EndCard(); } } inline void SettingsScreen::renderAdvancedSettings() { CardSpec cardSpec; cardSpec.elevation = 1; cardSpec.padding = 0; if (BeginCard(cardSpec)) { BeginList("advanced_settings", false); // Required confirmations { ListItemSpec itemSpec; itemSpec.primaryText = "Required Confirmations"; char confStr[32]; snprintf(confStr, sizeof(confStr), "%d blocks", m_settings.confirmations); itemSpec.secondaryText = confStr; itemSpec.trailingIcon = ">"; itemSpec.dividerBelow = true; ListItem(itemSpec); } // Data directory { ListItemSpec itemSpec; itemSpec.leadingIcon = "📁"; itemSpec.primaryText = "Data Directory"; itemSpec.secondaryText = m_settings.dataDir.empty() ? "(default)" : m_settings.dataDir.c_str(); itemSpec.trailingIcon = ">"; itemSpec.dividerBelow = true; ListItem(itemSpec); } // Rescan blockchain { ListItemSpec itemSpec; itemSpec.leadingIcon = ICON_MD_SYNC; itemSpec.primaryText = "Rescan Blockchain"; itemSpec.secondaryText = "Rebuild transaction history from blockchain"; itemSpec.trailingIcon = ">"; itemSpec.dividerBelow = true; if (ListItem(itemSpec)) { if (m_onRescan) m_onRescan(); } } // Debug mode { ListItemSpec itemSpec; itemSpec.leadingIcon = "🐛"; itemSpec.primaryText = "Debug Mode"; itemSpec.secondaryText = "Enable verbose logging"; ImGui::PushID("debug_mode"); ListItem(itemSpec); ImGui::SameLine(ImGui::GetContentRegionAvail().x - 60); if (Switch("##toggle", m_settings.debugMode)) { notifySettingsChanged(); } ImGui::PopID(); } EndList(); EndCard(); } } inline void SettingsScreen::renderAboutSection() { CardSpec cardSpec; cardSpec.elevation = 0; cardSpec.padding = spacing::dp(3); cardSpec.outlined = true; if (BeginCard(cardSpec)) { float availWidth = ImGui::GetContentRegionAvail().x; // App name and version centered const char* appName = "ObsidianDragon"; ImVec2 nameSize = ImGui::CalcTextSize(appName); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availWidth - nameSize.x) * 0.5f); Typography::instance().text(TypeStyle::H6, appName); char version[64]; snprintf(version, sizeof(version), "Version %s-imgui", DRAGONX_VERSION); ImVec2 versionSize = ImGui::CalcTextSize(version); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availWidth - versionSize.x) * 0.5f); Typography::instance().textColored(TypeStyle::Caption, OnSurfaceMedium(), version); ImGui::Dummy(ImVec2(0, spacing::dp(1))); const char* copyright = "© 2024-2026 The Hush Developers"; ImVec2 copySize = ImGui::CalcTextSize(copyright); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availWidth - copySize.x) * 0.5f); Typography::instance().textColored(TypeStyle::Caption, OnSurfaceMedium(), copyright); const char* license = "Released under GPLv3"; ImVec2 licenseSize = ImGui::CalcTextSize(license); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availWidth - licenseSize.x) * 0.5f); Typography::instance().textColored(TypeStyle::Caption, OnSurfaceMedium(), license); EndCard(); } } inline void SettingsScreen::notifySettingsChanged() { if (m_onSettingsChanged) { m_onSettingsChanged(m_settings); } } } // namespace screens } // namespace ui } // namespace dragonx