1
0
Fork 0

Janky first version of drag and drop

Just recognized as a drop target
This commit is contained in:
Jussi Viiri 2023-05-31 22:43:30 +03:00
parent c129b12ead
commit 0eff674319
3 changed files with 177 additions and 5 deletions

View file

@ -29,7 +29,7 @@ xcb-util = { version = "0.3", features = ["icccm"] }
nix = "0.22.0" nix = "0.22.0"
[target.'cfg(target_os="windows")'.dependencies] [target.'cfg(target_os="windows")'.dependencies]
winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi"] } winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi", "ole2", "oleidl", "winerror"] }
uuid = { version = "0.8", features = ["v4"], optional = true } uuid = { version = "0.8", features = ["v4"], optional = true }
[target.'cfg(target_os="macos")'.dependencies] [target.'cfg(target_os="macos")'.dependencies]

View file

@ -75,6 +75,12 @@ pub enum MouseEvent {
/// ///
/// May not be available on all platforms. /// May not be available on all platforms.
CursorLeft, CursorLeft,
// TODO: Document
DragEntered,
DragMoved,
DragLeft,
DragDropped,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -1,7 +1,14 @@
use winapi::shared::guiddef::GUID; use winapi::Interface;
use winapi::shared::minwindef::{ATOM, FALSE, LPARAM, LRESULT, UINT, WPARAM}; use winapi::shared::guiddef::{GUID, REFIID, IsEqualIID};
use winapi::shared::windef::{HWND, RECT}; use winapi::shared::minwindef::{ATOM, FALSE, LPARAM, LRESULT, UINT, WPARAM, DWORD};
use winapi::shared::ntdef::{HRESULT, ULONG};
use winapi::shared::windef::{HWND, RECT, POINTL};
use winapi::shared::winerror::{S_OK, E_NOINTERFACE};
use winapi::um::combaseapi::CoCreateGuid; use winapi::um::combaseapi::CoCreateGuid;
use winapi::um::objidl::IDataObject;
use winapi::um::ole2::{RegisterDragDrop, OleInitialize};
use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl, LPDROPTARGET, DROPEFFECT_COPY};
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,
@ -20,9 +27,11 @@ use std::cell::{Cell, RefCell};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ffi::{c_void, OsStr}; 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::ptr::null_mut; use std::ptr::null_mut;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle}; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle};
@ -460,6 +469,7 @@ struct WindowState {
handler: RefCell<Option<Box<dyn WindowHandler>>>, handler: RefCell<Option<Box<dyn WindowHandler>>>,
scale_policy: WindowScalePolicy, scale_policy: WindowScalePolicy,
dw_style: u32, dw_style: u32,
_drop_target: Option<Arc<DropTarget>>,
/// Tasks that should be executed at the end of `wnd_proc`. This is needed to avoid mutably /// Tasks that should be executed at the end of `wnd_proc`. This is needed to avoid mutably
/// borrowing the fields from `WindowState` more than once. For instance, when the window /// borrowing the fields from `WindowState` more than once. For instance, when the window
@ -666,6 +676,7 @@ impl Window<'_> {
handler: RefCell::new(None), handler: RefCell::new(None),
scale_policy: options.scale, scale_policy: options.scale,
dw_style: flags, dw_style: flags,
_drop_target: None,
deferred_tasks: RefCell::new(VecDeque::with_capacity(4)), deferred_tasks: RefCell::new(VecDeque::with_capacity(4)),
@ -711,7 +722,19 @@ impl Window<'_> {
None None
}; };
SetWindowLongPtrW(hwnd, GWLP_USERDATA, Box::into_raw(window_state) as *const _ as _); let window_state_ptr = Box::into_raw(window_state);
// TODO: Error handling
OleInitialize(null_mut());
let drop_target = Arc::new(DropTarget::new(window_state_ptr));
// TODO: Error handling
RegisterDragDrop(hwnd, Arc::as_ptr(&drop_target) as LPDROPTARGET);
(*window_state_ptr)._drop_target = Some(drop_target);
SetWindowLongPtrW(hwnd, GWLP_USERDATA, window_state_ptr as *const _ as _);
SetTimer(hwnd, WIN_FRAME_TIMER, 15, None); SetTimer(hwnd, WIN_FRAME_TIMER, 15, None);
if let Some(mut new_rect) = new_rect { if let Some(mut new_rect) = new_rect {
@ -767,3 +790,146 @@ unsafe impl HasRawWindowHandle for Window<'_> {
pub fn copy_to_clipboard(data: &str) { pub fn copy_to_clipboard(data: &str) {
todo!() todo!()
} }
#[repr(C)]
pub struct DropTarget {
base: IDropTarget,
vtbl: Arc<IDropTargetVtbl>,
window_state: *mut WindowState,
}
impl DropTarget {
fn new(window_state: *mut WindowState) -> Self {
let vtbl = Arc::new(IDropTargetVtbl {
parent: IUnknownVtbl {
QueryInterface: Self::query_interface,
AddRef: Self::add_ref,
Release: Self::release,
},
DragEnter: Self::drag_enter,
DragOver: Self::drag_over,
DragLeave: Self::drag_leave,
Drop: Self::drop,
});
Self {
base: IDropTarget { lpVtbl: Arc::as_ptr(&vtbl) },
vtbl,
window_state,
}
}
unsafe extern "system" fn query_interface(
this: *mut IUnknown,
riid: REFIID,
ppvObject: *mut *mut winapi::ctypes::c_void,
) -> HRESULT
{
println!("query_interface");
if IsEqualIID(&*riid, &IUnknown::uuidof()) || IsEqualIID(&*riid, &IDropTarget::uuidof()){
Self::add_ref(this);
*ppvObject = unsafe { transmute(this) };
return S_OK;
}
return E_NOINTERFACE;
}
unsafe extern "system" fn add_ref(this: *mut IUnknown) -> ULONG {
let arc = Arc::from_raw(this);
let result = Arc::strong_count(&arc) + 1;
let _ = Arc::into_raw(arc);
Arc::increment_strong_count(this);
result as ULONG
}
unsafe extern "system" fn release(this: *mut IUnknown) -> ULONG {
let arc = Arc::from_raw(this);
let result = Arc::strong_count(&arc) - 1;
let _ = Arc::into_raw(arc);
Arc::decrement_strong_count(this);
result as ULONG
}
unsafe extern "system" fn drag_enter(
this: *mut IDropTarget,
pDataObj: *const IDataObject,
grfKeyState: DWORD,
pt: *const POINTL,
pdwEffect: *mut DWORD,
) -> HRESULT
{
let drop_target = &*(this as *mut DropTarget);
let window_state = &*drop_target.window_state;
let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
let event = Event::Mouse(MouseEvent::DragEntered {});
window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event);
*pdwEffect = DROPEFFECT_COPY;
S_OK
}
unsafe extern "system" fn drag_over(
this: *mut IDropTarget,
grfKeyState: DWORD,
pt: *const POINTL,
pdwEffect: *mut DWORD,
) -> HRESULT
{
let drop_target = &*(this as *mut DropTarget);
let window_state = &*drop_target.window_state;
let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
let event = Event::Mouse(MouseEvent::DragMoved {});
window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event);
*pdwEffect = DROPEFFECT_COPY;
S_OK
}
unsafe extern "system" fn drag_leave(this: *mut IDropTarget) -> HRESULT {
let drop_target = &*(this as *mut DropTarget);
let window_state = &*drop_target.window_state;
let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
let event = Event::Mouse(MouseEvent::DragLeft {});
window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event);
S_OK
}
unsafe extern "system" fn drop(
this: *mut IDropTarget,
pDataObj: *const IDataObject,
grfKeyState: DWORD,
pt: *const POINTL,
pdwEffect: *mut DWORD,
) -> HRESULT
{
let drop_target = &*(this as *mut DropTarget);
let window_state = &*drop_target.window_state;
let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
let event = Event::Mouse(MouseEvent::DragDropped {});
window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event);
*pdwEffect = DROPEFFECT_COPY;
S_OK
}
}