Keep the selection visible when the grid scrolls under it
Scrolling rebuilds every cell, and a fresh cell carries `selected: false`. `sync_badges` and `sync_ratings` refilled what the rebuild cleared; nothing refilled the selection, so the ticks vanished on every scroll. The selection itself was never lost — it is a set of image ids and survives untouched — which made this worse than losing it: the header buttons still acted on forty photographs the user could no longer see were held. `load_window` reaches the selection through a `Weak` handle set at wiring. Weak because the two controllers are joined only through the window, and an `Rc` each way would leak both; absent, the grid draws nothing selected, which is what it did before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -121,6 +121,14 @@ pub struct LibraryController {
|
|||||||
/// A `Cell` because it is a `bool` read inside callbacks that already hold
|
/// A `Cell` because it is a `bool` read inside callbacks that already hold
|
||||||
/// other borrows.
|
/// other borrows.
|
||||||
viewing_trash: std::cell::Cell<bool>,
|
viewing_trash: std::cell::Cell<bool>,
|
||||||
|
/// The selection's owner, so a rebuilt window can restore the `selected`
|
||||||
|
/// flags it just cleared.
|
||||||
|
///
|
||||||
|
/// `Weak` because the two controllers outlive each other only through the
|
||||||
|
/// window, and an `Rc` both ways would leak both. Set once at wiring; a
|
||||||
|
/// `None` here means the grid simply draws nothing selected, which is the
|
||||||
|
/// old behaviour rather than a crash.
|
||||||
|
coll_ctl: RefCell<Option<std::rc::Weak<crate::collections_ui::CollectionsController>>>,
|
||||||
/// Timeline view state: how far zoomed in, and around what instant.
|
/// Timeline view state: how far zoomed in, and around what instant.
|
||||||
///
|
///
|
||||||
/// Zoom is a level rather than a span so the axis halves and doubles in
|
/// Zoom is a level rather than a span so the axis halves and doubles in
|
||||||
@@ -255,6 +263,7 @@ impl LibraryController {
|
|||||||
session: RefCell::new(None),
|
session: RefCell::new(None),
|
||||||
scope: RefCell::new(None),
|
scope: RefCell::new(None),
|
||||||
viewing_trash: std::cell::Cell::new(false),
|
viewing_trash: std::cell::Cell::new(false),
|
||||||
|
coll_ctl: RefCell::new(None),
|
||||||
timeline_zoom: RefCell::new(0),
|
timeline_zoom: RefCell::new(0),
|
||||||
timeline_centre: RefCell::new(None),
|
timeline_centre: RefCell::new(None),
|
||||||
pinch_accum: RefCell::new(1.0),
|
pinch_accum: RefCell::new(1.0),
|
||||||
@@ -1730,6 +1739,14 @@ fn load_window(window: &AppWindow, ctl: &Rc<LibraryController>) {
|
|||||||
.collect();
|
.collect();
|
||||||
crate::collections_ui::sync_badges(window, catalog, &ids);
|
crate::collections_ui::sync_badges(window, catalog, &ids);
|
||||||
sync_ratings(window, catalog, &ids);
|
sync_ratings(window, catalog, &ids);
|
||||||
|
// The rebuilt cells all carry `selected: false`, but the selection itself
|
||||||
|
// is a set of image ids and survives untouched. Without this the ticks
|
||||||
|
// vanished on every scroll — the selection was still there and still acted
|
||||||
|
// on, which is worse than losing it, because the user cannot see what the
|
||||||
|
// buttons are about to do.
|
||||||
|
if let Some(coll) = ctl.coll_ctl.borrow().as_ref().and_then(|w| w.upgrade()) {
|
||||||
|
crate::collections_ui::sync_selection(window, &coll, &ids);
|
||||||
|
}
|
||||||
// The filter chips' counts describe the whole library, not this window, so
|
// The filter chips' counts describe the whole library, not this window, so
|
||||||
// they are refreshed here rather than per cell.
|
// they are refreshed here rather than per cell.
|
||||||
refresh_rating_counts(window, catalog);
|
refresh_rating_counts(window, catalog);
|
||||||
@@ -3117,6 +3134,10 @@ pub fn wire<F>(
|
|||||||
) where
|
) where
|
||||||
F: Fn(String) + 'static,
|
F: Fn(String) + 'static,
|
||||||
{
|
{
|
||||||
|
// So a rebuilt window can put the selection ticks back. Weak, or the two
|
||||||
|
// controllers would hold each other alive for the life of the process.
|
||||||
|
*ctl.coll_ctl.borrow_mut() = Some(Rc::downgrade(&coll_ctl));
|
||||||
|
|
||||||
// Shared rather than moved: a click and `Return` both open an image, and
|
// Shared rather than moved: a click and `Return` both open an image, and
|
||||||
// they are two callbacks.
|
// they are two callbacks.
|
||||||
let on_open_image = Rc::new(on_open_image);
|
let on_open_image = Rc::new(on_open_image);
|
||||||
|
|||||||
Reference in New Issue
Block a user