Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: dialog open supports multiple dirs, fixes #4091 #4354

Merged
merged 4 commits into from
Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/dialog-multiple-folders.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'api': patch
'tauri': patch
---

Allow choosing multiple folders in `dialog.open`.
2 changes: 1 addition & 1 deletion core/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ attohttpc = { version = "0.19", features = [ "json", "form" ], optional = true }
open = { version = "3.0", optional = true }
shared_child = { version = "1.0", optional = true }
os_pipe = { version = "1.0", optional = true }
rfd = { version = "0.8", optional = true }
rfd = { version = "0.9", optional = true }
raw-window-handle = "0.4.3"
minisign-verify = { version = "0.2", optional = true }
time = { version = "0.3", features = [ "parsing", "formatting" ], optional = true }
Expand Down
50 changes: 50 additions & 0 deletions core/tauri/src/api/dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,30 @@ pub mod blocking {
response
}

/// Shows the dialog to select multiple folders.
/// This is a blocking operation,
/// and should *NOT* be used when running on the main thread context.
///
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::blocking::FileDialogBuilder;
/// #[tauri::command]
/// fn my_command() {
/// let folder_paths = FileDialogBuilder::new().pick_folders();
/// // do something with the optional folder paths here
/// // the folder paths value is `None` if the user closed the dialog
/// }
/// ```
pub fn pick_folders(self) -> Option<Vec<PathBuf>> {
#[allow(clippy::let_and_return)]
let response = run_dialog_sync!(self.0.pick_folders());
#[cfg(not(target_os = "linux"))]
let response =
response.map(|paths| paths.into_iter().map(|p| p.path().to_path_buf()).collect());
response
}

/// Shows the dialog to save a file.
/// This is a blocking operation,
/// and should *NOT* be used when running on the main thread context.
Expand Down Expand Up @@ -515,6 +539,32 @@ mod nonblocking {
run_file_dialog!(self.0.pick_folder(), f)
}

/// Shows the dialog to select multiple folders.
/// This is not a blocking operation,
/// and should be used when running on the main thread to avoid deadlocks with the event loop.
///
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::FileDialogBuilder;
/// tauri::Builder::default()
/// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
/// .expect("failed to build tauri app")
/// .run(|_app, _event| {
/// FileDialogBuilder::new().pick_folders(|file_paths| {
/// // do something with the optional folder paths here
/// // the folder paths value is `None` if the user closed the dialog
/// })
/// })
/// ```
pub fn pick_folders<F: FnOnce(Option<Vec<PathBuf>>) + Send + 'static>(self, f: F) {
#[cfg(not(target_os = "linux"))]
let f = |paths: Option<Vec<rfd::FileHandle>>| {
f(paths.map(|list| list.into_iter().map(|p| p.path().to_path_buf()).collect()))
};
run_file_dialog!(self.0.pick_folders(), f)
}

/// Shows the dialog to save a file.
///
/// This is not a blocking operation,
Expand Down
24 changes: 18 additions & 6 deletions core/tauri/src/endpoints/dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,25 @@ impl Cmd {
let scopes = context.window.state::<Scopes>();

let res = if options.directory {
let folder = dialog_builder.pick_folder();
if let Some(path) = &folder {
scopes
.allow_directory(path, options.recursive)
.map_err(crate::error::into_anyhow)?;
if options.multiple {
let folders = dialog_builder.pick_folders();
if let Some(folders) = &folders {
for folder in folders {
scopes
.allow_directory(folder, options.recursive)
.map_err(crate::error::into_anyhow)?;
}
}
folders.into()
} else {
let folder = dialog_builder.pick_folder();
if let Some(path) = &folder {
scopes
.allow_directory(path, options.recursive)
.map_err(crate::error::into_anyhow)?;
}
folder.into()
}
folder.into()
} else if options.multiple {
let files = dialog_builder.pick_files();
if let Some(files) = &files {
Expand Down