fix(dialogs): size the overlay glass card to its content

The overlay dialog's content child is AutoResizeY, but the glass card behind
it was drawn to a fixed viewport ratio — leaving a tall band of empty glass
below short dialogs (e.g. the key-export modal had a gap under its Close
button). Measure the rendered card height each frame and reuse it next frame
to draw the glass to the content; fall back to (and stay capped at) the ratio
so tall dialogs are unchanged and can't run off-screen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 17:38:19 -05:00
parent 3a4998f57c
commit 00ee61fe64

View File

@@ -916,6 +916,12 @@ inline bool DrawDialogTitleBar(const char* title, bool* p_open, ImU32 accent_col
// Creates a fullscreen semi-transparent overlay with a centered card dialog.
// Similar to the shutdown screen pattern but for interactive dialogs.
// Per-dialog content height (keyed by the child's id) measured at the end of each frame, so the next
// frame can size the glass card to its content instead of a fixed viewport band. g_overlayCurrentKey
// carries the active dialog's key from BeginOverlayDialog to EndOverlayDialog (overlays don't nest).
inline std::unordered_map<std::string, float> g_overlayCardHeights;
inline std::string g_overlayCurrentKey;
inline bool BeginOverlayDialog(const char* title, bool* p_open, float cardWidth = 460.0f, float scrimOpacity = 0.92f,
float cardBottomViewportRatio = 0.85f, const char* idSuffix = nullptr)
{
@@ -977,10 +983,22 @@ inline bool BeginOverlayDialog(const char* title, bool* p_open, float cardWidth
// Calculate card position (centered)
float cardX = vp_pos.x + (vp_size.x - cardWidth) * 0.5f;
float cardY = vp_pos.y + vp_size.y * 0.15f;
// Size the card height to its content. The content child below is AutoResizeY, so a glass card
// drawn to a fixed viewport ratio left a tall band of empty glass under short dialogs. Reuse the
// height the child reported LAST frame (content is stable frame-to-frame, so no visible lag) and
// fall back to the ratio on the first frame. Still capped at the ratio so a very tall dialog can't
// run off-screen (its content spills/scrolls as before).
g_overlayCurrentKey = childId;
float ratioMaxY = vp_pos.y + vp_size.y * cardBottomViewportRatio;
auto prevHeightIt = g_overlayCardHeights.find(childId);
float cardBottomY = (prevHeightIt != g_overlayCardHeights.end() && prevHeightIt->second > 0.0f)
? std::min(cardY + prevHeightIt->second, ratioMaxY)
: ratioMaxY;
// Draw glass card background
ImVec2 cardMin(cardX, cardY);
ImVec2 cardMax(cardX + cardWidth, vp_pos.y + vp_size.y * cardBottomViewportRatio);
ImVec2 cardMax(cardX + cardWidth, cardBottomY);
// Card background with glass effect
GlassPanelSpec cardGlass;
@@ -1020,6 +1038,11 @@ inline bool BeginOverlayDialog(const char* title, bool* p_open, float cardWidth
inline void EndOverlayDialog()
{
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.
if (!g_overlayCurrentKey.empty()) {
g_overlayCardHeights[g_overlayCurrentKey] = ImGui::GetItemRectSize().y;
}
ImGui::PopStyleColor(); // ChildBg
ImGui::PopStyleVar(2); // ChildRounding, WindowPadding (for child)