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