Files
ObsidianDragon/src/ui/effects/theme_effects.h
DanS 2c5a658ea5 feat: Full UI internationalization, pool hashrate stats, and layout caching
- Replace all hardcoded English strings with TR() translation keys across
  every tab, dialog, and component (~20 UI files)
- Expand all 8 language files (de, es, fr, ja, ko, pt, ru, zh) with
  complete translations (~37k lines added)
- Improve i18n loader with exe-relative path fallback and English base
  fallback for missing keys
- Add pool-side hashrate polling via pool stats API in xmrig_manager
- Introduce Layout::beginFrame() per-frame caching and refresh balance
  layout config only on schema generation change
- Offload daemon output parsing to worker thread
- Add CJK subset fallback font for Chinese/Japanese/Korean glyphs
2026-03-11 00:40:50 -05:00

251 lines
11 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#include <imgui.h>
#include <vector>
#include <string>
namespace dragonx {
namespace ui {
namespace effects {
/**
* @brief Per-theme visual effects system.
*
* Draws animated accents (hue-cycling, rainbow borders, shimmer sweeps,
* glow pulses, positional tinting) driven entirely by TOML theme config.
* All effects use ImDrawList primitives — no shaders or texture uploads.
*/
class ThemeEffects {
public:
static ThemeEffects& instance();
/// Called once per frame (timing updates)
void beginFrame();
/// Master toggle (from settings)
void setEnabled(bool enabled) { enabled_ = enabled; }
bool isEnabled() const { return enabled_; }
/// Background opacity (from window opacity slider). Multiplies
/// all effect alphas so they fade with the backdrop while UI stays opaque.
void setBackgroundOpacity(float o) { bgOpacity_ = std::max(0.0f, std::min(1.0f, o)); }
float backgroundOpacity() const { return bgOpacity_; }
/// Reduced transparency suppresses shimmer + rainbow border
void setReducedTransparency(bool rt) { reduced_transparency_ = rt; }
bool isReducedTransparency() const { return reduced_transparency_; }
/// Reload [effects] config from the active UISchema overlay
void loadFromTheme();
// === Drawing APIs ===
/// Get the current hue-cycled accent color (optionally phase-shifted)
ImU32 getAccentColor(float phaseOffset = 0.0f) const;
/// Draw rainbow gradient border around a rect (for glass panels)
void drawRainbowBorder(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
float rounding, float thickness) const;
/// Draw shimmer sweep over a rect
void drawShimmer(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
float rounding) const;
/// Get positional hue tint for a given screen Y
ImU32 getPositionalTint(float screenY) const;
/// Tint an existing color by positional hue
ImU32 tintByPosition(ImU32 baseColor, float screenY) const;
/// Draw glow pulse behind a rect (for active elements)
void drawGlowPulse(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
float rounding) const;
/// Draw a light that traces along the border perimeter
void drawEdgeTrace(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
float rounding) const;
/// Draw a border that shifts between two colors over time (gem-like)
void drawGradientBorderShift(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
float rounding) const;
/// Draw ember particles that rise from an element (fire theme)
void drawEmberRise(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax) const;
/// Draw specular glare highlights on a panel (polished surface look)
void drawSpecularGlare(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
float rounding) const;
/// Draw viewport-wide ambient ember particles (fire theme atmosphere)
void drawViewportEmbers(ImDrawList* dl) const;
/// Draw viewport-wide sandstorm particles (wind-driven diagonal sand/dust)
void drawSandstorm(ImDrawList* dl) const;
/// Draw shader-like viewport overlay (color wash + vignette post-processing)
void drawViewportOverlay(ImDrawList* dl) const;
/// Draw all applicable panel effects (edge trace + ember rise on glass panels)
/// phaseKey provides per-panel phase diversity based on panel position
void drawPanelEffects(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
float rounding) const;
// === Query which effects are available ===
bool hasRainbowBorder() const { return enabled_ && !reduced_transparency_ && rainbow_border_.enabled; }
bool hasShimmer() const { return enabled_ && !reduced_transparency_ && shimmer_.enabled; }
bool hasGlowPulse() const { return enabled_ && glow_pulse_.enabled; }
bool hasHueCycle() const { return enabled_ && hue_cycle_.enabled; }
bool hasPositionalHue() const { return enabled_ && positional_hue_.enabled; }
bool hasEdgeTrace() const { return enabled_ && edge_trace_.enabled; } bool hasGradientBorder() const { return enabled_ && gradient_border_.enabled; } bool hasEmberRise() const { return enabled_ && ember_rise_.enabled; }
bool hasSpecularGlare() const { return enabled_ && !reduced_transparency_ && specular_glare_.enabled; }
bool hasSandstorm() const { return enabled_ && sandstorm_.enabled; }
bool hasViewportOverlay() const { return enabled_ && (viewport_overlay_.colorWashEnabled || viewport_overlay_.vignetteEnabled); }
/// True when any per-panel visual effect is active (rainbow, shimmer, specular, edge, ember, gradient)
bool hasAnyPanelEffect() const {
return hasRainbowBorder() || hasShimmer() || hasSpecularGlare()
|| hasEdgeTrace() || hasEmberRise() || hasGradientBorder();
}
/// True when any time-dependent effect is active and needs continuous redraws
bool hasActiveAnimation() const {
if (!enabled_) return false;
return hue_cycle_.enabled || rainbow_border_.enabled || shimmer_.enabled
|| glow_pulse_.enabled || edge_trace_.enabled || ember_rise_.enabled
|| gradient_border_.enabled || specular_glare_.enabled || sandstorm_.enabled
|| (viewport_overlay_.colorWashEnabled
&& (viewport_overlay_.washRotateSpeed > 0.0f || viewport_overlay_.washPulseSpeed > 0.0f));
}
private:
ThemeEffects() = default;
bool enabled_ = true;
bool reduced_transparency_ = false;
float time_ = 0.0f;
float bgOpacity_ = 1.0f; ///< window-opacity multiplier for effect alphas
// Viewport bounds (set each frame)
float vpMinY_ = 0.0f;
float vpMaxY_ = 1.0f;
// ---- Cached config from [effects] ----
struct HueCycleConfig {
bool enabled = false;
float speed = 0.1f, sat = 0.6f, val = 0.85f;
float range = 1.0f, offset = 0.0f;
} hue_cycle_;
struct RainbowBorderConfig {
bool enabled = false;
float speed = 0.05f, alpha = 0.25f;
std::vector<ImU32> stops;
} rainbow_border_;
struct ShimmerConfig {
bool enabled = false;
float speed = 0.12f, width = 80.0f, alpha = 0.06f, angle = 30.0f;
ImU32 color = IM_COL32(255,255,255,255);
} shimmer_;
struct PositionalHueConfig {
bool enabled = false;
ImVec4 topColor{1,0.42f,0.62f,1};
ImVec4 bottomColor{0.4f,0.91f,0.98f,1};
float strength = 0.3f;
} positional_hue_;
struct GlowPulseConfig {
bool enabled = false;
float speed = 2.0f, minAlpha = 0.0f, maxAlpha = 0.15f, radius = 4.0f;
ImU32 color = IM_COL32(255,255,255,255);
} glow_pulse_;
struct EdgeTraceConfig {
bool enabled = false;
float speed = 0.3f; ///< full circuits per second
float length = 0.20f; ///< fraction of perimeter lit (tail length)
float thickness = 1.5f; ///< line thickness in pixels
float alpha = 0.6f; ///< peak alpha at head of trace
ImU32 color = IM_COL32(255,255,255,255);
} edge_trace_;
struct EmberRiseConfig {
bool enabled = false;
int count = 8; ///< number of ember particles
float speed = 0.4f; ///< rise cycles per second
float particleSize = 1.5f;///< ember radius in pixels
float alpha = 0.5f; ///< peak alpha
ImU32 color = IM_COL32(255, 120, 20, 255);
} ember_rise_;
struct GradientBorderConfig {
bool enabled = false;
float speed = 0.15f; ///< full color shift cycles per second
float thickness = 1.5f; ///< border line thickness in pixels
float alpha = 0.6f; ///< peak alpha
ImU32 colorA = IM_COL32(206, 147, 216, 255); ///< first color (amethyst)
ImU32 colorB = IM_COL32(26, 35, 126, 255); ///< second color (indigo)
} gradient_border_;
struct SpecularGlareConfig {
bool enabled = false;
float speed = 0.02f; ///< drift speed (Lissajous orbit cycles/sec)
float intensity = 0.06f; ///< peak alpha at glare center
float radius = 0.5f; ///< glare radius as fraction of panel min-dim
int count = 2; ///< number of specular spots per panel
ImU32 color = IM_COL32(255, 255, 255, 255);
} specular_glare_;
struct SandstormConfig {
bool enabled = false;
int count = 80; ///< number of sand particles
float speed = 0.35f; ///< base horizontal velocity (viewport widths/sec)
float windAngle = 15.0f; ///< degrees from horizontal (+ve = downward)
float particleSize = 1.5f; ///< base particle radius in pixels
float alpha = 0.35f; ///< peak particle alpha
float gustSpeed = 0.07f; ///< gust oscillation frequency (Hz)
float gustStrength = 0.4f; ///< speed variation from gusts (fraction)
float streakLength = 3.0f; ///< motion blur multiplier for fast particles
ImU32 color = IM_COL32(200, 160, 96, 255);
} sandstorm_;
struct ViewportOverlayConfig {
// --- Color wash: full-screen 4-corner gradient overlay ---
bool colorWashEnabled = false;
ImU32 cornerTL = IM_COL32(255,100,0,255); ///< top-left corner color (RGB; alpha applied separately)
ImU32 cornerTR = IM_COL32(68,34,0,255); ///< top-right
ImU32 cornerBL = IM_COL32(68,17,0,255); ///< bottom-left
ImU32 cornerBR = IM_COL32(255,153,0,255); ///< bottom-right
float washAlpha = 0.05f; ///< overall wash intensity
float washRotateSpeed = 0.0f; ///< corner color rotation (turns/sec)
float washPulseSpeed = 0.0f; ///< alpha breathing speed (Hz)
float washPulseDepth = 0.0f; ///< pulse depth (0=none, 1=full fade)
// --- Vignette: edge darkening/tinting ---
bool vignetteEnabled = false;
ImU32 vignetteColor = IM_COL32(0,0,0,255); ///< tint color at edges
float vignetteRadius = 0.35f; ///< fade zone as fraction of viewport
float vignetteAlpha = 0.30f; ///< max alpha at outer edge
} viewport_overlay_;
/// Map a 0..1 fraction to a point on the rounded rect perimeter
ImVec2 perimeterPoint(ImVec2 pMin, ImVec2 pMax, float t, float rounding = 0.0f) const;
// Helper: parse hex color string "#RRGGBB" or "#RRGGBBAA" to ImU32
static ImU32 parseHexColor(const std::string& hex, ImU32 fallback = IM_COL32(255,255,255,255));
static ImVec4 parseHexColorVec4(const std::string& hex, ImVec4 fallback = ImVec4(1,1,1,1));
// Helper: sample a rotating multi-stop gradient
ImU32 sampleGradient(float t) const;
};
} // namespace effects
} // namespace ui
} // namespace dragonx