@@ -63,6 +63,9 @@ pub struct ProjectPanel {
fs: Arc<dyn Fs>,
focus_handle: FocusHandle,
scroll_handle: UniformListScrollHandle,
+ // An update loop that keeps incrementing/decrementing scroll offset while there is a dragged entry that's
+ // hovered over the start/end of a list.
+ hover_scroll_task: Option<Task<()>>,
visible_entries: Vec<(WorktreeId, Vec<Entry>, OnceCell<HashSet<Arc<Path>>>)>,
/// Maps from leaf project entry ID to the currently selected ancestor.
/// Relevant only for auto-fold dirs, where a single project panel entry may actually consist of several
@@ -307,6 +310,7 @@ impl ProjectPanel {
let scroll_handle = UniformListScrollHandle::new();
let mut this = Self {
project: project.clone(),
+ hover_scroll_task: None,
fs: workspace.app_state().fs.clone(),
focus_handle,
visible_entries: Default::default(),
@@ -2544,6 +2548,7 @@ impl ProjectPanel {
))
.on_drop(cx.listener(
move |this, external_paths: &ExternalPaths, cx| {
+ this.hover_scroll_task.take();
this.last_external_paths_drag_over_entry = None;
this.marked_entries.clear();
this.drop_external_files(external_paths.paths(), entry_id, cx);
@@ -2563,6 +2568,7 @@ impl ProjectPanel {
style.bg(cx.theme().colors().drop_target_background)
})
.on_drop(cx.listener(move |this, selections: &DraggedSelection, cx| {
+ this.hover_scroll_task.take();
this.drag_onto(selections, entry_id, kind.is_file(), cx);
}))
.child(
@@ -3057,9 +3063,67 @@ impl Render for ProjectPanel {
.map(|(_, worktree_entries, _)| worktree_entries.len())
.sum();
+ fn handle_drag_move_scroll<T: 'static>(
+ this: &mut ProjectPanel,
+ e: &DragMoveEvent<T>,
+ cx: &mut ViewContext<ProjectPanel>,
+ ) {
+ if !e.bounds.contains(&e.event.position) {
+ return;
+ }
+ this.hover_scroll_task.take();
+ let panel_height = e.bounds.size.height;
+ if panel_height <= px(0.) {
+ return;
+ }
+
+ let event_offset = e.event.position.y - e.bounds.origin.y;
+ // How far along in the project panel is our cursor? (0. is the top of a list, 1. is the bottom)
+ let hovered_region_offset = event_offset / panel_height;
+
+ // We want the scrolling to be a bit faster when the cursor is closer to the edge of a list.
+ // These pixels offsets were picked arbitrarily.
+ let vertical_scroll_offset = if hovered_region_offset <= 0.05 {
+ 8.
+ } else if hovered_region_offset <= 0.15 {
+ 5.
+ } else if hovered_region_offset >= 0.95 {
+ -8.
+ } else if hovered_region_offset >= 0.85 {
+ -5.
+ } else {
+ return;
+ };
+ let adjustment = point(px(0.), px(vertical_scroll_offset));
+ this.hover_scroll_task = Some(cx.spawn(move |this, mut cx| async move {
+ loop {
+ let should_stop_scrolling = this
+ .update(&mut cx, |this, cx| {
+ this.hover_scroll_task.as_ref()?;
+ let handle = this.scroll_handle.0.borrow_mut();
+ let offset = handle.base_handle.offset();
+
+ handle.base_handle.set_offset(offset + adjustment);
+ cx.notify();
+ Some(())
+ })
+ .ok()
+ .flatten()
+ .is_some();
+ if should_stop_scrolling {
+ return;
+ }
+ cx.background_executor()
+ .timer(Duration::from_millis(16))
+ .await;
+ }
+ }));
+ }
h_flex()
.id("project-panel")
.group("project-panel")
+ .on_drag_move(cx.listener(handle_drag_move_scroll::<ExternalPaths>))
+ .on_drag_move(cx.listener(handle_drag_move_scroll::<DraggedSelection>))
.size_full()
.relative()
.on_hover(cx.listener(|this, hovered, cx| {
@@ -3291,6 +3355,7 @@ impl Render for ProjectPanel {
move |this, external_paths: &ExternalPaths, cx| {
this.last_external_paths_drag_over_entry = None;
this.marked_entries.clear();
+ this.hover_scroll_task.take();
if let Some(task) = this
.workspace
.update(cx, |workspace, cx| {