Skip to main content

dioxus_dnd/core/
effects.rs

1//! Target-side acceptance and drop-effect negotiation.
2
3use std::ops::{BitOr, BitOrAssign};
4
5use super::{DragId, DragMode, DropEffect, PointerKind, ZoneId};
6
7/// A compact set of drop effects a target supports.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub struct DropEffects(u8);
10
11impl DropEffects {
12    pub const EMPTY: Self = Self(0);
13    pub const MOVE: Self = Self(1 << 0);
14    pub const COPY: Self = Self(1 << 1);
15    pub const LINK: Self = Self(1 << 2);
16    pub const STANDARD: Self = Self(Self::MOVE.0 | Self::COPY.0 | Self::LINK.0);
17    pub const ALL: Self = Self::STANDARD;
18
19    pub fn contains(self, effect: DropEffect) -> bool {
20        let flag = match effect {
21            DropEffect::Move => Self::MOVE,
22            DropEffect::Copy => Self::COPY,
23            DropEffect::Link => Self::LINK,
24            DropEffect::None => return false,
25        };
26        self.0 & flag.0 != 0
27    }
28
29    pub fn is_empty(self) -> bool {
30        self.0 == 0
31    }
32
33    /// Accept the proposed effect or choose a deterministic supported
34    /// fallback. `None` is never chosen as a fallback for an active drop.
35    pub fn negotiate(self, proposed: DropEffect) -> Option<DropEffect> {
36        if proposed == DropEffect::None {
37            return None;
38        }
39        if self.contains(proposed) {
40            return Some(proposed);
41        }
42        [DropEffect::Move, DropEffect::Copy, DropEffect::Link]
43            .into_iter()
44            .find(|effect| self.contains(*effect))
45    }
46}
47
48impl Default for DropEffects {
49    fn default() -> Self {
50        Self::ALL
51    }
52}
53
54impl BitOr for DropEffects {
55    type Output = Self;
56
57    fn bitor(self, rhs: Self) -> Self::Output {
58        Self(self.0 | rhs.0)
59    }
60}
61
62impl BitOrAssign for DropEffects {
63    fn bitor_assign(&mut self, rhs: Self) {
64        self.0 |= rhs.0;
65    }
66}
67
68/// Full target-side acceptance input.
69#[derive(Debug, Clone, PartialEq)]
70#[non_exhaustive]
71pub struct DropQuery<T> {
72    pub payload: T,
73    pub source: Option<ZoneId>,
74    pub proposed_effect: DropEffect,
75    pub mode: DragMode,
76    pub pointer_kind: PointerKind,
77    pub drag_id: Option<DragId>,
78}
79
80impl<T> DropQuery<T> {
81    pub fn new(payload: T) -> Self {
82        Self {
83            payload,
84            source: None,
85            proposed_effect: DropEffect::default(),
86            mode: DragMode::default(),
87            pointer_kind: PointerKind::default(),
88            drag_id: None,
89        }
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn negotiation_keeps_supported_effect_and_falls_back_explicitly() {
99        let effects = DropEffects::MOVE | DropEffects::COPY;
100        assert_eq!(effects.negotiate(DropEffect::Copy), Some(DropEffect::Copy));
101        assert_eq!(effects.negotiate(DropEffect::Link), Some(DropEffect::Move));
102        assert_eq!(DropEffects::EMPTY.negotiate(DropEffect::Move), None);
103        assert_eq!(DropEffects::ALL.negotiate(DropEffect::None), None);
104        assert!(!DropEffects::ALL.contains(DropEffect::None));
105    }
106}