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;
24use theme::Theme;
25
26use crate::surface::{Surface, Surfaced};
27
28pub trait Carried: Styled + IntoElement + Sized {
30 fn ghost(self, theme: &Theme) -> Surface {
33 self.surface(theme, theme.carried_surface)
34 }
35}
36
37impl<E: Styled + IntoElement> Carried for E {}
38
39mod element;
40mod state;
41
42pub use element::{Handle, Region};
43pub(crate) use state::Gesture;
44use state::State;
45
46pub struct Carry<I> {
48 item: I,
49 pub(crate) gesture: Rc<Gesture>,
50}
51
52impl<I> Carry<I> {
53 pub fn item(&self) -> &I {
54 &self.item
55 }
56
57 pub fn claimed(&self) -> bool {
60 self.gesture.claimed.get()
61 }
62
63 pub fn ghost_bounds(&self, pointer: Point<Pixels>) -> gpui::Bounds<Pixels> {
65 self.gesture.ghost_bounds(pointer)
66 }
67
68 pub fn host(&self, origin: Point<Pixels>) {
71 self.gesture.host(origin);
72 }
73}
74
75#[derive(Clone, Debug, PartialEq, Eq)]
79pub struct Drop<R, I> {
80 pub item: I,
81 pub from: R,
82 pub region: R,
83 pub after: Option<I>,
84 pub before: Option<I>,
85}
86
87#[derive(Clone, Debug)]
89pub struct Outside<I> {
90 pub item: I,
91 pub position: Point<Pixels>,
92}
93
94#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
96pub enum Feedback {
97 #[default]
99 Displace,
100 Indicator,
102}
103
104type Ghost<I> = dyn Fn(&I, &mut Window, &mut App) -> AnyElement;
105type Paint = dyn Fn(&mut Window, &mut App) -> AnyElement;
106
107pub struct Domain<R, I>(Rc<RefCell<State<R, I>>>);
109
110impl<R, I> Clone for Domain<R, I> {
111 fn clone(&self) -> Self {
112 Self(self.0.clone())
113 }
114}
115
116impl<R: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> Domain<R, I> {
117 pub fn new(painter: Painter) -> Self {
119 Self(Rc::new(RefCell::new(State::new(painter, None))))
120 }
121
122 pub fn with_ghost(
126 painter: Painter,
127 ghost: impl Fn(&I, &mut Window, &mut App) -> AnyElement + 'static,
128 ) -> Self {
129 Self(Rc::new(RefCell::new(State::new(
130 painter,
131 Some(Rc::new(ghost)),
132 ))))
133 }
134
135 pub fn region(
137 &self,
138 element_id: impl Into<ElementId>,
139 id: R,
140 axis: Axis,
141 child: impl IntoElement,
142 ) -> Region<R, I> {
143 self.0.borrow_mut().begin_frame();
144 Region::new(
145 self.clone(),
146 element_id.into(),
147 id,
148 axis,
149 child.into_any_element(),
150 )
151 }
152
153 pub fn handle<E>(&self, item: I, element: E) -> Handle<R, I>
157 where
158 E: StatefulInteractiveElement + IntoElement + 'static,
159 {
160 let domain = self.clone();
161 let carry = Carry {
162 item: item.clone(),
163 gesture: Rc::new(Gesture::default()),
164 };
165 let element = element.on_drag(carry, move |carry, grab, window, cx| {
166 domain.start(carry, grab, window, cx)
167 });
168 Handle::new(self.clone(), item, element.into_any_element())
169 }
170
171 pub fn fixed(&self, item: I, element: impl IntoElement) -> Handle<R, I> {
174 Handle::new(self.clone(), item, element.into_any_element())
175 }
176
177 pub fn carries(&self, item: &I) -> bool {
179 self.0
180 .borrow()
181 .live
182 .as_ref()
183 .is_some_and(|live| &live.item == item)
184 }
185
186 pub fn cancel(&self, window: &mut Window, cx: &mut App) {
188 let live = self.0.borrow_mut().end(window, cx);
189 if live.is_some() {
190 cx.stop_active_drag(window);
191 }
192 }
193
194 pub fn carried(&self) -> Option<I> {
196 self.0.borrow().live.as_ref().map(|live| live.item.clone())
197 }
198
199 pub(crate) fn invalidate(&self) {
201 self.0.borrow_mut().stale = true;
202 }
203
204 fn start(
205 &self,
206 carry: &Carry<I>,
207 cursor: Point<Pixels>,
208 window: &mut Window,
209 cx: &mut App,
210 ) -> gpui::Entity<Ghosted> {
211 let (grab, ghost) = {
212 let mut state = self.0.borrow_mut();
213 let grab = state.start(&carry.item, carry.gesture.clone(), cursor, window, cx);
214 (grab, state.ghost.clone())
215 };
216 carry.gesture.ghosted.set(ghost.is_some());
217 let item = carry.item.clone();
218 let gesture = carry.gesture.clone();
219 cx.new(|_| Ghosted {
220 gesture,
221 cursor,
222 grab,
223 render: ghost.map(|ghost| {
224 Rc::new(move |window: &mut Window, cx: &mut App| ghost(&item, window, cx))
225 as Rc<Paint>
226 }),
227 })
228 }
229}
230
231pub struct Ghosted {
233 gesture: Rc<Gesture>,
234 cursor: Point<Pixels>,
236 grab: Point<Pixels>,
238 render: Option<Rc<Paint>>,
239}
240
241impl Render for Ghosted {
242 fn render(&mut self, window: &mut Window, cx: &mut gpui::Context<Self>) -> impl IntoElement {
243 let hosted = self.gesture.hosted.get() && !self.gesture.claimed.get();
244 match self.render.as_ref().filter(|_| !hosted) {
245 Some(render) => {
246 let gesture = self.gesture.clone();
247 gpui::div()
250 .child(AtGrab {
251 cursor: self.cursor,
252 grab: self.grab,
253 child: gpui::div()
254 .child(render(window, cx))
255 .child(
256 gpui::canvas(
257 move |bounds, _, _| gesture.shown.set(Some(bounds.origin)),
258 |_, _, _, _| {},
259 )
260 .absolute()
261 .top_0()
262 .left_0()
263 .size_full(),
264 )
265 .into_any_element(),
266 })
267 .into_any_element()
268 }
269 None => gpui::Empty.into_any_element(),
270 }
271 }
272}
273
274struct AtGrab {
277 cursor: Point<Pixels>,
278 grab: Point<Pixels>,
279 child: AnyElement,
280}
281
282impl IntoElement for AtGrab {
283 type Element = Self;
284
285 fn into_element(self) -> Self {
286 self
287 }
288}
289
290impl Element for AtGrab {
291 type RequestLayoutState = ();
292 type PrepaintState = ();
293
294 fn id(&self) -> Option<ElementId> {
295 None
296 }
297
298 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
299 None
300 }
301
302 fn request_layout(
303 &mut self,
304 _id: Option<&GlobalElementId>,
305 _inspector_id: Option<&InspectorElementId>,
306 window: &mut Window,
307 cx: &mut App,
308 ) -> (LayoutId, ()) {
309 (self.child.request_layout(window, cx), ())
310 }
311
312 fn prepaint(
313 &mut self,
314 _id: Option<&GlobalElementId>,
315 _inspector_id: Option<&InspectorElementId>,
316 bounds: Bounds<Pixels>,
317 _request_layout: &mut (),
318 window: &mut Window,
319 cx: &mut App,
320 ) {
321 let grab = point(
322 self.grab.x.clamp(Pixels::ZERO, bounds.size.width),
323 self.grab.y.clamp(Pixels::ZERO, bounds.size.height),
324 );
325 window.with_element_offset(self.cursor - grab, |window| self.child.prepaint(window, cx));
326 }
327
328 fn paint(
329 &mut self,
330 _id: Option<&GlobalElementId>,
331 _inspector_id: Option<&InspectorElementId>,
332 _bounds: Bounds<Pixels>,
333 _request_layout: &mut (),
334 _prepaint: &mut (),
335 window: &mut Window,
336 cx: &mut App,
337 ) {
338 self.child.paint(window, cx);
339 }
340}