summaryrefslogtreecommitdiff
path: root/iced_drop/src/widget/operation/drop.rs
blob: 12a2e305ac7f1c0625e71fc447d0f8bd91c33c46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use iced::{
    advanced::widget::{
        operation::{Outcome, Scrollable},
        Id, Operation,
    },
    Rectangle, Vector,
};

/// Produces an [`Operation`] that will find the drop zones that pass a filter on the zone's bounds.
/// For any drop zone to be considered, the Element must have some Id.
/// If `options` is `None`, all drop zones will be considered.
/// Depth determines how how deep into nested drop zones to go.
/// If 'depth' is `None`, nested dropzones will be fully explored
pub fn find_zones<F>(
    filter: F,
    options: Option<Vec<Id>>,
    depth: Option<usize>,
) -> impl Operation<Vec<(Id, Rectangle)>>
where
    F: Fn(&Rectangle) -> bool + 'static,
{
    struct FindDropZone<F> {
        filter: F,
        options: Option<Vec<Id>>,
        zones: Vec<(Id, Rectangle)>,
        max_depth: Option<usize>,
        c_depth: usize,
        offset: Vector,
    }

    impl<F> Operation<Vec<(Id, Rectangle)>> for FindDropZone<F>
    where
        F: Fn(&Rectangle) -> bool + 'static,
    {
        fn container(
            &mut self,
            id: Option<&Id>,
            bounds: iced::Rectangle,
            operate_on_children: &mut dyn FnMut(&mut dyn Operation<Vec<(Id, Rectangle)>>),
        ) {
            match id {
                Some(id) => {
                    let is_option = match &self.options {
                        Some(options) => options.contains(id),
                        None => true,
                    };
                    let bounds = bounds - self.offset;
                    if is_option && (self.filter)(&bounds) {
                        self.c_depth += 1;
                        self.zones.push((id.clone(), bounds));
                    }
                }
                None => (),
            }
            let goto_next = match &self.max_depth {
                Some(m_depth) => self.c_depth < *m_depth,
                None => true,
            };
            if goto_next {
                operate_on_children(self);
            }
        }

        fn finish(&self) -> Outcome<Vec<(Id, Rectangle)>> {
            Outcome::Some(self.zones.clone())
        }

        fn scrollable(
            &mut self,
            _state: &mut dyn Scrollable,
            _id: Option<&Id>,
            bounds: Rectangle,
            translation: Vector,
        ) {
            if (self.filter)(&bounds) {
                self.offset = self.offset + translation;
            }
        }
    }

    FindDropZone {
        filter,
        options,
        zones: vec![],
        max_depth: depth,
        c_depth: 0,
        offset: Vector { x: 0.0, y: 0.0 },
    }
}