diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 4eb26bf507..d936716032 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -1,12 +1,12 @@ use editor::{Cursor, HighlightedRange, HighlightedRangeLine}; use gpui::{ div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace, - BorrowWindow, Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font, - FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveBounds, InteractiveElement, + BorrowWindow, Bounds, DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle, + FontWeight, HighlightStyle, Hsla, InteractiveBounds, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, MouseMoveEvent, Pixels, PlatformInputHandler, Point, - ShapedLine, StatefulInteractiveElement, StyleRefinement, Styled, TextRun, TextStyle, - TextSystem, UnderlineStyle, WhiteSpace, WindowContext, + ShapedLine, StatefulInteractiveElement, Styled, TextRun, TextStyle, TextSystem, UnderlineStyle, + WhiteSpace, WindowContext, }; use itertools::Itertools; use language::CursorShape; @@ -25,7 +25,7 @@ use terminal::{ use theme::{ActiveTheme, Theme, ThemeSettings}; use ui::Tooltip; -use std::{any::TypeId, mem}; +use std::mem; use std::{fmt::Debug, ops::RangeInclusive}; ///The information generated during layout that is necessary for painting @@ -677,28 +677,6 @@ impl TerminalElement { } }); - self.interactivity.drag_over_styles.push(( - TypeId::of::(), - StyleRefinement::default().bg(cx.theme().colors().drop_target_background), - )); - self.interactivity.on_drop::({ - let focus = focus.clone(); - let terminal = terminal.clone(); - move |external_paths, cx| { - cx.focus(&focus); - let mut new_text = external_paths - .paths() - .iter() - .map(|path| format!(" {path:?}")) - .join(""); - new_text.push(' '); - terminal.update(cx, |terminal, _| { - terminal.paste(&new_text); - }); - cx.stop_propagation(); - } - }); - // Mouse mode handlers: // All mouse modes need the extra click handlers if mode.intersects(TermMode::MOUSE_MODE) { diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 86ac4a9818..c19d0bfc6c 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, sync::Arc}; +use std::{ops::ControlFlow, path::PathBuf, sync::Arc}; use crate::TerminalView; use db::kvp::KEY_VALUE_STORE; @@ -7,7 +7,8 @@ use gpui::{ FocusHandle, FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext, }; -use project::Fs; +use itertools::Itertools; +use project::{Fs, ProjectEntryId}; use search::{buffer_search::DivRegistrar, BufferSearchBar}; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; @@ -19,7 +20,7 @@ use workspace::{ item::Item, pane, ui::Icon, - Pane, Workspace, + DraggedTab, Pane, Workspace, }; use anyhow::Result; @@ -59,15 +60,7 @@ impl TerminalPanel { workspace.weak_handle(), workspace.project().clone(), Default::default(), - Some(Arc::new(|a, cx| { - if let Some(tab) = a.downcast_ref::() { - if let Some(item) = tab.pane.read(cx).item_for_index(tab.ix) { - return item.downcast::().is_some(); - } - } - - a.downcast_ref::().is_some() - })), + None, cx, ); pane.set_can_split(false, cx); @@ -102,6 +95,47 @@ impl TerminalPanel { }) .into_any_element() }); + + let workspace = workspace.weak_handle(); + pane.set_custom_drop_handle(cx, move |pane, dropped_item, cx| { + if let Some(tab) = dropped_item.downcast_ref::() { + if let Some(item) = tab.pane.read(cx).item_for_index(tab.ix) { + if item.downcast::().is_some() { + return ControlFlow::Continue(()); + } else if let Some(project_path) = item.project_path(cx) { + if let Some(entry_path) = workspace + .update(cx, |workspace, cx| { + workspace + .project() + .read(cx) + .absolute_path(&project_path, cx) + }) + .log_err() + .flatten() + { + add_paths_to_terminal(pane, &[entry_path], cx); + } + } + } + } else if let Some(&entry_id) = dropped_item.downcast_ref::() { + if let Some(entry_path) = workspace + .update(cx, |workspace, cx| { + let project = workspace.project().read(cx); + project + .path_for_entry(entry_id, cx) + .and_then(|project_path| project.absolute_path(&project_path, cx)) + }) + .log_err() + .flatten() + { + add_paths_to_terminal(pane, &[entry_path], cx); + } + } else if let Some(paths) = dropped_item.downcast_ref::() { + add_paths_to_terminal(pane, paths.paths(), cx); + } + + ControlFlow::Break(()) + }); let buffer_search_bar = cx.new_view(search::BufferSearchBar::new); pane.toolbar() .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx)); @@ -326,6 +360,22 @@ impl TerminalPanel { } } +fn add_paths_to_terminal(pane: &mut Pane, paths: &[PathBuf], cx: &mut ViewContext<'_, Pane>) { + if let Some(terminal_view) = pane + .active_item() + .and_then(|item| item.downcast::()) + { + cx.focus_view(&terminal_view); + let mut new_text = paths.iter().map(|path| format!(" {path:?}")).join(""); + new_text.push(' '); + terminal_view.update(cx, |terminal_view, cx| { + terminal_view.terminal().update(cx, |terminal, _| { + terminal.paste(&new_text); + }); + }); + } +} + impl EventEmitter for TerminalPanel {} impl Render for TerminalPanel { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index fe41d0f8b2..985311b18a 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -20,6 +20,7 @@ use settings::Settings; use std::{ any::Any, cmp, fmt, mem, + ops::ControlFlow, path::{Path, PathBuf}, rc::Rc, sync::{ @@ -183,6 +184,8 @@ pub struct Pane { project: Model, drag_split_direction: Option, can_drop_predicate: Option bool>>, + custom_drop_handle: + Option) -> ControlFlow<(), ()>>>, can_split: bool, render_tab_bar_buttons: Rc) -> AnyElement>, _subscriptions: Vec, @@ -375,6 +378,7 @@ impl Pane { workspace, project, can_drop_predicate, + custom_drop_handle: None, can_split: true, render_tab_bar_buttons: Rc::new(move |pane, cx| { h_stack() @@ -501,13 +505,6 @@ impl Pane { self.active_item_index } - // pub fn on_can_drop(&mut self, can_drop: F) - // where - // F: 'static + Fn(&DragAndDrop, &WindowContext) -> bool, - // { - // self.can_drop = Rc::new(can_drop); - // } - pub fn set_can_split(&mut self, can_split: bool, cx: &mut ViewContext) { self.can_split = can_split; cx.notify(); @@ -528,6 +525,14 @@ impl Pane { cx.notify(); } + pub fn set_custom_drop_handle(&mut self, cx: &mut ViewContext, handle: F) + where + F: 'static + Fn(&mut Pane, &dyn Any, &mut ViewContext) -> ControlFlow<(), ()>, + { + self.custom_drop_handle = Some(Arc::new(handle)); + cx.notify(); + } + pub fn nav_history_for_item(&self, item: &View) -> ItemNavHistory { ItemNavHistory { history: self.nav_history.clone(), @@ -1818,8 +1823,13 @@ impl Pane { &mut self, dragged_tab: &DraggedTab, ix: usize, - cx: &mut ViewContext<'_, Pane>, + cx: &mut ViewContext<'_, Self>, ) { + if let Some(custom_drop_handle) = self.custom_drop_handle.clone() { + if let ControlFlow::Break(()) = custom_drop_handle(self, dragged_tab, cx) { + return; + } + } let mut to_pane = cx.view().clone(); let split_direction = self.drag_split_direction; let item_id = dragged_tab.item_id; @@ -1839,8 +1849,13 @@ impl Pane { fn handle_project_entry_drop( &mut self, project_entry_id: &ProjectEntryId, - cx: &mut ViewContext<'_, Pane>, + cx: &mut ViewContext<'_, Self>, ) { + if let Some(custom_drop_handle) = self.custom_drop_handle.clone() { + if let ControlFlow::Break(()) = custom_drop_handle(self, project_entry_id, cx) { + return; + } + } let mut to_pane = cx.view().clone(); let split_direction = self.drag_split_direction; let project_entry_id = *project_entry_id; @@ -1867,8 +1882,13 @@ impl Pane { fn handle_external_paths_drop( &mut self, paths: &ExternalPaths, - cx: &mut ViewContext<'_, Pane>, + cx: &mut ViewContext<'_, Self>, ) { + if let Some(custom_drop_handle) = self.custom_drop_handle.clone() { + if let ControlFlow::Break(()) = custom_drop_handle(self, paths, cx) { + return; + } + } let mut to_pane = cx.view().clone(); let split_direction = self.drag_split_direction; let paths = paths.paths().to_vec(); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index f23dfca857..8e66f06b4a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -27,11 +27,11 @@ use futures::{ use gpui::{ actions, canvas, div, impl_actions, point, size, Action, AnyElement, AnyModel, AnyView, AnyWeakView, AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, BorrowWindow, - Bounds, Context, Div, DragMoveEvent, Element, Entity, EntityId, EventEmitter, ExternalPaths, - FocusHandle, FocusableView, GlobalPixels, InteractiveElement, IntoElement, KeyContext, - LayoutId, ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Pixels, Point, - PromptLevel, Render, Size, Styled, Subscription, Task, View, ViewContext, VisualContext, - WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions, + Bounds, Context, Div, DragMoveEvent, Element, Entity, EntityId, EventEmitter, FocusHandle, + FocusableView, GlobalPixels, InteractiveElement, IntoElement, KeyContext, LayoutId, + ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Pixels, Point, PromptLevel, + Render, Size, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, + WindowBounds, WindowContext, WindowHandle, WindowOptions, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem}; use itertools::Itertools; @@ -544,11 +544,7 @@ impl Workspace { weak_handle.clone(), project.clone(), pane_history_timestamp.clone(), - Some(Arc::new(|a, _| { - a.downcast_ref::().is_some() - || a.downcast_ref::().is_some() - || a.downcast_ref::().is_some() - })), + None, cx, ) });