Let a collection be picked up, rearranged, and emptied after the fact
Collections could be made and filled and never reorganised. Nesting had a drag; un-nesting had nothing, in either direction — "All photographs" refused every drop, which is right for a photograph and wrong for a collection, which has a top level to be returned to. So a collection put inside another was in there permanently. Right-click deleted an *empty* collection outright and refused otherwise, which is wrong in both directions at once: destructive with no confirmation, and no way at all to delete a collection that held anything without emptying it by hand, child by child. And a photograph could only leave the collection the grid was scoped to, since that is the only one a button in the header can name — the cell's badge says a photograph is in three collections and never which three. Three ways in, one vocabulary: **Hold a row.** The tree is inside a Flickable, which claims any drag beginning inside it, so with a finger a drag on a row is a scroll until something says otherwise. The hold is that something. It lifts the row — drawn before anything moves, so the gesture says it has been understood — and then what the user does decides which of two things they meant: move, and it is a rearrangement; let go, and it is the row menu. The same fork the grid already uses to tell hold-to-select from drag-to-file. `decide_release` is that fork, and it is tested, because getting it wrong one way puts a sheet over every tidied tree and the other way makes the menu unreachable by touch. **The row menu.** Rename, new collection inside, move to top level, keep offline, delete. Deleting asks once when there is anything to lose and says what survives: the photographs stay in the library, and nested collections move up rather than going with it — which is what the catalog does, and what a user would never assume. An empty collection goes on the first press, because a dialogue about losing nothing is how people learn to dismiss dialogues. **"Collections…" on a selection.** Every collection the selection is filed in, each with a count — "3 of 40", so nobody takes forty photographs out of a collection thirty-seven were never in — and a way out of any of them without navigating there first. The long press used to open the offline question by itself. That question is one item in this menu now: there is one hold per row, and while it was spent on a single action nothing else the tree can do had a touch route at all. Nothing is lost — the tray on the row keeps its tap, and the question gains a full-width control in place of a 30px icon in a row shorter than the touch minimum. The row-press handler moves to `collections_ui` with the rest of what a collection row does; it lived in `library_ui` only because it opened that prompt.
This commit is contained in:
@@ -357,11 +357,6 @@ pub struct LibraryController {
|
||||
/// subject back out of a string property would act on whatever the sidebar
|
||||
/// had been rebuilt to say since.
|
||||
offline_target: std::cell::Cell<Option<dr_types::CollectionId>>,
|
||||
/// TRACES: FR-NC-6a | FR-UI-2
|
||||
/// The timer that turns a held sidebar row into that question. Dropped on
|
||||
/// release, so a tap — or a press the Flickable takes for a scroll — is not
|
||||
/// a dialogue a moment later.
|
||||
row_hold_timer: RefCell<Option<slint::Timer>>,
|
||||
/// Narrow the grid to images whose original is stored locally.
|
||||
///
|
||||
/// A `Cell` beside `filter` rather than a field inside it: the rating
|
||||
@@ -455,7 +450,6 @@ impl LibraryController {
|
||||
pending_anchor: std::cell::Cell::new(None),
|
||||
pin_timer: RefCell::new(None),
|
||||
offline_target: std::cell::Cell::new(None),
|
||||
row_hold_timer: RefCell::new(None),
|
||||
local_only: std::cell::Cell::new(false),
|
||||
// The catalog's own floor until the settings page reports what the
|
||||
// user has stored, which it does at startup before any fetch.
|
||||
@@ -6594,48 +6588,15 @@ pub fn wire<F>(
|
||||
let ctl = ctl.clone();
|
||||
window.on_collection_offline_menu(move |id| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
*ctl.row_hold_timer.borrow_mut() = None;
|
||||
open_offline_prompt(&w, &ctl, dr_types::CollectionId(id as u64));
|
||||
});
|
||||
}
|
||||
|
||||
// TRACES: FR-NC-6a | FR-UI-2 | FR-UI-4
|
||||
// And the touch way in: hold the collection's name.
|
||||
//
|
||||
// The release that ends the hold still reaches the row's `clicked` and
|
||||
// scopes the grid to that collection. Left deliberately: the user is now
|
||||
// looking at the photographs they are being asked about, which is context
|
||||
// rather than a side effect — and suppressing it would mean a second
|
||||
// "ignore the next click" flag threaded through the sidebar for no gain.
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
let coll_for_press = coll_ctl.clone();
|
||||
window.on_collection_row_press(move |id, down| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
// A drag of this row, if one follows, carries this collection.
|
||||
if down {
|
||||
coll_for_press.note_row_press(dr_types::CollectionId(id as u64));
|
||||
}
|
||||
if !down {
|
||||
*ctl.row_hold_timer.borrow_mut() = None;
|
||||
return;
|
||||
}
|
||||
|
||||
let timer = slint::Timer::default();
|
||||
let weak = w.as_weak();
|
||||
let ctl_cb = ctl.clone();
|
||||
timer.start(
|
||||
slint::TimerMode::SingleShot,
|
||||
std::time::Duration::from_millis(crate::collections_ui::HOLD_DELAY_MS),
|
||||
move || {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
open_offline_prompt(&w, &ctl_cb, dr_types::CollectionId(id as u64));
|
||||
},
|
||||
);
|
||||
*ctl.row_hold_timer.borrow_mut() = Some(timer);
|
||||
});
|
||||
}
|
||||
// The hold on a sidebar row — which arms the drag that rearranges the tree
|
||||
// and opens the row menu — is wired in `collections_ui`, with the rest of
|
||||
// what a collection row does. It used to be here because it opened the
|
||||
// offline question and nothing else; that question is now one item in that
|
||||
// menu, so the gesture belongs with the menu rather than with the transfers.
|
||||
|
||||
// The three answers.
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user