Allow dragging and dropping tabs

Max Brunsfeld , Nathan , and Marshall created

Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Marshall <marshall@zed.dev>

Change summary

crates/collab_ui2/src/chat_panel.rs          |   6 
crates/gpui2/src/elements/div.rs             |  10 
crates/gpui2/src/styled.rs                   |   2 
crates/picker2/src/picker2.rs                |   2 
crates/storybook2/src/story_selector.rs      |   2 
crates/ui2/src/components/list/list_item.rs  |   8 
crates/ui2/src/components/stories/tab_bar.rs |  16 
crates/ui2/src/components/tab_bar.rs         |  25 
crates/workspace2/src/pane.rs                | 546 +--------------------
9 files changed, 82 insertions(+), 535 deletions(-)

Detailed changes

crates/collab_ui2/src/chat_panel.rs 🔗

@@ -288,7 +288,11 @@ impl ChatPanel {
                             ),
                     ),
             )
-            .child(div().grow().child(self.render_active_channel_messages(cx)))
+            .child(
+                div()
+                    .flex_grow()
+                    .child(self.render_active_channel_messages(cx)),
+            )
             .child(
                 div()
                     .z_index(1)

crates/gpui2/src/elements/div.rs 🔗

@@ -61,6 +61,10 @@ pub trait InteractiveElement: Sized {
     }
 
     fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
+        debug_assert!(
+            self.interactivity().hover_style.is_none(),
+            "hover style already set"
+        );
         self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
         self
     }
@@ -1313,16 +1317,16 @@ where
 
 impl<E> IntoElement for Focusable<E>
 where
-    E: Element,
+    E: IntoElement,
 {
-    type Element = E;
+    type Element = E::Element;
 
     fn element_id(&self) -> Option<ElementId> {
         self.element.element_id()
     }
 
     fn into_element(self) -> Self::Element {
-        self.element
+        self.element.into_element()
     }
 }
 

crates/gpui2/src/styled.rs 🔗

