diff --git a/AUTHORS b/AUTHORS index 41bdaf710..79bd5af30 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,3 +23,4 @@ Manmeet Singh Simon Fell Nick Larsen Thomas McAndrew +Paweł Pykało diff --git a/CHANGELOG.md b/CHANGELOG.md index 90f08567d..da22d12b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -139,6 +139,7 @@ You can find its changes [documented below](#070---2021-01-01). - X11: window focus events ([#1938] by [@Maan2003] - Preserve the aspect ratio of a clipped region in an Image ([#2195] by [@barsae]) - GTK: Hot state now properly resets when the mouse leaves the window via an occluded part. ([#2324] by [@xStrom]) +- Windows: generate `commands::OPEN_FILES` when using multiselect in file open dialog. ([#2332] by [@ppykalo]) ### Visual @@ -578,6 +579,7 @@ Last release without a changelog :( [@benoitryder]: https://github.com/benoitryder [@sprocklem]: https://github.com/sprocklem [@cbondurant]: https://github.com/cbondurant +[@ppykalo]: https://github.com/ppykalo [#599]: https://github.com/linebender/druid/pull/599 [#611]: https://github.com/linebender/druid/pull/611 @@ -884,6 +886,7 @@ Last release without a changelog :( [#2323]: https://github.com/linebender/druid/pull/2323 [#2324]: https://github.com/linebender/druid/pull/2324 [#2331]: https://github.com/linebender/druid/pull/2331 +[#2332]: https://github.com/linebender/druid/pull/2332 [Unreleased]: https://github.com/linebender/druid/compare/v0.7.0...master [0.7.0]: https://github.com/linebender/druid/compare/v0.6.0...v0.7.0 diff --git a/druid-shell/src/backend/windows/dialog.rs b/druid-shell/src/backend/windows/dialog.rs index 2dd761683..8c2eafa26 100644 --- a/druid-shell/src/backend/windows/dialog.rs +++ b/druid-shell/src/backend/windows/dialog.rs @@ -76,7 +76,7 @@ pub(crate) unsafe fn get_file_dialog_path( hwnd_owner: HWND, ty: FileDialogType, options: FileDialogOptions, -) -> Result { +) -> Result, Error> { let mut pfd: *mut IFileDialog = null_mut(); let (class, id) = match ty { FileDialogType::Open => (&CLSID_FileOpenDialog, IFileOpenDialog::uuidof()), @@ -165,13 +165,34 @@ pub(crate) unsafe fn get_file_dialog_path( // show the dialog as_result(file_dialog.Show(hwnd_owner))?; - let mut result_ptr: *mut IShellItem = null_mut(); - as_result(file_dialog.GetResult(&mut result_ptr))?; - let shell_item = ComPtr::from_raw(result_ptr); - let mut display_name: LPWSTR = null_mut(); - as_result(shell_item.GetDisplayName(SIGDN_FILESYSPATH, &mut display_name))?; - let filename = display_name.to_os_string(); - CoTaskMemFree(display_name as LPVOID); - - Ok(filename) + + let mut result_ptr_vec = Vec::new(); + if let Ok(open_file_dialog) = file_dialog.cast::() { + let mut results_ptr: *mut IShellItemArray = null_mut(); + as_result(open_file_dialog.GetResults(&mut results_ptr))?; + let shell_items = ComPtr::from_raw(results_ptr); + let mut count = 0; + as_result(shell_items.GetCount(&mut count))?; + for i in 0..count { + let mut result_ptr: *mut IShellItem = null_mut(); + as_result(shell_items.GetItemAt(i as DWORD, &mut result_ptr))?; + result_ptr_vec.push(result_ptr); + } + } else { + let mut result_ptr: *mut IShellItem = null_mut(); + as_result(file_dialog.GetResult(&mut result_ptr))?; + result_ptr_vec.push(result_ptr); + } + + let mut filename_vec = Vec::with_capacity(result_ptr_vec.len()); + for result_ptr in result_ptr_vec { + let shell_item = ComPtr::from_raw(result_ptr); + let mut display_name: LPWSTR = null_mut(); + as_result(shell_item.GetDisplayName(SIGDN_FILESYSPATH, &mut display_name))?; + let filename = display_name.to_os_string(); + CoTaskMemFree(display_name as LPVOID); + filename_vec.push(filename); + } + + Ok(filename_vec) } diff --git a/druid-shell/src/backend/windows/window.rs b/druid-shell/src/backend/windows/window.rs index d6c692d0f..36bb82bfd 100644 --- a/druid-shell/src/backend/windows/window.rs +++ b/druid-shell/src/backend/windows/window.rs @@ -622,23 +622,38 @@ impl MyWndProc { let info = unsafe { get_file_dialog_path(hwnd, FileDialogType::Save, options) .ok() - .map(|os_str| FileInfo { - path: os_str.into(), + .map(|s| FileInfo { + // `get_file_dialog_path` guarantees that save dialogs + // only return one path + path: s.first().unwrap().into(), format: None, }) }; self.with_wnd_state(|s| s.handler.save_as(token, info)); } DeferredOp::Open(options, token) => { - let info = unsafe { - get_file_dialog_path(hwnd, FileDialogType::Open, options) - .ok() - .map(|s| FileInfo { - path: s.into(), - format: None, - }) + let multi_selection = options.multi_selection; + let infos = unsafe { + match get_file_dialog_path(hwnd, FileDialogType::Open, options) { + Ok(infos) => infos + .iter() + .map(|path| FileInfo { + path: path.into(), + format: None, + }) + .collect(), + Err(err) => { + tracing::error!("Error trying to open file: {}", err); + vec![] + } + } }; - self.with_wnd_state(|s| s.handler.open_file(token, info)); + + if multi_selection { + self.with_wnd_state(|s| s.handler.open_files(token, infos)); + } else { + self.with_wnd_state(|s| s.handler.open_file(token, infos.first().cloned())); + } } DeferredOp::ContextMenu(menu, pos) => { let hmenu = menu.into_hmenu();