From 063f1fa4be30312ea94e7ebff62213eb5530bee1 Mon Sep 17 00:00:00 2001 From: DanS Date: Fri, 10 Jul 2026 20:07:24 -0500 Subject: [PATCH] feat(themes): subtle card drop shadow for Light + Marble MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cards on the pale Light / Marble backgrounds now lift off the surface with a subtle drop shadow. Drawn in DrawGlassPanel (behind the card fill) as fading rounded-rect STROKES offset down — strokes never paint across the card body, so only the outer soft blur remains once the fill covers the interior (the "render then mask out the UI area" result, no FBO needed). Gated to the Light and Marble themes (cached per theme-generation); dark themes are unchanged. Verified across marble / light (shadow present, smooth, body clean) and dark (none). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui/material/draw_helpers.h | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) 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