Impl default for drag and drop global. Make drag and drop global maintain a list of weak handles to the container.

K Simmons and Max Brunsfeld created

Co-Authored-By: Max Brunsfeld <max@zed.dev>

Change summary

crates/drag_and_drop/src/drag_and_drop.rs | 55 ++++++++++++++++--------
crates/workspace/src/workspace.rs         |  5 +
styles/package-lock.json                  |  1 
3 files changed, 40 insertions(+), 21 deletions(-)

Detailed changes

crates/drag_and_drop/src/drag_and_drop.rs 🔗

@@ -1,11 +1,12 @@
 use std::{any::Any, rc::Rc};
 
+use collections::HashSet;
 use gpui::{
     elements::{Container, MouseEventHandler},
     geometry::vector::Vector2F,
     scene::DragRegionEvent,
-    CursorStyle, Element, ElementBox, EventContext, MouseButton, RenderContext, View, ViewContext,
-    WeakViewHandle,
+    CursorStyle, Element, ElementBox, EventContext, MouseButton, MutableAppContext, RenderContext,
+    View, WeakViewHandle,
 };
 
 struct State<V: View> {
@@ -29,24 +30,23 @@ impl<V: View> Clone for State<V> {
 }
 
 pub struct DragAndDrop<V: View> {
-    parent: WeakViewHandle<V>,
+    containers: HashSet<WeakViewHandle<V>>,
     currently_dragged: Option<State<V>>,
 }
 
-impl<V: View> DragAndDrop<V> {
-    pub fn new(parent: WeakViewHandle<V>, cx: &mut ViewContext<V>) -> Self {
-        cx.observe_global::<Self, _>(|cx| {
-            if let Some(parent) = cx.global::<Self>().parent.upgrade(cx) {
-                parent.update(cx, |_, cx| cx.notify())
-            }
-        })
-        .detach();
-
+impl<V: View> Default for DragAndDrop<V> {
+    fn default() -> Self {
         Self {
-            parent,
-            currently_dragged: None,
+            containers: Default::default(),
+            currently_dragged: Default::default(),
         }
     }
+}
+
+impl<V: View> DragAndDrop<V> {
+    pub fn register_container(&mut self, handle: WeakViewHandle<V>) {
+        self.containers.insert(handle);
+    }
 
     pub fn currently_dragged<T: Any>(&self, window_id: usize) -> Option<(Vector2F, Rc<T>)> {
         self.currently_dragged.as_ref().and_then(
@@ -93,9 +93,7 @@ impl<V: View> DragAndDrop<V> {
                 }),
             });
 
-            if let Some(parent) = this.parent.upgrade(cx) {
-                parent.update(cx, |_, cx| cx.notify())
-            }
+            this.notify_containers_for_window(window_id, cx);
         });
     }
 
@@ -129,13 +127,13 @@ impl<V: View> DragAndDrop<V> {
                     .with_cursor_style(CursorStyle::Arrow)
                     .on_up(MouseButton::Left, |_, cx| {
                         cx.defer(|cx| {
-                            cx.update_global::<Self, _, _>(|this, _| this.currently_dragged.take());
+                            cx.update_global::<Self, _, _>(|this, cx| this.stop_dragging(cx));
                         });
                         cx.propogate_event();
                     })
                     .on_up_out(MouseButton::Left, |_, cx| {
                         cx.defer(|cx| {
-                            cx.update_global::<Self, _, _>(|this, _| this.currently_dragged.take());
+                            cx.update_global::<Self, _, _>(|this, cx| this.stop_dragging(cx));
                         });
                     })
                     // Don't block hover events or invalidations
@@ -145,6 +143,25 @@ impl<V: View> DragAndDrop<V> {
             },
         )
     }
+
+    fn stop_dragging(&mut self, cx: &mut MutableAppContext) {
+        if let Some(State { window_id, .. }) = self.currently_dragged.take() {
+            self.notify_containers_for_window(window_id, cx);
+        }
+    }
+
+    fn notify_containers_for_window(&mut self, window_id: usize, cx: &mut MutableAppContext) {
+        self.containers.retain(|container| {
+            if let Some(container) = container.upgrade(cx) {
+                if container.window_id() == window_id {
+                    container.update(cx, |_, cx| cx.notify());
+                }
+                true
+            } else {
+                false
+            }
+        });
+    }
 }
 
 pub trait Draggable {

crates/workspace/src/workspace.rs 🔗

@@ -949,8 +949,9 @@ impl Workspace {
             status_bar
         });
 
-        let drag_and_drop = DragAndDrop::new(cx.weak_handle(), cx);
-        cx.set_global(drag_and_drop);
+        cx.update_default_global::<DragAndDrop<Workspace>, _, _>(|drag_and_drop, _| {
+            drag_and_drop.register_container(weak_self.clone());
+        });
 
         let mut this = Workspace {
             modal: None,

styles/package-lock.json 🔗

@@ -5,6 +5,7 @@
   "requires": true,
   "packages": {
     "": {
+      "name": "styles",
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {