fix(material): make blur-overlay effect guard truly close-equivalent

The adversarial review found the ModalRenderGuard lagged the old
PortfolioEditorActive() signal by one extra frame on CLOSE (a possible 1-frame
"opaque panels" flash). Latch the overlay's open flag (already available via
spec.p_open) — "drew this frame AND still open after Esc/Close/outside handling" —
instead of just "drew last frame", so the guard drops and the acrylic re-capture
fire on the same frame the overlay closes, matching the old inline behavior on
both edges.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-04 02:46:11 -05:00
parent ebf01bd858
commit 5caffc84b0

View File

@@ -1039,18 +1039,25 @@ inline bool DrawDialogTitleBar(const char* title, bool* p_open, ImU32 accent_col
// "A blur overlay was drawn this frame" flag + a 1-frame latch. App::render builds its // "A blur overlay was drawn this frame" flag + a 1-frame latch. App::render builds its
// effect-suppression guard at the TOP of the frame, before dialogs dispatch, so it reads the // effect-suppression guard at the TOP of the frame, before dialogs dispatch, so it reads the
// LAST-frame latch (a stable signal) rather than this frame's in-progress state. // LAST-frame latch (a stable signal) rather than this frame's in-progress state.
inline bool& BlurOverlayDrawnThisFrameRef() { static bool v = false; return v; } inline bool& BlurOverlayDrawnThisFrameRef() { static bool v = false; return v; }
inline bool& BlurOverlayActiveLastFrameRef() { static bool v = false; return v; } inline bool& BlurOverlayActiveLastFrameRef() { static bool v = false; return v; }
inline void MarkBlurOverlayDrawn() { BlurOverlayDrawnThisFrameRef() = true; } inline bool*& BlurOverlayOpenFlagRef() { static bool* p = nullptr; return p; }
// Record that a blur overlay drew this frame, along with its open flag (so the latch below can tell
// "still open next frame" from "closed this frame" and match the modal's actual open state — no
// extra frame of suppression on close).
inline void MarkBlurOverlayDrawn(bool* openFlag) { BlurOverlayDrawnThisFrameRef() = true; BlurOverlayOpenFlagRef() = openFlag; }
inline bool AnyBlurOverlayActiveLastFrame() { return BlurOverlayActiveLastFrameRef(); } inline bool AnyBlurOverlayActiveLastFrame() { return BlurOverlayActiveLastFrameRef(); }
// Latch this frame's state for the next frame's guard; on the open->closed transition, invalidate // Latch whether an overlay will still be up next frame (drew this frame AND its open flag is still
// the acrylic capture so the other glass panels re-capture the (overlay-free) background. Call once // set after this frame's Esc/Close/outside-click handling); on the open->closed transition,
// at the end of App::render. // invalidate the acrylic capture so the other glass panels re-capture the (overlay-free)
// background. Call once at the end of App::render.
inline void LatchBlurOverlayActive() { inline void LatchBlurOverlayActive() {
bool now = BlurOverlayDrawnThisFrameRef(); bool* pf = BlurOverlayOpenFlagRef();
if (BlurOverlayActiveLastFrameRef() && !now) effects::ImGuiAcrylic::InvalidateCapture(); bool stillOpen = BlurOverlayDrawnThisFrameRef() && (!pf || *pf);
BlurOverlayActiveLastFrameRef() = now; if (BlurOverlayActiveLastFrameRef() && !stillOpen) effects::ImGuiAcrylic::InvalidateCapture();
BlurOverlayActiveLastFrameRef() = stillOpen;
BlurOverlayDrawnThisFrameRef() = false; BlurOverlayDrawnThisFrameRef() = false;
BlurOverlayOpenFlagRef() = nullptr;
} }
// True during the frames a blur overlay is (re)capturing the live backdrop — the scroll-fade shader // True during the frames a blur overlay is (re)capturing the live backdrop — the scroll-fade shader
@@ -1197,7 +1204,7 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec)
// few frames on open/resize, then reuse the frozen blurred snapshot. Low-spec / acrylic-off falls // few frames on open/resize, then reuse the frozen blurred snapshot. Low-spec / acrylic-off falls
// back to an opaque base inside DrawFullWindowBlurBackdrop (no blur cost). // back to an opaque base inside DrawFullWindowBlurBackdrop (no blur cost).
if (blur) { if (blur) {
MarkBlurOverlayDrawn(); MarkBlurOverlayDrawn(spec.p_open);
BlurCaptureState& st = BlurCaptureStateFor(scrimId); BlurCaptureState& st = BlurCaptureStateFor(scrimId);
int frame = ImGui::GetFrameCount(); int frame = ImGui::GetFrameCount();
bool justOpened = (st.lastSeenFrame < frame - 1); bool justOpened = (st.lastSeenFrame < frame - 1);