Add OS file drop event handler
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
5b04f965fa
commit
a01b507ef4
9 changed files with 141 additions and 85 deletions
|
@ -821,7 +821,7 @@ impl<G: 'static> DerefMut for GlobalLease<G> {
|
|||
}
|
||||
|
||||
pub(crate) struct AnyDrag {
|
||||
pub drag_handle_view: AnyView,
|
||||
pub drag_handle_view: Option<AnyView>,
|
||||
pub cursor_offset: Point<Pixels>,
|
||||
pub state: AnyBox,
|
||||
pub state_type: TypeId,
|
||||
|
|
|
@ -374,10 +374,12 @@ pub trait StatefulInteractive: StatelessInteractive {
|
|||
Some(Arc::new(move |view_state, cursor_offset, cx| {
|
||||
let drag = listener(view_state, cx);
|
||||
let view_handle = cx.handle().upgrade().unwrap();
|
||||
let drag_handle_view = view(view_handle, move |view_state, cx| {
|
||||
let drag_handle_view = Some(
|
||||
view(view_handle, move |view_state, cx| {
|
||||
(drag.render_drag_handle)(view_state, cx)
|
||||
})
|
||||
.into_any();
|
||||
.into_any(),
|
||||
);
|
||||
AnyDrag {
|
||||
drag_handle_view,
|
||||
cursor_offset,
|
||||
|
@ -780,11 +782,7 @@ impl GroupBounds {
|
|||
}
|
||||
|
||||
pub fn pop(name: &SharedString, cx: &mut AppContext) {
|
||||
cx.default_global::<Self>()
|
||||
.0
|
||||
.get_mut(name)
|
||||
.unwrap()
|
||||
.pop();
|
||||
cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1035,16 +1033,21 @@ impl Deref for MouseExitEvent {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DroppedFiles(pub(crate) SmallVec<[PathBuf; 2]>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum FileDropEvent {
|
||||
#[default]
|
||||
End,
|
||||
Entered {
|
||||
position: Point<Pixels>,
|
||||
files: DroppedFiles,
|
||||
},
|
||||
Pending {
|
||||
position: Point<Pixels>,
|
||||
},
|
||||
Submit {
|
||||
position: Point<Pixels>,
|
||||
paths: Vec<PathBuf>,
|
||||
},
|
||||
Exited,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -1054,7 +1057,7 @@ pub enum InputEvent {
|
|||
ModifiersChanged(ModifiersChangedEvent),
|
||||
MouseDown(MouseDownEvent),
|
||||
MouseUp(MouseUpEvent),
|
||||
MouseMoved(MouseMoveEvent),
|
||||
MouseMove(MouseMoveEvent),
|
||||
MouseExited(MouseExitEvent),
|
||||
ScrollWheel(ScrollWheelEvent),
|
||||
FileDrop(FileDropEvent),
|
||||
|
@ -1068,12 +1071,14 @@ impl InputEvent {
|
|||
InputEvent::ModifiersChanged { .. } => None,
|
||||
InputEvent::MouseDown(event) => Some(event.position),
|
||||
InputEvent::MouseUp(event) => Some(event.position),
|
||||
InputEvent::MouseMoved(event) => Some(event.position),
|
||||
InputEvent::MouseMove(event) => Some(event.position),
|
||||
InputEvent::MouseExited(event) => Some(event.position),
|
||||
InputEvent::ScrollWheel(event) => Some(event.position),
|
||||
InputEvent::FileDrop(FileDropEvent::End) => None,
|
||||
InputEvent::FileDrop(FileDropEvent::Exited) => None,
|
||||
InputEvent::FileDrop(
|
||||
FileDropEvent::Pending { position } | FileDropEvent::Submit { position, .. },
|
||||
FileDropEvent::Entered { position, .. }
|
||||
| FileDropEvent::Pending { position, .. }
|
||||
| FileDropEvent::Submit { position, .. },
|
||||
) => Some(*position),
|
||||
}
|
||||
}
|
||||
|
@ -1085,7 +1090,7 @@ impl InputEvent {
|
|||
InputEvent::ModifiersChanged { .. } => None,
|
||||
InputEvent::MouseDown(event) => Some(event),
|
||||
InputEvent::MouseUp(event) => Some(event),
|
||||
InputEvent::MouseMoved(event) => Some(event),
|
||||
InputEvent::MouseMove(event) => Some(event),
|
||||
InputEvent::MouseExited(event) => Some(event),
|
||||
InputEvent::ScrollWheel(event) => Some(event),
|
||||
InputEvent::FileDrop(event) => Some(event),
|
||||
|
@ -1099,7 +1104,7 @@ impl InputEvent {
|
|||
InputEvent::ModifiersChanged(event) => Some(event),
|
||||
InputEvent::MouseDown(_) => None,
|
||||
InputEvent::MouseUp(_) => None,
|
||||
InputEvent::MouseMoved(_) => None,
|
||||
InputEvent::MouseMove(_) => None,
|
||||
InputEvent::MouseExited(_) => None,
|
||||
InputEvent::ScrollWheel(_) => None,
|
||||
InputEvent::FileDrop(_) => None,
|
||||
|
|
|
@ -202,7 +202,7 @@ impl InputEvent {
|
|||
};
|
||||
|
||||
window_height.map(|window_height| {
|
||||
Self::MouseMoved(MouseMoveEvent {
|
||||
Self::MouseMove(MouseMoveEvent {
|
||||
pressed_button: Some(pressed_button),
|
||||
position: point(
|
||||
px(native_event.locationInWindow().x as f32),
|
||||
|
@ -213,7 +213,7 @@ impl InputEvent {
|
|||
})
|
||||
}
|
||||
NSEventType::NSMouseMoved => window_height.map(|window_height| {
|
||||
Self::MouseMoved(MouseMoveEvent {
|
||||
Self::MouseMove(MouseMoveEvent {
|
||||
position: point(
|
||||
px(native_event.locationInWindow().x as f32),
|
||||
window_height - px(native_event.locationInWindow().y as f32),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
|
||||
use crate::{
|
||||
display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, Executor, FileDropEvent,
|
||||
GlobalPixels, InputEvent, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent,
|
||||
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas,
|
||||
PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Scene, Size, Timer,
|
||||
WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
|
||||
display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, DroppedFiles, Executor,
|
||||
FileDropEvent, GlobalPixels, InputEvent, KeyDownEvent, Keystroke, Modifiers,
|
||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
|
||||
PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Scene, Size,
|
||||
Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
|
@ -31,6 +31,7 @@ use objc::{
|
|||
sel, sel_impl,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
|
@ -1177,7 +1178,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
|||
};
|
||||
|
||||
match &event {
|
||||
InputEvent::MouseMoved(
|
||||
InputEvent::MouseMove(
|
||||
event @ MouseMoveEvent {
|
||||
pressed_button: Some(_),
|
||||
..
|
||||
|
@ -1194,7 +1195,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
|||
.detach();
|
||||
}
|
||||
|
||||
InputEvent::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
|
||||
InputEvent::MouseMove(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
|
||||
|
||||
InputEvent::MouseUp(MouseUpEvent {
|
||||
button: MouseButton::Left,
|
||||
|
@ -1633,11 +1634,14 @@ 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) };
|
||||
if send_new_event(&window_state, {
|
||||
let position = drag_event_position(&window_state, dragging_info);
|
||||
if send_new_event(
|
||||
&window_state,
|
||||
InputEvent::FileDrop(FileDropEvent::Pending { position }),
|
||||
) {
|
||||
let paths = external_paths_from_event(dragging_info);
|
||||
InputEvent::FileDrop(FileDropEvent::Entered {
|
||||
position,
|
||||
files: paths,
|
||||
})
|
||||
}) {
|
||||
NSDragOperationCopy
|
||||
} else {
|
||||
NSDragOperationNone
|
||||
|
@ -1659,26 +1663,17 @@ extern "C" fn dragging_updated(this: &Object, _: Sel, dragging_info: id) -> NSDr
|
|||
|
||||
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));
|
||||
send_new_event(&window_state, InputEvent::FileDrop(FileDropEvent::Exited));
|
||||
}
|
||||
|
||||
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 files = external_paths_from_event(dragging_info);
|
||||
|
||||
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 }),
|
||||
InputEvent::FileDrop(FileDropEvent::Submit { position }),
|
||||
) {
|
||||
YES
|
||||
} else {
|
||||
|
@ -1686,9 +1681,23 @@ extern "C" fn perform_drag_operation(this: &Object, _: Sel, dragging_info: id) -
|
|||
}
|
||||
}
|
||||
|
||||
fn external_paths_from_event(dragging_info: *mut Object) -> DroppedFiles {
|
||||
let mut paths = SmallVec::new();
|
||||
let pasteboard: id = unsafe { msg_send![dragging_info, draggingPasteboard] };
|
||||
let filenames = unsafe { NSPasteboard::propertyListForType(pasteboard, 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))
|
||||
}
|
||||
DroppedFiles(paths)
|
||||
}
|
||||
|
||||
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));
|
||||
send_new_event(&window_state, InputEvent::FileDrop(FileDropEvent::Exited));
|
||||
}
|
||||
|
||||
async fn synthetic_drag(
|
||||
|
@ -1703,7 +1712,7 @@ async fn synthetic_drag(
|
|||
if lock.synthetic_drag_counter == drag_id {
|
||||
if let Some(mut callback) = lock.event_callback.take() {
|
||||
drop(lock);
|
||||
callback(InputEvent::MouseMoved(event.clone()));
|
||||
callback(InputEvent::MouseMove(event.clone()));
|
||||
window_state.lock().event_callback = Some(callback);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use crate::{
|
||||
px, size, Action, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, Bounds,
|
||||
BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect, Element,
|
||||
EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Handle,
|
||||
Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
|
||||
MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, MouseUpEvent, Path, Pixels,
|
||||
PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
|
||||
RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
|
||||
Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle,
|
||||
WindowOptions, SUBPIXEL_VARIANTS,
|
||||
px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
|
||||
Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, DroppedFiles,
|
||||
Edges, Effect, Element, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId,
|
||||
GlobalElementId, GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch,
|
||||
KeyMatcher, Keystroke, LayoutId, MainThread, MainThreadOnly, Modifiers, MonochromeSprite,
|
||||
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
|
||||
PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams,
|
||||
RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription,
|
||||
TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, WindowOptions,
|
||||
SUBPIXEL_VARIANTS,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use collections::HashMap;
|
||||
|
@ -816,7 +817,9 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
|||
cx.with_element_offset(Some(offset), |cx| {
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
draw_any_view(&mut active_drag.drag_handle_view, available_space, cx);
|
||||
if let Some(drag_handle_view) = &mut active_drag.drag_handle_view {
|
||||
draw_any_view(drag_handle_view, available_space, cx);
|
||||
}
|
||||
cx.active_drag = Some(active_drag);
|
||||
});
|
||||
});
|
||||
|
@ -889,27 +892,48 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
|||
}
|
||||
|
||||
fn dispatch_event(&mut self, event: InputEvent) -> bool {
|
||||
let event = match event {
|
||||
InputEvent::MouseMove(mouse_move) => {
|
||||
self.window.mouse_position = mouse_move.position;
|
||||
InputEvent::MouseMove(mouse_move)
|
||||
}
|
||||
InputEvent::FileDrop(file_drop) => match file_drop {
|
||||
FileDropEvent::Entered { position, files } => {
|
||||
self.active_drag.get_or_insert_with(|| AnyDrag {
|
||||
drag_handle_view: None,
|
||||
cursor_offset: position,
|
||||
state: Box::new(files),
|
||||
state_type: TypeId::of::<DroppedFiles>(),
|
||||
});
|
||||
InputEvent::MouseDown(MouseDownEvent {
|
||||
position,
|
||||
button: MouseButton::Left,
|
||||
click_count: 1,
|
||||
modifiers: Modifiers::default(),
|
||||
})
|
||||
}
|
||||
FileDropEvent::Pending { position } => InputEvent::MouseMove(MouseMoveEvent {
|
||||
position,
|
||||
pressed_button: Some(MouseButton::Left),
|
||||
modifiers: Modifiers::default(),
|
||||
}),
|
||||
FileDropEvent::Submit { position } => InputEvent::MouseUp(MouseUpEvent {
|
||||
button: MouseButton::Left,
|
||||
position,
|
||||
modifiers: Modifiers::default(),
|
||||
click_count: 1,
|
||||
}),
|
||||
FileDropEvent::Exited => InputEvent::MouseUp(MouseUpEvent {
|
||||
button: MouseButton::Left,
|
||||
position: Point::default(),
|
||||
modifiers: Modifiers::default(),
|
||||
click_count: 1,
|
||||
}),
|
||||
},
|
||||
_ => event,
|
||||
};
|
||||
|
||||
if let Some(any_mouse_event) = event.mouse_event() {
|
||||
if let Some(MouseMoveEvent { position, .. }) = any_mouse_event.downcast_ref() {
|
||||
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`
|
||||
self.app.propagate_event = true;
|
||||
self.window.default_prevented = false;
|
||||
|
|
|
@ -305,7 +305,18 @@ fn box_prefixes() -> Vec<(&'static str, bool, Vec<TokenStream2>, &'static str)>
|
|||
vec![quote! { padding.right }],
|
||||
"Sets the right padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-padding-to-a-single-side)"
|
||||
),
|
||||
("top", true, vec![quote! { inset.top }], "Sets the top value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",),
|
||||
(
|
||||
"inset",
|
||||
true,
|
||||
vec![quote! { inset.top }, quote! { inset.right }, quote! { inset.bottom }, quote! { inset.left }],
|
||||
"Sets the top, right, bottom, and left values of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",
|
||||
),
|
||||
(
|
||||
"top",
|
||||
true,
|
||||
vec![quote! { inset.top }],
|
||||
"Sets the top value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",
|
||||
),
|
||||
(
|
||||
"bottom",
|
||||
true,
|
||||
|
|
|
@ -159,8 +159,6 @@ pub struct ThemeColor {
|
|||
|
||||
impl std::fmt::Debug for ThemeColor {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
dbg!("ThemeColor debug");
|
||||
|
||||
f.debug_struct("ThemeColor")
|
||||
.field("transparent", &self.transparent.to_rgb().to_hex())
|
||||
.field(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use gpui2::{hsla, AnyElement, ElementId, Hsla, Length, Size};
|
||||
use gpui2::{hsla, red, AnyElement, DroppedFiles, ElementId, Hsla, Length, Size};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
@ -50,8 +50,19 @@ impl<S: 'static + Send + Sync> Pane<S> {
|
|||
.bg(self.fill)
|
||||
.w(self.size.width)
|
||||
.h(self.size.height)
|
||||
.overflow_y_scroll()
|
||||
.children(self.children.drain(..))
|
||||
.relative()
|
||||
.children(cx.stack(0, |_| self.children.drain(..)))
|
||||
.child(cx.stack(1, |_| {
|
||||
// TODO kb! Figure out why we can't we see the red background when we drag a file over this div.
|
||||
div()
|
||||
.id("drag-target")
|
||||
.drag_over::<DroppedFiles>(|d| d.bg(red()))
|
||||
.on_drop(|_, files: DroppedFiles, _| {
|
||||
dbg!("dropped files!", files);
|
||||
})
|
||||
.absolute()
|
||||
.inset_0()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -179,8 +179,6 @@ impl Workspace {
|
|||
|
||||
let color = ThemeColor::new(cx);
|
||||
|
||||
dbg!(color);
|
||||
|
||||
// HACK: This should happen inside of `debug_toggle_user_settings`, but
|
||||
// we don't have `cx.global::<FakeSettings>()` in event handlers at the moment.
|
||||
// Need to talk with Nathan/Antonio about this.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue