1use dioxus::prelude::*;
15
16use super::monitor::{CancelReason, DndEvent, DndMonitor, DragSnapshot, DropReceipt};
17use super::session::SourceCompletion;
18use super::types::{DragId, DragMode, DragSessionId, DropEffect, Point, PointerKind, Rect, ZoneId};
19
20#[derive(Store, Debug, Clone, PartialEq)]
25pub struct DragState<T: 'static> {
26 pub payload: Option<T>,
28 pub source: Option<ZoneId>,
30 pub over: Option<ZoneId>,
32 pub pointer: Point,
34 pub grab: Point,
36 pub effect: DropEffect,
38 pub mode: DragMode,
40 pub pointer_kind: PointerKind,
46 pub source_rect: Option<Rect>,
51 pub refocus: Option<T>,
58 pub settle: Option<Rect>,
62}
63
64#[derive(Debug, Clone, PartialEq)]
70#[non_exhaustive]
71pub struct DragStart<T> {
72 pub payload: T,
73 pub source: Option<ZoneId>,
74 pub pointer: Point,
75 pub grab: Point,
76 pub effect: DropEffect,
77 pub mode: DragMode,
78 pub pointer_kind: PointerKind,
79 pub source_rect: Option<Rect>,
80}
81
82impl<T> DragStart<T> {
83 pub fn new(payload: T, pointer: Point) -> Self {
85 Self {
86 payload,
87 source: None,
88 pointer,
89 grab: Point::default(),
90 effect: DropEffect::default(),
91 mode: DragMode::default(),
92 pointer_kind: PointerKind::default(),
93 source_rect: None,
94 }
95 }
96
97 pub fn with_source(mut self, source: Option<ZoneId>) -> Self {
98 self.source = source;
99 self
100 }
101
102 pub fn with_grab(mut self, grab: Point) -> Self {
103 self.grab = grab;
104 self
105 }
106
107 pub fn with_effect(mut self, effect: DropEffect) -> Self {
108 self.effect = effect;
109 self
110 }
111
112 pub fn with_mode(mut self, mode: DragMode) -> Self {
113 self.mode = mode;
114 self
115 }
116
117 pub fn with_pointer_kind(mut self, pointer_kind: PointerKind) -> Self {
118 self.pointer_kind = pointer_kind;
119 self
120 }
121
122 pub fn with_source_rect(mut self, source_rect: Option<Rect>) -> Self {
123 self.source_rect = source_rect;
124 self
125 }
126}
127
128pub(super) enum DragIdentity {
129 Generated,
130 Explicit(DragId),
131}
132
133impl<T> Default for DragState<T> {
134 fn default() -> Self {
135 Self {
136 payload: None,
137 source: None,
138 over: None,
139 pointer: Point::default(),
140 grab: Point::default(),
141 effect: DropEffect::default(),
142 mode: DragMode::default(),
143 pointer_kind: PointerKind::default(),
144 source_rect: None,
145 refocus: None,
146 settle: None,
147 }
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
152pub(crate) enum DragPhase {
153 #[default]
154 Idle,
155 Dragging,
156 Settling,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq)]
160struct DragRuntimeState {
161 id: Option<DragId>,
162 identity_explicit: bool,
163 session: Option<DragSessionId>,
164 proposed_effect: DropEffect,
165 phase: DragPhase,
166}
167
168impl Default for DragRuntimeState {
169 fn default() -> Self {
170 Self {
171 id: None,
172 identity_explicit: false,
173 session: None,
174 proposed_effect: DropEffect::default(),
175 phase: DragPhase::Idle,
176 }
177 }
178}
179
180pub(super) struct DragRuntime<T: 'static> {
181 state: Signal<DragRuntimeState>,
182 pub(super) completion: Signal<Option<SourceCompletion>>,
183 monitor: DndMonitor<T>,
184}
185
186#[derive(Clone, Copy, PartialEq, Eq)]
187enum PhaseAuthority {
188 State,
191 Runtime,
194}
195
196impl<T> Copy for DragRuntime<T> {}
197impl<T> Clone for DragRuntime<T> {
198 fn clone(&self) -> Self {
199 *self
200 }
201}
202impl<T> PartialEq for DragRuntime<T> {
203 fn eq(&self, other: &Self) -> bool {
204 self.state == other.state
205 && self.completion == other.completion
206 && self.monitor == other.monitor
207 }
208}
209
210impl<T: Clone + 'static> DragRuntime<T> {
211 fn new() -> Self {
212 Self {
213 state: Signal::new(DragRuntimeState::default()),
214 completion: Signal::new(None),
215 monitor: DndMonitor::new(),
216 }
217 }
218}
219
220pub struct DndContext<T: Clone + 'static> {
223 state: Store<DragState<T>>,
224 announcement: Signal<String>,
227 pub(super) runtime: DragRuntime<T>,
228 phase_authority: PhaseAuthority,
229}
230
231impl<T: Clone + 'static> Copy for DndContext<T> {}
234impl<T: Clone + 'static> Clone for DndContext<T> {
235 fn clone(&self) -> Self {
236 *self
237 }
238}
239impl<T: Clone + 'static> PartialEq for DndContext<T> {
240 fn eq(&self, other: &Self) -> bool {
241 self.announcement == other.announcement
245 }
246}
247
248impl<T: Clone + 'static> DndContext<T> {
249 pub fn from_parts(state: Store<DragState<T>>, announcement: Signal<String>) -> Self {
251 Self::from_parts_with_authority(state, announcement, PhaseAuthority::State)
252 }
253
254 pub(crate) fn managed(state: Store<DragState<T>>, announcement: Signal<String>) -> Self {
258 Self::from_parts_with_authority(state, announcement, PhaseAuthority::Runtime)
259 }
260
261 fn from_parts_with_authority(
262 state: Store<DragState<T>>,
263 announcement: Signal<String>,
264 phase_authority: PhaseAuthority,
265 ) -> Self {
266 let phase = if state.settle().peek().is_some() {
267 DragPhase::Settling
268 } else if state.payload().peek().is_some() {
269 DragPhase::Dragging
270 } else {
271 DragPhase::Idle
272 };
273 let mut runtime = DragRuntime::new();
274 {
275 let mut runtime_state = runtime.state.write();
276 runtime_state.phase = phase;
277 runtime_state.proposed_effect = *state.effect().peek();
278 }
279 Self {
280 state,
281 announcement,
282 runtime,
283 phase_authority,
284 }
285 }
286
287 pub fn start(
289 &mut self,
290 payload: T,
291 source: Option<ZoneId>,
292 pointer: Point,
293 grab: Point,
294 effect: DropEffect,
295 mode: DragMode,
296 ) {
297 if !self.prepare_start() {
298 return;
299 }
300 self.start_with_metadata(
301 DragIdentity::Generated,
302 None,
303 DragStart::new(payload, pointer)
304 .with_source(source)
305 .with_grab(grab)
306 .with_effect(effect)
307 .with_mode(mode),
308 );
309 }
310
311 pub fn start_with_id(&mut self, id: DragId, start: DragStart<T>) {
313 if !self.prepare_start() {
314 return;
315 }
316 self.start_with_metadata(DragIdentity::Explicit(id), None, start);
317 }
318
319 pub(super) fn start_with_metadata(
320 &mut self,
321 identity: DragIdentity,
322 session: Option<DragSessionId>,
323 start: DragStart<T>,
324 ) {
325 let (id, identity_explicit) = match identity {
326 DragIdentity::Generated => (DragId::auto(), false),
327 DragIdentity::Explicit(id) => (id, true),
328 };
329 self.runtime.state.set(DragRuntimeState {
330 id: Some(id),
331 identity_explicit,
332 session,
333 proposed_effect: start.effect,
334 phase: DragPhase::Dragging,
335 });
336 self.state.set(DragState {
337 payload: Some(start.payload),
338 source: start.source,
339 over: None,
340 pointer: start.pointer,
341 grab: start.grab,
342 effect: start.effect,
343 mode: start.mode,
344 pointer_kind: start.pointer_kind,
345 source_rect: start.source_rect,
346 refocus: None,
348 settle: None,
350 });
351 self.runtime
352 .monitor
353 .emit_lazy(|| self.snapshot().map(DndEvent::Started));
354 }
355
356 pub fn set_pointer_kind(&mut self, kind: PointerKind) {
364 self.state.pointer_kind().set(kind);
365 }
366
367 pub fn request_refocus(&mut self, payload: T) {
373 self.state.refocus().set(Some(payload));
374 }
375
376 pub fn claim_refocus(&mut self, payload: &T) -> bool
380 where
381 T: PartialEq,
382 {
383 let mut refocus = self.state.refocus();
384 let hit = refocus.peek().as_ref() == Some(payload);
385 if hit {
386 refocus.set(None);
387 }
388 hit
389 }
390
391 pub fn set_source_rect(&mut self, rect: Option<Rect>) {
396 self.state.source_rect().set(rect);
397 }
398
399 pub fn update_pointer(&mut self, pointer: Point) {
402 if pointer.x == 0.0 && pointer.y == 0.0 {
406 return;
407 }
408 self.state.pointer().set(pointer);
409 self.runtime
410 .monitor
411 .emit_lazy(|| self.snapshot().map(DndEvent::Moved));
412 }
413
414 pub(crate) fn set_proposed_effect(&mut self, effect: DropEffect) {
418 let mut runtime = self.runtime.state;
419 if runtime.peek().proposed_effect != effect {
420 runtime.write().proposed_effect = effect;
421 }
422 }
423
424 pub fn enter(&mut self, zone: ZoneId) {
426 let previous = self.over();
427 if previous == Some(zone) {
428 return;
429 }
430 self.state.over().set(Some(zone));
431 self.runtime.monitor.emit_lazy(|| {
432 self.snapshot().map(|drag| DndEvent::TargetChanged {
433 drag,
434 previous,
435 current: Some(zone),
436 })
437 });
438 }
439
440 pub fn leave(&mut self, zone: ZoneId) {
443 let previous = self.over();
444 if previous == Some(zone) {
445 self.state.over().set(None);
446 self.runtime.monitor.emit_lazy(|| {
447 self.snapshot().map(|drag| DndEvent::TargetChanged {
448 drag,
449 previous,
450 current: None,
451 })
452 });
453 }
454 }
455
456 pub fn take(&mut self) -> Option<(T, Option<ZoneId>)> {
459 let (payload, source) = {
460 let mut s = self.state.write();
461 (s.payload.take(), s.source)
462 };
463 let payload = payload?;
464 self.state.set(DragState::default());
465 self.runtime.state.set(DragRuntimeState::default());
466 Some((payload, source))
467 }
468
469 pub fn take_settling(&mut self, to: Rect) -> Option<(T, Option<ZoneId>)> {
481 let mut s = self.state.write();
482 let payload = s.payload.clone()?;
483 let source = s.source;
484 s.over = None;
485 s.settle = Some(to);
486 drop(s);
487 self.runtime.state.write().phase = DragPhase::Settling;
488 Some((payload, source))
489 }
490
491 pub fn retarget_settle(&mut self, to: Rect) {
496 let mut settle = self.state.settle();
497 if settle.peek().is_some() && *settle.peek() != Some(to) {
502 settle.set(Some(to));
503 }
504 }
505
506 pub fn finish_settle(&mut self) {
509 if self.phase_peek() == DragPhase::Settling {
510 self.state.set(DragState::default());
511 self.runtime.state.set(DragRuntimeState::default());
512 }
513 }
514
515 pub(crate) fn phase_peek(&self) -> DragPhase {
516 match self.phase_authority {
517 PhaseAuthority::State => {
518 if self.state.settle().peek().is_some() {
519 DragPhase::Settling
520 } else if self.state.payload().peek().is_some() {
521 DragPhase::Dragging
522 } else {
523 DragPhase::Idle
524 }
525 }
526 PhaseAuthority::Runtime => self
527 .runtime
528 .state
529 .try_peek()
530 .map(|runtime| runtime.phase)
531 .unwrap_or(DragPhase::Idle),
532 }
533 }
534
535 pub(crate) fn alive(&self) -> bool {
545 self.announcement.try_peek().is_ok()
546 }
547
548 pub fn cancel(&mut self) {
550 self.cancel_with_reason(CancelReason::User);
551 }
552
553 pub fn cancel_with_reason(&mut self, reason: CancelReason) {
555 if self.phase_peek() == DragPhase::Settling {
556 self.finish_settle();
557 return;
558 }
559 if let Some(session) = self.drag_session_id() {
560 if self.cancel_session(session, reason) {
561 return;
562 }
563 }
564 self.cancel_state(reason);
565 }
566
567 pub(super) fn cancel_state(&mut self, reason: CancelReason) {
568 let snapshot = (self.phase_peek() == DragPhase::Dragging
569 && self.runtime.monitor.has_listeners())
570 .then(|| self.snapshot())
571 .flatten();
572 self.state.set(DragState::default());
573 self.runtime.state.set(DragRuntimeState::default());
574 if let Some(drag) = snapshot {
575 self.runtime
576 .monitor
577 .emit(DndEvent::Cancelled { drag, reason });
578 }
579 }
580
581 pub fn dragging(&self) -> bool {
588 match self.phase_authority {
589 PhaseAuthority::State => {
590 self.state.payload().is_some() && self.state.settle().is_none()
591 }
592 PhaseAuthority::Runtime => self.runtime.state.read().phase == DragPhase::Dragging,
593 }
594 }
595
596 pub fn settling(&self) -> Option<Rect> {
599 match self.phase_authority {
600 PhaseAuthority::State => self.state.settle().cloned(),
601 PhaseAuthority::Runtime => (self.runtime.state.read().phase == DragPhase::Settling)
602 .then(|| self.state.settle().cloned())
603 .flatten(),
604 }
605 }
606
607 pub(crate) fn settling_peek(&self) -> bool {
610 self.phase_peek() == DragPhase::Settling
611 }
612
613 pub fn payload(&self) -> Option<T> {
615 self.state.payload().cloned()
616 }
617
618 pub fn drag_id(&self) -> Option<DragId> {
620 self.runtime.state.read().id
621 }
622
623 pub fn has_explicit_drag_id(&self) -> bool {
625 self.runtime.state.read().identity_explicit
626 }
627
628 pub fn drag_session_id(&self) -> Option<DragSessionId> {
630 self.runtime.state.read().session
631 }
632
633 pub fn over(&self) -> Option<ZoneId> {
635 self.state.over().cloned()
636 }
637
638 pub fn source(&self) -> Option<ZoneId> {
640 self.state.source().cloned()
641 }
642
643 pub fn pointer(&self) -> Point {
645 self.state.pointer().cloned()
646 }
647
648 pub fn grab(&self) -> Point {
650 self.state.grab().cloned()
651 }
652
653 pub fn source_rect(&self) -> Option<Rect> {
655 self.state.source_rect().cloned()
656 }
657
658 pub fn effect(&self) -> DropEffect {
660 self.state.effect().cloned()
661 }
662
663 pub(crate) fn proposed_effect(&self) -> DropEffect {
664 self.runtime.state.read().proposed_effect
665 }
666
667 pub fn mode(&self) -> DragMode {
669 self.state.mode().cloned()
670 }
671
672 pub fn pointer_kind(&self) -> PointerKind {
675 self.state.pointer_kind().cloned()
676 }
677
678 pub fn snapshot(&self) -> Option<DragSnapshot<T>> {
680 Some(DragSnapshot {
681 id: self.drag_id()?,
682 session: self.drag_session_id(),
683 payload: self.payload()?,
684 source: self.source(),
685 over: self.over(),
686 pointer: self.pointer(),
687 grab: self.grab(),
688 effect: self.effect(),
689 mode: self.mode(),
690 pointer_kind: self.pointer_kind(),
691 source_rect: self.source_rect(),
692 })
693 }
694
695 pub(crate) fn monitor_mut(&mut self) -> DndMonitor<T> {
696 self.runtime.monitor
697 }
698
699 pub(crate) fn emit_dropped(&self, receipt: DropReceipt<T>) {
700 self.runtime.monitor.emit(DndEvent::Dropped(receipt));
701 }
702
703 pub(crate) fn monitor_has_listeners(&self) -> bool {
704 self.runtime.monitor.has_listeners()
705 }
706
707 pub fn announce(&mut self, msg: impl Into<String>) {
711 self.announcement.set(msg.into());
712 }
713
714 pub fn announcement(&self) -> String {
716 self.announcement.read().clone()
717 }
718}