dioxus_dnd/core/
activation.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5#[non_exhaustive]
6pub enum Activator {
7 #[default]
9 Surface,
10 Handle,
12 Manual,
14}
15
16#[derive(Debug, Clone, PartialEq)]
18#[non_exhaustive]
19pub enum ActivationConstraint {
20 Distance(f64),
22 Delay { duration_ms: u32, tolerance: f64 },
24 Either(Vec<ActivationConstraint>),
26 Manual,
29}
30
31impl Default for ActivationConstraint {
32 fn default() -> Self {
33 Self::Distance(8.0)
34 }
35}
36
37impl ActivationConstraint {
38 fn collect_delays(&self, delays: &mut Vec<(u32, f64)>) {
39 match self {
40 Self::Delay {
41 duration_ms,
42 tolerance,
43 } => delays.push((*duration_ms, tolerance.max(0.0))),
44 Self::Either(items) => {
45 for item in items {
46 item.collect_delays(delays);
47 }
48 }
49 Self::Distance(_) | Self::Manual => {}
50 }
51 }
52
53 pub(crate) fn delays(&self) -> Vec<(u32, f64)> {
54 let mut delays = Vec::new();
55 self.collect_delays(&mut delays);
56 delays
57 }
58
59 pub fn distance(&self) -> Option<f64> {
61 match self {
62 Self::Distance(distance) => Some(distance.max(0.0)),
63 Self::Either(items) => items
64 .iter()
65 .filter_map(Self::distance)
66 .min_by(f64::total_cmp),
67 Self::Delay { .. } | Self::Manual => None,
68 }
69 }
70
71 pub fn delay(&self) -> Option<(u32, f64)> {
73 self.delays()
74 .into_iter()
75 .min_by_key(|(duration, _)| *duration)
76 }
77
78 pub fn is_manual(&self) -> bool {
80 match self {
81 Self::Manual => true,
82 Self::Either(items) => items.iter().all(Self::is_manual),
84 Self::Distance(_) | Self::Delay { .. } => false,
85 }
86 }
87
88 pub fn exceeded_delay_tolerance(&self, dx: f64, dy: f64) -> bool {
90 let distance = dx.hypot(dy);
91 let delays = self.delays();
92 !delays.is_empty() && delays.iter().all(|(_, tolerance)| distance > *tolerance)
93 }
94}
95
96#[derive(Debug, Clone, PartialEq)]
98#[non_exhaustive]
99pub struct ActivationPolicy {
100 pub activator: Activator,
101 pub constraint: ActivationConstraint,
102}
103
104impl Default for ActivationPolicy {
105 fn default() -> Self {
106 Self {
107 activator: Activator::Surface,
108 constraint: ActivationConstraint::default(),
109 }
110 }
111}
112
113impl ActivationPolicy {
114 pub fn surface(constraint: ActivationConstraint) -> Self {
115 Self {
116 activator: Activator::Surface,
117 constraint,
118 }
119 }
120
121 pub fn handle(constraint: ActivationConstraint) -> Self {
122 Self {
123 activator: Activator::Handle,
124 constraint,
125 }
126 }
127
128 pub fn manual() -> Self {
129 Self {
130 activator: Activator::Manual,
131 constraint: ActivationConstraint::Manual,
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn either_exposes_earliest_distance_and_delay() {
142 let policy = ActivationConstraint::Either(vec![
143 ActivationConstraint::Distance(12.0),
144 ActivationConstraint::Delay {
145 duration_ms: 300,
146 tolerance: 5.0,
147 },
148 ActivationConstraint::Distance(7.0),
149 ActivationConstraint::Delay {
150 duration_ms: 180,
151 tolerance: 3.0,
152 },
153 ]);
154
155 assert_eq!(policy.distance(), Some(7.0));
156 assert_eq!(policy.delay(), Some((180, 3.0)));
157 assert!(!policy.exceeded_delay_tolerance(4.0, 0.0));
158 assert!(policy.exceeded_delay_tolerance(6.0, 0.0));
159 assert_eq!(
160 policy.delays(),
161 vec![(300, 5.0), (180, 3.0)],
162 "nested delay alternatives retain their independent clocks and tolerances"
163 );
164 }
165
166 #[test]
167 fn empty_or_manual_only_disjunctions_are_manual() {
168 assert!(ActivationConstraint::Either(Vec::new()).is_manual());
169 assert!(ActivationConstraint::Either(vec![
170 ActivationConstraint::Manual,
171 ActivationConstraint::Either(Vec::new()),
172 ])
173 .is_manual());
174 }
175}