From 76a751f125ba2dfcbc49b038f740df2d450235db Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Fri, 21 Aug 2026 20:28:12 +0200 Subject: [PATCH] Measure the grid against the grid, not the whole view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `cell-size`, `columns` and `visible-rows` were all derived from the LibraryGrid's own width and height. The cells are not drawn in that box: the capture-time axis is a sibling of the grid, 96px of it, and the header takes another 44px off the top. So `columns` counted the timeline as room for thumbnails and fitted one more column than there was space for. The last column started inside the Flickable and ended outside it — clipped, with no sideways scroll to reach it. At the 180px size class on a phone in portrait the timeline is a quarter of the screen, which is most of a column. `visible-rows` was wrong the same way and fed `capacity`, so every window over-fetched by the ratio of the header to the viewport. Both now read `grid-area`, the layout the cells actually live in. That is a descendant, and reading a descendant's geometry is the shape that causes binding loops elsewhere in this UI — but not here: nothing derived from these feeds back into the layout. The cells are placed absolutely inside the Flickable and a Flickable's layout constraints are a bare `stretch: 1` that its viewport cannot influence. --- ui/dr-ui/ui/library.slint | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/ui/dr-ui/ui/library.slint b/ui/dr-ui/ui/library.slint index 1a8a305..cb29e7e 100644 --- a/ui/dr-ui/ui/library.slint +++ b/ui/dr-ui/ui/library.slint @@ -933,6 +933,25 @@ export component LibraryGrid inherits Rectangle { /// below, which is this capped to what the grid can hold. in property requested-cell-size: 180px; + // --- what the grid is measured against --------------------------------- + // + // **`grid-area`, not `self`.** Every quantity below describes the box the + // cells are actually drawn in, and that box is *not* this component: the + // capture-time axis is a sibling of it, 96px wide, and the header sits + // above it. Measuring against `self.width` therefore counted the timeline + // as room for thumbnails and fitted one more column than there was space + // for — the last column ran off the right-hand edge, clipped by the + // Flickable, with no way to scroll sideways to it. At 180px cells that is + // a whole column lost on a phone in portrait, where 96px of 400 is a + // quarter of the screen. + // + // Reading a descendant's geometry is safe here because nothing derived + // from it feeds back into the layout: the cells are placed absolutely + // inside the Flickable, and a Flickable's own layout constraints are a + // bare `stretch: 1` that its viewport cannot influence. That is the loop + // the comments on `panel-visible` in app.slint warn about, and this is not + // an instance of it. + /// The drawn cell, never wider than the column it has to sit in. /// /// `columns` floors to a minimum of one, so a cell larger than the grid @@ -946,9 +965,10 @@ export component LibraryGrid inherits Rectangle { /// choice intact — rotate to landscape, or close the sidebar, and the /// thumbnails grow back to the size that was asked for. property cell-size: - min(root.requested-cell-size, max(64px, self.width - 2 * Theme.gap)); + min(root.requested-cell-size, max(64px, grid-area.width - 2 * Theme.gap)); - property columns: max(1, floor((self.width - Theme.gap) / (cell-size + Theme.gap))); + property columns: + max(1, floor((grid-area.width - Theme.gap) / (cell-size + Theme.gap))); // Reported out so Rust can place month headings: a heading belongs on a // cell that begins a row, and only the grid knows how wide a row is. changed columns => { root.columns-changed(root.columns); } @@ -960,7 +980,8 @@ export component LibraryGrid inherits Rectangle { // Derived rather than a constant: a fixed window is simultaneously too // small on a maximised 4K window — where three screenfuls fit inside it — // and wasteful on a narrow one. - property visible-rows: max(1, ceil(self.height / (cell-size + Theme.gap))); + property visible-rows: + max(1, ceil(grid-area.height / (cell-size + Theme.gap))); property capacity: root.columns * (root.visible-rows * 3); changed capacity => { root.capacity-changed(root.capacity); } /// Rows the *whole library* occupies, which is what the scrollbar spans. @@ -1418,7 +1439,7 @@ export component LibraryGrid inherits Rectangle { zoom(d) => { root.timeline-zoom(d); } } - VerticalLayout { + grid-area := VerticalLayout { horizontal-stretch: 1; // --- empty state --------------------------------------------------