export now uses separate thread to avoid application locking up

This commit is contained in:
grimsace
2026-03-12 08:59:28 -05:00
parent b1779c4a25
commit 5da03bf2b5
3 changed files with 95 additions and 33 deletions
+23 -7
View File
@@ -19,6 +19,11 @@ const LOCKED_DOOR: (u8, u8, u8, u8) = (80, 200, 120, 255);
const ARCHWAY: (u8, u8, u8, u8) = (70, 130, 220, 255); const ARCHWAY: (u8, u8, u8, u8) = (70, 130, 220, 255);
const WHITE: (u8, u8, u8, u8) = (255, 255, 255, 255); const WHITE: (u8, u8, u8, u8) = (255, 255, 255, 255);
pub enum ExportTarget {
File(PathBuf),
Folder(PathBuf),
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum DoorStyle { enum DoorStyle {
Solid, Solid,
@@ -111,17 +116,13 @@ fn collect_scene_data(layout: &DungeonLayout, settings: &UiSettings) -> SceneDat
scene scene
} }
pub fn export_with_dialog( pub fn select_export_target(settings: &UiSettings) -> Result<ExportTarget, String> {
layout: &DungeonLayout,
settings: &UiSettings,
) -> Result<PathBuf, String> {
if settings.export_format == ExportFormat::Folder { if settings.export_format == ExportFormat::Folder {
let folder = FileDialog::new() let folder = FileDialog::new()
.set_title("Export composite masks folder") .set_title("Export composite masks folder")
.pick_folder() .pick_folder()
.ok_or_else(|| "Export canceled".to_string())?; .ok_or_else(|| "Export canceled".to_string())?;
export_composite_masks(layout, settings, &folder)?; return Ok(ExportTarget::Folder(folder));
return Ok(folder);
} }
let ext = settings.export_format.extension(); let ext = settings.export_format.extension();
@@ -141,6 +142,20 @@ pub fn export_with_dialog(
path.set_extension(ext); path.set_extension(ext);
} }
Ok(ExportTarget::File(path))
}
pub fn export_to_target(
layout: &DungeonLayout,
settings: &UiSettings,
target: ExportTarget,
) -> Result<PathBuf, String> {
match target {
ExportTarget::Folder(folder) => {
export_composite_masks(layout, settings, &folder)?;
Ok(folder)
}
ExportTarget::File(path) => {
match settings.export_format { match settings.export_format {
ExportFormat::Svg => { ExportFormat::Svg => {
let svg = build_svg(layout, settings); let svg = build_svg(layout, settings);
@@ -160,8 +175,9 @@ pub fn export_with_dialog(
} }
ExportFormat::Folder => unreachable!(), ExportFormat::Folder => unreachable!(),
} }
Ok(path) Ok(path)
}
}
} }
fn render_pixmap(layout: &DungeonLayout, settings: &UiSettings) -> Result<Pixmap, String> { fn render_pixmap(layout: &DungeonLayout, settings: &UiSettings) -> Result<Pixmap, String> {
+35 -3
View File
@@ -5,6 +5,8 @@ mod settings;
mod ui; mod ui;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::mpsc;
use std::thread;
use eframe::egui; use eframe::egui;
use egui::{Color32, Stroke}; use egui::{Color32, Stroke};
@@ -25,6 +27,7 @@ struct DungeonApp {
settings: UiSettings, settings: UiSettings,
layout: DungeonLayout, layout: DungeonLayout,
drag_state: Option<DragState>, drag_state: Option<DragState>,
export_rx: Option<mpsc::Receiver<Result<std::path::PathBuf, String>>>,
} }
impl Default for DungeonApp { impl Default for DungeonApp {
@@ -48,6 +51,7 @@ impl Default for DungeonApp {
settings, settings,
layout, layout,
drag_state: None, drag_state: None,
export_rx: None,
} }
} }
} }
@@ -62,7 +66,7 @@ impl Drop for DungeonApp {
impl eframe::App for DungeonApp { impl eframe::App for DungeonApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
let panel_result = draw_side_panel(ctx, &mut self.settings); let panel_result = draw_side_panel(ctx, &mut self.settings, self.export_rx.is_some());
if self.settings.min_room_size > self.settings.max_room_size { if self.settings.min_room_size > self.settings.max_room_size {
self.settings.max_room_size = self.settings.min_room_size; self.settings.max_room_size = self.settings.min_room_size;
@@ -77,9 +81,37 @@ impl eframe::App for DungeonApp {
if panel_result.reset_clicked || panel_result.settings_changed { if panel_result.reset_clicked || panel_result.settings_changed {
self.regenerate_layout(); self.regenerate_layout();
} }
if panel_result.export_clicked { if panel_result.export_clicked && self.export_rx.is_none() {
if let Err(err) = exporter::export_with_dialog(&self.layout, &self.settings) { let target = match exporter::select_export_target(&self.settings) {
Ok(target) => target,
Err(err) => {
eprintln!("{err}"); eprintln!("{err}");
return;
}
};
let layout = self.layout.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);
}
if let Some(rx) = &self.export_rx {
match rx.try_recv() {
Ok(Ok(_)) => {
self.export_rx = None;
}
Ok(Err(err)) => {
eprintln!("{err}");
self.export_rx = None;
}
Err(mpsc::TryRecvError::Empty) => {}
Err(mpsc::TryRecvError::Disconnected) => {
self.export_rx = None;
}
} }
} }
+17 -3
View File
@@ -152,7 +152,11 @@ pub struct SidePanelResult {
pub export_clicked: bool, pub export_clicked: bool,
} }
pub fn draw_side_panel(ctx: &egui::Context, settings: &mut UiSettings) -> SidePanelResult { pub fn draw_side_panel(
ctx: &egui::Context,
settings: &mut UiSettings,
export_in_progress: bool,
) -> SidePanelResult {
let mut result = SidePanelResult::default(); let mut result = SidePanelResult::default();
egui::SidePanel::left("options_panel") egui::SidePanel::left("options_panel")
@@ -175,7 +179,7 @@ pub fn draw_side_panel(ctx: &egui::Context, settings: &mut UiSettings) -> SidePa
ui.separator(); ui.separator();
match settings.active_tab { match settings.active_tab {
Tab::Generate => draw_generate_tab(ui, settings, &mut result), Tab::Generate => draw_generate_tab(ui, settings, &mut result, export_in_progress),
Tab::Layout => draw_layout_tab(ui, settings, &mut result), Tab::Layout => draw_layout_tab(ui, settings, &mut result),
} }
}); });
@@ -183,7 +187,12 @@ pub fn draw_side_panel(ctx: &egui::Context, settings: &mut UiSettings) -> SidePa
result result
} }
fn draw_generate_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut SidePanelResult) { fn draw_generate_tab(
ui: &mut egui::Ui,
settings: &mut UiSettings,
result: &mut SidePanelResult,
export_in_progress: bool,
) {
ui.label(RichText::new("Grid Settings").strong()); ui.label(RichText::new("Grid Settings").strong());
ui.add_space(8.0); ui.add_space(8.0);
@@ -339,6 +348,11 @@ fn draw_generate_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut
if ui.button("Export Image").clicked() { if ui.button("Export Image").clicked() {
result.export_clicked = true; result.export_clicked = true;
} }
if export_in_progress {
ui.add_space(6.0);
ui.add(egui::ProgressBar::new(0.0).animate(true));
}
} }
fn draw_layout_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut SidePanelResult) { fn draw_layout_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut SidePanelResult) {