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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use std::fmt;
use std::hash::Hash;
use std::marker::PhantomData;

use educe::Educe;
use indexmap::IndexMap;

use crate::reflow::{Lotus, Revisable};
use crate::{Scope, ViewId, Widget};

#[derive(Educe)]
#[educe(Debug)]
pub struct Each<Value, ITter, KeyFn, Key, TmplFn, Tmpl>
where
    Value: fmt::Debug + 'static,
    ITter: AsRef<[Value]> + fmt::Debug + 'static,
    KeyFn: Fn(&Value) -> Key + 'static,
    Key: Eq + Hash + Clone + fmt::Debug + 'static,
    TmplFn: Fn(&Value) -> Tmpl + 'static,
    Tmpl: Widget + 'static,
{
    items: Lotus<ITter>,
    #[educe(Debug(ignore))]
    key_fn: KeyFn,
    #[educe(Debug(ignore))]
    tmpl_fn: TmplFn,
    key_view_ids: IndexMap<Key, ViewId>,
    #[educe(Debug(ignore))]
    _pd: PhantomData<(Value, ITter)>,
}

impl<Value, ITter, KeyFn, Key, TmplFn, Tmpl> Each<Value, ITter, KeyFn, Key, TmplFn, Tmpl>
where
    Value: fmt::Debug + 'static,
    ITter: AsRef<[Value]> + fmt::Debug + 'static,
    KeyFn: Fn(&Value) -> Key + 'static,
    Key: Eq + Hash + Clone + fmt::Debug + 'static,
    TmplFn: Fn(&Value) -> Tmpl + 'static,
    Tmpl: Widget + 'static,
{
    pub fn new(items: impl Into<Lotus<ITter>>, key_fn: KeyFn, tmpl_fn: TmplFn) -> Self {
        Self {
            items: items.into(),
            key_fn,
            tmpl_fn,
            key_view_ids: IndexMap::new(),
            _pd: PhantomData,
        }
    }
}
impl<Value, KeyFn, Key, TmplFn, Tmpl> Each<Value, Vec<Value>, KeyFn, Key, TmplFn, Tmpl>
where
    Value: fmt::Debug + 'static,
    KeyFn: Fn(&Value) -> Key + 'static,
    Key: Eq + Hash + Clone + fmt::Debug + 'static,
    TmplFn: Fn(&Value) -> Tmpl + 'static,
    Tmpl: Widget + 'static,
{
    pub fn from_vec(items: impl Into<Lotus<Vec<Value>>>, key_fn: KeyFn, tmpl_fn: TmplFn) -> Self {
        Self {
            items: items.into(),
            key_fn,
            tmpl_fn,
            key_view_ids: IndexMap::new(),
            _pd: PhantomData,
        }
    }
}

impl<Value, ITter, KeyFn, Key, TmplFn, Tmpl> Widget for Each<Value, ITter, KeyFn, Key, TmplFn, Tmpl>
where
    Value: fmt::Debug + 'static,
    ITter: AsRef<[Value]> + fmt::Debug + 'static,
    KeyFn: Fn(&Value) -> Key + 'static,
    Key: Eq + Hash + Clone + fmt::Debug + 'static,
    TmplFn: Fn(&Value) -> Tmpl + 'static,
    Tmpl: Widget + 'static,
{
    fn build(&mut self, ctx: &mut Scope) {
        self.items.bind_view(ctx.view_id());
        for item in self.items.get().as_ref() {
            let key = (self.key_fn)(item);
            let view_id = (self.tmpl_fn)(item).show_in(ctx);
            self.key_view_ids.insert(key, view_id);
        }
    }

    fn patch(&mut self, ctx: &mut Scope) {
        let mut key_view_ids = std::mem::take(&mut self.key_view_ids);

        let mut operations = Vec::with_capacity(key_view_ids.len());
        for (index, item) in self.items.get().as_ref().iter().enumerate() {
            let key = (self.key_fn)(item);
            if let Some(view_id) = key_view_ids.remove(&key) {
                self.key_view_ids.insert(key, view_id.clone());
                operations.push(ViewOperation::Reuse(index, view_id));
            } else {
                let view_id = (self.tmpl_fn)(item).store_in(ctx);
                self.key_view_ids.insert(key, view_id.clone());
                operations.push(ViewOperation::Insert(index, view_id.clone()));
            }
        }
        crate::warn!("key_view_ids: {:?}", key_view_ids);
        if !key_view_ids.is_empty() {
            cfg_if! {
                if #[cfg(feature = "__single_holder")] {
                    crate::reflow::batch(|| {
                        for view_id in key_view_ids.values() {
                            ctx.detach_child(view_id);
                        }
                    });
                } else {
                    crate::reflow::batch(ctx.holder_id(), || {
                        for view_id in key_view_ids.values() {
                            ctx.detach_child(view_id);
                        }
                    });
                }
            }
        }
        operations.reverse();

        // crate::warn!("===========operations: {:#?}", operations);

        while let Some(operation) = operations.pop() {
            match operation {
                ViewOperation::Reuse(index, view_id) => {
                    let cur_index = ctx.child_views.get_index_of(&view_id).unwrap();
                    if cur_index != index {
                        ctx.child_views.move_index(cur_index, index);
                        let view = ctx.child_views.get_mut(&view_id).unwrap();
                        view.scope.is_attached = false;
                        ctx.attach_child(&view_id);
                    }
                }
                ViewOperation::Insert(index, view_id) => {
                    ctx.child_views.move_index(ctx.child_views.len() - 1, index);
                    ctx.attach_child(&view_id);
                }
            }
        }
    }
}

#[derive(Debug)]
enum ViewOperation {
    Reuse(usize, ViewId),
    Insert(usize, ViewId),
}