extract dragged item target

This commit is contained in:
K Simmons 2022-10-22 21:32:07 -07:00
parent d7b8a189e4
commit 8cde64d3f6
4 changed files with 179 additions and 197 deletions

View File

@ -24,12 +24,13 @@ impl Element for Stack {
fn layout(
&mut self,
constraint: SizeConstraint,
mut constraint: SizeConstraint,
cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) {
let mut size = constraint.min;
for child in &mut self.children {
size = size.max(child.layout(constraint, cx));
constraint.min = size;
}
(size, ())
}

View File

@ -9,7 +9,9 @@ use serde::Deserialize;
use settings::{DockAnchor, Settings};
use theme::Theme;
use crate::{sidebar::SidebarSide, ItemHandle, Pane, StatusItemView, Workspace};
use crate::{
handle_dropped_item, sidebar::SidebarSide, ItemHandle, Pane, StatusItemView, Workspace,
};
#[derive(PartialEq, Clone, Deserialize)]
pub struct MoveDock(pub DockAnchor);
@ -376,6 +378,7 @@ impl View for ToggleDockButton {
let dock_position = workspace.read(cx).dock.position;
let theme = cx.global::<Settings>().theme.clone();
let button = MouseEventHandler::<Self>::new(0, cx, {
let theme = theme.clone();
move |state, _| {
@ -400,7 +403,7 @@ impl View for ToggleDockButton {
.on_up(MouseButton::Left, move |event, cx| {
let dock_pane = workspace.read(cx.app).dock_pane();
let drop_index = dock_pane.read(cx.app).items_len() + 1;
Pane::handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx);
handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx);
});
if dock_position.is_visible() {

View File

@ -1,17 +1,19 @@
mod dragged_item_receiver;
use super::{ItemHandle, SplitDirection};
use crate::{
dock::{icon_for_dock_anchor, AnchorDockBottom, AnchorDockRight, ExpandDock, HideDock},
toolbar::Toolbar,
Item, NewFile, NewSearch, NewTerminal, SplitWithItem, WeakItemHandle, Workspace,
Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace,
};
use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque};
use context_menu::{ContextMenu, ContextMenuItem};
use drag_and_drop::{DragAndDrop, Draggable};
use drag_and_drop::Draggable;
pub use dragged_item_receiver::{dragged_item_receiver, handle_dropped_item};
use futures::StreamExt;
use gpui::{
actions,
color::Color,
elements::*,
geometry::{
rect::RectF,
@ -19,7 +21,6 @@ use gpui::{
},
impl_actions, impl_internal_actions,
platform::{CursorStyle, NavigationDirection},
scene::MouseUp,
Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
ModelHandle, MouseButton, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View,
ViewContext, ViewHandle, WeakViewHandle,
@ -1056,11 +1057,7 @@ impl Pane {
fn render_tabs(&mut self, cx: &mut RenderContext<Self>) -> impl Element {
let theme = cx.global::<Settings>().theme.clone();
let filler_index = self.items.len();
enum Tabs {}
enum Tab {}
enum Filler {}
let pane = cx.handle();
let autoscroll = if mem::take(&mut self.autoscroll) {
Some(self.active_item_index)
@ -1070,6 +1067,7 @@ impl Pane {
let pane_active = self.is_active;
enum Tabs {}
let mut row = Flex::row().scrollable::<Tabs, _>(1, autoscroll, cx);
for (ix, (item, detail)) in self
.items
@ -1082,7 +1080,8 @@ impl Pane {
let tab_active = ix == self.active_item_index;
row.add_child({
MouseEventHandler::<Tab>::above(ix, cx, {
enum Tab {}
dragged_item_receiver::<Tab, _>(ix, ix, true, None, cx, {
let item = item.clone();
let pane = pane.clone();
let detail = detail.clone();
@ -1092,16 +1091,7 @@ impl Pane {
move |mouse_state, cx| {
let tab_style = theme.workspace.tab_bar.tab_style(pane_active, tab_active);
let hovered = mouse_state.hovered();
Self::render_tab(
&item,
pane,
ix == 0,
detail,
hovered,
Self::tab_overlay_color(hovered, cx),
tab_style,
cx,
)
Self::render_tab(&item, pane, ix == 0, detail, hovered, tab_style, cx)
}
})
.with_cursor_style(if pane_active && tab_active {
@ -1123,10 +1113,6 @@ impl Pane {
})
}
})
.on_up(MouseButton::Left, {
let pane = pane.clone();
move |event, cx| Pane::handle_dropped_item(event, &pane, ix, true, None, cx)
})
.as_draggable(
DraggedItem {
item,
@ -1144,7 +1130,6 @@ impl Pane {
false,
detail,
false,
None,
&tab_style,
cx,
)
@ -1157,22 +1142,16 @@ impl Pane {
// Use the inactive tab style along with the current pane's active status to decide how to render
// the filler
let filler_index = self.items.len();
let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false);
enum Filler {}
row.add_child(
MouseEventHandler::<Filler>::new(0, cx, |mouse_state, cx| {
let mut filler = Empty::new()
dragged_item_receiver::<Filler, _>(0, filler_index, true, None, cx, |_, _| {
Empty::new()
.contained()
.with_style(filler_style.container)
.with_border(filler_style.container.border);
if let Some(overlay) = Self::tab_overlay_color(mouse_state.hovered(), cx) {
filler = filler.with_overlay_color(overlay);
}
filler.boxed()
})
.on_up(MouseButton::Left, move |event, cx| {
Pane::handle_dropped_item(event, &pane, filler_index, true, None, cx)
.with_border(filler_style.container.border)
.boxed()
})
.flex(1., true)
.named("filler"),
@ -1224,7 +1203,6 @@ impl Pane {
first: bool,
detail: Option<usize>,
hovered: bool,
overlay: Option<Color>,
tab_style: &theme::Tab,
cx: &mut RenderContext<V>,
) -> ElementBox {
@ -1234,7 +1212,7 @@ impl Pane {
container.border.left = false;
}
let mut tab = Flex::row()
Flex::row()
.with_child(
Align::new({
let diameter = 7.0;
@ -1312,13 +1290,10 @@ impl Pane {
.boxed(),
)
.contained()
.with_style(container);
if let Some(overlay) = overlay {
tab = tab.with_overlay_color(overlay);
}
tab.constrained().with_height(tab_style.height).boxed()
.with_style(container)
.constrained()
.with_height(tab_style.height)
.boxed()
}
fn render_tab_bar_buttons(
@ -1356,79 +1331,6 @@ impl Pane {
.flex(1., false)
.boxed()
}
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| Self::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 tab_overlay_color(hovered: bool, cx: &mut RenderContext<Self>) -> Option<Color> {
if hovered
&& cx
.global::<DragAndDrop<Workspace>>()
.currently_dragged::<DraggedItem>(cx.window_id())
.is_some()
{
Some(
cx.global::<Settings>()
.theme
.workspace
.drop_target_overlay_color,
)
} else {
None
}
}
}
impl Entity for Pane {
@ -1449,8 +1351,6 @@ impl View for Pane {
.with_child(
MouseEventHandler::<MouseNavigationHandler>::new(0, cx, |_, cx| {
if let Some(active_item) = self.active_item() {
enum PaneContentTabDropTarget {}
Flex::column()
.with_child({
let mut tab_row = Flex::row()
@ -1471,78 +1371,29 @@ impl View for Pane {
.named("tab bar")
})
.with_child({
let drop_index = self.active_item_index + 1;
MouseEventHandler::<PaneContentTabDropTarget>::above(
enum PaneContentTabDropTarget {}
dragged_item_receiver::<PaneContentTabDropTarget, _>(
0,
self.active_item_index + 1,
false,
Some(100.),
cx,
|state, cx| {
let overlay_color = Self::tab_overlay_color(true, cx);
// 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(
Flex::column()
.with_child(
ChildView::new(&self.toolbar, cx)
.expanded()
.boxed(),
)
.with_child(
ChildView::new(active_item, cx)
.flex(1., true)
.boxed(),
)
.boxed(),
)
.with_children(drag_position.map(|drag_position| {
Canvas::new(move |region, _, cx| {
if region.contains_point(drag_position) {
let overlay_region = if let Some(
split_direction,
) =
Self::drop_split_direction(
drag_position,
region,
100., /* Replace with theme value */
) {
split_direction.along_edge(region, 100.)
} else {
region
};
cx.scene.push_quad(Quad {
bounds: overlay_region,
background: overlay_color,
border: Default::default(),
corner_radius: 0.,
});
}
})
{
let toolbar = self.toolbar.clone();
move |_, cx| {
Flex::column()
.with_child(
ChildView::new(&toolbar, cx).expanded().boxed(),
)
.with_child(
ChildView::new(active_item, cx)
.flex(1., true)
.boxed(),
)
.boxed()
}))
.boxed()
}
},
)
.on_up(MouseButton::Left, {
let pane = cx.handle();
move |event, cx| {
Pane::handle_dropped_item(
event,
&pane,
drop_index,
false,
Some(100.), /* Use theme value */
cx,
)
}
})
.flex(1., true)
.boxed()
})
@ -1551,7 +1402,7 @@ impl View for Pane {
enum EmptyPane {}
let theme = cx.global::<Settings>().theme.clone();
MouseEventHandler::<EmptyPane>::new(0, cx, |_, _| {
dragged_item_receiver::<EmptyPane, _>(0, 0, false, None, cx, |_, _| {
Empty::new()
.contained()
.with_background_color(theme.workspace.background)
@ -1560,12 +1411,6 @@ impl View for Pane {
.on_down(MouseButton::Left, |_, cx| {
cx.focus_parent_view();
})
.on_up(MouseButton::Left, {
let pane = this.clone();
move |event, cx| {
Pane::handle_dropped_item(event, &pane, 0, true, None, cx)
}
})
.boxed()
}
})

View 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
}