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_indev_scroll.c (26707B)

      1 /**
      2  * @file lv_indev_scroll.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 #include "lv_indev.h"
     10 #include "lv_indev_scroll.h"
     11 
     12 /*********************
     13  *      DEFINES
     14  *********************/
     15 #define ELASTIC_SLOWNESS_FACTOR 4   /*Scrolling on elastic parts are slower by this factor*/
     16 
     17 /**********************
     18  *      TYPEDEFS
     19  **********************/
     20 
     21 /**********************
     22  *  STATIC PROTOTYPES
     23  **********************/
     24 static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc);
     25 static void init_scroll_limits(_lv_indev_proc_t * proc);
     26 static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
     27 static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
     28 static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y);
     29 static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc);
     30 static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc);
     31 static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end,
     32                                lv_dir_t dir);
     33 
     34 /**********************
     35  *  STATIC VARIABLES
     36  **********************/
     37 
     38 /**********************
     39  *      MACROS
     40  **********************/
     41 
     42 /**********************
     43  *   GLOBAL FUNCTIONS
     44  **********************/
     45 
     46 void _lv_indev_scroll_handler(_lv_indev_proc_t * proc)
     47 {
     48     lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
     49     /*If there is no scroll object yet try to find one*/
     50     if(scroll_obj == NULL) {
     51         proc->types.pointer.scroll_sum.x += proc->types.pointer.vect.x;
     52         proc->types.pointer.scroll_sum.y += proc->types.pointer.vect.y;
     53 
     54         scroll_obj = find_scroll_obj(proc);
     55         if(scroll_obj == NULL) return;
     56 
     57         init_scroll_limits(proc);
     58 
     59         lv_event_send(scroll_obj, LV_EVENT_SCROLL_BEGIN, NULL);
     60         if(proc->reset_query) return;
     61     }
     62 
     63     /*Set new position or scroll if the vector is not zero*/
     64     if(proc->types.pointer.vect.x != 0 || proc->types.pointer.vect.y != 0) {
     65         lv_coord_t diff_x = 0;
     66         lv_coord_t diff_y = 0;
     67 
     68         if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
     69             lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
     70             lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
     71             diff_x = elastic_diff(scroll_obj, proc->types.pointer.vect.x, sl, sr, LV_DIR_HOR);
     72         }
     73         else {
     74             lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
     75             lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
     76             diff_y = elastic_diff(scroll_obj, proc->types.pointer.vect.y, st, sb, LV_DIR_VER);
     77         }
     78 
     79         lv_dir_t scroll_dir = lv_obj_get_scroll_dir(scroll_obj);
     80         if((scroll_dir & LV_DIR_LEFT)   == 0 && diff_x > 0) diff_x = 0;
     81         if((scroll_dir & LV_DIR_RIGHT)  == 0 && diff_x < 0) diff_x = 0;
     82         if((scroll_dir & LV_DIR_TOP)    == 0 && diff_y > 0) diff_y = 0;
     83         if((scroll_dir & LV_DIR_BOTTOM) == 0 && diff_y < 0) diff_y = 0;
     84 
     85         /*Respect the scroll limit area*/
     86         scroll_limit_diff(proc, &diff_x, &diff_y);
     87 
     88         lv_obj_scroll_by(scroll_obj, diff_x, diff_y, LV_ANIM_OFF);
     89         proc->types.pointer.scroll_sum.x += diff_x;
     90         proc->types.pointer.scroll_sum.y += diff_y;
     91     }
     92 }
     93 
     94 
     95 void _lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc)
     96 {
     97     lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
     98     if(scroll_obj == NULL) return;
     99     if(proc->types.pointer.scroll_dir == LV_DIR_NONE) return;
    100 
    101 
    102     lv_indev_t * indev_act = lv_indev_get_act();
    103     lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
    104 
    105     if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_MOMENTUM) == false) {
    106         proc->types.pointer.scroll_throw_vect.y = 0;
    107         proc->types.pointer.scroll_throw_vect.x = 0;
    108     }
    109 
    110     lv_scroll_snap_t align_x = lv_obj_get_scroll_snap_x(scroll_obj);
    111     lv_scroll_snap_t align_y = lv_obj_get_scroll_snap_y(scroll_obj);
    112 
    113     if(proc->types.pointer.scroll_dir == LV_DIR_VER) {
    114         proc->types.pointer.scroll_throw_vect.x = 0;
    115         /*If no snapping "throw"*/
    116         if(align_y == LV_SCROLL_SNAP_NONE) {
    117             proc->types.pointer.scroll_throw_vect.y =
    118                 proc->types.pointer.scroll_throw_vect.y * (100 - scroll_throw) / 100;
    119 
    120             lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
    121             lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
    122 
    123             proc->types.pointer.scroll_throw_vect.y = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.y, st, sb,
    124                                                                    LV_DIR_VER);
    125 
    126             lv_obj_scroll_by(scroll_obj, 0, proc->types.pointer.scroll_throw_vect.y, LV_ANIM_OFF);
    127         }
    128         /*With snapping find the nearest snap point and scroll there*/
    129         else {
    130             lv_coord_t diff_y = scroll_throw_predict_y(proc);
    131             proc->types.pointer.scroll_throw_vect.y = 0;
    132             scroll_limit_diff(proc, NULL, &diff_y);
    133             lv_coord_t y = find_snap_point_y(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_y);
    134             lv_obj_scroll_by(scroll_obj, 0, diff_y + y, LV_ANIM_ON);
    135         }
    136     }
    137     else if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
    138         proc->types.pointer.scroll_throw_vect.y = 0;
    139         /*If no snapping "throw"*/
    140         if(align_x == LV_SCROLL_SNAP_NONE) {
    141             proc->types.pointer.scroll_throw_vect.x =
    142                 proc->types.pointer.scroll_throw_vect.x * (100 - scroll_throw) / 100;
    143 
    144             lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
    145             lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
    146 
    147             proc->types.pointer.scroll_throw_vect.x = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.x, sl, sr,
    148                                                                    LV_DIR_HOR);
    149 
    150             lv_obj_scroll_by(scroll_obj, proc->types.pointer.scroll_throw_vect.x, 0, LV_ANIM_OFF);
    151         }
    152         /*With snapping find the nearest snap point and scroll there*/
    153         else {
    154             lv_coord_t diff_x = scroll_throw_predict_x(proc);
    155             proc->types.pointer.scroll_throw_vect.x = 0;
    156             scroll_limit_diff(proc, &diff_x, NULL);
    157             lv_coord_t x = find_snap_point_x(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_x);
    158             lv_obj_scroll_by(scroll_obj, x + diff_x, 0, LV_ANIM_ON);
    159         }
    160     }
    161 
    162     /*Check if the scroll has finished*/
    163     if(proc->types.pointer.scroll_throw_vect.x == 0 && proc->types.pointer.scroll_throw_vect.y == 0) {
    164         /*Revert if scrolled in*/
    165         /*If vertically scrollable and not controlled by snap*/
    166         if(align_y == LV_SCROLL_SNAP_NONE) {
    167             lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
    168             lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
    169             if(st > 0 || sb > 0) {
    170                 if(st < 0) {
    171                     lv_obj_scroll_by(scroll_obj, 0, st, LV_ANIM_ON);
    172                 }
    173                 else if(sb < 0) {
    174                     lv_obj_scroll_by(scroll_obj, 0, -sb, LV_ANIM_ON);
    175                 }
    176             }
    177         }
    178 
    179         /*If horizontally scrollable and not controlled by snap*/
    180         if(align_x == LV_SCROLL_SNAP_NONE) {
    181             lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
    182             lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
    183             if(sl > 0 || sr > 0) {
    184                 if(sl < 0) {
    185                     lv_obj_scroll_by(scroll_obj, sl, 0, LV_ANIM_ON);
    186                 }
    187                 else if(sr < 0) {
    188                     lv_obj_scroll_by(scroll_obj, -sr, 0, LV_ANIM_ON);
    189                 }
    190             }
    191         }
    192 
    193         lv_event_send(scroll_obj, LV_EVENT_SCROLL_END, indev_act);
    194         if(proc->reset_query) return;
    195 
    196         proc->types.pointer.scroll_dir = LV_DIR_NONE;
    197         proc->types.pointer.scroll_obj = NULL;
    198     }
    199 }
    200 
    201 /**
    202  * Predict where would a scroll throw end
    203  * @param indev pointer to an input device
    204  * @param dir `LV_DIR_VER` or `LV_DIR_HOR`
    205  * @return the difference compared to the current position when the throw would be finished
    206  */
    207 lv_coord_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir)
    208 {
    209     if(indev == NULL) return 0;
    210     lv_coord_t v;
    211     switch(dir) {
    212         case LV_DIR_VER:
    213             v = indev->proc.types.pointer.scroll_throw_vect_ori.y;
    214             break;
    215         case LV_DIR_HOR:
    216             v = indev->proc.types.pointer.scroll_throw_vect_ori.x;
    217             break;
    218         default:
    219             return 0;
    220     }
    221 
    222     lv_coord_t scroll_throw = indev->driver->scroll_throw;
    223     lv_coord_t sum = 0;
    224     while(v) {
    225         sum += v;
    226         v = v * (100 - scroll_throw) / 100;
    227     }
    228 
    229     return sum;
    230 }
    231 
    232 void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p)
    233 {
    234     p->x = find_snap_point_x(obj, obj->coords.x1, obj->coords.x2, 0);
    235     p->y = find_snap_point_y(obj, obj->coords.y1, obj->coords.y2, 0);
    236 }
    237 
    238 /**********************
    239  *   STATIC FUNCTIONS
    240  **********************/
    241 
    242 static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc)
    243 {
    244     lv_obj_t * obj_candidate = NULL;
    245     lv_dir_t dir_candidate = LV_DIR_NONE;
    246     lv_indev_t * indev_act = lv_indev_get_act();
    247     lv_coord_t scroll_limit = indev_act->driver->scroll_limit;
    248 
    249     /*Go until find a scrollable object in the current direction
    250      *More precisely:
    251      * 1. Check the pressed object and all of its ancestors and try to find an object which is scrollable
    252      * 2. Scrollable means it has some content out of its area
    253      * 3. If an object can be scrolled into the current direction then use it ("real match"")
    254      * 4. If can be scrolled on the current axis (hor/ver) save it as candidate (at least show an elastic scroll effect)
    255      * 5. Use the last candidate. Always the "deepest" parent or the object from point 3*/
    256     lv_obj_t * obj_act = proc->types.pointer.act_obj;
    257 
    258     /*Decide if it's a horizontal or vertical scroll*/
    259     bool hor_en = false;
    260     bool ver_en = false;
    261     if(LV_ABS(proc->types.pointer.scroll_sum.x) > LV_ABS(proc->types.pointer.scroll_sum.y)) {
    262         hor_en = true;
    263     }
    264     else {
    265         ver_en = true;
    266     }
    267 
    268     while(obj_act) {
    269         if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLLABLE) == false) {
    270             /*If this object don't want to chain the scroll to the parent stop searching*/
    271             if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_HOR) == false && hor_en) break;
    272             if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_VER) == false && ver_en) break;
    273 
    274             obj_act = lv_obj_get_parent(obj_act);
    275             continue;
    276         }
    277 
    278         /*Consider both up-down or left/right scrollable according to the current direction*/
    279         bool up_en = ver_en;
    280         bool down_en = ver_en;
    281         bool left_en = hor_en;
    282         bool right_en = hor_en;
    283 
    284         /*The object might have disabled some directions.*/
    285         lv_dir_t scroll_dir = lv_obj_get_scroll_dir(obj_act);
    286         if((scroll_dir & LV_DIR_LEFT) == 0) left_en = false;
    287         if((scroll_dir & LV_DIR_RIGHT) == 0) right_en = false;
    288         if((scroll_dir & LV_DIR_TOP) == 0) up_en = false;
    289         if((scroll_dir & LV_DIR_BOTTOM) == 0) down_en = false;
    290 
    291         /*The object is scrollable to a direction if its content overflow in that direction.*/
    292         lv_coord_t st = lv_obj_get_scroll_top(obj_act);
    293         lv_coord_t sb = lv_obj_get_scroll_bottom(obj_act);
    294         lv_coord_t sl = lv_obj_get_scroll_left(obj_act);
    295         lv_coord_t sr = lv_obj_get_scroll_right(obj_act);
    296 
    297         /*If this object is scrollable into the current scroll direction then save it as a candidate.
    298          *It's important only to be scrollable on the current axis (hor/ver) because if the scroll
    299          *is propagated to this object it can show at least elastic scroll effect.
    300          *But if not hor/ver scrollable do not scroll it at all (so it's not a good candidate)*/
    301         if((st > 0 || sb > 0)  &&
    302            ((up_en    && proc->types.pointer.scroll_sum.y >=   scroll_limit) ||
    303             (down_en  && proc->types.pointer.scroll_sum.y <= - scroll_limit))) {
    304             obj_candidate = obj_act;
    305             dir_candidate = LV_DIR_VER;
    306         }
    307 
    308         if((sl > 0 || sr > 0)  &&
    309            ((left_en   && proc->types.pointer.scroll_sum.x >=   scroll_limit) ||
    310             (right_en  && proc->types.pointer.scroll_sum.x <= - scroll_limit))) {
    311             obj_candidate = obj_act;
    312             dir_candidate = LV_DIR_HOR;
    313         }
    314 
    315         if(st <= 0) up_en = false;
    316         if(sb <= 0) down_en = false;
    317         if(sl <= 0) left_en = false;
    318         if(sr <= 0) right_en = false;
    319 
    320         /*If the object really can be scrolled into the current direction the use it.*/
    321         if((left_en  && proc->types.pointer.scroll_sum.x >=   scroll_limit) ||
    322            (right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit) ||
    323            (up_en    && proc->types.pointer.scroll_sum.y >=   scroll_limit) ||
    324            (down_en  && proc->types.pointer.scroll_sum.y <= - scroll_limit)) {
    325             proc->types.pointer.scroll_dir = hor_en ? LV_DIR_HOR : LV_DIR_VER;
    326             break;
    327         }
    328 
    329         /*If this object don't want to chain the scroll to the parent stop searching*/
    330         if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_HOR) == false && hor_en) break;
    331         if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_VER) == false && ver_en) break;
    332 
    333         /*Try the parent*/
    334         obj_act = lv_obj_get_parent(obj_act);
    335     }
    336 
    337     /*Use the last candidate*/
    338     if(obj_candidate) {
    339         proc->types.pointer.scroll_dir = dir_candidate;
    340         proc->types.pointer.scroll_obj = obj_candidate;
    341         proc->types.pointer.scroll_sum.x = 0;
    342         proc->types.pointer.scroll_sum.y = 0;
    343     }
    344 
    345     return obj_candidate;
    346 }
    347 
    348 static void init_scroll_limits(_lv_indev_proc_t * proc)
    349 {
    350     lv_obj_t * obj = proc->types.pointer.scroll_obj;
    351     /*If there no STOP allow scrolling anywhere*/
    352     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ONE) == false) {
    353         lv_area_set(&proc->types.pointer.scroll_area, LV_COORD_MIN, LV_COORD_MIN, LV_COORD_MAX, LV_COORD_MAX);
    354     }
    355     /*With STOP limit the scrolling to the perv and next snap point*/
    356     else {
    357         switch(lv_obj_get_scroll_snap_y(obj)) {
    358             case LV_SCROLL_SNAP_START:
    359                 proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y1 + 1, LV_COORD_MAX, 0);
    360                 proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y1 - 1, 0);
    361                 break;
    362             case LV_SCROLL_SNAP_END:
    363                 proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y2, LV_COORD_MAX, 0);
    364                 proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y2, 0);
    365                 break;
    366             case LV_SCROLL_SNAP_CENTER: {
    367                     lv_coord_t y_mid = obj->coords.y1 + lv_area_get_height(&obj->coords) / 2;
    368                     proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, y_mid + 1, LV_COORD_MAX, 0);
    369                     proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, y_mid - 1, 0);
    370                     break;
    371                 }
    372             default:
    373                 proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
    374                 proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
    375                 break;
    376         }
    377 
    378         switch(lv_obj_get_scroll_snap_x(obj)) {
    379             case LV_SCROLL_SNAP_START:
    380                 proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x1, LV_COORD_MAX, 0);
    381                 proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x1, 0);
    382                 break;
    383             case LV_SCROLL_SNAP_END:
    384                 proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x2, LV_COORD_MAX, 0);
    385                 proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x2, 0);
    386                 break;
    387             case LV_SCROLL_SNAP_CENTER: {
    388                     lv_coord_t x_mid = obj->coords.x1 + lv_area_get_width(&obj->coords) / 2;
    389                     proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, x_mid + 1, LV_COORD_MAX, 0);
    390                     proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, x_mid - 1, 0);
    391                     break;
    392                 }
    393             default:
    394                 proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
    395                 proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
    396                 break;
    397         }
    398     }
    399 
    400     /*Allow scrolling on the edges. It will be reverted to the edge due to snapping anyway*/
    401     if(proc->types.pointer.scroll_area.x1 == 0) proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
    402     if(proc->types.pointer.scroll_area.x2 == 0) proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
    403     if(proc->types.pointer.scroll_area.y1 == 0) proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
    404     if(proc->types.pointer.scroll_area.y2 == 0) proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
    405 }
    406 
    407 /**
    408  * Search for snap point in the `min` - `max` range.
    409  * @param obj the object on which snap point should be found
    410  * @param min ignore snap points smaller than this. (Absolute coordinate)
    411  * @param max ignore snap points greater than this. (Absolute coordinate)
    412  * @param ofs offset to snap points. Useful the get a snap point in an imagined case
    413  *            what if children are already moved by this value
    414  * @return the distance of the snap point.
    415  */
    416 static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
    417 {
    418     lv_scroll_snap_t align = lv_obj_get_scroll_snap_x(obj);
    419     if(align == LV_SCROLL_SNAP_NONE) return 0;
    420 
    421     lv_coord_t dist = LV_COORD_MAX;
    422 
    423     lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
    424     lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
    425 
    426     uint32_t i;
    427     uint32_t child_cnt = lv_obj_get_child_cnt(obj);
    428     for(i = 0; i < child_cnt; i++) {
    429         lv_obj_t * child = obj->spec_attr->children[i];
    430         if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
    431         if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
    432             lv_coord_t x_child = 0;
    433             lv_coord_t x_parent = 0;
    434             switch(align) {
    435                 case LV_SCROLL_SNAP_START:
    436                     x_child = child->coords.x1;
    437                     x_parent = obj->coords.x1 + pad_left;
    438                     break;
    439                 case LV_SCROLL_SNAP_END:
    440                     x_child = child->coords.x2;
    441                     x_parent = obj->coords.x2 - pad_right;
    442                     break;
    443                 case LV_SCROLL_SNAP_CENTER:
    444                     x_child = child->coords.x1 + lv_area_get_width(&child->coords) / 2;
    445                     x_parent = obj->coords.x1 + pad_left + (lv_area_get_width(&obj->coords) - pad_left - pad_right) / 2;
    446                     break;
    447                 default:
    448                     continue;
    449             }
    450 
    451             x_child += ofs;
    452             if(x_child >= min && x_child <= max) {
    453                 lv_coord_t x = x_child -  x_parent;
    454                 if(LV_ABS(x) < LV_ABS(dist)) dist = x;
    455             }
    456         }
    457     }
    458 
    459     return dist == LV_COORD_MAX ? 0 : -dist;
    460 }
    461 
    462 /**
    463  * Search for snap point in the `min` - `max` range.
    464  * @param obj the object on which snap point should be found
    465  * @param min ignore snap points smaller than this. (Absolute coordinate)
    466  * @param max ignore snap points greater than this. (Absolute coordinate)
    467  * @param ofs offset to snap points. Useful to get a snap point in an imagined case
    468  *            what if children are already moved by this value
    469  * @return the distance of the snap point.
    470  */
    471 static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
    472 {
    473     lv_scroll_snap_t align = lv_obj_get_scroll_snap_y(obj);
    474     if(align == LV_SCROLL_SNAP_NONE) return 0;
    475 
    476     lv_coord_t dist = LV_COORD_MAX;
    477 
    478     lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
    479     lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
    480 
    481     uint32_t i;
    482     uint32_t child_cnt = lv_obj_get_child_cnt(obj);
    483     for(i = 0; i < child_cnt; i++) {
    484         lv_obj_t * child = obj->spec_attr->children[i];
    485         if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
    486         if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
    487             lv_coord_t y_child = 0;
    488             lv_coord_t y_parent = 0;
    489             switch(align) {
    490                 case LV_SCROLL_SNAP_START:
    491                     y_child = child->coords.y1;
    492                     y_parent = obj->coords.y1 + pad_top;
    493                     break;
    494                 case LV_SCROLL_SNAP_END:
    495                     y_child = child->coords.y2;
    496                     y_parent = obj->coords.y2 - pad_bottom;
    497                     break;
    498                 case LV_SCROLL_SNAP_CENTER:
    499                     y_child = child->coords.y1 + lv_area_get_height(&child->coords) / 2;
    500                     y_parent = obj->coords.y1 + pad_top + (lv_area_get_height(&obj->coords) - pad_top - pad_bottom) / 2;
    501                     break;
    502                 default:
    503                     continue;
    504             }
    505 
    506             y_child += ofs;
    507             if(y_child >= min && y_child <= max) {
    508                 lv_coord_t y = y_child -  y_parent;
    509                 if(LV_ABS(y) < LV_ABS(dist)) dist = y;
    510             }
    511         }
    512     }
    513 
    514     return dist == LV_COORD_MAX ? 0 : -dist;
    515 }
    516 
    517 static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y)
    518 {
    519     if(diff_y) {
    520         if(proc->types.pointer.scroll_sum.y + *diff_y < proc->types.pointer.scroll_area.y1) {
    521             *diff_y = proc->types.pointer.scroll_area.y1 - proc->types.pointer.scroll_sum.y;
    522         }
    523 
    524         if(proc->types.pointer.scroll_sum.y + *diff_y > proc->types.pointer.scroll_area.y2) {
    525             *diff_y = proc->types.pointer.scroll_area.y2 - proc->types.pointer.scroll_sum.y;
    526         }
    527     }
    528 
    529     if(diff_x) {
    530         if(proc->types.pointer.scroll_sum.x + *diff_x < proc->types.pointer.scroll_area.x1) {
    531             *diff_x = proc->types.pointer.scroll_area.x1 - proc->types.pointer.scroll_sum.x;
    532         }
    533 
    534         if(proc->types.pointer.scroll_sum.x + *diff_x > proc->types.pointer.scroll_area.x2) {
    535             *diff_x = proc->types.pointer.scroll_area.x2 - proc->types.pointer.scroll_sum.x;
    536         }
    537     }
    538 }
    539 
    540 
    541 
    542 static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc)
    543 {
    544     lv_coord_t y = proc->types.pointer.scroll_throw_vect.y;
    545     lv_coord_t move = 0;
    546 
    547     lv_indev_t * indev_act = lv_indev_get_act();
    548     lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
    549 
    550     while(y) {
    551         move += y;
    552         y = y * (100 - scroll_throw) / 100;
    553     }
    554     return move;
    555 }
    556 
    557 
    558 static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc)
    559 {
    560     lv_coord_t x = proc->types.pointer.scroll_throw_vect.x;
    561     lv_coord_t move = 0;
    562 
    563     lv_indev_t * indev_act = lv_indev_get_act();
    564     lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
    565 
    566     while(x) {
    567         move += x;
    568         x = x * (100 - scroll_throw) / 100;
    569     }
    570     return move;
    571 }
    572 
    573 static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end,
    574                                lv_dir_t dir)
    575 {
    576     if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_ELASTIC)) {
    577         /*If there is snapping in the current direction don't use the elastic factor because
    578          *it's natural that the first and last items are scrolled (snapped) in.*/
    579         lv_scroll_snap_t snap;
    580         snap = dir == LV_DIR_HOR ? lv_obj_get_scroll_snap_x(scroll_obj) : lv_obj_get_scroll_snap_y(scroll_obj);
    581 
    582         lv_obj_t * act_obj = lv_indev_get_obj_act();
    583         lv_coord_t snap_point = 0;
    584         lv_coord_t act_obj_point = 0;
    585 
    586         if(dir == LV_DIR_HOR) {
    587             lv_coord_t pad_left = lv_obj_get_style_pad_left(scroll_obj, LV_PART_MAIN);
    588             lv_coord_t pad_right = lv_obj_get_style_pad_right(scroll_obj, LV_PART_MAIN);
    589 
    590             switch(snap) {
    591                 case LV_SCROLL_SNAP_CENTER:
    592                     snap_point = pad_left + (lv_area_get_width(&scroll_obj->coords) - pad_left - pad_right) / 2 + scroll_obj->coords.x1;
    593                     act_obj_point = lv_area_get_width(&act_obj->coords) / 2 + act_obj->coords.x1;
    594                     break;
    595                 case LV_SCROLL_SNAP_START:
    596                     snap_point = scroll_obj->coords.x1 + pad_left;
    597                     act_obj_point = act_obj->coords.x1;
    598                     break;
    599                 case LV_SCROLL_SNAP_END:
    600                     snap_point = scroll_obj->coords.x2 - pad_right;
    601                     act_obj_point = act_obj->coords.x2;
    602                     break;
    603             }
    604         }
    605         else {
    606             lv_coord_t pad_top = lv_obj_get_style_pad_top(scroll_obj, LV_PART_MAIN);
    607             lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(scroll_obj, LV_PART_MAIN);
    608 
    609             switch(snap) {
    610                 case LV_SCROLL_SNAP_CENTER:
    611                     snap_point = pad_top + (lv_area_get_height(&scroll_obj->coords) - pad_top - pad_bottom) / 2 + scroll_obj->coords.y1;
    612                     act_obj_point = lv_area_get_height(&act_obj->coords) / 2 + act_obj->coords.y1;
    613                     break;
    614                 case LV_SCROLL_SNAP_START:
    615                     snap_point = scroll_obj->coords.y1 + pad_top;
    616                     act_obj_point = act_obj->coords.y1;
    617                     break;
    618                 case LV_SCROLL_SNAP_END:
    619                     snap_point = scroll_obj->coords.y2 - pad_bottom;
    620                     act_obj_point = act_obj->coords.y2;
    621                     break;
    622             }
    623         }
    624 
    625         if(scroll_end < 0) {
    626             if(snap != LV_SCROLL_SNAP_NONE && act_obj_point > snap_point) return diff;
    627 
    628             /*Rounding*/
    629             if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
    630             if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
    631             return diff / ELASTIC_SLOWNESS_FACTOR;
    632         }
    633         else if(scroll_start < 0) {
    634             if(snap != LV_SCROLL_SNAP_NONE && act_obj_point < snap_point) return diff;
    635 
    636             /*Rounding*/
    637             if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
    638             if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
    639             return diff / ELASTIC_SLOWNESS_FACTOR;
    640         }
    641     }
    642     else {
    643         /*Scroll back to the boundary if required*/
    644         if(scroll_end + diff < 0) diff = - scroll_end;
    645         if(scroll_start - diff < 0) diff = scroll_start;
    646     }
    647 
    648     return diff;
    649 }
    650 
    651