Skip to content

Commit dbe924c

Browse files
committed
refactor(main.rs): async dialog picker on linux
- run rfd dialogs in bg thread (no main thread freeze) - add FileDialogMsg + rx channel - spinner 'Waiting for file dialog…' - dedupe + append on receive - small doc/comment cleanup
1 parent ae8c631 commit dbe924c

1 file changed

Lines changed: 87 additions & 10 deletions

File tree

src/main.rs

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ use serde::{Deserialize, Serialize};
4545
/// - On failure: Contains an error string describing why parsing failed
4646
type RestoreMsg = Result<(FolderTreeNode, PathBuf), String>; // Result type for restore operations
4747

48+
/// Result of a background file dialog.
49+
type FileDialogMsg = Vec<PathBuf>;
50+
4851
/// A template representing a reusable set of file and folder paths.
4952
///
5053
/// Templates are serialized as JSON and can be saved/loaded by the user
@@ -118,14 +121,14 @@ fn build_tree_from_paths(paths: &[String]) -> FolderTreeNode {
118121

119122
/// Entry point
120123
///
121-
/// Initializes enviroment variables, loads the application icon,
124+
/// Initializes environment variables, loads the application icon,
122125
/// configures [`eframe::NativeOptions`], and launches the GUI.
123126
///
124127
/// Returns an [`eframe::Error`] if the GUI fails to start.
125128
fn main() -> Result<(), eframe::Error> {
126129
println!("[DEBUG] main: Starting application");
127130

128-
dotenv::dotenv().ok(); // Load enviroment variables from .env if available
131+
dotenv::dotenv().ok(); // Load environment variables from .env if available
129132
println!("[DEBUG] .env loaded (if present)");
130133

131134
let icon = load_icon_image(); // Load application image
@@ -180,6 +183,9 @@ struct GUIApp {
180183
restore_progress: Option<Progress>,
181184
restore_opening: bool,
182185
restore_rx: Option<mpsc::Receiver<RestoreMsg>>,
186+
// async file dialog handling for linux being fuck and freezing.
187+
file_dialog_rx: Option<mpsc::Receiver<FileDialogMsg>>,
188+
file_dialog_opening: bool,
183189
tab: MainTab,
184190
compression_enabled: bool,
185191
default_backup_location: Option<PathBuf>,
@@ -212,6 +218,8 @@ impl Default for GUIApp {
212218
restore_progress: None,
213219
restore_opening: false,
214220
restore_rx: None,
221+
file_dialog_rx: None,
222+
file_dialog_opening: false,
215223
tab: MainTab::Home,
216224
compression_enabled: config.compression_enabled,
217225
default_backup_location: config.default_backup_location.clone(),
@@ -415,28 +423,97 @@ impl eframe::App for GUIApp {
415423
self.restore_rx = None;
416424
}
417425

426+
if let Some(rx) = self.file_dialog_rx.as_ref() {
427+
use std::sync::mpsc::TryRecvError;
428+
429+
match rx.try_recv() {
430+
Ok(mut paths) => {
431+
self.selected_folders.append(&mut paths);
432+
self.selected_folders.sort();
433+
self.selected_folders.dedup();
434+
self.file_dialog_rx = None;
435+
self.file_dialog_opening = false;
436+
}
437+
Err(TryRecvError::Disconnected) => {
438+
self.file_dialog_rx = None;
439+
self.file_dialog_opening = false;
440+
}
441+
Err(TryRecvError::Empty) => {
442+
// waiting...
443+
}
444+
}
445+
}
446+
418447
ui.heading("Konserve");
419448
ui.separator();
420449

421450
// Folder and File Pickers
422451
ui.horizontal(|ui| {
423452
if ui.button("Add Folders").clicked() {
424-
if let Some(folders) = FileDialog::new().pick_folders() {
425-
self.selected_folders.extend(folders);
426-
self.selected_folders.sort();
427-
self.selected_folders.dedup();
453+
#[cfg(target_os = "macos")]
454+
{
455+
// macOS wants dialogs on the main thread
456+
if let Some(folders) = FileDialog::new().pick_folders() {
457+
self.selected_folders.extend(folders);
458+
self.selected_folders.sort();
459+
self.selected_folders.dedup();
460+
}
461+
}
462+
463+
#[cfg(not(target_os = "macos"))]
464+
{
465+
// Linux / Windows: run dialog in a background thread
466+
if self.file_dialog_rx.is_none() {
467+
self.file_dialog_opening = true;
468+
469+
let (tx, rx) = mpsc::channel::<FileDialogMsg>();
470+
self.file_dialog_rx = Some(rx);
471+
472+
std::thread::spawn(move || {
473+
let folders =
474+
FileDialog::new().pick_folders().unwrap_or_default();
475+
let _ = tx.send(folders);
476+
});
477+
}
428478
}
429479
}
430480

431481
if ui.button("Add Files").clicked() {
432-
if let Some(files) = FileDialog::new().pick_files() {
433-
self.selected_folders.extend(files);
434-
self.selected_folders.sort();
435-
self.selected_folders.dedup();
482+
#[cfg(target_os = "macos")]
483+
{
484+
if let Some(files) = FileDialog::new().pick_files() {
485+
self.selected_folders.extend(files);
486+
self.selected_folders.sort();
487+
self.selected_folders.dedup();
488+
}
489+
}
490+
491+
#[cfg(not(target_os = "macos"))]
492+
{
493+
if self.file_dialog_rx.is_none() {
494+
self.file_dialog_opening = true;
495+
496+
let (tx, rx) = mpsc::channel::<FileDialogMsg>();
497+
self.file_dialog_rx = Some(rx);
498+
499+
std::thread::spawn(move || {
500+
let files =
501+
FileDialog::new().pick_files().unwrap_or_default();
502+
let _ = tx.send(files);
503+
});
504+
}
436505
}
437506
}
438507
});
439508

509+
if self.file_dialog_opening {
510+
ui.horizontal(|ui| {
511+
ui.add(egui::Spinner::new().size(12.0));
512+
ui.label("Waiting for file dialog…");
513+
});
514+
ctx.request_repaint_after(std::time::Duration::from_millis(50));
515+
}
516+
440517
// Show selected paths
441518
if !self.selected_folders.is_empty() {
442519
ui.add_space(4.0);

0 commit comments

Comments
 (0)