Let a manual collection be put in the order it is meant to be seen in
`collection_members.position` and `Sort::CollectionPosition` have been in the catalog since collections were, and nothing above dr-catalog has ever written or read either: `collections::set_order` had no callers, and the grid ordered everything by capture time whatever it was scoped to — dr-ui does not construct a `Query` at all, it has its own `GRID_ORDER` constant. So a manual collection was a set with an order nobody could see or change. Three pieces, because it could not be fewer: `grid_order_for` decides the ordering from the scope, and both readers take it from there. That is the load-bearing part. An ordinal only names a photograph relative to an ordering, so the window read and the span read have to agree — a shift-click resolved through a different ORDER BY than the cells were drawn with selects a different run than the one on screen, and the user finds out when the export runs. `read_ids_span` already stated that invariant about `GRID_ORDER`; this widens it to an ordering that depends on the scope. Only a single manual collection has one. A set draws its descendants' images too, and two children's positions are unrelated integers that interleave arbitrarily; a smart collection has no member rows to carry a position at all. Both fall back to capture time and refuse the drop rather than pretending. The drop is on the cell, on whichever half of it the finger landed — the trailing edge is the only way to name the last place in a collection, since there is no cell beyond the last one to drop in front of. `reordered` is pure and the membership is rewritten whole. `set_order` sets the positions it is given and leaves the rest, so a partial write would interleave the moved run with rows nobody touched; and it is read unfiltered, so what the filter is hiding keeps its place relative to what the user can see. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1223,6 +1223,27 @@ export component LibraryGrid inherits Rectangle {
|
||||
in property <bool> select-mode: false;
|
||||
callback toggle-select-mode();
|
||||
/// TRACES: FR-CAT-5
|
||||
// --- reordering a manual collection (FR-CAT-7) --------------------------
|
||||
//
|
||||
// `collection_members.position` and `Sort::CollectionPosition` have existed
|
||||
// in the catalog since collections did, and nothing above it ever wrote or
|
||||
// read them: the grid ordered everything by capture time, always. This is
|
||||
// the gesture that makes the column mean something.
|
||||
//
|
||||
// Only where there is a manual order to change — a single manual collection
|
||||
// with no children. A set interleaves two children's unrelated positions
|
||||
// and a smart collection has no member rows at all, so both fall back to
|
||||
// capture time and refuse the drop rather than pretending.
|
||||
/// Whether a drop on a cell should move photographs within the collection
|
||||
/// being shown. Set by Rust from the scope, because what a scope *is* is a
|
||||
/// catalog question.
|
||||
in property <bool> reorderable: false;
|
||||
/// Move the selection so it sits beside the photograph at this row of the
|
||||
/// loaded window — before it, or after it where the drop landed on the
|
||||
/// cell's trailing half. The trailing half is what makes the last position
|
||||
/// reachable at all; without it there is no cell to drop "before".
|
||||
callback reorder-to(int, bool);
|
||||
|
||||
/// Drop the selection without leaving select mode.
|
||||
callback clear-selection();
|
||||
/// TRACES: FR-CAT-5 | FR-UI-4
|
||||
@@ -2756,6 +2777,41 @@ export component LibraryGrid inherits Rectangle {
|
||||
}
|
||||
drag-finished(action) => { root.drag-finished(); }
|
||||
|
||||
// Where a reorder lands. Behind the cell's content and
|
||||
// before it in the file, for the reason `TreeRow`'s drop
|
||||
// area gives: a `DropArea` only takes part in a drag, so it
|
||||
// does not block the presses the cell's own TouchArea
|
||||
// needs — but drawn first it cannot paint over the
|
||||
// thumbnail either.
|
||||
//
|
||||
// Present on every cell rather than wrapped in an `if`, so
|
||||
// the marker below can name it. `can-drop` is where the
|
||||
// refusal lives, which also means the cursor says no while
|
||||
// the user can still aim somewhere else.
|
||||
reorder-drop := DropArea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
/// Whether the run would land after this photograph
|
||||
/// rather than before it. Tracked during the hover so
|
||||
/// the marker can move to the edge the drop will
|
||||
/// actually use.
|
||||
property <bool> after: false;
|
||||
|
||||
can-drop(ev) => {
|
||||
if (!root.reorderable) {
|
||||
return DragAction.none;
|
||||
}
|
||||
self.after = ev.position.x > self.width / 2;
|
||||
return DragAction.copy;
|
||||
}
|
||||
|
||||
dropped(ev) => {
|
||||
root.reorder-to(i, ev.position.x > self.width / 2);
|
||||
return DragAction.copy;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
// Lifted cells shrink toward their own centre, as though pulled
|
||||
// off the page. Inset rather than scaled: Slint has no transform
|
||||
@@ -3030,6 +3086,28 @@ export component LibraryGrid inherits Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Where the run would land. A bar in the gutter beside the
|
||||
// cell it would sit next to, on whichever side the drop
|
||||
// will actually use — the trailing edge is what makes the
|
||||
// last place in a collection reachable, and a marker that
|
||||
// did not move with it would be pointing at the wrong gap
|
||||
// half the time.
|
||||
//
|
||||
// Last child of the `DragArea`, so it draws over the
|
||||
// thumbnail rather than under it. In the gutter rather than
|
||||
// on the cell, because a bar drawn *on* the first cell of a
|
||||
// row reads as belonging to that cell instead of to the
|
||||
// space before it.
|
||||
if reorder-drop.has-drag: Rectangle {
|
||||
x: reorder-drop.after
|
||||
? parent.width + Theme.gap / 2 - 1.5px
|
||||
: -Theme.gap / 2 - 1.5px;
|
||||
y: 0;
|
||||
width: 3px;
|
||||
height: parent.height;
|
||||
background: Theme.active;
|
||||
border-radius: 1.5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user