fix(modals): a fixed-card modal can no longer wheel-scroll as a whole

The overlay content child had NoScrollbar (hides the scrollbar) but not
NoScrollWithMouse — so when a fixed-height card's content overflowed by even a
few px, the mouse wheel silently drifted the ENTIRE dialog (title + panes +
footer). Add NoScrollWithMouse to the content child: a modal is a fixed frame;
inner scroll regions (version list, release notes, command list) still scroll on
their own, and auto-height cards resize to content so they never overflow.

Also fix the daemon updater's footer budget, which under-counted the
Spacing/Separator/Spacing + inter-item gaps and produced exactly that few-px
overflow. Verified daemon / console / wallets / portfolio render intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 16:52:43 -05:00
parent 6b7438b529
commit c352364bdc
2 changed files with 11 additions and 2 deletions

View File

@@ -1476,9 +1476,13 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec)
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, floating ? ImVec2(28, 20) : ImVec2(28, 24)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, floating ? ImVec2(28, 20) : ImVec2(28, 24));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0)); // transparent (glass/blur behind) ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0)); // transparent (glass/blur behind)
ImGuiChildFlags cflags = ImGuiChildFlags_AlwaysUseWindowPadding | (fixedHeight ? 0 : ImGuiChildFlags_AutoResizeY); ImGuiChildFlags cflags = ImGuiChildFlags_AlwaysUseWindowPadding | (fixedHeight ? 0 : ImGuiChildFlags_AutoResizeY);
// NoScrollWithMouse (not just NoScrollbar): a modal is a fixed frame — the wheel must never drift
// the WHOLE card. If content marginally overflows a fixed card, the wheel would otherwise scroll
// the entire dialog (title + footer and all). Inner scroll regions (lists, notes) still scroll on
// their own; auto-height cards resize to content so they never overflow anyway.
bool childVisible = ImGui::BeginChild(childId.c_str(), bool childVisible = ImGui::BeginChild(childId.c_str(),
ImVec2(cardWidth, fixedHeight ? (cardBottomY - cardY) : 0.0f), ImVec2(cardWidth, fixedHeight ? (cardBottomY - cardY) : 0.0f),
cflags, ImGuiWindowFlags_NoScrollbar); cflags, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
// Floating (portfolio-style) cards: the padding applies to this content child only, so pop it // Floating (portfolio-style) cards: the padding applies to this content child only, so pop it
// now (nested children mustn't inherit it), and center button labels. Net style-var count stays // now (nested children mustn't inherit it), and center button labels. Net style-var count stays
// at 2 (ChildRounding + ButtonTextAlign) so EndOverlayDialog's PopStyleVar(2) is unchanged. // at 2 (ChildRounding + ButtonTextAlign) so EndOverlayDialog's PopStyleVar(2) is unchanged.

View File

@@ -226,7 +226,12 @@ private:
ImFont* capF = Type().caption(); ImFont* capF = Type().caption();
const bool showFooter = (p.state == St::ReleaseList); const bool showFooter = (p.state == St::ReleaseList);
const float footerH = showFooter ? (ImGui::GetFrameHeight() + Layout::spacingSm() * 2.0f + 1.0f) : 0.0f; // Budget the whole footer block — the Spacing + Separator + Spacing + button row and every
// inter-item gap — so the two-pane body leaves exact room and nothing overflows the card.
const float footerH = showFooter
? (ImGui::GetFrameHeightWithSpacing() + ImGui::GetStyle().ItemSpacing.y * 3.0f
+ Layout::spacingSm() * 2.0f + 1.0f)
: 0.0f;
const float bodyH = std::max(120.0f * dp, ImGui::GetContentRegionAvail().y - footerH); const float bodyH = std::max(120.0f * dp, ImGui::GetContentRegionAvail().y - footerH);
const float contentW = ImGui::GetContentRegionAvail().x; const float contentW = ImGui::GetContentRegionAvail().x;
const float gap = Layout::spacingLg(); const float gap = Layout::spacingLg();