extract dragged item target
This commit is contained in:
parent
d7b8a189e4
commit
8cde64d3f6
4 changed files with 179 additions and 197 deletions
133
crates/workspace/src/pane/dragged_item_receiver.rs
Normal file
133
crates/workspace/src/pane/dragged_item_receiver.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
use drag_and_drop::DragAndDrop;
|
||||
use gpui::{
|
||||
color::Color,
|
||||
elements::{Canvas, MouseEventHandler, ParentElement, Stack},
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
scene::MouseUp,
|
||||
AppContext, Element, ElementBox, EventContext, MouseButton, MouseState, Quad, RenderContext,
|
||||
WeakViewHandle,
|
||||
};
|
||||
use settings::Settings;
|
||||
|
||||
use crate::{MoveItem, Pane, SplitDirection, SplitWithItem, Workspace};
|
||||
|
||||
use super::DraggedItem;
|
||||
|
||||
pub fn dragged_item_receiver<Tag, F>(
|
||||
region_id: usize,
|
||||
drop_index: usize,
|
||||
allow_same_pane: bool,
|
||||
split_margin: Option<f32>,
|
||||
cx: &mut RenderContext<Pane>,
|
||||
render_child: F,
|
||||
) -> MouseEventHandler<Tag>
|
||||
where
|
||||
Tag: 'static,
|
||||
F: FnOnce(&mut MouseState, &mut RenderContext<Pane>) -> ElementBox,
|
||||
{
|
||||
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 hovered = state.hovered();
|
||||
let drag_position = cx
|
||||
.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
.filter(|_| hovered)
|
||||
.map(|_| state.mouse_position());
|
||||
|
||||
Stack::new()
|
||||
.with_child(render_child(state, cx))
|
||||
.with_children(drag_position.map(|drag_position| {
|
||||
Canvas::new(move |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);
|
||||
|
||||
cx.paint_stacking_context(None, |cx| {
|
||||
cx.scene.push_quad(Quad {
|
||||
bounds: overlay_region,
|
||||
background: Some(overlay_color(cx)),
|
||||
border: Default::default(),
|
||||
corner_radius: 0.,
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.boxed()
|
||||
}))
|
||||
.boxed()
|
||||
})
|
||||
.on_up(MouseButton::Left, {
|
||||
let pane = cx.handle();
|
||||
move |event, cx| {
|
||||
handle_dropped_item(event, &pane, drop_index, allow_same_pane, split_margin, cx);
|
||||
cx.notify();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle_dropped_item(
|
||||
event: MouseUp,
|
||||
pane: &WeakViewHandle<Pane>,
|
||||
index: usize,
|
||||
allow_same_pane: bool,
|
||||
split_margin: Option<f32>,
|
||||
cx: &mut EventContext,
|
||||
) {
|
||||
if let Some((_, dragged_item)) = cx
|
||||
.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<DraggedItem>(cx.window_id)
|
||||
{
|
||||
if let Some(split_direction) = split_margin
|
||||
.and_then(|margin| drop_split_direction(event.position, event.region, margin))
|
||||
{
|
||||
cx.dispatch_action(SplitWithItem {
|
||||
from: dragged_item.pane.clone(),
|
||||
item_id_to_move: dragged_item.item.id(),
|
||||
pane_to_split: pane.clone(),
|
||||
split_direction,
|
||||
});
|
||||
} else if pane != &dragged_item.pane || allow_same_pane {
|
||||
// If no split margin or not close enough to the edge, just move the item
|
||||
cx.dispatch_action(MoveItem {
|
||||
item_id: dragged_item.item.id(),
|
||||
from: dragged_item.pane.clone(),
|
||||
to: pane.clone(),
|
||||
destination_index: index,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
cx.propagate_event();
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
cx.global::<Settings>()
|
||||
.theme
|
||||
.workspace
|
||||
.drop_target_overlay_color
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue