use std::{ffi::CStr, path::PathBuf};
use objc2::{
runtime::{Bool, ProtocolObject},
DeclaredClass,
};
use objc2_app_kit::{NSDragOperation, NSDraggingInfo, NSFilenamesPboardType};
use objc2_foundation::{NSArray, NSPoint, NSRect, NSString};
use crate::DragDropEvent;
use super::WryWebView;
pub(crate) unsafe fn collect_paths(drag_info: &ProtocolObject<dyn NSDraggingInfo>) -> Vec<PathBuf> {
let pb = drag_info.draggingPasteboard();
let mut drag_drop_paths = Vec::new();
let types = NSArray::arrayWithObject(NSFilenamesPboardType);
if pb.availableTypeFromArray(&types).is_some() {
let paths = pb.propertyListForType(NSFilenamesPboardType).unwrap();
let paths = paths.downcast::<NSArray>().unwrap();
for path in paths {
let path = path.downcast::<NSString>().unwrap();
let path = CStr::from_ptr(path.UTF8String()).to_string_lossy();
drag_drop_paths.push(PathBuf::from(path.into_owned()));
}
}
drag_drop_paths
}
pub(crate) fn dragging_entered(
this: &WryWebView,
drag_info: &ProtocolObject<dyn NSDraggingInfo>,
) -> NSDragOperation {
let paths = unsafe { collect_paths(drag_info) };
let dl: NSPoint = unsafe { drag_info.draggingLocation() };
let frame: NSRect = this.frame();
let position = (dl.x as i32, (frame.size.height - dl.y) as i32);
let listener = &this.ivars().drag_drop_handler;
if !listener(DragDropEvent::Enter { paths, position }) {
unsafe { objc2::msg_send![super(this), draggingEntered: drag_info] }
} else {
NSDragOperation::Copy
}
}
pub(crate) fn dragging_updated(
this: &WryWebView,
drag_info: &ProtocolObject<dyn NSDraggingInfo>,
) -> NSDragOperation {
let dl: NSPoint = unsafe { drag_info.draggingLocation() };
let frame: NSRect = this.frame();
let position = (dl.x as i32, (frame.size.height - dl.y) as i32);
let listener = &this.ivars().drag_drop_handler;
if !listener(DragDropEvent::Over { position }) {
unsafe {
let os_operation = objc2::msg_send![super(this), draggingUpdated: drag_info];
if os_operation == NSDragOperation::None {
NSDragOperation::Copy
} else {
os_operation
}
}
} else {
NSDragOperation::Copy
}
}
pub(crate) fn perform_drag_operation(
this: &WryWebView,
drag_info: &ProtocolObject<dyn NSDraggingInfo>,
) -> Bool {
let paths = unsafe { collect_paths(drag_info) };
let dl: NSPoint = unsafe { drag_info.draggingLocation() };
let frame: NSRect = this.frame();
let position = (dl.x as i32, (frame.size.height - dl.y) as i32);
let listener = &this.ivars().drag_drop_handler;
if !listener(DragDropEvent::Drop { paths, position }) {
unsafe { objc2::msg_send![super(this), performDragOperation: drag_info] }
} else {
Bool::YES
}
}
pub(crate) fn dragging_exited(this: &WryWebView, drag_info: &ProtocolObject<dyn NSDraggingInfo>) {
let listener = &this.ivars().drag_drop_handler;
if !listener(DragDropEvent::Leave) {
unsafe { objc2::msg_send![super(this), draggingExited: drag_info] }
}
}