Add a draft of the mac platform file drag and drop events
This commit is contained in:
parent
4d5ca37edb
commit
0e48465adb
3 changed files with 165 additions and 16 deletions
|
@ -13,6 +13,7 @@ use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
|
path::PathBuf,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1048,6 +1049,19 @@ impl Deref for MouseExitEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub enum FileDropEvent {
|
||||||
|
#[default]
|
||||||
|
End,
|
||||||
|
Pending {
|
||||||
|
position: Point<Pixels>,
|
||||||
|
},
|
||||||
|
Submit {
|
||||||
|
position: Point<Pixels>,
|
||||||
|
paths: Vec<PathBuf>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum InputEvent {
|
pub enum InputEvent {
|
||||||
KeyDown(KeyDownEvent),
|
KeyDown(KeyDownEvent),
|
||||||
|
@ -1058,6 +1072,7 @@ pub enum InputEvent {
|
||||||
MouseMoved(MouseMoveEvent),
|
MouseMoved(MouseMoveEvent),
|
||||||
MouseExited(MouseExitEvent),
|
MouseExited(MouseExitEvent),
|
||||||
ScrollWheel(ScrollWheelEvent),
|
ScrollWheel(ScrollWheelEvent),
|
||||||
|
FileDrop(FileDropEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputEvent {
|
impl InputEvent {
|
||||||
|
@ -1071,6 +1086,10 @@ impl InputEvent {
|
||||||
InputEvent::MouseMoved(event) => Some(event.position),
|
InputEvent::MouseMoved(event) => Some(event.position),
|
||||||
InputEvent::MouseExited(event) => Some(event.position),
|
InputEvent::MouseExited(event) => Some(event.position),
|
||||||
InputEvent::ScrollWheel(event) => Some(event.position),
|
InputEvent::ScrollWheel(event) => Some(event.position),
|
||||||
|
InputEvent::FileDrop(FileDropEvent::End) => None,
|
||||||
|
InputEvent::FileDrop(
|
||||||
|
FileDropEvent::Pending { position } | FileDropEvent::Submit { position, .. },
|
||||||
|
) => Some(*position),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1084,6 +1103,7 @@ impl InputEvent {
|
||||||
InputEvent::MouseMoved(event) => Some(event),
|
InputEvent::MouseMoved(event) => Some(event),
|
||||||
InputEvent::MouseExited(event) => Some(event),
|
InputEvent::MouseExited(event) => Some(event),
|
||||||
InputEvent::ScrollWheel(event) => Some(event),
|
InputEvent::ScrollWheel(event) => Some(event),
|
||||||
|
InputEvent::FileDrop(event) => Some(event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1097,6 +1117,7 @@ impl InputEvent {
|
||||||
InputEvent::MouseMoved(_) => None,
|
InputEvent::MouseMoved(_) => None,
|
||||||
InputEvent::MouseExited(_) => None,
|
InputEvent::MouseExited(_) => None,
|
||||||
InputEvent::ScrollWheel(_) => None,
|
InputEvent::ScrollWheel(_) => None,
|
||||||
|
InputEvent::FileDrop(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
|
use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
|
||||||
use crate::{
|
use crate::{
|
||||||
display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, Executor, GlobalPixels,
|
display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, Executor, FileDropEvent,
|
||||||
InputEvent, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
|
GlobalPixels, InputEvent, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent,
|
||||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay,
|
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas,
|
||||||
PlatformInputHandler, PlatformWindow, Point, Scene, Size, Timer, WindowAppearance,
|
PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Scene, Size, Timer,
|
||||||
WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
|
WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
|
||||||
};
|
};
|
||||||
use block::ConcreteBlock;
|
use block::ConcreteBlock;
|
||||||
use cocoa::{
|
use cocoa::{
|
||||||
appkit::{
|
appkit::{
|
||||||
CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
|
CGPoint, NSApplication, NSBackingStoreBuffered, NSFilenamesPboardType, NSPasteboard,
|
||||||
NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
|
NSScreen, NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowButton,
|
||||||
NSWindowStyleMask, NSWindowTitleVisibility,
|
NSWindowCollectionBehavior, NSWindowStyleMask, NSWindowTitleVisibility,
|
||||||
},
|
},
|
||||||
base::{id, nil},
|
base::{id, nil},
|
||||||
foundation::{
|
foundation::{
|
||||||
NSAutoreleasePool, NSDictionary, NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger,
|
NSArray, NSAutoreleasePool, NSDictionary, NSFastEnumeration, NSInteger, NSPoint, NSRect,
|
||||||
|
NSSize, NSString, NSUInteger,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use core_graphics::display::CGRect;
|
use core_graphics::display::CGRect;
|
||||||
|
@ -37,6 +38,7 @@ use std::{
|
||||||
mem,
|
mem,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
os::raw::c_char,
|
os::raw::c_char,
|
||||||
|
path::PathBuf,
|
||||||
ptr,
|
ptr,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
|
@ -68,6 +70,13 @@ const NSTrackingInVisibleRect: NSUInteger = 0x200;
|
||||||
const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
|
const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;
|
const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;
|
||||||
|
// https://developer.apple.com/documentation/appkit/nsdragoperation
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
type NSDragOperation = NSUInteger;
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
const NSDragOperationNone: NSDragOperation = 0;
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
const NSDragOperationCopy: NSDragOperation = 1;
|
||||||
|
|
||||||
#[ctor]
|
#[ctor]
|
||||||
unsafe fn build_classes() {
|
unsafe fn build_classes() {
|
||||||
|
@ -259,6 +268,28 @@ unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const C
|
||||||
window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
|
window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||||
);
|
);
|
||||||
decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
|
decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
|
||||||
|
|
||||||
|
decl.add_method(
|
||||||
|
sel!(draggingEntered:),
|
||||||
|
dragging_entered as extern "C" fn(&Object, Sel, id) -> NSDragOperation,
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(draggingUpdated:),
|
||||||
|
dragging_updated as extern "C" fn(&Object, Sel, id) -> NSDragOperation,
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(draggingExited:),
|
||||||
|
dragging_exited as extern "C" fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(performDragOperation:),
|
||||||
|
perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(concludeDragOperation:),
|
||||||
|
conclude_drag_operation as extern "C" fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
|
||||||
decl.register()
|
decl.register()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,6 +503,11 @@ impl MacWindow {
|
||||||
target_screen,
|
target_screen,
|
||||||
);
|
);
|
||||||
assert!(!native_window.is_null());
|
assert!(!native_window.is_null());
|
||||||
|
let () = msg_send![
|
||||||
|
native_window,
|
||||||
|
registerForDraggedTypes:
|
||||||
|
NSArray::arrayWithObject(nil, NSFilenamesPboardType)
|
||||||
|
];
|
||||||
|
|
||||||
let screen = native_window.screen();
|
let screen = native_window.screen();
|
||||||
match options.bounds {
|
match options.bounds {
|
||||||
|
@ -1595,6 +1631,66 @@ extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn dragging_entered(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
|
||||||
|
let window_state = unsafe { get_window_state(this) };
|
||||||
|
let position = drag_event_position(&window_state, dragging_info);
|
||||||
|
if send_new_event(
|
||||||
|
&window_state,
|
||||||
|
InputEvent::FileDrop(FileDropEvent::Pending { position }),
|
||||||
|
) {
|
||||||
|
NSDragOperationCopy
|
||||||
|
} else {
|
||||||
|
NSDragOperationNone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn dragging_updated(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
|
||||||
|
let window_state = unsafe { get_window_state(this) };
|
||||||
|
let position = drag_event_position(&window_state, dragging_info);
|
||||||
|
if send_new_event(
|
||||||
|
&window_state,
|
||||||
|
InputEvent::FileDrop(FileDropEvent::Pending { position }),
|
||||||
|
) {
|
||||||
|
NSDragOperationCopy
|
||||||
|
} else {
|
||||||
|
NSDragOperationNone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) {
|
||||||
|
let window_state = unsafe { get_window_state(this) };
|
||||||
|
send_new_event(&window_state, InputEvent::FileDrop(FileDropEvent::End));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn perform_drag_operation(this: &Object, _: Sel, dragging_info: id) -> BOOL {
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
let pb: id = unsafe { msg_send![dragging_info, draggingPasteboard] };
|
||||||
|
let filenames = unsafe { NSPasteboard::propertyListForType(pb, NSFilenamesPboardType) };
|
||||||
|
for file in unsafe { filenames.iter() } {
|
||||||
|
let path = unsafe {
|
||||||
|
let f = NSString::UTF8String(file);
|
||||||
|
CStr::from_ptr(f).to_string_lossy().into_owned()
|
||||||
|
};
|
||||||
|
paths.push(PathBuf::from(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
let window_state = unsafe { get_window_state(this) };
|
||||||
|
let position = drag_event_position(&window_state, dragging_info);
|
||||||
|
if send_new_event(
|
||||||
|
&window_state,
|
||||||
|
InputEvent::FileDrop(FileDropEvent::Submit { position, paths }),
|
||||||
|
) {
|
||||||
|
YES
|
||||||
|
} else {
|
||||||
|
NO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn conclude_drag_operation(this: &Object, _: Sel, _: id) {
|
||||||
|
let window_state = unsafe { get_window_state(this) };
|
||||||
|
send_new_event(&window_state, InputEvent::FileDrop(FileDropEvent::End));
|
||||||
|
}
|
||||||
|
|
||||||
async fn synthetic_drag(
|
async fn synthetic_drag(
|
||||||
window_state: Weak<Mutex<MacWindowState>>,
|
window_state: Weak<Mutex<MacWindowState>>,
|
||||||
drag_id: usize,
|
drag_id: usize,
|
||||||
|
@ -1617,6 +1713,22 @@ async fn synthetic_drag(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_new_event(window_state_lock: &Mutex<MacWindowState>, e: InputEvent) -> bool {
|
||||||
|
let window_state = window_state_lock.lock().event_callback.take();
|
||||||
|
if let Some(mut callback) = window_state {
|
||||||
|
callback(e);
|
||||||
|
window_state_lock.lock().event_callback = Some(callback);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drag_event_position(window_state: &Mutex<MacWindowState>, dragging_info: id) -> Point<Pixels> {
|
||||||
|
let drag_location: NSPoint = unsafe { msg_send![dragging_info, draggingLocation] };
|
||||||
|
convert_mouse_position(drag_location, window_state.lock().content_size().height)
|
||||||
|
}
|
||||||
|
|
||||||
fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
|
fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut dyn PlatformInputHandler) -> R,
|
F: FnOnce(&mut dyn PlatformInputHandler) -> R,
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
px, size, Action, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, Bounds,
|
px, size, Action, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, Bounds,
|
||||||
BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect, Element,
|
BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect, Element,
|
||||||
EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData,
|
EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Handle,
|
||||||
InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId, MainThread,
|
Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
|
||||||
MainThreadOnly, MonochromeSprite, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
|
MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, MouseUpEvent, Path, Pixels,
|
||||||
PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams,
|
PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
|
||||||
RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription,
|
RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
|
||||||
TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, WindowOptions,
|
Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle,
|
||||||
SUBPIXEL_VARIANTS,
|
WindowOptions, SUBPIXEL_VARIANTS,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
@ -894,6 +894,22 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||||
self.window.mouse_position = *position;
|
self.window.mouse_position = *position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match any_mouse_event.downcast_ref() {
|
||||||
|
Some(FileDropEvent::Pending { position }) => {
|
||||||
|
dbg!("FileDropEvent::Pending", position);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Some(FileDropEvent::Submit { position, paths }) => {
|
||||||
|
dbg!("FileDropEvent::Submit", position, paths);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Some(FileDropEvent::End) => {
|
||||||
|
self.active_drag = None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
// Handlers may set this to false by calling `stop_propagation`
|
// Handlers may set this to false by calling `stop_propagation`
|
||||||
self.app.propagate_event = true;
|
self.app.propagate_event = true;
|
||||||
self.window.default_prevented = false;
|
self.window.default_prevented = false;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue