Files
ObsidianDragon/src/ui/schema/skin_manager.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

193 lines
6.5 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#include <string>
#include <vector>
#include <functional>
#include <filesystem>
namespace dragonx {
namespace ui {
namespace schema {
/**
* @brief Manages bundled and user-installed skins (unified TOML files)
*
* Responsibilities:
* - Enumerate bundled skins from res/themes/ directory (any .toml except ui.toml)
* - Enumerate user themes from ~/.config/ObsidianDragon/themes/<folder>/theme.toml
* - Import / remove user skins
* - Validate skin TOML structure before import
* - Track active skin ID in settings
*/
class SkinManager {
public:
/**
* @brief Metadata about an available skin
*/
struct SkinInfo {
std::string id; ///< Unique identifier (folder name or filename stem)
std::string name; ///< Display name from theme.name
std::string author; ///< Author from theme.author
std::string path; ///< Full filesystem path to the TOML file
std::string directory; ///< Folder containing theme.toml (empty for bundled flat files)
std::string backgroundImagePath; ///< Resolved path to background image override (empty = use default)
std::string logoPath; ///< Resolved path to logo image override (empty = use default)
bool dark = true; ///< Dark mode flag from theme.dark
bool bundled = true; ///< true = shipped with app, false = user-installed
bool valid = true; ///< true if theme.toml passed validation
std::string validationError; ///< Error message if !valid
};
/**
* @brief Result of a skin validation
*/
struct ValidationResult {
bool valid = false;
std::string error; ///< Error message if !valid
};
/**
* @brief Get the singleton instance
*/
static SkinManager& instance();
/**
* @brief Scan for available skins (bundled + user)
*
* Re-scans both the bundled res/ directory and the user skins directory.
* Call this on startup and after import/remove operations.
*/
void refresh();
/**
* @brief Get the list of available skins
* @return Sorted list: bundled skins first (with "dragonx" at top), then user skins
*/
const std::vector<SkinInfo>& available() const { return skins_; }
/**
* @brief Find a skin by ID
* @return Pointer to SkinInfo, or nullptr if not found
*/
const SkinInfo* findById(const std::string& id) const;
/**
* @brief Validate a skin TOML file
* @param path Path to the TOML file
* @return Validation result with error message if invalid
*
* Validation rules:
* 1. File must be valid TOML
* 2. Must contain [theme] table
* 3. theme.name must be a non-empty string
* 4. theme.palette must be a table with at least --primary and --background
* 5. If [globals] exists, it must be a table
*/
static ValidationResult validateSkinFile(const std::string& path);
/**
* @brief Import a skin file into the user skins directory
* @param sourcePath Path to the source TOML file
* @return true if imported successfully
*
* Validates the file first. Copies to user skins directory.
* Calls refresh() on success.
*/
bool importSkin(const std::string& sourcePath);
/**
* @brief Remove a user-installed skin
* @param id Skin ID to remove
* @return true if removed successfully
*
* Cannot remove bundled skins. Calls refresh() on success.
*/
bool removeSkin(const std::string& id);
/**
* @brief Apply a skin by ID
* @param id Skin ID to activate
* @return true if skin was found and loaded
*
* Loads the skin file into UISchema and applies ImGui colors.
*/
bool setActiveSkin(const std::string& id);
/**
* @brief Get the currently active skin ID
*/
const std::string& activeSkinId() const { return activeSkinId_; }
/**
* @brief Get the bundled skins directory path
*/
static std::string getBundledSkinsDirectory();
/**
* @brief Get the user skins directory path
*/
static std::string getUserSkinsDirectory();
/**
* @brief Set callback invoked after skin changes (for image reloading)
* @param cb Callback receiving backgroundImagePath and logoPath (empty = use default)
*/
void setImageReloadCallback(std::function<void(const std::string& bgPath, const std::string& logoPath)> cb) {
imageReloadCb_ = std::move(cb);
}
/**
* @brief Re-resolve image paths from UISchema and trigger reload callback
*
* Called by UISchema hot-reload when [theme.images] values change.
* Updates the active SkinInfo's image paths and fires imageReloadCb_.
*
* @param skinId Active skin ID to update
* @param tomlPath Path to the TOML file whose images section changed
*/
void resolveAndReloadImages(const std::string& skinId, const std::string& tomlPath);
/**
* @brief Enable/disable gradient background mode
*
* When enabled, theme backgrounds are replaced with their gradient
* variants (e.g. "gradient_drgx_bg.png" instead of "drgx_bg.png").
* Falls back to dark_gradient.png or light_gradient.png when no
* theme-specific gradient exists.
*/
void setGradientMode(bool enabled);
bool isGradientMode() const { return gradientMode_; }
private:
SkinManager() = default;
~SkinManager() = default;
SkinManager(const SkinManager&) = delete;
SkinManager& operator=(const SkinManager&) = delete;
void scanDirectory(const std::string& dir, bool bundled);
/// Resolve the image directory for a given skin
std::filesystem::path resolveImgDir(const SkinInfo* skin) const;
/// Given an original bg relative path and img dir, resolve the gradient variant
std::string resolveGradientBg(const std::string& bgFilename,
const std::filesystem::path& imgDir,
bool isDark) const;
/// Common helper: resolve bg (optionally gradient) and logo, fire callback
void resolveAndFireCallback(SkinInfo* skin, const std::filesystem::path& imgDir);
std::vector<SkinInfo> skins_;
std::string activeSkinId_ = "dragonx";
bool gradientMode_ = false;
std::function<void(const std::string&, const std::string&)> imageReloadCb_;
};
} // namespace schema
} // namespace ui
} // namespace dragonx