LR-BEHAV-DIALOGS: the confirm label, the slideshow export body and the contact sheet body stay under the complexity limit

main denies cognitive complexity above 30: ok_label (now with the Export count), slideshow_export_body and contact_sheet_body each split out one part (the fixed labels, the summary line and the size rows). No behavior change.
This commit is contained in:
Nathaniel Ledford
2026-10-28 09:52:17 -04:00
parent 2bacaaa717
commit 4c1e996a64
2 changed files with 67 additions and 46 deletions
+25 -20
View File
@@ -329,16 +329,24 @@ fn cancel_label(dlg: &Dialog, sam: &SamState) -> &'static str {
/// The label of a dialog's confirm button (empty: it has none).
fn ok_label(app: &LightcraftApp, dlg: &Dialog, informational: bool, sam: &SamState) -> String {
let label = match dlg {
Dialog::Import { opts } => {
let n = opts.selected_paths().len();
let verb = if opts.copy && opts.move_files { "Move" } else { "Import" };
return crate::i18n::tr_format!("{verb} {n} Photo{}", if n == 1 { "" } else { "s" }, verb = crate::i18n::tr(verb), n = n);
}
Dialog::Export { .. } => {
let n = app.session.selection.ids.len().max(1);
return crate::i18n::tr_format!("{verb} {n} Photo{}", if n == 1 { "" } else { "s" }, verb = crate::i18n::tr("Export"), n = n);
}
match dlg {
Dialog::Import { opts } => photos_ok_label(if opts.copy && opts.move_files { "Move" } else { "Import" }, opts.selected_paths().len()),
Dialog::Export { .. } => photos_ok_label("Export", app.session.selection.ids.len().max(1)),
Dialog::SamModel { .. } => sam_ok_label(dlg, sam).to_string(),
_ => fixed_ok_label(app, dlg, informational).to_string(),
}
}
/// "Import 3 Photos", "Export 1 Photo": a verb and how many photos it acts on.
fn photos_ok_label(verb: &str, n: usize) -> String {
crate::i18n::tr_format!("{verb} {n} Photo{}", crate::i18n::plural_s(n), verb = crate::i18n::tr(verb), n = n)
}
/// The confirm labels that are one fixed phrase.
fn fixed_ok_label(app: &LightcraftApp, dlg: &Dialog, informational: bool) -> &'static str {
match dlg {
// worded with a count or by the SAM state: `ok_label` builds them
Dialog::Import { .. } | Dialog::Export { .. } | Dialog::SamModel { .. } => "",
Dialog::LightroomResult { first: Some(_), .. } => "Show in Library",
Dialog::ContactSheet { .. } => "Choose PDF…",
Dialog::SlideshowExport { .. } => "Export…",
@@ -376,7 +384,6 @@ fn ok_label(app: &LightcraftApp, dlg: &Dialog, informational: bool, sam: &SamSta
d @ Dialog::LightroomDestination { .. } => crate::lightroom_destination::ok_label(d),
Dialog::SynchronizeFolder { .. } if informational => "Close",
Dialog::SynchronizeFolder { .. } => "Synchronize",
Dialog::SamModel { .. } => return sam_ok_label(dlg, sam).to_string(),
Dialog::RenamePerson { from, to, .. } if person_rename_plan(&app.session.catalog, from, to).needs_merge => "Merge",
Dialog::RenamePerson { .. } => "Rename",
// the dialogs that only show something, or a model file that can't be used
@@ -391,8 +398,7 @@ fn ok_label(app: &LightcraftApp, dlg: &Dialog, informational: bool, sam: &SamSta
| Dialog::LightroomResult { .. }
| Dialog::DenoiseModel { .. }
| Dialog::FaceModel { .. } => "Close",
};
label.to_string()
}
}
/// The label of the SAM 3 dialog's confirm button.
@@ -1105,13 +1111,7 @@ fn contact_sheet_body(app: &mut LightcraftApp, ui: &mut egui::Ui, dlg: &mut Dial
options.rows = options.rows.clamp(1, 10);
let n = app.session.selected().len();
let pages = n.div_ceil(options.columns * options.rows);
ui.label(crate::i18n::tr_format!(
"{n} selected photo{} · {pages} page{}",
if n == 1 { "" } else { "s" },
if pages == 1 { "" } else { "s" },
n = n,
pages = pages
));
ui.label(contact_sheet_summary(n, pages));
field(ui, "Paper", |ui| {
let c = crate::widgets::combo_box("contactSheetPaper").selected_text(&options.paper).show_ui(ui, |ui| {
ui.selectable_value(&mut options.paper, "a4".into(), "A4");
@@ -1131,6 +1131,11 @@ fn contact_sheet_body(app: &mut LightcraftApp, ui: &mut egui::Ui, dlg: &mut Dial
ui.label(crate::i18n::tr("Choose a PDF destination after confirming. Print the PDF from your viewer."));
}
/// "N selected photos · M pages".
fn contact_sheet_summary(n: usize, pages: usize) -> String {
crate::i18n::tr_format!("{n} selected photo{} · {pages} page{}", crate::i18n::plural_s(n), crate::i18n::plural_s(pages), n = n, pages = pages)
}
/// The body of the Export dialog.
fn export_body(app: &mut LightcraftApp, ui: &mut egui::Ui, t: &Tokens, dlg: &mut Dialog) {
let Dialog::Export { opts, full_size, resize, preset_name, limit_kb, dir } = dlg else { return };
+42 -26
View File
@@ -79,37 +79,13 @@ pub fn poll(app: &mut LightcraftApp, ctx: &egui::Context) {
pub(super) fn slideshow_export_body(app: &mut LightcraftApp, ui: &mut egui::Ui, settings: &mut lightcraft_engine::slideshow_export::ExportSettings) {
use lightcraft_engine::slideshow_export::{ExportSettings, Format};
let t = Tokens::get(ui.ctx());
let photos = app.session.visible_cloned().len();
let slide = app.session.slideshow.slide_duration;
let (intro, ending) = app.session.slide_titles.durations(slide);
let titles = usize::from(intro.is_some()) + usize::from(ending.is_some());
let seconds = photos as f64 * slide + intro.unwrap_or(0.0) + ending.unwrap_or(0.0);
let summary = crate::i18n::tr_format!(
"{n} photo{} in view · {slides} slide{} · {time}",
if photos == 1 { "" } else { "s" },
if photos + titles == 1 { "" } else { "s" },
n = photos,
slides = photos + titles,
time = format!("{}:{:02}", (seconds / 60.0).floor() as u64, (seconds % 60.0).round() as u64)
);
ui.label(egui::RichText::new(summary).color(t.text_dim));
ui.label(egui::RichText::new(slideshow_summary(app)).color(t.text_dim));
let before = settings.format;
choices(ui, "Format", "slideshowExportFormat", &[(Format::Pdf, "PDF"), (Format::Jpeg, "JPEG"), (Format::Video, "Video")], &mut settings.format);
if settings.format != before {
*settings = ExportSettings { full_screen: settings.full_screen, ..ExportSettings::for_format(settings.format) };
}
match settings.format {
Format::Video => {
choices(ui, "Size", "slideshowExportSize", &[(720, "720p"), (1080, "1080p")], &mut settings.size);
choices(ui, "Frame rate", "slideshowExportFps", &[(24, "24 fps"), (30, "30 fps")], &mut settings.fps);
let mut webm = settings.container == "webm";
choices(ui, "Container", "slideshowExportContainer", &[(false, "MP4"), (true, "WebM")], &mut webm);
settings.container = if webm { "webm".into() } else { "mp4".into() };
}
_ => {
choices(ui, "Size", "slideshowExportSize", &[(1024, "1024 px"), (1920, "1920 px"), (3840, "3840 px")], &mut settings.size);
}
}
slideshow_size_rows(ui, settings);
field(ui, "Quality", |ui| {
let r = ui.add(egui::Slider::new(&mut settings.quality, 1..=100));
crate::widgets::register(ui, "slider:slideshowExportQuality", r.rect);
@@ -130,6 +106,46 @@ pub(super) fn slideshow_export_body(app: &mut LightcraftApp, ui: &mut egui::Ui,
);
}
/// How many photos are in view, how many slides they make with the intro and ending screens, and how long it plays.
fn slideshow_counts(app: &mut LightcraftApp) -> (usize, usize, f64) {
let photos = app.session.visible_cloned().len();
let slide = app.session.slideshow.slide_duration;
let (intro, ending) = app.session.slide_titles.durations(slide);
let titles = usize::from(intro.is_some()) + usize::from(ending.is_some());
(photos, photos + titles, photos as f64 * slide + intro.unwrap_or(0.0) + ending.unwrap_or(0.0))
}
/// "N photos in view · M slides · m:ss".
fn slideshow_summary(app: &mut LightcraftApp) -> String {
let (photos, slides, seconds) = slideshow_counts(app);
let time = format!("{}:{:02}", (seconds / 60.0).floor() as u64, (seconds % 60.0).round() as u64);
crate::i18n::tr_format!(
"{n} photo{} in view · {slides} slide{} · {time}",
crate::i18n::plural_s(photos),
crate::i18n::plural_s(slides),
n = photos,
slides = slides,
time = time
)
}
/// The size choices of the chosen format, and a video's frame rate and container.
fn slideshow_size_rows(ui: &mut egui::Ui, settings: &mut lightcraft_engine::slideshow_export::ExportSettings) {
use lightcraft_engine::slideshow_export::Format;
match settings.format {
Format::Video => {
choices(ui, "Size", "slideshowExportSize", &[(720, "720p"), (1080, "1080p")], &mut settings.size);
choices(ui, "Frame rate", "slideshowExportFps", &[(24, "24 fps"), (30, "30 fps")], &mut settings.fps);
let mut webm = settings.container == "webm";
choices(ui, "Container", "slideshowExportContainer", &[(false, "MP4"), (true, "WebM")], &mut webm);
settings.container = if webm { "webm".into() } else { "mp4".into() };
}
_ => {
choices(ui, "Size", "slideshowExportSize", &[(1024, "1024 px"), (1920, "1920 px"), (3840, "3840 px")], &mut settings.size);
}
}
}
#[cfg(test)]
mod tests {
use super::*;