235 lines
8.3 KiB
Rust
235 lines
8.3 KiB
Rust
use super::DraggedItem;
|
|
use crate::{Pane, SplitDirection, Workspace};
|
|
use drag_and_drop::DragAndDrop;
|
|
use gpui::{
|
|
color::Color,
|
|
elements::{Canvas, MouseEventHandler, ParentElement, Stack},
|
|
geometry::{rect::RectF, vector::Vector2F},
|
|
platform::MouseButton,
|
|
scene::MouseUp,
|
|
AppContext, Element, EventContext, MouseState, Quad, View, ViewContext, WeakViewHandle,
|
|
};
|
|
use project::ProjectEntryId;
|
|
|
|
pub fn dragged_item_receiver<Tag, D, F>(
|
|
pane: &Pane,
|
|
region_id: usize,
|
|
drop_index: usize,
|
|
allow_same_pane: bool,
|
|
split_margin: Option<f32>,
|
|
cx: &mut ViewContext<Pane>,
|
|
render_child: F,
|
|
) -> MouseEventHandler<Tag, Pane>
|
|
where
|
|
Tag: 'static,
|
|
D: Element<Pane>,
|
|
F: FnOnce(&mut MouseState, &mut ViewContext<Pane>) -> D,
|
|
{
|
|
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
|
let drag_position = if (pane.can_drop)(drag_and_drop, cx) {
|
|
drag_and_drop
|
|
.currently_dragged::<DraggedItem>(cx.window_id())
|
|
.map(|(drag_position, _)| drag_position)
|
|
.or_else(|| {
|
|
drag_and_drop
|
|
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
|
.map(|(drag_position, _)| drag_position)
|
|
})
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let mut handler = MouseEventHandler::<Tag, _>::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 drag_position = if state.hovered() { drag_position } else { None };
|
|
Stack::new()
|
|
.with_child(render_child(state, cx))
|
|
.with_children(drag_position.map(|drag_position| {
|
|
Canvas::new(move |scene, bounds, _, _, cx| {
|
|
if bounds.contains_point(drag_position) {
|
|
let overlay_region = split_margin
|
|
.and_then(|split_margin| {
|
|
drop_split_direction(drag_position, bounds, split_margin)
|
|
.map(|dir| (dir, split_margin))
|
|
})
|
|
.map(|(dir, margin)| dir.along_edge(bounds, margin))
|
|
.unwrap_or(bounds);
|
|
|
|
scene.paint_stacking_context(None, None, |scene| {
|
|
scene.push_quad(Quad {
|
|
bounds: overlay_region,
|
|
background: Some(overlay_color(cx)),
|
|
border: Default::default(),
|
|
corner_radius: 0.,
|
|
});
|
|
});
|
|
}
|
|
})
|
|
}))
|
|
});
|
|
|
|
if drag_position.is_some() {
|
|
handler = handler
|
|
.on_up(MouseButton::Left, {
|
|
move |event, pane, cx| {
|
|
let workspace = pane.workspace.clone();
|
|
let pane = cx.weak_handle();
|
|
handle_dropped_item(
|
|
event,
|
|
workspace,
|
|
&pane,
|
|
drop_index,
|
|
allow_same_pane,
|
|
split_margin,
|
|
cx,
|
|
);
|
|
cx.notify();
|
|
}
|
|
})
|
|
.on_move(|_, _, cx| {
|
|
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
|
|
|
if drag_and_drop
|
|
.currently_dragged::<DraggedItem>(cx.window_id())
|
|
.is_some()
|
|
|| drag_and_drop
|
|
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
|
.is_some()
|
|
{
|
|
cx.notify();
|
|
} else {
|
|
cx.propagate_event();
|
|
}
|
|
})
|
|
}
|
|
|
|
handler
|
|
}
|
|
|
|
pub fn handle_dropped_item<V: View>(
|
|
event: MouseUp,
|
|
workspace: WeakViewHandle<Workspace>,
|
|
pane: &WeakViewHandle<Pane>,
|
|
index: usize,
|
|
allow_same_pane: bool,
|
|
split_margin: Option<f32>,
|
|
cx: &mut EventContext<V>,
|
|
) {
|
|
enum Action {
|
|
Move(WeakViewHandle<Pane>, usize),
|
|
Open(ProjectEntryId),
|
|
}
|
|
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
|
let action = if let Some((_, dragged_item)) =
|
|
drag_and_drop.currently_dragged::<DraggedItem>(cx.window_id())
|
|
{
|
|
Action::Move(dragged_item.pane.clone(), dragged_item.handle.id())
|
|
} else if let Some((_, project_entry)) =
|
|
drag_and_drop.currently_dragged::<ProjectEntryId>(cx.window_id())
|
|
{
|
|
Action::Open(*project_entry)
|
|
} else {
|
|
cx.propagate_event();
|
|
return;
|
|
};
|
|
|
|
if let Some(split_direction) =
|
|
split_margin.and_then(|margin| drop_split_direction(event.position, event.region, margin))
|
|
{
|
|
let pane_to_split = pane.clone();
|
|
match action {
|
|
Action::Move(from, item_id_to_move) => {
|
|
cx.window_context().defer(move |cx| {
|
|
if let Some(workspace) = workspace.upgrade(cx) {
|
|
workspace.update(cx, |workspace, cx| {
|
|
workspace.split_pane_with_item(
|
|
pane_to_split,
|
|
split_direction,
|
|
from,
|
|
item_id_to_move,
|
|
cx,
|
|
);
|
|
})
|
|
}
|
|
});
|
|
}
|
|
Action::Open(project_entry) => {
|
|
cx.window_context().defer(move |cx| {
|
|
if let Some(workspace) = workspace.upgrade(cx) {
|
|
workspace.update(cx, |workspace, cx| {
|
|
if let Some(task) = workspace.split_pane_with_project_entry(
|
|
pane_to_split,
|
|
split_direction,
|
|
project_entry,
|
|
cx,
|
|
) {
|
|
task.detach_and_log_err(cx);
|
|
}
|
|
})
|
|
}
|
|
});
|
|
}
|
|
};
|
|
} else {
|
|
match action {
|
|
Action::Move(from, item_id) => {
|
|
if pane != &from || allow_same_pane {
|
|
let pane = pane.clone();
|
|
cx.window_context().defer(move |cx| {
|
|
if let Some(((workspace, from), to)) = workspace
|
|
.upgrade(cx)
|
|
.zip(from.upgrade(cx))
|
|
.zip(pane.upgrade(cx))
|
|
{
|
|
workspace.update(cx, |workspace, cx| {
|
|
workspace.move_item(from, to, item_id, index, cx);
|
|
})
|
|
}
|
|
});
|
|
} else {
|
|
cx.propagate_event();
|
|
}
|
|
}
|
|
Action::Open(project_entry) => {
|
|
let pane = pane.clone();
|
|
cx.window_context().defer(move |cx| {
|
|
if let Some(workspace) = workspace.upgrade(cx) {
|
|
workspace.update(cx, |workspace, cx| {
|
|
if let Some(path) =
|
|
workspace.project.read(cx).path_for_entry(project_entry, cx)
|
|
{
|
|
workspace
|
|
.open_path(path, Some(pane), true, cx)
|
|
.detach_and_log_err(cx);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn drop_split_direction(
|
|
position: Vector2F,
|
|
region: RectF,
|
|
split_margin: f32,
|
|
) -> Option<SplitDirection> {
|
|
let mut min_direction = None;
|
|
let mut min_distance = split_margin;
|
|
for direction in SplitDirection::all() {
|
|
let edge_distance = (direction.edge(region) - direction.axis().component(position)).abs();
|
|
|
|
if edge_distance < min_distance {
|
|
min_direction = Some(direction);
|
|
min_distance = edge_distance;
|
|
}
|
|
}
|
|
|
|
min_direction
|
|
}
|
|
|
|
fn overlay_color(cx: &AppContext) -> Color {
|
|
theme::current(cx).workspace.drop_target_overlay_color
|
|
}
|