wip tab drag and drop
This commit is contained in:
parent
86fdd55fd4
commit
133c194f4a
20 changed files with 1642 additions and 413 deletions
15
crates/drag_and_drop/Cargo.toml
Normal file
15
crates/drag_and_drop/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "drag_and_drop"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/drag_and_drop.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
collections = { path = "../collections" }
|
||||
gpui = { path = "../gpui" }
|
||||
|
||||
[dev-dependencies]
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
151
crates/drag_and_drop/src/drag_and_drop.rs
Normal file
151
crates/drag_and_drop/src/drag_and_drop.rs
Normal file
|
@ -0,0 +1,151 @@
|
|||
use std::{any::Any, sync::Arc};
|
||||
|
||||
use gpui::{
|
||||
elements::{Container, MouseEventHandler},
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
Element, ElementBox, EventContext, MouseButton, RenderContext, View, ViewContext,
|
||||
WeakViewHandle,
|
||||
};
|
||||
|
||||
struct State<V: View> {
|
||||
position: Vector2F,
|
||||
region_offset: Vector2F,
|
||||
payload: Arc<dyn Any>,
|
||||
render: Arc<dyn Fn(Arc<dyn Any>, &mut RenderContext<V>) -> ElementBox>,
|
||||
}
|
||||
|
||||
impl<V: View> Clone for State<V> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
position: self.position.clone(),
|
||||
region_offset: self.region_offset.clone(),
|
||||
payload: self.payload.clone(),
|
||||
render: self.render.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DragAndDrop<V: View> {
|
||||
parent: WeakViewHandle<V>,
|
||||
currently_dragged: Option<State<V>>,
|
||||
}
|
||||
|
||||
impl<V: View> DragAndDrop<V> {
|
||||
pub fn new(parent: WeakViewHandle<V>, cx: &mut ViewContext<V>) -> Self {
|
||||
// TODO: Figure out if detaching here would result in a memory leak
|
||||
cx.observe_global::<Self, _>(|cx| {
|
||||
if let Some(parent) = cx.global::<Self>().parent.upgrade(cx) {
|
||||
parent.update(cx, |_, cx| cx.notify())
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Self {
|
||||
parent,
|
||||
currently_dragged: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn currently_dragged<T: Any>(&self) -> Option<(Vector2F, &T)> {
|
||||
self.currently_dragged.as_ref().and_then(
|
||||
|State {
|
||||
position, payload, ..
|
||||
}| {
|
||||
payload
|
||||
.downcast_ref::<T>()
|
||||
.map(|payload| (position.clone(), payload))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dragging<T: Any>(
|
||||
relative_to: Option<RectF>,
|
||||
position: Vector2F,
|
||||
payload: Arc<T>,
|
||||
cx: &mut EventContext,
|
||||
render: Arc<impl 'static + Fn(&T, &mut RenderContext<V>) -> ElementBox>,
|
||||
) {
|
||||
cx.update_global::<Self, _, _>(|this, cx| {
|
||||
let region_offset = if let Some(previous_state) = this.currently_dragged.as_ref() {
|
||||
previous_state.region_offset
|
||||
} else {
|
||||
if let Some(relative_to) = relative_to {
|
||||
relative_to.origin() - position
|
||||
} else {
|
||||
Vector2F::zero()
|
||||
}
|
||||
};
|
||||
|
||||
this.currently_dragged = Some(State {
|
||||
region_offset,
|
||||
position,
|
||||
payload,
|
||||
render: Arc::new(move |payload, cx| {
|
||||
render(payload.downcast_ref::<T>().unwrap(), cx)
|
||||
}),
|
||||
});
|
||||
|
||||
if let Some(parent) = this.parent.upgrade(cx) {
|
||||
parent.update(cx, |_, cx| cx.notify())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn render(cx: &mut RenderContext<V>) -> Option<ElementBox> {
|
||||
let currently_dragged = cx.global::<Self>().currently_dragged.clone();
|
||||
|
||||
currently_dragged.map(
|
||||
|State {
|
||||
region_offset,
|
||||
position,
|
||||
payload,
|
||||
render,
|
||||
}| {
|
||||
let position = position + region_offset;
|
||||
|
||||
MouseEventHandler::new::<Self, _, _>(0, cx, |_, cx| {
|
||||
Container::new(render(payload, cx))
|
||||
.with_margin_left(position.x())
|
||||
.with_margin_top(position.y())
|
||||
.boxed()
|
||||
})
|
||||
.on_up(MouseButton::Left, |_, cx| {
|
||||
cx.defer(|cx| {
|
||||
cx.update_global::<Self, _, _>(|this, _| this.currently_dragged.take());
|
||||
});
|
||||
cx.propogate_event();
|
||||
})
|
||||
.boxed()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Draggable {
|
||||
fn as_draggable<V: View, P: Any>(
|
||||
self,
|
||||
payload: P,
|
||||
render: impl 'static + Fn(&P, &mut RenderContext<V>) -> ElementBox,
|
||||
) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl Draggable for MouseEventHandler {
|
||||
fn as_draggable<V: View, P: Any>(
|
||||
self,
|
||||
payload: P,
|
||||
render: impl 'static + Fn(&P, &mut RenderContext<V>) -> ElementBox,
|
||||
) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let payload = Arc::new(payload);
|
||||
let render = Arc::new(render);
|
||||
self.on_drag(MouseButton::Left, move |e, cx| {
|
||||
let payload = payload.clone();
|
||||
let render = render.clone();
|
||||
DragAndDrop::<V>::dragging(Some(e.region), e.position, payload, cx, render)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue