Move DropTarget to its own module
This commit is contained in:
parent
b33398703f
commit
cfa06f5b59
266
src/win/drop_target.rs
Normal file
266
src/win/drop_target.rs
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::mem::transmute;
|
||||||
|
use std::os::windows::prelude::OsStringExt;
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
use std::rc::{Weak, Rc};
|
||||||
|
|
||||||
|
use winapi::Interface;
|
||||||
|
use winapi::shared::guiddef::{REFIID, IsEqualIID};
|
||||||
|
use winapi::shared::minwindef::{DWORD, WPARAM};
|
||||||
|
use winapi::shared::ntdef::{HRESULT, ULONG};
|
||||||
|
use winapi::shared::windef::POINTL;
|
||||||
|
use winapi::shared::winerror::{S_OK, E_NOINTERFACE, E_UNEXPECTED};
|
||||||
|
use winapi::shared::wtypes::DVASPECT_CONTENT;
|
||||||
|
use winapi::um::objidl::{IDataObject, FORMATETC, TYMED_HGLOBAL, STGMEDIUM};
|
||||||
|
use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl, DROPEFFECT_COPY, DROPEFFECT_MOVE, DROPEFFECT_LINK, DROPEFFECT_SCROLL, DROPEFFECT_NONE};
|
||||||
|
use winapi::um::shellapi::DragQueryFileW;
|
||||||
|
use winapi::um::unknwnbase::{IUnknownVtbl, IUnknown};
|
||||||
|
use winapi::um::winuser::CF_HDROP;
|
||||||
|
|
||||||
|
use crate::{Point, DropData, MouseEvent, Event, EventStatus, DropEffect, PhyPoint};
|
||||||
|
|
||||||
|
use super::WindowState;
|
||||||
|
|
||||||
|
// These function pointers have to be stored in a (const) variable before they can be transmuted
|
||||||
|
const DRAG_ENTER_PTR: unsafe extern "system" fn(this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drag_enter;
|
||||||
|
const DRAG_OVER_PTR: unsafe extern "system" fn(this: *mut IDropTarget, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drag_over;
|
||||||
|
const DROP_PTR: unsafe extern "system" fn(this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drop;
|
||||||
|
const DROP_TARGET_VTBL: IDropTargetVtbl = IDropTargetVtbl {
|
||||||
|
parent: IUnknownVtbl {
|
||||||
|
QueryInterface: DropTarget::query_interface,
|
||||||
|
AddRef: DropTarget::add_ref,
|
||||||
|
Release: DropTarget::release,
|
||||||
|
},
|
||||||
|
DragEnter: unsafe { transmute(DRAG_ENTER_PTR) },
|
||||||
|
DragOver: unsafe { transmute(DRAG_OVER_PTR) },
|
||||||
|
DragLeave: DropTarget::drag_leave,
|
||||||
|
Drop: unsafe { transmute(DROP_PTR) },
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub(super) struct DropTarget {
|
||||||
|
base: IDropTarget,
|
||||||
|
|
||||||
|
window_state: Weak<WindowState>,
|
||||||
|
|
||||||
|
// These are cached since DragOver and DragLeave callbacks don't provide them,
|
||||||
|
// and handling drag move events gets awkward on the client end otherwise
|
||||||
|
drag_position: Point,
|
||||||
|
drop_data: DropData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DropTarget {
|
||||||
|
pub(super) fn new(window_state: Weak<WindowState>) -> Self {
|
||||||
|
Self {
|
||||||
|
base: IDropTarget { lpVtbl: &DROP_TARGET_VTBL },
|
||||||
|
|
||||||
|
window_state,
|
||||||
|
|
||||||
|
drag_position: Point::new(0.0, 0.0),
|
||||||
|
drop_data: DropData::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(&self, pdwEffect: Option<*mut DWORD>, event: MouseEvent) {
|
||||||
|
let Some(window_state) = self.window_state.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut window = window_state.create_window();
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
let event = Event::Mouse(event);
|
||||||
|
let event_status = window_state.handler_mut().as_mut().unwrap().on_event(&mut window, event);
|
||||||
|
|
||||||
|
if let Some(pdwEffect) = pdwEffect {
|
||||||
|
match event_status {
|
||||||
|
EventStatus::AcceptDrop(DropEffect::Copy) => *pdwEffect = DROPEFFECT_COPY,
|
||||||
|
EventStatus::AcceptDrop(DropEffect::Move) => *pdwEffect = DROPEFFECT_MOVE,
|
||||||
|
EventStatus::AcceptDrop(DropEffect::Link) => *pdwEffect = DROPEFFECT_LINK,
|
||||||
|
EventStatus::AcceptDrop(DropEffect::Scroll) => *pdwEffect = DROPEFFECT_SCROLL,
|
||||||
|
_ => *pdwEffect = DROPEFFECT_NONE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_coordinates(&mut self, pt: POINTL) {
|
||||||
|
let Some(window_state) = self.window_state.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let phy_point = PhyPoint::new(pt.x, pt.y);
|
||||||
|
self.drag_position = phy_point.to_logical(&window_state.window_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_drop_data(&mut self, data_object: &IDataObject) {
|
||||||
|
let format = FORMATETC {
|
||||||
|
cfFormat: CF_HDROP as u16,
|
||||||
|
ptd: null_mut(),
|
||||||
|
dwAspect: DVASPECT_CONTENT,
|
||||||
|
lindex: -1,
|
||||||
|
tymed: TYMED_HGLOBAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut medium = STGMEDIUM {
|
||||||
|
tymed: 0,
|
||||||
|
u: null_mut(),
|
||||||
|
pUnkForRelease: null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let hresult = data_object.GetData(&format, &mut medium);
|
||||||
|
if hresult != S_OK {
|
||||||
|
self.drop_data = DropData::None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hdrop = transmute((*medium.u).hGlobal());
|
||||||
|
|
||||||
|
let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, null_mut(), 0);
|
||||||
|
if item_count == 0 {
|
||||||
|
self.drop_data = DropData::None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut paths = Vec::with_capacity(item_count as usize);
|
||||||
|
|
||||||
|
for i in 0..item_count {
|
||||||
|
let characters = DragQueryFileW(hdrop, i, null_mut(), 0);
|
||||||
|
let buffer_size = characters as usize + 1;
|
||||||
|
let mut buffer = Vec::<u16>::with_capacity(buffer_size);
|
||||||
|
|
||||||
|
DragQueryFileW(hdrop, i, transmute(buffer.spare_capacity_mut().as_mut_ptr()), buffer_size as u32);
|
||||||
|
buffer.set_len(buffer_size);
|
||||||
|
|
||||||
|
paths.push(OsString::from_wide(&buffer[..characters as usize]).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
self.drop_data = DropData::Files(paths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn query_interface(
|
||||||
|
this: *mut IUnknown,
|
||||||
|
riid: REFIID,
|
||||||
|
ppvObject: *mut *mut winapi::ctypes::c_void,
|
||||||
|
) -> HRESULT
|
||||||
|
{
|
||||||
|
if IsEqualIID(&*riid, &IUnknown::uuidof()) || IsEqualIID(&*riid, &IDropTarget::uuidof()){
|
||||||
|
Self::add_ref(this);
|
||||||
|
*ppvObject = this as *mut winapi::ctypes::c_void;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn add_ref(this: *mut IUnknown) -> ULONG {
|
||||||
|
let arc = Rc::from_raw(this);
|
||||||
|
let result = Rc::strong_count(&arc) + 1;
|
||||||
|
let _ = Rc::into_raw(arc);
|
||||||
|
|
||||||
|
Rc::increment_strong_count(this);
|
||||||
|
|
||||||
|
result as ULONG
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn release(this: *mut IUnknown) -> ULONG {
|
||||||
|
let arc = Rc::from_raw(this);
|
||||||
|
let result = Rc::strong_count(&arc) - 1;
|
||||||
|
let _ = Rc::into_raw(arc);
|
||||||
|
|
||||||
|
Rc::decrement_strong_count(this);
|
||||||
|
|
||||||
|
result as ULONG
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn drag_enter(
|
||||||
|
this: *mut IDropTarget,
|
||||||
|
pDataObj: *const IDataObject,
|
||||||
|
grfKeyState: DWORD,
|
||||||
|
pt: POINTL,
|
||||||
|
pdwEffect: *mut DWORD,
|
||||||
|
) -> HRESULT
|
||||||
|
{
|
||||||
|
let drop_target = &mut *(this as *mut DropTarget);
|
||||||
|
let Some(window_state) = drop_target.window_state.upgrade() else {
|
||||||
|
return E_UNEXPECTED;
|
||||||
|
};
|
||||||
|
|
||||||
|
let modifiers = window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
|
||||||
|
|
||||||
|
drop_target.parse_coordinates(pt);
|
||||||
|
drop_target.parse_drop_data(&*pDataObj);
|
||||||
|
|
||||||
|
let event = MouseEvent::DragEntered {
|
||||||
|
position: drop_target.drag_position,
|
||||||
|
modifiers,
|
||||||
|
data: drop_target.drop_data.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
drop_target.on_event(Some(pdwEffect), event);
|
||||||
|
S_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn drag_over(
|
||||||
|
this: *mut IDropTarget,
|
||||||
|
grfKeyState: DWORD,
|
||||||
|
pt: POINTL,
|
||||||
|
pdwEffect: *mut DWORD,
|
||||||
|
) -> HRESULT
|
||||||
|
{
|
||||||
|
let drop_target = &mut *(this as *mut DropTarget);
|
||||||
|
let Some(window_state) = drop_target.window_state.upgrade() else {
|
||||||
|
return E_UNEXPECTED;
|
||||||
|
};
|
||||||
|
|
||||||
|
let modifiers = window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
|
||||||
|
|
||||||
|
drop_target.parse_coordinates(pt);
|
||||||
|
|
||||||
|
let event = MouseEvent::DragMoved {
|
||||||
|
position: drop_target.drag_position,
|
||||||
|
modifiers,
|
||||||
|
data: drop_target.drop_data.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
drop_target.on_event(Some(pdwEffect), event);
|
||||||
|
S_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn drag_leave(this: *mut IDropTarget) -> HRESULT {
|
||||||
|
let drop_target = &mut *(this as *mut DropTarget);
|
||||||
|
drop_target.on_event(None, MouseEvent::DragLeft);
|
||||||
|
S_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn drop(
|
||||||
|
this: *mut IDropTarget,
|
||||||
|
pDataObj: *const IDataObject,
|
||||||
|
grfKeyState: DWORD,
|
||||||
|
pt: POINTL,
|
||||||
|
pdwEffect: *mut DWORD,
|
||||||
|
) -> HRESULT
|
||||||
|
{
|
||||||
|
let drop_target = &mut *(this as *mut DropTarget);
|
||||||
|
let Some(window_state) = drop_target.window_state.upgrade() else {
|
||||||
|
return E_UNEXPECTED;
|
||||||
|
};
|
||||||
|
|
||||||
|
let modifiers = window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
|
||||||
|
|
||||||
|
drop_target.parse_coordinates(pt);
|
||||||
|
drop_target.parse_drop_data(&*pDataObj);
|
||||||
|
|
||||||
|
let event = MouseEvent::DragDropped {
|
||||||
|
position: drop_target.drag_position,
|
||||||
|
modifiers,
|
||||||
|
data: drop_target.drop_data.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
drop_target.on_event(Some(pdwEffect), event);
|
||||||
|
S_OK
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod drop_target;
|
||||||
mod keyboard;
|
mod keyboard;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,9 @@
|
||||||
use winapi::Interface;
|
use winapi::shared::guiddef::GUID;
|
||||||
use winapi::shared::guiddef::{GUID, REFIID, IsEqualIID};
|
use winapi::shared::minwindef::{ATOM, FALSE, LPARAM, LRESULT, UINT, WPARAM};
|
||||||
use winapi::shared::minwindef::{ATOM, FALSE, LPARAM, LRESULT, UINT, WPARAM, DWORD};
|
use winapi::shared::windef::{HWND, RECT};
|
||||||
use winapi::shared::ntdef::{HRESULT, ULONG};
|
|
||||||
use winapi::shared::windef::{HWND, RECT, POINTL};
|
|
||||||
use winapi::shared::winerror::{S_OK, E_NOINTERFACE, E_UNEXPECTED};
|
|
||||||
use winapi::shared::wtypes::DVASPECT_CONTENT;
|
|
||||||
use winapi::um::combaseapi::CoCreateGuid;
|
use winapi::um::combaseapi::CoCreateGuid;
|
||||||
use winapi::um::objidl::{IDataObject, STGMEDIUM, FORMATETC, TYMED_HGLOBAL};
|
|
||||||
use winapi::um::ole2::{RegisterDragDrop, OleInitialize, RevokeDragDrop};
|
use winapi::um::ole2::{RegisterDragDrop, OleInitialize, RevokeDragDrop};
|
||||||
use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl, LPDROPTARGET, DROPEFFECT_COPY, DROPEFFECT_NONE, DROPEFFECT_MOVE, DROPEFFECT_LINK, DROPEFFECT_SCROLL};
|
use winapi::um::oleidl::LPDROPTARGET;
|
||||||
use winapi::um::shellapi::DragQueryFileW;
|
|
||||||
use winapi::um::unknwnbase::{IUnknownVtbl, IUnknown};
|
|
||||||
use winapi::um::winuser::{
|
use winapi::um::winuser::{
|
||||||
AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW,
|
AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW,
|
||||||
GetDpiForWindow, GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, RegisterClassW,
|
GetDpiForWindow, GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, RegisterClassW,
|
||||||
|
@ -22,18 +15,16 @@ use winapi::um::winuser::{
|
||||||
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP,
|
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP,
|
||||||
WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD,
|
WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD,
|
||||||
WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE,
|
WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE,
|
||||||
XBUTTON1, XBUTTON2, CF_HDROP,
|
XBUTTON1, XBUTTON2,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell, Ref, RefMut};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::ffi::{c_void, OsStr, OsString};
|
use std::ffi::{c_void, OsStr};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::transmute;
|
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
use std::os::windows::prelude::OsStringExt;
|
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::Rc;
|
||||||
|
|
||||||
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle};
|
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle};
|
||||||
|
|
||||||
|
@ -41,9 +32,10 @@ const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent,
|
Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent,
|
||||||
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, DropEffect, EventStatus, DropData, Point,
|
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::drop_target::DropTarget;
|
||||||
use super::keyboard::KeyboardState;
|
use super::keyboard::KeyboardState;
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
|
@ -458,7 +450,7 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) {
|
||||||
/// because of the Windows message loops' reentrant nature. Care still needs to be taken to prevent
|
/// because of the Windows message loops' reentrant nature. Care still needs to be taken to prevent
|
||||||
/// `handler` from indirectly triggering other events that would also need to be handled using
|
/// `handler` from indirectly triggering other events that would also need to be handled using
|
||||||
/// `handler`.
|
/// `handler`.
|
||||||
struct WindowState {
|
pub(super) struct WindowState {
|
||||||
/// The HWND belonging to this window. The window's actual state is stored in the `WindowState`
|
/// The HWND belonging to this window. The window's actual state is stored in the `WindowState`
|
||||||
/// struct associated with this HWND through `unsafe { GetWindowLongPtrW(self.hwnd,
|
/// struct associated with this HWND through `unsafe { GetWindowLongPtrW(self.hwnd,
|
||||||
/// GWLP_USERDATA) } as *const WindowState`.
|
/// GWLP_USERDATA) } as *const WindowState`.
|
||||||
|
@ -486,10 +478,22 @@ struct WindowState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowState {
|
impl WindowState {
|
||||||
fn create_window(&self) -> Window {
|
pub(super) fn create_window(&self) -> Window {
|
||||||
Window { state: self }
|
Window { state: self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn window_info(&self) -> Ref<WindowInfo> {
|
||||||
|
self.window_info.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn keyboard_state(&self) -> Ref<KeyboardState> {
|
||||||
|
self.keyboard_state.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handler_mut(&self) -> RefMut<Option<Box<dyn WindowHandler>>> {
|
||||||
|
self.handler.borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle a deferred task as described in [`Self::deferred_tasks
|
/// Handle a deferred task as described in [`Self::deferred_tasks
|
||||||
pub(self) fn handle_deferred_task(&self, task: WindowTask) {
|
pub(self) fn handle_deferred_task(&self, task: WindowTask) {
|
||||||
match task {
|
match task {
|
||||||
|
@ -530,7 +534,7 @@ impl WindowState {
|
||||||
/// Tasks that must be deferred until the end of [`wnd_proc()`] to avoid reentrant `WindowState`
|
/// Tasks that must be deferred until the end of [`wnd_proc()`] to avoid reentrant `WindowState`
|
||||||
/// borrows. See the docstring on [`WindowState::deferred_tasks`] for more information.
|
/// borrows. See the docstring on [`WindowState::deferred_tasks`] for more information.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum WindowTask {
|
pub(super) enum WindowTask {
|
||||||
/// Resize the window to the given size. The size is in logical pixels. DPI scaling is applied
|
/// Resize the window to the given size. The size is in logical pixels. DPI scaling is applied
|
||||||
/// automatically.
|
/// automatically.
|
||||||
Resize(Size),
|
Resize(Size),
|
||||||
|
@ -787,247 +791,3 @@ unsafe impl HasRawWindowHandle for Window<'_> {
|
||||||
pub fn copy_to_clipboard(data: &str) {
|
pub fn copy_to_clipboard(data: &str) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
// These function pointers have to be stored in a (const) variable before they can be transmuted
|
|
||||||
const DRAG_ENTER_PTR: unsafe extern "system" fn(this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drag_enter;
|
|
||||||
const DRAG_OVER_PTR: unsafe extern "system" fn(this: *mut IDropTarget, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drag_over;
|
|
||||||
const DROP_PTR: unsafe extern "system" fn(this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drop;
|
|
||||||
const DROP_TARGET_VTBL: IDropTargetVtbl = IDropTargetVtbl {
|
|
||||||
parent: IUnknownVtbl {
|
|
||||||
QueryInterface: DropTarget::query_interface,
|
|
||||||
AddRef: DropTarget::add_ref,
|
|
||||||
Release: DropTarget::release,
|
|
||||||
},
|
|
||||||
DragEnter: unsafe { transmute(DRAG_ENTER_PTR) },
|
|
||||||
DragOver: unsafe { transmute(DRAG_OVER_PTR) },
|
|
||||||
DragLeave: DropTarget::drag_leave,
|
|
||||||
Drop: unsafe { transmute(DROP_PTR) },
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct DropTarget {
|
|
||||||
base: IDropTarget,
|
|
||||||
|
|
||||||
window_state: Weak<WindowState>,
|
|
||||||
|
|
||||||
// These are cached since DragOver and DragLeave callbacks don't provide them,
|
|
||||||
// and handling drag move events gets awkward on the client end otherwise
|
|
||||||
drag_position: Point,
|
|
||||||
drop_data: DropData,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DropTarget {
|
|
||||||
fn new(window_state: Weak<WindowState>) -> Self {
|
|
||||||
Self {
|
|
||||||
base: IDropTarget { lpVtbl: &DROP_TARGET_VTBL },
|
|
||||||
|
|
||||||
window_state,
|
|
||||||
|
|
||||||
drag_position: Point::new(0.0, 0.0),
|
|
||||||
drop_data: DropData::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_event(&self, pdwEffect: Option<*mut DWORD>, event: MouseEvent) {
|
|
||||||
let Some(window_state) = self.window_state.upgrade() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut window = window_state.create_window();
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
|
||||||
|
|
||||||
let event = Event::Mouse(event);
|
|
||||||
let event_status = window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event);
|
|
||||||
|
|
||||||
if let Some(pdwEffect) = pdwEffect {
|
|
||||||
match event_status {
|
|
||||||
EventStatus::AcceptDrop(DropEffect::Copy) => *pdwEffect = DROPEFFECT_COPY,
|
|
||||||
EventStatus::AcceptDrop(DropEffect::Move) => *pdwEffect = DROPEFFECT_MOVE,
|
|
||||||
EventStatus::AcceptDrop(DropEffect::Link) => *pdwEffect = DROPEFFECT_LINK,
|
|
||||||
EventStatus::AcceptDrop(DropEffect::Scroll) => *pdwEffect = DROPEFFECT_SCROLL,
|
|
||||||
_ => *pdwEffect = DROPEFFECT_NONE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_coordinates(&mut self, pt: POINTL) {
|
|
||||||
let Some(window_state) = self.window_state.upgrade() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let phy_point = PhyPoint::new(pt.x, pt.y);
|
|
||||||
self.drag_position = phy_point.to_logical(&window_state.window_info.borrow());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_drop_data(&mut self, data_object: &IDataObject) {
|
|
||||||
let format = FORMATETC {
|
|
||||||
cfFormat: CF_HDROP as u16,
|
|
||||||
ptd: null_mut(),
|
|
||||||
dwAspect: DVASPECT_CONTENT,
|
|
||||||
lindex: -1,
|
|
||||||
tymed: TYMED_HGLOBAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut medium = STGMEDIUM {
|
|
||||||
tymed: 0,
|
|
||||||
u: null_mut(),
|
|
||||||
pUnkForRelease: null_mut(),
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let hresult = data_object.GetData(&format, &mut medium);
|
|
||||||
if hresult != S_OK {
|
|
||||||
self.drop_data = DropData::None;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let hdrop = transmute((*medium.u).hGlobal());
|
|
||||||
|
|
||||||
let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, null_mut(), 0);
|
|
||||||
if item_count == 0 {
|
|
||||||
self.drop_data = DropData::None;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut paths = Vec::with_capacity(item_count as usize);
|
|
||||||
|
|
||||||
for i in 0..item_count {
|
|
||||||
let characters = DragQueryFileW(hdrop, i, null_mut(), 0);
|
|
||||||
let buffer_size = characters as usize + 1;
|
|
||||||
let mut buffer = Vec::<u16>::with_capacity(buffer_size);
|
|
||||||
|
|
||||||
DragQueryFileW(hdrop, i, transmute(buffer.spare_capacity_mut().as_mut_ptr()), buffer_size as u32);
|
|
||||||
buffer.set_len(buffer_size);
|
|
||||||
|
|
||||||
paths.push(OsString::from_wide(&buffer[..characters as usize]).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
self.drop_data = DropData::Files(paths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "system" fn query_interface(
|
|
||||||
this: *mut IUnknown,
|
|
||||||
riid: REFIID,
|
|
||||||
ppvObject: *mut *mut winapi::ctypes::c_void,
|
|
||||||
) -> HRESULT
|
|
||||||
{
|
|
||||||
if IsEqualIID(&*riid, &IUnknown::uuidof()) || IsEqualIID(&*riid, &IDropTarget::uuidof()){
|
|
||||||
Self::add_ref(this);
|
|
||||||
*ppvObject = this as *mut winapi::ctypes::c_void;
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return E_NOINTERFACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "system" fn add_ref(this: *mut IUnknown) -> ULONG {
|
|
||||||
let arc = Rc::from_raw(this);
|
|
||||||
let result = Rc::strong_count(&arc) + 1;
|
|
||||||
let _ = Rc::into_raw(arc);
|
|
||||||
|
|
||||||
Rc::increment_strong_count(this);
|
|
||||||
|
|
||||||
result as ULONG
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "system" fn release(this: *mut IUnknown) -> ULONG {
|
|
||||||
let arc = Rc::from_raw(this);
|
|
||||||
let result = Rc::strong_count(&arc) - 1;
|
|
||||||
let _ = Rc::into_raw(arc);
|
|
||||||
|
|
||||||
Rc::decrement_strong_count(this);
|
|
||||||
|
|
||||||
result as ULONG
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "system" fn drag_enter(
|
|
||||||
this: *mut IDropTarget,
|
|
||||||
pDataObj: *const IDataObject,
|
|
||||||
grfKeyState: DWORD,
|
|
||||||
pt: POINTL,
|
|
||||||
pdwEffect: *mut DWORD,
|
|
||||||
) -> HRESULT
|
|
||||||
{
|
|
||||||
let drop_target = &mut *(this as *mut DropTarget);
|
|
||||||
let Some(window_state) = drop_target.window_state.upgrade() else {
|
|
||||||
return E_UNEXPECTED;
|
|
||||||
};
|
|
||||||
|
|
||||||
let modifiers = window_state.keyboard_state.borrow().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
|
|
||||||
|
|
||||||
drop_target.parse_coordinates(pt);
|
|
||||||
drop_target.parse_drop_data(&*pDataObj);
|
|
||||||
|
|
||||||
let event = MouseEvent::DragEntered {
|
|
||||||
position: drop_target.drag_position,
|
|
||||||
modifiers,
|
|
||||||
data: drop_target.drop_data.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
drop_target.on_event(Some(pdwEffect), event);
|
|
||||||
S_OK
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "system" fn drag_over(
|
|
||||||
this: *mut IDropTarget,
|
|
||||||
grfKeyState: DWORD,
|
|
||||||
pt: POINTL,
|
|
||||||
pdwEffect: *mut DWORD,
|
|
||||||
) -> HRESULT
|
|
||||||
{
|
|
||||||
let drop_target = &mut *(this as *mut DropTarget);
|
|
||||||
let Some(window_state) = drop_target.window_state.upgrade() else {
|
|
||||||
return E_UNEXPECTED;
|
|
||||||
};
|
|
||||||
|
|
||||||
let modifiers = window_state.keyboard_state.borrow().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
|
|
||||||
|
|
||||||
drop_target.parse_coordinates(pt);
|
|
||||||
|
|
||||||
let event = MouseEvent::DragMoved {
|
|
||||||
position: drop_target.drag_position,
|
|
||||||
modifiers,
|
|
||||||
data: drop_target.drop_data.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
drop_target.on_event(Some(pdwEffect), event);
|
|
||||||
S_OK
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "system" fn drag_leave(this: *mut IDropTarget) -> HRESULT {
|
|
||||||
let drop_target = &mut *(this as *mut DropTarget);
|
|
||||||
drop_target.on_event(None, MouseEvent::DragLeft);
|
|
||||||
S_OK
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "system" fn drop(
|
|
||||||
this: *mut IDropTarget,
|
|
||||||
pDataObj: *const IDataObject,
|
|
||||||
grfKeyState: DWORD,
|
|
||||||
pt: POINTL,
|
|
||||||
pdwEffect: *mut DWORD,
|
|
||||||
) -> HRESULT
|
|
||||||
{
|
|
||||||
let drop_target = &mut *(this as *mut DropTarget);
|
|
||||||
let Some(window_state) = drop_target.window_state.upgrade() else {
|
|
||||||
return E_UNEXPECTED;
|
|
||||||
};
|
|
||||||
|
|
||||||
let modifiers = window_state.keyboard_state.borrow().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
|
|
||||||
|
|
||||||
drop_target.parse_coordinates(pt);
|
|
||||||
drop_target.parse_drop_data(&*pDataObj);
|
|
||||||
|
|
||||||
let event = MouseEvent::DragDropped {
|
|
||||||
position: drop_target.drag_position,
|
|
||||||
modifiers,
|
|
||||||
data: drop_target.drop_data.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
drop_target.on_event(Some(pdwEffect), event);
|
|
||||||
S_OK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue