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