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}