@@ -303,7 +303,7 @@ pub trait Styled: Sized {
 
     /// Sets the element to allow a flex item to grow to fill any available space.
     /// [Docs](https://tailwindcss.com/docs/flex-grow)
-    fn grow(mut self) -> Self {
+    fn flex_grow(mut self) -> Self {
         self.style().flex_grow = Some(1.);
         self
     }

crates/picker2/src/picker2.rs 🔗

@@ -228,7 +228,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
             .when(self.delegate.match_count() > 0, |el| {
                 el.child(
                     v_stack()
-                        .grow()
+                        .flex_grow()
                         .child(
                             uniform_list(
                                 cx.view().clone(),

crates/storybook2/src/story_selector.rs 🔗

@@ -61,7 +61,7 @@ impl ComponentStory {
             Self::Scroll => ScrollStory::view(cx).into(),
             Self::Text => TextStory::view(cx).into(),
             Self::Tab => cx.build_view(|_| ui::TabStory).into(),
-            Self::TabBar => cx.build_view(|cx| ui::TabBarStory::new(cx)).into(),
+            Self::TabBar => cx.build_view(|_| ui::TabBarStory).into(),
             Self::ViewportUnits => cx.build_view(|_| crate::stories::ViewportUnitsStory).into(),
             Self::ZIndex => cx.build_view(|_| ZIndexStory).into(),
             Self::Picker => PickerStory::new(cx).into(),

crates/ui2/src/components/list/list_item.rs 🔗

@@ -54,14 +54,6 @@ impl ListItem {
         self
     }
 
-    pub fn on_drag(
-        mut self,
-        handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
-    ) -> Self {
-        self.on_secondary_mouse_down = Some(Box::new(handler));
-        self
-    }
-
     pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
         self.tooltip = Some(Box::new(tooltip));
         self

crates/ui2/src/components/stories/tab_bar.rs 🔗

@@ -1,19 +1,9 @@
-use gpui::{Div, FocusHandle, Render};
+use gpui::{Div, Render};
 use story::Story;
 
 use crate::{prelude::*, Tab, TabBar, TabPosition};
 
-pub struct TabBarStory {
-    tab_bar_focus_handle: FocusHandle,
-}
-
-impl TabBarStory {
-    pub fn new(cx: &mut ViewContext<Self>) -> Self {
-        Self {
-            tab_bar_focus_handle: cx.focus_handle(),
-        }
-    }
-}
+pub struct TabBarStory;
 
 impl Render for TabBarStory {
     type Element = Div;
@@ -48,7 +38,7 @@ impl Render for TabBarStory {
             .child(Story::label("Default"))
             .child(
                 h_stack().child(
-                    TabBar::new("tab_bar_1", self.tab_bar_focus_handle.clone())
+                    TabBar::new("tab_bar_1")
                         .start_child(
                             IconButton::new("navigate_backward", Icon::ArrowLeft)
                                 .icon_size(IconSize::Small),

crates/ui2/src/components/tab_bar.rs 🔗

@@ -1,22 +1,20 @@
-use gpui::{AnyElement, FocusHandle, Focusable, Stateful};
+use gpui::{AnyElement, Stateful};
 use smallvec::SmallVec;
 
 use crate::prelude::*;
 
 #[derive(IntoElement)]
 pub struct TabBar {
-    id: ElementId,
-    focus_handle: FocusHandle,
+    div: Stateful<Div>,
     start_children: SmallVec<[AnyElement; 2]>,
     children: SmallVec<[AnyElement; 2]>,
     end_children: SmallVec<[AnyElement; 2]>,
 }
 
 impl TabBar {
-    pub fn new(id: impl Into<ElementId>, focus_handle: FocusHandle) -> Self {
+    pub fn new(id: impl Into<ElementId>) -> Self {
         Self {
-            id: id.into(),
-            focus_handle,
+            div: div().id(id),
             start_children: SmallVec::new(),
             children: SmallVec::new(),
             end_children: SmallVec::new(),
@@ -83,16 +81,22 @@ impl ParentElement for TabBar {
     }
 }
 
+impl InteractiveElement for TabBar {
+    fn interactivity(&mut self) -> &mut gpui::Interactivity {
+        self.div.interactivity()
+    }
+}
+
+impl StatefulInteractiveElement for TabBar {}
+
 impl RenderOnce for TabBar {
-    type Rendered = Focusable<Stateful<Div>>;
+    type Rendered = Stateful<Div>;
 
     fn render(self, cx: &mut WindowContext) -> Self::Rendered {
         const HEIGHT_IN_REMS: f32 = 30. / 16.;
 
-        div()
-            .id(self.id)
+        self.div
             .group("tab_bar")
-            .track_focus(&self.focus_handle)
             .flex()
             .flex_none()
             .w_full()
@@ -128,6 +132,7 @@ impl RenderOnce for TabBar {
                         h_stack()
                             .id("tabs")
                             .z_index(2)
+                            .flex_grow()
                             .overflow_x_scroll()
                             .children(self.children),
                     ),

crates/workspace2/src/pane.rs 🔗

@@ -7,10 +7,10 @@ use crate::{
 use anyhow::Result;
 use collections::{HashMap, HashSet, VecDeque};
 use gpui::{
-    actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AnyDrag, AnyWeakView,
-    AppContext, AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle,
-    Focusable, FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel,
-    Render, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
+    actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AnyWeakView, AppContext,
+    AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable,
+    FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel, Render,
+    Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
 };
 use parking_lot::Mutex;
 use project::{Project, ProjectEntryId, ProjectPath};
@@ -32,7 +32,7 @@ use ui::{
     Indicator, Label, Tab, TabBar, TabPosition, Tooltip,
 };
 use ui::{v_stack, ContextMenu};
-use util::{maybe, truncate_and_remove_front};
+use util::{maybe, truncate_and_remove_front, ResultExt};
 
 #[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
 #[serde(rename_all = "camelCase")]
@@ -235,6 +235,7 @@ pub struct NavigationEntry {
 struct DraggedTab {
     pub pane: View<Pane>,
     pub ix: usize,
+    pub item_id: EntityId,
     pub detail: usize,
     pub is_active: bool,
 }
@@ -1486,8 +1487,7 @@ impl Pane {
             Some(Indicator::dot().color(indicator_color))
         });
 
-        let id = item.item_id();
-
+        let item_id = item.item_id();
         let is_first_item = ix == 0;
         let is_last_item = ix == self.items.len() - 1;
         let position_relative_to_active_item = ix.cmp(&self.active_item_index);
@@ -1515,15 +1515,18 @@ impl Pane {
                         cx.build_view(|cx| DraggedTab {
                             pane: pane.clone(),
                             detail,
+                            item_id,
                             is_active,
                             ix,
                         })
                     }
                 })
-                // .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
-                // .on_drop(|_view, state: View<DraggedTab>, cx| {
-                //     eprintln!("{:?}", state.read(cx));
-                // })
+                .drag_over::<DraggedTab>(|tab| tab.bg(cx.theme().colors().tab_active_background))
+                .on_drop(
+                    cx.listener(move |this, dragged_tab: &View<DraggedTab>, cx| {
+                        this.handle_tab_drop(dragged_tab, ix, cx)
+                    }),
+                )
                 .when_some(item.tab_tooltip_text(cx), |tab, text| {
                     tab.tooltip(move |cx| Tooltip::text(text.clone(), cx))
                 })
@@ -1534,7 +1537,7 @@ impl Pane {
                         .size(ButtonSize::None)
                         .icon_size(IconSize::XSmall)
                         .on_click(cx.listener(move |pane, _, cx| {
-                            pane.close_item_by_id(id, SaveIntent::Close, cx)
+                            pane.close_item_by_id(item_id, SaveIntent::Close, cx)
                                 .detach_and_log_err(cx);
                         })),
                 )
@@ -1580,7 +1583,7 @@ impl Pane {
     }
 
     fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement {
-        TabBar::new("tab_bar", self.tab_bar_focus_handle.clone())
+        TabBar::new("tab_bar")
             .start_child(
                 IconButton::new("navigate_backward", Icon::ArrowLeft)
                     .icon_size(IconSize::Small)
@@ -1653,6 +1656,20 @@ impl Pane {
                     .zip(self.tab_details(cx))
                     .map(|((ix, item), detail)| self.render_tab(ix, item, detail, cx)),
             )
+            .child(
+                div()
+                    .h_full()
+                    .flex_grow()
+                    .drag_over::<DraggedTab>(|bar| {
+                        bar.bg(cx.theme().colors().tab_active_background)
+                    })
+                    .on_drop(
+                        cx.listener(move |this, dragged_tab: &View<DraggedTab>, cx| {
+                            this.handle_tab_drop(dragged_tab, this.items.len(), cx)
+                        }),
+                    ),
+            )
+            .track_focus(&self.tab_bar_focus_handle)
     }
 
     fn render_menu_overlay(menu: &View<ContextMenu>) -> Div {
@@ -1665,161 +1682,6 @@ impl Pane {
             .child(overlay().anchor(AnchorCorner::TopRight).child(menu.clone()))
     }
 
-    //     fn render_tabs(&mut self, cx: &mut ViewContext<Self>) -> impl Element<Self> {
-    //         let theme = theme::current(cx).clone();
-
-    //         let pane = cx.handle().downgrade();
-    //         let autoscroll = if mem::take(&mut self.autoscroll) {
-    //             Some(self.active_item_index)
-    //         } else {
-    //             None
-    //         };
-
-    //         let pane_active = self.has_focus;
-
-    //         enum Tabs {}
-    //         let mut row = Flex::row().scrollable::<Tabs>(1, autoscroll, cx);
-    //         for (ix, (item, detail)) in self
-    //             .items
-    //             .iter()
-    //             .cloned()
-    //             .zip(self.tab_details(cx))
-    //             .enumerate()
-    //         {
-    //             let git_status = item
-    //                 .project_path(cx)
-    //                 .and_then(|path| self.project.read(cx).entry_for_path(&path, cx))
-    //                 .and_then(|entry| entry.git_status());
-
-    //             let detail = if detail == 0 { None } else { Some(detail) };
-    //             let tab_active = ix == self.active_item_index;
-
-    //             row.add_child({
-    //                 enum TabDragReceiver {}
-    //                 let mut receiver =
-    //                     dragged_item_receiver::<TabDragReceiver, _, _>(self, ix, ix, true, None, cx, {
-    //                         let item = item.clone();
-    //                         let pane = pane.clone();
-    //                         let detail = detail.clone();
-
-    //                         let theme = theme::current(cx).clone();
-    //                         let mut tooltip_theme = theme.tooltip.clone();
-    //                         tooltip_theme.max_text_width = None;
-    //                         let tab_tooltip_text =
-    //                             item.tab_tooltip_text(cx).map(|text| text.into_owned());
-
-    //                         let mut tab_style = theme
-    //                             .workspace
-    //                             .tab_bar
-    //                             .tab_style(pane_active, tab_active)
-    //                             .clone();
-    //                         let should_show_status = settings::get::<ItemSettings>(cx).git_status;
-    //                         if should_show_status && git_status != None {
-    //                             tab_style.label.text.color = match git_status.unwrap() {
-    //                                 GitFileStatus::Added => tab_style.git.inserted,
-    //                                 GitFileStatus::Modified => tab_style.git.modified,
-    //                                 GitFileStatus::Conflict => tab_style.git.conflict,
-    //                             };
-    //                         }
-
-    //                         move |mouse_state, cx| {
-    //                             let hovered = mouse_state.hovered();
-
-    //                             enum Tab {}
-    //                             let mouse_event_handler =
-    //                                 MouseEventHandler::new::<Tab, _>(ix, cx, |_, cx| {
-    //                                     Self::render_tab(
-    //                                         &item,
-    //                                         pane.clone(),
-    //                                         ix == 0,
-    //                                         detail,
-    //                                         hovered,
-    //                                         &tab_style,
-    //                                         cx,
-    //                                     )
-    //                                 })
-    //                                 .on_down(MouseButton::Left, move |_, this, cx| {
-    //                                     this.activate_item(ix, true, true, cx);
-    //                                 })
-    //                                 .on_click(MouseButton::Middle, {
-    //                                     let item_id = item.id();
-    //                                     move |_, pane, cx| {
-    //                                         pane.close_item_by_id(item_id, SaveIntent::Close, cx)
-    //                                             .detach_and_log_err(cx);
-    //                                     }
-    //                                 })
-    //                                 .on_down(
-    //                                     MouseButton::Right,
-    //                                     move |event, pane, cx| {
-    //                                         pane.deploy_tab_context_menu(event.position, item.id(), cx);
-    //                                     },
-    //                                 );
-
-    //                             if let Some(tab_tooltip_text) = tab_tooltip_text {
-    //                                 mouse_event_handler
-    //                                     .with_tooltip::<Self>(
-    //                                         ix,
-    //                                         tab_tooltip_text,
-    //                                         None,
-    //                                         tooltip_theme,
-    //                                         cx,
-    //                                     )
-    //                                     .into_any()
-    //                             } else {
-    //                                 mouse_event_handler.into_any()
-    //                             }
-    //                         }
-    //                     });
-
-    //                 if !pane_active || !tab_active {
-    //                     receiver = receiver.with_cursor_style(CursorStyle::PointingHand);
-    //                 }
-
-    //                 receiver.as_draggable(
-    //                     DraggedItem {
-    //                         handle: item,
-    //                         pane: pane.clone(),
-    //                     },
-    //                     {
-    //                         let theme = theme::current(cx).clone();
-
-    //                         let detail = detail.clone();
-    //                         move |_, dragged_item: &DraggedItem, cx: &mut ViewContext<Workspace>| {
-    //                             let tab_style = &theme.workspace.tab_bar.dragged_tab;
-    //                             Self::render_dragged_tab(
-    //                                 &dragged_item.handle,
-    //                                 dragged_item.pane.clone(),
-    //                                 false,
-    //                                 detail,
-    //                                 false,
-    //                                 &tab_style,
-    //                                 cx,
-    //                             )
-    //                         }
-    //                     },
-    //                 )
-    //             })
-    //         }
-
-    //         // Use the inactive tab style along with the current pane's active status to decide how to render
-    //         // the filler
-    //         let filler_index = self.items.len();
-    //         let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false);
-    //         enum Filler {}
-    //         row.add_child(
-    //             dragged_item_receiver::<Filler, _, _>(self, 0, filler_index, true, None, cx, |_, _| {
-    //                 Empty::new()
-    //                     .contained()
-    //                     .with_style(filler_style.container)
-    //                     .with_border(filler_style.container.border)
-    //             })
-    //             .flex(1., true)
-    //             .into_any_named("filler"),
-    //         );
-
-    //         row
-    //     }
-
     fn tab_details(&self, cx: &AppContext) -> Vec<usize> {
         let mut tab_details = self.items.iter().map(|_| 0).collect::<Vec<_>>();
 
@@ -1857,192 +1719,6 @@ impl Pane {
         tab_details
     }
 
-    //     fn render_tab(
-    //         item: &Box<dyn ItemHandle>,
-    //         pane: WeakView<Pane>,
-    //         first: bool,
-    //         detail: Option<usize>,
-    //         hovered: bool,
-    //         tab_style: &theme::Tab,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> AnyElement<Self> {
-    //         let title = item.tab_content(detail, &tab_style, cx);
-    //         Self::render_tab_with_title(title, item, pane, first, hovered, tab_style, cx)
-    //     }
-
-    //     fn render_dragged_tab(
-    //         item: &Box<dyn ItemHandle>,
-    //         pane: WeakView<Pane>,
-    //         first: bool,
-    //         detail: Option<usize>,
-    //         hovered: bool,
-    //         tab_style: &theme::Tab,
-    //         cx: &mut ViewContext<Workspace>,
-    //     ) -> AnyElement<Workspace> {
-    //         let title = item.dragged_tab_content(detail, &tab_style, cx);
-    //         Self::render_tab_with_title(title, item, pane, first, hovered, tab_style, cx)
-    //     }
-
-    //     fn render_tab_with_title<T: View>(
-    //         title: AnyElement<T>,
-    //         item: &Box<dyn ItemHandle>,
-    //         pane: WeakView<Pane>,
-    //         first: bool,
-    //         hovered: bool,
-    //         tab_style: &theme::Tab,
-    //         cx: &mut ViewContext<T>,
-    //     ) -> AnyElement<T> {
-    //         let mut container = tab_style.container.clone();
-    //         if first {
-    //             container.border.left = false;
-    //         }
-
-    //         let buffer_jewel_element = {
-    //             let diameter = 7.0;
-    //             let icon_color = if item.has_conflict(cx) {
-    //                 Some(tab_style.icon_conflict)
-    //             } else if item.is_dirty(cx) {
-    //                 Some(tab_style.icon_dirty)
-    //             } else {
-    //                 None
-    //             };
-
-    //             Canvas::new(move |bounds, _, _, cx| {
-    //                 if let Some(color) = icon_color {
-    //                     let square = RectF::new(bounds.origin(), vec2f(diameter, diameter));
-    //                     cx.scene().push_quad(Quad {
-    //                         bounds: square,
-    //                         background: Some(color),
-    //                         border: Default::default(),
-    //                         corner_radii: (diameter / 2.).into(),
-    //                     });
-    //                 }
-    //             })
-    //             .constrained()
-    //             .with_width(diameter)
-    //             .with_height(diameter)
-    //             .aligned()
-    //         };
-
-    //         let title_element = title.aligned().contained().with_style(ContainerStyle {
-    //             margin: Margin {
-    //                 left: tab_style.spacing,
-    //                 right: tab_style.spacing,
-    //                 ..Default::default()
-    //             },
-    //             ..Default::default()
-    //         });
-
-    //         let close_element = if hovered {
-    //             let item_id = item.id();
-    //             enum TabCloseButton {}
-    //             let icon = Svg::new("icons/x.svg");
-    //             MouseEventHandler::new::<TabCloseButton, _>(item_id, cx, |mouse_state, _| {
-    //                 if mouse_state.hovered() {
-    //                     icon.with_color(tab_style.icon_close_active)
-    //                 } else {
-    //                     icon.with_color(tab_style.icon_close)
-    //                 }
-    //             })
-    //             .with_padding(Padding::uniform(4.))
-    //             .with_cursor_style(CursorStyle::PointingHand)
-    //             .on_click(MouseButton::Left, {
-    //                 let pane = pane.clone();
-    //                 move |_, _, cx| {
-    //                     let pane = pane.clone();
-    //                     cx.window_context().defer(move |cx| {
-    //                         if let Some(pane) = pane.upgrade(cx) {
-    //                             pane.update(cx, |pane, cx| {
-    //                                 pane.close_item_by_id(item_id, SaveIntent::Close, cx)
-    //                                     .detach_and_log_err(cx);
-    //                             });
-    //                         }
-    //                     });
-    //                 }
-    //             })
-    //             .into_any_named("close-tab-icon")
-    //             .constrained()
-    //         } else {
-    //             Empty::new().constrained()
-    //         }
-    //         .with_width(tab_style.close_icon_width)
-    //         .aligned();
-
-    //         let close_right = settings::get::<ItemSettings>(cx).close_position.right();
-
-    //         if close_right {
-    //             Flex::row()
-    //                 .with_child(buffer_jewel_element)
-    //                 .with_child(title_element)
-    //                 .with_child(close_element)
-    //         } else {
-    //             Flex::row()
-    //                 .with_child(close_element)
-    //                 .with_child(title_element)
-    //                 .with_child(buffer_jewel_element)
-    //         }
-    //         .contained()
-    //         .with_style(container)
-    //         .constrained()
-    //         .with_height(tab_style.height)
-    //         .into_any()
-    //     }
-
-    //     pub fn render_tab_bar_button<
-    //         F1: 'static + Fn(&mut Pane, &mut EventContext<Pane>),
-    //         F2: 'static + Fn(&mut Pane, &mut EventContext<Pane>),
-    //     >(
-    //         index: usize,
-    //         icon: &'static str,
-    //         is_active: bool,
-    //         tooltip: Option<(&'static str, Option<Box<dyn Action>>)>,
-    //         cx: &mut ViewContext<Pane>,
-    //         on_click: F1,
-    //         on_down: F2,
-    //         context_menu: Option<ViewHandle<ContextMenu>>,
-    //     ) -> AnyElement<Pane> {
-    //         enum TabBarButton {}
-
-    //         let mut button = MouseEventHandler::new::<TabBarButton, _>(index, cx, |mouse_state, cx| {
-    //             let theme = &settings2::get::<ThemeSettings>(cx).theme.workspace.tab_bar;
-    //             let style = theme.pane_button.in_state(is_active).style_for(mouse_state);
-    //             Svg::new(icon)
-    //                 .with_color(style.color)
-    //                 .constrained()
-    //                 .with_width(style.icon_width)
-    //                 .aligned()
-    //                 .constrained()
-    //                 .with_width(style.button_width)
-    //                 .with_height(style.button_width)
-    //         })
-    //         .with_cursor_style(CursorStyle::PointingHand)
-    //         .on_down(MouseButton::Left, move |_, pane, cx| on_down(pane, cx))
-    //         .on_click(MouseButton::Left, move |_, pane, cx| on_click(pane, cx))
-    //         .into_any();
-    //         if let Some((tooltip, action)) = tooltip {
-    //             let tooltip_style = settings::get::<ThemeSettings>(cx).theme.tooltip.clone();
-    //             button = button
-    //                 .with_tooltip::<TabBarButton>(index, tooltip, action, tooltip_style, cx)
-    //                 .into_any();
-    //         }
-
-    //         Stack::new()
-    //             .with_child(button)
-    //             .with_children(
-    //                 context_menu.map(|menu| ChildView::new(&menu, cx).aligned().bottom().right()),
-    //             )
-    //             .flex(1., false)
-    //             .into_any_named("tab bar button")
-    //     }
-
-    //     fn render_blank_pane(&self, theme: &Theme, _cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-    //         let background = theme.workspace.background;
-    //         Empty::new()
-    //             .contained()
-    //             .with_background_color(background)
-    //             .into_any()
-    //     }
-
     pub fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>) {
         self.zoomed = zoomed;
         cx.notify();
@@ -2051,6 +1727,25 @@ impl Pane {
     pub fn is_zoomed(&self) -> bool {
         self.zoomed
     }
+
+    fn handle_tab_drop(
+        &mut self,
+        dragged_tab: &View<DraggedTab>,
+        ix: usize,
+        cx: &mut ViewContext<'_, Pane>,
+    ) {
+        let dragged_tab = dragged_tab.read(cx);
+        let item_id = dragged_tab.item_id;
+        let from_pane = dragged_tab.pane.clone();
+        let to_pane = cx.view().clone();
+        self.workspace
+            .update(cx, |workspace, cx| {
+                cx.defer(move |workspace, cx| {
+                    workspace.move_item(from_pane, to_pane, item_id, ix, cx);
+                });
+            })
+            .log_err();
+    }
 }
 
 impl FocusableView for Pane {
@@ -2152,148 +1847,6 @@ impl Render for Pane {
                     .justify_center()
                     .child(Label::new("Open a file or project to get started.").color(Color::Muted))
             })
-            // enum MouseNavigationHandler {}
-            // MouseEventHandler::new::<MouseNavigationHandler, _>(0, cx, |_, cx| {
-            //     let active_item_index = self.active_item_index;
-            //     if let Some(active_item) = self.active_item() {
-            //         Flex::column()
-            //             .with_child({
-            //                 let theme = theme::current(cx).clone();
-            //                 let mut stack = Stack::new();
-            //                 enum TabBarEventHandler {}
-            //                 stack.add_child(
-            //                     MouseEventHandler::new::<TabBarEventHandler, _>(0, cx, |_, _| {
-            //                         Empty::new()
-            //                             .contained()
-            //                             .with_style(theme.workspace.tab_bar.container)
-            //                     })
-            //                     .on_down(
-            //                         MouseButton::Left,
-            //                         move |_, this, cx| {
-            //                             this.activate_item(active_item_index, true, true, cx);
-            //                         },
-            //                     ),
-            //                 );
-            //                 let tooltip_style = theme.tooltip.clone();
-            //                 let tab_bar_theme = theme.workspace.tab_bar.clone();
-            //                 let nav_button_height = tab_bar_theme.height;
-            //                 let button_style = tab_bar_theme.nav_button;
-            //                 let border_for_nav_buttons = tab_bar_theme
-            //                     .tab_style(false, false)
-            //                     .container
-            //                     .border
-            //                     .clone();
-            //                 let mut tab_row = Flex::row()
-            //                     .with_child(nav_button(
-            //                         "icons/arrow_left.svg",
-            //                         button_style.clone(),
-            //                         nav_button_height,
-            //                         tooltip_style.clone(),
-            //                         self.can_navigate_backward(),
-            //                         {
-            //                             move |pane, cx| {
-            //                                 if let Some(workspace) = pane.workspace.upgrade(cx) {
-            //                                     let pane = cx.weak_handle();
-            //                                     cx.window_context().defer(move |cx| {
-            //                                         workspace.update(cx, |workspace, cx| {
-            //                                             workspace
-            //                                                 .go_back(pane, cx)
-            //                                                 .detach_and_log_err(cx)
-            //                                         })
-            //                                     })
-            //                                 }
-            //                             }
-            //                         },
-            //                         super::GoBack,
-            //                         "Go Back",
-            //                         cx,
-            //                     ))
-            //                     .with_child(
-            //                         nav_button(
-            //                             "icons/arrow_right.svg",
-            //                             button_style.clone(),
-            //                             nav_button_height,
-            //                             tooltip_style,
-            //                             self.can_navigate_forward(),
-            //                             {
-            //                                 move |pane, cx| {
-            //                                     if let Some(workspace) = pane.workspace.upgrade(cx) {
-            //                                         let pane = cx.weak_handle();
-            //                                         cx.window_context().defer(move |cx| {
-            //                                             workspace.update(cx, |workspace, cx| {
-            //                                                 workspace
-            //                                                     .go_forward(pane, cx)
-            //                                                     .detach_and_log_err(cx)
-            //                                             })
-            //                                         })
-            //                                     }
-            //                                 }
-            //                             },
-            //                             super::GoForward,
-            //                             "Go Forward",
-            //                             cx,
-            //                         )
-            //                         .contained()
-            //                         .with_border(border_for_nav_buttons),
-            //                     )
-            //                     .with_child(self.render_tabs(cx).flex(1., true).into_any_named("tabs"));
-            //                 if self.has_focus {
-            //                     let render_tab_bar_buttons = self.render_tab_bar_buttons.clone();
-            //                     tab_row.add_child(
-            //                         (render_tab_bar_buttons)(self, cx)
-            //                             .contained()
-            //                             .with_style(theme.workspace.tab_bar.pane_button_container)
-            //                             .flex(1., false)
-            //                             .into_any(),
-            //                     )
-            //                 }
-            //                 stack.add_child(tab_row);
-            //                 stack
-            //                     .constrained()
-            //                     .with_height(theme.workspace.tab_bar.height)
-            //                     .flex(1., false)
-            //                     .into_any_named("tab bar")
-            //             })
-            //             .with_child({
-            //                 enum PaneContentTabDropTarget {}
-            //                 dragged_item_receiver::<PaneContentTabDropTarget, _, _>(
-            //                     self,
-            //                     0,
-            //                     self.active_item_index + 1,
-            //                     !self.can_split,
-            //                     if self.can_split { Some(100.) } else { None },
-            //                     cx,
-            //                     {
-            //                         let toolbar = self.toolbar.clone();
-            //                         let toolbar_hidden = toolbar.read(cx).hidden();
-            //                         move |_, cx| {
-            //                             Flex::column()
-            //                                 .with_children(
-            //                                     (!toolbar_hidden)
-            //                                         .then(|| ChildView::new(&toolbar, cx).expanded()),
-            //                                 )
-            //                                 .with_child(
-            //                                     ChildView::new(active_item.as_any(), cx).flex(1., true),
-            //                                 )
-            //                         }
-            //                     },
-            //                 )
-            //                 .flex(1., true)
-            //             })
-            //             .with_child(ChildView::new(&self.tab_context_menu, cx))
-            //             .into_any()
-            //     } else {
-            //         enum EmptyPane {}
-            //         let theme = theme::current(cx).clone();
-            //         dragged_item_receiver::<EmptyPane, _, _>(self, 0, 0, false, None, cx, |_, cx| {
-            //             self.render_blank_pane(&theme, cx)
-            //         })
-            //         .on_down(MouseButton::Left, |_, _, cx| {
-            //             cx.focus_parent();
-            //         })
-            //         .into_any()
-            //     }
-            // })
             .on_mouse_down(
                 MouseButton::Navigate(NavigationDirection::Back),
                 cx.listener(|pane, _, cx| {
@@ -2320,7 +1873,6 @@ impl Render for Pane {
                     }
                 }),
             )
-        // .into_any_named("pane")
     }
 
     // fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {