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}