windows: Use drop target helper (#33203)

Matin Aniss and 张小白 created

It now utilises the
[`IDropTargetHelper`](https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-idroptargethelper)
in drag and drop events to render the proper item drop cursor icon which
includes the thumbnail when available and action text. Also swaps the
drop effect from `DROPEFFECT_LINK` to `DROPEFFECT_COPY` to match other
Windows application behaviour.

Example of drop icon

![example_drop](https://github.com/user-attachments/assets/4f8ea86c-929a-4813-9f8e-b3553ecf4d6e)

Release Notes:

- N/A

---------

Co-authored-by: 张小白 <364772080@qq.com>

Change summary

crates/gpui/src/platform/windows/platform.rs |  8 +++++
crates/gpui/src/platform/windows/window.rs   | 34 ++++++++++++++++++---
2 files changed, 37 insertions(+), 5 deletions(-)

Detailed changes

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -42,6 +42,7 @@ pub(crate) struct WindowsPlatform {
     text_system: Arc<DirectWriteTextSystem>,
     windows_version: WindowsVersion,
     bitmap_factory: ManuallyDrop<IWICImagingFactory>,
+    drop_target_helper: IDropTargetHelper,
     validation_number: usize,
     main_thread_id_win32: u32,
 }
@@ -103,6 +104,10 @@ impl WindowsPlatform {
             DirectWriteTextSystem::new(&bitmap_factory)
                 .context("Error creating DirectWriteTextSystem")?,
         );
+        let drop_target_helper: IDropTargetHelper = unsafe {
+            CoCreateInstance(&CLSID_DragDropHelper, None, CLSCTX_INPROC_SERVER)
+                .context("Error creating drop target helper.")?
+        };
         let icon = load_icon().unwrap_or_default();
         let state = RefCell::new(WindowsPlatformState::new());
         let raw_window_handles = RwLock::new(SmallVec::new());
@@ -120,6 +125,7 @@ impl WindowsPlatform {
             text_system,
             windows_version,
             bitmap_factory,
+            drop_target_helper,
             validation_number,
             main_thread_id_win32,
         })
@@ -177,6 +183,7 @@ impl WindowsPlatform {
             executor: self.foreground_executor.clone(),
             current_cursor: self.state.borrow().current_cursor,
             windows_version: self.windows_version,
+            drop_target_helper: self.drop_target_helper.clone(),
             validation_number: self.validation_number,
             main_receiver: self.main_receiver.clone(),
             main_thread_id_win32: self.main_thread_id_win32,
@@ -728,6 +735,7 @@ pub(crate) struct WindowCreationInfo {
     pub(crate) executor: ForegroundExecutor,
     pub(crate) current_cursor: Option<HCURSOR>,
     pub(crate) windows_version: WindowsVersion,
+    pub(crate) drop_target_helper: IDropTargetHelper,
     pub(crate) validation_number: usize,
     pub(crate) main_receiver: flume::Receiver<Runnable>,
     pub(crate) main_thread_id_win32: u32,

crates/gpui/src/platform/windows/window.rs 🔗

@@ -63,6 +63,7 @@ pub struct WindowsWindowState {
 pub(crate) struct WindowsWindowStatePtr {
     hwnd: HWND,
     this: Weak<Self>,
+    drop_target_helper: IDropTargetHelper,
     pub(crate) state: RefCell<WindowsWindowState>,
     pub(crate) handle: AnyWindowHandle,
     pub(crate) hide_title_bar: bool,
@@ -210,6 +211,7 @@ impl WindowsWindowStatePtr {
         Ok(Rc::new_cyclic(|this| Self {
             hwnd,
             this: this.clone(),
+            drop_target_helper: context.drop_target_helper.clone(),
             state,
             handle: context.handle,
             hide_title_bar: context.hide_title_bar,
@@ -331,6 +333,7 @@ struct WindowCreateContext<'a> {
     executor: ForegroundExecutor,
     current_cursor: Option<HCURSOR>,
     windows_version: WindowsVersion,
+    drop_target_helper: IDropTargetHelper,
     validation_number: usize,
     main_receiver: flume::Receiver<Runnable>,
     gpu_context: &'a BladeContext,
@@ -349,6 +352,7 @@ impl WindowsWindow {
             executor,
             current_cursor,
             windows_version,
+            drop_target_helper,
             validation_number,
             main_receiver,
             main_thread_id_win32,
@@ -394,6 +398,7 @@ impl WindowsWindow {
             executor,
             current_cursor,
             windows_version,
+            drop_target_helper,
             validation_number,
             main_receiver,
             gpu_context,
@@ -831,8 +836,9 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
                 lindex: -1,
                 tymed: TYMED_HGLOBAL.0 as _,
             };
+            let cursor_position = POINT { x: pt.x, y: pt.y };
             if idata_obj.QueryGetData(&config as _) == S_OK {
-                *pdweffect = DROPEFFECT_LINK;
+                *pdweffect = DROPEFFECT_COPY;
                 let Some(mut idata) = idata_obj.GetData(&config as _).log_err() else {
                     return Ok(());
                 };
@@ -847,7 +853,7 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
                     }
                 });
                 ReleaseStgMedium(&mut idata);
-                let mut cursor_position = POINT { x: pt.x, y: pt.y };
+                let mut cursor_position = cursor_position;
                 ScreenToClient(self.0.hwnd, &mut cursor_position)
                     .ok()
                     .log_err();
@@ -864,6 +870,10 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
             } else {
                 *pdweffect = DROPEFFECT_NONE;
             }
+            self.0
+                .drop_target_helper
+                .DragEnter(self.0.hwnd, idata_obj, &cursor_position, *pdweffect)
+                .log_err();
         }
         Ok(())
     }
@@ -872,10 +882,15 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
         &self,
         _grfkeystate: MODIFIERKEYS_FLAGS,
         pt: &POINTL,
-        _pdweffect: *mut DROPEFFECT,
+        pdweffect: *mut DROPEFFECT,
     ) -> windows::core::Result<()> {
         let mut cursor_position = POINT { x: pt.x, y: pt.y };
         unsafe {
+            *pdweffect = DROPEFFECT_COPY;
+            self.0
+                .drop_target_helper
+                .DragOver(&cursor_position, *pdweffect)
+                .log_err();
             ScreenToClient(self.0.hwnd, &mut cursor_position)
                 .ok()
                 .log_err();
@@ -894,6 +909,9 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
     }
 
     fn DragLeave(&self) -> windows::core::Result<()> {
+        unsafe {
+            self.0.drop_target_helper.DragLeave().log_err();
+        }
         let input = PlatformInput::FileDrop(FileDropEvent::Exited);
         self.handle_drag_drop(input);
 
@@ -902,13 +920,19 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
 
     fn Drop(
         &self,
-        _pdataobj: windows::core::Ref<IDataObject>,
+        pdataobj: windows::core::Ref<IDataObject>,
         _grfkeystate: MODIFIERKEYS_FLAGS,
         pt: &POINTL,
-        _pdweffect: *mut DROPEFFECT,
+        pdweffect: *mut DROPEFFECT,
     ) -> windows::core::Result<()> {
+        let idata_obj = pdataobj.ok()?;
         let mut cursor_position = POINT { x: pt.x, y: pt.y };
         unsafe {
+            *pdweffect = DROPEFFECT_COPY;
+            self.0
+                .drop_target_helper
+                .Drop(idata_obj, &cursor_position, *pdweffect)
+                .log_err();
             ScreenToClient(self.0.hwnd, &mut cursor_position)
                 .ok()
                 .log_err();