mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-29 12:37:51 -06:00
MENU-CTX-ALBUM + MENU-CTX-GRID: export / move albums from their menu, set album cover
- Album rows: Export Album… (shows and selects the album, opens Export), Export Album with Preset (background), Move to (a folder or Top Level; never into itself or a subfolder). - Photo context menu inside an album: Set as Album Cover. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
cc6c0e457d
commit
a8744c07e7
@@ -467,12 +467,16 @@ pub fn context_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) {
|
||||
}
|
||||
}
|
||||
});
|
||||
// in a (non-smart) album: take the selection out of it
|
||||
// in a (non-smart) album: take the selection out of it, or make this photo its cover
|
||||
if let lightcraft_engine::LibrarySource::Album(aid) = app.session.source
|
||||
&& app.session.catalog.album(aid).is_some_and(|a| !a.folder && !a.is_smart())
|
||||
&& ui.button("Remove from Album").clicked()
|
||||
{
|
||||
let _ = app.run("album.removePhotos", json!({"id": aid.0}));
|
||||
if ui.button("Remove from Album").clicked() {
|
||||
let _ = app.run("album.removePhotos", json!({"id": aid.0}));
|
||||
}
|
||||
if ui.button("Set as Album Cover").clicked() {
|
||||
let _ = app.run("album.setCover", json!({"id": aid.0, "photo": id.0}));
|
||||
}
|
||||
}
|
||||
if ui.button("Rename…").clicked() {
|
||||
let _ = app.run("dialog.rename", json!({}));
|
||||
|
||||
@@ -158,6 +158,45 @@ fn folder_menu(app: &mut LightcraftApp, resp: &egui::Response, a: &Album) {
|
||||
if a.is_smart() && ui.button("Update Rules from Current Filter").clicked() {
|
||||
let _ = app.run("album.setRules", json!({"id": a.id.0, "fromView": true}));
|
||||
}
|
||||
if !a.folder {
|
||||
// export: show the album, select its photos, then the dialog / a preset
|
||||
let show_all = |app: &mut LightcraftApp| {
|
||||
let _ = app.run("library.source", json!({"kind": "album", "id": a.id.0}));
|
||||
let _ = app.run("library.selectAll", json!({}));
|
||||
};
|
||||
let has_photos = app.session.catalog.album_count(a.id) > 0;
|
||||
if ui.add_enabled(has_photos, egui::Button::new("Export Album…")).clicked() {
|
||||
show_all(app);
|
||||
let _ = app.run("dialog.export", json!({}));
|
||||
}
|
||||
ui.add_enabled_ui(has_photos, |ui| {
|
||||
ui.menu_button("Export Album with Preset", |ui| {
|
||||
for (p, _) in app.session.all_export_presets() {
|
||||
if ui.button(&p.name).clicked() {
|
||||
show_all(app);
|
||||
if let Err(e) = app.run("app.export", json!({"preset": p.name, "background": true})) {
|
||||
app.toast(ui.ctx(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
ui.separator();
|
||||
}
|
||||
// move into another folder (not into itself or one of its own subfolders)
|
||||
let mut folders: Vec<(u64, String)> =
|
||||
app.session.catalog.albums().filter(|f| f.folder && !is_within(app, f.id, a.id)).map(|f| (f.id.0, f.name.clone())).collect();
|
||||
folders.sort_by_key(|(_, n)| n.to_lowercase());
|
||||
ui.menu_button("Move to", |ui| {
|
||||
if ui.add_enabled(a.parent.is_some(), egui::Button::new("Top Level")).clicked() {
|
||||
let _ = app.run("album.move", json!({"id": a.id.0, "parent": null}));
|
||||
}
|
||||
for (fid, name) in &folders {
|
||||
if ui.add_enabled(a.parent.map(|p| p.0) != Some(*fid), egui::Button::new(name)).clicked() {
|
||||
let _ = app.run("album.move", json!({"id": a.id.0, "parent": fid}));
|
||||
}
|
||||
}
|
||||
});
|
||||
if ui.button("Rename…").clicked() {
|
||||
app.ui.dialog = Some(crate::state::Dialog::RenameAlbum { id: a.id.0, name: a.name.clone() });
|
||||
}
|
||||
@@ -167,6 +206,23 @@ fn folder_menu(app: &mut LightcraftApp, resp: &egui::Response, a: &Album) {
|
||||
});
|
||||
}
|
||||
|
||||
/// Whether `id` is `ancestor` or lies inside it.
|
||||
fn is_within(app: &LightcraftApp, id: lightcraft_catalog::AlbumId, ancestor: lightcraft_catalog::AlbumId) -> bool {
|
||||
let mut cur = Some(id);
|
||||
let mut guard = 0;
|
||||
while let Some(c) = cur {
|
||||
if c == ancestor {
|
||||
return true;
|
||||
}
|
||||
cur = app.session.catalog.album(c).and_then(|x| x.parent);
|
||||
guard += 1;
|
||||
if guard > 64 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// "Keywords": the library's keyword tree with photo counts (`a|b|c` keywords nest). A click
|
||||
/// filters the grid by the keyword (children included), the triangle opens a level, and the
|
||||
/// context menu renames, merges or deletes the keyword across the library.
|
||||
|
||||
+4
-4
@@ -45,10 +45,10 @@ and prints the summary; `cargo xtask parity --write` refreshes the summary table
|
||||
| V. Preferences (PREF) | 4 | 1 | 3 | 3 | 1/1 (100%) | 3/4 (75%) |
|
||||
| W. Cloud & AI infrastructure (CLOUD / AI) | 0 | 1 | 1 | 2 | — | — |
|
||||
| X. Cross-cutting behaviours (BEHAV) | 11 | 2 | 4 | 1 | 8/8 (100%) | 3/5 (60%) |
|
||||
| Y. Menus | 57 | 15 | 13 | 8 | 42/47 (89%) | 13/23 (57%) |
|
||||
| Y. Menus | 59 | 13 | 13 | 8 | 44/47 (94%) | 13/23 (57%) |
|
||||
| Z. Keyboard shortcuts (desktop) | 56 | 13 | 11 | 1 | 47/52 (90%) | 9/23 (39%) |
|
||||
| Lightroom Classic extras | 4 | 20 | 55 | 9 | — | 3/21 (14%) |
|
||||
| **Total** | 267 | 77 | 156 | 37 | 185/198 (93%) | 72/144 (50%) |
|
||||
| **Total** | 269 | 75 | 156 | 37 | 187/198 (94%) | 72/144 (50%) |
|
||||
<!-- /parity:summary -->
|
||||
|
||||
## Top gaps
|
||||
@@ -579,9 +579,9 @@ visible menu bar yet: the menu model is only exposed through the control channel
|
||||
| MENU-HELP-SHORTCUTS | Keyboard shortcuts | P1 | ✅ | `cmd:app.shortcuts` | |
|
||||
| MENU-HELP-FEEDBACK | Send feedback | P2 | ⬜ | | |
|
||||
| MENU-HELP-SYSINFO | System info | P2 | ⬜ | `cmd:library.info` | library info only |
|
||||
| MENU-CTX-GRID | Photo context menu | P0 | 🟡 | `crates/ui-egui/src/panels/grid.rs` (`context_menu`) | rate, flag, label, add to / remove from album, rename, virtual copy, version, stack, copy / paste / paste selected, reset, merge, rotate, show in Finder, export / export with preset, delete; no set-as-album-cover |
|
||||
| MENU-CTX-GRID | Photo context menu | P0 | ✅ | `crates/ui-egui/src/panels/grid.rs` (`context_menu`) | rate, flag, label, add to / remove from album, rename, virtual copy, version, stack, copy / paste / paste selected, reset, merge, rotate, show in Finder, export / export with preset, set as album cover (in an album), delete |
|
||||
| MENU-CTX-DETAIL | Loupe context menu | P1 | 🟡 | `crates/ui-egui/src/panels/detail.rs` | same as grid; no zoom submenu |
|
||||
| MENU-CTX-ALBUM | Album / folder row menu | P0 | 🟡 | `crates/ui-egui/src/panels/left.rs` | add selected, rename, delete; no move-to, export album |
|
||||
| MENU-CTX-ALBUM | Album / folder row menu | P0 | ✅ | `crates/ui-egui/src/panels/left.rs` (`folder_menu`), `cmd:album.move`, `cmd:dialog.export` | add selected, export album (dialog / preset), move to a folder or the top level, rename, delete; smart albums: update rules |
|
||||
| MENU-CTX-MASK | Mask / component menu | P0 | 🟡 | `crates/ui-egui/src/panels/masking.rs` (`mask_menu`) | mask rows: duplicate (and invert), invert, show/hide, move, rename, delete; components: add/subtract only (no per-component menu) |
|
||||
| MENU-CTX-PRESET | Preset menu | P1 | 🟡 | `crates/ui-egui/src/panels/presets.rs` | favourite, delete, export group; no rename/update/move |
|
||||
| MENU-CTX-PROFILE | Profile favourites | P2 | ⬜ | | |
|
||||
|
||||
Reference in New Issue
Block a user