Merge pull request #1585 from zed-industries/more-drag-and-drop-fixes

K Simmons created

Some more drag and drop fixes

Change summary

crates/drag_and_drop/src/drag_and_drop.rs | 67 ++++++++++++++++--------
crates/gpui/src/platform/event.rs         | 14 +++++
crates/gpui/src/presenter.rs              | 16 +++++
crates/workspace/src/pane.rs              |  4 
4 files changed, 77 insertions(+), 24 deletions(-)

Detailed changes

crates/drag_and_drop/src/drag_and_drop.rs 🔗

@@ -9,6 +9,7 @@ use gpui::{
 };
 
 struct State<V: View> {
+    window_id: usize,
     position: Vector2F,
     region_offset: Vector2F,
     payload: Rc<dyn Any + 'static>,
@@ -18,6 +19,7 @@ struct State<V: View> {
 impl<V: View> Clone for State<V> {
     fn clone(&self) -> Self {
         Self {
+            window_id: self.window_id.clone(),
             position: self.position.clone(),
             region_offset: self.region_offset.clone(),
             payload: self.payload.clone(),
@@ -46,11 +48,18 @@ impl<V: View> DragAndDrop<V> {
         }
     }
 
-    pub fn currently_dragged<T: Any>(&self) -> Option<(Vector2F, Rc<T>)> {
+    pub fn currently_dragged<T: Any>(&self, window_id: usize) -> Option<(Vector2F, Rc<T>)> {
         self.currently_dragged.as_ref().and_then(
             |State {
-                 position, payload, ..
+                 position,
+                 payload,
+                 window_id: window_dragged_from,
+                 ..
              }| {
+                if &window_id != window_dragged_from {
+                    return None;
+                }
+
                 payload
                     .clone()
                     .downcast::<T>()
@@ -66,6 +75,7 @@ impl<V: View> DragAndDrop<V> {
         cx: &mut EventContext,
         render: Rc<impl 'static + Fn(&T, &mut RenderContext<V>) -> ElementBox>,
     ) {
+        let window_id = cx.window_id();
         cx.update_global::<Self, _, _>(|this, cx| {
             let region_offset = if let Some(previous_state) = this.currently_dragged.as_ref() {
                 previous_state.region_offset
@@ -74,6 +84,7 @@ impl<V: View> DragAndDrop<V> {
             };
 
             this.currently_dragged = Some(State {
+                window_id,
                 region_offset,
                 position: event.position,
                 payload,
@@ -91,34 +102,46 @@ impl<V: View> DragAndDrop<V> {
     pub fn render(cx: &mut RenderContext<V>) -> Option<ElementBox> {
         let currently_dragged = cx.global::<Self>().currently_dragged.clone();
 
-        currently_dragged.map(
+        currently_dragged.and_then(
             |State {
+                 window_id,
                  region_offset,
                  position,
                  payload,
                  render,
              }| {
+                if cx.window_id() != window_id {
+                    return None;
+                }
+
                 let position = position + region_offset;
 
-                MouseEventHandler::new::<Self, _, _>(0, cx, |_, cx| {
-                    Container::new(render(payload, cx))
-                        .with_margin_left(position.x())
-                        .with_margin_top(position.y())
-                        .aligned()
-                        .top()
-                        .left()
-                        .boxed()
-                })
-                .with_cursor_style(CursorStyle::Arrow)
-                .on_up(MouseButton::Left, |_, cx| {
-                    cx.defer(|cx| {
-                        cx.update_global::<Self, _, _>(|this, _| this.currently_dragged.take());
-                    });
-                    cx.propogate_event();
-                })
-                // Don't block hover events or invalidations
-                .with_hoverable(false)
-                .boxed()
+                Some(
+                    MouseEventHandler::new::<Self, _, _>(0, cx, |_, cx| {
+                        Container::new(render(payload, cx))
+                            .with_margin_left(position.x())
+                            .with_margin_top(position.y())
+                            .aligned()
+                            .top()
+                            .left()
+                            .boxed()
+                    })
+                    .with_cursor_style(CursorStyle::Arrow)
+                    .on_up(MouseButton::Left, |_, cx| {
+                        cx.defer(|cx| {
+                            cx.update_global::<Self, _, _>(|this, _| this.currently_dragged.take());
+                        });
+                        cx.propogate_event();
+                    })
+                    .on_up_out(MouseButton::Left, |_, cx| {
+                        cx.defer(|cx| {
+                            cx.update_global::<Self, _, _>(|this, _| this.currently_dragged.take());
+                        });
+                    })
+                    // Don't block hover events or invalidations
+                    .with_hoverable(false)
+                    .boxed(),
+                )
             },
         )
     }

crates/gpui/src/platform/event.rs 🔗

@@ -89,6 +89,20 @@ pub struct MouseMovedEvent {
     pub shift: bool,
 }
 
+impl MouseMovedEvent {
+    pub fn to_button_event(&self, button: MouseButton) -> MouseButtonEvent {
+        MouseButtonEvent {
+            position: self.position,
+            button: self.pressed_button.unwrap_or(button),
+            ctrl: self.ctrl,
+            alt: self.alt,
+            shift: self.shift,
+            cmd: self.cmd,
+            click_count: 0,
+        }
+    }
+}
+
 #[derive(Clone, Debug)]
 pub enum Event {
     KeyDown(KeyDownEvent),

crates/gpui/src/presenter.rs 🔗

@@ -310,7 +310,23 @@ impl Presenter {
                                 prev_mouse_position: self.mouse_position,
                                 platform_event: e.clone(),
                             }));
+                        } else if let Some(clicked_button) = self.clicked_button {
+                            // Mouse up event happened outside the current window. Simulate mouse up button event
+                            let button_event = e.to_button_event(clicked_button);
+                            events_to_send.push(MouseRegionEvent::Up(UpRegionEvent {
+                                region: Default::default(),
+                                platform_event: button_event.clone(),
+                            }));
+                            events_to_send.push(MouseRegionEvent::UpOut(UpOutRegionEvent {
+                                region: Default::default(),
+                                platform_event: button_event.clone(),
+                            }));
+                            events_to_send.push(MouseRegionEvent::Click(ClickRegionEvent {
+                                region: Default::default(),
+                                platform_event: button_event.clone(),
+                            }));
                         }
+
                         events_to_send.push(MouseRegionEvent::Move(MoveRegionEvent {
                             region: Default::default(),
                             platform_event: e.clone(),

crates/workspace/src/pane.rs 🔗

@@ -1256,7 +1256,7 @@ impl Pane {
     fn handle_dropped_item(pane: &WeakViewHandle<Pane>, index: usize, cx: &mut EventContext) {
         if let Some((_, dragged_item)) = cx
             .global::<DragAndDrop<Workspace>>()
-            .currently_dragged::<DraggedItem>()
+            .currently_dragged::<DraggedItem>(cx.window_id)
         {
             cx.dispatch_action(MoveItem {
                 item_id: dragged_item.item.id(),
@@ -1277,7 +1277,7 @@ impl Pane {
         if hovered
             && cx
                 .global::<DragAndDrop<Workspace>>()
-                .currently_dragged::<DraggedItem>()
+                .currently_dragged::<DraggedItem>(cx.window_id())
                 .is_some()
         {
             Some(theme.workspace.tab_bar.drop_target_overlay_color)