Skip to main content

guise/dnd/
droptarget.rs

1//! `DropTarget` — receive typed drags with a built-in hover highlight.
2
3use std::rc::Rc;
4
5use gpui::prelude::*;
6use gpui::{div, AnyElement, App, ElementId, IntoElement, Window};
7
8use super::chip::DragChip;
9use crate::devtools::Probed;
10use crate::theme::theme;
11
12type DropHandler<T> = Rc<dyn Fn(&T, &mut Window, &mut App) + 'static>;
13
14/// A region that accepts drags from [`Draggable`](super::Draggable)s carrying
15/// the same payload type. While a matching drag hovers, the target shows a
16/// primary border + tint (disable with `.plain()`).
17///
18/// ```ignore
19/// DropTarget::<CardId>::new("done-lane")
20///     .on_drop(|card, _window, _cx| move_to_done(*card))
21///     .child(lane_content)
22/// ```
23#[derive(IntoElement)]
24pub struct DropTarget<T: Clone + 'static> {
25  id: ElementId,
26  child: Option<AnyElement>,
27  highlight: bool,
28  on_drop: Option<DropHandler<T>>,
29}
30
31impl<T: Clone + 'static> DropTarget<T> {
32  pub fn new(id: impl Into<ElementId>) -> Self {
33    DropTarget {
34      id: id.into(),
35      child: None,
36      highlight: true,
37      on_drop: None,
38    }
39  }
40
41  /// Disable the built-in drag-over highlight.
42  pub fn plain(mut self) -> Self {
43    self.highlight = false;
44    self
45  }
46
47  pub fn child(mut self, child: impl IntoElement) -> Self {
48    self.child = Some(child.into_any_element());
49    self
50  }
51
52  /// Receives the dropped payload.
53  pub fn on_drop(mut self, handler: impl Fn(&T, &mut Window, &mut App) + 'static) -> Self {
54    self.on_drop = Some(Rc::new(handler));
55    self
56  }
57}
58
59impl<T: Clone + 'static> RenderOnce for DropTarget<T> {
60  fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
61    let t = theme(cx);
62    let accent = t.primary();
63    let accent_border = accent.hsla();
64    let accent_tint = accent.alpha(0.08);
65
66    let mut root = div().id(self.id);
67    if self.highlight {
68      root = root.drag_over::<DragChip<T>>(move |style, _drag, _window, _cx| {
69        style.border_color(accent_border).bg(accent_tint)
70      });
71    }
72    if let Some(handler) = self.on_drop {
73      root = root.on_drop(move |dragged: &DragChip<T>, window, cx| {
74        handler(&dragged.value, window, cx);
75      });
76    }
77    if let Some(child) = self.child {
78      root = root.child(child);
79    }
80    root.probe("DropTarget")
81  }
82}