1 /**
2  * @file lv_obj_scroll.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_obj_scroll_private.h"
10 #include "../misc/lv_anim_private.h"
11 #include "lv_obj_private.h"
12 #include "../indev/lv_indev.h"
13 #include "../indev/lv_indev_scroll.h"
14 #include "../display/lv_display.h"
15 #include "../misc/lv_area.h"
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 #define MY_CLASS (&lv_obj_class)
21 #define SCROLL_ANIM_TIME_MIN    200    /*ms*/
22 #define SCROLL_ANIM_TIME_MAX    400    /*ms*/
23 #define SCROLLBAR_MIN_SIZE      (LV_DPX(10))
24 
25 /**********************
26  *      TYPEDEFS
27  **********************/
28 
29 /**********************
30  *  GLOBAL PROTOTYPES
31  **********************/
32 
33 /**********************
34  *  STATIC PROTOTYPES
35  **********************/
36 static void scroll_x_anim(void * obj, int32_t v);
37 static void scroll_y_anim(void * obj, int32_t v);
38 static void scroll_end_cb(lv_anim_t * a);
39 static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
40                                   lv_anim_enable_t anim_en);
41 
42 /**********************
43  *  STATIC VARIABLES
44  **********************/
45 
46 /**********************
47  *      MACROS
48  **********************/
49 
50 /**********************
51  *   GLOBAL FUNCTIONS
52  **********************/
53 
54 /*=====================
55  * Setter functions
56  *====================*/
57 
lv_obj_set_scrollbar_mode(lv_obj_t * obj,lv_scrollbar_mode_t mode)58 void lv_obj_set_scrollbar_mode(lv_obj_t * obj, lv_scrollbar_mode_t mode)
59 {
60     LV_ASSERT_OBJ(obj, MY_CLASS);
61 
62     lv_obj_allocate_spec_attr(obj);
63 
64     if(obj->spec_attr->scrollbar_mode == mode) return;
65     obj->spec_attr->scrollbar_mode = mode;
66     lv_obj_invalidate(obj);
67 }
68 
lv_obj_set_scroll_dir(lv_obj_t * obj,lv_dir_t dir)69 void lv_obj_set_scroll_dir(lv_obj_t * obj, lv_dir_t dir)
70 {
71     lv_obj_allocate_spec_attr(obj);
72 
73     if(dir != obj->spec_attr->scroll_dir) {
74         obj->spec_attr->scroll_dir = dir;
75     }
76 }
77 
lv_obj_set_scroll_snap_x(lv_obj_t * obj,lv_scroll_snap_t align)78 void lv_obj_set_scroll_snap_x(lv_obj_t * obj, lv_scroll_snap_t align)
79 {
80     lv_obj_allocate_spec_attr(obj);
81     obj->spec_attr->scroll_snap_x = align;
82 }
83 
lv_obj_set_scroll_snap_y(lv_obj_t * obj,lv_scroll_snap_t align)84 void lv_obj_set_scroll_snap_y(lv_obj_t * obj, lv_scroll_snap_t align)
85 {
86     lv_obj_allocate_spec_attr(obj);
87     obj->spec_attr->scroll_snap_y = align;
88 }
89 
90 /*=====================
91  * Getter functions
92  *====================*/
93 
lv_obj_get_scrollbar_mode(const lv_obj_t * obj)94 lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const lv_obj_t * obj)
95 {
96     if(obj->spec_attr) return (lv_scrollbar_mode_t) obj->spec_attr->scrollbar_mode;
97     else return LV_SCROLLBAR_MODE_AUTO;
98 }
99 
lv_obj_get_scroll_dir(const lv_obj_t * obj)100 lv_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj)
101 {
102     if(obj->spec_attr) return (lv_dir_t) obj->spec_attr->scroll_dir;
103     else return LV_DIR_ALL;
104 }
105 
lv_obj_get_scroll_snap_x(const lv_obj_t * obj)106 lv_scroll_snap_t lv_obj_get_scroll_snap_x(const lv_obj_t * obj)
107 {
108     if(obj->spec_attr) return (lv_scroll_snap_t) obj->spec_attr->scroll_snap_x;
109     else return LV_SCROLL_SNAP_NONE;
110 }
111 
lv_obj_get_scroll_snap_y(const lv_obj_t * obj)112 lv_scroll_snap_t lv_obj_get_scroll_snap_y(const lv_obj_t * obj)
113 {
114     if(obj->spec_attr) return (lv_scroll_snap_t) obj->spec_attr->scroll_snap_y;
115     else return LV_SCROLL_SNAP_NONE;
116 }
117 
lv_obj_get_scroll_x(const lv_obj_t * obj)118 int32_t lv_obj_get_scroll_x(const lv_obj_t * obj)
119 {
120     if(obj->spec_attr == NULL) return 0;
121     return -obj->spec_attr->scroll.x;
122 }
123 
lv_obj_get_scroll_y(const lv_obj_t * obj)124 int32_t lv_obj_get_scroll_y(const lv_obj_t * obj)
125 {
126     if(obj->spec_attr == NULL) return 0;
127     return -obj->spec_attr->scroll.y;
128 }
129 
lv_obj_get_scroll_top(lv_obj_t * obj)130 int32_t lv_obj_get_scroll_top(lv_obj_t * obj)
131 {
132     if(obj->spec_attr == NULL) return 0;
133     return -obj->spec_attr->scroll.y;
134 }
135 
lv_obj_get_scroll_bottom(lv_obj_t * obj)136 int32_t lv_obj_get_scroll_bottom(lv_obj_t * obj)
137 {
138     LV_ASSERT_OBJ(obj, MY_CLASS);
139 
140     int32_t child_res = LV_COORD_MIN;
141     uint32_t i;
142     uint32_t child_cnt = lv_obj_get_child_count(obj);
143     for(i = 0; i < child_cnt; i++) {
144         lv_obj_t * child = obj->spec_attr->children[i];
145         if(lv_obj_has_flag_any(child,  LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
146 
147         int32_t tmp_y = child->coords.y2 + lv_obj_get_style_margin_bottom(child, LV_PART_MAIN);
148         child_res = LV_MAX(child_res, tmp_y);
149     }
150 
151     int32_t space_top = lv_obj_get_style_space_top(obj, LV_PART_MAIN);
152     int32_t space_bottom = lv_obj_get_style_space_bottom(obj, LV_PART_MAIN);
153 
154     if(child_res != LV_COORD_MIN) {
155         child_res -= (obj->coords.y2 - space_bottom);
156     }
157 
158     int32_t self_h = lv_obj_get_self_height(obj);
159     self_h = self_h - (lv_obj_get_height(obj) - space_top - space_bottom);
160     self_h -= lv_obj_get_scroll_y(obj);
161     return LV_MAX(child_res, self_h);
162 }
163 
lv_obj_get_scroll_left(lv_obj_t * obj)164 int32_t lv_obj_get_scroll_left(lv_obj_t * obj)
165 {
166     LV_ASSERT_OBJ(obj, MY_CLASS);
167 
168     /*Normally can't scroll the object out on the left.
169      *So simply use the current scroll position as "left size"*/
170     if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
171         if(obj->spec_attr == NULL) return 0;
172         return -obj->spec_attr->scroll.x;
173     }
174 
175     /*With RTL base direction scrolling the left is normal so find the left most coordinate*/
176     int32_t space_right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
177     int32_t space_left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
178 
179     int32_t child_res = 0;
180 
181     uint32_t i;
182     int32_t x1 = LV_COORD_MAX;
183     uint32_t child_cnt = lv_obj_get_child_count(obj);
184     for(i = 0; i < child_cnt; i++) {
185         lv_obj_t * child = obj->spec_attr->children[i];
186         if(lv_obj_has_flag_any(child,  LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
187 
188         int32_t tmp_x = child->coords.x1 - lv_obj_get_style_margin_left(child, LV_PART_MAIN);
189         x1 = LV_MIN(x1, tmp_x);
190     }
191 
192     if(x1 != LV_COORD_MAX) {
193         child_res = x1;
194         child_res = (obj->coords.x1 + space_left) - child_res;
195     }
196     else {
197         child_res = LV_COORD_MIN;
198     }
199 
200     int32_t self_w = lv_obj_get_self_width(obj);
201     self_w = self_w - (lv_obj_get_width(obj) - space_right - space_left);
202     self_w += lv_obj_get_scroll_x(obj);
203 
204     return LV_MAX(child_res, self_w);
205 }
206 
lv_obj_get_scroll_right(lv_obj_t * obj)207 int32_t lv_obj_get_scroll_right(lv_obj_t * obj)
208 {
209     LV_ASSERT_OBJ(obj, MY_CLASS);
210 
211     /*With RTL base dir can't scroll to the object out on the right.
212      *So simply use the current scroll position as "right size"*/
213     if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
214         if(obj->spec_attr == NULL) return 0;
215         return obj->spec_attr->scroll.x;
216     }
217 
218     /*With other base direction (LTR) scrolling to the right is normal so find the right most coordinate*/
219     int32_t child_res = LV_COORD_MIN;
220     uint32_t i;
221     uint32_t child_cnt = lv_obj_get_child_count(obj);
222     for(i = 0; i < child_cnt; i++) {
223         lv_obj_t * child = obj->spec_attr->children[i];
224         if(lv_obj_has_flag_any(child,  LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
225 
226         int32_t tmp_x = child->coords.x2 + lv_obj_get_style_margin_right(child, LV_PART_MAIN);
227         child_res = LV_MAX(child_res, tmp_x);
228     }
229 
230     int32_t space_right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
231     int32_t space_left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
232 
233     if(child_res != LV_COORD_MIN) {
234         child_res -= (obj->coords.x2 - space_right);
235     }
236 
237     int32_t self_w;
238     self_w = lv_obj_get_self_width(obj);
239     self_w = self_w - (lv_obj_get_width(obj) - space_right - space_left);
240     self_w -= lv_obj_get_scroll_x(obj);
241     return LV_MAX(child_res, self_w);
242 }
243 
lv_obj_get_scroll_end(lv_obj_t * obj,lv_point_t * end)244 void lv_obj_get_scroll_end(lv_obj_t * obj, lv_point_t * end)
245 {
246     lv_anim_t * a;
247     a = lv_anim_get(obj, scroll_x_anim);
248     end->x = a ? -a->end_value : lv_obj_get_scroll_x(obj);
249 
250     a = lv_anim_get(obj, scroll_y_anim);
251     end->y = a ? -a->end_value : lv_obj_get_scroll_y(obj);
252 }
253 
254 /*=====================
255  * Other functions
256  *====================*/
257 
lv_obj_scroll_by_bounded(lv_obj_t * obj,int32_t dx,int32_t dy,lv_anim_enable_t anim_en)258 void lv_obj_scroll_by_bounded(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en)
259 {
260     if(dx == 0 && dy == 0) return;
261 
262     /*We need to know the final sizes for bound check*/
263     lv_obj_update_layout(obj);
264 
265     /*Don't let scroll more than naturally possible by the size of the content*/
266     int32_t x_current = -lv_obj_get_scroll_x(obj);
267     int32_t x_bounded = x_current + dx;
268 
269     if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
270         if(x_bounded > 0) x_bounded = 0;
271         if(x_bounded < 0) {
272             int32_t  scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
273             if(scroll_max < 0) scroll_max = 0;
274 
275             if(x_bounded < -scroll_max) x_bounded = -scroll_max;
276         }
277     }
278     else {
279         if(x_bounded < 0) x_bounded = 0;
280         if(x_bounded > 0) {
281             int32_t  scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
282             if(scroll_max < 0) scroll_max = 0;
283 
284             if(x_bounded > scroll_max) x_bounded = scroll_max;
285         }
286     }
287 
288     /*Don't let scroll more than naturally possible by the size of the content*/
289     int32_t y_current = -lv_obj_get_scroll_y(obj);
290     int32_t y_bounded = y_current + dy;
291 
292     if(y_bounded > 0) y_bounded = 0;
293     if(y_bounded < 0) {
294         int32_t  scroll_max = lv_obj_get_scroll_top(obj) + lv_obj_get_scroll_bottom(obj);
295         if(scroll_max < 0) scroll_max = 0;
296         if(y_bounded < -scroll_max) y_bounded = -scroll_max;
297     }
298 
299     dx = x_bounded - x_current;
300     dy = y_bounded - y_current;
301     if(dx || dy) {
302         lv_obj_scroll_by(obj, dx, dy, anim_en);
303     }
304 }
305 
lv_obj_scroll_by(lv_obj_t * obj,int32_t dx,int32_t dy,lv_anim_enable_t anim_en)306 void lv_obj_scroll_by(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en)
307 {
308     if(dx == 0 && dy == 0) return;
309     if(anim_en) {
310         lv_display_t * d = lv_obj_get_display(obj);
311         lv_anim_t a;
312         lv_anim_init(&a);
313         lv_anim_set_var(&a, obj);
314         lv_anim_set_deleted_cb(&a, scroll_end_cb);
315 
316         if(dx) {
317             uint32_t t = lv_anim_speed_clamped((lv_display_get_horizontal_resolution(d)) >> 1, SCROLL_ANIM_TIME_MIN,
318                                                SCROLL_ANIM_TIME_MAX);
319             lv_anim_set_duration(&a, t);
320             int32_t sx = lv_obj_get_scroll_x(obj);
321             lv_anim_set_values(&a, -sx, -sx + dx);
322             lv_anim_set_exec_cb(&a, scroll_x_anim);
323             lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
324 
325             lv_result_t res;
326             res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, &a);
327             if(res != LV_RESULT_OK) return;
328             lv_anim_start(&a);
329         }
330 
331         if(dy) {
332             uint32_t t = lv_anim_speed_clamped((lv_display_get_vertical_resolution(d)) >> 1, SCROLL_ANIM_TIME_MIN,
333                                                SCROLL_ANIM_TIME_MAX);
334             lv_anim_set_duration(&a, t);
335             int32_t sy = lv_obj_get_scroll_y(obj);
336             lv_anim_set_values(&a, -sy, -sy + dy);
337             lv_anim_set_exec_cb(&a,  scroll_y_anim);
338             lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
339 
340             lv_result_t res;
341             res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, &a);
342             if(res != LV_RESULT_OK) return;
343             lv_anim_start(&a);
344         }
345     }
346     else {
347         /*Remove pending animations*/
348         lv_anim_delete(obj, scroll_y_anim);
349         lv_anim_delete(obj, scroll_x_anim);
350 
351         lv_result_t res;
352         res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, NULL);
353         if(res != LV_RESULT_OK) return;
354 
355         res = lv_obj_scroll_by_raw(obj, dx, dy);
356         if(res != LV_RESULT_OK) return;
357 
358         res = lv_obj_send_event(obj, LV_EVENT_SCROLL_END, NULL);
359         if(res != LV_RESULT_OK) return;
360     }
361 }
362 
lv_obj_scroll_to(lv_obj_t * obj,int32_t x,int32_t y,lv_anim_enable_t anim_en)363 void lv_obj_scroll_to(lv_obj_t * obj, int32_t x, int32_t y, lv_anim_enable_t anim_en)
364 {
365     lv_obj_scroll_to_x(obj, x, anim_en);
366     lv_obj_scroll_to_y(obj, y, anim_en);
367 }
368 
lv_obj_scroll_to_x(lv_obj_t * obj,int32_t x,lv_anim_enable_t anim_en)369 void lv_obj_scroll_to_x(lv_obj_t * obj, int32_t x, lv_anim_enable_t anim_en)
370 {
371     lv_anim_delete(obj, scroll_x_anim);
372 
373     int32_t scroll_x = lv_obj_get_scroll_x(obj);
374     int32_t diff = -x + scroll_x;
375 
376     lv_obj_scroll_by_bounded(obj, diff, 0, anim_en);
377 }
378 
lv_obj_scroll_to_y(lv_obj_t * obj,int32_t y,lv_anim_enable_t anim_en)379 void lv_obj_scroll_to_y(lv_obj_t * obj, int32_t y, lv_anim_enable_t anim_en)
380 {
381     lv_anim_delete(obj, scroll_y_anim);
382 
383     int32_t scroll_y = lv_obj_get_scroll_y(obj);
384     int32_t diff = -y + scroll_y;
385 
386     lv_obj_scroll_by_bounded(obj, 0, diff, anim_en);
387 }
388 
lv_obj_scroll_to_view(lv_obj_t * obj,lv_anim_enable_t anim_en)389 void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en)
390 {
391     /*Be sure the screens layout is correct*/
392     lv_obj_update_layout(obj);
393 
394     lv_point_t p = {0, 0};
395     scroll_area_into_view(&obj->coords, obj, &p, anim_en);
396 }
397 
lv_obj_scroll_to_view_recursive(lv_obj_t * obj,lv_anim_enable_t anim_en)398 void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en)
399 {
400     /*Be sure the screens layout is correct*/
401     lv_obj_update_layout(obj);
402 
403     lv_point_t p = {0, 0};
404     lv_obj_t * child = obj;
405     lv_obj_t * parent = lv_obj_get_parent(child);
406     while(parent) {
407         scroll_area_into_view(&obj->coords, child, &p, anim_en);
408         child = parent;
409         parent = lv_obj_get_parent(parent);
410     }
411 }
412 
lv_obj_scroll_by_raw(lv_obj_t * obj,int32_t x,int32_t y)413 lv_result_t lv_obj_scroll_by_raw(lv_obj_t * obj, int32_t x, int32_t y)
414 {
415     if(x == 0 && y == 0) return LV_RESULT_OK;
416 
417     lv_obj_allocate_spec_attr(obj);
418 
419     obj->spec_attr->scroll.x += x;
420     obj->spec_attr->scroll.y += y;
421 
422     lv_obj_move_children_by(obj, x, y, true);
423     lv_result_t res = lv_obj_send_event(obj, LV_EVENT_SCROLL, NULL);
424     if(res != LV_RESULT_OK) return res;
425     lv_obj_invalidate(obj);
426     return LV_RESULT_OK;
427 }
428 
lv_obj_is_scrolling(const lv_obj_t * obj)429 bool lv_obj_is_scrolling(const lv_obj_t * obj)
430 {
431     lv_indev_t * indev = lv_indev_get_next(NULL);
432     while(indev) {
433         if(lv_indev_get_scroll_obj(indev) == obj) return true;
434         indev = lv_indev_get_next(indev);
435     }
436 
437     if(lv_anim_get((lv_obj_t *)obj, scroll_x_anim) != NULL ||
438        lv_anim_get((lv_obj_t *)obj, scroll_y_anim) != NULL) {
439         return true;
440     }
441 
442     return false;
443 }
444 
lv_obj_stop_scroll_anim(const lv_obj_t * obj)445 void lv_obj_stop_scroll_anim(const lv_obj_t * obj)
446 {
447     lv_anim_delete((lv_obj_t *)obj, scroll_y_anim);
448     lv_anim_delete((lv_obj_t *)obj, scroll_x_anim);
449 }
450 
lv_obj_update_snap(lv_obj_t * obj,lv_anim_enable_t anim_en)451 void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en)
452 {
453     lv_obj_update_layout(obj);
454     lv_point_t p;
455     lv_indev_scroll_get_snap_dist(obj, &p);
456     if(p.x == LV_COORD_MAX || p.x == LV_COORD_MIN) p.x = 0;
457     if(p.y == LV_COORD_MAX || p.y == LV_COORD_MIN) p.y = 0;
458     lv_obj_scroll_by(obj, p.x, p.y, anim_en);
459 }
460 
lv_obj_get_scrollbar_area(lv_obj_t * obj,lv_area_t * hor_area,lv_area_t * ver_area)461 void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_area)
462 {
463     lv_area_set(hor_area, 0, 0, -1, -1);
464     lv_area_set(ver_area, 0, 0, -1, -1);
465 
466     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE) == false) return;
467 
468     lv_scrollbar_mode_t sm = lv_obj_get_scrollbar_mode(obj);
469     if(sm == LV_SCROLLBAR_MODE_OFF)  return;
470 
471     /*If there is no indev scrolling this object but `mode==active` return*/
472     lv_indev_t * indev = lv_indev_get_next(NULL);
473     if(sm == LV_SCROLLBAR_MODE_ACTIVE) {
474         while(indev) {
475             if(lv_indev_get_scroll_obj(indev) == obj) break;
476             indev = lv_indev_get_next(indev);
477         }
478         if(indev == NULL)  return;
479     }
480 
481     int32_t st = lv_obj_get_scroll_top(obj);
482     int32_t sb = lv_obj_get_scroll_bottom(obj);
483     int32_t sl = lv_obj_get_scroll_left(obj);
484     int32_t sr = lv_obj_get_scroll_right(obj);
485 
486     lv_dir_t dir = lv_obj_get_scroll_dir(obj);
487 
488     bool ver_draw = false;
489     if((dir & LV_DIR_VER) &&
490        ((sm == LV_SCROLLBAR_MODE_ON) ||
491         (sm == LV_SCROLLBAR_MODE_AUTO && (st > 0 || sb > 0)) ||
492         (sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_VER))) {
493         ver_draw = true;
494     }
495 
496     bool hor_draw = false;
497     if((dir & LV_DIR_HOR) &&
498        ((sm == LV_SCROLLBAR_MODE_ON) ||
499         (sm == LV_SCROLLBAR_MODE_AUTO && (sl > 0 || sr > 0)) ||
500         (sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_HOR))) {
501         hor_draw = true;
502     }
503 
504     if(!hor_draw && !ver_draw) return;
505 
506     bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_SCROLLBAR) == LV_BASE_DIR_RTL;
507 
508     int32_t top_space = lv_obj_get_style_pad_top(obj, LV_PART_SCROLLBAR);
509     int32_t bottom_space = lv_obj_get_style_pad_bottom(obj, LV_PART_SCROLLBAR);
510     int32_t left_space = lv_obj_get_style_pad_left(obj, LV_PART_SCROLLBAR);
511     int32_t right_space = lv_obj_get_style_pad_right(obj, LV_PART_SCROLLBAR);
512     int32_t thickness = lv_obj_get_style_width(obj, LV_PART_SCROLLBAR);
513     int32_t length = lv_obj_get_style_length(obj, LV_PART_SCROLLBAR);
514 
515     int32_t obj_h = lv_obj_get_height(obj);
516     int32_t obj_w = lv_obj_get_width(obj);
517 
518     /*Space required for the vertical and horizontal scrollbars*/
519     int32_t ver_reg_space = ver_draw ? thickness : 0;
520     int32_t hor_req_space = hor_draw ? thickness : 0;
521     int32_t rem;
522 
523     if(lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR) < LV_OPA_MIN &&
524        lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR) < LV_OPA_MIN) {
525         return;
526     }
527 
528     /*Draw vertical scrollbar if the mode is ON or can be scrolled in this direction*/
529     int32_t content_h = obj_h + st + sb;
530     if(ver_draw && content_h) {
531         ver_area->y1 = obj->coords.y1;
532         ver_area->y2 = obj->coords.y2;
533         if(rtl) {
534             ver_area->x1 = obj->coords.x1 + left_space;
535             ver_area->x2 = ver_area->x1 + thickness - 1;
536         }
537         else {
538             ver_area->x2 = obj->coords.x2 - right_space;
539             ver_area->x1 = ver_area->x2 - thickness + 1;
540         }
541 
542         int32_t sb_h = ((obj_h - top_space - bottom_space - hor_req_space) * obj_h) / content_h;
543         sb_h = LV_MAX(length > 0 ? length : sb_h, SCROLLBAR_MIN_SIZE); /*Style-defined size, calculated size, or minimum size*/
544         sb_h = LV_MIN(sb_h, obj_h); /*Limit scrollbar length to parent height*/
545         rem = (obj_h - top_space - bottom_space - hor_req_space) -
546               sb_h;  /*Remaining size from the scrollbar track that is not the scrollbar itself*/
547         int32_t scroll_h = content_h - obj_h; /*The size of the content which can be really scrolled*/
548         if(scroll_h <= 0) {
549             ver_area->y1 = obj->coords.y1 + top_space;
550             ver_area->y2 = obj->coords.y2 - bottom_space - hor_req_space - 1;
551         }
552         else {
553             int32_t sb_y = (rem * sb) / scroll_h;
554             sb_y = rem - sb_y;
555 
556             ver_area->y1 = obj->coords.y1 + sb_y + top_space;
557             ver_area->y2 = ver_area->y1 + sb_h - 1;
558             if(ver_area->y1 < obj->coords.y1 + top_space) {
559                 ver_area->y1 = obj->coords.y1 + top_space;
560                 if(ver_area->y1 + SCROLLBAR_MIN_SIZE > ver_area->y2) {
561                     ver_area->y2 = ver_area->y1 + SCROLLBAR_MIN_SIZE;
562                 }
563             }
564             if(ver_area->y2 > obj->coords.y2 - hor_req_space - bottom_space) {
565                 ver_area->y2 = obj->coords.y2 - hor_req_space - bottom_space;
566                 if(ver_area->y2 - SCROLLBAR_MIN_SIZE < ver_area->y1) {
567                     ver_area->y1 = ver_area->y2 - SCROLLBAR_MIN_SIZE;
568                 }
569             }
570         }
571     }
572 
573     /*Draw horizontal scrollbar if the mode is ON or can be scrolled in this direction*/
574     int32_t content_w = obj_w + sl + sr;
575     if(hor_draw && content_w) {
576         hor_area->y2 = obj->coords.y2 - bottom_space;
577         hor_area->y1 = hor_area->y2 - thickness + 1;
578         hor_area->x1 = obj->coords.x1;
579         hor_area->x2 = obj->coords.x2;
580 
581         int32_t sb_w = ((obj_w - left_space - right_space - ver_reg_space) * obj_w) / content_w;
582         sb_w = LV_MAX(length > 0 ? length : sb_w, SCROLLBAR_MIN_SIZE); /*Style-defined size, calculated size, or minimum size*/
583         sb_w = LV_MIN(sb_w, obj_w); /*Limit scrollbar length to parent width*/
584         rem = (obj_w - left_space - right_space - ver_reg_space) -
585               sb_w;  /*Remaining size from the scrollbar track that is not the scrollbar itself*/
586         int32_t scroll_w = content_w - obj_w; /*The size of the content which can be really scrolled*/
587         if(scroll_w <= 0) {
588             if(rtl) {
589                 hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space - 1;
590                 hor_area->x2 = obj->coords.x2 - right_space;
591             }
592             else {
593                 hor_area->x1 = obj->coords.x1 + left_space;
594                 hor_area->x2 = obj->coords.x2 - right_space - ver_reg_space - 1;
595             }
596         }
597         else {
598             int32_t sb_x = (rem * sr) / scroll_w;
599             sb_x = rem - sb_x;
600 
601             if(rtl) {
602                 hor_area->x1 = obj->coords.x1 + sb_x + left_space + ver_reg_space;
603                 hor_area->x2 = hor_area->x1 + sb_w - 1;
604                 if(hor_area->x1 < obj->coords.x1 + left_space + ver_reg_space) {
605                     hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space;
606                     if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
607                         hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
608                     }
609                 }
610                 if(hor_area->x2 > obj->coords.x2 - right_space) {
611                     hor_area->x2 = obj->coords.x2 - right_space;
612                     if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
613                         hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
614                     }
615                 }
616             }
617             else {
618                 hor_area->x1 = obj->coords.x1 + sb_x + left_space;
619                 hor_area->x2 = hor_area->x1 + sb_w - 1;
620                 if(hor_area->x1 < obj->coords.x1 + left_space) {
621                     hor_area->x1 = obj->coords.x1 + left_space;
622                     if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
623                         hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
624                     }
625                 }
626                 if(hor_area->x2 > obj->coords.x2 - ver_reg_space - right_space) {
627                     hor_area->x2 = obj->coords.x2 - ver_reg_space - right_space;
628                     if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
629                         hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
630                     }
631                 }
632             }
633         }
634     }
635 }
636 
lv_obj_scrollbar_invalidate(lv_obj_t * obj)637 void lv_obj_scrollbar_invalidate(lv_obj_t * obj)
638 {
639     lv_area_t hor_area;
640     lv_area_t ver_area;
641     lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
642 
643     if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
644 
645     if(lv_area_get_size(&hor_area) > 0) lv_obj_invalidate_area(obj, &hor_area);
646     if(lv_area_get_size(&ver_area) > 0) lv_obj_invalidate_area(obj, &ver_area);
647 }
648 
lv_obj_readjust_scroll(lv_obj_t * obj,lv_anim_enable_t anim_en)649 void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en)
650 {
651     /*Be sure the bottom side is not remains scrolled in*/
652     /*With snapping the content can't be scrolled in*/
653     if(lv_obj_get_scroll_snap_y(obj) == LV_SCROLL_SNAP_NONE) {
654         int32_t st = lv_obj_get_scroll_top(obj);
655         int32_t sb = lv_obj_get_scroll_bottom(obj);
656         if(sb < 0 && st > 0) {
657             sb = LV_MIN(st, -sb);
658             lv_obj_scroll_by(obj, 0, sb, anim_en);
659         }
660     }
661 
662     if(lv_obj_get_scroll_snap_x(obj) == LV_SCROLL_SNAP_NONE) {
663         int32_t sl = lv_obj_get_scroll_left(obj);
664         int32_t sr = lv_obj_get_scroll_right(obj);
665         if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
666             /*Be sure the left side is not remains scrolled in*/
667             if(sr < 0 && sl > 0) {
668                 sr = LV_MIN(sl, -sr);
669                 lv_obj_scroll_by(obj, sr, 0, anim_en);
670             }
671         }
672         else {
673             /*Be sure the right side is not remains scrolled in*/
674             if(sl < 0 && sr > 0) {
675                 sr = LV_MIN(sr, -sl);
676                 lv_obj_scroll_by(obj, sl, 0, anim_en);
677             }
678         }
679     }
680 }
681 
682 /**********************
683  *   STATIC FUNCTIONS
684  **********************/
685 
scroll_x_anim(void * obj,int32_t v)686 static void scroll_x_anim(void * obj, int32_t v)
687 {
688     lv_obj_scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0);
689 }
690 
scroll_y_anim(void * obj,int32_t v)691 static void scroll_y_anim(void * obj, int32_t v)
692 {
693     lv_obj_scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj));
694 }
695 
scroll_end_cb(lv_anim_t * a)696 static void scroll_end_cb(lv_anim_t * a)
697 {
698     /*Do not sent END event if there wasn't a BEGIN*/
699     if(a->start_cb_called) lv_obj_send_event(a->var, LV_EVENT_SCROLL_END, NULL);
700 }
701 
scroll_area_into_view(const lv_area_t * area,lv_obj_t * child,lv_point_t * scroll_value,lv_anim_enable_t anim_en)702 static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
703                                   lv_anim_enable_t anim_en)
704 {
705     lv_obj_t * parent = lv_obj_get_parent(child);
706     if(!lv_obj_has_flag(parent, LV_OBJ_FLAG_SCROLLABLE)) return;
707 
708     lv_dir_t scroll_dir = lv_obj_get_scroll_dir(parent);
709     int32_t snap_goal = 0;
710     int32_t act = 0;
711     const lv_area_t * area_tmp;
712 
713     int32_t y_scroll = 0;
714     lv_scroll_snap_t snap_y = lv_obj_get_scroll_snap_y(parent);
715     if(snap_y != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
716     else area_tmp = area;
717 
718     int32_t stop = lv_obj_get_style_space_top(parent, LV_PART_MAIN);
719     int32_t sbottom = lv_obj_get_style_space_bottom(parent, LV_PART_MAIN);
720     int32_t top_diff = parent->coords.y1 + stop - area_tmp->y1 - scroll_value->y;
721     int32_t bottom_diff = -(parent->coords.y2 - sbottom - area_tmp->y2 - scroll_value->y);
722     int32_t parent_h = lv_obj_get_height(parent) - stop - sbottom;
723     if((top_diff >= 0 && bottom_diff >= 0)) y_scroll = 0;
724     else if(top_diff > 0) {
725         y_scroll = top_diff;
726         /*Do not let scrolling in*/
727         int32_t st = lv_obj_get_scroll_top(parent);
728         if(st - y_scroll < 0) y_scroll = 0;
729     }
730     else if(bottom_diff > 0) {
731         y_scroll = -bottom_diff;
732         /*Do not let scrolling in*/
733         int32_t sb = lv_obj_get_scroll_bottom(parent);
734         if(sb + y_scroll < 0) y_scroll = 0;
735     }
736 
737     switch(snap_y) {
738         case LV_SCROLL_SNAP_START:
739             snap_goal = parent->coords.y1 + stop;
740             act = area_tmp->y1 + y_scroll;
741             y_scroll += snap_goal - act;
742             break;
743         case LV_SCROLL_SNAP_END:
744             snap_goal = parent->coords.y2 - sbottom;
745             act = area_tmp->y2 + y_scroll;
746             y_scroll += snap_goal - act;
747             break;
748         case LV_SCROLL_SNAP_CENTER:
749             snap_goal = parent->coords.y1 + stop + parent_h / 2;
750             act = lv_area_get_height(area_tmp) / 2 + area_tmp->y1 + y_scroll;
751             y_scroll += snap_goal - act;
752             break;
753         case LV_SCROLL_SNAP_NONE:
754             break;
755     }
756 
757     int32_t x_scroll = 0;
758     lv_scroll_snap_t snap_x = lv_obj_get_scroll_snap_x(parent);
759     if(snap_x != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
760     else area_tmp = area;
761 
762     int32_t sleft = lv_obj_get_style_space_left(parent, LV_PART_MAIN);
763     int32_t sright = lv_obj_get_style_space_right(parent, LV_PART_MAIN);
764     int32_t left_diff = parent->coords.x1 + sleft - area_tmp->x1 - scroll_value->x;
765     int32_t right_diff = -(parent->coords.x2 - sright - area_tmp->x2 - scroll_value->x);
766     if((left_diff >= 0 && right_diff >= 0)) x_scroll = 0;
767     else if(left_diff > 0) {
768         x_scroll = left_diff;
769         /*Do not let scrolling in*/
770         int32_t sl = lv_obj_get_scroll_left(parent);
771         if(sl - x_scroll < 0) x_scroll = 0;
772     }
773     else if(right_diff > 0) {
774         x_scroll = -right_diff;
775         /*Do not let scrolling in*/
776         int32_t sr = lv_obj_get_scroll_right(parent);
777         if(sr + x_scroll < 0) x_scroll = 0;
778     }
779 
780     int32_t parent_w = lv_obj_get_width(parent) - sleft - sright;
781     switch(snap_x) {
782         case LV_SCROLL_SNAP_START:
783             snap_goal = parent->coords.x1 + sleft;
784             act = area_tmp->x1 + x_scroll;
785             x_scroll += snap_goal - act;
786             break;
787         case LV_SCROLL_SNAP_END:
788             snap_goal = parent->coords.x2 - sright;
789             act = area_tmp->x2 + x_scroll;
790             x_scroll += snap_goal - act;
791             break;
792         case LV_SCROLL_SNAP_CENTER:
793             snap_goal = parent->coords.x1 + sleft + parent_w / 2;
794             act = lv_area_get_width(area_tmp) / 2 + area_tmp->x1 + x_scroll;
795             x_scroll += snap_goal - act;
796             break;
797         case LV_SCROLL_SNAP_NONE:
798             break;
799     }
800 
801     /*Remove any pending scroll animations.*/
802     lv_anim_delete(parent, scroll_y_anim);
803     lv_anim_delete(parent, scroll_x_anim);
804 
805     if((scroll_dir & LV_DIR_LEFT) == 0 && x_scroll < 0) x_scroll = 0;
806     if((scroll_dir & LV_DIR_RIGHT) == 0 && x_scroll > 0) x_scroll = 0;
807     if((scroll_dir & LV_DIR_TOP) == 0 && y_scroll < 0) y_scroll = 0;
808     if((scroll_dir & LV_DIR_BOTTOM) == 0 && y_scroll > 0) y_scroll = 0;
809 
810     scroll_value->x += anim_en ? x_scroll : 0;
811     scroll_value->y += anim_en ? y_scroll : 0;
812     lv_obj_scroll_by(parent, x_scroll, y_scroll, anim_en);
813 }
814