windows: Use drop target helper (#33203)

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>
This commit is contained in:
Matin Aniss 2025-06-23 23:30:21 +10:00 committed by GitHub
parent 1a6c1b2159
commit a067c16c82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 37 additions and 5 deletions

View file

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

View file

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