Merge pull request #149 from ilmai/drag-and-drop-mac
Drag and drop for Mac OS
This commit is contained in:
commit
d8e3b52acd
|
@ -273,6 +273,10 @@ impl KeyboardState {
|
||||||
KeyboardState { last_mods }
|
KeyboardState { last_mods }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn last_mods(&self) -> NSEventModifierFlags {
|
||||||
|
self.last_mods
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn process_native_event(&mut self, event: id) -> Option<KeyboardEvent> {
|
pub(crate) fn process_native_event(&mut self, event: id) -> Option<KeyboardEvent> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let event_type = event.eventType();
|
let event_type = event.eventType();
|
||||||
|
|
|
@ -2,4 +2,12 @@ mod keyboard;
|
||||||
mod view;
|
mod view;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
|
use cocoa::foundation::NSUInteger;
|
||||||
pub use window::*;
|
pub use window::*;
|
||||||
|
|
||||||
|
const NSDragOperationNone: NSUInteger = 0;
|
||||||
|
const NSDragOperationCopy: NSUInteger = 1;
|
||||||
|
const NSDragOperationLink: NSUInteger = 2;
|
||||||
|
const NSDragOperationGeneric: NSUInteger = 4;
|
||||||
|
const NSDragOperationMove: NSUInteger = 16;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::ffi::c_void;
|
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::base::{id, nil, BOOL, NO, YES};
|
||||||
use cocoa::foundation::{NSArray, NSPoint, NSRect, NSSize};
|
use cocoa::foundation::{NSArray, NSPoint, NSRect, NSSize, NSUInteger};
|
||||||
|
|
||||||
use objc::{
|
use objc::{
|
||||||
class,
|
class,
|
||||||
|
@ -16,10 +16,11 @@ use uuid::Uuid;
|
||||||
use crate::MouseEvent::{ButtonPressed, ButtonReleased};
|
use crate::MouseEvent::{ButtonPressed, ButtonReleased};
|
||||||
use crate::{
|
use crate::{
|
||||||
Event, EventStatus, MouseButton, MouseEvent, Point, ScrollDelta, Size, WindowEvent, WindowInfo,
|
Event, EventStatus, MouseButton, MouseEvent, Point, ScrollDelta, Size, WindowEvent, WindowInfo,
|
||||||
WindowOpenOptions,
|
WindowOpenOptions, DropData, DropEffect,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::keyboard::make_modifiers;
|
use super::{NSDragOperationGeneric, NSDragOperationCopy, NSDragOperationMove, NSDragOperationLink, NSDragOperationNone};
|
||||||
|
use super::keyboard::{make_modifiers, from_nsstring};
|
||||||
use super::window::WindowState;
|
use super::window::WindowState;
|
||||||
|
|
||||||
/// Name of the field used to store the `WindowState` pointer.
|
/// Name of the field used to store the `WindowState` pointer.
|
||||||
|
@ -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)));
|
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
|
view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,6 +157,27 @@ unsafe fn create_view_class() -> &'static Class {
|
||||||
view_did_change_backing_properties as extern "C" fn(&Object, Sel, id),
|
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, mouseDown, ButtonPressed, MouseButton::Left);
|
||||||
add_mouse_button_class_method!(class, mouseUp, ButtonReleased, MouseButton::Left);
|
add_mouse_button_class_method!(class, mouseUp, ButtonReleased, MouseButton::Left);
|
||||||
add_mouse_button_class_method!(class, rightMouseDown, ButtonPressed, MouseButton::Right);
|
add_mouse_button_class_method!(class, rightMouseDown, ButtonPressed, MouseButton::Right);
|
||||||
|
@ -372,3 +396,103 @@ extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) {
|
||||||
modifiers: make_modifiers(modifiers),
|
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 {
|
||||||
|
if sender == nil {
|
||||||
|
return DropData::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let pasteboard: id = msg_send![sender, draggingPasteboard];
|
||||||
|
let file_list: id = msg_send![pasteboard, propertyListForType: NSFilenamesPboardType];
|
||||||
|
|
||||||
|
if file_list == nil {
|
||||||
|
return DropData::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut files = vec![];
|
||||||
|
for i in 0..NSArray::count(file_list) {
|
||||||
|
let data = NSArray::objectAtIndex(file_list, i);
|
||||||
|
files.push(from_nsstring(data).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
DropData::Files(files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 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::DragMoved {
|
||||||
|
position: get_drag_position(sender),
|
||||||
|
modifiers: make_modifiers(modifiers),
|
||||||
|
data: drop_data,
|
||||||
|
};
|
||||||
|
|
||||||
|
on_event(state, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn prepare_for_drag_operation(_this: &Object, _sel: Sel, sender: id) -> BOOL {
|
||||||
|
// Always accept drag operation if we get this far
|
||||||
|
// This function won't be called unless dragging_entered/updated
|
||||||
|
// has returned an acceptable operation
|
||||||
|
YES
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn perform_drag_operation(this: &Object, _sel: Sel, sender: id) -> BOOL {
|
||||||
|
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::DragDropped {
|
||||||
|
position: get_drag_position(sender),
|
||||||
|
modifiers: make_modifiers(modifiers),
|
||||||
|
data: drop_data,
|
||||||
|
};
|
||||||
|
|
||||||
|
let event_status = state.trigger_event(Event::Mouse(event));
|
||||||
|
match event_status {
|
||||||
|
EventStatus::AcceptDrop(_) => YES,
|
||||||
|
_ => NO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn dragging_exited(this: &Object, _sel: Sel, sender: id) {
|
||||||
|
let state: &mut WindowState = unsafe { WindowState::from_field(this) };
|
||||||
|
let modifiers = state.keyboard_state().last_mods();
|
||||||
|
let drop_data = get_drop_data(sender);
|
||||||
|
|
||||||
|
on_event(state, MouseEvent::DragLeft);
|
||||||
|
}
|
||||||
|
|
|
@ -418,6 +418,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<KeyboardEvent> {
|
pub(super) fn process_native_key_event(&mut self, event: *mut Object) -> Option<KeyboardEvent> {
|
||||||
self.keyboard_state.process_native_event(event)
|
self.keyboard_state.process_native_event(event)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue