diff --git a/crates/drag_and_drop/src/drag_and_drop.rs b/crates/drag_and_drop/src/drag_and_drop.rs index 9a707fd054..e8a6d7f038 100644 --- a/crates/drag_and_drop/src/drag_and_drop.rs +++ b/crates/drag_and_drop/src/drag_and_drop.rs @@ -1,32 +1,47 @@ +pub mod shared_payloads; + use std::{any::Any, rc::Rc}; use collections::HashSet; use gpui::{ - elements::{MouseEventHandler, Overlay}, + elements::{Empty, MouseEventHandler, Overlay}, geometry::{rect::RectF, vector::Vector2F}, scene::MouseDrag, CursorStyle, Element, ElementBox, EventContext, MouseButton, MutableAppContext, RenderContext, View, WeakViewHandle, }; -struct State { - window_id: usize, - position: Vector2F, - region_offset: Vector2F, - region: RectF, - payload: Rc, - render: Rc, &mut RenderContext) -> ElementBox>, +enum State { + Dragging { + window_id: usize, + position: Vector2F, + region_offset: Vector2F, + region: RectF, + payload: Rc, + render: Rc, &mut RenderContext) -> ElementBox>, + }, + Canceled, } impl Clone for State { fn clone(&self) -> Self { - Self { - window_id: self.window_id.clone(), - position: self.position.clone(), - region_offset: self.region_offset.clone(), - region: self.region.clone(), - payload: self.payload.clone(), - render: self.render.clone(), + match self { + State::Dragging { + window_id, + position, + region_offset, + region, + payload, + render, + } => Self::Dragging { + window_id: window_id.clone(), + position: position.clone(), + region_offset: region_offset.clone(), + region: region.clone(), + payload: payload.clone(), + render: render.clone(), + }, + State::Canceled => State::Canceled, } } } @@ -51,24 +66,27 @@ impl DragAndDrop { } pub fn currently_dragged(&self, window_id: usize) -> Option<(Vector2F, Rc)> { - self.currently_dragged.as_ref().and_then( - |State { - position, - payload, - window_id: window_dragged_from, - .. - }| { + self.currently_dragged.as_ref().and_then(|state| { + if let State::Dragging { + position, + payload, + window_id: window_dragged_from, + .. + } = state + { if &window_id != window_dragged_from { return None; } payload - .clone() - .downcast::() - .ok() + .is::() + .then(|| payload.clone().downcast::().ok()) + .flatten() .map(|payload| (position.clone(), payload)) - }, - ) + } else { + None + } + }) } pub fn dragging( @@ -79,17 +97,27 @@ impl DragAndDrop { ) { let window_id = cx.window_id(); cx.update_global::(|this, cx| { - let (region_offset, region) = - if let Some(previous_state) = this.currently_dragged.as_ref() { - (previous_state.region_offset, previous_state.region) - } else { - ( - event.region.origin() - event.prev_mouse_position, - event.region, - ) - }; + this.notify_containers_for_window(window_id, cx); - this.currently_dragged = Some(State { + if matches!(this.currently_dragged, Some(State::Canceled)) { + return; + } + + let (region_offset, region) = if let Some(State::Dragging { + region_offset, + region, + .. + }) = this.currently_dragged.as_ref() + { + (*region_offset, *region) + } else { + ( + event.region.origin() - event.prev_mouse_position, + event.region, + ) + }; + + this.currently_dragged = Some(State::Dragging { window_id, region_offset, region, @@ -99,63 +127,114 @@ impl DragAndDrop { render(payload.downcast_ref::().unwrap(), cx) }), }); - - this.notify_containers_for_window(window_id, cx); }); } pub fn render(cx: &mut RenderContext) -> Option { - let currently_dragged = cx.global::().currently_dragged.clone(); + enum DraggedElementHandler {} + cx.global::() + .currently_dragged + .clone() + .and_then(|state| { + match state { + State::Dragging { + window_id, + region_offset, + position, + region, + payload, + render, + } => { + if cx.window_id() != window_id { + return None; + } - currently_dragged.and_then( - |State { - window_id, - region_offset, - position, - region, - payload, - render, - }| { - if cx.window_id() != window_id { - return None; + dbg!("Rendered dragging state"); + let position = position + region_offset; + Some( + Overlay::new( + MouseEventHandler::::new(0, cx, |_, cx| { + render(payload, cx) + }) + .with_cursor_style(CursorStyle::Arrow) + .on_up(MouseButton::Left, |_, cx| { + cx.defer(|cx| { + cx.update_global::(|this, cx| { + dbg!("Up with dragging state"); + this.finish_dragging(cx) + }); + }); + cx.propagate_event(); + }) + .on_up_out(MouseButton::Left, |_, cx| { + cx.defer(|cx| { + cx.update_global::(|this, cx| { + dbg!("Up out with dragging state"); + this.finish_dragging(cx) + }); + }); + }) + // Don't block hover events or invalidations + .with_hoverable(false) + .constrained() + .with_width(region.width()) + .with_height(region.height()) + .boxed(), + ) + .with_anchor_position(position) + .boxed(), + ) + } + + State::Canceled => { + dbg!("Rendered canceled state"); + Some( + MouseEventHandler::::new(0, cx, |_, _| { + Empty::new() + .constrained() + .with_width(0.) + .with_height(0.) + .boxed() + }) + .on_up(MouseButton::Left, |_, cx| { + cx.defer(|cx| { + cx.update_global::(|this, _| { + dbg!("Up with canceled state"); + this.currently_dragged = None; + }); + }); + }) + .on_up_out(MouseButton::Left, |_, cx| { + cx.defer(|cx| { + cx.update_global::(|this, _| { + dbg!("Up out with canceled state"); + this.currently_dragged = None; + }); + }); + }) + .boxed(), + ) + } } - - let position = position + region_offset; - - enum DraggedElementHandler {} - Some( - Overlay::new( - MouseEventHandler::::new(0, cx, |_, cx| { - render(payload, cx) - }) - .with_cursor_style(CursorStyle::Arrow) - .on_up(MouseButton::Left, |_, cx| { - cx.defer(|cx| { - cx.update_global::(|this, cx| this.stop_dragging(cx)); - }); - cx.propagate_event(); - }) - .on_up_out(MouseButton::Left, |_, cx| { - cx.defer(|cx| { - cx.update_global::(|this, cx| this.stop_dragging(cx)); - }); - }) - // Don't block hover events or invalidations - .with_hoverable(false) - .constrained() - .with_width(region.width()) - .with_height(region.height()) - .boxed(), - ) - .with_anchor_position(position) - .boxed(), - ) - }, - ) + }) } - fn stop_dragging(&mut self, cx: &mut MutableAppContext) { - if let Some(State { window_id, .. }) = self.currently_dragged.take() { + pub fn cancel_dragging(&mut self, cx: &mut MutableAppContext) { + if let Some(State::Dragging { + payload, window_id, .. + }) = &self.currently_dragged + { + if payload.is::

() { + let window_id = *window_id; + self.currently_dragged = Some(State::Canceled); + dbg!("Canceled"); + self.notify_containers_for_window(window_id, cx); + } + } + } + + fn finish_dragging(&mut self, cx: &mut MutableAppContext) { + if let Some(State::Dragging { window_id, .. }) = self.currently_dragged.take() { self.notify_containers_for_window(window_id, cx); } } diff --git a/crates/drag_and_drop/src/shared_payloads.rs b/crates/drag_and_drop/src/shared_payloads.rs new file mode 100644 index 0000000000..8644277abd --- /dev/null +++ b/crates/drag_and_drop/src/shared_payloads.rs @@ -0,0 +1,6 @@ +use std::{path::Path, sync::Arc}; + +#[derive(Debug, Clone)] +pub struct DraggedProjectEntry { + pub path: Arc, +} diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 0c7796358a..797c68ae3c 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1,5 +1,5 @@ use context_menu::{ContextMenu, ContextMenuItem}; -use drag_and_drop::Draggable; +use drag_and_drop::{shared_payloads::DraggedProjectEntry, DragAndDrop, Draggable}; use editor::{Cancel, Editor}; use futures::stream::StreamExt; use gpui::{ @@ -72,8 +72,8 @@ pub enum ClipboardEntry { }, } -#[derive(Debug, PartialEq, Eq, Clone)] -struct EntryDetails { +#[derive(Debug, PartialEq, Eq)] +pub struct EntryDetails { filename: String, path: Arc, depth: usize, @@ -605,6 +605,10 @@ impl ProjectPanel { cx.notify(); } } + + cx.update_global(|drag_and_drop: &mut DragAndDrop, cx| { + drag_and_drop.cancel_dragging::(cx); + }) } } @@ -1014,8 +1018,8 @@ impl ProjectPanel { } fn render_entry_visual_element( - details: EntryDetails, - editor: &ViewHandle, + details: &EntryDetails, + editor: Option<&ViewHandle>, padding: f32, row_container_style: ContainerStyle, style: &ProjectPanelEntry, @@ -1046,8 +1050,8 @@ impl ProjectPanel { .with_width(style.icon_size) .boxed(), ) - .with_child(if show_editor { - ChildView::new(editor.clone(), cx) + .with_child(if show_editor && editor.is_some() { + ChildView::new(editor.unwrap().clone(), cx) .contained() .with_margin_left(style.icon_spacing) .aligned() @@ -1099,8 +1103,8 @@ impl ProjectPanel { }; Self::render_entry_visual_element( - details.clone(), - editor, + &details, + Some(editor), padding, row_container_style, &style, @@ -1123,22 +1127,26 @@ impl ProjectPanel { position: e.position, }) }) - .as_draggable(details.clone(), { - let editor = editor.clone(); - let row_container_style = theme.dragged_entry.container; + .as_draggable( + DraggedProjectEntry { + path: details.path.clone(), + }, + { + let row_container_style = theme.dragged_entry.container; - move |payload, cx: &mut RenderContext| { - let theme = cx.global::().theme.clone(); - Self::render_entry_visual_element( - payload.clone(), - &editor, - padding, - row_container_style, - &theme.project_panel.dragged_entry, - cx, - ) - } - }) + move |_, cx: &mut RenderContext| { + let theme = cx.global::().theme.clone(); + Self::render_entry_visual_element( + &details, + None, + padding, + row_container_style, + &theme.project_panel.dragged_entry, + cx, + ) + } + }, + ) .with_cursor_style(CursorStyle::PointingHand) .boxed() } diff --git a/crates/workspace/src/pane/dragged_item_receiver.rs b/crates/workspace/src/pane/dragged_item_receiver.rs index 38613a0376..a18d99630e 100644 --- a/crates/workspace/src/pane/dragged_item_receiver.rs +++ b/crates/workspace/src/pane/dragged_item_receiver.rs @@ -1,4 +1,4 @@ -use drag_and_drop::DragAndDrop; +use drag_and_drop::{shared_payloads::DraggedProjectEntry, DragAndDrop}; use gpui::{ color::Color, elements::{Canvas, MouseEventHandler, ParentElement, Stack}, @@ -28,12 +28,18 @@ where MouseEventHandler::::above(region_id, cx, |state, cx| { // Observing hovered will cause a render when the mouse enters regardless // of if mouse position was accessed before - let hovered = state.hovered(); - let drag_position = cx - .global::>() - .currently_dragged::(cx.window_id()) - .filter(|_| hovered) - .map(|(drag_position, _)| drag_position); + let drag_position = if state.hovered() { + cx.global::>() + .currently_dragged::(cx.window_id()) + .map(|(drag_position, _)| drag_position) + .or_else(|| { + cx.global::>() + .currently_dragged::(cx.window_id()) + .map(|(drag_position, _)| drag_position) + }) + } else { + None + }; Stack::new() .with_child(render_child(state, cx)) @@ -70,10 +76,14 @@ where } }) .on_move(|_, cx| { - if cx - .global::>() + let drag_and_drop = cx.global::>(); + + if drag_and_drop .currently_dragged::(cx.window_id()) .is_some() + || drag_and_drop + .currently_dragged::(cx.window_id()) + .is_some() { cx.notify(); } else {