Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impl prompts and savefile dialog on Windows #9009

Merged
merged 5 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,11 @@ features = [
"Wdk_System_SystemServices",
"Win32_Graphics_Gdi",
"Win32_Graphics_DirectComposition",
"Win32_UI_Controls",
"Win32_UI_WindowsAndMessaging",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Shell",
"Win32_System_Com",
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Time",
Expand Down
8 changes: 6 additions & 2 deletions crates/gpui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,13 @@ copypasta = "0.10.1"
open = "5.0.1"
ashpd = "0.7.0"
xcb = { version = "1.3", features = ["as-raw-xcb-connection", "randr", "xkb"] }
wayland-client= { version = "0.31.2" }
wayland-client = { version = "0.31.2" }
wayland-cursor = "0.31.1"
wayland-protocols = { version = "0.31.2", features = ["client", "staging", "unstable"] }
wayland-protocols = { version = "0.31.2", features = [
"client",
"staging",
"unstable",
] }
wayland-backend = { version = "0.3.3", features = ["client_system"] }
xkbcommon = { version = "0.7", features = ["wayland", "x11"] }
as-raw-xcb-connection = "1"
Expand Down
66 changes: 58 additions & 8 deletions crates/gpui/src/platform/windows/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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};
Expand All @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
76 changes: 73 additions & 3 deletions crates/gpui/src/platform/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{
any::Any,
cell::{Cell, RefCell},
ffi::c_void,
iter::once,
num::NonZeroIsize,
path::PathBuf,
rc::{Rc, Weak},
Expand All @@ -14,7 +15,8 @@ use std::{
};

use blade_graphics as gpu;
use futures::channel::oneshot::Receiver;
use futures::channel::oneshot::{self, Receiver};
use itertools::Itertools;
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use smallvec::SmallVec;
use windows::{
Expand All @@ -33,6 +35,10 @@ use windows::{
},
},
UI::{
Controls::{
TaskDialogIndirect, TASKDIALOGCONFIG, TASKDIALOG_BUTTON, TD_ERROR_ICON,
TD_INFORMATION_ICON, TD_WARNING_ICON,
},
Input::KeyboardAndMouse::{
GetKeyState, VIRTUAL_KEY, VK_BACK, VK_CONTROL, VK_DOWN, VK_END, VK_ESCAPE, VK_F1,
VK_F24, VK_HOME, VK_INSERT, VK_LEFT, VK_LWIN, VK_MENU, VK_NEXT, VK_PRIOR,
Expand Down Expand Up @@ -778,15 +784,79 @@ impl PlatformWindow for WindowsWindow {
self.inner.input_handler.take()
}

// todo(windows)
fn prompt(
&self,
level: PromptLevel,
msg: &str,
detail: Option<&str>,
answers: &[&str],
) -> Option<Receiver<usize>> {
unimplemented!()
let (done_tx, done_rx) = oneshot::channel();
let msg = msg.to_string();
let detail_string = match detail {
Some(info) => Some(info.to_string()),
None => None,
};
let answers = answers.iter().map(|s| s.to_string()).collect::<Vec<_>>();
let handle = self.inner.hwnd;
self.inner
.platform_inner
.foreground_executor
.spawn(async move {
unsafe {
let mut config;
config = std::mem::zeroed::<TASKDIALOGCONFIG>();
config.cbSize = std::mem::size_of::<TASKDIALOGCONFIG>() as _;
config.hwndParent = handle;
let title;
let main_icon;
match level {
crate::PromptLevel::Info => {
title = windows::core::w!("Info");
main_icon = TD_INFORMATION_ICON;
}
crate::PromptLevel::Warning => {
title = windows::core::w!("Warning");
main_icon = TD_WARNING_ICON;
}
crate::PromptLevel::Critical => {
title = windows::core::w!("Critical");
main_icon = TD_ERROR_ICON;
}
};
config.pszWindowTitle = title;
config.Anonymous1.pszMainIcon = main_icon;
let instruction = msg.encode_utf16().chain(once(0)).collect_vec();
config.pszMainInstruction = PCWSTR::from_raw(instruction.as_ptr());
let hints_encoded;
if let Some(ref hints) = detail_string {
hints_encoded = hints.encode_utf16().chain(once(0)).collect_vec();
config.pszContent = PCWSTR::from_raw(hints_encoded.as_ptr());
};
let mut buttons = Vec::new();
let mut btn_encoded = Vec::new();
for (index, btn_string) in answers.iter().enumerate() {
let encoded = btn_string.encode_utf16().chain(once(0)).collect_vec();
buttons.push(TASKDIALOG_BUTTON {
nButtonID: index as _,
pszButtonText: PCWSTR::from_raw(encoded.as_ptr()),
});
btn_encoded.push(encoded);
}
config.cButtons = buttons.len() as _;
config.pButtons = buttons.as_ptr();

config.pfCallback = None;
let mut res = std::mem::zeroed();
let _ = TaskDialogIndirect(&config, Some(&mut res), None, None)
.inspect_err(|e| log::error!("unable to create task dialog: {}", e));

let _ = done_tx.send(res as usize);
}
})
.detach();

Some(done_rx)
}

// todo(windows)
Expand Down
8 changes: 8 additions & 0 deletions crates/zed/resources/windows/manifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,12 @@
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity type='win32'
name='Microsoft.Windows.Common-Controls'
version='6.0.0.0' processorArchitecture='*'
publicKeyToken='6595b64144ccf1df' />
</dependentAssembly>
</dependency>
</assembly>
Loading