1use super::DraggedItem;
2use crate::{Pane, SplitDirection, Workspace};
3use gpui::{
4 color::Color,
5 elements::{Canvas, MouseEventHandler, ParentComponent, Stack},
6 geometry::{rect::RectF, vector::Vector2F},
7 platform::MouseButton,
8 scene::MouseUp,
9 AppContext, Element, EventContext, MouseState, Quad, ViewContext, WeakViewHandle,
10};
11use project2::ProjectEntryId;
12
13pub fn dragged_item_receiver<Tag, D, F>(
14 pane: &Pane,
15 region_id: usize,
16 drop_index: usize,
17 allow_same_pane: bool,
18 split_margin: Option<f32>,
19 cx: &mut ViewContext<Pane>,
20 render_child: F,
21) -> MouseEventHandler<Pane>
22where
23 Tag: 'static,
24 D: Element<Pane>,
25 F: FnOnce(&mut MouseState, &mut ViewContext<Pane>) -> D,
26{
27 let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
28 let drag_position = if (pane.can_drop)(drag_and_drop, cx) {
29 drag_and_drop
30 .currently_dragged::<DraggedItem>(cx.window())
31 .map(|(drag_position, _)| drag_position)
32 .or_else(|| {
33 drag_and_drop
34 .currently_dragged::<ProjectEntryId>(cx.window())
35 .map(|(drag_position, _)| drag_position)
36 })
37 } else {
38 None
39 };
40
41 let mut handler = MouseEventHandler::above::<Tag, _>(region_id, cx, |state, cx| {
42 // Observing hovered will cause a render when the mouse enters regardless
43 // of if mouse position was accessed before
44 let drag_position = if state.dragging() {
45 drag_position
46 } else {
47 None
48 };
49 Stack::new()
50 .with_child(render_child(state, cx))
51 .with_children(drag_position.map(|drag_position| {
52 Canvas::new(move |bounds, _, _, cx| {
53 if bounds.contains_point(drag_position) {
54 let overlay_region = split_margin
55 .and_then(|split_margin| {
56 drop_split_direction(drag_position, bounds, split_margin)
57 .map(|dir| (dir, split_margin))
58 })
59 .map(|(dir, margin)| dir.along_edge(bounds, margin))
60 .unwrap_or(bounds);
61
62 cx.scene().push_stacking_context(None, None);
63 let background = overlay_color(cx);
64 cx.scene().push_quad(Quad {
65 bounds: overlay_region,
66 background: Some(background),
67 border: Default::default(),
68 corner_radii: Default::default(),
69 });
70 cx.scene().pop_stacking_context();
71 }
72 })
73 }))
74 });
75
76 if drag_position.is_some() {
77 handler = handler
78 .on_up(MouseButton::Left, {
79 move |event, pane, cx| {
80 let workspace = pane.workspace.clone();
81 let pane = cx.weak_handle();
82 handle_dropped_item(
83 event,
84 workspace,
85 &pane,
86 drop_index,
87 allow_same_pane,
88 split_margin,
89 cx,
90 );
91 cx.notify();
92 }
93 })
94 .on_move(|_, _, cx| {
95 let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
96
97 if drag_and_drop
98 .currently_dragged::<DraggedItem>(cx.window())
99 .is_some()
100 || drag_and_drop
101 .currently_dragged::<ProjectEntryId>(cx.window())
102 .is_some()
103 {
104 cx.notify();
105 } else {
106 cx.propagate_event();
107 }
108 })
109 }
110
111 handler
112}
113
114pub fn handle_dropped_item<V: 'static>(
115 event: MouseUp,
116 workspace: WeakViewHandle<Workspace>,
117 pane: &WeakViewHandle<Pane>,
118 index: usize,
119 allow_same_pane: bool,
120 split_margin: Option<f32>,
121 cx: &mut EventContext<V>,
122) {
123 enum Action {
124 Move(WeakViewHandle<Pane>, usize),
125 Open(ProjectEntryId),
126 }
127 let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
128 let action = if let Some((_, dragged_item)) =
129 drag_and_drop.currently_dragged::<DraggedItem>(cx.window())
130 {
131 Action::Move(dragged_item.pane.clone(), dragged_item.handle.id())
132 } else if let Some((_, project_entry)) =
133 drag_and_drop.currently_dragged::<ProjectEntryId>(cx.window())
134 {
135 Action::Open(*project_entry)
136 } else {
137 cx.propagate_event();
138 return;
139 };
140
141 if let Some(split_direction) =
142 split_margin.and_then(|margin| drop_split_direction(event.position, event.region, margin))
143 {
144 let pane_to_split = pane.clone();
145 match action {
146 Action::Move(from, item_id_to_move) => {
147 cx.window_context().defer(move |cx| {
148 if let Some(workspace) = workspace.upgrade(cx) {
149 workspace.update(cx, |workspace, cx| {
150 workspace.split_pane_with_item(
151 pane_to_split,
152 split_direction,
153 from,
154 item_id_to_move,
155 cx,
156 );
157 })
158 }
159 });
160 }
161 Action::Open(project_entry) => {
162 cx.window_context().defer(move |cx| {
163 if let Some(workspace) = workspace.upgrade(cx) {
164 workspace.update(cx, |workspace, cx| {
165 if let Some(task) = workspace.split_pane_with_project_entry(
166 pane_to_split,
167 split_direction,
168 project_entry,
169 cx,
170 ) {
171 task.detach_and_log_err(cx);
172 }
173 })
174 }
175 });
176 }
177 };
178 } else {
179 match action {
180 Action::Move(from, item_id) => {
181 if pane != &from || allow_same_pane {
182 let pane = pane.clone();
183 cx.window_context().defer(move |cx| {
184 if let Some(((workspace, from), to)) = workspace
185 .upgrade(cx)
186 .zip(from.upgrade(cx))
187 .zip(pane.upgrade(cx))
188 {
189 workspace.update(cx, |workspace, cx| {
190 workspace.move_item(from, to, item_id, index, cx);
191 })
192 }
193 });
194 } else {
195 cx.propagate_event();
196 }
197 }
198 Action::Open(project_entry) => {
199 let pane = pane.clone();
200 cx.window_context().defer(move |cx| {
201 if let Some(workspace) = workspace.upgrade(cx) {
202 workspace.update(cx, |workspace, cx| {
203 if let Some(path) =
204 workspace.project.read(cx).path_for_entry(project_entry, cx)
205 {
206 workspace
207 .open_path(path, Some(pane), true, cx)
208 .detach_and_log_err(cx);
209 }
210 });
211 }
212 });
213 }
214 }
215 }
216}
217
218fn drop_split_direction(
219 position: Vector2F,
220 region: RectF,
221 split_margin: f32,
222) -> Option<SplitDirection> {
223 let mut min_direction = None;
224 let mut min_distance = split_margin;
225 for direction in SplitDirection::all() {
226 let edge_distance = (direction.edge(region) - direction.axis().component(position)).abs();
227
228 if edge_distance < min_distance {
229 min_direction = Some(direction);
230 min_distance = edge_distance;
231 }
232 }
233
234 min_direction
235}
236
237fn overlay_color(cx: &AppContext) -> Color {
238 theme2::current(cx).workspace.drop_target_overlay_color
239}