diff --git a/ui/dr-ui/src/collections_ui.rs b/ui/dr-ui/src/collections_ui.rs index 719402d..2af8d92 100644 --- a/ui/dr-ui/src/collections_ui.rs +++ b/ui/dr-ui/src/collections_ui.rs @@ -898,19 +898,25 @@ pub fn sync_lifted(window: &AppWindow, lifted: &[ImageId], ids: &[ImageId]) { pub fn sync_selection(window: &AppWindow, ctl: &Rc, ids: &[ImageId]) { let selection = ctl.selection.borrow(); let model = window.get_library_cells(); - // The anchor is an ordinal and the model is a window, so it marks a row - // only while the photograph it names is on screen. Off screen it marks - // nothing, which is the honest answer: the range still extends from there. - let offset = window.get_library_offset().max(0) as usize; - let anchor_row = ctl.anchor.borrow().and_then(|at| at.checked_sub(offset)); + // The anchor is deliberately **not** pushed to the grid. + // + // It used to be, and it was drawn as a thin ring inside the cell. Two + // things were wrong with that. It was the clearest mark on the cell, so it + // read as *the* selection to anyone who had not written it; and an anchor + // outlives a deselection, so the ring sat around the last photograph + // touched while nothing was selected at all — indistinguishable from a cell + // that had stayed behind. + // + // The ordinal still lives in the controller and still decides where a range + // extends from. What is gone is the claim that the user needs to see it: + // the selection bar says "Tap the last photograph" while a range is armed, + // which answers the question the ring was there to answer. for (row, id) in ids.iter().enumerate() { let want = selection.contains(id); - let anchored = anchor_row == Some(row); if let Some(mut cell) = model.row_data(row) { - if cell.selected != want || cell.anchor != anchored { + if cell.selected != want { cell.selected = want; - cell.anchor = anchored; model.set_row_data(row, cell); } } diff --git a/ui/dr-ui/src/library_ui.rs b/ui/dr-ui/src/library_ui.rs index d9edafa..e1a9b19 100644 --- a/ui/dr-ui/src/library_ui.rs +++ b/ui/dr-ui/src/library_ui.rs @@ -2305,7 +2305,6 @@ fn load_window(window: &AppWindow, ctl: &Rc) { // which owns the selection and queries the badge counts for the // whole window in one statement rather than one per cell. selected: false, - anchor: false, collection_count: 0, // Likewise filled by `sync_ratings` below — one query for the // window, not one per cell. diff --git a/ui/dr-ui/ui/library.slint b/ui/dr-ui/ui/library.slint index 604e57f..57df578 100644 --- a/ui/dr-ui/ui/library.slint +++ b/ui/dr-ui/ui/library.slint @@ -447,14 +447,6 @@ export struct LibraryCell { // Part of the current selection. Selection is what a drag carries, so this // has to be per-cell state rather than a single "current" index. selected: bool, - // The cell a shift-click would extend *from* — the last one plainly - // clicked. Marked because otherwise the gesture has a hidden operand: the - // user picks the far end of a run with no way to see which end the range - // is being measured from, and a range that comes out wrong gives no clue - // why. At most one cell in the library carries this, and often none of - // them is on screen — which is itself the answer to "why did that select - // so much". - anchor: bool, // How many collections this image belongs to. An image can be in many at // once, and without a cue the grid gives no hint that a photograph has // already been filed — the user re-files it, or hunts for where it went. @@ -2973,12 +2965,18 @@ export component LibraryGrid inherits Rectangle { height: parent.height - 2 * self.y; animate x, y, width, height { duration: 120ms; easing: ease-out; } - background: cell.selected ? Theme.selected : Theme.surface; + background: Theme.surface; border-radius: Theme.radius; - // Selection outranks hover: a selected cell must stay legible - // once the pointer has moved on to the collection it is being - // dragged toward. - border-width: cell.selected ? 2px : (cell-touch.has-hover ? 1px : 0px); + // Hover only. **Selection is the inset ring below**, and + // nothing out here changes when a cell is selected. + // + // It used to be a 2px border and a lifted fill, and a + // border drawn on the outside of a 6px-padded cell eats + // into the thumbnail: selecting appeared to nudge the + // photograph, which is exactly the wrong feedback for a + // gesture whose whole job is to say "this one". One + // treatment, drawn inside, in one place. + border-width: cell-touch.has-hover ? 1px : 0px; border-color: Theme.selected-ring; clip: true; @@ -3061,18 +3059,37 @@ export component LibraryGrid inherits Rectangle { } } - // The end a shift-click measures from. An inner ring rather - // than a colour of its own: it has to be legible against a - // thumbnail of any brightness and must not read as a second - // kind of selection, which a hue would. + // **Selection, and the only mark of it.** + // + // This ring used to mean something narrower — the end a + // shift-click measures from — and it was the clearest thing + // on the cell, so it read as the selection to everyone who + // had not written it. It also outlived what it described: an + // anchor survives a deselection, so a thin box sat around + // the last photograph touched with nothing selected at all, + // and there was no way to tell it apart from a cell that had + // stayed behind. + // + // So the ring is now what it already looked like. The anchor + // has no mark of its own, which costs nothing it was earning: + // the range gesture announces itself in the bar — "Tap the + // last photograph" — and does not need the user to find + // where it will measure from. + // + // Drawn *inside* the cell and 4px clear of the edge, so it + // never touches the thumbnail and never changes a single + // dimension. Selecting adds ink and moves nothing. Rectangle { - visible: cell.anchor; + visible: cell.selected; x: 4px; y: 4px; width: parent.width - 8px; height: parent.height - 8px; background: transparent; - border-width: 1px; + // Two, not one: this is now the whole of the cue, and it + // has to survive being read across forty cells at arm's + // length against a thumbnail of any brightness. + border-width: 2px; border-color: Theme.selected-ring; border-radius: Theme.radius-sm; }