LR-BEHAV-DRAGDROP: only the primary button drags photos out of the grid and drops them

A right-button drag over a grid cell picked the photos up and, on an unselected photo, replaced the selection with it. A right-button release while photos were dragged dropped them on an album, folder or keyword row, or ended the drag. The drag now starts, ends and drops on the primary button only.
This commit is contained in:
Nathaniel Ledford
2026-10-27 08:28:23 -04:00
parent 662df66f1c
commit 3c8cc5d665
8 changed files with 152 additions and 8 deletions
+4 -3
View File
@@ -943,8 +943,9 @@ fn cell(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, r: Rect, square
let _ = app.run("library.select", json!({"ids": [id.0]}));
let _ = app.run("view.detail", json!({}));
}
// drag the selection (this photo joins it, or replaces it when it wasn't selected)
if resp.drag_started() {
// drag the selection (this photo joins it, or replaces it when it wasn't selected); only the
// primary button drags, so a right-button drag leaves the selection alone
if resp.drag_started_by(egui::PointerButton::Primary) {
if !app.session.selection.contains(id) {
let _ = app.run("library.select", json!({"ids": [id.0]}));
}
@@ -1077,7 +1078,7 @@ pub fn drag_hint(ctx: &egui::Context, text: String) {
pub fn drag_feedback(app: &mut LightcraftApp, ctx: &egui::Context) {
let Some(ids) = &app.ui.dragging_photos else { return };
let (released, down, pos) = ctx.input(|i| (i.pointer.any_released(), i.pointer.any_down(), i.pointer.latest_pos()));
let (released, down, pos) = ctx.input(|i| (i.pointer.primary_released(), i.pointer.primary_down(), i.pointer.latest_pos()));
if released || !down {
app.ui.dragging_photos = None;
return;
+3 -1
View File
@@ -307,7 +307,9 @@ fn inside_said(inside: &[String]) -> Option<String> {
/// do something (a keyword never goes inside itself), and a release there does it: nests the
/// keyword inside this one, or gives this keyword to the photos.
fn drop_target(app: &mut LightcraftApp, ui: &mut egui::Ui, rect: Rect, path: &str) {
let (pointer, released) = ui.input(|i| (i.pointer.latest_pos(), i.pointer.any_released()));
// a dragged keyword drops on any button's release (it began with any button); photos only on the primary's
let (pointer, released) =
ui.input(|i| (i.pointer.latest_pos(), i.pointer.primary_released() || (app.ui.dragging_keyword.is_some() && i.pointer.any_released())));
if !shown_under(ui, rect, pointer) {
return;
}
+1 -1
View File
@@ -378,7 +378,7 @@ fn drop_target(app: &mut LightcraftApp, ui: &mut egui::Ui, resp: &egui::Response
if !over {
return;
}
let released = ui.input(|i| i.pointer.any_released());
let released = ui.input(|i| i.pointer.primary_released());
// albums hold library photos (issue #684): Local ones can't be dropped in; the row doesn't
// light up, and a release says why
let photos: Vec<lightcraft_catalog::PhotoId> = ids.iter().map(|id| lightcraft_catalog::PhotoId(*id)).collect();
+1 -1
View File
@@ -393,7 +393,7 @@ fn folder_drop_target(app: &mut LightcraftApp, ui: &mut egui::Ui, resp: &egui::R
};
let all_here = !photos.is_empty() && photos.iter().all(|p| here(p));
let count = photos.len();
let released = ui.input(|i| i.pointer.any_released());
let released = ui.input(|i| i.pointer.primary_released());
let hint = match (&refused, all_here) {
(Some(_), _) => crate::i18n::tr("Can't be moved here").to_string(),
(None, true) => crate::i18n::tr_format!("Already in “{}”", name),
+1
View File
@@ -29,6 +29,7 @@ mod focus_point;
mod folder_drop;
mod grading;
mod grid;
mod grid_drag;
mod guided_guides;
mod histogram_drag;
mod info_dialogs;
+134
View File
@@ -0,0 +1,134 @@
//! Only the primary button drags photos out of the grid and drops them: a right-button drag over a
//! cell leaves the selection alone and carries nothing, and a right-button release while photos
//! are being dragged neither drops them nor ends the drag.
use std::time::Duration;
use egui::{Pos2, Rect};
use lightcraft_catalog::AlbumId;
use serde_json::json;
use crate::headless::Headless;
use crate::{LightcraftApp, Services};
const T: Duration = Duration::from_secs(20);
/// The demo library in the grid, the Albums section on the left.
fn app() -> Headless {
let session = lightcraft_engine::Session::with_demo();
let mut h = Headless::new(LightcraftApp::new(session, Services { png: None, ..Default::default() }), [1300.0, 1000.0], 1.0);
let r = h.request("ui.set", json!({"view": "photoGrid", "leftPanel": true, "sidebar": ["albums"]}), T);
assert_eq!(r["ok"], true, "{r}");
for _ in 0..6 {
h.step();
}
h
}
fn widget(h: &Headless, id: &str) -> Rect {
h.app.widgets.iter().find(|(w, _)| w == id).map(|(_, r)| *r).unwrap_or_else(|| panic!("no widget {id}"))
}
fn button(h: &mut Headless, at: Pos2, button: egui::PointerButton, pressed: bool) {
h.app.synthetic.push(egui::Event::PointerMoved(at));
h.step();
h.app.synthetic.push(egui::Event::PointerButton { pos: at, button, pressed, modifiers: egui::Modifiers::NONE });
h.step();
}
fn glide(h: &mut Headless, from: Pos2, to: Pos2) {
for i in 1..=8 {
h.app.synthetic.push(egui::Event::PointerMoved(from + (to - from) * (i as f32 / 8.0)));
h.step();
}
}
fn selected(h: &Headless) -> Vec<u64> {
let mut v: Vec<u64> = h.app.session.selection.ids.iter().map(|p| p.0).collect();
v.sort_unstable();
v
}
/// Three photos drawn at the top of the grid, in grid order.
fn three(h: &mut Headless) -> [u64; 3] {
let v = h.app.session.visible_cloned();
[v[0].0, v[1].0, v[2].0]
}
fn select(h: &mut Headless, ids: &[u64]) {
h.app.run("library.select", json!({"ids": ids})).unwrap();
h.settle(Duration::from_secs(60));
}
#[test]
fn a_right_button_drag_on_a_grid_cell_starts_no_photo_drag() {
let mut h = app();
let [a, b, c] = three(&mut h);
select(&mut h, &[a, b]);
let from = widget(&h, &format!("thumb:{c}")).center();
button(&mut h, from, egui::PointerButton::Secondary, true);
glide(&mut h, from, from + egui::vec2(60.0, 200.0));
assert_eq!(h.app.ui.dragging_photos, None, "no photo drag from the secondary button");
let mut both = vec![a, b];
both.sort_unstable();
assert_eq!(selected(&h), both, "the selection stays");
}
#[test]
fn a_left_button_drag_on_an_unselected_grid_cell_still_carries_just_that_photo() {
let mut h = app();
let [a, b, c] = three(&mut h);
select(&mut h, &[a, b]);
let from = widget(&h, &format!("thumb:{c}")).center();
button(&mut h, from, egui::PointerButton::Primary, true);
glide(&mut h, from, from + egui::vec2(60.0, 200.0));
assert_eq!(h.app.ui.dragging_photos, Some(vec![c]));
assert_eq!(selected(&h), vec![c]);
}
#[test]
fn a_right_button_release_over_an_album_row_neither_drops_nor_ends_the_photo_drag() {
let mut h = app();
let album = h.app.run("album.create", json!({"name": "Grid Target", "addSelected": false})).unwrap()["id"].as_u64().unwrap();
h.step();
h.step();
let [a, ..] = three(&mut h);
select(&mut h, &[a]);
let row = widget(&h, &format!("source:album:{album}")).center();
let from = widget(&h, &format!("thumb:{a}")).center();
button(&mut h, from, egui::PointerButton::Primary, true);
glide(&mut h, from, row);
assert!(h.app.ui.dragging_photos.is_some(), "the drag is on");
button(&mut h, row, egui::PointerButton::Secondary, true);
button(&mut h, row, egui::PointerButton::Secondary, false);
h.step();
assert!(h.app.session.catalog.album(AlbumId(album)).unwrap().photos.is_empty(), "a right-button release is not a drop");
assert!(h.app.ui.dragging_photos.is_some(), "and the drag goes on");
button(&mut h, row, egui::PointerButton::Primary, false);
h.step();
assert_eq!(h.app.session.catalog.album(AlbumId(album)).unwrap().photos.len(), 1, "the left-button release drops");
assert!(h.app.ui.dragging_photos.is_none());
}
#[test]
fn a_right_button_release_over_a_keyword_row_does_not_tag_the_dragged_photos() {
let mut h = app();
let r = h.request("ui.set", json!({"right": "keywords"}), T);
assert_eq!(r["ok"], true, "{r}");
for _ in 0..6 {
h.step();
}
let [a, ..] = three(&mut h);
select(&mut h, &[a]);
let before = h.app.session.catalog.photo(lightcraft_catalog::PhotoId(a)).unwrap().meta.keywords.clone();
let row = widget(&h, "keywordRow:mountains").center();
let from = widget(&h, &format!("thumb:{a}")).center();
button(&mut h, from, egui::PointerButton::Primary, true);
glide(&mut h, from, row);
assert!(h.app.ui.dragging_photos.is_some(), "the drag is on");
button(&mut h, row, egui::PointerButton::Secondary, true);
button(&mut h, row, egui::PointerButton::Secondary, false);
h.step();
assert_eq!(h.app.session.catalog.photo(lightcraft_catalog::PhotoId(a)).unwrap().meta.keywords, before, "a right-button release is not a drop");
assert!(h.app.ui.dragging_photos.is_some(), "and the drag goes on");
}
+3 -2
View File
@@ -1,6 +1,6 @@
# Lightroom parity checklist
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** minor (LR-BEHAV-TITLEBAR: a double-click follows the system setting, and a panel over the bar keeps the window still; KEYC-METADATA, LRC-LIB-KEYWORDS: ⌥0 / ⌥⇧0 go to the next / previous keyword set; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings; KEYC-METADATA: ⌘K / Ctrl+K Add Keywords; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** minor (LR-BEHAV-DRAGDROP: only the primary button drags photos out of the grid and drops them; LR-BEHAV-TITLEBAR: a double-click follows the system setting, and a panel over the bar keeps the window still; KEYC-METADATA, LRC-LIB-KEYWORDS: ⌥0 / ⌥⇧0 go to the next / previous keyword set; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings; KEYC-METADATA: ⌘K / Ctrl+K Add Keywords; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
The measured, row-by-row checklist: one row per Lightroom feature, menu item and shortcut, what we
have and what is missing. It answers *does the feature exist?* (feature breadth). Whether it is good
@@ -466,7 +466,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts).
| LR-BEHAV-WEB-SAFETY | Library safety in the browser build | P1 | 🟡 | `cmd:file.backupLibrary`, `cmd:file.restoreLibrary`, `apps/lightcraft-web/src/safety.rs`, `apps/lightcraft-web/src/backup.rs`, `apps/lightcraft-web/src/files.rs` | web only (experimental): Back Up Library (zip of the catalog and every stored original) and a non-destructive Restore; failed saves show as unsaved and are retried; a photo that can't be stored isn't added; one tab per library (Web Locks); notices for storage that isn't persistent and for a library that can't open; `?reset` asks first; a panic shows a message. Missing: `?safe` start, deleting originals of removed photos, zip64 (backups > 4 GB) |
| LR-BEHAV-GPU | GPU acceleration | P0 | ✅ | `crates/gpu/src/render.rs`, `cmd:app.gpu`, `docs/gpu-pipeline.md`, `crates/ui-egui/src/panels/notices.rs` | CPU fallback per render on device limits, out of memory, device errors / loss, incomplete or black results (`perf.gpuReason`, `perf.gpuFallback`; each render reports its own reason — `lightcraft_gpu::try_render`, `gpuFallback` per exported file — issue #675); short submissions for slow iGPUs; backend choice (DX12 only on Windows, `LIGHTCRAFT_GPU_BACKEND` / `WGPU_BACKEND`), device created after the window opens, crash sentinel (issue #136); a GPU lost while LightCraft runs (driver reset) stops it with that message and turns GPU rendering off for the next start, which says why (issue #1337; the window's renderer can't survive the lost device); quitting waits (up to 2 s) for renders in flight and starts no new GPU work, so none is inside the driver when the process exits (`crates/gpu/src/exit.rs`, issue #620; seen crashing only on Linux + NVIDIA, other drivers unverified); with GPU rendering on and no adapter found at all (e.g. no Vulkan loader on NixOS outside `nix develop`), the desktop app says once per session that it renders on the CPU and points to Help ▸ System Info (`gpu_notice`; not when the GPU is turned off or only a software adapter exists); no tiling beyond the buffer limit (CPU) |
| LR-BEHAV-RENDER-FIDELITY | Rendering matches Lightroom | P1 | 🟡 | `crates/pipeline/src/lib.rs`, `crates/pipeline/src/tone.rs` | every slider exists and works, but the character of the result (tone curve shape, highlight recovery, texture / clarity / dehaze, noise reduction, sharpening, default look) is tuned by eye; there is no systematic side-by-side comparison against Lightroom on the same CC0 raws. Needs a fidelity suite: Lightroom reference renders kept only in the local `plan/` (never committed), compared per slider and preset with a perceptual metric. Tuning that changes how existing settings render ships as a new process version (LRC-DEV-PROCESS, `docs/process-versions.md`) |
| LR-BEHAV-DRAGDROP | Drag and drop | P1 | ✅ | `crates/ui-egui/src/lib.rs`, `crates/ui-egui/src/panels/grid.rs` (`drag_feedback`), `crates/ui-egui/src/panels/left.rs` (`drop_target`, `album_drag_over`, `top_level_drop_target`, `drag_auto_scroll`) | files → app (a drop-target card while they hover, then the import review); grid photos → an album row (adds the selection, with a count badge while dragging); sidebar album / folder rows → a folder (moves it; its name follows the pointer); a drag of albums or grid photos near the top or bottom edge of the sidebar scrolls it (faster nearer the edge, only while the pointer is over the sidebar) |
| LR-BEHAV-DRAGDROP | Drag and drop | P1 | ✅ | `crates/ui-egui/src/lib.rs`, `crates/ui-egui/src/panels/grid.rs` (`drag_feedback`), `crates/ui-egui/src/panels/left.rs` (`drop_target`, `album_drag_over`, `top_level_drop_target`, `drag_auto_scroll`) | files → app (a drop-target card while they hover, then the import review); grid photos → an album row (adds the selection, with a count badge while dragging); sidebar album / folder rows → a folder (moves it; its name follows the pointer); a drag of albums or grid photos near the top or bottom edge of the sidebar scrolls it (faster nearer the edge, only while the pointer is over the sidebar); only the primary button drags photos and drops them: a right-button drag over a grid cell neither picks photos up nor changes the selection, and a right-button release while photos are dragged neither drops them nor ends the drag |
| LR-BEHAV-TOAST | Toast notifications | P1 | ✅ | `crates/ui-egui/src/panels.rs` | |
| LR-BEHAV-DIALOGS | Dialogs fit the window | P1 | ✅ | `crates/ui-egui/src/dialogs.rs` (`dialog_body`, `list_height`), `crates/ui-egui/src/tests/dialog_fit.rs`, `crates/ui-egui/src/tests/dialogs.rs` (`a_click_outside_closes_the_dialog_when_settings_allow_it`) | a dialog taller than the window (Export on a 1366×768 screen, issue #781) never runs off it: its options scroll while the title bar and the Cancel / OK row stay on screen; lists that scroll on their own (What's New, All Metadata, shortcuts, smart-album rules, credits) get shorter first; dialogs that fit look as before; Import keeps its own resizable, scrolling window; Settings → Dialogs → Close a dialog by clicking outside it (off by default, so a stray click never throws away a dialog's choices): a click pressed and released on the dimmed backdrop closes the dialog like Esc and doesn't reach the panels below, while a drag that ends outside (a slider pulled past the edge) or a click that closes a dropdown keeps it; the import review never closes this way (`clicked_backdrop`) |
| LR-BEHAV-SIDEBAR-COLLAPSE | Collapsible left-sidebar sections | P2 | ✅ | `crates/ui-egui/src/panels/left.rs` (`section`, `section_header`), `crates/ui-egui/src/sidebar.rs` (`SidebarLayout`), `crates/ui-egui/src/tests/panels.rs` | click the Albums, Local, By Date, Folders or Keywords header (or drive `ui.clickWidget` `sidebarSection:<albums\|local\|byDate\|folders\|keywords>`) to fold or unfold the section; a chevron before the title shows the state; the choice is kept across restarts (`sidebar` in the UI state; the `collapsedSidebar` list of older settings files is still read). the click itself is UI-only, like the By Date / Keywords disclosure triangles, and `cmd:view.sidebarSection` `{section, collapsed}` folds and unfolds for agents; photos dropped on a folded Albums section have no target |
@@ -959,3 +959,4 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove
| 2026-10-23 | patch | LR-BEHAV-ACCESS: the name field of New Album, New Smart Album, Rename and the other one-field dialogs takes the keyboard focus once when the dialog opens instead of every frame, so Tab and Shift+Tab reach the check box, Cancel and OK; status unchanged (🟡) |
| 2026-10-22 | minor | LR-CROP-AUTO: Auto straighten turns by at most 10° and only on at least 3 agreeing lines, and says when it finds nothing to level by; measured on 68 photos straightened by hand and 300 left as shot, unwanted turns of more than 1° fell from 79 to 19 of the 300. Downgraded ✅ → 🟡: it still leaves most tilted photos alone |
| 2026-10-23 | patch | LRC-LIB-FILTERBAR: showing an album (`library.source`) while the filter holds an album drops the filter's album, as showing a library folder already dropped its folder filter; before, the old album stayed in the filter unseen (the filter bar has no album chip) and narrowed the next source such as All Photos; the row's note says so; status unchanged (✅) |
| 2026-10-23 | patch | LR-BEHAV-DRAGDROP: only the primary button drags photos out of the grid and drops them onto sidebar rows; a right-button drag over a grid cell no longer replaces the selection and carries photos, and a right-button release during a drag no longer drops them; status unchanged (✅) |
+5
View File
@@ -21,6 +21,11 @@
## 2026-10-23
### Right-button drags no longer pick up photos in the grid
- Dragging with the right mouse button over a photo in the grid used to pick up the photos and, on a photo that wasn't
selected, replace the selection with it. Now only the left button drags photos, and a right-button release while you
are dragging photos no longer drops them on an album, folder or keyword. Right-click still opens the photo menu.
### Tab leaves the name field in New Album, New Smart Album and the name prompts
- In New Album, New Smart Album, Save Metadata Preset and the other dialogs with one name field, the field has the
keyboard focus when the dialog opens, and Tab now moves on to the check box, Cancel and OK (Shift+Tab goes back).