Skip to main content

guise/dnd/
draggable.rs

1//! `Draggable` — make any element the source of a typed drag.
2
3use gpui::prelude::*;
4use gpui::{div, AnyElement, App, ElementId, IntoElement, SharedString, Window};
5
6use super::chip::DragChip;
7use crate::devtools::Probed;
8
9/// Wraps a child so dragging it carries `payload`; pair with a
10/// [`DropTarget`](super::DropTarget) of the same payload type.
11///
12/// ```ignore
13/// Draggable::new("card-3", CardId(3))
14///     .label("Q3 report")
15///     .child(Card::new().child(summary))
16/// ```
17#[derive(IntoElement)]
18pub struct Draggable<T: Clone + 'static> {
19    id: ElementId,
20    payload: T,
21    label: SharedString,
22    child: Option<AnyElement>,
23}
24
25impl<T: Clone + 'static> Draggable<T> {
26    pub fn new(id: impl Into<ElementId>, payload: T) -> Self {
27        Draggable {
28            id: id.into(),
29            payload,
30            label: SharedString::new_static("…"),
31            child: None,
32        }
33    }
34
35    /// Text on the chip that follows the pointer (default "…").
36    pub fn label(mut self, label: impl Into<SharedString>) -> Self {
37        self.label = label.into();
38        self
39    }
40
41    pub fn child(mut self, child: impl IntoElement) -> Self {
42        self.child = Some(child.into_any_element());
43        self
44    }
45}
46
47impl<T: Clone + 'static> RenderOnce for Draggable<T> {
48    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
49        let chip = DragChip {
50            value: self.payload,
51            label: self.label,
52        };
53        let mut root = div()
54            .id(self.id)
55            .cursor_grab()
56            .on_drag(chip, |dragged: &DragChip<T>, _offset, _window, cx| {
57                cx.new(|_| dragged.clone())
58            });
59        if let Some(child) = self.child {
60            root = root.child(child);
61        }
62        root.probe("Draggable")
63    }
64}