From 93029376d977cfc262ce42d2e87432dc37a7e4b2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Dec 2023 16:52:11 -0800 Subject: [PATCH 1/3] Start work on allowing dragging tabs onto panes and pane edges --- crates/workspace2/src/pane.rs | 76 ++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index bcbadc4e532bcde153d4d70fc7044d51a490d44d..275f78dd9f2fc0c3b8165ff92b80200c46298da0 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1759,6 +1759,33 @@ impl Pane { }) .log_err(); } + + fn handle_split_tab_drop( + &mut self, + dragged_tab: &View, + split_direction: SplitDirection, + 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| { + let item = from_pane + .read(cx) + .items() + .find(|item| item.item_id() == item_id) + .map(|i| i.boxed_clone()); + + if let Some(item) = item { + workspace.split_item(split_direction, item, cx); + } + }); + }) + .log_err(); + } } impl FocusableView for Pane { @@ -1852,7 +1879,54 @@ impl Render for Pane { .child(self.render_tab_bar(cx)) .child(self.toolbar.clone()) .child(if let Some(item) = self.active_item() { - div().flex().flex_1().child(item.to_any()) + let mut drag_target_color = cx.theme().colors().text; + drag_target_color.a = 0.5; + + div() + .flex() + .flex_1() + .relative() + .child(item.to_any()) + .child( + div() + .absolute() + .full() + .z_index(1) + .drag_over::(|style| style.bg(drag_target_color)) + .on_drop(cx.listener( + move |this, dragged_tab: &View, cx| { + this.handle_tab_drop(dragged_tab, this.active_item_index(), cx) + }, + )), + ) + .children( + [ + (SplitDirection::Up, 2), + (SplitDirection::Down, 2), + (SplitDirection::Left, 3), + (SplitDirection::Right, 3), + ] + .into_iter() + .map(|(direction, z_index)| { + let div = div() + .absolute() + .z_index(z_index) + .invisible() + .bg(drag_target_color) + .drag_over::(|style| style.visible()) + .on_drop(cx.listener( + move |this, dragged_tab: &View, cx| { + this.handle_split_tab_drop(dragged_tab, direction, cx) + }, + )); + match direction { + SplitDirection::Up => div.top_0().left_0().right_0().h_32(), + SplitDirection::Down => div.left_0().bottom_0().right_0().h_32(), + SplitDirection::Left => div.top_0().left_0().bottom_0().w_32(), + SplitDirection::Right => div.top_0().bottom_0().right_0().w_32(), + } + }), + ) } else { h_stack() .items_center() From 9059d7015369f21f6060f23eb64c21bff439ffbd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Dec 2023 17:07:23 -0800 Subject: [PATCH 2/3] Ensure only top layer is styled with drag over style --- crates/gpui2/src/elements/div.rs | 110 ++++++++++++++++--------------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index a102c71a6fbc1d8d0dbc0b2d984efcbdbc677276..dfbb5aff21bc10ff419cd311480dc592b8fbc077 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -740,7 +740,7 @@ impl Interactivity { if style .background .as_ref() - .is_some_and(|fill| fill.color().is_some_and(|color| !color.is_transparent())) + .is_some_and(|fill| fill.color().is_some()) { cx.with_z_index(style.z_index.unwrap_or(0), |cx| cx.add_opaque_layer(bounds)) } @@ -1120,78 +1120,84 @@ impl Interactivity { let mut style = Style::default(); style.refine(&self.base_style); - if let Some(focus_handle) = self.tracked_focus_handle.as_ref() { - if let Some(in_focus_style) = self.in_focus_style.as_ref() { - if focus_handle.within_focused(cx) { - style.refine(in_focus_style); + cx.with_z_index(style.z_index.unwrap_or(0), |cx| { + if let Some(focus_handle) = self.tracked_focus_handle.as_ref() { + if let Some(in_focus_style) = self.in_focus_style.as_ref() { + if focus_handle.within_focused(cx) { + style.refine(in_focus_style); + } } - } - if let Some(focus_style) = self.focus_style.as_ref() { - if focus_handle.is_focused(cx) { - style.refine(focus_style); + if let Some(focus_style) = self.focus_style.as_ref() { + if focus_handle.is_focused(cx) { + style.refine(focus_style); + } } } - } - if let Some(bounds) = bounds { - let mouse_position = cx.mouse_position(); - if let Some(group_hover) = self.group_hover_style.as_ref() { - if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) { - if group_bounds.contains(&mouse_position) + if let Some(bounds) = bounds { + let mouse_position = cx.mouse_position(); + if let Some(group_hover) = self.group_hover_style.as_ref() { + if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) { + if group_bounds.contains(&mouse_position) + && cx.was_top_layer(&mouse_position, cx.stacking_order()) + { + style.refine(&group_hover.style); + } + } + } + if let Some(hover_style) = self.hover_style.as_ref() { + if bounds + .intersect(&cx.content_mask().bounds) + .contains(&mouse_position) && cx.was_top_layer(&mouse_position, cx.stacking_order()) { - style.refine(&group_hover.style); + style.refine(hover_style); } } - } - if let Some(hover_style) = self.hover_style.as_ref() { - if bounds - .intersect(&cx.content_mask().bounds) - .contains(&mouse_position) - && cx.was_top_layer(&mouse_position, cx.stacking_order()) - { - style.refine(hover_style); - } - } - 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 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.view.entity_type() + && 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.view.entity_type() - && group_bounds.contains(&mouse_position) + && bounds + .intersect(&cx.content_mask().bounds) + .contains(&mouse_position) + && cx.was_top_layer_under_active_drag( + &mouse_position, + cx.stacking_order(), + ) { - style.refine(&group_drag_style.style); + style.refine(drag_over_style); } } - } - for (state_type, drag_over_style) in &self.drag_over_styles { - if *state_type == drag.view.entity_type() - && bounds - .intersect(&cx.content_mask().bounds) - .contains(&mouse_position) - { - style.refine(drag_over_style); - } + cx.active_drag = Some(drag); } - - cx.active_drag = Some(drag); } - } - let clicked_state = element_state.clicked_state.borrow(); - if clicked_state.group { - if let Some(group) = self.group_active_style.as_ref() { - style.refine(&group.style) + let clicked_state = element_state.clicked_state.borrow(); + if clicked_state.group { + if let Some(group) = self.group_active_style.as_ref() { + style.refine(&group.style) + } } - } - if let Some(active_style) = self.active_style.as_ref() { - if clicked_state.element { - style.refine(active_style) + if let Some(active_style) = self.active_style.as_ref() { + if clicked_state.element { + style.refine(active_style) + } } - } + }); style } From 4f32f662711a6885e1a14ab7d17317dc36868d23 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Dec 2023 17:14:16 -0800 Subject: [PATCH 3/3] Clone item when dragging to split --- crates/workspace2/src/pane.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 275f78dd9f2fc0c3b8165ff92b80200c46298da0..a55469fbadd096bc3a041b3ebb3cc12614be871d 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1777,10 +1777,11 @@ impl Pane { .read(cx) .items() .find(|item| item.item_id() == item_id) - .map(|i| i.boxed_clone()); - + .map(|item| item.boxed_clone()); if let Some(item) = item { - workspace.split_item(split_direction, item, cx); + if let Some(item) = item.clone_on_split(workspace.database_id(), cx) { + workspace.split_item(split_direction, item, cx); + } } }); })