mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Impl drag-drop action for Windows (#8959)
### Description This is a part of #8809 https://github.com/zed-industries/zed/assets/14981363/2b085b9d-8b83-4ac7-8b84-07c679760eba Release Notes: - N/A --------- Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
parent
e85d484952
commit
b50f86735f
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -12160,6 +12160,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538"
|
checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core",
|
"windows-core",
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
"windows-targets 0.52.4",
|
"windows-targets 0.52.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -12173,6 +12175,28 @@ dependencies = [
|
|||||||
"windows-targets 0.52.4",
|
"windows-targets 0.52.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.53.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.53.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -325,6 +325,7 @@ sys-locale = "0.3.1"
|
|||||||
[workspace.dependencies.windows]
|
[workspace.dependencies.windows]
|
||||||
version = "0.53.0"
|
version = "0.53.0"
|
||||||
features = [
|
features = [
|
||||||
|
"implement",
|
||||||
"Wdk_System_SystemServices",
|
"Wdk_System_SystemServices",
|
||||||
"Win32_Graphics_Gdi",
|
"Win32_Graphics_Gdi",
|
||||||
"Win32_Graphics_DirectComposition",
|
"Win32_Graphics_DirectComposition",
|
||||||
@ -335,6 +336,8 @@ features = [
|
|||||||
"Win32_System_SystemServices",
|
"Win32_System_SystemServices",
|
||||||
"Win32_System_Time",
|
"Win32_System_Time",
|
||||||
"Win32_Security",
|
"Win32_Security",
|
||||||
|
"Win32_System_Com",
|
||||||
|
"Win32_System_Com_StructuredStorage",
|
||||||
"Win32_System_Threading",
|
"Win32_System_Threading",
|
||||||
"Win32_System_DataExchange",
|
"Win32_System_DataExchange",
|
||||||
"Win32_System_Ole",
|
"Win32_System_Ole",
|
||||||
|
@ -24,19 +24,21 @@ use windows::{
|
|||||||
Foundation::{CloseHandle, BOOL, HANDLE, HWND, LPARAM, TRUE},
|
Foundation::{CloseHandle, BOOL, HANDLE, HWND, LPARAM, TRUE},
|
||||||
Graphics::DirectComposition::DCompositionWaitForCompositorClock,
|
Graphics::DirectComposition::DCompositionWaitForCompositorClock,
|
||||||
System::{
|
System::{
|
||||||
Threading::{CreateEventW, GetCurrentThreadId, INFINITE},
|
|
||||||
Time::{GetTimeZoneInformation, TIME_ZONE_ID_INVALID},
|
Time::{GetTimeZoneInformation, TIME_ZONE_ID_INVALID},
|
||||||
|
{
|
||||||
|
Ole::{OleInitialize, OleUninitialize},
|
||||||
|
Threading::{CreateEventW, GetCurrentThreadId, INFINITE},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
UI::{
|
UI::{
|
||||||
Input::KeyboardAndMouse::GetDoubleClickTime,
|
Input::KeyboardAndMouse::GetDoubleClickTime,
|
||||||
Shell::ShellExecuteW,
|
Shell::ShellExecuteW,
|
||||||
WindowsAndMessaging::{
|
WindowsAndMessaging::{
|
||||||
DispatchMessageW, EnumThreadWindows, LoadImageW, MsgWaitForMultipleObjects,
|
DispatchMessageW, EnumThreadWindows, LoadImageW, PeekMessageW, PostQuitMessage,
|
||||||
PeekMessageW, PostQuitMessage, SetCursor, SystemParametersInfoW, TranslateMessage,
|
SetCursor, SystemParametersInfoW, TranslateMessage, HCURSOR, IDC_ARROW, IDC_CROSS,
|
||||||
HCURSOR, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_IBEAM, IDC_NO, IDC_SIZENS, IDC_SIZEWE,
|
IDC_HAND, IDC_IBEAM, IDC_NO, IDC_SIZENS, IDC_SIZEWE, IMAGE_CURSOR, LR_DEFAULTSIZE,
|
||||||
IMAGE_CURSOR, LR_DEFAULTSIZE, LR_SHARED, MSG, PM_REMOVE, QS_ALLINPUT,
|
LR_SHARED, MSG, PM_REMOVE, SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES,
|
||||||
SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SW_SHOWDEFAULT,
|
SW_SHOWDEFAULT, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE,
|
||||||
SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -147,6 +149,9 @@ impl WindowsPlatformSystemSettings {
|
|||||||
|
|
||||||
impl WindowsPlatform {
|
impl WindowsPlatform {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
|
unsafe {
|
||||||
|
OleInitialize(None).expect("unable to initialize Windows OLE");
|
||||||
|
}
|
||||||
let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
|
let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
|
||||||
let event = unsafe { CreateEventW(None, false, false, None) }.unwrap();
|
let event = unsafe { CreateEventW(None, false, false, None) }.unwrap();
|
||||||
let dispatcher = Arc::new(WindowsDispatcher::new(main_sender, event));
|
let dispatcher = Arc::new(WindowsDispatcher::new(main_sender, event));
|
||||||
@ -518,6 +523,14 @@ impl Platform for WindowsPlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for WindowsPlatform {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
OleUninitialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn load_cursor(name: PCWSTR) -> Result<HANDLE> {
|
unsafe fn load_cursor(name: PCWSTR) -> Result<HANDLE> {
|
||||||
LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED).map_err(|e| anyhow!(e))
|
LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED).map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
@ -7,20 +7,30 @@ use std::{
|
|||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
num::NonZeroIsize,
|
num::NonZeroIsize,
|
||||||
|
path::PathBuf,
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
|
str::FromStr,
|
||||||
sync::{Arc, Once},
|
sync::{Arc, Once},
|
||||||
};
|
};
|
||||||
|
|
||||||
use blade_graphics as gpu;
|
use blade_graphics as gpu;
|
||||||
use futures::channel::oneshot::Receiver;
|
use futures::channel::oneshot::Receiver;
|
||||||
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use windows::{
|
use windows::{
|
||||||
core::{w, HSTRING, PCWSTR},
|
core::{implement, w, HSTRING, PCWSTR},
|
||||||
Win32::{
|
Win32::{
|
||||||
Foundation::{FALSE, HINSTANCE, HWND, LPARAM, LRESULT, WPARAM},
|
Foundation::{FALSE, HINSTANCE, HWND, LPARAM, LRESULT, MAX_PATH, POINTL, S_OK, WPARAM},
|
||||||
Graphics::Gdi::{BeginPaint, EndPaint, InvalidateRect, PAINTSTRUCT},
|
Graphics::Gdi::{BeginPaint, EndPaint, InvalidateRect, PAINTSTRUCT},
|
||||||
System::SystemServices::{
|
System::{
|
||||||
MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_XBUTTON1, MK_XBUTTON2, MODIFIERKEYS_FLAGS,
|
Com::{IDataObject, DVASPECT_CONTENT, FORMATETC, TYMED_HGLOBAL},
|
||||||
|
Ole::{
|
||||||
|
IDropTarget, IDropTarget_Impl, RegisterDragDrop, ReleaseStgMedium, RevokeDragDrop,
|
||||||
|
CF_HDROP, DROPEFFECT, DROPEFFECT_LINK, DROPEFFECT_NONE,
|
||||||
|
},
|
||||||
|
SystemServices::{
|
||||||
|
MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_XBUTTON1, MK_XBUTTON2, MODIFIERKEYS_FLAGS,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
UI::{
|
UI::{
|
||||||
Input::KeyboardAndMouse::{
|
Input::KeyboardAndMouse::{
|
||||||
@ -28,6 +38,7 @@ use windows::{
|
|||||||
VK_F24, VK_HOME, VK_INSERT, VK_LEFT, VK_LWIN, VK_MENU, VK_NEXT, VK_PRIOR,
|
VK_F24, VK_HOME, VK_INSERT, VK_LEFT, VK_LWIN, VK_MENU, VK_NEXT, VK_PRIOR,
|
||||||
VK_RETURN, VK_RIGHT, VK_RWIN, VK_SHIFT, VK_SPACE, VK_TAB, VK_UP,
|
VK_RETURN, VK_RIGHT, VK_RWIN, VK_SHIFT, VK_SPACE, VK_TAB, VK_UP,
|
||||||
},
|
},
|
||||||
|
Shell::{DragQueryFileW, HDROP},
|
||||||
WindowsAndMessaging::{
|
WindowsAndMessaging::{
|
||||||
CreateWindowExW, DefWindowProcW, GetWindowLongPtrW, LoadCursorW, PostQuitMessage,
|
CreateWindowExW, DefWindowProcW, GetWindowLongPtrW, LoadCursorW, PostQuitMessage,
|
||||||
RegisterClassW, SetWindowLongPtrW, SetWindowTextW, ShowWindow, CREATESTRUCTW,
|
RegisterClassW, SetWindowLongPtrW, SetWindowTextW, ShowWindow, CREATESTRUCTW,
|
||||||
@ -559,6 +570,14 @@ impl WindowsWindowInner {
|
|||||||
}
|
}
|
||||||
LRESULT(1)
|
LRESULT(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_drag_drop(&self, input: PlatformInput) {
|
||||||
|
let mut callbacks = self.callbacks.borrow_mut();
|
||||||
|
let Some(ref mut func) = callbacks.input else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
func(input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -576,6 +595,7 @@ struct Callbacks {
|
|||||||
|
|
||||||
pub(crate) struct WindowsWindow {
|
pub(crate) struct WindowsWindow {
|
||||||
inner: Rc<WindowsWindowInner>,
|
inner: Rc<WindowsWindowInner>,
|
||||||
|
drag_drop_handler: IDropTarget,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WindowCreateContext {
|
struct WindowCreateContext {
|
||||||
@ -640,8 +660,19 @@ impl WindowsWindow {
|
|||||||
lpparam,
|
lpparam,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
let drag_drop_handler = {
|
||||||
|
let inner = context.inner.as_ref().unwrap();
|
||||||
|
let handler = WindowsDragDropHandler(Rc::clone(inner));
|
||||||
|
let drag_drop_handler: IDropTarget = handler.into();
|
||||||
|
unsafe {
|
||||||
|
RegisterDragDrop(inner.hwnd, &drag_drop_handler)
|
||||||
|
.expect("unable to register drag-drop event")
|
||||||
|
};
|
||||||
|
drag_drop_handler
|
||||||
|
};
|
||||||
let wnd = Self {
|
let wnd = Self {
|
||||||
inner: context.inner.unwrap(),
|
inner: context.inner.unwrap(),
|
||||||
|
drag_drop_handler,
|
||||||
};
|
};
|
||||||
platform_inner.window_handles.borrow_mut().insert(handle);
|
platform_inner.window_handles.borrow_mut().insert(handle);
|
||||||
match options.bounds {
|
match options.bounds {
|
||||||
@ -679,6 +710,14 @@ impl HasDisplayHandle for WindowsWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for WindowsWindow {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let _ = RevokeDragDrop(self.inner.hwnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PlatformWindow for WindowsWindow {
|
impl PlatformWindow for WindowsWindow {
|
||||||
fn bounds(&self) -> WindowBounds {
|
fn bounds(&self) -> WindowBounds {
|
||||||
WindowBounds::Fixed(Bounds {
|
WindowBounds::Fixed(Bounds {
|
||||||
@ -836,6 +875,113 @@ impl PlatformWindow for WindowsWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[implement(IDropTarget)]
|
||||||
|
struct WindowsDragDropHandler(pub Rc<WindowsWindowInner>);
|
||||||
|
|
||||||
|
impl IDropTarget_Impl for WindowsDragDropHandler {
|
||||||
|
fn DragEnter(
|
||||||
|
&self,
|
||||||
|
pdataobj: Option<&IDataObject>,
|
||||||
|
_grfkeystate: MODIFIERKEYS_FLAGS,
|
||||||
|
pt: &POINTL,
|
||||||
|
pdweffect: *mut DROPEFFECT,
|
||||||
|
) -> windows::core::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let Some(idata_obj) = pdataobj else {
|
||||||
|
log::info!("no dragging file or directory detected");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let config = FORMATETC {
|
||||||
|
cfFormat: CF_HDROP.0,
|
||||||
|
ptd: std::ptr::null_mut() as _,
|
||||||
|
dwAspect: DVASPECT_CONTENT.0,
|
||||||
|
lindex: -1,
|
||||||
|
tymed: TYMED_HGLOBAL.0 as _,
|
||||||
|
};
|
||||||
|
let mut paths = SmallVec::<[PathBuf; 2]>::new();
|
||||||
|
if idata_obj.QueryGetData(&config as _) == S_OK {
|
||||||
|
*pdweffect = DROPEFFECT_LINK;
|
||||||
|
let Ok(mut idata) = idata_obj.GetData(&config as _) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
if idata.u.hGlobal.is_invalid() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let hdrop = idata.u.hGlobal.0 as *mut HDROP;
|
||||||
|
let file_count = DragQueryFileW(*hdrop, DRAGDROP_GET_FILES_COUNT, None);
|
||||||
|
for file_index in 0..file_count {
|
||||||
|
let mut buffer = [0u16; MAX_PATH as _];
|
||||||
|
let filename_length = DragQueryFileW(*hdrop, file_index, None) as usize;
|
||||||
|
let ret = DragQueryFileW(*hdrop, file_index, Some(&mut buffer));
|
||||||
|
if ret == 0 {
|
||||||
|
log::error!("unable to read file name");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Ok(file_name) = String::from_utf16(&buffer[0..filename_length]) {
|
||||||
|
if let Ok(path) = PathBuf::from_str(&file_name) {
|
||||||
|
paths.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ReleaseStgMedium(&mut idata);
|
||||||
|
let input = PlatformInput::FileDrop(crate::FileDropEvent::Entered {
|
||||||
|
position: Point {
|
||||||
|
x: Pixels(pt.x as _),
|
||||||
|
y: Pixels(pt.y as _),
|
||||||
|
},
|
||||||
|
paths: crate::ExternalPaths(paths),
|
||||||
|
});
|
||||||
|
self.0.handle_drag_drop(input);
|
||||||
|
} else {
|
||||||
|
*pdweffect = DROPEFFECT_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn DragOver(
|
||||||
|
&self,
|
||||||
|
_grfkeystate: MODIFIERKEYS_FLAGS,
|
||||||
|
pt: &POINTL,
|
||||||
|
_pdweffect: *mut DROPEFFECT,
|
||||||
|
) -> windows::core::Result<()> {
|
||||||
|
let input = PlatformInput::FileDrop(crate::FileDropEvent::Pending {
|
||||||
|
position: Point {
|
||||||
|
x: Pixels(pt.x as _),
|
||||||
|
y: Pixels(pt.y as _),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
self.0.handle_drag_drop(input);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn DragLeave(&self) -> windows::core::Result<()> {
|
||||||
|
let input = PlatformInput::FileDrop(crate::FileDropEvent::Exited);
|
||||||
|
self.0.handle_drag_drop(input);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Drop(
|
||||||
|
&self,
|
||||||
|
_pdataobj: Option<&IDataObject>,
|
||||||
|
_grfkeystate: MODIFIERKEYS_FLAGS,
|
||||||
|
pt: &POINTL,
|
||||||
|
_pdweffect: *mut DROPEFFECT,
|
||||||
|
) -> windows::core::Result<()> {
|
||||||
|
let input = PlatformInput::FileDrop(crate::FileDropEvent::Submit {
|
||||||
|
position: Point {
|
||||||
|
x: Pixels(pt.x as _),
|
||||||
|
y: Pixels(pt.y as _),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
self.0.handle_drag_drop(input);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn register_wnd_class() -> PCWSTR {
|
fn register_wnd_class() -> PCWSTR {
|
||||||
const CLASS_NAME: PCWSTR = w!("Zed::Window");
|
const CLASS_NAME: PCWSTR = w!("Zed::Window");
|
||||||
|
|
||||||
@ -923,3 +1069,6 @@ unsafe fn set_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX, dwnewlong:
|
|||||||
SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize
|
SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragqueryfilew
|
||||||
|
const DRAGDROP_GET_FILES_COUNT: u32 = 0xFFFFFFFF;
|
||||||
|
@ -16,6 +16,8 @@ extend-exclude = [
|
|||||||
"crates/editor/src/editor_tests.rs",
|
"crates/editor/src/editor_tests.rs",
|
||||||
# Clojure uses .edn filename extension, which is not a misspelling of "end"
|
# Clojure uses .edn filename extension, which is not a misspelling of "end"
|
||||||
"crates/languages/src/clojure/config.toml",
|
"crates/languages/src/clojure/config.toml",
|
||||||
|
# Windows likes it's abbreviations
|
||||||
|
"crates/gpui/src/platform/windows/",
|
||||||
]
|
]
|
||||||
|
|
||||||
[default]
|
[default]
|
||||||
|
Loading…
Reference in New Issue
Block a user