From 51bb936263de3be5f98a003226afc4f64b1d168f Mon Sep 17 00:00:00 2001 From: grimsace Date: Tue, 28 Apr 2026 13:14:13 -0500 Subject: [PATCH] added an all layer option to exports --- src/exporter.rs | 85 ++++++++++++++++++++++++++++++++++++------------- src/main.rs | 22 +++++-------- src/ui.rs | 10 ++++-- 3 files changed, 78 insertions(+), 39 deletions(-) diff --git a/src/exporter.rs b/src/exporter.rs index 5e6acab..432ae51 100644 --- a/src/exporter.rs +++ b/src/exporter.rs @@ -136,8 +136,13 @@ fn collect_scene_data(layout: &DungeonLayout, settings: &UiSettings) -> SceneDat // Open a dialog to select an export destination. pub fn select_export_target(settings: &UiSettings) -> Result { - if settings.export_format == ExportFormat::Folder { - let mut dialog = FileDialog::new().set_title("Export composite masks folder"); + if settings.export_format == ExportFormat::Folder || settings.export_level_index == 999 { + let title = if settings.export_level_index == 999 { + "Select export folder for all levels" + } else { + "Export composite masks folder" + }; + let mut dialog = FileDialog::new().set_title(title); if let Some(ref last_path) = settings.last_export_path { dialog = dialog.set_directory(last_path); } @@ -173,40 +178,74 @@ pub fn select_export_target(settings: &UiSettings) -> Result Result { + let export_indices: Vec = if settings.export_level_index == 999 { + (0..layouts.len()).collect() + } else { + let idx = settings + .export_level_index + .min(layouts.len().saturating_sub(1)); + vec![idx] + }; + match target { ExportTarget::Folder(folder) => { - export_composite_masks(layout, settings, &folder)?; + for &idx in &export_indices { + let layout = &layouts[idx]; + if settings.export_format == ExportFormat::Folder { + let subfolder = if settings.export_level_index == 999 { + folder.join(format!("level {}", idx + 1)) + } else { + folder.clone() + }; + export_composite_masks(layout, settings, &subfolder)?; + } else { + let ext = settings.export_format.extension(); + let filename = format!("level{}.{ext}", idx + 1); + let path = folder.join(filename); + save_layout_to_file(layout, settings, &path)?; + } + } Ok(folder) } ExportTarget::File(path) => { - match settings.export_format { - ExportFormat::Svg => { - let svg = build_svg(layout, settings); - std::fs::write(&path, svg).map_err(|e| format!("Failed writing SVG: {e}"))?; - } - ExportFormat::Png | ExportFormat::Jpeg | ExportFormat::Webp => { - let pixmap = render_pixmap(layout, settings)?; - let img = raster_image_from_pixmap(&pixmap, settings)?; - let format = match settings.export_format { - ExportFormat::Png => ImageFormat::Png, - ExportFormat::Jpeg => ImageFormat::Jpeg, - ExportFormat::Webp => ImageFormat::WebP, - ExportFormat::Svg | ExportFormat::Folder => unreachable!(), - }; - img.save_with_format(&path, format) - .map_err(|e| format!("Failed writing image: {e}"))?; - } - ExportFormat::Folder => unreachable!(), - } + let idx = export_indices[0]; + save_layout_to_file(&layouts[idx], settings, &path)?; Ok(path) } } } +fn save_layout_to_file( + layout: &DungeonLayout, + settings: &UiSettings, + path: &std::path::Path, +) -> Result<(), String> { + match settings.export_format { + ExportFormat::Svg => { + let svg = build_svg(layout, settings); + std::fs::write(path, svg).map_err(|e| format!("Failed writing SVG: {e}"))?; + } + ExportFormat::Png | ExportFormat::Jpeg | ExportFormat::Webp => { + let pixmap = render_pixmap(layout, settings)?; + let img = raster_image_from_pixmap(&pixmap, settings)?; + let format = match settings.export_format { + ExportFormat::Png => ImageFormat::Png, + ExportFormat::Jpeg => ImageFormat::Jpeg, + ExportFormat::Webp => ImageFormat::WebP, + ExportFormat::Svg | ExportFormat::Folder => unreachable!(), + }; + img.save_with_format(path, format) + .map_err(|e| format!("Failed writing image: {e}"))?; + } + ExportFormat::Folder => unreachable!(), + } + Ok(()) +} + // Render the dungeon layout to a pixmap. fn render_pixmap(layout: &DungeonLayout, settings: &UiSettings) -> Result { let g = ExportGeometry::new(settings.cols, settings.rows); diff --git a/src/main.rs b/src/main.rs index e2e661e..bfacc89 100644 --- a/src/main.rs +++ b/src/main.rs @@ -223,20 +223,14 @@ impl eframe::App for DungeonApp { } } - let export_idx = self - .settings - .export_level_index - .min(self.levels.len().saturating_sub(1)); - if export_idx < self.levels.len() { - let layout = self.levels[export_idx].clone(); - let settings = self.settings.clone(); - let (tx, rx) = mpsc::channel(); - thread::spawn(move || { - let result = exporter::export_to_target(&layout, &settings, target); - let _ = tx.send(result); - }); - self.export_rx = Some(rx); - } + let layouts = self.levels.clone(); + let settings = self.settings.clone(); + let (tx, rx) = mpsc::channel(); + thread::spawn(move || { + let result = exporter::export_to_target(&layouts, &settings, target); + let _ = tx.send(result); + }); + self.export_rx = Some(rx); } if let Some(rx) = &self.export_rx { diff --git a/src/ui.rs b/src/ui.rs index e4e3f74..8e05cc6 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -432,14 +432,20 @@ fn draw_generate_tab( let level_count = num_levels; ui.horizontal(|ui| { ui.label("Level"); + let selected_text = if settings.export_level_index == 999 { + "All Levels".to_string() + } else { + format!("Level {}", settings.export_level_index + 1) + }; egui::ComboBox::from_id_salt("export_level") - .selected_text(format!("Level {}", settings.export_level_index)) + .selected_text(selected_text) .show_ui(ui, |ui| { + ui.selectable_value(&mut settings.export_level_index, 999, "All Levels"); for i in 0..level_count { ui.selectable_value( &mut settings.export_level_index, i, - format!("Level {}", i), + format!("Level {}", i + 1), ); } });