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
lv_obj_scroll_by(lv_obj_t * obj,lv_coord_t dx,lv_coord_t dy,lv_anim_enable_t anim_en)302 void lv_obj_scroll_by(lv_obj_t * obj, lv_coord_t dx, lv_coord_t dy, lv_anim_enable_t anim_en)
303 {
304 if(dx == 0 && dy == 0) return;
305 if(anim_en == LV_ANIM_ON) {
306 lv_disp_t * d = lv_obj_get_disp(obj);
307 lv_anim_t a;
308 lv_anim_init(&a);
309 lv_anim_set_var(&a, obj);
310 lv_anim_set_ready_cb(&a, scroll_anim_ready_cb);
311
312 if(dx) {
313 uint32_t t = lv_anim_speed_to_time((lv_disp_get_hor_res(d) * 2) >> 2, 0, dx);
314 if(t < SCROLL_ANIM_TIME_MIN) t = SCROLL_ANIM_TIME_MIN;
315 if(t > SCROLL_ANIM_TIME_MAX) t = SCROLL_ANIM_TIME_MAX;
316 lv_anim_set_time(&a, t);
317 lv_coord_t sx = lv_obj_get_scroll_x(obj);
318 lv_anim_set_values(&a, -sx, -sx + dx);
319 lv_anim_set_exec_cb(&a, scroll_x_anim);
320 lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
321
322 lv_res_t res;
323 res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, &a);
324 if(res != LV_RES_OK) return;
325 lv_anim_start(&a);
326 }
327
328 if(dy) {
329 uint32_t t = lv_anim_speed_to_time((lv_disp_get_ver_res(d) * 2) >> 2, 0, dy);
330 if(t < SCROLL_ANIM_TIME_MIN) t = SCROLL_ANIM_TIME_MIN;
331 if(t > SCROLL_ANIM_TIME_MAX) t = SCROLL_ANIM_TIME_MAX;
332 lv_anim_set_time(&a, t);
333 lv_coord_t sy = lv_obj_get_scroll_y(obj);
334 lv_anim_set_values(&a, -sy, -sy + dy);
335 lv_anim_set_exec_cb(&a, scroll_y_anim);
336 lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
337
338 lv_res_t res;
339 res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, &a);
340 if(res != LV_RES_OK) return;
341 lv_anim_start(&a);
342 }
343 }
344 else {
345 /*Remove pending animations*/
346 lv_anim_del(obj, scroll_y_anim);
347 lv_anim_del(obj, scroll_x_anim);
348
349 lv_res_t res;
350 res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, NULL);
351 if(res != LV_RES_OK) return;
352
353 res = _lv_obj_scroll_by_raw(obj, dx, dy);
354 if(res != LV_RES_OK) return;
355
356 res = lv_event_send(obj, LV_EVENT_SCROLL_END, NULL);
357 if(res != LV_RES_OK) return;
358 }
359 }
360
lv_obj_scroll_to(lv_obj_t * obj,lv_coord_t x,lv_coord_t y,lv_anim_enable_t anim_en)361 void lv_obj_scroll_to(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en)
362 {
363 lv_obj_scroll_to_x(obj, x, anim_en);
364 lv_obj_scroll_to_y(obj, y, anim_en);
365 }
366
lv_obj_scroll_to_x(lv_obj_t * obj,lv_coord_t x,lv_anim_enable_t anim_en)367 void lv_obj_scroll_to_x(lv_obj_t * obj, lv_coord_t x, lv_anim_enable_t anim_en)
368 {
369 lv_anim_del(obj, scroll_x_anim);
370
371 lv_coord_t scroll_x = lv_obj_get_scroll_x(obj);
372 lv_coord_t diff = -x + scroll_x;
373
374 lv_obj_scroll_by_bounded(obj, diff, 0, anim_en);
375 }
376
lv_obj_scroll_to_y(lv_obj_t * obj,lv_coord_t y,lv_anim_enable_t anim_en)377 void lv_obj_scroll_to_y(lv_obj_t * obj, lv_coord_t y, lv_anim_enable_t anim_en)
378 {
379 lv_anim_del(obj, scroll_y_anim);
380
381 lv_coord_t scroll_y = lv_obj_get_scroll_y(obj);
382 lv_coord_t diff = -y + scroll_y;
383
384 lv_obj_scroll_by_bounded(obj, 0, diff, anim_en);
385 }
386
lv_obj_scroll_to_view(lv_obj_t * obj,lv_anim_enable_t anim_en)387 void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en)
388 {
389 /*Be sure the screens layout is correct*/
390 lv_obj_update_layout(obj);
391
392 lv_point_t p = {0, 0};
393 scroll_area_into_view(&obj->coords, obj, &p, anim_en);
394 }
395
lv_obj_scroll_to_view_recursive(lv_obj_t * obj,lv_anim_enable_t anim_en)396 void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en)
397 {
398 /*Be sure the screens layout is correct*/
399 lv_obj_update_layout(obj);
400
401 lv_point_t p = {0, 0};
402 lv_obj_t * child = obj;
403 lv_obj_t * parent = lv_obj_get_parent(child);
404 while(parent) {
405 scroll_area_into_view(&obj->coords, child, &p, anim_en);
406 child = parent;
407 parent = lv_obj_get_parent(parent);
408 }
409 }
410
_lv_obj_scroll_by_raw(lv_obj_t * obj,lv_coord_t x,lv_coord_t y)411 lv_res_t _lv_obj_scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
412 {
413 if(x == 0 && y == 0) return LV_RES_OK;
414
415 lv_obj_allocate_spec_attr(obj);
416
417 obj->spec_attr->scroll.x += x;
418 obj->spec_attr->scroll.y += y;
419
420 lv_obj_move_children_by(obj, x, y, true);
421 lv_res_t res = lv_event_send(obj, LV_EVENT_SCROLL, NULL);
422 if(res != LV_RES_OK) return res;
423 lv_obj_invalidate(obj);
424 return LV_RES_OK;
425 }
426
lv_obj_is_scrolling(const lv_obj_t * obj)427 bool lv_obj_is_scrolling(const lv_obj_t * obj)
428 {
429 lv_indev_t * indev = lv_indev_get_next(NULL);
430 while(indev) {
431 if(lv_indev_get_scroll_obj(indev) == obj) return true;
432 indev = lv_indev_get_next(indev);
433 }
434
435 return false;
436 }
437
lv_obj_update_snap(lv_obj_t * obj,lv_anim_enable_t anim_en)438 void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en)
439 {
440 lv_obj_update_layout(obj);
441 lv_point_t p;
442 lv_indev_scroll_get_snap_dist(obj, &p);
443 lv_obj_scroll_by(obj, p.x, p.y, anim_en);
444 }
445
lv_obj_get_scrollbar_area(lv_obj_t * obj,lv_area_t * hor_area,lv_area_t * ver_area)446 void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_area)
447 {
448 lv_area_set(hor_area, 0, 0, -1, -1);
449 lv_area_set(ver_area, 0, 0, -1, -1);
450
451 if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE) == false) return;
452
453 lv_dir_t sm = lv_obj_get_scrollbar_mode(obj);
454 if(sm == LV_SCROLLBAR_MODE_OFF) return;
455
456 /*If there is no indev scrolling this object but `mode==active` return*/
457 lv_indev_t * indev = lv_indev_get_next(NULL);
458 if(sm == LV_SCROLLBAR_MODE_ACTIVE) {
459 while(indev) {
460 if(lv_indev_get_scroll_obj(indev) == obj) break;
461 indev = lv_indev_get_next(indev);
462 }
463 if(indev == NULL) return;
464 }
465
466 lv_coord_t st = lv_obj_get_scroll_top(obj);
467 lv_coord_t sb = lv_obj_get_scroll_bottom(obj);
468 lv_coord_t sl = lv_obj_get_scroll_left(obj);
469 lv_coord_t sr = lv_obj_get_scroll_right(obj);
470
471 lv_dir_t dir = lv_obj_get_scroll_dir(obj);
472
473 bool ver_draw = false;
474 if((dir & LV_DIR_VER) &&
475 ((sm == LV_SCROLLBAR_MODE_ON) ||
476 (sm == LV_SCROLLBAR_MODE_AUTO && (st > 0 || sb > 0)) ||
477 (sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_VER))) {
478 ver_draw = true;
479 }
480
481 bool hor_draw = false;
482 if((dir & LV_DIR_HOR) &&
483 ((sm == LV_SCROLLBAR_MODE_ON) ||
484 (sm == LV_SCROLLBAR_MODE_AUTO && (sl > 0 || sr > 0)) ||
485 (sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_HOR))) {
486 hor_draw = true;
487 }
488
489 if(!hor_draw && !ver_draw) return;
490
491 bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_SCROLLBAR) == LV_BASE_DIR_RTL ? true : false;
492
493 lv_coord_t top_space = lv_obj_get_style_pad_top(obj, LV_PART_SCROLLBAR);
494 lv_coord_t bottom_space = lv_obj_get_style_pad_bottom(obj, LV_PART_SCROLLBAR);
495 lv_coord_t left_space = lv_obj_get_style_pad_left(obj, LV_PART_SCROLLBAR);
496 lv_coord_t right_space = lv_obj_get_style_pad_right(obj, LV_PART_SCROLLBAR);
497 lv_coord_t tickness = lv_obj_get_style_width(obj, LV_PART_SCROLLBAR);
498
499 lv_coord_t obj_h = lv_obj_get_height(obj);
500 lv_coord_t obj_w = lv_obj_get_width(obj);
501
502 /*Space required for the vertical and horizontal scrollbars*/
503 lv_coord_t ver_reg_space = ver_draw ? tickness : 0;
504 lv_coord_t hor_req_space = hor_draw ? tickness : 0;
505 lv_coord_t rem;
506
507 if(lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR) < LV_OPA_MIN &&
508 lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR) < LV_OPA_MIN) {
509 return;
510 }
511
512 /*Draw vertical scrollbar if the mode is ON or can be scrolled in this direction*/
513 lv_coord_t content_h = obj_h + st + sb;
514 if(ver_draw && content_h) {
515 ver_area->y1 = obj->coords.y1;
516 ver_area->y2 = obj->coords.y2;
517 if(rtl) {
518 ver_area->x1 = obj->coords.x1 + left_space;
519 ver_area->x2 = ver_area->x1 + tickness - 1;
520 }
521 else {
522 ver_area->x2 = obj->coords.x2 - right_space;
523 ver_area->x1 = ver_area->x2 - tickness + 1;
524 }
525
526 lv_coord_t sb_h = ((obj_h - top_space - bottom_space - hor_req_space) * obj_h) / content_h;
527 sb_h = LV_MAX(sb_h, SCROLLBAR_MIN_SIZE);
528 rem = (obj_h - top_space - bottom_space - hor_req_space) -
529 sb_h; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
530 lv_coord_t scroll_h = content_h - obj_h; /*The size of the content which can be really scrolled*/
531 if(scroll_h <= 0) {
532 ver_area->y1 = obj->coords.y1 + top_space;
533 ver_area->y2 = obj->coords.y2 - bottom_space - hor_req_space - 1;
534 }
535 else {
536 lv_coord_t sb_y = (rem * sb) / scroll_h;
537 sb_y = rem - sb_y;
538
539 ver_area->y1 = obj->coords.y1 + sb_y + top_space;
540 ver_area->y2 = ver_area->y1 + sb_h - 1;
541 if(ver_area->y1 < obj->coords.y1 + top_space) {
542 ver_area->y1 = obj->coords.y1 + top_space;
543 if(ver_area->y1 + SCROLLBAR_MIN_SIZE > ver_area->y2) {
544 ver_area->y2 = ver_area->y1 + SCROLLBAR_MIN_SIZE;
545 }
546 }
547 if(ver_area->y2 > obj->coords.y2 - hor_req_space - bottom_space) {
548 ver_area->y2 = obj->coords.y2 - hor_req_space - bottom_space;
549 if(ver_area->y2 - SCROLLBAR_MIN_SIZE < ver_area->y1) {
550 ver_area->y1 = ver_area->y2 - SCROLLBAR_MIN_SIZE;
551 }
552 }
553 }
554 }
555
556 /*Draw horizontal scrollbar if the mode is ON or can be scrolled in this direction*/
557 lv_coord_t content_w = obj_w + sl + sr;
558 if(hor_draw && content_w) {
559 hor_area->y2 = obj->coords.y2 - bottom_space;
560 hor_area->y1 = hor_area->y2 - tickness + 1;
561 hor_area->x1 = obj->coords.x1;
562 hor_area->x2 = obj->coords.x2;
563
564 lv_coord_t sb_w = ((obj_w - left_space - right_space - ver_reg_space) * obj_w) / content_w;
565 sb_w = LV_MAX(sb_w, SCROLLBAR_MIN_SIZE);
566 rem = (obj_w - left_space - right_space - ver_reg_space) -
567 sb_w; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
568 lv_coord_t scroll_w = content_w - obj_w; /*The size of the content which can be really scrolled*/
569 if(scroll_w <= 0) {
570 if(rtl) {
571 hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space - 1;
572 hor_area->x2 = obj->coords.x2 - right_space;
573 }
574 else {
575 hor_area->x1 = obj->coords.x1 + left_space;
576 hor_area->x2 = obj->coords.x2 - right_space - ver_reg_space - 1;
577 }
578 }
579 else {
580 lv_coord_t sb_x = (rem * sr) / scroll_w;
581 sb_x = rem - sb_x;
582
583 if(rtl) {
584 hor_area->x1 = obj->coords.x1 + sb_x + left_space + ver_reg_space;
585 hor_area->x2 = hor_area->x1 + sb_w - 1;
586 if(hor_area->x1 < obj->coords.x1 + left_space + ver_reg_space) {
587 hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space;
588 if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
589 hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
590 }
591 }
592 if(hor_area->x2 > obj->coords.x2 - right_space) {
593 hor_area->x2 = obj->coords.x2 - right_space;
594 if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
595 hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
596 }
597 }
598 }
599 else {
600 hor_area->x1 = obj->coords.x1 + sb_x + left_space;
601 hor_area->x2 = hor_area->x1 + sb_w - 1;
602 if(hor_area->x1 < obj->coords.x1 + left_space) {
603 hor_area->x1 = obj->coords.x1 + left_space;
604 if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
605 hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
606 }
607 }
608 if(hor_area->x2 > obj->coords.x2 - ver_reg_space - right_space) {
609 hor_area->x2 = obj->coords.x2 - ver_reg_space - right_space;
610 if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
611 hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
612 }
613 }
614 }
615 }
616 }
617 }
618
lv_obj_scrollbar_invalidate(lv_obj_t * obj)619 void lv_obj_scrollbar_invalidate(lv_obj_t * obj)
620 {
621 lv_area_t hor_area;
622 lv_area_t ver_area;
623 lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
624
625 if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
626
627 if(lv_area_get_size(&hor_area) > 0) lv_obj_invalidate_area(obj, &hor_area);
628 if(lv_area_get_size(&ver_area) > 0) lv_obj_invalidate_area(obj, &ver_area);
629 }
630
lv_obj_readjust_scroll(lv_obj_t * obj,lv_anim_enable_t anim_en)631 void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en)
632 {
633 /*Be sure the bottom side is not remains scrolled in*/
634 /*With snapping the content can't be scrolled in*/
635 if(lv_obj_get_scroll_snap_y(obj) == LV_SCROLL_SNAP_NONE) {
636 lv_coord_t st = lv_obj_get_scroll_top(obj);
637 lv_coord_t sb = lv_obj_get_scroll_bottom(obj);
638 if(sb < 0 && st > 0) {
639 sb = LV_MIN(st, -sb);
640 lv_obj_scroll_by(obj, 0, sb, anim_en);
641 }
642 }
643
644 if(lv_obj_get_scroll_snap_x(obj) == LV_SCROLL_SNAP_NONE) {
645 lv_coord_t sl = lv_obj_get_scroll_left(obj);
646 lv_coord_t sr = lv_obj_get_scroll_right(obj);
647 if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
648 /*Be sure the left side is not remains scrolled in*/
649 if(sr < 0 && sl > 0) {
650 sr = LV_MIN(sl, -sr);
651 lv_obj_scroll_by(obj, sr, 0, anim_en);
652 }
653 }
654 else {
655 /*Be sure the right side is not remains scrolled in*/
656 if(sl < 0 && sr > 0) {
657 sr = LV_MIN(sr, -sl);
658 lv_obj_scroll_by(obj, sl, 0, anim_en);
659 }
660 }
661 }
662 }
663
664 /**********************
665 * STATIC FUNCTIONS
666 **********************/
667
scroll_x_anim(void * obj,int32_t v)668 static void scroll_x_anim(void * obj, int32_t v)
669 {
670 _lv_obj_scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0);
671 }
672
scroll_y_anim(void * obj,int32_t v)673 static void scroll_y_anim(void * obj, int32_t v)
674 {
675 _lv_obj_scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj));
676 }
677
scroll_anim_ready_cb(lv_anim_t * a)678 static void scroll_anim_ready_cb(lv_anim_t * a)
679 {
680 lv_event_send(a->var, LV_EVENT_SCROLL_END, NULL);
681 }
682
scroll_area_into_view(const lv_area_t * area,lv_obj_t * child,lv_point_t * scroll_value,lv_anim_enable_t anim_en)683 static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
684 lv_anim_enable_t anim_en)
685 {
686 lv_obj_t * parent = lv_obj_get_parent(child);
687 if(!lv_obj_has_flag(parent, LV_OBJ_FLAG_SCROLLABLE)) return;
688
689 lv_dir_t scroll_dir = lv_obj_get_scroll_dir(parent);
690 lv_coord_t snap_goal = 0;
691 lv_coord_t act = 0;
692 const lv_area_t * area_tmp;
693
694 lv_coord_t y_scroll = 0;
695 lv_scroll_snap_t snap_y = lv_obj_get_scroll_snap_y(parent);
696 if(snap_y != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
697 else area_tmp = area;
698
699 lv_coord_t border_width = lv_obj_get_style_border_width(parent, LV_PART_MAIN);
700 lv_coord_t ptop = lv_obj_get_style_pad_top(parent, LV_PART_MAIN) + border_width;
701 lv_coord_t pbottom = lv_obj_get_style_pad_bottom(parent, LV_PART_MAIN) + border_width;
702 lv_coord_t top_diff = parent->coords.y1 + ptop - area_tmp->y1 - scroll_value->y;
703 lv_coord_t bottom_diff = -(parent->coords.y2 - pbottom - area_tmp->y2 - scroll_value->y);
704 lv_coord_t parent_h = lv_obj_get_height(parent) - ptop - pbottom;
705 if((top_diff >= 0 && bottom_diff >= 0)) y_scroll = 0;
706 else if(top_diff > 0) {
707 y_scroll = top_diff;
708 /*Do not let scrolling in*/
709 lv_coord_t st = lv_obj_get_scroll_top(parent);
710 if(st - y_scroll < 0) y_scroll = 0;
711 }
712 else if(bottom_diff > 0) {
713 y_scroll = -bottom_diff;
714 /*Do not let scrolling in*/
715 lv_coord_t sb = lv_obj_get_scroll_bottom(parent);
716 if(sb + y_scroll < 0) y_scroll = 0;
717 }
718
719 switch(snap_y) {
720 case LV_SCROLL_SNAP_START:
721 snap_goal = parent->coords.y1 + ptop;
722 act = area_tmp->y1 + y_scroll;
723 y_scroll += snap_goal - act;
724 break;
725 case LV_SCROLL_SNAP_END:
726 snap_goal = parent->coords.y2 - pbottom;
727 act = area_tmp->y2 + y_scroll;
728 y_scroll += snap_goal - act;
729 break;
730 case LV_SCROLL_SNAP_CENTER:
731 snap_goal = parent->coords.y1 + ptop + parent_h / 2;
732 act = lv_area_get_height(area_tmp) / 2 + area_tmp->y1 + y_scroll;
733 y_scroll += snap_goal - act;
734 break;
735 }
736
737 lv_coord_t x_scroll = 0;
738 lv_scroll_snap_t snap_x = lv_obj_get_scroll_snap_x(parent);
739 if(snap_x != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
740 else area_tmp = area;
741
742 lv_coord_t pleft = lv_obj_get_style_pad_left(parent, LV_PART_MAIN) + border_width;
743 lv_coord_t pright = lv_obj_get_style_pad_right(parent, LV_PART_MAIN) + border_width;
744 lv_coord_t left_diff = parent->coords.x1 + pleft - area_tmp->x1 - scroll_value->x;
745 lv_coord_t right_diff = -(parent->coords.x2 - pright - area_tmp->x2 - scroll_value->x);
746 if((left_diff >= 0 && right_diff >= 0)) x_scroll = 0;
747 else if(left_diff > 0) {
748 x_scroll = left_diff;
749 /*Do not let scrolling in*/
750 lv_coord_t sl = lv_obj_get_scroll_left(parent);
751 if(sl - x_scroll < 0) x_scroll = 0;
752 }
753 else if(right_diff > 0) {
754 x_scroll = -right_diff;
755 /*Do not let scrolling in*/
756 lv_coord_t sr = lv_obj_get_scroll_right(parent);
757 if(sr + x_scroll < 0) x_scroll = 0;
758 }
759
760 lv_coord_t parent_w = lv_obj_get_width(parent) - pleft - pright;
761 switch(snap_x) {
762 case LV_SCROLL_SNAP_START:
763 snap_goal = parent->coords.x1 + pleft;
764 act = area_tmp->x1 + x_scroll;
765 x_scroll += snap_goal - act;
766 break;
767 case LV_SCROLL_SNAP_END:
768 snap_goal = parent->coords.x2 - pright;
769 act = area_tmp->x2 + x_scroll;
770 x_scroll += snap_goal - act;
771 break;
772 case LV_SCROLL_SNAP_CENTER:
773 snap_goal = parent->coords.x1 + pleft + parent_w / 2;
774 act = lv_area_get_width(area_tmp) / 2 + area_tmp->x1 + x_scroll;
775 x_scroll += snap_goal - act;
776 break;
777 }
778
779 /*Remove any pending scroll animations.*/
780 bool y_del = lv_anim_del(parent, scroll_y_anim);
781 bool x_del = lv_anim_del(parent, scroll_x_anim);
782 if(y_del || x_del) {
783 lv_res_t res;
784 res = lv_event_send(parent, LV_EVENT_SCROLL_END, NULL);
785 if(res != LV_RES_OK) return;
786 }
787
788 if((scroll_dir & LV_DIR_LEFT) == 0 && x_scroll < 0) x_scroll = 0;
789 if((scroll_dir & LV_DIR_RIGHT) == 0 && x_scroll > 0) x_scroll = 0;
790 if((scroll_dir & LV_DIR_TOP) == 0 && y_scroll < 0) y_scroll = 0;
791 if((scroll_dir & LV_DIR_BOTTOM) == 0 && y_scroll > 0) y_scroll = 0;
792
793 scroll_value->x += anim_en == LV_ANIM_OFF ? 0 : x_scroll;
794 scroll_value->y += anim_en == LV_ANIM_OFF ? 0 : y_scroll;
795 lv_obj_scroll_by(parent, x_scroll, y_scroll, anim_en);
796 }
797