fix(ui): stop the Consolidate modal from jumping around during a run

The overlay dialog is auto-height and vertically centred, so it re-centers
whenever its content height changes. During a consolidation run two lines toggle
and made it jump: the pause notice (waiting for a batch gap / sync / a send),
which flickers on and off every batch, and the "run again" leftovers hint, which
appears partway through. Reserve a fixed slot for the pause notice and, while the
run is live, reserve the leftovers hint's measured height up front, so the
content height stays constant and the modal holds still. The end-of-run
transition to the result view is unchanged. Build + tests + hygiene green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 15:09:27 -05:00
co-authored by Claude Opus 4.8
parent 7138f633a8
commit 3b828e8a49
+22 -4
View File
@@ -720,7 +720,14 @@ void ShieldDialog::render(App* app)
std::max(1, p.batchesEstimated), p.notesMerged);
wrappedCaption(material::OnSurfaceMedium(), cap);
if (!p.stopping) {
if (const char* key = services::runWaitKey(run.lastWait())) wrappedCaption(material::OnSurfaceMedium(), TR(key));
// The pause notice (waiting for a batch gap / sync / a send) toggles on and off every batch.
// Reserve a fixed two-line slot for it so its coming and going doesn't change the content
// height and make the auto-height, vertically-centred modal jump around during the run.
const float waitTop = ImGui::GetCursorPosY();
if (const char* key = services::runWaitKey(run.lastWait()))
wrappedCaption(material::OnSurfaceMedium(), TR(key));
const float waitSlotH = ImGui::GetTextLineHeightWithSpacing() * 2.0f;
if (ImGui::GetCursorPosY() < waitTop + waitSlotH) ImGui::SetCursorPosY(waitTop + waitSlotH);
}
}
ImGui::ProgressBar(static_cast<float>(p.batchesDone) / static_cast<float>(std::max(1, p.batchesEstimated)),
@@ -765,9 +772,20 @@ void ShieldDialog::render(App* app)
}
}
ImGui::EndChild();
if (leftovers) {
ImGui::Spacing();
wrappedCaption(material::OnSurfaceMedium(), TR("merge_keep_each_run_again"));
{
// The "run again" hint appears partway through a run (once an address has leftover notes). While
// the run is live, reserve its measured height up front so it appearing doesn't shift the modal.
const float loTop = ImGui::GetCursorPosY();
if (leftovers) {
ImGui::Spacing();
wrappedCaption(material::OnSurfaceMedium(), TR("merge_keep_each_run_again"));
}
if (live) {
const float loH = ImGui::GetStyle().ItemSpacing.y +
ImGui::CalcTextSize(TR("merge_keep_each_run_again"), nullptr, false,
ImGui::GetContentRegionAvail().x).y;
if (ImGui::GetCursorPosY() < loTop + loH) ImGui::SetCursorPosY(loTop + loH);
}
}
if (!live) { // below the rows: merge_keep_each_some_failed says "see the list above"
std::string endLine;