Show what is selected, and let a collection be made of it
Build and test / Desktop (Linux) (push) Failing after 2m37s
Build and test / Layer separation (push) Successful in 26s
🐳 Android image / Build and push (push) Successful in 4s
Build and test / android-image (push) Successful in 5s
Traceability / Requirement traces (push) Successful in 40s
Build and test / Android (aarch64) (push) Failing after 6s

Three things a selection needed and did not have.

**Seeing it.** The count existed — "12 selected" — in the header row, which
scrolls sideways. On a tablet it sat past the right-hand edge along with every
button beside it, so a selection was something you could make and then not
see. A selection you cannot see is one you act on by accident.

**Putting it down.** The only way to clear one was "Done", which also leaves
select mode — so after filing forty photographs the next forty began by
re-entering a mode the user had not meant to leave. Clearing is now its own
action and keeps the mode.

**Filing it somewhere new.** Making a collection of a selection took four
steps: create one, find it in the tree, select the photographs again because
creating it changed the scope, then add them. It is one press, which is how a
selection is usually meant — it is gathered *because* it is going somewhere.

The new collection is created at the top level rather than inside the current
scope, unlike the tree's "+". A selection can be gathered from anywhere,
including across collections, so filing it under whichever one happens to be
open would put it somewhere its contents did not come from. It opens straight
into its name field, for the reason `collection_new` already does: the
placeholder name is nobody's choice, and making the user find the rename
afterwards is asking them to finish a job we started.

All of it on its own strip beside the date range's, appearing only while there
is a selection — the third control this session that was invisible for being
put in a row that scrolls.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-23 17:57:35 +02:00
co-authored by Claude Opus 5
parent ffc40c42d2
commit b0b6dd559a
4 changed files with 175 additions and 16 deletions
+88
View File
@@ -1516,6 +1516,94 @@ pub fn wire<S, R, C>(
});
}
// TRACES: FR-CAT-5
// Put the selection down without leaving select mode.
//
// Distinct from "Done", which does both: after picking forty photographs
// and filing them, the next forty start with the mode already on, and
// making the user re-enter it is asking them to undo a step they did not
// take. It is also the only way back from a mis-tap that is not tapping
// every one of them again.
{
let weak = window.as_weak();
let ctl = ctl.clone();
let visible = visible_ids.clone();
window.on_library_clear_selection(move || {
let Some(w) = weak.upgrade() else { return };
ctl.clear_selection();
sync_selection(&w, &ctl, &visible());
});
}
// TRACES: FR-CAT-5
// A collection holding exactly what is selected.
//
// This was four steps — make a collection, find it in the tree, select the
// photographs again because creating one changed the scope, then add them
// — and the selection is usually made *because* it is going somewhere. One
// press instead.
//
// Created at the top level rather than inside the current scope, unlike
// the tree's "+". A selection can be gathered from anywhere, including
// across collections, so filing it under whichever one happens to be open
// would put it somewhere its contents did not come from.
{
let weak = window.as_weak();
let ctl = ctl.clone();
let catalog = catalog.clone();
let visible = visible_ids.clone();
let reload = on_scope_changed.clone();
window.on_library_collection_from_selection(move || {
let Some(w) = weak.upgrade() else { return };
let images = ctl.selected();
if images.is_empty() {
return;
}
let borrow = catalog.borrow();
let Some(cat) = borrow.as_ref() else {
w.set_collection_error("Open a library first.".into());
return;
};
let name = unique_name(cat.connection(), None);
let made = coll::create(cat.connection(), &name, None, CollectionKind::Manual)
.and_then(|id| coll::add_images(cat.connection(), id, &images).map(|n| (id, n)));
match made {
Ok((id, added)) => {
w.set_collection_error(slint::SharedString::new());
w.set_library_status(
format!(
"{added} photograph{} in {name}",
if added == 1 { "" } else { "s" }
)
.into(),
);
refresh_tree(&w, &ctl, cat);
drop(borrow);
// The selection has been filed; holding on to it invites
// the next press acting on photographs the user considers
// dealt with.
ctl.clear_selection();
sync_selection(&w, &ctl, &visible());
// Into the name field, for the same reason `collection_new`
// does it: "New collection" is a placeholder nobody wants
// to keep, and making them find the rename afterwards is
// asking them to finish a job we started.
w.set_collection_renaming(id.0 as i32);
reload();
}
Err(e) => {
drop(borrow);
w.set_collection_error(format!("making a collection: {e}").into());
}
}
});
}
// TRACES: FR-UI-2 | FR-UI-4
// The button half of selection mode. The long press is faster and this is
// the one that can be found — a gesture with no visible counterpart is a