diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 6b5033e0..f45ce7a9 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -51,6 +51,8 @@ mod tests_filmstrip; #[cfg(test)] mod tests_grid; #[cfg(test)] +mod tests_info_dialogs; +#[cfg(test)] mod tests_keymap; #[cfg(test)] mod tests_keyword_files; diff --git a/crates/ui-egui/src/panels/dialogs.rs b/crates/ui-egui/src/panels/dialogs.rs index aa287c95..11593f27 100644 --- a/crates/ui-egui/src/panels/dialogs.rs +++ b/crates/ui-egui/src/panels/dialogs.rs @@ -948,9 +948,14 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { } ui.add_space(4.0); ui.horizontal(|ui| { - // a model file LightCraft cannot use has nothing to confirm + // a model file LightCraft cannot use has nothing to confirm, nor has a dialog that + // only shows something (one Close, no Cancel / OK) let unusable_model = matches!(&dlg, Dialog::DenoiseModel { info, .. } | Dialog::FaceModel { info, .. } if info["kind"] == "unsupported"); - let informational = unusable_model || matches!(dlg, Dialog::About | Dialog::Shortcuts | Dialog::Settings { .. }); + let informational = unusable_model + || matches!( + dlg, + Dialog::About | Dialog::Shortcuts | Dialog::Settings { .. } | Dialog::SystemInfo { .. } | Dialog::AllMetadata { .. } | Dialog::WhatsNew + ); let sam = &app.session.segmenter; let (sam_installed, sam_running, sam_failed) = (sam.installed(), sam.download_status().running, sam.download_status().error.is_some()); // no download location in this build: nothing to offer but the manual install diff --git a/crates/ui-egui/src/tests_info_dialogs.rs b/crates/ui-egui/src/tests_info_dialogs.rs new file mode 100644 index 00000000..7fd07384 --- /dev/null +++ b/crates/ui-egui/src/tests_info_dialogs.rs @@ -0,0 +1,50 @@ +//! #782: dialogs that only show something (System Info, All Metadata, What's New) end with one +//! Close, like About, instead of a Cancel / OK pair that both just close them. + +use crate::headless::Headless; +use crate::{LightcraftApp, Services}; +use serde_json::json; +use std::time::Duration; + +const T: Duration = Duration::from_secs(20); +const SETTLE: Duration = Duration::from_secs(60); + +fn has(h: &Headless, id: &str) -> bool { + h.app.widgets.iter().any(|(w, _)| w == id) +} + +/// Open the dialog through its menu item; let the new window size itself before reading it. +fn open(h: &mut Headless, id: &str) { + let r = h.request("ui.menu.invoke", json!({"id": id}), T); + assert_eq!(r["ok"], true, "{id}: {r}"); + assert!(h.app.ui.dialog.is_some(), "{id}: no dialog"); + h.step(); + h.step(); +} + +#[test] +fn information_dialogs_end_with_one_close() { + let app = LightcraftApp::new(lightcraft_engine::Session::with_demo(), Services { png: None, ..Default::default() }); + let mut h = Headless::new(app, [1300.0, 900.0], 1.0); + h.settle(SETTLE); + for id in ["app.systemInfo", "dialog.allMetadata", "app.whatsNew"] { + open(&mut h, id); + assert!(!has(&h, "button:dialogCancel"), "{id}: a Cancel button, though there is nothing to confirm"); + assert!(has(&h, "button:dialogOk"), "{id}: no Close button"); + let close = h.app.widgets.iter().filter(|(w, _)| w == "button:dialogOk").count(); + assert_eq!(close, 1, "{id}: one footer button"); + let text = h.painted_text(); + assert!(text.iter().any(|t| t == "Close"), "{id}: the button reads Close: {text:?}"); + assert!(!text.iter().any(|t| t == "Cancel" || t == "OK"), "{id}: no Cancel / OK: {text:?}"); + let r = h.request("ui.clickWidget", json!({"id": "button:dialogOk"}), T); + assert_eq!(r["ok"], true, "{id}: {r}"); + h.step(); + assert!(h.app.ui.dialog.is_none(), "{id}: Close closes the dialog"); + // Esc still closes it too + open(&mut h, id); + let r = h.request("ui.key", json!({"key": "Escape"}), T); + assert_eq!(r["ok"], true, "{id}: {r}"); + h.step(); + assert!(h.app.ui.dialog.is_none(), "{id}: Esc closes the dialog"); + } +} diff --git a/docs/parity.md b/docs/parity.md index d0bdc35c..7a407fc1 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -631,7 +631,7 @@ 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 | ✅ | `cmd:app.feedback` | opens a new issue on the project's GitHub | | MENU-HELP-LOGFOLDER | Open log folder | P2 | ✅ | `cmd:app.openLogFolder` | an extra (the reference app has no such item): Help ▸ Open Log Folder reveals `logs/lightcraft.log` in the system file manager (Finder, Explorer, or the folder on Linux); off on the web and in `--memory` sessions, which keep no log (issue #260) | -| MENU-HELP-SYSINFO | System info | P2 | ✅ | `cmd:app.systemInfo`, `cmd:library.info` | Help ▸ System Info…: version, OS, CPU threads, GPU, memory budget, preview size, library, timings; Copy to Clipboard; JSON for agents (`open: false`) | +| MENU-HELP-SYSINFO | System info | P2 | ✅ | `cmd:app.systemInfo`, `cmd:library.info` | Help ▸ System Info…: version, OS, CPU threads, GPU, memory budget, preview size, library, timings; Copy to Clipboard; one Close button, like About (#782); JSON for agents (`open: false`) | | 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 (restore / delete permanently in Recently Deleted) | | MENU-CTX-DETAIL | Loupe context menu | P1 | ✅ | `crates/ui-egui/src/panels/detail.rs` | Zoom submenu (fit, 100%, in, out), then the photo menu | | 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), New ▸ Create Album / Smart Album / Smart Album from Filter / Folder (on a folder: made inside it), move to a folder or the top level, rename, delete; smart albums: update rules | diff --git a/docs/whats-new.md b/docs/whats-new.md index fb1fd316..812a06df 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -95,6 +95,10 @@ Selected Settings). Ctrl+V only reaches LightCraft while the system clipboard holds text; otherwise use Edit ▸ Paste Edit Settings. +### Dialogs +- System Info, All Metadata and What's New end with one Close button, like About, instead of Cancel and OK + (issue #782). + ### Synchronize Folder - Right-click a folder (or a disk other than the startup disk) in the sidebar's Folders section ▸ Synchronize Folder…. The folder and the folders inside it are scanned in the background for photos added on disk, photos whose