1use std::{cell::RefCell, rc::Rc};
17
18use gpui::{
19 AnyElement, App, Axis, Bounds, Element, ElementId, GlobalElementId, InspectorElementId,
20 IntoElement, LayoutId, Pixels, Point, Render, StatefulInteractiveElement, Window, point,
21 prelude::*,
22};
23use motion::Painter;
24
25mod element;
26mod state;
27
28pub use element::{Handle, Region};
29pub(crate) use state::Gesture;
30use state::State;
31
32pub struct Carry<I> {
34 item: I,
35 pub(crate) gesture: Rc<Gesture>,
36}
37
38impl<I> Carry<I> {
39 pub fn item(&self) -> &I {
40 &self.item
41 }
42
43 pub fn claimed(&self) -> bool {
46 self.gesture.claimed.get()
47 }
48
49 pub fn ghost_bounds(&self, pointer: Point<Pixels>) -> gpui::Bounds<Pixels> {
51 self.gesture.ghost_bounds(pointer)
52 }
53
54 pub fn host(&self, origin: Point<Pixels>) {
57 self.gesture.host(origin);
58 }
59}
60
61#[derive(Clone, Debug, PartialEq, Eq)]
65pub struct Drop<R, I> {
66 pub item: I,
67 pub from: R,
68 pub region: R,
69 pub after: Option<I>,
70 pub before: Option<I>,
71}
72
73#[derive(Clone, Debug)]
75pub struct Outside<I> {
76 pub item: I,
77 pub position: Point<Pixels>,
78}
79
80#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
82pub enum Feedback {
83 #[default]
85 Displace,
86 Indicator,
88}
89
90type Ghost<I> = dyn Fn(&I, &mut Window, &mut App) -> AnyElement;
91type Paint = dyn Fn(&mut Window, &mut App) -> AnyElement;
92
93pub struct Domain<R, I>(Rc<RefCell<State<R, I>>>);
95
96impl<R, I> Clone for Domain<R, I> {
97 fn clone(&self) -> Self {
98 Self(self.0.clone())
99 }
100}
101
102impl<R: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> Domain<R, I> {
103 pub fn new(painter: Painter) -> Self {
105 Self(Rc::new(RefCell::new(State::new(painter, None))))
106 }
107
108 pub fn with_ghost(
112 painter: Painter,
113 ghost: impl Fn(&I, &mut Window, &mut App) -> AnyElement + 'static,
114 ) -> Self {
115 Self(Rc::new(RefCell::new(State::new(
116 painter,
117 Some(Rc::new(ghost)),
118 ))))
119 }
120
121 pub fn region(
123 &self,
124 element_id: impl Into<ElementId>,
125 id: R,
126 axis: Axis,
127 child: impl IntoElement,
128 ) -> Region<R, I> {
129 self.0.borrow_mut().begin_frame();
130 Region::new(
131 self.clone(),
132 element_id.into(),
133 id,
134 axis,
135 child.into_any_element(),
136 )
137 }
138
139 pub fn handle<E>(&self, item: I, element: E) -> Handle<R, I>
143 where
144 E: StatefulInteractiveElement + IntoElement + 'static,
145 {
146 let domain = self.clone();
147 let carry = Carry {
148 item: item.clone(),
149 gesture: Rc::new(Gesture::default()),
150 };
151 let element = element.on_drag(carry, move |carry, grab, window, cx| {
152 domain.start(carry, grab, window, cx)
153 });
154 Handle::new(self.clone(), item, element.into_any_element())
155 }
156
157 pub fn fixed(&self, item: I, element: impl IntoElement) -> Handle<R, I> {
160 Handle::new(self.clone(), item, element.into_any_element())
161 }
162
163 pub fn carries(&self, item: &I) -> bool {
165 self.0
166 .borrow()
167 .live
168 .as_ref()
169 .is_some_and(|live| &live.item == item)
170 }
171
172 pub fn cancel(&self, window: &mut Window, cx: &mut App) {
174 let live = self.0.borrow_mut().end(window, cx);
175 if live.is_some() {
176 cx.stop_active_drag(window);
177 }
178 }
179
180 pub fn carried(&self) -> Option<I> {
182 self.0.borrow().live.as_ref().map(|live| live.item.clone())
183 }
184
185 pub(crate) fn invalidate(&self) {
187 self.0.borrow_mut().stale = true;
188 }
189
190 fn start(
191 &self,
192 carry: &Carry<I>,
193 cursor: Point<Pixels>,
194 window: &mut Window,
195 cx: &mut App,
196 ) -> gpui::Entity<Ghosted> {
197 let (grab, ghost) = {
198 let mut state = self.0.borrow_mut();
199 let grab = state.start(&carry.item, carry.gesture.clone(), cursor, window, cx);
200 (grab, state.ghost.clone())
201 };
202 let item = carry.item.clone();
203 let gesture = carry.gesture.clone();
204 cx.new(|_| Ghosted {
205 gesture,
206 cursor,
207 grab,
208 render: ghost.map(|ghost| {
209 Rc::new(move |window: &mut Window, cx: &mut App| ghost(&item, window, cx))
210 as Rc<Paint>
211 }),
212 })
213 }
214}
215
216pub struct Ghosted {
218 gesture: Rc<Gesture>,
219 cursor: Point<Pixels>,
221 grab: Point<Pixels>,
223 render: Option<Rc<Paint>>,
224}
225
226impl Render for Ghosted {
227 fn render(&mut self, window: &mut Window, cx: &mut gpui::Context<Self>) -> impl IntoElement {
228 let hosted = self.gesture.hosted.get() && !self.gesture.claimed.get();
229 match self.render.as_ref().filter(|_| !hosted) {
230 Some(render) => {
231 let gesture = self.gesture.clone();
232 gpui::div()
235 .child(AtGrab {
236 cursor: self.cursor,
237 grab: self.grab,
238 child: gpui::div()
239 .child(render(window, cx))
240 .child(
241 gpui::canvas(
242 move |bounds, _, _| gesture.shown.set(Some(bounds.origin)),
243 |_, _, _, _| {},
244 )
245 .absolute()
246 .top_0()
247 .left_0()
248 .size_full(),
249 )
250 .into_any_element(),
251 })
252 .into_any_element()
253 }
254 None => gpui::Empty.into_any_element(),
255 }
256 }
257}
258
259struct AtGrab {
262 cursor: Point<Pixels>,
263 grab: Point<Pixels>,
264 child: AnyElement,
265}
266
267impl IntoElement for AtGrab {
268 type Element = Self;
269
270 fn into_element(self) -> Self {
271 self
272 }
273}
274
275impl Element for AtGrab {
276 type RequestLayoutState = ();
277 type PrepaintState = ();
278
279 fn id(&self) -> Option<ElementId> {
280 None
281 }
282
283 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
284 None
285 }
286
287 fn request_layout(
288 &mut self,
289 _id: Option<&GlobalElementId>,
290 _inspector_id: Option<&InspectorElementId>,
291 window: &mut Window,
292 cx: &mut App,
293 ) -> (LayoutId, ()) {
294 (self.child.request_layout(window, cx), ())
295 }
296
297 fn prepaint(
298 &mut self,
299 _id: Option<&GlobalElementId>,
300 _inspector_id: Option<&InspectorElementId>,
301 bounds: Bounds<Pixels>,
302 _request_layout: &mut (),
303 window: &mut Window,
304 cx: &mut App,
305 ) {
306 let grab = point(
307 self.grab.x.clamp(Pixels::ZERO, bounds.size.width),
308 self.grab.y.clamp(Pixels::ZERO, bounds.size.height),
309 );
310 window.with_element_offset(self.cursor - grab, |window| self.child.prepaint(window, cx));
311 }
312
313 fn paint(
314 &mut self,
315 _id: Option<&GlobalElementId>,
316 _inspector_id: Option<&InspectorElementId>,
317 _bounds: Bounds<Pixels>,
318 _request_layout: &mut (),
319 _prepaint: &mut (),
320 window: &mut Window,
321 cx: &mut App,
322 ) {
323 self.child.paint(window, cx);
324 }
325}