diff --git a/src/ui/material/draw_helpers.h b/src/ui/material/draw_helpers.h index 3524548..dede7ee 100644 --- a/src/ui/material/draw_helpers.h +++ b/src/ui/material/draw_helpers.h @@ -545,10 +545,51 @@ inline bool& FullWindowBlurOverlayActiveRef() { static bool v = false; return v; inline void SetFullWindowBlurOverlayActive(bool v) { FullWindowBlurOverlayActiveRef() = v; } inline bool IsFullWindowBlurOverlayActive() { return FullWindowBlurOverlayActiveRef(); } +// Light-surface themes (Light / Marble) want cards to lift off the pale background with a subtle +// drop shadow. Cached per theme-generation so the string compare runs once per theme load, not per +// panel. (Data-driven later if more themes want it.) +inline bool CardDropShadowWanted() +{ + static uint32_t s_gen = ~0u; + static bool s_want = false; + uint32_t g = schema::UI().generation(); + if (g != s_gen) { + s_gen = g; + const std::string& tn = schema::UI().themeName(); + s_want = (tn == "Light" || tn == "Marble"); + } + return s_want; +} + +// Soft drop shadow OUTSIDE a rounded rect, offset down. Drawn as fading rounded-rect STROKES (not +// fills), so it never paints across the card body — only the outer blur remains once the card fill +// covers the interior. This is the "render then mask out the UI area" result without an FBO. +inline void DrawCardDropShadow(ImDrawList* dl, const ImVec2& pMin, const ImVec2& pMax, float rounding) +{ + const float dp = Layout::dpiScale(); + const int kRings = 10; + const float spread = 13.0f * dp; // how far the shadow reaches beyond the card edge + const float offY = 3.0f * dp; // downward drop offset + for (int i = kRings; i >= 1; --i) { + float t = (float)i / (float)kRings; // 1 (outer) .. 0.1 (near the card) + float e = spread * t; // expansion at this ring + int a = (int)(20.0f * (1.0f - t) + 0.5f); // alpha: ~0 at the outer edge, strongest by the card + if (a < 1) continue; + ImVec2 smn(pMin.x - e, pMin.y - e + offY); + ImVec2 smx(pMax.x + e, pMax.y + e + offY); + dl->AddRect(smn, smx, IM_COL32(0, 0, 0, a), rounding + e, 0, 2.0f * dp); + } +} + inline void DrawGlassPanel(ImDrawList* dl, const ImVec2& pMin, const ImVec2& pMax, const GlassPanelSpec& spec = GlassPanelSpec()) { + // Subtle drop shadow behind the card (light themes only). Drawn first so the card fill below + // covers the interior, leaving only the outer soft blur — the card body is never contaminated. + if (CardDropShadowWanted()) + DrawCardDropShadow(dl, pMin, pMax, spec.rounding); + if (IsBackdropActive() && !dragonx::ui::effects::isLowSpecMode() && !IsFullWindowBlurOverlayActive()) { // --- Cached color lookups (invalidated on theme change) --- // These 3 resolveColor() calls do string parsing + map lookup