From f3fda8018f37afae08121b8ce28b195cf52a75b9 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 21 Dec 2023 17:52:29 -0500 Subject: [PATCH] Prevent drag-dropping non-terminal item into terminal pane --- crates/gpui2/src/elements/div.rs | 78 +++++++++++++++------ crates/terminal_view2/src/terminal_panel.rs | 9 +++ crates/workspace2/src/pane.rs | 17 ++++- crates/workspace2/src/workspace2.rs | 2 + 4 files changed, 81 insertions(+), 25 deletions(-) diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 90af9e130e..7c74eb1039 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -297,6 +297,10 @@ impl Interactivity { )); } + pub fn can_drop(&mut self, predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static) { + self.can_drop_predicate = Some(Box::new(predicate)); + } + pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static) where Self: Sized, @@ -569,6 +573,14 @@ pub trait InteractiveElement: Sized { self } + fn can_drop( + mut self, + predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static, + ) -> Self { + self.interactivity().can_drop(predicate); + self + } + fn block_mouse(mut self) -> Self { self.interactivity().block_mouse(); self @@ -699,6 +711,8 @@ pub type DragListener = Box AnyView + 's type DropListener = Box; +type CanDropPredicate = Box bool + 'static>; + pub type TooltipBuilder = Rc AnyView + 'static>; pub type KeyDownListener = Box; @@ -887,6 +901,7 @@ pub struct Interactivity { pub key_up_listeners: Vec, pub action_listeners: Vec<(TypeId, ActionListener)>, pub drop_listeners: Vec<(TypeId, DropListener)>, + pub can_drop_predicate: Option, pub click_listeners: Vec, pub drag_listener: Option<(Box, DragListener)>, pub hover_listener: Option>, @@ -1198,6 +1213,7 @@ impl Interactivity { let mut drag_listener = mem::take(&mut self.drag_listener); let drop_listeners = mem::take(&mut self.drop_listeners); let click_listeners = mem::take(&mut self.click_listeners); + let can_drop_predicate = mem::take(&mut self.can_drop_predicate); if !drop_listeners.is_empty() { cx.on_mouse_event({ @@ -1215,9 +1231,17 @@ impl Interactivity { "checked for type drag state type above", ); - listener(drag.value.as_ref(), cx); - cx.notify(); - cx.stop_propagation(); + let mut can_drop = true; + if let Some(predicate) = &can_drop_predicate { + can_drop = + predicate(drag.value.as_ref(), cx); + } + + if can_drop { + listener(drag.value.as_ref(), cx); + cx.notify(); + cx.stop_propagation(); + } } } } @@ -1596,27 +1620,36 @@ impl Interactivity { } if let Some(drag) = cx.active_drag.take() { - for (state_type, group_drag_style) in &self.group_drag_over_styles { - if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) { - if *state_type == drag.value.as_ref().type_id() - && group_bounds.contains(&mouse_position) - { - style.refine(&group_drag_style.style); - } - } + let mut can_drop = true; + if let Some(can_drop_predicate) = &self.can_drop_predicate { + can_drop = can_drop_predicate(drag.value.as_ref(), cx); } - for (state_type, drag_over_style) in &self.drag_over_styles { - if *state_type == drag.value.as_ref().type_id() - && bounds - .intersect(&cx.content_mask().bounds) - .contains(&mouse_position) - && cx.was_top_layer_under_active_drag( - &mouse_position, - cx.stacking_order(), - ) - { - style.refine(drag_over_style); + if can_drop { + for (state_type, group_drag_style) in &self.group_drag_over_styles { + if let Some(group_bounds) = + GroupBounds::get(&group_drag_style.group, cx) + { + if *state_type == drag.value.as_ref().type_id() + && group_bounds.contains(&mouse_position) + { + style.refine(&group_drag_style.style); + } + } + } + + for (state_type, drag_over_style) in &self.drag_over_styles { + if *state_type == drag.value.as_ref().type_id() + && bounds + .intersect(&cx.content_mask().bounds) + .contains(&mouse_position) + && cx.was_top_layer_under_active_drag( + &mouse_position, + cx.stacking_order(), + ) + { + style.refine(drag_over_style); + } } } @@ -1672,6 +1705,7 @@ impl Default for Interactivity { key_up_listeners: Vec::new(), action_listeners: Vec::new(), drop_listeners: Vec::new(), + can_drop_predicate: None, click_listeners: Vec::new(), drag_listener: None, hover_listener: None, diff --git a/crates/terminal_view2/src/terminal_panel.rs b/crates/terminal_view2/src/terminal_panel.rs index fa266ceb6c..245add3eeb 100644 --- a/crates/terminal_view2/src/terminal_panel.rs +++ b/crates/terminal_view2/src/terminal_panel.rs @@ -58,6 +58,15 @@ impl TerminalPanel { workspace.weak_handle(), workspace.project().clone(), Default::default(), + Some(Arc::new(|a, cx| { + if let Some(tab) = a.downcast_ref::() { + if let Some(item) = tab.pane.read(cx).item_for_index(tab.ix) { + return item.downcast::().is_some(); + } + } + + false + })), cx, ); pane.set_can_split(false, cx); diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index d0b47a2d1e..66bbe85087 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -181,7 +181,7 @@ pub struct Pane { workspace: WeakView, project: Model, drag_split_direction: Option, - // can_drop: Rc, &WindowContext) -> bool>, + can_drop_predicate: Option bool>>, can_split: bool, // render_tab_bar_buttons: Rc) -> AnyElement>, _subscriptions: Vec, @@ -229,7 +229,7 @@ pub struct NavigationEntry { } #[derive(Clone)] -struct DraggedTab { +pub struct DraggedTab { pub pane: View, pub ix: usize, pub item_id: EntityId, @@ -325,6 +325,7 @@ impl Pane { workspace: WeakView, project: Model, next_timestamp: Arc, + can_drop_predicate: Option bool + 'static>>, cx: &mut ViewContext, ) -> Self { // todo!("context menu") @@ -371,7 +372,7 @@ impl Pane { // tab_context_menu: cx.build_view(|_| ContextMenu::new(pane_view_id, cx)), workspace, project, - // can_drop: Rc::new(|_, _| true), + can_drop_predicate, can_split: true, // render_tab_bar_buttons: Rc::new(move |pane, cx| { // Flex::row() @@ -746,6 +747,10 @@ impl Pane { .position(|i| i.item_id() == item.item_id()) } + pub fn item_for_index(&self, ix: usize) -> Option<&dyn ItemHandle> { + self.items.get(ix).map(|i| i.as_ref()) + } + pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { if self.zoomed { cx.emit(Event::ZoomOut); @@ -1530,6 +1535,9 @@ impl Pane { ) .drag_over::(|tab| tab.bg(cx.theme().colors().drop_target_background)) .drag_over::(|tab| tab.bg(cx.theme().colors().drop_target_background)) + .when_some(self.can_drop_predicate.clone(), |this, p| { + this.can_drop(move |a, cx| p(a, cx)) + }) .on_drop(cx.listener(move |this, dragged_tab: &DraggedTab, cx| { this.drag_split_direction = None; this.handle_tab_drop(dragged_tab, ix, cx) @@ -1947,6 +1955,9 @@ impl Render for Pane { )) .group_drag_over::("", |style| style.visible()) .group_drag_over::("", |style| style.visible()) + .when_some(self.can_drop_predicate.clone(), |this, p| { + this.can_drop(move |a, cx| p(a, cx)) + }) .on_drop(cx.listener(move |this, dragged_tab, cx| { this.handle_tab_drop(dragged_tab, this.active_item_index(), cx) })) diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index b4508b9dda..c1695891f6 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -542,6 +542,7 @@ impl Workspace { weak_handle.clone(), project.clone(), pane_history_timestamp.clone(), + None, cx, ) }); @@ -1724,6 +1725,7 @@ impl Workspace { self.weak_handle(), self.project.clone(), self.pane_history_timestamp.clone(), + None, cx, ) });