1
2use crate::{
3    widget::*,
4    makepad_derive_widget::*,
5    makepad_draw::*,
6    scroll_bar::{ScrollBar, ScrollBarAction}
7};
8
9live_design!{
10    PortalListBase = {{PortalList}} {}
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)]
27enum ListDrawState {
28    Begin,
29    Down {index: u64, pos: f64, viewport: Rect},
30    Up {index: u64, pos: f64, hit_bottom: bool, viewport: Rect},
31    DownAgain {index: u64, pos: f64, viewport: Rect},
32    End {viewport: Rect}
33}
34
35
36#[derive(Clone, WidgetAction)]
37pub enum PortalListAction {
38    Scroll,
39    None
40}
41impl ListDrawState {
42    fn is_down_again(&self) -> bool {
43        match self {
44            Self::DownAgain {..} => true,
45            _ => false
46        }
47    }
48}
49#[derive(Live)]
50pub struct PortalList {
51    #[rust] area: Area,
52    #[walk] walk: Walk,
53    #[layout] layout: Layout,
54    
55    #[rust] range_start: u64,
56    #[rust(u64::MAX)] range_end: u64,
57    #[rust(0u64)] view_window: u64,
58    #[live(0.2)] flick_scroll_minimum: f64,
59    #[live(80.0)] flick_scroll_maximum: f64,
60    #[live(0.005)] flick_scroll_scaling: f64,
61    #[live(0.98)] flick_scroll_decay: f64,
62    #[live(0.2)] swipe_drag_duration: f64,
63    #[live(100.0)] max_pull_down: f64,
64    #[live(true)] align_top_when_empty: bool,
65    #[live(false)] grab_key_focus: bool,
66    #[live(true)] drag_scrolling: bool,
67    #[live(false)] allow_empty: bool,
68    #[rust] first_id: u64,
69    #[rust] first_scroll: f64,
70    #[rust(Vec2Index::X)] vec_index: Vec2Index,
71    #[live] scroll_bar: ScrollBar,
72    #[live] capture_overload: bool,
73    #[live(false)] keep_invisible: bool,
74    #[rust] draw_state: DrawStateWrap<ListDrawState>,
75    #[rust] draw_align_list: Vec<AlignItem>,
76    #[rust] detect_tail_in_draw: bool,
77    #[live(false)] auto_tail: bool,
78    #[rust(false)] tail_range: bool,
79    
80    #[rust] templates: ComponentMap<LiveId, LivePtr>,
81    #[rust] items: ComponentMap<(u64, LiveId), WidgetRef>,
82    #[rust(ScrollState::Stopped)] scroll_state: ScrollState
84}
85
86struct AlignItem {
87    align_range: TurtleAlignRange,
88    size: DVec2,
89    shift: f64,
90    index: u64
91}
92
93impl LiveHook for PortalList {
94    fn before_live_design(cx: &mut Cx) {
95        register_widget!(cx, PortalList)
96    }
97    
98    fn before_apply(&mut self, _cx: &mut Cx, from: ApplyFrom, _index: usize, _nodes: &[LiveNode]) {
99        if let ApplyFrom::UpdateFromDoc {..} = from {
100            self.templates.clear();
101        }
102    }
103    
104    fn apply_value_instance(&mut self, cx: &mut Cx, from: ApplyFrom, index: usize, nodes: &[LiveNode]) -> usize {
106        let id = nodes[index].id;
107        match from {
108            ApplyFrom::NewFromDoc {file_id} | ApplyFrom::UpdateFromDoc {file_id} => {
109                if nodes[index].origin.has_prop_type(LivePropType::Instance) {
110                    let live_ptr = cx.live_registry.borrow().file_id_index_to_live_ptr(file_id, index);
111                    self.templates.insert(id, live_ptr);
112                    for ((_, templ_id), node) in self.items.iter_mut() {
114                        if *templ_id == id {
115                            node.apply(cx, from, index, nodes);
116                        }
117                    }
118                }
119                else {
120                    cx.apply_error_no_matching_field(live_error_origin!(), index, nodes);
121                }
122            }
123            _ => ()
124        }
125        nodes.skip_node(index)
126    }
127    
128    fn after_apply(&mut self, _cx: &mut Cx, _from: ApplyFrom, _index: usize, _nodes: &[LiveNode]) {
129        if let Flow::Down = self.layout.flow {
130            self.vec_index = Vec2Index::Y
131        }
132        else {
133            self.vec_index = Vec2Index::X
134        }
135        if self.auto_tail{
136            self.tail_range = true;
137        }
138    }
139}
140
141impl PortalList {
142    
143    fn begin(&mut self, cx: &mut Cx2d, walk: Walk) {
144        cx.begin_turtle(walk, self.layout);
145        self.draw_align_list.clear();
146    }
147    
148    fn end(&mut self, cx: &mut Cx2d) {
149        
150        let vi = self.vec_index;
153        let mut at_end = false;
154        let mut visible_items = 0;
155
156        if let Some(ListDrawState::End {viewport}) = self.draw_state.get() {
157            let list = &mut self.draw_align_list;
158            if list.len()>0 {
159                list.sort_by( | a, b | a.index.cmp(&b.index));
160                let first_index = list.iter().position( | v | v.index == self.first_id).unwrap();
161                
162                let mut first_pos = self.first_scroll;
165                for i in (0..first_index).rev() {
166                    let item = &list[i];
167                    first_pos -= item.size.index(vi);
168                }
169                
170                let mut last_pos = self.first_scroll;
175                let mut last_item_pos = None;
176                for i in first_index..list.len() {
177                    let item = &list[i];
178                    last_pos += item.size.index(vi);
179                    if item.index < self.range_end {
180                        last_item_pos = Some(last_pos);
181                    }
182                    else {
183                        break;
184                    }
185                }
186                
187                let mut not_filling_viewport = false;
190                if list[0].index == self.range_start {
191                    let mut total = 0.0;
192                    for item in list.iter() {
193                        if item.index >= self.range_end {
194                            break;
195                        }
196                        total += item.size.index(vi);
197                    }
198                    not_filling_viewport = total < viewport.size.index(vi);
199                }
200                
201                if list.first().unwrap().index == self.range_start && first_pos > 0.0 {
203                    let min = if let ScrollState::Stopped = self.scroll_state {
204                        0.0
205                    }
206                    else {
207                        self.max_pull_down
208                    };
209                    
210                    let mut pos = first_pos.min(min); for item in list {
212                        let shift = DVec2::from_index_pair(vi, pos, 0.0);
213                        cx.shift_align_range(&item.align_range, shift - DVec2::from_index_pair(vi, item.shift, 0.0));
214                        pos += item.size.index(vi);
215                        visible_items += 1;
216                    }
217                    self.first_scroll = first_pos.min(min);
218                    self.first_id = self.range_start;
219                }
220                else {
221                    let shift = if let Some(last_item_pos) = last_item_pos {
224                        if self.align_top_when_empty && not_filling_viewport {
225                            -first_pos
226                        }
227                        else {
228                            let ret = (viewport.size.index(vi) - last_item_pos).max(0.0);
229                            if ret > 0.0 {
230                                at_end = true;
231                            }
232                            ret
233                        }
234                    }
235                    else {
236                        0.0
237                    };
238                    let mut first_id_changed = false;
240                    let start_pos = self.first_scroll + shift;
241                    let mut pos = start_pos;
242                    for i in (0..first_index).rev() {
243                        let item = &list[i];
244                        let visible = pos > 0.0;
245                        pos -= item.size.index(vi);
246                        let shift = DVec2::from_index_pair(vi, pos, 0.0);
247                        cx.shift_align_range(&item.align_range, shift - DVec2::from_index_pair(vi, item.shift, 0.0));
248                        if visible { self.first_scroll = pos;
250                            self.first_id = item.index;
251                            first_id_changed = true;
252                            if item.index < self.range_end {
253                                visible_items += 1;
254                            }
255                        }
256                    }
257                    let mut pos = start_pos;
259                    for i in first_index..list.len() {
260                        let item = &list[i];
261                        let shift = DVec2::from_index_pair(vi, pos, 0.0);
262                        cx.shift_align_range(&item.align_range, shift - DVec2::from_index_pair(vi, item.shift, 0.0));
263                        pos += item.size.index(vi);
264                        let invisible = pos < 0.0;
265                        if invisible { self.first_scroll = pos - item.size.index(vi);
267                            self.first_id = item.index;
268                            first_id_changed = true;
269                        }
270                        else if item.index < self.range_end {
271                            visible_items += 1;
272                        }
273                    }
274                    if !first_id_changed {
276                        self.first_scroll = start_pos;
277                    }
278                }
279                if !self.scroll_bar.animator_in_state(cx, id!(hover.pressed)){
280                    self.update_scroll_bar(cx);
281                }
282            }
283        }
284        else {
285            log!("Draw state not at end in listview, please review your next_visible_item loop")
286        }
287        let rect = cx.turtle().rect();
288        if at_end || self.view_window == 0 || self.view_window > visible_items{
289            self.view_window = visible_items.max(4) - 3;
290        }
291        if self.detect_tail_in_draw{
292            self.detect_tail_in_draw = false;
293            if self.auto_tail && at_end{
294                self.tail_range = true;
295            }
296        }
297        let total_views = (self.range_end - self.range_start) as f64 / self.view_window as f64;
298        self.scroll_bar.draw_scroll_bar(cx, Axis::Vertical, rect, dvec2(100.0, rect.size.y * total_views));
299        if !self.keep_invisible{
300            self.items.retain_visible();
301        }
302        cx.end_turtle_with_area(&mut self.area);
303    }
304    
305    pub fn next_visible_item(&mut self, cx: &mut Cx2d) -> Option<u64> {
306        let vi = self.vec_index;
307        if let Some(draw_state) = self.draw_state.get() {
308            match draw_state {
309                ListDrawState::Begin => {
310                    let viewport = cx.turtle().padded_rect();
311                    self.draw_state.set(ListDrawState::Down {
312                        index: self.first_id,
313                        pos: self.first_scroll,
314                        viewport,
315                    });
316                    
317                    cx.begin_turtle(Walk {
318                        abs_pos: Some(dvec2(viewport.pos.x, viewport.pos.y + self.first_scroll)),
319                        margin: Default::default(),
320                        width: Size::Fill,
321                        height: Size::Fit
322                    }, Layout::flow_down());
323                    return Some(self.first_id)
324                }
325                ListDrawState::Down {index, pos, viewport} | ListDrawState::DownAgain {index, pos, viewport} => {
326                    let is_down_again = draw_state.is_down_again();
327                    let did_draw = cx.turtle_has_align_items() || self.allow_empty;
328                    let align_range = cx.get_turtle_align_range();
329                    let rect = cx.end_turtle();
330                    self.draw_align_list.push(AlignItem {
331                        align_range,
332                        shift: pos, 
333                        size: rect.size,
334                        index
335                    });
336                    
337                    if !did_draw || pos + rect.size.index(vi) > viewport.size.index(vi) {
338                        if self.first_id>0 && !is_down_again {
340                            self.draw_state.set(ListDrawState::Up {
341                                index: self.first_id - 1,
342                                pos: self.first_scroll,
343                                hit_bottom: index >= self.range_end,
344                                viewport
345                            });
346                            cx.begin_turtle(Walk {
347                                abs_pos: Some(dvec2(viewport.pos.x, viewport.pos.y)),
348                                margin: Default::default(),
349                                width: Size::Fill,
350                                height: Size::Fit
351                            }, Layout::flow_down());
352                            return Some(self.first_id - 1);
353                        }
354                        else {
355                            self.draw_state.set(ListDrawState::End {viewport});
356                            return None
357                        }
358                    }
359                    if is_down_again {
360                        self.draw_state.set(ListDrawState::DownAgain {
361                            index: index + 1,
362                            pos: pos + rect.size.index(vi),
363                            viewport
364                        });
365                    }
366                    else {
367                        self.draw_state.set(ListDrawState::Down {
368                            index: index + 1,
369                            pos: pos + rect.size.index(vi),
370                            viewport
371                        });
372                    }
373                    cx.begin_turtle(Walk {
374                        abs_pos: Some(dvec2(viewport.pos.x, viewport.pos.y + pos + rect.size.index(vi))),
375                        margin: Default::default(),
376                        width: Size::Fill,
377                        height: Size::Fit
378                    }, Layout::flow_down());
379                    return Some(index + 1)
380                }
381                ListDrawState::Up {index, pos, hit_bottom, viewport} => {
382                    let did_draw = cx.turtle_has_align_items() || self.allow_empty;
383                    let align_range = cx.get_turtle_align_range();
384                    let rect = cx.end_turtle();
385                    self.draw_align_list.push(AlignItem {
386                        align_range,
387                        size: rect.size,
388                        shift: 0.0,
389                        index
390                    });
391                    if index == self.range_start {
392                        if pos - rect.size.index(vi) > 0.0 {
396                            if let Some(last_index) = self.draw_align_list.iter().map( | v | v.index).max() {
398                                let total_height: f64 = self.draw_align_list.iter().map( | v | v.size.index(vi)).sum();
400                                self.draw_state.set(ListDrawState::DownAgain {
401                                    index: last_index + 1,
402                                    pos: total_height,
403                                    viewport
404                                });
405                                cx.begin_turtle(Walk {
406                                    abs_pos: Some(dvec2(viewport.pos.x, viewport.pos.y)),
407                                    margin: Default::default(),
408                                    width: Size::Fill,
409                                    height: Size::Fit
410                                }, Layout::flow_down());
411                                return Some(last_index + 1);
412                            }
413                        }
414                        self.draw_state.set(ListDrawState::End {viewport});
415                        return None
416                    }
417                    
418                    if !did_draw || pos < if hit_bottom {-viewport.size.index(vi)} else {0.0} {
419                        self.draw_state.set(ListDrawState::End {viewport});
420                        return None
421                    }
422                    
423                    self.draw_state.set(ListDrawState::Up {
424                        index: index - 1,
425                        hit_bottom,
426                        pos: pos - rect.size.index(vi),
427                        viewport
428                    });
429                    
430                    cx.begin_turtle(Walk {
431                        abs_pos: Some(dvec2(viewport.pos.x, viewport.pos.y)),
432                        margin: Default::default(),
433                        width: Size::Fill,
434                        height: Size::Fit
435                    }, Layout::flow_down());
436                    
437                    return Some(index - 1);
438                }
439                _ => ()
440            }
441        }
442        None
443    }
444    
445    pub fn item(&mut self, cx: &mut Cx, entry_id: u64, template: LiveId) -> Option<WidgetRef> {
446        if let Some(ptr) = self.templates.get(&template) {
447            let entry = self.items.get_or_insert(cx, (entry_id, template), | cx | {
448                WidgetRef::new_from_ptr(cx, Some(*ptr))
449            });
450            return Some(entry.clone())
451        }
452        None
453    }
454    
455    pub fn set_item_range(&mut self, cx: &mut Cx, range_start: u64, range_end: u64) {
456        self.range_start = range_start;
457        if self.range_end != range_end {
458            self.range_end = range_end;
459            if self.tail_range{
460                self.first_id = self.range_end.max(1) - 1;
461                self.first_scroll = 0.0;
462            }
463            self.update_scroll_bar(cx);
464        }
465    }
466    
467    pub fn update_scroll_bar(&mut self, cx: &mut Cx) {
468        let scroll_pos = ((self.first_id - self.range_start) as f64 / ((self.range_end - self.range_start).max(self.view_window + 1) - self.view_window) as f64) * self.scroll_bar.get_scroll_view_total();
469        self.scroll_bar.set_scroll_pos_no_action(cx, scroll_pos);
471    }
472    
473    fn delta_top_scroll(&mut self, cx: &mut Cx, delta: f64, clip_top: bool) {
474        self.first_scroll += delta;
475        if self.first_id == self.range_start {
476            self.first_scroll = self.first_scroll.min(self.max_pull_down);
477        }
478        if self.first_id == self.range_start && self.first_scroll > 0.0 && clip_top {
479            self.first_scroll = 0.0;
480        }
481        self.update_scroll_bar(cx);
482    }
483}
484
485
486impl Widget for PortalList {
487    fn redraw(&mut self, cx: &mut Cx) {
488        self.area.redraw(cx);
489    }
490    
491    fn handle_widget_event_with(&mut self, cx: &mut Cx, event: &Event, dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem)) {
492        let uid = self.widget_uid();
493        
494        let mut scroll_to = None;
495        self.scroll_bar.handle_event_with(cx, event, &mut | _cx, action | {
496            if let ScrollBarAction::Scroll {scroll_pos, view_total, view_visible} = action {
498                scroll_to = Some((scroll_pos, scroll_pos+0.5 >= view_total - view_visible))
499            }
500        });
501        if let Some((scroll_to, at_end)) = scroll_to {
502            if at_end && self.auto_tail{
503                self.first_id = self.range_end.max(1) - 1;
504                self.first_scroll = 0.0;
505                self.tail_range = true;
506            }
507            else if self.tail_range {
508                self.tail_range = false;
509            }
510
511            let scroll_to = ((scroll_to / self.scroll_bar.get_scroll_view_visible()) * self.view_window as f64) as u64;
512            self.first_id = scroll_to;
513            self.first_scroll = 0.0;
514            dispatch_action(cx, WidgetActionItem::new(PortalListAction::Scroll.into(), uid));
515            self.area.redraw(cx);
516        }
517        
518        for item in self.items.values_mut() {
519            let item_uid = item.widget_uid();
520            item.handle_widget_event_with(cx, event, &mut | cx, action | {
521                dispatch_action(cx, action.with_container(uid).with_item(item_uid))
522            });
523        }
524        
525        match &mut self.scroll_state {
526            ScrollState::Flick {delta, next_frame} => {
527                if let Some(_) = next_frame.is_event(event) {
528                    *delta = *delta * self.flick_scroll_decay;
529                    if delta.abs()>self.flick_scroll_minimum {
530                        *next_frame = cx.new_next_frame();
531                        let delta = *delta;
532                        self.delta_top_scroll(cx, delta, true);
533                        dispatch_action(cx, PortalListAction::Scroll.into_action(uid));
534                        self.area.redraw(cx);
535                    } else {
536                        self.scroll_state = ScrollState::Stopped;
537                    }
538                }
539            }
540            ScrollState::Pulldown {next_frame} => {
541                if let Some(_) = next_frame.is_event(event) {
542                    if self.first_id == self.range_start && self.first_scroll > 0.0 {
544                        self.first_scroll *= 0.9;
545                        if self.first_scroll < 1.0 {
546                            self.first_scroll = 0.0;
547                        }
548                        else {
549                            *next_frame = cx.new_next_frame();
550                            dispatch_action(cx, PortalListAction::Scroll.into_action(uid));
551                        }
552                        self.area.redraw(cx);
553                    }
554                    else {
555                        self.scroll_state = ScrollState::Stopped
556                    }
557                }
558            }
559            _=>()
560        }
561        let vi = self.vec_index;
562        let is_scroll = if let Event::Scroll(_) = event {true} else {false};
563        if self.scroll_bar.is_area_captured(cx){
564            self.scroll_state = ScrollState::Stopped;
565        }
566        if !self.scroll_bar.is_area_captured(cx) || is_scroll{ 
567            match event.hits_with_capture_overload(cx, self.area, self.capture_overload) {
568                Hit::FingerScroll(e) => {
569                    if self.tail_range {
570                        self.tail_range = false;
571                    }
572                    self.detect_tail_in_draw = true;
573                    self.scroll_state = ScrollState::Stopped;
574                    self.delta_top_scroll(cx, -e.scroll.index(vi), true);
575                    dispatch_action(cx, PortalListAction::Scroll.into_action(uid));
576                    self.area.redraw(cx);
577                },
578                
579                Hit::KeyDown(ke) => match ke.key_code {
580                    KeyCode::Home => {
581                        self.first_id = 0;
582                        self.first_scroll = 0.0;
583                        self.tail_range = false;
584                        self.update_scroll_bar(cx);
585                        self.area.redraw(cx);
586                    },
587                    KeyCode::End => {
588                        self.first_id = self.range_end.max(1) - 1;
589                        self.first_scroll = 0.0;
590                        if self.auto_tail {
591                            self.tail_range = true;
592                        }
593                        self.update_scroll_bar(cx);
594                        self.area.redraw(cx);
595                    },
596                    KeyCode::PageUp => {
597                        self.first_id = self.first_id.max(self.view_window) - self.view_window;
598                        self.first_scroll = 0.0;
599                        self.tail_range = false;
600                        self.update_scroll_bar(cx);
601                        self.area.redraw(cx);
602                    },
603                    KeyCode::PageDown => {
604                        self.first_id += self.view_window;
605                        self.first_scroll = 0.0;
606                        if self.first_id >= self.range_end.max(1) {
607                            self.first_id = self.range_end.max(1) - 1;
608                        }
609                        self.detect_tail_in_draw = true;
610                        self.update_scroll_bar(cx);
611                        self.area.redraw(cx);
612                    },
613                    KeyCode::ArrowDown => {
614                        self.first_id += 1;
615                        if self.first_id >= self.range_end.max(1) {
616                            self.first_id = self.range_end.max(1) - 1;
617                        }
618                        self.detect_tail_in_draw = true;
619                        self.first_scroll = 0.0;
620                        self.update_scroll_bar(cx);
621                        self.area.redraw(cx);
622                    },
623                    KeyCode::ArrowUp => {
624                        if self.first_id > 0 {
625                            self.first_id -= 1;
626                            if self.first_id < self.range_start {
627                                self.first_id = self.range_start;
628                            }
629                            self.first_scroll = 0.0;
630                            self.area.redraw(cx);
631                            self.tail_range = false;
632                            self.update_scroll_bar(cx);
633                        }
634                    },
635                    _ => ()
636                }
637                Hit::FingerDown(e) => {
638                    if self.grab_key_focus {
640                        cx.set_key_focus(self.area);
641                    }
642                    if self.tail_range {
644                        self.tail_range = false;
645                    }
646                    if self.drag_scrolling{
647                        self.scroll_state = ScrollState::Drag {
648                            samples: vec![ScrollSample{abs:e.abs.index(vi),time:e.time}]
649                        };
650                    }
651                }
652                Hit::FingerMove(e) => {
653                    cx.set_cursor(MouseCursor::Default);
655                    match &mut self.scroll_state {
656                        ScrollState::Drag {samples}=>{
657                            let new_abs = e.abs.index(vi);
658                            let old_sample = *samples.last().unwrap();
659                            samples.push(ScrollSample{abs:new_abs, time:e.time});
660                            if samples.len()>4{
661                                samples.remove(0);
662                            }
663                            self.delta_top_scroll(cx, new_abs - old_sample.abs, false);
664                            self.area.redraw(cx);
665                        }
666                        _=>()
667                    }
668                }
669                Hit::FingerUp(_e) => {
670                    match &mut self.scroll_state {
672                        ScrollState::Drag {samples}=>{
673                            let mut last = None;
676                            let mut scaled_delta = 0.0;
677                            let mut total_delta = 0.0;
678                            for sample in samples.iter().rev(){
679                                if last.is_none(){
680                                    last = Some(sample);
681                                }
682                                else{
683                                    total_delta += last.unwrap().abs - sample.abs;
684                                    scaled_delta += (last.unwrap().abs - sample.abs)/ (last.unwrap().time - sample.time)
685                                }
686                            }
687                            scaled_delta *= self.flick_scroll_scaling;
688                            if self.first_id == self.range_start && self.first_scroll > 0.0 {
689                                self.scroll_state = ScrollState::Pulldown {next_frame: cx.new_next_frame()};
690                            }
691                            else if total_delta.abs() > 10.0 && scaled_delta.abs() > self.flick_scroll_minimum{
692                                
693                                self.scroll_state = ScrollState::Flick {
694                                    delta: scaled_delta.min(self.flick_scroll_maximum).max(-self.flick_scroll_maximum),
695                                    next_frame: cx.new_next_frame()
696                                };
697                            }
698                            else{
699                                self.scroll_state = ScrollState::Stopped;
700                            }
701                        }
702                        _=>()
703                    }
704                    }
707                Hit::KeyFocus(_) => {
708                }
709                Hit::KeyFocusLost(_) => {
710                }
711                _ => ()
712            }
713        }
714    }
715    
716    fn walk(&mut self, _cx:&mut Cx) -> Walk {self.walk}
717    
718    fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
719        if self.draw_state.begin(cx, ListDrawState::Begin) {
720            self.begin(cx, walk);
721            return WidgetDraw::hook_above()
722        }
723        if let Some(_) = self.draw_state.get() {
725            self.end(cx);
726            self.draw_state.end();
727        }
728        WidgetDraw::done()
729    }
730}
731
732#[derive(Clone, Default, PartialEq, WidgetRef)]
733pub struct PortalListRef(WidgetRef);
734
735impl PortalListRef {
736    pub fn set_first_id_and_scroll(&self, id: u64, s: f64) {
737        if let Some(mut inner) = self.borrow_mut() {
738            inner.first_id = id;
739            inner.first_scroll = s;
740        }
741    }
742    
743    pub fn set_first_id(&self, id: u64) {
744        if let Some(mut inner) = self.borrow_mut() {
745            inner.first_id = id;
746        }
747    }
748    
749    pub fn first_id(&self) -> u64 {
750        if let Some(inner) = self.borrow() {
751            inner.first_id
752        }
753        else {
754            0
755        }
756    }
757    
758    pub fn set_tail_range(&self, tail_range: bool) {
759        if let Some(mut inner) = self.borrow_mut() {
760            inner.tail_range = tail_range
761        }
762    }
763    
764    pub fn item(&self, cx: &mut Cx, entry_id: u64, template: LiveId) -> Option<WidgetRef> {
765        if let Some(mut inner) = self.borrow_mut() {
766            inner.item(cx, entry_id, template)
767        }
768        else {
769            None
770        }
771    }
772    
773    pub fn items_with_actions(&self, actions: &WidgetActions) -> Vec<(u64, WidgetRef)> {
774        let mut set = Vec::new();
775        self.items_with_actions_vec(actions, &mut set);
776        set
777    }
778    
779    fn items_with_actions_vec(&self, actions: &WidgetActions, set: &mut Vec<(u64, WidgetRef)>) {
780        let uid = self.widget_uid();
781        for action in actions {
782            if action.container_uid == uid {
783                
784                if let Some(inner) = self.borrow() {
785                    for ((item_id, _), item) in inner.items.iter() {
786                        
787                        if item.widget_uid() == action.item_uid {
788                            set.push((*item_id, item.clone()))
789                        }
790                    }
791                }
792            }
793        }
794    }
795}
796
797#[derive(Clone, Default, WidgetSet)]
798pub struct PortalListSet(WidgetSet);
799
800impl PortalListSet {
801    pub fn set_first_id(&self, id: u64) {
802        for list in self.iter() {
803            list.set_first_id(id)
804        }
805    }
806    
807    
808    pub fn items_with_actions(&self, actions: &WidgetActions) -> Vec<(u64, WidgetRef)> {
809        let mut set = Vec::new();
810        for list in self.iter() {
811            list.items_with_actions_vec(actions, &mut set)
812        }
813        set
814    }
815}