From 56c93be4de4f3792b4578c381e68332665aba74a Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 14 Nov 2024 19:29:18 +0100 Subject: [PATCH] project panel: Fix rendering of groups of dragged project panel entries (#20686) This PR introduces a new parameter for `on_drag` in gpui, which is an offset from the element origin to the mouse event origin. Release Notes: - Fixed rendering of dragged project panel entries --- crates/collab_ui/src/collab_panel.rs | 2 +- crates/gpui/src/elements/div.rs | 17 ++++++++++------ crates/project_panel/src/project_panel.rs | 24 +++++++++++++++-------- crates/workspace/src/dock.rs | 2 +- crates/workspace/src/pane.rs | 2 +- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 14cab63f63..c93a48096a 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2521,7 +2521,7 @@ impl CollabPanel { .flex() .w_full() .when(!channel.is_root_channel(), |el| { - el.on_drag(channel.clone(), move |channel, cx| { + el.on_drag(channel.clone(), move |channel, _, cx| { cx.new_view(|_| DraggedChannelView { channel: channel.clone(), width, diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index 840dbd5dc4..961e28364f 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -443,7 +443,7 @@ impl Interactivity { pub fn on_drag( &mut self, value: T, - constructor: impl Fn(&T, &mut WindowContext) -> View + 'static, + constructor: impl Fn(&T, Point, &mut WindowContext) -> View + 'static, ) where Self: Sized, T: 'static, @@ -455,7 +455,9 @@ impl Interactivity { ); self.drag_listener = Some(( Box::new(value), - Box::new(move |value, cx| constructor(value.downcast_ref().unwrap(), cx).into()), + Box::new(move |value, offset, cx| { + constructor(value.downcast_ref().unwrap(), offset, cx).into() + }), )); } @@ -966,14 +968,15 @@ pub trait StatefulInteractiveElement: InteractiveElement { /// On drag initiation, this callback will be used to create a new view to render the dragged value for a /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with - /// the [`Self::on_drag_move`] API + /// the [`Self::on_drag_move`] API. + /// The callback also has access to the offset of triggering click from the origin of parent element. /// The fluent API equivalent to [`Interactivity::on_drag`] /// /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback. fn on_drag( mut self, value: T, - constructor: impl Fn(&T, &mut WindowContext) -> View + 'static, + constructor: impl Fn(&T, Point, &mut WindowContext) -> View + 'static, ) -> Self where Self: Sized, @@ -1056,7 +1059,8 @@ pub(crate) type ScrollWheelListener = pub(crate) type ClickListener = Box; -pub(crate) type DragListener = Box AnyView + 'static>; +pub(crate) type DragListener = + Box, &mut WindowContext) -> AnyView + 'static>; type DropListener = Box; @@ -1818,7 +1822,8 @@ impl Interactivity { if let Some((drag_value, drag_listener)) = drag_listener.take() { *clicked_state.borrow_mut() = ElementClickedState::default(); let cursor_offset = event.position - hitbox.origin; - let drag = (drag_listener)(drag_value.as_ref(), cx); + let drag = + (drag_listener)(drag_value.as_ref(), cursor_offset, cx); cx.active_drag = Some(AnyDrag { view: drag, value: drag_value, diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 8c738d2fd3..0a80728aff 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -240,6 +240,7 @@ struct DraggedProjectEntryView { selection: SelectedEntry, details: EntryDetails, width: Pixels, + click_offset: Point, selections: Arc>, } @@ -2681,10 +2682,11 @@ impl ProjectPanel { }, )) }) - .on_drag(dragged_selection, move |selection, cx| { + .on_drag(dragged_selection, move |selection, click_offset, cx| { cx.new_view(|_| DraggedProjectEntryView { details: details.clone(), width, + click_offset, selection: selection.active_selection, selections: selection.marked_selections.clone(), }) @@ -3559,15 +3561,21 @@ impl Render for DraggedProjectEntryView { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { let settings = ProjectPanelSettings::get_global(cx); let ui_font = ThemeSettings::get_global(cx).ui_font.clone(); + h_flex().font(ui_font).map(|this| { - if self.selections.contains(&self.selection) { - this.flex_shrink() - .p_1() - .items_end() - .rounded_md() - .child(self.selections.len().to_string()) + if self.selections.len() > 1 && self.selections.contains(&self.selection) { + this.flex_none() + .w(self.width) + .child(div().w(self.click_offset.x)) + .child( + div() + .p_1() + .rounded_xl() + .bg(cx.theme().colors().background) + .child(Label::new(format!("{} entries", self.selections.len()))), + ) } else { - this.bg(cx.theme().colors().background).w(self.width).child( + this.w(self.width).bg(cx.theme().colors().background).child( ListItem::new(self.selection.entry_id.to_proto() as usize) .indent_level(self.details.depth) .indent_step_size(px(settings.indent_size)) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 2317d02a5a..30ab109879 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -605,7 +605,7 @@ impl Render for Dock { let create_resize_handle = || { let handle = div() .id("resize-handle") - .on_drag(DraggedDock(position), |dock, cx| { + .on_drag(DraggedDock(position), |dock, _, cx| { cx.stop_propagation(); cx.new_view(|_| dock.clone()) }) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 306e48c41a..ec5d0883d7 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1950,7 +1950,7 @@ impl Pane { is_active, ix, }, - |tab, cx| cx.new_view(|_| tab.clone()), + |tab, _, cx| cx.new_view(|_| tab.clone()), ) .drag_over::(|tab, _, cx| { tab.bg(cx.theme().colors().drop_target_background)