fix(modals): vertically center auto-height dialogs in the window

Fixed-height overlay cards were already centered, but auto-height cards
hard-anchored at 15% from the top, so shorter dialogs (e.g. Migrate to a seed
wallet, Import Key, About) rendered top-heavy with a large empty area below.

Center auto-height cards on last frame's measured content height (already cached
per dialog id in g_overlayCardHeights). A re-opened dialog centers from frame 1;
only the very first open of a given dialog this session briefly anchors near the
top before it re-centers. Content taller than the window top-anchors at a 16px
margin. One framework change centers every modal (verified across migrate /
import-key / about / seed-backup at 100%).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 15:00:15 -05:00
parent ed4d61c364
commit 1fd794ccc1

View File

@@ -1396,8 +1396,10 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec)
ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonRight |
ImGuiButtonFlags_MouseButtonMiddle); ImGuiButtonFlags_MouseButtonMiddle);
// Card geometry: fixed-height (large modals) is centered; auto-height reuses last frame's // Card geometry: both fixed- and auto-height cards are VERTICALLY CENTERED in the window.
// measured content height (short dialogs get short cards), capped at the viewport ratio. // Fixed-height centers its known height; auto-height centers last frame's measured content
// height (cached per dialog id, so a re-opened dialog is centered from frame 1 — only the very
// 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 cardX = vp_pos.x + (vp_size.x - cardWidth) * 0.5f;
float cardY, cardBottomY; float cardY, cardBottomY;
const bool fixedHeight = (spec.cardHeight > 0.0f); const bool fixedHeight = (spec.cardHeight > 0.0f);
@@ -1407,12 +1409,20 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec)
cardBottomY = cardY + cardH; cardBottomY = cardY + cardH;
g_overlayCurrentKey.clear(); g_overlayCurrentKey.clear();
} else { } else {
cardY = vp_pos.y + vp_size.y * 0.15f;
g_overlayCurrentKey = childId; g_overlayCurrentKey = childId;
float ratioMaxY = vp_pos.y + vp_size.y * spec.cardBottomViewportRatio;
auto it = g_overlayCardHeights.find(childId); auto it = g_overlayCardHeights.find(childId);
cardBottomY = (it != g_overlayCardHeights.end() && it->second > 0.0f) const float measuredH = (it != g_overlayCardHeights.end()) ? it->second : 0.0f;
? std::min(cardY + it->second, ratioMaxY) : ratioMaxY; if (measuredH > 0.0f) {
// Center the measured content; if it's taller than the window, anchor at the top margin.
cardY = (measuredH < vp_size.y - 32.0f)
? vp_pos.y + (vp_size.y - measuredH) * 0.5f
: 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.
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); ImVec2 cardMin(cardX, cardY), cardMax(cardX + cardWidth, cardBottomY);