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
This commit is contained in:
Piotr Osiewicz 2024-11-14 19:29:18 +01:00 committed by GitHub
parent 43999c47e1
commit 56c93be4de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 30 additions and 17 deletions

View file

@ -2521,7 +2521,7 @@ impl CollabPanel {
.flex() .flex()
.w_full() .w_full()
.when(!channel.is_root_channel(), |el| { .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 { cx.new_view(|_| DraggedChannelView {
channel: channel.clone(), channel: channel.clone(),
width, width,

View file

@ -443,7 +443,7 @@ impl Interactivity {
pub fn on_drag<T, W>( pub fn on_drag<T, W>(
&mut self, &mut self,
value: T, value: T,
constructor: impl Fn(&T, &mut WindowContext) -> View<W> + 'static, constructor: impl Fn(&T, Point<Pixels>, &mut WindowContext) -> View<W> + 'static,
) where ) where
Self: Sized, Self: Sized,
T: 'static, T: 'static,
@ -455,7 +455,9 @@ impl Interactivity {
); );
self.drag_listener = Some(( self.drag_listener = Some((
Box::new(value), 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 /// 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 /// 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`] /// 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. /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_drag<T, W>( fn on_drag<T, W>(
mut self, mut self,
value: T, value: T,
constructor: impl Fn(&T, &mut WindowContext) -> View<W> + 'static, constructor: impl Fn(&T, Point<Pixels>, &mut WindowContext) -> View<W> + 'static,
) -> Self ) -> Self
where where
Self: Sized, Self: Sized,
@ -1056,7 +1059,8 @@ pub(crate) type ScrollWheelListener =
pub(crate) type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>; pub(crate) type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
pub(crate) type DragListener = Box<dyn Fn(&dyn Any, &mut WindowContext) -> AnyView + 'static>; pub(crate) type DragListener =
Box<dyn Fn(&dyn Any, Point<Pixels>, &mut WindowContext) -> AnyView + 'static>;
type DropListener = Box<dyn Fn(&dyn Any, &mut WindowContext) + 'static>; type DropListener = Box<dyn Fn(&dyn Any, &mut WindowContext) + 'static>;
@ -1818,7 +1822,8 @@ impl Interactivity {
if let Some((drag_value, drag_listener)) = drag_listener.take() { if let Some((drag_value, drag_listener)) = drag_listener.take() {
*clicked_state.borrow_mut() = ElementClickedState::default(); *clicked_state.borrow_mut() = ElementClickedState::default();
let cursor_offset = event.position - hitbox.origin; 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 { cx.active_drag = Some(AnyDrag {
view: drag, view: drag,
value: drag_value, value: drag_value,

View file

@ -240,6 +240,7 @@ struct DraggedProjectEntryView {
selection: SelectedEntry, selection: SelectedEntry,
details: EntryDetails, details: EntryDetails,
width: Pixels, width: Pixels,
click_offset: Point<Pixels>,
selections: Arc<BTreeSet<SelectedEntry>>, selections: Arc<BTreeSet<SelectedEntry>>,
} }
@ -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 { cx.new_view(|_| DraggedProjectEntryView {
details: details.clone(), details: details.clone(),
width, width,
click_offset,
selection: selection.active_selection, selection: selection.active_selection,
selections: selection.marked_selections.clone(), selections: selection.marked_selections.clone(),
}) })
@ -3559,15 +3561,21 @@ impl Render for DraggedProjectEntryView {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let settings = ProjectPanelSettings::get_global(cx); let settings = ProjectPanelSettings::get_global(cx);
let ui_font = ThemeSettings::get_global(cx).ui_font.clone(); let ui_font = ThemeSettings::get_global(cx).ui_font.clone();
h_flex().font(ui_font).map(|this| { h_flex().font(ui_font).map(|this| {
if self.selections.contains(&self.selection) { if self.selections.len() > 1 && self.selections.contains(&self.selection) {
this.flex_shrink() this.flex_none()
.p_1() .w(self.width)
.items_end() .child(div().w(self.click_offset.x))
.rounded_md() .child(
.child(self.selections.len().to_string()) div()
.p_1()
.rounded_xl()
.bg(cx.theme().colors().background)
.child(Label::new(format!("{} entries", self.selections.len()))),
)
} else { } 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) ListItem::new(self.selection.entry_id.to_proto() as usize)
.indent_level(self.details.depth) .indent_level(self.details.depth)
.indent_step_size(px(settings.indent_size)) .indent_step_size(px(settings.indent_size))

View file

@ -605,7 +605,7 @@ impl Render for Dock {
let create_resize_handle = || { let create_resize_handle = || {
let handle = div() let handle = div()
.id("resize-handle") .id("resize-handle")
.on_drag(DraggedDock(position), |dock, cx| { .on_drag(DraggedDock(position), |dock, _, cx| {
cx.stop_propagation(); cx.stop_propagation();
cx.new_view(|_| dock.clone()) cx.new_view(|_| dock.clone())
}) })

View file

@ -1950,7 +1950,7 @@ impl Pane {
is_active, is_active,
ix, ix,
}, },
|tab, cx| cx.new_view(|_| tab.clone()), |tab, _, cx| cx.new_view(|_| tab.clone()),
) )
.drag_over::<DraggedTab>(|tab, _, cx| { .drag_over::<DraggedTab>(|tab, _, cx| {
tab.bg(cx.theme().colors().drop_target_background) tab.bg(cx.theme().colors().drop_target_background)