Take a target view when marking an element as draggable

Antonio Scandurra created

Change summary

crates/diagnostics/src/diagnostics.rs         |  8 ++--
crates/drag_and_drop/src/drag_and_drop.rs     | 16 +++++-----
crates/editor/src/items.rs                    |  4 +-
crates/feedback/src/feedback_editor.rs        |  9 ++++-
crates/project_panel/src/project_panel.rs     |  2 
crates/search/src/project_search.rs           |  4 +-
crates/terminal_view/src/terminal_view.rs     |  4 +-
crates/theme_testbench/src/theme_testbench.rs |  7 +++
crates/welcome/src/welcome.rs                 |  6 +-
crates/workspace/src/item.rs                  | 23 ++++++++++++--
crates/workspace/src/pane.rs                  | 33 ++++++++++++++++++--
crates/workspace/src/shared_screen.rs         |  6 +-
12 files changed, 86 insertions(+), 36 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

@@ -530,12 +530,12 @@ impl ProjectDiagnosticsEditor {
 }
 
 impl Item for ProjectDiagnosticsEditor {
-    fn tab_content(
+    fn tab_content<T: View>(
         &self,
         _detail: Option<usize>,
         style: &theme::Tab,
         cx: &AppContext,
-    ) -> Element<Pane> {
+    ) -> Element<T> {
         render_summary(
             &self.summary,
             &style.label.text,
@@ -717,11 +717,11 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
     })
 }
 
-pub(crate) fn render_summary(
+pub(crate) fn render_summary<T: View>(
     summary: &DiagnosticSummary,
     text_style: &TextStyle,
     theme: &theme::ProjectDiagnostics,
-) -> Element<Pane> {
+) -> Element<T> {
     if summary.error_count == 0 && summary.warning_count == 0 {
         Label::new("No problems", text_style.clone()).boxed()
     } else {

crates/drag_and_drop/src/drag_and_drop.rs 🔗

@@ -111,7 +111,7 @@ impl<V: View> DragAndDrop<V> {
         })
     }
 
-    pub fn drag_started(event: MouseDown, cx: &mut ViewContext<V>) {
+    pub fn drag_started(event: MouseDown, cx: &mut WindowContext) {
         cx.update_global(|this: &mut Self, _| {
             this.currently_dragged = Some(State::Down {
                 region_offset: event.position - event.region.origin(),
@@ -123,7 +123,7 @@ impl<V: View> DragAndDrop<V> {
     pub fn dragging<T: Any>(
         event: MouseDrag,
         payload: Rc<T>,
-        cx: &mut ViewContext<V>,
+        cx: &mut WindowContext,
         render: Rc<impl 'static + Fn(&T, &mut ViewContext<V>) -> Element<V>>,
     ) {
         let window_id = cx.window_id();
@@ -297,20 +297,20 @@ impl<V: View> DragAndDrop<V> {
 }
 
 pub trait Draggable<V: View> {
-    fn as_draggable<P: Any>(
+    fn as_draggable<D: View, P: Any>(
         self,
         payload: P,
-        render: impl 'static + Fn(&P, &mut ViewContext<V>) -> Element<V>,
+        render: impl 'static + Fn(&P, &mut ViewContext<D>) -> Element<D>,
     ) -> Self
     where
         Self: Sized;
 }
 
 impl<Tag, V: View> Draggable<V> for MouseEventHandler<Tag, V> {
-    fn as_draggable<P: Any>(
+    fn as_draggable<D: View, P: Any>(
         self,
         payload: P,
-        render: impl 'static + Fn(&P, &mut ViewContext<V>) -> Element<V>,
+        render: impl 'static + Fn(&P, &mut ViewContext<D>) -> Element<D>,
     ) -> Self
     where
         Self: Sized,
@@ -319,12 +319,12 @@ impl<Tag, V: View> Draggable<V> for MouseEventHandler<Tag, V> {
         let render = Rc::new(render);
         self.on_down(MouseButton::Left, move |e, _, cx| {
             cx.propagate_event();
-            DragAndDrop::<V>::drag_started(e, cx);
+            DragAndDrop::<D>::drag_started(e, cx);
         })
         .on_drag(MouseButton::Left, move |e, _, cx| {
             let payload = payload.clone();
             let render = render.clone();
-            DragAndDrop::<V>::dragging(e, payload, cx, render)
+            DragAndDrop::<D>::dragging(e, payload, cx, render)
         })
     }
 }

crates/editor/src/items.rs 🔗

@@ -558,12 +558,12 @@ impl Item for Editor {
         }
     }
 
-    fn tab_content(
+    fn tab_content<T: View>(
         &self,
         detail: Option<usize>,
         style: &theme::Tab,
         cx: &AppContext,
-    ) -> Element<Pane> {
+    ) -> Element<T> {
         Flex::row()
             .with_child(
                 Label::new(self.title(cx).to_string(), style.label.clone())

crates/feedback/src/feedback_editor.rs 🔗

@@ -26,7 +26,7 @@ use util::ResultExt;
 use workspace::{
     item::{Item, ItemHandle},
     searchable::{SearchableItem, SearchableItemHandle},
-    AppState, Pane, Workspace,
+    AppState, Workspace,
 };
 
 use crate::{submit_feedback_button::SubmitFeedbackButton, system_specs::SystemSpecs};
@@ -250,7 +250,12 @@ impl Item for FeedbackEditor {
         Some("Send Feedback".into())
     }
 
-    fn tab_content(&self, _: Option<usize>, style: &theme::Tab, _: &AppContext) -> Element<Pane> {
+    fn tab_content<T: View>(
+        &self,
+        _: Option<usize>,
+        style: &theme::Tab,
+        _: &AppContext,
+    ) -> Element<T> {
         Flex::row()
             .with_child(
                 Svg::new("icons/feedback_16.svg")

crates/project_panel/src/project_panel.rs 🔗

@@ -1251,7 +1251,7 @@ impl ProjectPanel {
         .as_draggable(entry_id, {
             let row_container_style = theme.dragged_entry.container;
 
-            move |_, cx: &mut ViewContext<ProjectPanel>| {
+            move |_, cx: &mut ViewContext<Workspace>| {
                 let theme = cx.global::<Settings>().theme.clone();
                 Self::render_entry_visual_element(
                     &details,

crates/search/src/project_search.rs 🔗

@@ -249,12 +249,12 @@ impl Item for ProjectSearchView {
             .update(cx, |editor, cx| editor.deactivated(cx));
     }
 
-    fn tab_content(
+    fn tab_content<T: View>(
         &self,
         _detail: Option<usize>,
         tab_theme: &theme::Tab,
         cx: &AppContext,
-    ) -> Element<Pane> {
+    ) -> Element<T> {
         Flex::row()
             .with_child(
                 Svg::new("icons/magnifying_glass_12.svg")

crates/terminal_view/src/terminal_view.rs 🔗

@@ -546,12 +546,12 @@ impl Item for TerminalView {
         Some(self.terminal().read(cx).title().into())
     }
 
-    fn tab_content(
+    fn tab_content<T: View>(
         &self,
         _detail: Option<usize>,
         tab_theme: &theme::Tab,
         cx: &gpui::AppContext,
-    ) -> Element<Pane> {
+    ) -> Element<T> {
         let title = self.terminal().read(cx).title();
 
         Flex::row()

crates/theme_testbench/src/theme_testbench.rs 🔗

@@ -298,7 +298,12 @@ impl View for ThemeTestbench {
 }
 
 impl Item for ThemeTestbench {
-    fn tab_content(&self, _: Option<usize>, style: &theme::Tab, _: &AppContext) -> Element<Pane> {
+    fn tab_content<T: View>(
+        &self,
+        _: Option<usize>,
+        style: &theme::Tab,
+        _: &AppContext,
+    ) -> Element<T> {
         Label::new("Theme Testbench", style.label.clone())
             .aligned()
             .contained()

crates/welcome/src/welcome.rs 🔗

@@ -10,7 +10,7 @@ use gpui::{
 use settings::{settings_file::SettingsFile, Settings};
 
 use workspace::{
-    item::Item, open_new, sidebar::SidebarSide, AppState, Pane, PaneBackdrop, Welcome, Workspace,
+    item::Item, open_new, sidebar::SidebarSide, AppState, PaneBackdrop, Welcome, Workspace,
     WorkspaceId,
 };
 
@@ -202,12 +202,12 @@ impl Item for WelcomePage {
         Some("Welcome to Zed!".into())
     }
 
-    fn tab_content(
+    fn tab_content<T: View>(
         &self,
         _detail: Option<usize>,
         style: &theme::Tab,
         _cx: &gpui::AppContext,
-    ) -> Element<Pane> {
+    ) -> Element<T> {
         Flex::row()
             .with_child(
                 Label::new("Welcome to Zed!", style.label.clone())

crates/workspace/src/item.rs 🔗

@@ -54,12 +54,12 @@ pub trait Item: View {
     fn tab_description<'a>(&'a self, _: usize, _: &'a AppContext) -> Option<Cow<str>> {
         None
     }
-    fn tab_content(
+    fn tab_content<V: View>(
         &self,
         detail: Option<usize>,
         style: &theme::Tab,
         cx: &AppContext,
-    ) -> Element<Pane>;
+    ) -> Element<V>;
     fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project::Item)) {}
     fn is_singleton(&self, _cx: &AppContext) -> bool {
         false
@@ -181,6 +181,12 @@ pub trait ItemHandle: 'static + fmt::Debug {
         style: &theme::Tab,
         cx: &AppContext,
     ) -> Element<Pane>;
+    fn dragged_tab_content(
+        &self,
+        detail: Option<usize>,
+        style: &theme::Tab,
+        cx: &AppContext,
+    ) -> Element<Workspace>;
     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
     fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]>;
     fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[usize; 3]>;
@@ -281,6 +287,15 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
         self.read(cx).tab_content(detail, style, cx)
     }
 
+    fn dragged_tab_content(
+        &self,
+        detail: Option<usize>,
+        style: &theme::Tab,
+        cx: &AppContext,
+    ) -> Element<Workspace> {
+        self.read(cx).tab_content(detail, style, cx)
+    }
+
     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
         let this = self.read(cx);
         let mut result = None;
@@ -927,12 +942,12 @@ pub(crate) mod test {
             })
         }
 
-        fn tab_content(
+        fn tab_content<V: View>(
             &self,
             detail: Option<usize>,
             _: &theme::Tab,
             _: &AppContext,
-        ) -> Element<Pane> {
+        ) -> Element<V> {
             self.tab_detail.set(detail);
             Empty::new().boxed()
         }

crates/workspace/src/pane.rs 🔗

@@ -1401,7 +1401,7 @@ impl Pane {
                             enum Tab {}
                             let mouse_event_handler =
                                 MouseEventHandler::<Tab, Pane>::new(ix, cx, |_, cx| {
-                                    Self::render_tab::<Pane>(
+                                    Self::render_tab(
                                         &item,
                                         pane.clone(),
                                         ix == 0,
@@ -1466,9 +1466,9 @@ impl Pane {
                             let theme = cx.global::<Settings>().theme.clone();
 
                             let detail = detail.clone();
-                            move |dragged_item: &DraggedItem, cx: &mut ViewContext<Pane>| {
+                            move |dragged_item: &DraggedItem, cx: &mut ViewContext<Workspace>| {
                                 let tab_style = &theme.workspace.tab_bar.dragged_tab;
-                                Self::render_tab::<Pane>(
+                                Self::render_dragged_tab(
                                     &dragged_item.item,
                                     dragged_item.pane.clone(),
                                     false,
@@ -1541,7 +1541,7 @@ impl Pane {
         tab_details
     }
 
-    fn render_tab<V: View>(
+    fn render_tab(
         item: &Box<dyn ItemHandle>,
         pane: WeakViewHandle<Pane>,
         first: bool,
@@ -1551,6 +1551,31 @@ impl Pane {
         cx: &mut ViewContext<Self>,
     ) -> Element<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: WeakViewHandle<Pane>,
+        first: bool,
+        detail: Option<usize>,
+        hovered: bool,
+        tab_style: &theme::Tab,
+        cx: &mut ViewContext<Workspace>,
+    ) -> Element<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: Element<T>,
+        item: &Box<dyn ItemHandle>,
+        pane: WeakViewHandle<Pane>,
+        first: bool,
+        hovered: bool,
+        tab_style: &theme::Tab,
+        cx: &mut ViewContext<T>,
+    ) -> Element<T> {
         let mut container = tab_style.container.clone();
         if first {
             container.border.left = false;

crates/workspace/src/shared_screen.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     item::{Item, ItemEvent},
-    ItemNavHistory, Pane, WorkspaceId,
+    ItemNavHistory, WorkspaceId,
 };
 use anyhow::Result;
 use call::participant::{Frame, RemoteVideoTrack};
@@ -106,12 +106,12 @@ impl Item for SharedScreen {
         }
     }
 
-    fn tab_content(
+    fn tab_content<V: View>(
         &self,
         _: Option<usize>,
         style: &theme::Tab,
         _: &AppContext,
-    ) -> gpui::Element<Pane> {
+    ) -> gpui::Element<V> {
         Flex::row()
             .with_child(
                 Svg::new("icons/disable_screen_sharing_12.svg")