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