Merge PR #3262: LR-BEHAV-ACCESS: the photo in Detail is named with its file name

crates/ui-egui/tests/it/main.rs: all mod lines kept. docs: both sides kept; header keeps the newer date.
This commit is contained in:
aiongg
2026-10-31 02:09:11 +00:00
23 changed files with 103 additions and 21 deletions
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "Soubor {name} se nepodařilo přečíst: {e}",
"Added {n} photo{}": "Přidáno fotografií: {n}{:.0}",
"Where should the Lightroom catalog “{catalog}” go?": "Kam má jít katalog Lightroomu „{catalog}“?",
"Merge into the open library “{library}” ({count} photo{})": "Sloučit s otevřenou knihovnou „{library}“ (fotografie: {count}{:.0})"
"Merge into the open library “{library}” ({count} photo{})": "Sloučit s otevřenou knihovnou „{library}“ (fotografie: {count}{:.0})",
"Photo: {name}": "Fotografie: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "{name} konnte nicht gelesen werden: {e}",
"Added {n} photo{}": "{n} Foto{} hinzugefügt",
"Where should the Lightroom catalog “{catalog}” go?": "Wohin soll der Lightroom-Katalog „{catalog}“?",
"Merge into the open library “{library}” ({count} photo{})": "Mit der geöffneten Bibliothek „{library}“ zusammenführen ({count} Foto{})"
"Merge into the open library “{library}” ({count} photo{})": "Mit der geöffneten Bibliothek „{library}“ zusammenführen ({count} Foto{})",
"Photo: {name}": "Foto: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "No se pudo leer {name}: {e}",
"Added {n} photo{}": "{n} foto{0} añadida{0}",
"Where should the Lightroom catalog “{catalog}” go?": "¿Dónde debe ir el catálogo de Lightroom “{catalog}”?",
"Merge into the open library “{library}” ({count} photo{})": "Combinar con la biblioteca abierta “{library}” ({count} foto{})"
"Merge into the open library “{library}” ({count} photo{})": "Combinar con la biblioteca abierta “{library}” ({count} foto{})",
"Photo: {name}": "Foto: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Drop to import {n} preset{}": "Pudota tuodaksesi esiasetuksia: {n}{:.0}",
"Drop to review {n} item{} for import": "Pudota tarkistaaksesi ennen tuontia (kohteita: {n}){:.0}",
"Where should the Lightroom catalog “{catalog}” go?": "Minne Lightroom-luettelo ”{catalog}” tuodaan?",
"Merge into the open library “{library}” ({count} photo{})": "Yhdistä avoimeen kirjastoon ”{library}” (kuvia: {count}{:.0})"
"Merge into the open library “{library}” ({count} photo{})": "Yhdistä avoimeen kirjastoon ”{library}” (kuvia: {count}{:.0})",
"Photo: {name}": "Kuva: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "Impossible de lire {name} : {e}",
"Added {n} photo{}": "{n} photo{} ajoutée(s)",
"Where should the Lightroom catalog “{catalog}” go?": "Où importer le catalogue Lightroom « {catalog} » ?",
"Merge into the open library “{library}” ({count} photo{})": "Fusionner avec la bibliothèque ouverte « {library} » ({count} photo{})"
"Merge into the open library “{library}” ({count} photo{})": "Fusionner avec la bibliothèque ouverte « {library} » ({count} photo{})",
"Photo: {name}": "Photo : {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Drop to review {n} item{} for import": "Jatuhkan untuk meninjau {n} item sebelum impor{:.0}",
"Inside: {names}": "Di dalam: {names}",
"Where should the Lightroom catalog “{catalog}” go?": "Ke mana katalog Lightroom “{catalog}” diimpor?",
"Merge into the open library “{library}” ({count} photo{})": "Gabungkan ke pustaka yang terbuka “{library}” ({count} foto{:.0})"
"Merge into the open library “{library}” ({count} photo{})": "Gabungkan ke pustaka yang terbuka “{library}” ({count} foto{:.0})",
"Photo: {name}": "Foto: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "Impossibile leggere {name}: {e}",
"Added {n} photo{}": "Foto aggiunte: {n}{:.0}",
"Where should the Lightroom catalog “{catalog}” go?": "Dove importare il catalogo Lightroom “{catalog}”?",
"Merge into the open library “{library}” ({count} photo{})": "Unisci alla libreria aperta “{library}” (foto: {count}{:.0})"
"Merge into the open library “{library}” ({count} photo{})": "Unisci alla libreria aperta “{library}” (foto: {count}{:.0})",
"Photo: {name}": "Foto: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "{name} を読み取れませんでした: {e}",
"Added {n} photo{}": "{n}枚を追加しました{:.0}",
"Where should the Lightroom catalog “{catalog}” go?": "Lightroom カタログ「{catalog}」をどこに読み込みますか?",
"Merge into the open library “{library}” ({count} photo{})": "開いているライブラリ「{library}」に統合(写真{count}枚){:.0}"
"Merge into the open library “{library}” ({count} photo{})": "開いているライブラリ「{library}」に統合(写真{count}枚){:.0}",
"Photo: {name}": "写真: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "ვერ წაიკითხა {name}: {e}",
"Added {n} photo{}": "დაემატა ფოტო: {n}{:.0}",
"Where should the Lightroom catalog “{catalog}” go?": "სად გადავიტანოთ Lightroom-ის კატალოგი „{catalog}“?",
"Merge into the open library “{library}” ({count} photo{})": "გაერთიანება ღია ბიბლიოთეკასთან „{library}“ (ფოტოები: {count}){:.0}"
"Merge into the open library “{library}” ({count} photo{})": "გაერთიანება ღია ბიბლიოთეკასთან „{library}“ (ფოტოები: {count}){:.0}",
"Photo: {name}": "ფოტო: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Added {n} photo{}": "Dodano zdjęcia: {n}{:.0}",
"Inside: {names}": "Wewnątrz: {names}",
"Where should the Lightroom catalog “{catalog}” go?": "Dokąd zaimportować katalog Lightroom „{catalog}”?",
"Merge into the open library “{library}” ({count} photo{})": "Scal z otwartą biblioteką „{library}” (zdjęcia: {count}){:.0}"
"Merge into the open library “{library}” ({count} photo{})": "Scal z otwartą biblioteką „{library}” (zdjęcia: {count}){:.0}",
"Photo: {name}": "Zdjęcie: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "Não foi possível ler {name}: {e}",
"Added {n} photo{}": "{n} foto{0} adicionada{0}",
"Where should the Lightroom catalog “{catalog}” go?": "Para onde vai o catálogo do Lightroom “{catalog}”?",
"Merge into the open library “{library}” ({count} photo{})": "Mesclar na biblioteca aberta “{library}” ({count} foto{})"
"Merge into the open library “{library}” ({count} photo{})": "Mesclar na biblioteca aberta “{library}” ({count} foto{})",
"Photo: {name}": "Foto: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "Не удалось прочитать {name}: {e}",
"Added {n} photo{}": "Добавлено фото: {n}{:.0}",
"Where should the Lightroom catalog “{catalog}” go?": "Куда импортировать каталог Lightroom «{catalog}»?",
"Merge into the open library “{library}” ({count} photo{})": "Объединить с открытой библиотекой «{library}» (фото: {count}){:.0}"
"Merge into the open library “{library}” ({count} photo{})": "Объединить с открытой библиотекой «{library}» (фото: {count}){:.0}",
"Photo: {name}": "Фото: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Drop to review {n} item{} for import": "Släpp för att granska {n} objekt före import{:.0}",
"Inside: {names}": "Inuti: {names}",
"Where should the Lightroom catalog “{catalog}” go?": "Vart ska Lightroom-katalogen ”{catalog}” importeras?",
"Merge into the open library “{library}” ({count} photo{})": "Slå samman med det öppna biblioteket ”{library}” ({count} foto{:.0})"
"Merge into the open library “{library}” ({count} photo{})": "Slå samman med det öppna biblioteket ”{library}” ({count} foto{:.0})",
"Photo: {name}": "Foto: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "Не вдалося прочитати {name}: {e}",
"Added {n} photo{}": "Додано фото: {n}{:.0}",
"Where should the Lightroom catalog “{catalog}” go?": "Куди імпортувати каталог Lightroom «{catalog}»?",
"Merge into the open library “{library}” ({count} photo{})": "Об’єднати з відкритою бібліотекою «{library}» (фото: {count}){:.0}"
"Merge into the open library “{library}” ({count} photo{})": "Об’єднати з відкритою бібліотекою «{library}» (фото: {count}){:.0}",
"Photo: {name}": "Фото: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Drop to import {n} preset{}": "Thả để nhập {n} thiết lập sẵn{:.0}",
"Drop to review {n} item{} for import": "Thả để xem lại {n} mục trước khi nhập{:.0}",
"Where should the Lightroom catalog “{catalog}” go?": "Nhập danh mục Lightroom «{catalog}» vào đâu?",
"Merge into the open library “{library}” ({count} photo{})": "Gộp vào thư viện đang mở «{library}» ({count} ảnh{:.0})"
"Merge into the open library “{library}” ({count} photo{})": "Gộp vào thư viện đang mở «{library}» ({count} ảnh{:.0})",
"Photo: {name}": "Ảnh: {name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "无法读取 {name}:{e}",
"Added {n} photo{}": "已添加 {n} 张照片{:.0}",
"Where should the Lightroom catalog “{catalog}” go?": "Lightroom 目录“{catalog}”要导入到哪里?",
"Merge into the open library “{library}” ({count} photo{})": "合并到当前打开的图库“{library}”({count} 张照片){:.0}"
"Merge into the open library “{library}” ({count} photo{})": "合并到当前打开的图库“{library}”({count} 张照片){:.0}",
"Photo: {name}": "照片:{name}"
}
+2 -1
View File
@@ -308,5 +308,6 @@
"Could not read {name}: {e}": "無法讀取 {name}:{e}",
"Added {n} photo{}": "已加入 {n} 張照片{:.0}",
"Where should the Lightroom catalog “{catalog}” go?": "Lightroom 目錄「{catalog}」要匯入到哪裡?",
"Merge into the open library “{library}” ({count} photo{})": "合併到目前開啟的照片庫「{library}」({count} 張照片){:.0}"
"Merge into the open library “{library}” ({count} photo{})": "合併到目前開啟的照片庫「{library}」({count} 張照片){:.0}",
"Photo: {name}": "照片:{name}"
}
+7 -1
View File
@@ -164,7 +164,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
// tool's overlays need the frame's own geometry.
let display_aspect = visible_texture_aspect.filter(|a| !crop_tool && (a / aspect - 1.0).abs() > 0.02).unwrap_or(aspect);
let resp = ui.interact(canvas, egui::Id::unique("loupe"), Sense::click_and_drag());
crate::widgets::announce(&resp, crate::i18n::tr("Photo"));
announce_photo(app, &resp, &photo);
let mut target_rect = fit_rect(main_area, display_aspect, app.ui.zoom, native, ppp, app.ui.pan);
let mut img_rect = animated_rect(ui.ctx(), &mut app.ui.zoom_anim, target_rect);
let in_pane = |pane: Rect, image: Rect| image.translate(pane.center() - main_area.center());
@@ -356,6 +356,12 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
loupe_context_menu(app, &resp, id);
}
/// Names the loupe for screen readers as the grid names the same photo (file, rating, flag, label), not
/// just "Photo".
fn announce_photo(app: &LightcraftApp, resp: &egui::Response, photo: &lightcraft_catalog::Photo) {
crate::widgets::announce(resp, crate::i18n::tr_format!("Photo: {name}", name = super::grid::cell_description(app, photo)));
}
/// The loupe's right-click menu: zoom commands and the grid's photo menu.
fn loupe_context_menu(app: &mut LightcraftApp, resp: &egui::Response, id: PhotoId) {
crate::widgets::context_menu(resp, |ui| {
+1 -1
View File
@@ -804,7 +804,7 @@ fn cell(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, r: Rect, square
cell_interaction(app, ui, &resp, id, &photo);
}
fn cell_description(app: &LightcraftApp, photo: &lightcraft_catalog::Photo) -> String {
pub(crate) fn cell_description(app: &LightcraftApp, photo: &lightcraft_catalog::Photo) -> String {
let mut spoken = photo.file_name.clone();
if photo.rating > 0 {
spoken.push_str(&crate::i18n::tr_format!(", {} star{}", photo.rating, if photo.rating == 1 { "" } else { "s" }));
+1
View File
@@ -5,3 +5,4 @@ mod accessibility;
mod docking_workflows;
mod sidebar_names;
mod grid_names;
mod photo_name;
+53
View File
@@ -0,0 +1,53 @@
//! The photo shown in Detail has a name a screen reader can read: its file, and its rating, flag and
//! label as the grid says them, not the bare word "Photo".
use egui::accesskit::Role;
use egui::vec2;
use egui_kittest::{Harness, kittest::NodeT};
use lightcraft_ui_egui::{
LightcraftApp, Services,
state::{RightPanel, ViewMode},
};
fn detail() -> Harness<'static, LightcraftApp> {
let mut app = LightcraftApp::new(lightcraft_engine::Session::with_demo(), Services { png: None, ..Default::default() });
app.ui.view = ViewMode::Detail;
app.ui.right = RightPanel::None;
let mut h = Harness::builder().with_size(vec2(1500.0, 950.0)).build_ui_state(
|ui, app: &mut LightcraftApp| {
app.logic(&ui.ctx().clone());
app.ui(ui);
},
app,
);
h.run_steps(10);
h
}
/// The names of the photo's nodes ("Photo: <file>…"); the in-window "Photo" menu button is not the photo.
fn photo_names(h: &Harness<'_, LightcraftApp>) -> Vec<String> {
h.root()
.children_recursive()
.map(|n| n.accesskit_node())
.filter(|n| n.role() == Role::Button)
.filter_map(|n| n.label())
.filter(|l| l.starts_with("Photo: "))
.collect()
}
#[test]
fn the_photo_in_detail_is_named_with_its_file() {
let h = detail();
let app = h.state();
let file = app.session.active().and_then(|id| app.session.catalog.photo(id)).map(|p| p.file_name.clone()).expect("a photo is shown");
assert_eq!(photo_names(&h), vec![format!("Photo: {file}")]);
}
#[test]
fn the_photo_in_detail_says_its_rating_and_flag_as_the_grid_does() {
let mut h = detail();
h.state_mut().run("photo.rate", serde_json::json!({"rating": 4})).expect("rates");
h.state_mut().run("photo.flag", serde_json::json!({"flag": "pick"})).expect("flags");
h.run_steps(4);
let names = photo_names(&h);
assert!(names.len() == 1 && names[0].ends_with(", 4 stars, picked"), "{names:?}");
}
+3 -2
View File
@@ -1,6 +1,6 @@
# Lightroom parity checklist
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-28 · **Change:** minor (LR-VIEW-PHOTOGRID: the grid's empty-view message names its shortcut as this platform writes it and as it is bound; LR-BEHAV-ACCESS: the grid's date headers are named as they read; LR-BEHAV-ACCESS: the sidebar's disclosure triangles name their row; KEYC-METADATA, LRC-LIB-KEYWORDS: ⌘⌥⇧K Set Keyword Shortcut and ⇧K; KEYC-METADATA: ⌘⇧K / Ctrl+Shift+K Edit Keywords; LR-FILT-SEARCH-META: `>=` / `<=` in fielded search tokens; MENU-FILE-CLOSE, KEY-CLOSEWINDOW: the notes say Close Window quits the app, after the same check as Quit; MENU-EDIT-SELECTNONE, MENU-EDIT-PASTESELECTED: Edit lists Select Only Active Photo after Deselect All and Paste Settings from Previous after Paste Selected Settings…; on macOS no doubled separators are left where Settings… moved to the app menu; 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-28 · **Change:** minor (LR-BEHAV-ACCESS: the Detail photo is named with its file name; LR-VIEW-PHOTOGRID: the grid's empty-view message names its shortcut as this platform writes it and as it is bound; LR-BEHAV-ACCESS: the grid's date headers are named as they read; LR-BEHAV-ACCESS: the sidebar's disclosure triangles name their row; KEYC-METADATA, LRC-LIB-KEYWORDS: ⌘⌥⇧K Set Keyword Shortcut and ⇧K; KEYC-METADATA: ⌘⇧K / Ctrl+Shift+K Edit Keywords; LR-FILT-SEARCH-META: `>=` / `<=` in fielded search tokens; MENU-FILE-CLOSE, KEY-CLOSEWINDOW: the notes say Close Window quits the app, after the same check as Quit; MENU-EDIT-SELECTNONE, MENU-EDIT-PASTESELECTED: Edit lists Select Only Active Photo after Deselect All and Paste Settings from Previous after Paste Selected Settings…; on macOS no doubled separators are left where Settings… moved to the app menu; 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
@@ -479,7 +479,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts).
| LR-BEHAV-TOOLTIPS | Tooltips with shortcuts | P0 | ✅ | `crates/ui-egui/src/panels/bottombar.rs` | |
| LR-BEHAV-TEXTFIELD | Text fields behave alike | P1 | 🟡 | `crates/ui-egui/src/widgets/text_field.rs`, `crates/ui-egui/src/panels/topbar.rs`, `crates/ui-egui/src/widgets.rs`, `crates/ui-egui/src/panels/right.rs` (`meta_field`), `crates/ui-egui/src/dialogs.rs`, `crates/ui-egui/src/panels/unnamed.rs` | one shared field: right-click Cut / Copy / Paste / Select All (no Paste in the browser), Esc gives back the text from before the edit, Return / leaving / Esc reported to the caller, optional select-on-focus; agents use `ui.clipboard` (MCP `clipboard`) and `ui.inspect` → `copied`. Used by Search Photos, slider values, the Info panel's metadata fields (multi-line captions), the one-field dialogs (New / Rename Album, New Smart Album, Rename / Merge Keywords, text prompts) and the unnamed faces' name field so far; the other text fields (most dialogs, import, settings, masking, keywords …) still use egui's own and have no menu |
| LR-BEHAV-HEADLESS | Headless UI snapshots | P2 | ✅ | `apps/lightcraft-cli/src/main.rs`, `crates/ui-egui/src/headless.rs`, `docs/control-protocol.md` | `snapshot` rasterizes the UI and defaults photo development to the CPU before the first frame, without adapter discovery; scaled dimensions are validated before allocation, and UI zoom preserves the requested physical viewport; JSON-lines control scripts are supported |
| LR-BEHAV-ACCESS | Accessibility | P2 | 🟡 | `crates/ui-egui/src/widgets.rs`, `crates/ui-egui/src/widgets/text_field.rs`, `crates/ui-egui/src/panels/grid.rs`, `crates/ui-egui/tests/it/accessibility.rs` | AccessKit (VoiceOver / Narrator / AT-SPI): sliders announce control, value, range and step and take Increment / Decrement, buttons / icon buttons / dropdowns / section headers / sources their labels and state, text fields their caption, grid thumbnails file name, rating, flag and label. `tests/it/accessibility.rs` fails on an unnamed control, a slider without range or an untitled dialog in every view, Develop tool and 13 dialogs, and checks that Crop and Remove sliders are reached with Tab and that Tab walks the right panels top to bottom. Canvas tools by keyboard (`crates/ui-egui/src/canvas_keys.rs`, `crates/ui-egui/src/tests/canvas_keys.rs`): crop corners, edges and box, spot targets and sources, a New spot point (Return adds), radial / linear gradient handles and mask pins take the focus, are announced with their position and move with the arrows (Shift: further). Dialogs with one name field (New Album, New Smart Album, Save Metadata Preset, the renames) take the focus once when they open, so Tab and Shift+Tab reach their check box, Cancel and OK, and Return and Esc still confirm and cancel (`crates/ui-egui/src/tests/dialog_tab.rs`). Brush strokes and red-eye ellipses are pointer-only; not tried with a real screen reader ([audit](ui-parity.md#accessibility-audit)). The sidebar's and the keyword list's disclosure triangles say which row they open or close, as "Expand Travel" (`crates/ui-egui/tests/it/sidebar_names.rs`). A date header in the photo grid is named with the text it shows (`crates/ui-egui/tests/it/grid_names.rs`). |
| LR-BEHAV-ACCESS | Accessibility | P2 | 🟡 | `crates/ui-egui/src/widgets.rs`, `crates/ui-egui/src/widgets/text_field.rs`, `crates/ui-egui/src/panels/grid.rs`, `crates/ui-egui/tests/it/accessibility.rs` | AccessKit (VoiceOver / Narrator / AT-SPI): sliders announce control, value, range and step and take Increment / Decrement, buttons / icon buttons / dropdowns / section headers / sources their labels and state, text fields their caption, grid thumbnails file name, rating, flag and label. `tests/it/accessibility.rs` fails on an unnamed control, a slider without range or an untitled dialog in every view, Develop tool and 13 dialogs, and checks that Crop and Remove sliders are reached with Tab and that Tab walks the right panels top to bottom. Canvas tools by keyboard (`crates/ui-egui/src/canvas_keys.rs`, `crates/ui-egui/src/tests/canvas_keys.rs`): crop corners, edges and box, spot targets and sources, a New spot point (Return adds), radial / linear gradient handles and mask pins take the focus, are announced with their position and move with the arrows (Shift: further). Dialogs with one name field (New Album, New Smart Album, Save Metadata Preset, the renames) take the focus once when they open, so Tab and Shift+Tab reach their check box, Cancel and OK, and Return and Esc still confirm and cancel (`crates/ui-egui/src/tests/dialog_tab.rs`). The photo in Detail is named like its grid cell, `Photo: <file>, <rating>, <flag>, <label>` (`crates/ui-egui/src/panels/detail.rs`, `crates/ui-egui/tests/it/photo_name.rs`). Brush strokes and red-eye ellipses are pointer-only; not tried with a real screen reader ([audit](ui-parity.md#accessibility-audit)). The sidebar's and the keyword list's disclosure triangles say which row they open or close, as "Expand Travel" (`crates/ui-egui/tests/it/sidebar_names.rs`). A date header in the photo grid is named with the text it shows (`crates/ui-egui/tests/it/grid_names.rs`). |
| LR-BEHAV-LOCALE | Language options | P2 | ✅ | `cmd:app.language.english`, `cmd:app.language.simplifiedChinese`, `cmd:app.language.traditionalChinese`, `cmd:app.language.japanese`, `cmd:app.language.portuguese`, `cmd:app.language.french`, `cmd:app.language.spanish`, `cmd:app.language.german`, `cmd:app.language.russian`, `cmd:app.language.ukrainian`, `cmd:app.language.georgian`, `cmd:app.language.polish`, `cmd:app.language.czech`, `cmd:app.language.italian`, `cmd:app.language.swedish`, `cmd:app.language.finnish`, `cmd:app.language.indonesian`, `cmd:app.language.vietnamese` | Edit > Language lists every language in the table and the active one is checked; shared with Settings > General (docs/localization.md) |
| LR-BEHAV-LOCALIZE | Localisation | P2 | 🟡 | `crates/ui-egui/src/i18n.rs`, `crates/ui-egui/locales/`, `docs/localization.md`, `docs/localization-zh-hans.md`, `docs/localization-zh-hant.md`, `docs/localization-ja.md`, `docs/localization-pt-br.md`, `docs/localization-es.md`, `docs/localization-de.md`, `docs/localization-ru.md`, `docs/localization-ka.md`, `docs/localization-pl.md` | One table entry per language (code, endonym, ISO 15924 script, catalog): English, Simplified Chinese, Traditional Chinese (Taiwan), Japanese, Brazilian Portuguese, Spanish, German, Russian, French, Ukrainian, Polish, Czech, Italian, Georgian, Swedish, Finnish, Indonesian and Vietnamese UI with a persisted language preference and craft-fonts regular/bold faces per script (a variable face, as for Arabic, is drawn heavier for bold from its one file); menus, edit, crop/masks, settings, import/export and primary progress messages. Adding a language registers the locale and its language command, plus two catalogs. Stock presets/profiles, history steps, library headings, date headings and capture times are localised (user-named items stay verbatim); the Ukrainian catalog covers every existing catalog key, command/control label and rule-editor label; the German catalog reports newly added gaps; the other catalogs fall back to English for messages they lack. Technical errors remain English; release notes are translated in German and Ukrainian; craft-fonts faces for other scripts (Thai, Arabic, …) are UI fallbacks in every language, for file, folder and album names (#736); Traditional Chinese borrows the Simplified Chinese face (no TC face in craft-fonts yet), and the web build embeds the Japanese, Thai and Georgian faces in WASM while loading the Simplified Chinese face from a separate font file. |
| LR-BEHAV-LEARN | Tutorials | OOS | 🚫 | | |
@@ -969,3 +969,4 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove
| 2026-10-24 | patch | LR-BEHAV-MULTISELECT: `library.select` in `replace` mode keeps each photo once, in the order first given; status unchanged (✅) |
| 2026-10-27 | patch | LR-BEHAV-ACCESS: the sidebar's and the keyword list's disclosure triangles are named with their row ("Expand Travel"), not just "Expand" or "Collapse"; status stays 🟡 |
| 2026-10-27 | patch | LR-BEHAV-ACCESS: a date header in the photo grid is named with the text it shows ("Sunday, 20 September 2026 · 1 photo"), not its date key, for VoiceOver and Voice Control; status stays 🟡 |
| 2026-10-28 | patch | LR-BEHAV-ACCESS: the photo in Detail view is named with its file name, rating, flag and label (`Photo: <name>`) for screen readers, as the grid names it, instead of "Photo"; status stays 🟡 |
+4
View File
@@ -14,6 +14,10 @@
### The empty photo grid names Import Photos' shortcut as it is bound
- When a view is empty and the way in is File ▸ Import Photos…, the message under "No photos" used a fixed "Cmd+Shift+I" (or a translation's fixed copy of it) whatever the platform or the binding. It now shows the key as the menu bar writes it (on a Mac its glyphs, elsewhere Ctrl+Shift+I) on its own line under the sentence, follows a remapped key, and names no key when Import Photos has none. The sentence now wraps in a narrow window instead of running off its edge.
### The photo in Detail is named with its file for screen readers
- With VoiceOver, the photo in Detail view is now announced with its file name, rating, flag and colour label ("Photo:
IMG_0042.JPG, 4 stars, picked") instead of only "Photo", as the grid names the same photo.
## 2026-10-27
### The Edit menu groups Select Only Active Photo and Paste Settings from Previous with their kin