# Conflicts:
#	crates/ui-egui/src/tests_panels.rs
This commit is contained in:
bflatastic committed 2026-10-13 11:04:33 +07:00
commit 94808da369
8 files changed
+84 -13

No files matched your search

+1
View File
@@ -703,6 +703,7 @@
"In memory — nothing is saved": "Im Arbeitsspeicher — nichts wird gespeichert",
"In use": "In Verwendung",
"In-memory session: these settings last until LightCraft quits.": "Sitzung im Arbeitsspeicher: Diese Einstellungen gelten bis zum Beenden von LightCraft.",
"Include selected photos": "Ausgewählte Fotos einschließen",
"Include subfolders": "Unterordner einbeziehen",
"Increase Brush Feather": "Weiche Pinselkante vergrößern",
"Increase Brush Size": "Pinselgröße vergrößern",
+1 -1
View File
@@ -1806,7 +1806,7 @@ mod tests {
}
assert_eq!(h.app.ui.dialog, None, "Return confirmed the dialog ({typed})");
};
confirm(&mut h, Dialog::NewAlbum { name: String::new(), folder: false, parent: None }, "Weddings");
confirm(&mut h, Dialog::NewAlbum { name: String::new(), folder: false, parent: None, include_selected: true }, "Weddings");
assert!(album(&h, "Weddings").is_some(), "New Album");
confirm(&mut h, Dialog::RenameAlbum { id: garden, name: "Garden".into() }, "Flowers");
assert_eq!(album(&h, "Flowers"), Some(garden), "Rename Album");
+13 -4
View File
@@ -1081,13 +1081,22 @@ pub fn run_ui_command(app: &mut LightcraftApp, id: &str, p: &Value) -> Option<Re
Ok(Value::Null)
}
"dialog.newFolder" => {
app.ui.dialog =
Some(Dialog::NewAlbum { name: p.get("name").and_then(Value::as_str).unwrap_or("").into(), folder: true, parent: parent_param(p) });
app.ui.dialog = Some(Dialog::NewAlbum {
name: p.get("name").and_then(Value::as_str).unwrap_or("").into(),
folder: true,
parent: parent_param(p),
include_selected: false,
});
Ok(Value::Null)
}
"dialog.newAlbum" => {
app.ui.dialog =
Some(Dialog::NewAlbum { name: p.get("name").and_then(Value::as_str).unwrap_or("").into(), folder: false, parent: parent_param(p) });
// {name?, parent?, includeSelected? (true)}
app.ui.dialog = Some(Dialog::NewAlbum {
name: p.get("name").and_then(Value::as_str).unwrap_or("").into(),
folder: false,
parent: parent_param(p),
include_selected: p.get("includeSelected").and_then(Value::as_bool).unwrap_or(true),
});
Ok(Value::Null)
}
"dialog.autoStack" => {
+14 -3
View File
@@ -609,7 +609,18 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) {
confirm = true;
}
}
Dialog::NewAlbum { name, .. } | Dialog::RenameAlbum { name, .. } => {
Dialog::NewAlbum { name, folder, include_selected, .. } => {
if dialog_field(ui, "field:albumName", name, "Name") {
confirm = true;
}
// as in Lightroom, the option is only there when something is selected
if !*folder && !app.session.targets(&json!({})).is_empty() {
ui.add_space(6.0);
let r = ui.checkbox(include_selected, crate::i18n::tr("Include selected photos"));
crate::widgets::register(ui.ctx(), "check:albumIncludeSelected", r.rect);
}
}
Dialog::RenameAlbum { name, .. } => {
if dialog_field(ui, "field:albumName", name, "Name") {
confirm = true;
}
@@ -1529,8 +1540,8 @@ pub fn confirm_dialog(app: &mut LightcraftApp, dlg: &Dialog) -> Result<serde_jso
}
r
}
Dialog::NewAlbum { name, folder, parent } => {
created_in(app, "album.create", json!({"name": name, "folder": folder, "addSelected": !folder, "parent": parent}))
Dialog::NewAlbum { name, folder, parent, include_selected } => {
created_in(app, "album.create", json!({"name": name, "folder": folder, "addSelected": !folder && *include_selected, "parent": parent}))
}
Dialog::RenameAlbum { id, name } => app.run("album.rename", json!({"id": id, "name": name})),
Dialog::TextPrompt { value, command, params, key, .. } => {
+2 -2
View File
@@ -533,7 +533,7 @@ fn sections_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, on: Option<SidebarS
/// The menu of Albums' +: Create Album, Smart Album, Folder…
fn new_album_menu(app: &mut LightcraftApp, ui: &mut egui::Ui) {
if ui.button(crate::i18n::tr("Create Album…")).clicked() {
app.ui.dialog = Some(crate::state::Dialog::NewAlbum { name: String::new(), folder: false, parent: None });
app.ui.dialog = Some(crate::state::Dialog::NewAlbum { name: String::new(), folder: false, parent: None, include_selected: true });
}
if ui.button(crate::i18n::tr("Create Smart Album…")).clicked() {
app.ui.dialog = Some(crate::state::Dialog::SmartRules {
@@ -547,7 +547,7 @@ fn new_album_menu(app: &mut LightcraftApp, ui: &mut egui::Ui) {
app.ui.dialog = Some(crate::state::Dialog::NewSmartAlbum { name: String::new(), parent: None });
}
if ui.button(crate::i18n::tr("Create Folder…")).clicked() {
app.ui.dialog = Some(crate::state::Dialog::NewAlbum { name: String::new(), folder: true, parent: None });
app.ui.dialog = Some(crate::state::Dialog::NewAlbum { name: String::new(), folder: true, parent: None, include_selected: false });
}
// only once the albums were put in an order by hand
if app.session.catalog.album_children_are_ordered(None) {
+4 -1
View File
@@ -768,12 +768,15 @@ pub enum Dialog {
ContactSheet {
options: lightcraft_engine::contact_sheet::Options,
},
/// `parent`: the folder to create it in (none: the top level).
/// `parent`: the folder to create it in (none: the top level). `include_selected`: the new album
/// holds the selected photos (Lightroom's "Include selected photos"; folders hold none).
NewAlbum {
name: String,
folder: bool,
#[serde(default)]
parent: Option<u64>,
#[serde(default = "yes")]
include_selected: bool,
},
RenameAlbum {
id: u64,
+48 -1
View File
@@ -2186,7 +2186,7 @@ fn right_click_add_to_album_offers_a_new_album_when_there_is_none() {
h.step();
h.step();
match &mut h.app.ui.dialog {
Some(crate::state::Dialog::NewAlbum { name, folder: false, parent: None }) => *name = "Trip".into(),
Some(crate::state::Dialog::NewAlbum { name, folder: false, parent: None, .. }) => *name = "Trip".into(),
other => panic!("New Album dialog expected, got {other:?}"),
}
h.step();
@@ -2309,4 +2309,51 @@ fn deleting_a_photo_asks_first_from_the_key_the_menu_and_the_right_click_menu()
click(&mut h, "button:contextDeletePhoto");
assert_eq!(h.app.ui.dialog, None);
assert!(deleted(&h));
/// New Album's "Include selected photos" (as in Lightroom): on by default; unticked, the new album
/// starts empty; with nothing selected the option isn't shown, and a folder never has it.
#[test]
fn new_album_can_leave_the_selected_photos_out() {
let mut h = demo([1400.0, 900.0], json!({"view": "photoGrid", "leftPanel": true}));
let has = |h: &Headless, id: &str| h.app.widgets.iter().any(|(w, _)| w == id);
let photo = lightcraft_catalog::PhotoId(h.app.session.visible_cloned()[1].0);
h.app.session.execute("library.select", &json!({"ids": [photo.0]})).unwrap();
let create = |h: &mut Headless, command: &str, name: &str| {
h.app.run(command, json!({"name": name})).unwrap();
h.step();
h.step();
};
let confirm = |h: &mut Headless| {
let r = h.request("ui.clickWidget", json!({"id": "button:dialogOk"}), T);
assert_eq!(r["ok"], true, "{r}");
h.step();
h.step();
assert!(h.app.ui.dialog.is_none(), "{:?}", h.app.ui.dialog);
};
let photos = |h: &Headless, name: &str| h.app.session.catalog.albums().find(|a| a.name == name).expect("the album was created").photos.clone();
// ticked by default: the selection goes in, as before
create(&mut h, "dialog.newAlbum", "With");
assert!(has(&h, "check:albumIncludeSelected"), "the option is offered with a selection");
confirm(&mut h);
assert_eq!(photos(&h, "With"), vec![photo]);
// unticked: an empty album
create(&mut h, "dialog.newAlbum", "Without");
let r = h.request("ui.clickWidget", json!({"id": "check:albumIncludeSelected"}), T);
assert_eq!(r["ok"], true, "{r}");
h.step();
confirm(&mut h);
assert!(photos(&h, "Without").is_empty(), "nothing was added");
// a folder never offers it
create(&mut h, "dialog.newFolder", "Folder");
assert!(!has(&h, "check:albumIncludeSelected"), "folders hold no photos");
confirm(&mut h);
// nothing selected: no option, an empty album
h.app.session.selection = lightcraft_engine::Selection { ids: vec![], active: None };
create(&mut h, "dialog.newAlbum", "Nothing");
assert!(!has(&h, "check:albumIncludeSelected"), "no selection, no option");
confirm(&mut h);
assert!(photos(&h, "Nothing").is_empty());
}
+1 -1
View File
@@ -104,7 +104,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts).
| LR-LIB-ALLPHOTOS | All photos | P0 | ✅ | `cmd:library.source`, `crates/ui-egui/src/panels/left.rs` | |
| LR-LIB-RECENT-ADDED | Recently added | P1 | ✅ | `cmd:library.source` (`recentlyAdded`), `crates/engine/src/view.rs` (`RECENT_DAYS`) | photos imported in the 30 days up to the latest import, newest import first, grouped by import day |
| LR-LIB-BYDATE | Browse by date | P1 | ✅ | `cmd:library.filter` (`date`), `crates/ui-egui/src/panels/left.rs` (`date_row`), `crates/catalog/src/query.rs` (`date_groups`) | year → month → day tree with library-wide counts; a row filters the current source by that date. Active filters show as removable chips under the grid header (`crates/ui-egui/src/panels/chips.rs`, with “+N more” and Clear all), the header counts “N of M photos” (matching of the source's total), a badge on the top-bar filter icon counts them, and an empty result says no photos match the active filters |
| LR-LIB-ALBUM | Albums | P0 | ✅ | `cmd:album.create`, `cmd:album.rename`, `cmd:album.delete`, `cmd:album.addPhotos`, `cmd:album.removePhotos`, `cmd:dialog.newAlbum`, `crates/engine/src/tests_albums_local.rs`, `crates/ui-egui/src/tests_panels.rs` | photos dragged from the grid onto an album row are added to it (LR-BEHAV-DRAGDROP); albums hold library photos (issue #684): `album.create {addSelected}`, `album.addPhotos` and `album.toggleTarget` (B) skip browsed Local photos and return `skipped` (a toast says so), a Local-only selection is an error and leaves Photo ▸ Add to Album, the grid's right-click Add to Album, a folder row's Add Selected Photos and a drop onto an album row off with the hint to Add to My Photos first (`button:contextAddToAlbum`, `button:contextAlbum:<id>`); right-click a photo ▸ Add to Album lists the albums and ends with New Album…, which creates one holding the selection — also when the library has no album yet (issue #676; `button:contextNewAlbum`) |
| LR-LIB-ALBUM | Albums | P0 | ✅ | `cmd:album.create`, `cmd:album.rename`, `cmd:album.delete`, `cmd:album.addPhotos`, `cmd:album.removePhotos`, `cmd:dialog.newAlbum`, `crates/engine/src/tests_albums_local.rs`, `crates/ui-egui/src/tests_panels.rs` | photos dragged from the grid onto an album row are added to it (LR-BEHAV-DRAGDROP); albums hold library photos (issue #684): `album.create {addSelected}`, `album.addPhotos` and `album.toggleTarget` (B) skip browsed Local photos and return `skipped` (a toast says so), a Local-only selection is an error and leaves Photo ▸ Add to Album, the grid's right-click Add to Album, a folder row's Add Selected Photos and a drop onto an album row off with the hint to Add to My Photos first (`button:contextAddToAlbum`, `button:contextAlbum:<id>`); right-click a photo ▸ Add to Album lists the albums and ends with New Album…, which creates one holding the selection — also when the library has no album yet (issue #676; `button:contextNewAlbum`); New Album's "Include selected photos" (`check:albumIncludeSelected`, on by default; `dialog.newAlbum {includeSelected}`) leaves the selection out when unticked, and, as in Lightroom, is only shown when something is selected (never for a folder) |
| LR-LIB-FOLDER | Folders of albums | P0 | ✅ | `cmd:album.create` (`folder`, `parent`), `cmd:album.move`, `cmd:album.reorder`, `cmd:album.sort`, `cmd:dialog.newAlbum` / `cmd:dialog.newFolder` / `cmd:dialog.smartAlbum` / `cmd:dialog.newSmartAlbum` (`parent`), `crates/ui-egui/src/panels/left.rs` (`albums_tree`, `folder_menu`, `disclosure_triangle`), `crates/ui-egui/src/tests_panels.rs` | Delete removes a folder and its nested albums as one undoable edit, keeps photos in the library and returns a deleted album view to All Photos (issue #501); folders nest and fold with the same disclosure triangle as Local / By Date / Folders / Keywords (`albumToggle:<id>`; the row folds them too; open by default); right-click a folder ▸ New ▸ Create Album… / Create Smart Album… / Create Smart Album from Filter… / Create Folder… creates inside it (the `dialog.new*` commands take `parent`), opening the folders down to the new row; creating is only allowed inside folders; moving: drag an album or folder row onto a folder (outlined when allowed; a closed folder opens after 0.6 s under the pointer; the "Albums" header takes an item that sits in a folder back to the top level while it is dragged), the Move to menu or `album.move` — one undo step each; not onto a plain album, itself or something inside it; drop on the top / bottom half of an album or the edge of a folder to place it there by hand (`cmd:album.reorder`, a line shows the place; ordered per folder, folders stay first, new albums go after the ordered ones; right-click ▸ Sort Contents A–Z or + ▸ Sort Albums A–Z = `cmd:album.sort`) |
| LR-LIB-SMARTALBUM | Smart albums | P1 | ✅ | `cmd:album.createSmart`, `cmd:album.setRules`, `crates/catalog/src/query.rs`, `crates/ui-egui/src/panels/filterbar.rs`, `crates/ui-egui/src/lib.rs` (`album_counts`) | saved filters (rating/flag/label/kind/edited/keyword/camera/lens/date range/text/album) or a rule set from the rule editor (match all / any / none, nested groups; see LRC-LIB-SMARTCOLL), live; sidebar counts are cached per catalog revision (and per minute while an “in the last…” rule is used), counted without building id lists |
| LR-LIB-SHARED-ALBUM | Shared albums | P2 | ⬜ | | needs a sharing service |