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