Impl prompts and savefile dialog on Windows (#9009)

### Description
This is a part of #8809 , and this PR dose not include `open file
dialog`, as I already saw two PRs impl this.



https://github.com/zed-industries/zed/assets/14981363/3223490a-de77-4892-986f-97cf85aec3ae




Release Notes:

- N/A
This commit is contained in:
张小白 2024-03-09 00:14:47 +08:00 committed by GitHub
parent bf295eac90
commit a550b9cecf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 147 additions and 13 deletions

View file

@ -5,6 +5,7 @@ use std::{
cell::RefCell,
collections::HashSet,
ffi::{c_uint, c_void},
os::windows::ffi::OsStrExt,
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
@ -14,7 +15,8 @@ use std::{
use anyhow::{anyhow, Result};
use async_task::Runnable;
use copypasta::{ClipboardContext, ClipboardProvider};
use futures::channel::oneshot::Receiver;
use futures::channel::oneshot::{self, Receiver};
use itertools::Itertools;
use parking_lot::Mutex;
use time::UtcOffset;
use util::{ResultExt, SemanticVersion};
@ -25,15 +27,17 @@ use windows::{
Foundation::{CloseHandle, BOOL, HANDLE, HWND, LPARAM, TRUE},
Graphics::DirectComposition::DCompositionWaitForCompositorClock,
System::{
Com::{CoCreateInstance, CreateBindCtx, CLSCTX_ALL},
Ole::{OleInitialize, OleUninitialize},
Threading::{CreateEventW, GetCurrentThreadId, INFINITE},
Time::{GetTimeZoneInformation, TIME_ZONE_ID_INVALID},
{
Ole::{OleInitialize, OleUninitialize},
Threading::{CreateEventW, GetCurrentThreadId, INFINITE},
},
},
UI::{
Input::KeyboardAndMouse::GetDoubleClickTime,
Shell::ShellExecuteW,
Shell::{
FileSaveDialog, IFileSaveDialog, IShellItem, SHCreateItemFromParsingName,
ShellExecuteW, SIGDN_FILESYSPATH,
},
WindowsAndMessaging::{
DispatchMessageW, EnumThreadWindows, LoadImageW, PeekMessageW, PostQuitMessage,
SetCursor, SystemParametersInfoW, TranslateMessage, HCURSOR, IDC_ARROW, IDC_CROSS,
@ -342,9 +346,32 @@ impl Platform for WindowsPlatform {
unimplemented!()
}
// todo(windows)
fn prompt_for_new_path(&self, directory: &Path) -> Receiver<Option<PathBuf>> {
unimplemented!()
let directory = directory.to_owned();
let (tx, rx) = oneshot::channel();
self.foreground_executor()
.spawn(async move {
unsafe {
let Ok(dialog) = show_savefile_dialog(directory) else {
let _ = tx.send(None);
return;
};
let Ok(_) = dialog.Show(None) else {
let _ = tx.send(None); // user cancel
return;
};
if let Ok(shell_item) = dialog.GetResult() {
if let Ok(file) = shell_item.GetDisplayName(SIGDN_FILESYSPATH) {
let _ = tx.send(Some(PathBuf::from(file.to_string().unwrap())));
return;
}
}
let _ = tx.send(None);
}
})
.detach();
rx
}
fn reveal_path(&self, path: &Path) {
@ -555,3 +582,26 @@ fn open_target(target: &str) {
}
}
}
unsafe fn show_savefile_dialog(directory: PathBuf) -> Result<IFileSaveDialog> {
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);
}
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));
}
Ok(dialog)
}