diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index b05f6806ef..2dcaf72ef2 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -1,11 +1,6 @@ -// todo(windows): remove -#![allow(unused_variables)] - use std::{ - cell::{Cell, RefCell}, - ffi::{c_void, OsString}, + cell::RefCell, mem::ManuallyDrop, - os::windows::ffi::{OsStrExt, OsStringExt}, path::{Path, PathBuf}, rc::Rc, sync::Arc, @@ -288,7 +283,7 @@ impl Platform for WindowsPlatform { } // todo(windows) - fn activate(&self, ignoring_other_apps: bool) {} + fn activate(&self, _ignoring_other_apps: bool) {} // todo(windows) fn hide(&self) { @@ -366,68 +361,9 @@ impl Platform for WindowsPlatform { options: PathPromptOptions, ) -> Receiver>>> { 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::, 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(Ok(None)).unwrap(); - } - return; - } - } - - let mut results = unsafe { folder_dialog.GetResults().unwrap() }; - - let mut paths: Vec = 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.is_empty() { - tx.send(Ok(None)).unwrap(); - } else { - tx.send(Ok(Some(paths))).unwrap(); - } - } + let _ = tx.send(file_open_dialog(options)); }) .detach(); @@ -439,23 +375,7 @@ impl Platform for WindowsPlatform { let (tx, rx) = oneshot::channel(); self.foreground_executor() .spawn(async move { - unsafe { - let Ok(dialog) = show_savefile_dialog(directory) else { - let _ = tx.send(Ok(None)); - return; - }; - let Ok(_) = dialog.Show(None) else { - let _ = tx.send(Ok(None)); // user cancel - return; - }; - if let Ok(shell_item) = dialog.GetResult() { - if let Ok(file) = shell_item.GetDisplayName(SIGDN_FILESYSPATH) { - let _ = tx.send(Ok(Some(PathBuf::from(file.to_string().unwrap())))); - return; - } - } - let _ = tx.send(Ok(None)); - } + let _ = tx.send(file_save_dialog(directory)); }) .detach(); @@ -489,8 +409,8 @@ impl Platform for WindowsPlatform { } // todo(windows) - fn set_menus(&self, menus: Vec, keymap: &Keymap) {} - fn set_dock_menu(&self, menus: Vec, keymap: &Keymap) {} + fn set_menus(&self, _menus: Vec, _keymap: &Keymap) {} + fn set_dock_menu(&self, _menus: Vec, _keymap: &Keymap) {} fn on_app_menu_action(&self, callback: Box) { self.state.borrow_mut().callbacks.app_menu_action = Some(callback); @@ -509,7 +429,7 @@ impl Platform for WindowsPlatform { } // todo(windows) - fn path_for_auxiliary_executable(&self, name: &str) -> Result { + fn path_for_auxiliary_executable(&self, _name: &str) -> Result { Err(anyhow!("not yet implemented")) } @@ -589,7 +509,7 @@ impl Platform for WindowsPlatform { ) }; let password = credential_blob.to_vec(); - unsafe { CredFree(credentials as *const c_void) }; + unsafe { CredFree(credentials as *const _ as _) }; Ok(Some((username, password))) } }) @@ -655,27 +575,61 @@ fn open_target_in_explorer(target: &str) { } } -unsafe fn show_savefile_dialog(directory: PathBuf) -> Result { - let dialog: IFileSaveDialog = CoCreateInstance(&FileSaveDialog, None, CLSCTX_ALL)?; - let bind_context = CreateBindCtx(0)?; - let Ok(full_path) = directory.canonicalize() else { - return Ok(dialog); - }; - let dir_str = full_path.into_os_string(); - if dir_str.is_empty() { - return Ok(dialog); +fn file_open_dialog(options: PathPromptOptions) -> Result>> { + let folder_dialog: IFileOpenDialog = + unsafe { CoCreateInstance(&FileOpenDialog, None, CLSCTX_ALL)? }; + + let mut dialog_options = FOS_FILEMUSTEXIST; + if options.multiple { + dialog_options |= FOS_ALLOWMULTISELECT; } - let dir_vec = dir_str.encode_wide().collect_vec(); - let ret = SHCreateItemFromParsingName(PCWSTR::from_raw(dir_vec.as_ptr()), &bind_context) - .inspect_err(|e| log::error!("unable to create IShellItem: {}", e)); - if ret.is_ok() { - let dir_shell_item: IShellItem = ret.unwrap(); - let _ = dialog - .SetFolder(&dir_shell_item) - .inspect_err(|e| log::error!("unable to set folder for save file dialog: {}", e)); + if options.directories { + dialog_options |= FOS_PICKFOLDERS; } - Ok(dialog) + unsafe { + folder_dialog.SetOptions(dialog_options)?; + if folder_dialog.Show(None).is_err() { + // User cancelled + return Ok(None); + } + } + + let results = unsafe { folder_dialog.GetResults()? }; + let file_count = unsafe { results.GetCount()? }; + if file_count == 0 { + return Ok(None); + } + + let mut paths = Vec::new(); + for i in 0..file_count { + let item = unsafe { results.GetItemAt(i)? }; + let path = unsafe { item.GetDisplayName(SIGDN_FILESYSPATH)?.to_string()? }; + paths.push(PathBuf::from(path)); + } + + Ok(Some(paths)) +} + +fn file_save_dialog(directory: PathBuf) -> Result> { + let dialog: IFileSaveDialog = unsafe { CoCreateInstance(&FileSaveDialog, None, CLSCTX_ALL)? }; + if let Some(full_path) = directory.canonicalize().log_err() { + let full_path = full_path.to_string_lossy().to_string(); + if !full_path.is_empty() { + let path_item: IShellItem = + unsafe { SHCreateItemFromParsingName(&HSTRING::from(&full_path), None)? }; + unsafe { dialog.SetFolder(&path_item).log_err() }; + } + } + unsafe { + if dialog.Show(None).is_err() { + // User cancelled + return Ok(None); + } + } + let shell_item = unsafe { dialog.GetResult()? }; + let file_path_string = unsafe { shell_item.GetDisplayName(SIGDN_FILESYSPATH)?.to_string()? }; + Ok(Some(PathBuf::from(file_path_string))) } fn begin_vsync(vsync_event: HANDLE) {