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}