acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_obj_scroll.c (27720B)

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