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