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 PointerEventKind::Scroll
370 | PointerEventKind::Zoom
371 | PointerEventKind::RotaryScrollPre
372 | PointerEventKind::RotaryScroll
373 | PointerEventKind::Enter
374 | PointerEventKind::Exit => {}
375 }
376 }
377
378 fn on_move(
379 &mut self,
380 event: &PointerEvent,
381 source: &DragAndDropSource,
382 state: &DragAndDropState,
383 point: DragAndDropPoint,
384 ) {
385 let Some(press) = self.press else {
386 return;
387 };
388 if self.dragging {
389 state.record(DragAndDropEvent::Moved(point));
390 event.consume();
391 return;
392 }
393 let dx = event.global_position.x - press.x;
394 let dy = event.global_position.y - press.y;
395 if (dx * dx + dy * dy).sqrt() > DRAG_THRESHOLD {
396 self.dragging = true;
397 state.start(source.clone(), point);
398 event.consume();
399 }
400 }
401}
402
403type PointerHandler = Rc<dyn Fn(PointerEvent)>;
404
405fn source_handler(
406 source: Rc<RefCell<DragAndDropSource>>,
407 gesture: Rc<RefCell<SourceGesture>>,
408) -> PointerHandler {
409 Rc::new(move |event: PointerEvent| {
410 let Some(context) = current_app_context() else {
411 return;
412 };
413 let source = source.borrow().clone();
414 gesture
415 .borrow_mut()
416 .on_event(&event, &source, context.drag_and_drop());
417 })
418}
419
420pub struct DragAndDropSourceNode {
422 source: Rc<RefCell<DragAndDropSource>>,
423 handler: PointerHandler,
424 state: NodeState,
425}
426
427impl fmt::Debug for DragAndDropSourceNode {
428 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429 f.debug_struct("DragAndDropSourceNode")
430 .finish_non_exhaustive()
431 }
432}
433
434impl DelegatableNode for DragAndDropSourceNode {
435 fn node_state(&self) -> &NodeState {
436 &self.state
437 }
438}
439
440impl ModifierNode for DragAndDropSourceNode {
441 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
442 context.invalidate(InvalidationKind::PointerInput);
443 }
444
445 cranpose_foundation::impl_modifier_node!(pointer_input);
446}
447
448impl PointerInputNode for DragAndDropSourceNode {
449 fn on_pointer_event(
450 &mut self,
451 _context: &mut dyn ModifierNodeContext,
452 event: &PointerEvent,
453 ) -> bool {
454 (self.handler)(event.clone());
455 event.is_consumed()
456 }
457
458 fn pointer_input_handler(&self) -> Option<PointerHandler> {
459 Some(Rc::clone(&self.handler))
460 }
461}
462
463#[derive(Clone, Debug)]
465pub struct DragAndDropSourceElement {
466 source: DragAndDropSource,
467}
468
469impl PartialEq for DragAndDropSourceElement {
470 fn eq(&self, _other: &Self) -> bool {
471 true
472 }
473}
474
475impl Hash for DragAndDropSourceElement {
476 fn hash<H: Hasher>(&self, state: &mut H) {
477 "dragAndDropSource".hash(state);
478 }
479}
480
481impl ModifierNodeElement for DragAndDropSourceElement {
482 type Node = DragAndDropSourceNode;
483
484 fn create(&self) -> Self::Node {
485 let source = Rc::new(RefCell::new(self.source.clone()));
486 let gesture = Rc::new(RefCell::new(SourceGesture::default()));
487 DragAndDropSourceNode {
488 handler: source_handler(Rc::clone(&source), gesture),
489 source,
490 state: NodeState::new(),
491 }
492 }
493
494 fn update(&self, node: &mut Self::Node) {
495 *node.source.borrow_mut() = self.source.clone();
496 }
497
498 fn always_update(&self) -> bool {
499 true
500 }
501
502 fn capabilities(&self) -> NodeCapabilities {
503 NodeCapabilities::POINTER_INPUT
504 }
505
506 fn inspector_name(&self) -> &'static str {
507 "dragAndDropSource"
508 }
509}
510
511pub struct DragAndDropTargetNode {
515 target: DragAndDropTarget,
516 node_id: Cell<Option<NodeId>>,
517 owner: Cell<Option<AppContextId>>,
518 handler: PointerHandler,
519 state: NodeState,
520}
521
522impl fmt::Debug for DragAndDropTargetNode {
523 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
524 f.debug_struct("DragAndDropTargetNode")
525 .field("node_id", &self.node_id.get())
526 .finish()
527 }
528}
529
530impl DragAndDropTargetNode {
531 fn register(&self) {
532 let Some(node) = self.node_id.get() else {
533 return;
534 };
535 let Some(context) = current_app_context() else {
536 return;
537 };
538 self.owner.set(Some(context.id()));
539 context
540 .drag_and_drop()
541 .register_target(node, self.target.clone());
542 }
543
544 fn unregister(&self) {
545 let (Some(node), Some(owner)) = (self.node_id.get(), self.owner.take()) else {
546 return;
547 };
548 with_app_context_by_id(owner, |context| {
549 context.drag_and_drop().unregister_target(node);
550 });
551 }
552}
553
554impl DelegatableNode for DragAndDropTargetNode {
555 fn node_state(&self) -> &NodeState {
556 &self.state
557 }
558}
559
560impl ModifierNode for DragAndDropTargetNode {
561 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
562 self.node_id.set(context.node_id());
563 self.register();
564 context.invalidate(InvalidationKind::PointerInput);
565 }
566
567 fn on_detach(&mut self) {
568 self.unregister();
569 }
570
571 cranpose_foundation::impl_modifier_node!(pointer_input);
572}
573
574impl PointerInputNode for DragAndDropTargetNode {
575 fn pointer_input_handler(&self) -> Option<PointerHandler> {
576 Some(Rc::clone(&self.handler))
577 }
578}
579
580#[derive(Clone, Debug)]
582pub struct DragAndDropTargetElement {
583 target: DragAndDropTarget,
584}
585
586impl PartialEq for DragAndDropTargetElement {
587 fn eq(&self, _other: &Self) -> bool {
588 true
589 }
590}
591
592impl Hash for DragAndDropTargetElement {
593 fn hash<H: Hasher>(&self, state: &mut H) {
594 "dragAndDropTarget".hash(state);
595 }
596}
597
598impl ModifierNodeElement for DragAndDropTargetElement {
599 type Node = DragAndDropTargetNode;
600
601 fn create(&self) -> Self::Node {
602 DragAndDropTargetNode {
603 target: self.target.clone(),
604 node_id: Cell::new(None),
605 owner: Cell::new(None),
606 handler: Rc::new(|_event: PointerEvent| {}),
607 state: NodeState::new(),
608 }
609 }
610
611 fn update(&self, node: &mut Self::Node) {
612 node.target = self.target.clone();
613 node.register();
614 }
615
616 fn always_update(&self) -> bool {
617 true
618 }
619
620 fn capabilities(&self) -> NodeCapabilities {
621 NodeCapabilities::POINTER_INPUT
622 }
623
624 fn inspector_name(&self) -> &'static str {
625 "dragAndDropTarget"
626 }
627}
628
629impl Modifier {
630 pub fn drag_and_drop_source(self, source: DragAndDropSource) -> Self {
634 self.then(Self::with_element(DragAndDropSourceElement { source }))
635 }
636
637 pub fn drag_and_drop_target(self, target: DragAndDropTarget) -> Self {
641 self.then(Self::with_element(DragAndDropTargetElement { target }))
642 }
643}
644
645#[cfg(test)]
646#[path = "tests/drag_and_drop_tests.rs"]
647mod tests;