1use std::{
10 any::Any,
11 cell::{Cell, RefCell},
12 collections::HashMap,
13 fmt,
14 hash::{Hash, Hasher},
15 rc::Rc,
16};
17
18use cranpose_core::NodeId;
19use cranpose_foundation::{
20 DelegatableNode, InvalidationKind, ModifierNode, ModifierNodeContext, ModifierNodeElement,
21 NodeCapabilities, NodeState, PointerEvent, PointerEventKind, PointerInputNode,
22 gesture_constants::DRAG_THRESHOLD,
23};
24
25use super::{Modifier, Point};
26use crate::render_state::{AppContextId, current_app_context, with_app_context_by_id};
27
28pub type DragAndDropPayload = Rc<dyn Any>;
31
32type PayloadHandler = Rc<dyn Fn(&DragAndDropPayload)>;
33type PayloadAtHandler = Rc<dyn Fn(&DragAndDropPayload, Point)>;
34type DropHandler = Rc<dyn Fn(&DragAndDropPayload, Point) -> bool>;
35type PointHandler = Rc<dyn Fn(DragAndDropPoint)>;
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum DragAndDropOutcome {
40 Dropped,
42 Missed,
44 Cancelled,
46}
47
48#[derive(Clone, Copy, Debug, PartialEq)]
51pub struct DragAndDropPoint {
52 pub screen: Option<Point>,
54 pub local: Point,
56}
57
58#[derive(Clone)]
61pub struct DragAndDropSource {
62 payload: DragAndDropPayload,
63 on_started: Option<PointHandler>,
64 on_moved: Option<PointHandler>,
65 on_ended: Option<Rc<dyn Fn(DragAndDropOutcome)>>,
66}
67
68impl DragAndDropSource {
69 pub fn new(payload: impl Any) -> Self {
71 Self {
72 payload: Rc::new(payload),
73 on_started: None,
74 on_moved: None,
75 on_ended: None,
76 }
77 }
78
79 pub fn on_started(mut self, handler: impl Fn(DragAndDropPoint) + 'static) -> Self {
81 self.on_started = Some(Rc::new(handler));
82 self
83 }
84
85 pub fn on_moved(mut self, handler: impl Fn(DragAndDropPoint) + 'static) -> Self {
87 self.on_moved = Some(Rc::new(handler));
88 self
89 }
90
91 pub fn on_ended(mut self, handler: impl Fn(DragAndDropOutcome) + 'static) -> Self {
93 self.on_ended = Some(Rc::new(handler));
94 self
95 }
96}
97
98impl fmt::Debug for DragAndDropSource {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.debug_struct("DragAndDropSource").finish_non_exhaustive()
101 }
102}
103
104#[derive(Clone, Default)]
107pub struct DragAndDropTarget {
108 on_entered: Option<PayloadHandler>,
109 on_moved: Option<PayloadAtHandler>,
110 on_exited: Option<PayloadHandler>,
111 on_drop: Option<DropHandler>,
112}
113
114impl DragAndDropTarget {
115 pub fn new() -> Self {
117 Self::default()
118 }
119
120 pub fn on_entered(mut self, handler: impl Fn(&DragAndDropPayload) + 'static) -> Self {
122 self.on_entered = Some(Rc::new(handler));
123 self
124 }
125
126 pub fn on_moved(mut self, handler: impl Fn(&DragAndDropPayload, Point) + 'static) -> Self {
128 self.on_moved = Some(Rc::new(handler));
129 self
130 }
131
132 pub fn on_exited(mut self, handler: impl Fn(&DragAndDropPayload) + 'static) -> Self {
134 self.on_exited = Some(Rc::new(handler));
135 self
136 }
137
138 pub fn on_drop(
141 mut self,
142 handler: impl Fn(&DragAndDropPayload, Point) -> bool + 'static,
143 ) -> Self {
144 self.on_drop = Some(Rc::new(handler));
145 self
146 }
147}
148
149impl fmt::Debug for DragAndDropTarget {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 f.debug_struct("DragAndDropTarget").finish_non_exhaustive()
152 }
153}
154
155#[derive(Clone, Copy, Debug, PartialEq)]
157pub enum DragAndDropEvent {
158 Started(DragAndDropPoint),
160 Moved(DragAndDropPoint),
162 Dropped(DragAndDropPoint),
164 Cancelled,
166}
167
168struct Transfer {
169 source: DragAndDropSource,
170 over: Option<NodeId>,
171}
172
173#[derive(Default)]
176pub struct DragAndDropState {
177 targets: RefCell<HashMap<NodeId, DragAndDropTarget>>,
178 transfer: RefCell<Option<Transfer>>,
179 pending: RefCell<Vec<DragAndDropEvent>>,
180}
181
182impl DragAndDropState {
183 fn register_target(&self, node: NodeId, target: DragAndDropTarget) {
184 self.targets.borrow_mut().insert(node, target);
185 }
186
187 fn unregister_target(&self, node: NodeId) {
188 self.targets.borrow_mut().remove(&node);
189 let mut transfer = self.transfer.borrow_mut();
190 if let Some(transfer) = transfer.as_mut()
191 && transfer.over == Some(node)
192 {
193 transfer.over = None;
194 }
195 }
196
197 pub fn is_target(&self, node: NodeId) -> bool {
199 self.targets.borrow().contains_key(&node)
200 }
201
202 pub fn is_active(&self) -> bool {
204 self.transfer.borrow().is_some()
205 }
206
207 fn start(&self, source: DragAndDropSource, point: DragAndDropPoint) {
208 *self.transfer.borrow_mut() = Some(Transfer { source, over: None });
209 self.pending
210 .borrow_mut()
211 .push(DragAndDropEvent::Started(point));
212 }
213
214 fn record(&self, event: DragAndDropEvent) {
215 if self.transfer.borrow().is_some() {
216 self.pending.borrow_mut().push(event);
217 }
218 }
219
220 pub fn route(
225 &self,
226 mut find_target: impl FnMut(DragAndDropPoint) -> Option<(NodeId, Point)>,
227 ) -> bool {
228 let events = std::mem::take(&mut *self.pending.borrow_mut());
229 let routed = !events.is_empty();
230 for event in events {
231 match event {
232 DragAndDropEvent::Started(point) => {
233 self.tell_source(|source| source.on_started.clone(), point);
234 self.hover(find_target(point));
235 }
236 DragAndDropEvent::Moved(point) => {
237 self.tell_source(|source| source.on_moved.clone(), point);
238 self.hover(find_target(point));
239 }
240 DragAndDropEvent::Dropped(point) => {
241 let hit = find_target(point);
242 self.hover(hit);
243 let accepted = hit.is_some_and(|(node, local)| self.drop_on(node, local));
244 self.finish(if accepted {
245 DragAndDropOutcome::Dropped
246 } else {
247 DragAndDropOutcome::Missed
248 });
249 }
250 DragAndDropEvent::Cancelled => {
251 self.hover(None);
252 self.finish(DragAndDropOutcome::Cancelled);
253 }
254 }
255 }
256 routed
257 }
258
259 fn tell_source(
260 &self,
261 handler: impl Fn(&DragAndDropSource) -> Option<PointHandler>,
262 point: DragAndDropPoint,
263 ) {
264 let handler = self
265 .transfer
266 .borrow()
267 .as_ref()
268 .and_then(|transfer| handler(&transfer.source));
269 if let Some(handler) = handler {
270 handler(point);
271 }
272 }
273
274 fn payload(&self) -> Option<DragAndDropPayload> {
275 self.transfer
276 .borrow()
277 .as_ref()
278 .map(|transfer| Rc::clone(&transfer.source.payload))
279 }
280
281 fn target(&self, node: NodeId) -> Option<DragAndDropTarget> {
282 self.targets.borrow().get(&node).cloned()
283 }
284
285 fn hover(&self, hit: Option<(NodeId, Point)>) {
286 let Some(payload) = self.payload() else {
287 return;
288 };
289 let previous = self
290 .transfer
291 .borrow()
292 .as_ref()
293 .and_then(|transfer| transfer.over);
294 let current = hit.map(|(node, _)| node);
295 if previous != current {
296 if let Some(exited) = previous.and_then(|node| self.target(node))
297 && let Some(on_exited) = exited.on_exited
298 {
299 on_exited(&payload);
300 }
301 if let Some(entered) = current.and_then(|node| self.target(node))
302 && let Some(on_entered) = entered.on_entered
303 {
304 on_entered(&payload);
305 }
306 if let Some(transfer) = self.transfer.borrow_mut().as_mut() {
307 transfer.over = current;
308 }
309 }
310 if let Some((node, local)) = hit
311 && let Some(on_moved) = self.target(node).and_then(|target| target.on_moved)
312 {
313 on_moved(&payload, local);
314 }
315 }
316
317 fn drop_on(&self, node: NodeId, local: Point) -> bool {
318 let Some(payload) = self.payload() else {
319 return false;
320 };
321 self.target(node)
322 .and_then(|target| target.on_drop)
323 .is_some_and(|on_drop| on_drop(&payload, local))
324 }
325
326 fn finish(&self, outcome: DragAndDropOutcome) {
327 let transfer = self.transfer.borrow_mut().take();
328 if let Some(on_ended) = transfer.and_then(|transfer| transfer.source.on_ended) {
329 on_ended(outcome);
330 }
331 }
332}
333
334#[derive(Default)]
335struct SourceGesture {
336 press: Option<Point>,
337 dragging: bool,
338}
339
340impl SourceGesture {
341 fn on_event(
342 &mut self,
343 event: &PointerEvent,
344 source: &DragAndDropSource,
345 state: &DragAndDropState,
346 ) {
347 let point = DragAndDropPoint {
348 screen: event.screen_position,
349 local: event.global_position,
350 };
351 match event.kind {
352 PointerEventKind::Down => {
353 self.press = Some(event.global_position);
354 self.dragging = false;
355 }
356 PointerEventKind::Move => self.on_move(event, source, state, point),
357 PointerEventKind::Up => {
358 if self.dragging {
359 state.record(DragAndDropEvent::Dropped(point));
360 event.consume();
361 }
362 *self = Self::default();
363 }
364 PointerEventKind::Cancel => {
365 if self.dragging {
366 state.record(DragAndDropEvent::Cancelled);
367 }
368 *self = Self::default();
369 }
370 PointerEventKind::Scroll
371 | PointerEventKind::Zoom
372 | PointerEventKind::RotaryScrollPre
373 | PointerEventKind::RotaryScroll
374 | PointerEventKind::Enter
375 | PointerEventKind::Exit => {}
376 }
377 }
378
379 fn on_move(
380 &mut self,
381 event: &PointerEvent,
382 source: &DragAndDropSource,
383 state: &DragAndDropState,
384 point: DragAndDropPoint,
385 ) {
386 let Some(press) = self.press else {
387 return;
388 };
389 if self.dragging {
390 state.record(DragAndDropEvent::Moved(point));
391 event.consume();
392 return;
393 }
394 let dx = event.global_position.x - press.x;
395 let dy = event.global_position.y - press.y;
396 if (dx * dx + dy * dy).sqrt() > DRAG_THRESHOLD {
397 self.dragging = true;
398 state.start(source.clone(), point);
399 event.consume();
400 }
401 }
402}
403
404type PointerHandler = Rc<dyn Fn(PointerEvent)>;
405
406fn source_handler(
407 source: Rc<RefCell<DragAndDropSource>>,
408 gesture: Rc<RefCell<SourceGesture>>,
409) -> PointerHandler {
410 Rc::new(move |event: PointerEvent| {
411 let Some(context) = current_app_context() else {
412 return;
413 };
414 let source = source.borrow().clone();
415 gesture
416 .borrow_mut()
417 .on_event(&event, &source, context.drag_and_drop());
418 })
419}
420
421pub struct DragAndDropSourceNode {
423 source: Rc<RefCell<DragAndDropSource>>,
424 handler: PointerHandler,
425 state: NodeState,
426}
427
428impl fmt::Debug for DragAndDropSourceNode {
429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430 f.debug_struct("DragAndDropSourceNode")
431 .finish_non_exhaustive()
432 }
433}
434
435impl DelegatableNode for DragAndDropSourceNode {
436 fn node_state(&self) -> &NodeState {
437 &self.state
438 }
439}
440
441impl ModifierNode for DragAndDropSourceNode {
442 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
443 context.invalidate(InvalidationKind::PointerInput);
444 }
445
446 cranpose_foundation::impl_modifier_node!(pointer_input);
447}
448
449impl PointerInputNode for DragAndDropSourceNode {
450 fn on_pointer_event(
451 &mut self,
452 _context: &mut dyn ModifierNodeContext,
453 event: &PointerEvent,
454 ) -> bool {
455 (self.handler)(event.clone());
456 event.is_consumed()
457 }
458
459 fn pointer_input_handler(&self) -> Option<PointerHandler> {
460 Some(Rc::clone(&self.handler))
461 }
462}
463
464#[derive(Clone, Debug)]
466pub struct DragAndDropSourceElement {
467 source: DragAndDropSource,
468}
469
470impl PartialEq for DragAndDropSourceElement {
471 fn eq(&self, _other: &Self) -> bool {
472 true
473 }
474}
475
476impl Hash for DragAndDropSourceElement {
477 fn hash<H: Hasher>(&self, state: &mut H) {
478 "dragAndDropSource".hash(state);
479 }
480}
481
482impl ModifierNodeElement for DragAndDropSourceElement {
483 type Node = DragAndDropSourceNode;
484
485 fn create(&self) -> Self::Node {
486 let source = Rc::new(RefCell::new(self.source.clone()));
487 let gesture = Rc::new(RefCell::new(SourceGesture::default()));
488 DragAndDropSourceNode {
489 handler: source_handler(Rc::clone(&source), gesture),
490 source,
491 state: NodeState::new(),
492 }
493 }
494
495 fn update(&self, node: &mut Self::Node) {
496 *node.source.borrow_mut() = self.source.clone();
497 }
498
499 fn always_update(&self) -> bool {
500 true
501 }
502
503 fn capabilities(&self) -> NodeCapabilities {
504 NodeCapabilities::POINTER_INPUT
505 }
506
507 fn inspector_name(&self) -> &'static str {
508 "dragAndDropSource"
509 }
510}
511
512pub struct DragAndDropTargetNode {
516 target: DragAndDropTarget,
517 node_id: Cell<Option<NodeId>>,
518 owner: Cell<Option<AppContextId>>,
519 handler: PointerHandler,
520 state: NodeState,
521}
522
523impl fmt::Debug for DragAndDropTargetNode {
524 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
525 f.debug_struct("DragAndDropTargetNode")
526 .field("node_id", &self.node_id.get())
527 .finish()
528 }
529}
530
531impl DragAndDropTargetNode {
532 fn register(&self) {
533 let Some(node) = self.node_id.get() else {
534 return;
535 };
536 let Some(context) = current_app_context() else {
537 return;
538 };
539 self.owner.set(Some(context.id()));
540 context
541 .drag_and_drop()
542 .register_target(node, self.target.clone());
543 }
544
545 fn unregister(&self) {
546 let (Some(node), Some(owner)) = (self.node_id.get(), self.owner.take()) else {
547 return;
548 };
549 with_app_context_by_id(owner, |context| {
550 context.drag_and_drop().unregister_target(node)
551 });
552 }
553}
554
555impl DelegatableNode for DragAndDropTargetNode {
556 fn node_state(&self) -> &NodeState {
557 &self.state
558 }
559}
560
561impl ModifierNode for DragAndDropTargetNode {
562 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
563 self.node_id.set(context.node_id());
564 self.register();
565 context.invalidate(InvalidationKind::PointerInput);
566 }
567
568 fn on_detach(&mut self) {
569 self.unregister();
570 }
571
572 cranpose_foundation::impl_modifier_node!(pointer_input);
573}
574
575impl PointerInputNode for DragAndDropTargetNode {
576 fn pointer_input_handler(&self) -> Option<PointerHandler> {
577 Some(Rc::clone(&self.handler))
578 }
579}
580
581#[derive(Clone, Debug)]
583pub struct DragAndDropTargetElement {
584 target: DragAndDropTarget,
585}
586
587impl PartialEq for DragAndDropTargetElement {
588 fn eq(&self, _other: &Self) -> bool {
589 true
590 }
591}
592
593impl Hash for DragAndDropTargetElement {
594 fn hash<H: Hasher>(&self, state: &mut H) {
595 "dragAndDropTarget".hash(state);
596 }
597}
598
599impl ModifierNodeElement for DragAndDropTargetElement {
600 type Node = DragAndDropTargetNode;
601
602 fn create(&self) -> Self::Node {
603 DragAndDropTargetNode {
604 target: self.target.clone(),
605 node_id: Cell::new(None),
606 owner: Cell::new(None),
607 handler: Rc::new(|_event: PointerEvent| {}),
608 state: NodeState::new(),
609 }
610 }
611
612 fn update(&self, node: &mut Self::Node) {
613 node.target = self.target.clone();
614 node.register();
615 }
616
617 fn always_update(&self) -> bool {
618 true
619 }
620
621 fn capabilities(&self) -> NodeCapabilities {
622 NodeCapabilities::POINTER_INPUT
623 }
624
625 fn inspector_name(&self) -> &'static str {
626 "dragAndDropTarget"
627 }
628}
629
630impl Modifier {
631 pub fn drag_and_drop_source(self, source: DragAndDropSource) -> Self {
635 self.then(Self::with_element(DragAndDropSourceElement { source }))
636 }
637
638 pub fn drag_and_drop_target(self, target: DragAndDropTarget) -> Self {
642 self.then(Self::with_element(DragAndDropTargetElement { target }))
643 }
644}
645
646#[cfg(test)]
647#[path = "tests/drag_and_drop_tests.rs"]
648mod tests;