1 /**
2 * @file lv_indev_scroll.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_indev.h"
10 #include "lv_indev_scroll.h"
11
12 /*********************
13 * DEFINES
14 *********************/
15 #define ELASTIC_SLOWNESS_FACTOR 4 /*Scrolling on elastic parts are slower by this factor*/
16
17 /**********************
18 * TYPEDEFS
19 **********************/
20
21 /**********************
22 * STATIC PROTOTYPES
23 **********************/
24 static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc);
25 static void init_scroll_limits(_lv_indev_proc_t * proc);
26 static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
27 static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
28 static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y);
29 static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc);
30 static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc);
31 static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end,
32 lv_dir_t dir);
33
34 /**********************
35 * STATIC VARIABLES
36 **********************/
37
38 /**********************
39 * MACROS
40 **********************/
41
42 /**********************
43 * GLOBAL FUNCTIONS
44 **********************/
45
_lv_indev_scroll_handler(_lv_indev_proc_t * proc)46 void _lv_indev_scroll_handler(_lv_indev_proc_t * proc)
47 {
48 lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
49 /*If there is no scroll object yet try to find one*/
50 if(scroll_obj == NULL) {
51 proc->types.pointer.scroll_sum.x += proc->types.pointer.vect.x;
52 proc->types.pointer.scroll_sum.y += proc->types.pointer.vect.y;
53
54 scroll_obj = find_scroll_obj(proc);
55 if(scroll_obj == NULL) return;
56
57 init_scroll_limits(proc);
58
59 lv_event_send(scroll_obj, LV_EVENT_SCROLL_BEGIN, NULL);
60 if(proc->reset_query) return;
61 }
62
63 /*Set new position or scroll if the vector is not zero*/
64 if(proc->types.pointer.vect.x != 0 || proc->types.pointer.vect.y != 0) {
65 lv_coord_t diff_x = 0;
66 lv_coord_t diff_y = 0;
67
68 if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
69 lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
70 lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
71 diff_x = elastic_diff(scroll_obj, proc->types.pointer.vect.x, sl, sr, LV_DIR_HOR);
72 }
73 else {
74 lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
75 lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
76 diff_y = elastic_diff(scroll_obj, proc->types.pointer.vect.y, st, sb, LV_DIR_VER);
77 }
78
79 lv_dir_t scroll_dir = lv_obj_get_scroll_dir(scroll_obj);
80 if((scroll_dir & LV_DIR_LEFT) == 0 && diff_x > 0) diff_x = 0;
81 if((scroll_dir & LV_DIR_RIGHT) == 0 && diff_x < 0) diff_x = 0;
82 if((scroll_dir & LV_DIR_TOP) == 0 && diff_y > 0) diff_y = 0;
83 if((scroll_dir & LV_DIR_BOTTOM) == 0 && diff_y < 0) diff_y = 0;
84
85 /*Respect the scroll limit area*/
86 scroll_limit_diff(proc, &diff_x, &diff_y);
87
88 lv_obj_scroll_by(scroll_obj, diff_x, diff_y, LV_ANIM_OFF);
89 proc->types.pointer.scroll_sum.x += diff_x;
90 proc->types.pointer.scroll_sum.y += diff_y;
91 }
92 }
93
94
_lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc)95 void _lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc)
96 {
97 lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
98 if(scroll_obj == NULL) return;
99 if(proc->types.pointer.scroll_dir == LV_DIR_NONE) return;
100
101
102 lv_indev_t * indev_act = lv_indev_get_act();
103 lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
104
105 if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_MOMENTUM) == false) {
106 proc->types.pointer.scroll_throw_vect.y = 0;
107 proc->types.pointer.scroll_throw_vect.x = 0;
108 }
109
110 lv_scroll_snap_t align_x = lv_obj_get_scroll_snap_x(scroll_obj);
111 lv_scroll_snap_t align_y = lv_obj_get_scroll_snap_y(scroll_obj);
112
113 if(proc->types.pointer.scroll_dir == LV_DIR_VER) {
114 proc->types.pointer.scroll_throw_vect.x = 0;
115 /*If no snapping "throw"*/
116 if(align_y == LV_SCROLL_SNAP_NONE) {
117 proc->types.pointer.scroll_throw_vect.y =
118 proc->types.pointer.scroll_throw_vect.y * (100 - scroll_throw) / 100;
119
120 lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
121 lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
122
123 proc->types.pointer.scroll_throw_vect.y = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.y, st, sb,
124 LV_DIR_VER);
125
126 lv_obj_scroll_by(scroll_obj, 0, proc->types.pointer.scroll_throw_vect.y, LV_ANIM_OFF);
127 }
128 /*With snapping find the nearest snap point and scroll there*/
129 else {
130 lv_coord_t diff_y = scroll_throw_predict_y(proc);
131 proc->types.pointer.scroll_throw_vect.y = 0;
132 scroll_limit_diff(proc, NULL, &diff_y);
133 lv_coord_t y = find_snap_point_y(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_y);
134 lv_obj_scroll_by(scroll_obj, 0, diff_y + y, LV_ANIM_ON);
135 }
136 }
137 else if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
138 proc->types.pointer.scroll_throw_vect.y = 0;
139 /*If no snapping "throw"*/
140 if(align_x == LV_SCROLL_SNAP_NONE) {
141 proc->types.pointer.scroll_throw_vect.x =
142 proc->types.pointer.scroll_throw_vect.x * (100 - scroll_throw) / 100;
143
144 lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
145 lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
146
147 proc->types.pointer.scroll_throw_vect.x = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.x, sl, sr,
148 LV_DIR_HOR);
149
150 lv_obj_scroll_by(scroll_obj, proc->types.pointer.scroll_throw_vect.x, 0, LV_ANIM_OFF);
151 }
152 /*With snapping find the nearest snap point and scroll there*/
153 else {
154 lv_coord_t diff_x = scroll_throw_predict_x(proc);
155 proc->types.pointer.scroll_throw_vect.x = 0;
156 scroll_limit_diff(proc, &diff_x, NULL);
157 lv_coord_t x = find_snap_point_x(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_x);
158 lv_obj_scroll_by(scroll_obj, x + diff_x, 0, LV_ANIM_ON);
159 }
160 }
161
162 /*Check if the scroll has finished*/
163 if(proc->types.pointer.scroll_throw_vect.x == 0 && proc->types.pointer.scroll_throw_vect.y == 0) {
164 /*Revert if scrolled in*/
165 /*If vertically scrollable and not controlled by snap*/
166 if(align_y == LV_SCROLL_SNAP_NONE) {
167 lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
168 lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
169 if(st > 0 || sb > 0) {
170 if(st < 0) {
171 lv_obj_scroll_by(scroll_obj, 0, st, LV_ANIM_ON);
172 }
173 else if(sb < 0) {
174 lv_obj_scroll_by(scroll_obj, 0, -sb, LV_ANIM_ON);
175 }
176 }
177 }
178
179 /*If horizontally scrollable and not controlled by snap*/
180 if(align_x == LV_SCROLL_SNAP_NONE) {
181 lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
182 lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
183 if(sl > 0 || sr > 0) {
184 if(sl < 0) {
185 lv_obj_scroll_by(scroll_obj, sl, 0, LV_ANIM_ON);
186 }
187 else if(sr < 0) {
188 lv_obj_scroll_by(scroll_obj, -sr, 0, LV_ANIM_ON);
189 }
190 }
191 }
192
193 lv_event_send(scroll_obj, LV_EVENT_SCROLL_END, indev_act);
194 if(proc->reset_query) return;
195
196 proc->types.pointer.scroll_dir = LV_DIR_NONE;
197 proc->types.pointer.scroll_obj = NULL;
198 }
199 }
200
201 /**
202 * Predict where would a scroll throw end
203 * @param indev pointer to an input device
204 * @param dir `LV_DIR_VER` or `LV_DIR_HOR`
205 * @return the difference compared to the current position when the throw would be finished
206 */
lv_indev_scroll_throw_predict(lv_indev_t * indev,lv_dir_t dir)207 lv_coord_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir)
208 {
209 if(indev == NULL) return 0;
210 lv_coord_t v;
211 switch(dir) {
212 case LV_DIR_VER:
213 v = indev->proc.types.pointer.scroll_throw_vect_ori.y;
214 break;
215 case LV_DIR_HOR:
216 v = indev->proc.types.pointer.scroll_throw_vect_ori.x;
217 break;
218 default:
219 return 0;
220 }
221
222 lv_coord_t scroll_throw = indev->driver->scroll_throw;
223 lv_coord_t sum = 0;
224 while(v) {
225 sum += v;
226 v = v * (100 - scroll_throw) / 100;
227 }
228
229 return sum;
230 }
231
lv_indev_scroll_get_snap_dist(lv_obj_t * obj,lv_point_t * p)232 void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p)
233 {
234 p->x = find_snap_point_x(obj, obj->coords.x1, obj->coords.x2, 0);
235 p->y = find_snap_point_y(obj, obj->coords.y1, obj->coords.y2, 0);
236 }
237
238 /**********************
239 * STATIC FUNCTIONS
240 **********************/
241
find_scroll_obj(_lv_indev_proc_t * proc)242 static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc)
243 {
244 lv_obj_t * obj_candidate = NULL;
245 lv_dir_t dir_candidate = LV_DIR_NONE;
246 lv_indev_t * indev_act = lv_indev_get_act();
247 lv_coord_t scroll_limit = indev_act->driver->scroll_limit;
248
249 /*Go until find a scrollable object in the current direction
250 *More precisely:
251 * 1. Check the pressed object and all of its ancestors and try to find an object which is scrollable
252 * 2. Scrollable means it has some content out of its area
253 * 3. If an object can be scrolled into the current direction then use it ("real match"")
254 * 4. If can be scrolled on the current axis (hor/ver) save it as candidate (at least show an elastic scroll effect)
255 * 5. Use the last candidate. Always the "deepest" parent or the object from point 3*/
256 lv_obj_t * obj_act = proc->types.pointer.act_obj;
257
258 /*Decide if it's a horizontal or vertical scroll*/
259 bool hor_en = false;
260 bool ver_en = false;
261 if(LV_ABS(proc->types.pointer.scroll_sum.x) > LV_ABS(proc->types.pointer.scroll_sum.y)) {
262 hor_en = true;
263 }
264 else {
265 ver_en = true;
266 }
267
268 while(obj_act) {
269 if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLLABLE) == false) {
270 /*If this object don't want to chain the scroll to the parent stop searching*/
271 if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_HOR) == false && hor_en) break;
272 if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_VER) == false && ver_en) break;
273
274 obj_act = lv_obj_get_parent(obj_act);
275 continue;
276 }
277
278 /*Consider both up-down or left/right scrollable according to the current direction*/
279 bool up_en = ver_en;
280 bool down_en = ver_en;
281 bool left_en = hor_en;
282 bool right_en = hor_en;
283
284 /*The object might have disabled some directions.*/
285 lv_dir_t scroll_dir = lv_obj_get_scroll_dir(obj_act);
286 if((scroll_dir & LV_DIR_LEFT) == 0) left_en = false;
287 if((scroll_dir & LV_DIR_RIGHT) == 0) right_en = false;
288 if((scroll_dir & LV_DIR_TOP) == 0) up_en = false;
289 if((scroll_dir & LV_DIR_BOTTOM) == 0) down_en = false;
290
291 /*The object is scrollable to a direction if its content overflow in that direction.*/
292 lv_coord_t st = lv_obj_get_scroll_top(obj_act);
293 lv_coord_t sb = lv_obj_get_scroll_bottom(obj_act);
294 lv_coord_t sl = lv_obj_get_scroll_left(obj_act);
295 lv_coord_t sr = lv_obj_get_scroll_right(obj_act);
296
297 /*If this object is scrollable into the current scroll direction then save it as a candidate.
298 *It's important only to be scrollable on the current axis (hor/ver) because if the scroll
299 *is propagated to this object it can show at least elastic scroll effect.
300 *But if not hor/ver scrollable do not scroll it at all (so it's not a good candidate)*/
301 if((st > 0 || sb > 0) &&
302 ((up_en && proc->types.pointer.scroll_sum.y >= scroll_limit) ||
303 (down_en && proc->types.pointer.scroll_sum.y <= - scroll_limit))) {
304 obj_candidate = obj_act;
305 dir_candidate = LV_DIR_VER;
306 }
307
308 if((sl > 0 || sr > 0) &&
309 ((left_en && proc->types.pointer.scroll_sum.x >= scroll_limit) ||
310 (right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit))) {
311 obj_candidate = obj_act;
312 dir_candidate = LV_DIR_HOR;
313 }
314
315 if(st <= 0) up_en = false;
316 if(sb <= 0) down_en = false;
317 if(sl <= 0) left_en = false;
318 if(sr <= 0) right_en = false;
319
320 /*If the object really can be scrolled into the current direction the use it.*/
321 if((left_en && proc->types.pointer.scroll_sum.x >= scroll_limit) ||
322 (right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit) ||
323 (up_en && proc->types.pointer.scroll_sum.y >= scroll_limit) ||
324 (down_en && proc->types.pointer.scroll_sum.y <= - scroll_limit)) {
325 proc->types.pointer.scroll_dir = hor_en ? LV_DIR_HOR : LV_DIR_VER;
326 break;
327 }
328
329 /*If this object don't want to chain the scroll to the parent stop searching*/
330 if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_HOR) == false && hor_en) break;
331 if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_VER) == false && ver_en) break;
332
333 /*Try the parent*/
334 obj_act = lv_obj_get_parent(obj_act);
335 }
336
337 /*Use the last candidate*/
338 if(obj_candidate) {
339 proc->types.pointer.scroll_dir = dir_candidate;
340 proc->types.pointer.scroll_obj = obj_candidate;
341 proc->types.pointer.scroll_sum.x = 0;
342 proc->types.pointer.scroll_sum.y = 0;
343 }
344
345 return obj_candidate;
346 }
347
init_scroll_limits(_lv_indev_proc_t * proc)348 static void init_scroll_limits(_lv_indev_proc_t * proc)
349 {
350 lv_obj_t * obj = proc->types.pointer.scroll_obj;
351 /*If there no STOP allow scrolling anywhere*/
352 if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ONE) == false) {
353 lv_area_set(&proc->types.pointer.scroll_area, LV_COORD_MIN, LV_COORD_MIN, LV_COORD_MAX, LV_COORD_MAX);
354 }
355 /*With STOP limit the scrolling to the perv and next snap point*/
356 else {
357 switch(lv_obj_get_scroll_snap_y(obj)) {
358 case LV_SCROLL_SNAP_START:
359 proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y1 + 1, LV_COORD_MAX, 0);
360 proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y1 - 1, 0);
361 break;
362 case LV_SCROLL_SNAP_END:
363 proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y2, LV_COORD_MAX, 0);
364 proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y2, 0);
365 break;
366 case LV_SCROLL_SNAP_CENTER: {
367 lv_coord_t y_mid = obj->coords.y1 + lv_area_get_height(&obj->coords) / 2;
368 proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, y_mid + 1, LV_COORD_MAX, 0);
369 proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, y_mid - 1, 0);
370 break;
371 }
372 default:
373 proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
374 proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
375 break;
376 }
377
378 switch(lv_obj_get_scroll_snap_x(obj)) {
379 case LV_SCROLL_SNAP_START:
380 proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x1, LV_COORD_MAX, 0);
381 proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x1, 0);
382 break;
383 case LV_SCROLL_SNAP_END:
384 proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x2, LV_COORD_MAX, 0);
385 proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x2, 0);
386 break;
387 case LV_SCROLL_SNAP_CENTER: {
388 lv_coord_t x_mid = obj->coords.x1 + lv_area_get_width(&obj->coords) / 2;
389 proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, x_mid + 1, LV_COORD_MAX, 0);
390 proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, x_mid - 1, 0);
391 break;
392 }
393 default:
394 proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
395 proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
396 break;
397 }
398 }
399
400 /*Allow scrolling on the edges. It will be reverted to the edge due to snapping anyway*/
401 if(proc->types.pointer.scroll_area.x1 == 0) proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
402 if(proc->types.pointer.scroll_area.x2 == 0) proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
403 if(proc->types.pointer.scroll_area.y1 == 0) proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
404 if(proc->types.pointer.scroll_area.y2 == 0) proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
405 }
406
407 /**
408 * Search for snap point in the `min` - `max` range.
409 * @param obj the object on which snap point should be found
410 * @param min ignore snap points smaller than this. (Absolute coordinate)
411 * @param max ignore snap points greater than this. (Absolute coordinate)
412 * @param ofs offset to snap points. Useful the get a snap point in an imagined case
413 * what if children are already moved by this value
414 * @return the distance of the snap point.
415 */
find_snap_point_x(const lv_obj_t * obj,lv_coord_t min,lv_coord_t max,lv_coord_t ofs)416 static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
417 {
418 lv_scroll_snap_t align = lv_obj_get_scroll_snap_x(obj);
419 if(align == LV_SCROLL_SNAP_NONE) return 0;
420
421 lv_coord_t dist = LV_COORD_MAX;
422
423 lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
424 lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
425
426 uint32_t i;
427 uint32_t child_cnt = lv_obj_get_child_cnt(obj);
428 for(i = 0; i < child_cnt; i++) {
429 lv_obj_t * child = obj->spec_attr->children[i];
430 if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
431 if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
432 lv_coord_t x_child = 0;
433 lv_coord_t x_parent = 0;
434 switch(align) {
435 case LV_SCROLL_SNAP_START:
436 x_child = child->coords.x1;
437 x_parent = obj->coords.x1 + pad_left;
438 break;
439 case LV_SCROLL_SNAP_END:
440 x_child = child->coords.x2;
441 x_parent = obj->coords.x2 - pad_right;
442 break;
443 case LV_SCROLL_SNAP_CENTER:
444 x_child = child->coords.x1 + lv_area_get_width(&child->coords) / 2;
445 x_parent = obj->coords.x1 + pad_left + (lv_area_get_width(&obj->coords) - pad_left - pad_right) / 2;
446 break;
447 default:
448 continue;
449 }
450
451 x_child += ofs;
452 if(x_child >= min && x_child <= max) {
453 lv_coord_t x = x_child - x_parent;
454 if(LV_ABS(x) < LV_ABS(dist)) dist = x;
455 }
456 }
457 }
458
459 return dist == LV_COORD_MAX ? 0 : -dist;
460 }
461
462 /**
463 * Search for snap point in the `min` - `max` range.
464 * @param obj the object on which snap point should be found
465 * @param min ignore snap points smaller than this. (Absolute coordinate)
466 * @param max ignore snap points greater than this. (Absolute coordinate)
467 * @param ofs offset to snap points. Useful to get a snap point in an imagined case
468 * what if children are already moved by this value
469 * @return the distance of the snap point.
470 */
find_snap_point_y(const lv_obj_t * obj,lv_coord_t min,lv_coord_t max,lv_coord_t ofs)471 static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
472 {
473 lv_scroll_snap_t align = lv_obj_get_scroll_snap_y(obj);
474 if(align == LV_SCROLL_SNAP_NONE) return 0;
475
476 lv_coord_t dist = LV_COORD_MAX;
477
478 lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
479 lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
480
481 uint32_t i;
482 uint32_t child_cnt = lv_obj_get_child_cnt(obj);
483 for(i = 0; i < child_cnt; i++) {
484 lv_obj_t * child = obj->spec_attr->children[i];
485 if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
486 if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
487 lv_coord_t y_child = 0;
488 lv_coord_t y_parent = 0;
489 switch(align) {
490 case LV_SCROLL_SNAP_START:
491 y_child = child->coords.y1;
492 y_parent = obj->coords.y1 + pad_top;
493 break;
494 case LV_SCROLL_SNAP_END:
495 y_child = child->coords.y2;
496 y_parent = obj->coords.y2 - pad_bottom;
497 break;
498 case LV_SCROLL_SNAP_CENTER:
499 y_child = child->coords.y1 + lv_area_get_height(&child->coords) / 2;
500 y_parent = obj->coords.y1 + pad_top + (lv_area_get_height(&obj->coords) - pad_top - pad_bottom) / 2;
501 break;
502 default:
503 continue;
504 }
505
506 y_child += ofs;
507 if(y_child >= min && y_child <= max) {
508 lv_coord_t y = y_child - y_parent;
509 if(LV_ABS(y) < LV_ABS(dist)) dist = y;
510 }
511 }
512 }
513
514 return dist == LV_COORD_MAX ? 0 : -dist;
515 }
516
scroll_limit_diff(_lv_indev_proc_t * proc,lv_coord_t * diff_x,lv_coord_t * diff_y)517 static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y)
518 {
519 if(diff_y) {
520 if(proc->types.pointer.scroll_sum.y + *diff_y < proc->types.pointer.scroll_area.y1) {
521 *diff_y = proc->types.pointer.scroll_area.y1 - proc->types.pointer.scroll_sum.y;
522 }
523
524 if(proc->types.pointer.scroll_sum.y + *diff_y > proc->types.pointer.scroll_area.y2) {
525 *diff_y = proc->types.pointer.scroll_area.y2 - proc->types.pointer.scroll_sum.y;
526 }
527 }
528
529 if(diff_x) {
530 if(proc->types.pointer.scroll_sum.x + *diff_x < proc->types.pointer.scroll_area.x1) {
531 *diff_x = proc->types.pointer.scroll_area.x1 - proc->types.pointer.scroll_sum.x;
532 }
533
534 if(proc->types.pointer.scroll_sum.x + *diff_x > proc->types.pointer.scroll_area.x2) {
535 *diff_x = proc->types.pointer.scroll_area.x2 - proc->types.pointer.scroll_sum.x;
536 }
537 }
538 }
539
540
541
scroll_throw_predict_y(_lv_indev_proc_t * proc)542 static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc)
543 {
544 lv_coord_t y = proc->types.pointer.scroll_throw_vect.y;
545 lv_coord_t move = 0;
546
547 lv_indev_t * indev_act = lv_indev_get_act();
548 lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
549
550 while(y) {
551 move += y;
552 y = y * (100 - scroll_throw) / 100;
553 }
554 return move;
555 }
556
557
scroll_throw_predict_x(_lv_indev_proc_t * proc)558 static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc)
559 {
560 lv_coord_t x = proc->types.pointer.scroll_throw_vect.x;
561 lv_coord_t move = 0;
562
563 lv_indev_t * indev_act = lv_indev_get_act();
564 lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
565
566 while(x) {
567 move += x;
568 x = x * (100 - scroll_throw) / 100;
569 }
570 return move;
571 }
572
elastic_diff(lv_obj_t * scroll_obj,lv_coord_t diff,lv_coord_t scroll_start,lv_coord_t scroll_end,lv_dir_t dir)573 static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end,
574 lv_dir_t dir)
575 {
576 if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_ELASTIC)) {
577 /*If there is snapping in the current direction don't use the elastic factor because
578 *it's natural that the first and last items are scrolled (snapped) in.*/
579 lv_scroll_snap_t snap;
580 snap = dir == LV_DIR_HOR ? lv_obj_get_scroll_snap_x(scroll_obj) : lv_obj_get_scroll_snap_y(scroll_obj);
581
582 lv_obj_t * act_obj = lv_indev_get_obj_act();
583 lv_coord_t snap_point = 0;
584 lv_coord_t act_obj_point = 0;
585
586 if(dir == LV_DIR_HOR) {
587 lv_coord_t pad_left = lv_obj_get_style_pad_left(scroll_obj, LV_PART_MAIN);
588 lv_coord_t pad_right = lv_obj_get_style_pad_right(scroll_obj, LV_PART_MAIN);
589
590 switch(snap) {
591 case LV_SCROLL_SNAP_CENTER:
592 snap_point = pad_left + (lv_area_get_width(&scroll_obj->coords) - pad_left - pad_right) / 2 + scroll_obj->coords.x1;
593 act_obj_point = lv_area_get_width(&act_obj->coords) / 2 + act_obj->coords.x1;
594 break;
595 case LV_SCROLL_SNAP_START:
596 snap_point = scroll_obj->coords.x1 + pad_left;
597 act_obj_point = act_obj->coords.x1;
598 break;
599 case LV_SCROLL_SNAP_END:
600 snap_point = scroll_obj->coords.x2 - pad_right;
601 act_obj_point = act_obj->coords.x2;
602 break;
603 }
604 }
605 else {
606 lv_coord_t pad_top = lv_obj_get_style_pad_top(scroll_obj, LV_PART_MAIN);
607 lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(scroll_obj, LV_PART_MAIN);
608
609 switch(snap) {
610 case LV_SCROLL_SNAP_CENTER:
611 snap_point = pad_top + (lv_area_get_height(&scroll_obj->coords) - pad_top - pad_bottom) / 2 + scroll_obj->coords.y1;
612 act_obj_point = lv_area_get_height(&act_obj->coords) / 2 + act_obj->coords.y1;
613 break;
614 case LV_SCROLL_SNAP_START:
615 snap_point = scroll_obj->coords.y1 + pad_top;
616 act_obj_point = act_obj->coords.y1;
617 break;
618 case LV_SCROLL_SNAP_END:
619 snap_point = scroll_obj->coords.y2 - pad_bottom;
620 act_obj_point = act_obj->coords.y2;
621 break;
622 }
623 }
624
625 if(scroll_end < 0) {
626 if(snap != LV_SCROLL_SNAP_NONE && act_obj_point > snap_point) return diff;
627
628 /*Rounding*/
629 if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
630 if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
631 return diff / ELASTIC_SLOWNESS_FACTOR;
632 }
633 else if(scroll_start < 0) {
634 if(snap != LV_SCROLL_SNAP_NONE && act_obj_point < snap_point) return diff;
635
636 /*Rounding*/
637 if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
638 if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
639 return diff / ELASTIC_SLOWNESS_FACTOR;
640 }
641 }
642 else {
643 /*Scroll back to the boundary if required*/
644 if(scroll_end + diff < 0) diff = - scroll_end;
645 if(scroll_start - diff < 0) diff = scroll_start;
646 }
647
648 return diff;
649 }
650
651
652