Handle tab drag end on pane items to insert after active item

Julia and Kay Simmons created

Co-Authored-By: Kay Simmons <kay@zed.dev>

Change summary

crates/gpui/src/elements/mouse_event_handler.rs | 38 ++++++++++---------
crates/gpui/src/elements/stack.rs               |  6 +-
crates/gpui/src/presenter.rs                    | 10 +++++
crates/workspace/src/dock.rs                    |  1 
crates/workspace/src/pane.rs                    | 35 ++++++++++++++++-
5 files changed, 66 insertions(+), 24 deletions(-)

Detailed changes

crates/gpui/src/elements/mouse_event_handler.rs 🔗

@@ -172,26 +172,28 @@ impl<Tag> Element for MouseEventHandler<Tag> {
     ) -> Self::PaintState {
         let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
         let hit_bounds = self.hit_bounds(visible_bounds);
-        if let Some(style) = self.cursor_style {
-            cx.scene.push_cursor_region(CursorRegion {
-                bounds: hit_bounds,
-                style,
-            });
-        }
-
-        cx.scene.push_mouse_region(
-            MouseRegion::from_handlers::<Tag>(
-                cx.current_view_id(),
-                self.region_id,
-                hit_bounds,
-                self.handlers.clone(),
-            )
-            .with_hoverable(self.hoverable)
-            .with_notify_on_hover(self.notify_on_hover)
-            .with_notify_on_click(self.notify_on_click),
-        );
 
         self.child.paint(bounds.origin(), visible_bounds, cx);
+
+        cx.paint_stacking_context(None, |cx| {
+            if let Some(style) = self.cursor_style {
+                cx.scene.push_cursor_region(CursorRegion {
+                    bounds: hit_bounds,
+                    style,
+                });
+            }
+            cx.scene.push_mouse_region(
+                MouseRegion::from_handlers::<Tag>(
+                    cx.current_view_id(),
+                    self.region_id,
+                    hit_bounds,
+                    self.handlers.clone(),
+                )
+                .with_hoverable(self.hoverable)
+                .with_notify_on_hover(self.notify_on_hover)
+                .with_notify_on_click(self.notify_on_click),
+            );
+        });
     }
 
     fn rect_for_text_range(

crates/gpui/src/elements/stack.rs 🔗

@@ -42,9 +42,9 @@ impl Element for Stack {
         cx: &mut PaintContext,
     ) -> Self::PaintState {
         for child in &mut self.children {
-            cx.scene.push_layer(None);
-            child.paint(bounds.origin(), visible_bounds, cx);
-            cx.scene.pop_layer();
+            cx.paint_layer(None, |cx| {
+                child.paint(bounds.origin(), visible_bounds, cx);
+            });
         }
     }
 

crates/gpui/src/presenter.rs 🔗

@@ -707,6 +707,16 @@ impl<'a> PaintContext<'a> {
         }
     }
 
+    #[inline]
+    pub fn paint_stacking_context<F>(&mut self, clip_bounds: Option<RectF>, f: F)
+    where
+        F: FnOnce(&mut Self),
+    {
+        self.scene.push_stacking_context(clip_bounds);
+        f(self);
+        self.scene.pop_stacking_context();
+    }
+
     #[inline]
     pub fn paint_layer<F>(&mut self, clip_bounds: Option<RectF>, f: F)
     where

crates/workspace/src/dock.rs 🔗

@@ -362,6 +362,7 @@ impl View for ToggleDockButton {
             }
         })
         .with_cursor_style(CursorStyle::PointingHand);
+        .on_
 
         if dock_position.is_visible() {
             button

crates/workspace/src/pane.rs 🔗

@@ -1373,10 +1373,12 @@ impl View for Pane {
             .with_child(
                 MouseEventHandler::<MouseNavigationHandler>::new(0, cx, |_, cx| {
                     if let Some(active_item) = self.active_item() {
+                        enum PaneContentTabDropTarget {}
+
                         Flex::column()
                             .with_child({
                                 let mut tab_row = Flex::row()
-                                    .with_child(self.render_tabs(cx).flex(1.0, true).named("tabs"));
+                                    .with_child(self.render_tabs(cx).flex(1., true).named("tabs"));
 
                                 // Render pane buttons
                                 let theme = cx.global::<Settings>().theme.clone();
@@ -1440,8 +1442,35 @@ impl View for Pane {
                                     .flex(1., false)
                                     .named("tab bar")
                             })
-                            .with_child(ChildView::new(&self.toolbar, cx).expanded().boxed())
-                            .with_child(ChildView::new(active_item, cx).flex(1., true).boxed())
+                            .with_child({
+                                let drop_index = self.active_item_index + 1;
+                                MouseEventHandler::<PaneContentTabDropTarget>::new(
+                                    0,
+                                    cx,
+                                    |_, cx| {
+                                        Flex::column()
+                                            .with_child(
+                                                ChildView::new(&self.toolbar, cx)
+                                                    .expanded()
+                                                    .boxed(),
+                                            )
+                                            .with_child(
+                                                ChildView::new(active_item, cx)
+                                                    .flex(1., true)
+                                                    .boxed(),
+                                            )
+                                            .boxed()
+                                    },
+                                )
+                                .on_up(MouseButton::Left, {
+                                    let pane = cx.handle();
+                                    move |_, cx: &mut EventContext| {
+                                        Pane::handle_dropped_item(&pane, drop_index, cx)
+                                    }
+                                })
+                                .flex(1., true)
+                                .boxed()
+                            })
                             .boxed()
                     } else {
                         enum EmptyPane {}