Просмотр исходного кода

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

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
Didrik Nordström 3 лет назад
Родитель
Сommit
4e51dce6ca

+ 6 - 0
.changes/dialog-multiple-folders.md

@@ -0,0 +1,6 @@
+---
+'api': patch
+'tauri': patch
+---
+
+Allow choosing multiple folders in `dialog.open`.

+ 1 - 1
core/tauri/Cargo.toml

@@ -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 }

+ 50 - 0
core/tauri/src/api/dialog.rs

@@ -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.
@@ -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,

+ 18 - 6
core/tauri/src/endpoints/dialog.rs

@@ -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 {