makepad_widgets/flat_list.rs
1
2use crate::{
3    widget::*,
4    makepad_derive_widget::*,
5    makepad_draw::*,
6    scroll_bars::{ScrollBars}
7};
8
9live_design!{
10    FlatListBase = {{FlatList}} {}
11}
12/*
13#[derive(Clone,Copy)]
14struct ScrollSample{
15    abs: f64,
16    time: f64,
17}*/
18/*
19enum ScrollState {
20    Stopped,
21    Drag{samples:Vec<ScrollSample>},
22    Flick {delta: f64, next_frame: NextFrame},
23    Pulldown {next_frame: NextFrame},
24}
25*/
26#[derive(Clone, WidgetAction)]
27pub enum FlatListAction {
28    Scroll,
29    None
30}
31
32#[derive(Live)]
33pub struct FlatList {
34    //#[rust] area: Area,
35    #[walk] walk: Walk,
36    #[layout] layout: Layout,
37
38    #[live(0.2)] flick_scroll_minimum: f64,
39    #[live(80.0)] flick_scroll_maximum: f64,
40    #[live(0.005)] flick_scroll_scaling: f64,
41    #[live(0.98)] flick_scroll_decay: f64,
42    #[live(0.2)] swipe_drag_duration: f64,
43    #[live(100.0)] max_pull_down: f64,
44    #[live(true)] align_top_when_empty: bool,
45    #[live(false)] grab_key_focus: bool,
46    #[live(true)] drag_scrolling: bool,
47    
48    #[rust(Vec2Index::X)] vec_index: Vec2Index,
49    #[live] scroll_bars: ScrollBars,
50    #[live] capture_overload: bool,
51    #[rust] draw_state: DrawStateWrap<()>,
52    
53    #[rust] templates: ComponentMap<LiveId, LivePtr>,
54    #[rust] items: ComponentMap<LiveId, (LiveId,WidgetRef)>,
55    //#[rust(DragState::None)] drag_state: DragState,
56    /*#[rust(ScrollState::Stopped)] scroll_state: ScrollState*/
57}
58
59
60impl LiveHook for FlatList {
61    fn before_live_design(cx: &mut Cx) {
62        register_widget!(cx, FlatList)
63    }
64    
65    fn before_apply(&mut self, _cx: &mut Cx, from: ApplyFrom, _index: usize, _nodes: &[LiveNode]) {
66        if let ApplyFrom::UpdateFromDoc {..} = from {
67            self.templates.clear();
68        }
69    }
70    
71    // hook the apply flow to collect our templates and apply to instanced childnodes
72    fn apply_value_instance(&mut self, cx: &mut Cx, from: ApplyFrom, index: usize, nodes: &[LiveNode]) -> usize {
73        let id = nodes[index].id;
74        match from {
75            ApplyFrom::NewFromDoc {file_id} | ApplyFrom::UpdateFromDoc {file_id} => {
76                if nodes[index].origin.has_prop_type(LivePropType::Instance) {
77                    let live_ptr = cx.live_registry.borrow().file_id_index_to_live_ptr(file_id, index);
78                    self.templates.insert(id, live_ptr);
79                    // lets apply this thing over all our childnodes with that template
80                    for (templ_id, node) in self.items.values_mut() {
81                        if *templ_id == id {
82                            node.apply(cx, from, index, nodes);
83                        }
84                    }
85                }
86                else {
87                    cx.apply_error_no_matching_field(live_error_origin!(), index, nodes);
88                }
89            }
90            _ => ()
91        }
92        nodes.skip_node(index)
93    }
94    
95    fn after_apply(&mut self, _cx: &mut Cx, _from: ApplyFrom, _index: usize, _nodes: &[LiveNode]) {
96        if let Flow::Down = self.layout.flow {
97            self.vec_index = Vec2Index::Y
98        }
99        else {
100            self.vec_index = Vec2Index::X
101        }
102    }
103}
104
105impl FlatList {
106    
107    fn begin(&mut self, cx: &mut Cx2d, walk: Walk) {
108        self.scroll_bars.begin(cx, walk, self.layout);
109    }
110    
111    fn end(&mut self, cx: &mut Cx2d) {
112        self.scroll_bars.end(cx);
113    }
114
115    pub fn space_left(&self, cx:&mut Cx2d)->f64{
116        let view_total = cx.turtle().used();
117        let rect_now = cx.turtle().rect();
118        rect_now.size.y - view_total.y
119    }
120
121    pub fn item(&mut self, cx: &mut Cx, id: LiveId, template: LiveId) -> Option<WidgetRef> {
122        if let Some(ptr) = self.templates.get(&template) {
123            let (_, entry) = self.items.get_or_insert(cx, id, | cx | {
124                (template, WidgetRef::new_from_ptr(cx, Some(*ptr)))
125            });
126            return Some(entry.clone())
127        }
128        None
129    }
130
131    /*
132    fn delta_top_scroll(&mut self, cx: &mut Cx, delta: f64, clip_top: bool) {
133        self.first_scroll += delta;
134        if self.first_scroll > 0.0 && clip_top{
135            self.first_scroll = 0.0;
136        }
137        self.scroll_bar.set_scroll_pos_no_action(cx, self.first_scroll);
138    }*/
139}
140
141
142impl Widget for FlatList {
143    fn redraw(&mut self, cx: &mut Cx) {
144        self.scroll_bars.redraw(cx);
145    }
146    
147    fn handle_widget_event_with(&mut self, cx: &mut Cx, event: &Event, dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem)) {
148        let uid = self.widget_uid();
149        self.scroll_bars.handle_event_with(cx, event, &mut | _, _ | {});
150        /*
151        let mut scroll_to = None;
152        self.scroll_bars.handle_event_with(cx, event, &mut | _cx, action | {
153            // snap the scrollbar to a top-index with scroll_pos 0
154            if let ScrollBarAction::Scroll {scroll_pos, view_total, view_visible} = action {
155                scroll_to = Some((scroll_pos, scroll_pos+0.5 >= view_total - view_visible))
156            }
157        });
158*/
159        for (_,item) in self.items.values_mut() {
160            let item_uid = item.widget_uid();
161            item.handle_widget_event_with(cx, event, &mut | cx, action | {
162                dispatch_action(cx, action.with_container(uid).with_item(item_uid))
163            });
164        }
165        /*
166        match &mut self.scroll_state {
167            ScrollState::Flick {delta, next_frame} => {
168                if let Some(_) = next_frame.is_event(event) {
169                    *delta = *delta * self.flick_scroll_decay;
170                    if delta.abs()>self.flick_scroll_minimum {
171                        *next_frame = cx.new_next_frame();
172                        let delta = *delta;
173                        self.delta_top_scroll(cx, delta, true);
174                        dispatch_action(cx, FlatListAction::Scroll.into_action(uid));
175                        self.scroll_bars.redraw(cx);
176                    } else {
177                        self.scroll_state = ScrollState::Stopped;
178                    }
179                }
180            }
181            ScrollState::Pulldown {next_frame} => {
182                if let Some(_) = next_frame.is_event(event) {
183                    // we have to bounce back
184                    if self.first_scroll > 0.0 {
185                        self.first_scroll *= 0.9;
186                        if self.first_scroll < 1.0 {
187                            self.first_scroll = 0.0;
188                        }
189                        else {
190                            *next_frame = cx.new_next_frame();
191                            dispatch_action(cx, FlatListAction::Scroll.into_action(uid));
192                        }
193                        self.scroll_bars.redraw(cx);
194                    }
195                    else {
196                        self.scroll_state = ScrollState::Stopped
197                    }
198                }
199            }
200            _=>()
201        }*/
202        /*
203        let vi = self.vec_index;
204        let is_scroll = if let Event::Scroll(_) = event {true} else {false};
205        if self.scroll_bars.is_area_captured(cx){
206            self.scroll_state = ScrollState::Stopped;
207        }*/
208        /*
209        if !self.scroll_bars.is_area_captured(cx) || is_scroll{ 
210            match event.hits_with_capture_overload(cx, self.area, self.capture_overload) {
211                Hit::FingerScroll(e) => {
212                    self.scroll_state = ScrollState::Stopped;
213                    self.delta_top_scroll(cx, -e.scroll.index(vi), true);
214                    dispatch_action(cx, FlatListAction::Scroll.into_action(uid));
215                    self.area.redraw(cx);
216                },
217                
218                Hit::FingerDown(e) => {
219                    if self.grab_key_focus {
220                        cx.set_key_focus(self.area);
221                    }
222                    if self.drag_scrolling{
223                        self.scroll_state = ScrollState::Drag {
224                            samples: vec![ScrollSample{abs:e.abs.index(vi),time:e.time}]
225                        };
226                    }
227                }
228                Hit::FingerMove(e) => {
229                    //log!("Finger move {} {}", e.time, e.abs);
230                    cx.set_cursor(MouseCursor::Default);
231                    match &mut self.scroll_state {
232                        ScrollState::Drag {samples}=>{
233                            let new_abs = e.abs.index(vi);
234                            let old_sample = *samples.last().unwrap();
235                            samples.push(ScrollSample{abs:new_abs, time:e.time});
236                            if samples.len()>4{
237                                samples.remove(0);
238                            }
239                            self.delta_top_scroll(cx, new_abs - old_sample.abs, false);
240                            self.area.redraw(cx);
241                        }
242                        _=>()
243                    }
244                }
245                Hit::FingerUp(_e) => {
246                    //log!("Finger up {} {}", e.time, e.abs);
247                    match &mut self.scroll_state {
248                        ScrollState::Drag {samples}=>{
249                            // alright so we need to see if in the last couple of samples
250                            // we have a certain distance per time
251                            let mut last = None;
252                            let mut scaled_delta = 0.0;
253                            for sample in samples.iter().rev(){
254                                if last.is_none(){
255                                    last = Some(sample);
256                                }
257                                else{
258                                    scaled_delta += (last.unwrap().abs - sample.abs)/ (last.unwrap().time - sample.time)
259                                }
260                            }
261                            scaled_delta *= self.flick_scroll_scaling;
262                            if  self.first_scroll > 0.0 {
263                                self.scroll_state = ScrollState::Pulldown {next_frame: cx.new_next_frame()};
264                            }
265                            else if scaled_delta.abs() > self.flick_scroll_minimum{
266                                
267                                self.scroll_state = ScrollState::Flick {
268                                    delta: scaled_delta.min(self.flick_scroll_maximum).max(-self.flick_scroll_maximum),
269                                    next_frame: cx.new_next_frame()
270                                };
271                            }
272                            else{
273                                self.scroll_state = ScrollState::Stopped;
274                            }
275                        }
276                        _=>()
277                    }
278                    // ok so. lets check our gap from 'drag'
279                    // here we kinda have to take our last delta and animate it
280                }
281                Hit::KeyFocus(_) => {
282                }
283                Hit::KeyFocusLost(_) => {
284                }
285                _ => ()
286            }
287        }*/
288    }
289    
290    fn walk(&mut self, _cx:&mut Cx) -> Walk {self.walk}
291    
292    fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
293        if self.draw_state.begin(cx, ()) {
294            self.begin(cx, walk);
295            return WidgetDraw::hook_above()
296        }
297        self.end(cx);
298        self.draw_state.end();
299        WidgetDraw::done()
300    }
301}
302
303#[derive(Clone, Default, PartialEq, WidgetRef)]
304pub struct FlatListRef(WidgetRef);
305
306impl FlatListRef {
307   
308    pub fn item(&self, cx: &mut Cx, entry_id: LiveId, template: LiveId) -> Option<WidgetRef> {
309        if let Some(mut inner) = self.borrow_mut() {
310            inner.item(cx, entry_id, template)
311        }
312        else {
313            None
314        }
315    }
316    
317    pub fn items_with_actions(&self, actions: &WidgetActions) -> Vec<(LiveId, WidgetRef)> {
318        let mut set = Vec::new();
319        self.items_with_actions_vec(actions, &mut set);
320        set
321    }
322    
323    fn items_with_actions_vec(&self, actions: &WidgetActions, set: &mut Vec<(LiveId, WidgetRef)>) {
324        let uid = self.widget_uid();
325        for action in actions {
326            if action.container_uid == uid {
327                
328                if let Some(inner) = self.borrow() {
329                    for (item_id, (_,item)) in inner.items.iter() {
330                        
331                        if item.widget_uid() == action.item_uid {
332                            set.push((*item_id, item.clone()))
333                        }
334                    }
335                }
336            }
337        }
338    }
339}
340
341#[derive(Clone, Default, WidgetSet)]
342pub struct FlatListSet(WidgetSet);
343
344impl FlatListSet {
345    pub fn items_with_actions(&self, actions: &WidgetActions) -> Vec<(LiveId, WidgetRef)> {
346        let mut set = Vec::new();
347        for list in self.iter() {
348            list.items_with_actions_vec(actions, &mut set)
349        }
350        set
351    }
352}