From 1921d477ff6520182498e73b3bc960cf9f84478d Mon Sep 17 00:00:00 2001 From: Jussi Viiri Date: Mon, 19 Jun 2023 19:11:39 +0300 Subject: [PATCH] Start implementing drag and drop for Mac --- src/macos/keyboard.rs | 4 ++ src/macos/mod.rs | 8 +++ src/macos/view.rs | 116 ++++++++++++++++++++++++++++++++++++++++-- src/macos/window.rs | 4 ++ 4 files changed, 129 insertions(+), 3 deletions(-) diff --git a/src/macos/keyboard.rs b/src/macos/keyboard.rs index ba2bdc4..7e41c95 100644 --- a/src/macos/keyboard.rs +++ b/src/macos/keyboard.rs @@ -273,6 +273,10 @@ impl KeyboardState { KeyboardState { last_mods } } + pub(crate) fn last_mods(&self) -> NSEventModifierFlags { + self.last_mods + } + pub(crate) fn process_native_event(&mut self, event: id) -> Option { unsafe { let event_type = event.eventType(); diff --git a/src/macos/mod.rs b/src/macos/mod.rs index e1fa7b8..02a53c8 100644 --- a/src/macos/mod.rs +++ b/src/macos/mod.rs @@ -2,4 +2,12 @@ mod keyboard; mod view; mod window; +use cocoa::foundation::NSUInteger; pub use window::*; + +const NSDragOperationNone: NSUInteger = 0; +const NSDragOperationCopy: NSUInteger = 1; +const NSDragOperationLink: NSUInteger = 2; +const NSDragOperationGeneric: NSUInteger = 4; +const NSDragOperationMove: NSUInteger = 16; + diff --git a/src/macos/view.rs b/src/macos/view.rs index 52c9311..ded1a9b 100644 --- a/src/macos/view.rs +++ b/src/macos/view.rs @@ -1,8 +1,8 @@ use std::ffi::c_void; -use cocoa::appkit::{NSEvent, NSView, NSWindow}; +use cocoa::appkit::{NSEvent, NSView, NSWindow, NSFilenamesPboardType}; use cocoa::base::{id, nil, BOOL, NO, YES}; -use cocoa::foundation::{NSArray, NSPoint, NSRect, NSSize}; +use cocoa::foundation::{NSArray, NSPoint, NSRect, NSSize, NSUInteger}; use objc::{ class, @@ -16,9 +16,10 @@ use uuid::Uuid; use crate::MouseEvent::{ButtonPressed, ButtonReleased}; use crate::{ Event, EventStatus, MouseButton, MouseEvent, Point, ScrollDelta, Size, WindowEvent, WindowInfo, - WindowOpenOptions, + WindowOpenOptions, DropData, DropEffect, }; +use super::{NSDragOperationGeneric, NSDragOperationCopy, NSDragOperationMove, NSDragOperationLink, NSDragOperationNone}; use super::keyboard::make_modifiers; use super::window::WindowState; @@ -105,6 +106,8 @@ pub(super) unsafe fn create_view(window_options: &WindowOpenOptions) -> id { view.initWithFrame_(NSRect::new(NSPoint::new(0., 0.), NSSize::new(size.width, size.height))); + let _: id = msg_send![view, registerForDraggedTypes: NSArray::arrayWithObjects(nil, &[NSFilenamesPboardType])]; + view } @@ -154,6 +157,27 @@ unsafe fn create_view_class() -> &'static Class { view_did_change_backing_properties as extern "C" fn(&Object, Sel, id), ); + class.add_method( + sel!(draggingEntered:), + dragging_entered as extern "C" fn(&Object, Sel, id) -> NSUInteger, + ); + class.add_method( + sel!(prepareForDragOperation:), + prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + class.add_method( + sel!(performDragOperation:), + perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + class.add_method( + sel!(draggingUpdated:), + dragging_updated as extern "C" fn(&Object, Sel, id) -> NSUInteger, + ); + class.add_method( + sel!(draggingExited:), + dragging_exited as extern "C" fn(&Object, Sel, id), + ); + add_mouse_button_class_method!(class, mouseDown, ButtonPressed, MouseButton::Left); add_mouse_button_class_method!(class, mouseUp, ButtonReleased, MouseButton::Left); add_mouse_button_class_method!(class, rightMouseDown, ButtonPressed, MouseButton::Right); @@ -372,3 +396,89 @@ extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) { modifiers: make_modifiers(modifiers), })); } + +fn get_drag_position(sender: id) -> Point { + let point: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + Point::new(point.x, point.y) +} + +fn get_drop_data(sender: id) -> DropData { + DropData::None +} + +fn on_event(window_state: &mut WindowState, event: MouseEvent) -> NSUInteger { + let event_status = window_state.trigger_event(Event::Mouse(event)); + match event_status { + EventStatus::AcceptDrop(DropEffect::Copy) => NSDragOperationCopy, + EventStatus::AcceptDrop(DropEffect::Move) => NSDragOperationMove, + EventStatus::AcceptDrop(DropEffect::Link) => NSDragOperationLink, + EventStatus::AcceptDrop(DropEffect::Scroll) => NSDragOperationGeneric, + _ => NSDragOperationNone, + } +} + +extern "C" fn dragging_entered(this: &Object, _sel: Sel, sender: id) -> NSUInteger { + let state: &mut WindowState = unsafe { WindowState::from_field(this) }; + let modifiers = state.keyboard_state().last_mods(); + let drop_data = get_drop_data(sender); + + let event = MouseEvent::DragEntered { + position: get_drag_position(sender), + modifiers: make_modifiers(modifiers), + data: drop_data, + }; + + on_event(state, event) +} + +extern "C" fn dragging_updated(this: &Object, _sel: Sel, dragging_info: id) -> NSUInteger { + if let Some(drag_info) = unsafe { get_drag_info(dragging_info) } { + let state: &mut WindowState = unsafe { WindowState::from_field(this) }; + let event = WindowEvent::DragUpdated(drag_info); + state.trigger_event(Event::Window(event)); + } + 4 // NSDragOperationGeneric +} + +extern "C" fn prepare_for_drag_operation(_this: &Object, _sel: Sel, _dragging_info: id) -> BOOL { + YES +} + +extern "C" fn perform_drag_operation(this: &Object, _sel: Sel, dragging_info: id) -> BOOL { + if let Some(drag_info) = unsafe { get_drag_info(dragging_info) } { + let state: &mut WindowState = unsafe { WindowState::from_field(this) }; + let event = WindowEvent::DragPerformed(drag_info); + state.trigger_event(Event::Window(event)); + } + YES +} + +extern "C" fn dragging_exited(this: &Object, _sel: Sel, dragging_info: id) { + let drag_info = unsafe { get_drag_info(dragging_info) }; + let state: &mut WindowState = unsafe { WindowState::from_field(this) }; + let event = WindowEvent::DragExited(drag_info); + state.trigger_event(Event::Window(event)); +} + +unsafe fn get_drag_info(dragging_info: id) -> Option { + if dragging_info == nil { + return None; + } + + let pasteboard: id = msg_send![dragging_info, draggingPasteboard]; + let file_list: id = msg_send![pasteboard, propertyListForType: NSFilenamesPboardType]; + + if file_list == nil { + return None; + } + + let mut file_vec: Vec = vec![]; + let count = NSArray::count(file_list); + if count > 0 { + let data = NSArray::objectAtIndex(file_list, 0); + let str = from_nsstring(data); + file_vec.push(str); + } + + Some(DragInfo::FilesDragged { files: file_vec }) +} \ No newline at end of file diff --git a/src/macos/window.rs b/src/macos/window.rs index 54046dd..67e8d20 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -415,6 +415,10 @@ impl WindowState { } } + pub(super) fn keyboard_state(&self) -> &KeyboardState { + &self.keyboard_state + } + pub(super) fn process_native_key_event(&mut self, event: *mut Object) -> Option { self.keyboard_state.process_native_event(event) }