1 Commits
Author SHA1 Message Date
chris.giteaandClaude Sonnet 5 b6c9ab8a3f feat(gallery): replace ThumbnailGalleryPane with a justified grid (JustifiedGalleryPane)
New component (JustifiedRow, JustifiedGridItem, JustifiedGridCell,
JustifiedGalleryPane) replaces ThumbnailGalleryPane as the pane
GalleryView instantiates. Single navigation selection only (0 or 1
file, driven uniformly by click and arrow-key browsing) plus a
separate, independent action-selection set (0..N files, keyed by id,
for a future grouped-action trigger not wired up yet) — the old
per-file checkbox/grouped-actions machinery is not ported.

Layout: JustifiedRow.justify groups files into fixed-height rows
(GalleryRow.chunk's own day-merging rule, ported as-is), each entry
rendered at exactly its own natural aspect-ratio width — never
stretched to fill the row, and never squeezed via a growable gap
either (both were tried and reverted: image distortion and an
uneven, growing gap between thumbnails were both worse than a row
occasionally falling short of the container's full width). The one
exception is a single file alone in its row already wider than
availableWidth (an extreme panorama), clamped narrower — cropped,
never stretched. JustifiedGridItem.itemize splits date headers into
their own fixed-height grid item, ahead of the row they label,
keeping exactly two fixed item heights for the ListView (never a
third, row-dependent one) — the same VirtualFlow cell-recycling
desync class of bug legacy.GalleryGridItem's own javadoc documents.

Selection frame: JustifiedGridCell draws navigation selection as a
plain Rectangle added to/removed from its card's children — never a
CSS pseudo-class or Node.visible — after both of those were confirmed
not to reliably repaint a virtualised cell's descendant under
AtlantaFX (see the GuiBootstrap fix below for the actual root cause).

GuiBootstrap now also sets prism.dirtyopts=false before the JavaFX
toolkit starts, working around a genuine Prism dirty-region
computation bug (confirmed against JavaFX 27): a changed pixel region
inside a virtualised ListCell descendant can go unpainted until an
unrelated pointer event forces a larger repaint elsewhere. Isolated
by ruling out every other layer in turn — reproduces regardless of
-Dprism.order (software or hardware rasteriser), disappears entirely
under JavaFX's own near-empty default stylesheet, and the pulse
logger (-Djavafx.pulseLogger=true) named the responsible phase
directly ("Dirty Opts Computed"). Cost measured on a 67 000+ file
library: average paint time per pulse rises from 7.15ms to 10.26ms,
frames missing a 16ms/60Hz budget from 1.3% to 1.8% — real but
imperceptible in manual scroll testing. GuiBootstrapTest guards the
property-setting code itself (a real visual regression test for the
underlying Prism bug is impractical in a headless unit test).

ThumbnailGalleryPane/GalleryGridCell/GalleryRow/GalleryGridItem move
to a new legacy package, package declarations only, zero functional
change (diffed against HEAD to confirm) — kept because they still
carry the grouped-actions mechanism this component doesn't yet have.
GalleryTimelineBar widens to public (used by both packages now, no
duplication, zero behaviour change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sfcVC7rYy5k8XiKCbh4Tr
2026-09-28 22:34:20 -04:00