windows: Add file dialog using IFileOpenDialog (#8919)

Release Notes:

- Added a file dialog for Windows
This commit is contained in:
Jason Wen 2024-03-08 20:07:48 -08:00 committed by GitHub
parent d4ec78f328
commit 456efb53ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 77 additions and 9 deletions

3
.gitignore vendored
View File

@ -22,4 +22,5 @@ DerivedData/
**/*.db
.pytest_cache
.venv
.blob_store
.blob_store
.vscode

View File

@ -347,6 +347,7 @@ features = [
"Win32_System_Threading",
"Win32_System_DataExchange",
"Win32_System_Ole",
"Win32_System_Com",
]
[patch.crates-io]

View File

@ -2,10 +2,10 @@
#![allow(unused_variables)]
use std::{
cell::RefCell,
cell::{Cell, RefCell},
collections::HashSet,
ffi::{c_uint, c_void},
os::windows::ffi::OsStrExt,
ffi::{c_uint, c_void, OsString},
os::windows::ffi::{OsStrExt, OsStringExt},
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
@ -21,7 +21,7 @@ use parking_lot::Mutex;
use time::UtcOffset;
use util::{ResultExt, SemanticVersion};
use windows::{
core::{HSTRING, PCWSTR},
core::{IUnknown, HRESULT, HSTRING, PCWSTR, PWSTR},
Wdk::System::SystemServices::RtlGetVersion,
Win32::{
Foundation::{CloseHandle, BOOL, HANDLE, HWND, LPARAM, TRUE},
@ -35,8 +35,9 @@ use windows::{
UI::{
Input::KeyboardAndMouse::GetDoubleClickTime,
Shell::{
FileSaveDialog, IFileSaveDialog, IShellItem, SHCreateItemFromParsingName,
ShellExecuteW, SIGDN_FILESYSPATH,
FileOpenDialog, FileSaveDialog, IFileOpenDialog, IFileSaveDialog, IShellItem,
SHCreateItemFromParsingName, ShellExecuteW, FILEOPENDIALOGOPTIONS,
FOS_ALLOWMULTISELECT, FOS_FILEMUSTEXIST, FOS_PICKFOLDERS, SIGDN_FILESYSPATH,
},
WindowsAndMessaging::{
DispatchMessageW, EnumThreadWindows, LoadImageW, PeekMessageW, PostQuitMessage,
@ -341,9 +342,74 @@ impl Platform for WindowsPlatform {
self.inner.callbacks.lock().open_urls = Some(callback);
}
// todo(windows)
fn prompt_for_paths(&self, options: PathPromptOptions) -> Receiver<Option<Vec<PathBuf>>> {
unimplemented!()
let (tx, rx) = oneshot::channel();
self.foreground_executor()
.spawn(async move {
let tx = Cell::new(Some(tx));
// create file open dialog
let folder_dialog: IFileOpenDialog = unsafe {
CoCreateInstance::<std::option::Option<&IUnknown>, IFileOpenDialog>(
&FileOpenDialog,
None,
CLSCTX_ALL,
)
.unwrap()
};
// dialog options
let mut dialog_options: FILEOPENDIALOGOPTIONS = FOS_FILEMUSTEXIST;
if options.multiple {
dialog_options |= FOS_ALLOWMULTISELECT;
}
if options.directories {
dialog_options |= FOS_PICKFOLDERS;
}
unsafe {
folder_dialog.SetOptions(dialog_options).unwrap();
folder_dialog
.SetTitle(&HSTRING::from(OsString::from("Select a folder")))
.unwrap();
}
let hr = unsafe { folder_dialog.Show(None) };
if hr.is_err() {
if hr.unwrap_err().code() == HRESULT(0x800704C7u32 as i32) {
// user canceled error
if let Some(tx) = tx.take() {
tx.send(None).unwrap();
}
return;
}
}
let mut results = unsafe { folder_dialog.GetResults().unwrap() };
let mut paths: Vec<PathBuf> = Vec::new();
for i in 0..unsafe { results.GetCount().unwrap() } {
let mut item: IShellItem = unsafe { results.GetItemAt(i).unwrap() };
let mut path: PWSTR =
unsafe { item.GetDisplayName(SIGDN_FILESYSPATH).unwrap() };
let mut path_os_string = OsString::from_wide(unsafe { path.as_wide() });
paths.push(PathBuf::from(path_os_string));
}
if let Some(tx) = tx.take() {
if paths.len() == 0 {
tx.send(None).unwrap();
} else {
tx.send(Some(paths)).unwrap();
}
}
})
.detach();
rx
}
fn prompt_for_new_path(&self, directory: &Path) -> Receiver<Option<PathBuf>> {