1#![doc = include_str!("../../docs/api/drop-effects.md")]
2
3use std::collections::HashMap;
4
5use super::{DropEffect, DropOutcome, ZoneId};
6
7pub fn apply_clone_or_move<T, K>(
25 zones: &mut HashMap<ZoneId, Vec<T>>,
26 outcome: DropOutcome<T>,
27 key: impl Fn(&T) -> K,
28 mut clone_item: impl FnMut(T) -> T,
29) where
30 K: PartialEq,
31{
32 let DropOutcome {
33 payload,
34 from,
35 to,
36 effect,
37 ..
38 } = outcome;
39 let item = if effect == DropEffect::Copy {
40 clone_item(payload)
41 } else {
42 if let Some(from) = from {
43 let payload_key = key(&payload);
44 if let Some(source) = zones.get_mut(&from) {
45 source.retain(|item| key(item) != payload_key);
46 }
47 }
48 payload
49 };
50
51 zones.entry(to).or_default().push(item);
52}
53
54pub fn apply_list_clone_or_move<T, K>(
66 source: Option<&mut Vec<T>>,
67 target: &mut Vec<T>,
68 outcome: DropOutcome<T>,
69 key: impl Fn(&T) -> K,
70 mut clone_item: impl FnMut(T) -> T,
71) where
72 K: PartialEq,
73{
74 let DropOutcome {
75 payload, effect, ..
76 } = outcome;
77 let item = if effect == DropEffect::Copy {
78 clone_item(payload)
79 } else {
80 if let Some(source) = source {
81 let payload_key = key(&payload);
82 source.retain(|item| key(item) != payload_key);
83 }
84 payload
85 };
86
87 target.push(item);
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::core::{DragMode, Point};
94
95 #[derive(Debug, Clone, PartialEq)]
96 struct Card {
97 id: u32,
98 title: &'static str,
99 }
100
101 fn outcome(
102 payload: Card,
103 from: Option<ZoneId>,
104 to: ZoneId,
105 effect: DropEffect,
106 ) -> DropOutcome<Card> {
107 DropOutcome {
108 payload,
109 from,
110 to,
111 effect,
112 mode: DragMode::Pointer,
113 client: Point::default(),
114 element: Point::default(),
115 grab: Point::default(),
116 edge: None,
117 }
118 }
119
120 #[test]
121 fn move_removes_from_source_and_appends_to_target() {
122 let a = ZoneId(1);
123 let b = ZoneId(2);
124 let mut zones = HashMap::from([
125 (
126 a,
127 vec![
128 Card {
129 id: 1,
130 title: "one",
131 },
132 Card {
133 id: 2,
134 title: "two",
135 },
136 ],
137 ),
138 (
139 b,
140 vec![Card {
141 id: 3,
142 title: "three",
143 }],
144 ),
145 ]);
146
147 apply_clone_or_move(
148 &mut zones,
149 outcome(
150 Card {
151 id: 2,
152 title: "two",
153 },
154 Some(a),
155 b,
156 DropEffect::Move,
157 ),
158 |card| card.id,
159 |card| card,
160 );
161
162 assert_eq!(
163 zones[&a],
164 vec![Card {
165 id: 1,
166 title: "one"
167 }]
168 );
169 assert_eq!(
170 zones[&b],
171 vec![
172 Card {
173 id: 3,
174 title: "three"
175 },
176 Card {
177 id: 2,
178 title: "two"
179 }
180 ]
181 );
182 }
183
184 #[test]
185 fn copy_leaves_source_and_allows_new_identity() {
186 let a = ZoneId(1);
187 let b = ZoneId(2);
188 let mut zones = HashMap::from([
189 (
190 a,
191 vec![Card {
192 id: 1,
193 title: "one",
194 }],
195 ),
196 (b, Vec::new()),
197 ]);
198
199 apply_clone_or_move(
200 &mut zones,
201 outcome(
202 Card {
203 id: 1,
204 title: "one",
205 },
206 Some(a),
207 b,
208 DropEffect::Copy,
209 ),
210 |card| card.id,
211 |mut card| {
212 card.id = 10;
213 card
214 },
215 );
216
217 assert_eq!(
218 zones[&a],
219 vec![Card {
220 id: 1,
221 title: "one"
222 }]
223 );
224 assert_eq!(
225 zones[&b],
226 vec![Card {
227 id: 10,
228 title: "one"
229 }]
230 );
231 }
232
233 #[test]
236 fn move_onto_own_zone_reorders_to_end() {
237 let a = ZoneId(1);
238 let mut zones = HashMap::from([(
239 a,
240 vec![
241 Card {
242 id: 1,
243 title: "one",
244 },
245 Card {
246 id: 2,
247 title: "two",
248 },
249 ],
250 )]);
251
252 apply_clone_or_move(
253 &mut zones,
254 outcome(
255 Card {
256 id: 1,
257 title: "one",
258 },
259 Some(a),
260 a,
261 DropEffect::Move,
262 ),
263 |card| card.id,
264 |card| card,
265 );
266
267 assert_eq!(
268 zones[&a],
269 vec![
270 Card {
271 id: 2,
272 title: "two"
273 },
274 Card {
275 id: 1,
276 title: "one"
277 }
278 ]
279 );
280 }
281
282 #[test]
285 fn move_without_source_zone_just_appends() {
286 let b = ZoneId(2);
287 let mut zones = HashMap::from([(b, Vec::new())]);
288
289 apply_clone_or_move(
290 &mut zones,
291 outcome(
292 Card {
293 id: 7,
294 title: "seven",
295 },
296 None,
297 b,
298 DropEffect::Move,
299 ),
300 |card| card.id,
301 |card| card,
302 );
303
304 assert_eq!(
305 zones[&b],
306 vec![Card {
307 id: 7,
308 title: "seven"
309 }]
310 );
311 }
312
313 #[test]
315 fn unknown_target_zone_is_created() {
316 let a = ZoneId(1);
317 let ghost = ZoneId(99);
318 let mut zones = HashMap::from([(
319 a,
320 vec![Card {
321 id: 1,
322 title: "one",
323 }],
324 )]);
325
326 apply_clone_or_move(
327 &mut zones,
328 outcome(
329 Card {
330 id: 1,
331 title: "one",
332 },
333 Some(a),
334 ghost,
335 DropEffect::Move,
336 ),
337 |card| card.id,
338 |card| card,
339 );
340
341 assert!(zones[&a].is_empty());
342 assert_eq!(
343 zones[&ghost],
344 vec![Card {
345 id: 1,
346 title: "one"
347 }]
348 );
349 }
350
351 #[test]
352 fn list_move_removes_from_source_and_appends_to_target() {
353 let mut source = vec![
354 Card {
355 id: 1,
356 title: "one",
357 },
358 Card {
359 id: 2,
360 title: "two",
361 },
362 ];
363 let mut target = vec![Card {
364 id: 3,
365 title: "three",
366 }];
367
368 apply_list_clone_or_move(
369 Some(&mut source),
370 &mut target,
371 outcome(
372 Card {
373 id: 2,
374 title: "two",
375 },
376 Some(ZoneId(1)),
377 ZoneId(2),
378 DropEffect::Move,
379 ),
380 |card| card.id,
381 |card| card,
382 );
383
384 assert_eq!(
385 source,
386 vec![Card {
387 id: 1,
388 title: "one"
389 }]
390 );
391 assert_eq!(
392 target,
393 vec![
394 Card {
395 id: 3,
396 title: "three"
397 },
398 Card {
399 id: 2,
400 title: "two"
401 }
402 ]
403 );
404 }
405
406 #[test]
407 fn list_copy_leaves_source_and_allows_new_identity() {
408 let mut source = vec![Card {
409 id: 1,
410 title: "one",
411 }];
412 let mut target = Vec::new();
413
414 apply_list_clone_or_move(
415 Some(&mut source),
416 &mut target,
417 outcome(
418 Card {
419 id: 1,
420 title: "one",
421 },
422 Some(ZoneId(1)),
423 ZoneId(2),
424 DropEffect::Copy,
425 ),
426 |card| card.id,
427 |mut card| {
428 card.id = 10;
429 card
430 },
431 );
432
433 assert_eq!(
434 source,
435 vec![Card {
436 id: 1,
437 title: "one"
438 }]
439 );
440 assert_eq!(
441 target,
442 vec![Card {
443 id: 10,
444 title: "one"
445 }]
446 );
447 }
448
449 #[test]
451 fn list_move_without_source_just_appends() {
452 let mut target = Vec::new();
453
454 apply_list_clone_or_move(
455 None,
456 &mut target,
457 outcome(
458 Card {
459 id: 7,
460 title: "seven",
461 },
462 None,
463 ZoneId(2),
464 DropEffect::Move,
465 ),
466 |card| card.id,
467 |card| card,
468 );
469
470 assert_eq!(
471 target,
472 vec![Card {
473 id: 7,
474 title: "seven"
475 }]
476 );
477 }
478}