fix(gallery): force a follow-up pulse after a large grid relayout

Clearing a search filter that had narrowed the grid to a handful of rows
swaps in a much longer item list in one go. Some of VirtualFlow's newly
realised GalleryRowCells were laid out (thumbnail decode requested) but
not actually painted until an unrelated later pulse caught up -- moving
the mouse only "fixed" it because Scene's picking machinery forces a
synchronous CSS+layout pass before hit-testing. Scheduling
rows.requestLayout() on a fresh Platform.runLater pulse reproduces that
same forced pass without needing real input, the same trick already used
elsewhere in this class (pulseCardOnceSettled, hookNativeScrollBar).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 15:03:33 -04:00
co-authored by Claude Sonnet 5
parent 05e4b0bf92
commit 09c58aba1b
@@ -499,6 +499,16 @@ public class ThumbnailGalleryPane extends StackPane implements Disposable {
List<GalleryRow> grouped = GalleryRow.chunk(sorted, availableWidth, thumbnailHeight, SPACING, DATE_SPACING,
date -> date == null ? i18n.get("gallery.unknownDate") : i18n.formatDate(date, headerFormat));
rows.getItems().setAll(grouped);
// A relayout that swaps in a much longer item list in one go (typically: clearing a search filter
// that had narrowed the grid down to a handful of rows) can leave some of VirtualFlow's newly
// realised cells laid out but not actually painted — their GalleryRowCell.renderPhotos already ran,
// async thumbnail decodes already requested, but nothing visible until some later, unrelated pulse
// catches it up; moving the mouse "fixes" it only because Scene's own picking machinery forces a
// synchronous CSS+layout pass before hit-testing. requestLayout() on a fresh pulse (never this one,
// already mid-reflow) reproduces that same forced pass without needing real input — the same
// "VirtualFlow needs another pulse to settle" trick #reveal/#pulseCardOnceSettled and
// #hookNativeScrollBar already rely on Platform.runLater for elsewhere in this class.
Platform.runLater(rows::requestLayout);
// Only when height/quality actually changed since the last relayout: a change small enough to
// leave the column count (and so every GalleryRow's own grouping) unchanged produces a
// structurally-equal item list — nothing for the ListView to notice on its own — while every