diff --git a/src/ui/material/draw_helpers.h b/src/ui/material/draw_helpers.h index e96a03e..dd8ea65 100644 --- a/src/ui/material/draw_helpers.h +++ b/src/ui/material/draw_helpers.h @@ -1288,6 +1288,9 @@ inline int SegmentedControl(ImDrawList* dl, ImVec2 origin, float totalW, float h // carries the active dialog's key from BeginOverlayDialog to EndOverlayDialog (overlays don't nest). inline std::unordered_map g_overlayCardHeights; inline std::string g_overlayCurrentKey; +// Set when BeginOverlayDialog clips the card on an auto-height dialog's measuring frame (so it never +// flashes off-center); EndOverlayDialog pops the clip. Overlays don't nest, so a single flag is fine. +inline bool g_overlayHideFrameClip = false; // The dark base color for any full-window overlay backdrop (dialog scrims + the shutdown screen), // so every overlay shares one backdrop tone. `opacity` is the alpha (dialogs vary it per call). @@ -1402,6 +1405,7 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec) // first open of a given dialog this session briefly anchors near the top before it re-centers). float cardX = vp_pos.x + (vp_size.x - cardWidth) * 0.5f; float cardY, cardBottomY; + bool hideForMeasure = false; // true on an auto-height dialog's first (unmeasured) frame const bool fixedHeight = (spec.cardHeight > 0.0f); if (fixedHeight) { float cardH = std::min(spec.cardHeight * dp, vp_size.y - 32.0f); @@ -1419,23 +1423,27 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec) : vp_pos.y + 16.0f; cardBottomY = cardY + measuredH; } else { - // First frame: content height not yet known. Anchor near the top; it re-centers next frame. + // First frame: content height not yet known. Lay it out (so the auto-height child gets + // measured) but keep the card hidden (hideForMeasure below) so it never flashes off-center + // — it appears, already centered, on the next frame once the height is known. + hideForMeasure = true; cardY = vp_pos.y + vp_size.y * 0.15f; cardBottomY = vp_pos.y + vp_size.y * spec.cardBottomViewportRatio; } } ImVec2 cardMin(cardX, cardY), cardMax(cardX + cardWidth, cardBottomY); - // Card background — a glass panel unless the content floats directly on the backdrop. - if (!floating) { + // Card background — a glass panel unless the content floats directly on the backdrop. Skipped on + // the measuring frame (its geometry is a placeholder; the whole card is hidden until centered). + if (!floating && !hideForMeasure) { GlassPanelSpec cardGlass; cardGlass.rounding = 16.0f; cardGlass.fillAlpha = 35; cardGlass.borderAlpha = 50; cardGlass.borderWidth = 1.0f; DrawGlassPanel(dl, cardMin, cardMax, cardGlass); } - // Click outside the card dismisses — but not on the appearing frame (the opening click) or while - // a popup opened from the dialog is showing (a click in an overhanging combo would look outside). - if (spec.p_open && !overlayPopupOpen && !ImGui::IsWindowAppearing() && + // Click outside the card dismisses — but not on the measuring frame (placeholder geometry), the + // appearing frame (the opening click), or while a popup opened from the dialog is showing. + if (spec.p_open && !hideForMeasure && !overlayPopupOpen && !ImGui::IsWindowAppearing() && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsMouseHoveringRect(cardMin, cardMax, false)) { *spec.p_open = false; @@ -1458,6 +1466,14 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec) ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.5f, 0.5f)); } + // Measuring frame: clip the card to nothing. Content (title + the caller's body) still lays out, + // so the child's auto-height is measured for next frame, but nothing is drawn — no off-center + // flash. EndOverlayDialog pops this before EndChild. Only entered when childVisible (End runs). + if (childVisible && hideForMeasure) { + ImGui::PushClipRect(ImVec2(0, 0), ImVec2(0, 0), false); + g_overlayHideFrameClip = true; + } + if (childVisible && spec.title) { if (plainHd) { ImGui::PushFont(Type().h6()); @@ -1491,6 +1507,7 @@ inline bool BeginOverlayDialog(const char* title, bool* p_open, float cardWidth inline void EndOverlayDialog() { + if (g_overlayHideFrameClip) { ImGui::PopClipRect(); g_overlayHideFrameClip = false; } ImGui::EndChild(); // Remember the rendered card height (the child is the last item) so the next frame's // BeginOverlayDialog can size the glass to the content — kills the empty band under short dialogs.