Fix freeze on file/folder picker dialogs: use the non-blocking dialog API instead of blocking_pick_files()/blocking_pick_folder(), which deadlock when called from a command handler
This commit is contained in:
Generated
+1
@@ -2536,6 +2536,7 @@ dependencies = [
|
|||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-dialog",
|
"tauri-plugin-dialog",
|
||||||
|
"tokio",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -18,3 +18,4 @@ tauri-plugin-dialog = "2"
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
tokio = { version = "1", features = ["sync"] }
|
||||||
|
|||||||
+31
-6
@@ -71,9 +71,23 @@ fn expand(paths: &[String]) -> Vec<FileEntry> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Native "Select Files" dialog, mirroring the original's Load Files button.
|
/// Native "Select Files" dialog, mirroring the original's Load Files button.
|
||||||
|
///
|
||||||
|
/// Uses the non-blocking callback API (not `blocking_pick_files()`): the blocking
|
||||||
|
/// variant must never be called from a Tauri command handler, because on macOS it has
|
||||||
|
/// to hand off to the main thread to actually show the panel and then wait for a
|
||||||
|
/// response - if the command itself is occupying the thread the dialog needs, the app
|
||||||
|
/// deadlocks the moment the dialog tries to close (this is exactly what "froze after I
|
||||||
|
/// hit Cancel" looked like). The callback form lets the dialog signal completion
|
||||||
|
/// asynchronously instead, which we bridge back into an `async fn` with a oneshot
|
||||||
|
/// channel.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn pick_files(app: AppHandle) -> Vec<FileEntry> {
|
pub async fn pick_files(app: AppHandle) -> Vec<FileEntry> {
|
||||||
let selection = app.dialog().file().blocking_pick_files();
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
|
app.dialog().file().pick_files(move |result| {
|
||||||
|
let _ = tx.send(result);
|
||||||
|
});
|
||||||
|
let selection = rx.await.unwrap_or(None);
|
||||||
|
|
||||||
let paths: Vec<String> = selection
|
let paths: Vec<String> = selection
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
@@ -84,9 +98,16 @@ pub fn pick_files(app: AppHandle) -> Vec<FileEntry> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Native "Select Folders" dialog; the folder's contents are expanded recursively.
|
/// Native "Select Folders" dialog; the folder's contents are expanded recursively.
|
||||||
|
/// See `pick_files` above for why this uses the callback API instead of
|
||||||
|
/// `blocking_pick_folder()`.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn pick_folder(app: AppHandle) -> Vec<FileEntry> {
|
pub async fn pick_folder(app: AppHandle) -> Vec<FileEntry> {
|
||||||
let selection = app.dialog().file().blocking_pick_folder();
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
|
app.dialog().file().pick_folder(move |result| {
|
||||||
|
let _ = tx.send(result);
|
||||||
|
});
|
||||||
|
let selection = rx.await.unwrap_or(None);
|
||||||
|
|
||||||
let paths: Vec<String> = selection
|
let paths: Vec<String> = selection
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|f| f.into_path().ok())
|
.filter_map(|f| f.into_path().ok())
|
||||||
@@ -96,7 +117,11 @@ pub fn pick_folder(app: AppHandle) -> Vec<FileEntry> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Expands paths dropped onto the window (files and/or folders) into their files.
|
/// Expands paths dropped onto the window (files and/or folders) into their files.
|
||||||
|
/// Runs off the main thread too - not for a deadlock reason like the dialogs above,
|
||||||
|
/// just so recursively walking a very large dropped folder doesn't stall the UI.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn expand_dropped_paths(paths: Vec<String>) -> Vec<FileEntry> {
|
pub async fn expand_dropped_paths(paths: Vec<String>) -> Vec<FileEntry> {
|
||||||
expand(&paths)
|
tauri::async_runtime::spawn_blocking(move || expand(&paths))
|
||||||
|
.await
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user