From fe186282162f1c32eb3cff6f94de581a05890afa Mon Sep 17 00:00:00 2001 From: DanS Date: Fri, 3 Jul 2026 21:15:12 -0500 Subject: [PATCH] feat(market): bump the portfolio backdrop blur radius (64) via sole-consumer isolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Raising the backdrop radius alone would thrash the shared radius-keyed blur cache (the glass panels blur at ~30). Instead, while a full-window blur overlay is active, glass panels use their opaque fallback (IsFullWindowBlurOverlayActive) — they're covered by the backdrop anyway — so the backdrop is the SOLE applyBlur caller and can use a strong custom radius (64) with the cache staying coherent and frozen. The flag is toggled alongside the theme-effect suppression by a RAII guard in App::render(). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app.cpp | 24 ++++++++++++++++-------- src/ui/material/draw_helpers.h | 18 +++++++++++++----- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index 211463c..60a54b1 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -1243,16 +1243,24 @@ void App::render() // Process deferred encryption from wizard (runs in background) processDeferredEncryption(); - // While the full-window portfolio modal is open, suppress panel theme-effects for the whole - // frame so their foreground-draw-list borders (sidebar included) don't bleed over the overlay. + // While the full-window portfolio modal is open: (1) suppress panel theme-effects for the whole + // frame so their foreground-draw-list borders (sidebar included) don't bleed over the overlay; + // (2) mark a full-window blur overlay active so glass panels use their opaque fallback (they're + // covered by the overlay backdrop, and this leaves the backdrop as the sole acrylic blur caller). // RAII-restored to the prior state on any return path. - struct FxSuppressGuard { - bool active; - explicit FxSuppressGuard(bool on) : active(on) { - if (active) ui::effects::ThemeEffects::instance().setEnabled(false); + struct ModalRenderGuard { + bool fxOn, wasEffects; + explicit ModalRenderGuard(bool modalOpen) { + wasEffects = ui::effects::ThemeEffects::instance().isEnabled(); + fxOn = modalOpen && wasEffects; + if (fxOn) ui::effects::ThemeEffects::instance().setEnabled(false); + ui::material::SetFullWindowBlurOverlayActive(modalOpen); } - ~FxSuppressGuard() { if (active) ui::effects::ThemeEffects::instance().setEnabled(true); } - } fxGuard(ui::PortfolioEditorActive() && ui::effects::ThemeEffects::instance().isEnabled()); + ~ModalRenderGuard() { + if (fxOn) ui::effects::ThemeEffects::instance().setEnabled(true); + ui::material::SetFullWindowBlurOverlayActive(false); + } + } modalGuard(ui::PortfolioEditorActive()); // Main content area - use full window (no menu bar) ImGuiViewport* viewport = ImGui::GetMainViewport(); diff --git a/src/ui/material/draw_helpers.h b/src/ui/material/draw_helpers.h index 38e78dd..4e37150 100644 --- a/src/ui/material/draw_helpers.h +++ b/src/ui/material/draw_helpers.h @@ -377,11 +377,19 @@ struct GlassPanelSpec { float borderWidth = 1.0f; }; +// When a full-window blur overlay (e.g. the portfolio modal) is active, glass panels use their +// opaque fallback instead of the acrylic blur. They're covered by the overlay's backdrop anyway, and +// this leaves the backdrop as the SOLE applyBlur caller — so it can use its own (stronger) blur +// radius without thrashing the shared, radius-keyed blur cache. Toggled from App::render(). +inline bool& FullWindowBlurOverlayActiveRef() { static bool v = false; return v; } +inline void SetFullWindowBlurOverlayActive(bool v) { FullWindowBlurOverlayActiveRef() = v; } +inline bool IsFullWindowBlurOverlayActive() { return FullWindowBlurOverlayActiveRef(); } + inline void DrawGlassPanel(ImDrawList* dl, const ImVec2& pMin, const ImVec2& pMax, const GlassPanelSpec& spec = GlassPanelSpec()) { - if (IsBackdropActive() && !dragonx::ui::effects::isLowSpecMode()) { + if (IsBackdropActive() && !dragonx::ui::effects::isLowSpecMode() && !IsFullWindowBlurOverlayActive()) { // --- Cached color lookups (invalidated on theme change) --- // These 3 resolveColor() calls do string parsing + map lookup // each time. Cache them per-frame using the schema generation @@ -490,11 +498,11 @@ inline void DrawFullWindowBlurBackdrop(ImDrawList* dl, const ImVec2& pMin, const dl->AddRectFilled(pMin, pMax, IM_COL32(9, 10, 16, 255)); // opaque base (shows through if the blur is faint) if (allowBlur && IsBackdropActive() && !dragonx::ui::effects::isLowSpecMode() && effects::ImGuiAcrylic::IsEnabled() && effects::ImGuiAcrylic::IsAvailable()) { - // Keep the theme card's blurRadius (do NOT override it): applyBlur caches a single blurred - // buffer keyed on radius, shared with every glass panel drawn this frame (market cards + the - // modal preview card). A different radius here would thrash that cache every frame and make - // the backdrop sample the wrong blur. Same radius => one blur serves all, and it stays frozen. + // Safe to use a strong custom radius here: while a full-window overlay is active every glass + // panel skips acrylic (IsFullWindowBlurOverlayActive), so the backdrop is the SOLE applyBlur + // caller — no other radius contends for the shared, radius-keyed blur cache. auto params = GetCurrentAcrylicTheme().card; + params.blurRadius = 64.0f; // strong full-window blur params.fallbackColor.w = 1.0f; // full-strength blur so the live content reads effects::ImGuiAcrylic::DrawAcrylicRect(dl, pMin, pMax, params, 0.0f); dl->AddRectFilled(pMin, pMax, IM_COL32(6, 8, 18, 70)); // slight dim so the card reads as foreground