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