From 21e7765a482d8231906e67ff4cc997d1cabc6077 Mon Sep 17 00:00:00 2001 From: tims <0xtimsb@gmail.com> Date: Fri, 17 Jan 2025 09:24:37 +0530 Subject: [PATCH] project_panel: Add directory auto-expand after 500ms hover during dragging (#23080) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR resolves one part of issue #14496 In project panel, when dragging, if you hover over a directory for ~500ms, it now auto-expands so you can drag and drop into nested directories. Task cleanup is handled in these cases: - Dragged onto a different entry. - Dragged anywhere else, and the 500ms timer runs out (for example, out of the project panel). - Dropped onto any entry. I don’t see any edge cases where task isn’t cleaned up after 500ms. https://github.com/user-attachments/assets/19da0da1-f9e2-42df-8ee4-fab6dc9a185a Release Notes: - Added auto-expand for directories on hover for a while during dragging. --- crates/project_panel/src/project_panel.rs | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index f75cb8f803..928c5475f4 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -81,6 +81,7 @@ pub struct ProjectPanel { /// project entries (and all non-leaf nodes are guaranteed to be directories). ancestors: HashMap, last_worktree_root_id: Option, + last_selection_drag_over_entry: Option, last_external_paths_drag_over_entry: Option, expanded_dir_ids: HashMap>, unfolded_dir_ids: HashSet, @@ -104,6 +105,7 @@ pub struct ProjectPanel { // We keep track of the mouse down state on entries so we don't flash the UI // in case a user clicks to open a file. mouse_down: bool, + hover_expand_task: Option>, } #[derive(Clone, Debug)] @@ -380,6 +382,7 @@ impl ProjectPanel { ancestors: Default::default(), last_worktree_root_id: Default::default(), last_external_paths_drag_over_entry: None, + last_selection_drag_over_entry: None, expanded_dir_ids: Default::default(), unfolded_dir_ids: Default::default(), selection: None, @@ -402,6 +405,7 @@ impl ProjectPanel { diagnostics: Default::default(), scroll_handle, mouse_down: false, + hover_expand_task: None, }; this.update_visible_entries(None, cx); @@ -3356,6 +3360,44 @@ impl ProjectPanel { }, )) }) + .on_drag_move::(cx.listener( + move |this, event: &DragMoveEvent, cx| { + if event.bounds.contains(&event.event.position) { + if this.last_selection_drag_over_entry == Some(entry_id) { + return; + } + this.last_selection_drag_over_entry = Some(entry_id); + this.hover_expand_task.take(); + + if !kind.is_dir() + || this + .expanded_dir_ids + .get(&details.worktree_id) + .map_or(false, |ids| ids.binary_search(&entry_id).is_ok()) + { + return; + } + + let bounds = event.bounds; + this.hover_expand_task = Some(cx.spawn(|this, mut cx| async move { + cx.background_executor() + .timer(Duration::from_millis(500)) + .await; + this.update(&mut cx, |this, cx| { + this.hover_expand_task.take(); + if this.last_selection_drag_over_entry == Some(entry_id) + && bounds.contains(&cx.mouse_position()) + { + this.expand_entry(worktree_id, entry_id, cx); + this.update_visible_entries(Some((worktree_id, entry_id)), cx); + cx.notify(); + } + }) + .ok(); + })); + } + }, + )) .on_drag(dragged_selection, move |selection, click_offset, cx| { cx.new_view(|_| DraggedProjectEntryView { details: details.clone(), @@ -3368,6 +3410,7 @@ impl ProjectPanel { .drag_over::(move |style, _, _| style.bg(item_colors.drag_over)) .on_drop(cx.listener(move |this, selections: &DraggedSelection, cx| { this.hover_scroll_task.take(); + this.hover_expand_task.take(); this.drag_onto(selections, entry_id, kind.is_file(), cx); })) .on_mouse_down(