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