1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-14 06:42:58 +08:00

fix(scroll): handle scroll chain with snapping too (#7491)

This commit is contained in:
Gabor Kiss-Vamosi 2025-01-10 02:08:51 +01:00 committed by GitHub
parent 20bfb4855b
commit 51a3d93852
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -30,6 +30,7 @@ static int32_t find_snap_point_y(const lv_obj_t * obj, int32_t min, int32_t max,
static void scroll_limit_diff(lv_indev_t * indev, int32_t * diff_x, int32_t * diff_y);
static int32_t elastic_diff(lv_obj_t * scroll_obj, int32_t diff, int32_t scroll_start, int32_t scroll_end,
lv_dir_t dir);
static void has_more_snap_points(lv_obj_t * scroll_obj, lv_dir_t dir, bool * has_start_snap, bool * has_end_snap);
/**********************
* STATIC VARIABLES
@ -345,58 +346,43 @@ lv_obj_t * lv_indev_find_scroll_obj(lv_indev_t * indev)
if((scroll_dir & LV_DIR_TOP) == 0) up_en = false;
if((scroll_dir & LV_DIR_BOTTOM) == 0) down_en = false;
/*The object is scrollable to a direction if its content overflow in that direction.
*If there are at least 2 snapable children always assume
*scrolling to allow scrolling to the other child*/
uint32_t snap_cnt = 0;
/*Horizontal scroll*/
int32_t sl = 0;
int32_t sr = 0;
lv_scroll_snap_t snap_x = lv_obj_get_scroll_snap_x(obj_act);
if(snap_x != LV_SCROLL_SNAP_NONE) {
uint32_t child_cnt = lv_obj_get_child_count(obj_act);
uint32_t i;
for(i = 0; i < child_cnt; i++) {
if(lv_obj_has_flag(lv_obj_get_child(obj_act, i), LV_OBJ_FLAG_SNAPPABLE)) {
snap_cnt++;
if(snap_cnt == 2) {
sl = 1; /*Assume scrolling*/
sr = 1;
break;
}
}
}
}
if(snap_x == LV_SCROLL_SNAP_NONE || snap_cnt < 2) {
if(snap_x == LV_SCROLL_SNAP_NONE) {
sl = lv_obj_get_scroll_left(obj_act);
sr = lv_obj_get_scroll_right(obj_act);
}
else {
bool has_start_snap;
bool has_end_snap;
has_more_snap_points(obj_act, LV_DIR_HOR, &has_start_snap, &has_end_snap);
/*Assume scrolling is there are more snap point
*Assumed scroll in if there are NO more nap points*/
sl = has_start_snap ? 1 : -1;
sr = has_end_snap ? 1 : -1;
}
/*Vertical scroll*/
snap_cnt = 0;
int32_t st = 0;
int32_t sb = 0;
lv_scroll_snap_t snap_y = lv_obj_get_scroll_snap_y(obj_act);
if(snap_y != LV_SCROLL_SNAP_NONE) {
uint32_t child_cnt = lv_obj_get_child_count(obj_act);
uint32_t i;
for(i = 0; i < child_cnt; i++) {
if(lv_obj_has_flag(lv_obj_get_child(obj_act, i), LV_OBJ_FLAG_SNAPPABLE)) {
snap_cnt++;
if(snap_cnt == 2) {
st = 1; /*Assume scrolling*/
sb = 1;
break;
}
}
}
}
if(snap_y == LV_SCROLL_SNAP_NONE || snap_cnt < 2) {
if(snap_y == LV_SCROLL_SNAP_NONE) {
st = lv_obj_get_scroll_top(obj_act);
sb = lv_obj_get_scroll_bottom(obj_act);
}
else {
bool has_start_snap;
bool has_end_snap;
has_more_snap_points(obj_act, LV_DIR_VER, &has_start_snap, &has_end_snap);
/*Assume scrolling is there are more snap point
*Assumed scroll in if there are NO more nap points*/
st = has_start_snap ? 1 : -1;
sb = has_end_snap ? 1 : -1;
}
/*If this object is scrollable into the current scroll direction then save it as a candidate.
*It's important only to be scrollable on the current axis (hor/ver) because if the scroll
@ -671,80 +657,31 @@ static int32_t elastic_diff(lv_obj_t * scroll_obj, int32_t diff, int32_t scroll_
}
/*Handle elastic scrolling*/
else {
/*If there is snapping in the current direction don't use the elastic factor because
*it's natural that the first and last items are scrolled (snapped) in.*/
lv_scroll_snap_t snap;
snap = dir == LV_DIR_HOR ? lv_obj_get_scroll_snap_x(scroll_obj) : lv_obj_get_scroll_snap_y(scroll_obj);
bool no_more_start_snap = false;
bool no_more_end_snap = false;
/*Without snapping just scale down the diff is scrolled out*/
/*Without snapping just scale down the diff when scrolled out*/
if(snap == LV_SCROLL_SNAP_NONE) {
if(scroll_end < 0 || scroll_start < 0) {
/*Rounding*/
if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
diff = diff / ELASTIC_SLOWNESS_FACTOR;
}
}
/*With snapping the widget is scrolled out if there are no more snap points*/
else {
if(dir == LV_DIR_HOR) {
int32_t x = 0;
switch(snap) {
case LV_SCROLL_SNAP_CENTER: {
int32_t pad_left = lv_obj_get_style_pad_left(scroll_obj, 0);
int32_t pad_right = lv_obj_get_style_pad_right(scroll_obj, 0);
x = scroll_obj->coords.x1;
x += (lv_area_get_width(&scroll_obj->coords) - pad_left - pad_right) / 2;
x += pad_left;
}
break;
case LV_SCROLL_SNAP_START:
x = scroll_obj->coords.x1 + lv_obj_get_style_pad_left(scroll_obj, 0);
break;
case LV_SCROLL_SNAP_END:
x = scroll_obj->coords.x2 - lv_obj_get_style_pad_right(scroll_obj, 0);
break;
default:
break;
}
int32_t d;
d = find_snap_point_x(scroll_obj, x + 1, LV_COORD_MAX, 0);
if(d == LV_COORD_MAX) no_more_end_snap = true;
d = find_snap_point_x(scroll_obj, LV_COORD_MIN, x, 0);
if(d == LV_COORD_MAX) no_more_start_snap = true;
return diff / ELASTIC_SLOWNESS_FACTOR;
}
else {
int32_t y = 0;
switch(snap) {
case LV_SCROLL_SNAP_CENTER: {
int32_t pad_top = lv_obj_get_style_pad_top(scroll_obj, 0);
int32_t pad_bottom = lv_obj_get_style_pad_bottom(scroll_obj, 0);
y = scroll_obj->coords.y1;
y += (lv_area_get_height(&scroll_obj->coords) - pad_top - pad_bottom) / 2;
y += pad_top;
}
break;
case LV_SCROLL_SNAP_START:
y = scroll_obj->coords.y1 + lv_obj_get_style_pad_top(scroll_obj, 0);
break;
case LV_SCROLL_SNAP_END:
y = scroll_obj->coords.y2 - lv_obj_get_style_pad_bottom(scroll_obj, 0);
break;
default:
break;
}
int32_t d;
d = find_snap_point_y(scroll_obj, y, LV_COORD_MAX, 0);
if(d == LV_COORD_MAX) no_more_end_snap = true;
d = find_snap_point_y(scroll_obj, LV_COORD_MIN, y, 0);
if(d == LV_COORD_MAX) no_more_start_snap = true;
return diff;
}
}
if(no_more_start_snap || no_more_end_snap) {
/*With snapping the widget is scrolled out if there are no more snap points
*at least in one direction (start or end)*/
bool has_start_snap;
bool has_end_snap;
has_more_snap_points(scroll_obj, dir, &has_start_snap, &has_end_snap);
if(!has_start_snap || !has_end_snap) {
/*Rounding*/
if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
return diff / ELASTIC_SLOWNESS_FACTOR;
@ -756,3 +693,76 @@ static int32_t elastic_diff(lv_obj_t * scroll_obj, int32_t diff, int32_t scroll_
return diff;
}
/**
* Tell is there are more snap point in a given direction considering snap position.
* There is a snap point if there is a snapanble object in the given direction
* @param scroll_obj the object on which snap points should be found
* @param dir LV_DIR_HOR or LV_DIR_VER
* @param has_start_snap true: there is snap point in the start direction (top or left depending on dir)
* @param has_end_snap true: there is snap point in the end direction (bottom or right depending on dir)
* @note snap points will be searched relative to the
* center point in case of LV_SCROLL_SNAP_CENTER
* start point (top or left) in case of LV_SCROLL_SNAP_START
* end point (bottom or right) in case of LV_SCROLL_SNAP_END
*/
static void has_more_snap_points(lv_obj_t * scroll_obj, lv_dir_t dir, bool * has_start_snap, bool * has_end_snap)
{
*has_start_snap = true;
*has_end_snap = true;
lv_scroll_snap_t snap;
snap = dir == LV_DIR_HOR ? lv_obj_get_scroll_snap_x(scroll_obj) : lv_obj_get_scroll_snap_y(scroll_obj);
if(dir == LV_DIR_HOR) {
int32_t x = 0;
switch(snap) {
case LV_SCROLL_SNAP_CENTER: {
int32_t pad_left = lv_obj_get_style_pad_left(scroll_obj, 0);
int32_t pad_right = lv_obj_get_style_pad_right(scroll_obj, 0);
x = scroll_obj->coords.x1;
x += (lv_area_get_width(&scroll_obj->coords) - pad_left - pad_right) / 2;
x += pad_left;
}
break;
case LV_SCROLL_SNAP_START:
x = scroll_obj->coords.x1 + lv_obj_get_style_pad_left(scroll_obj, 0);
break;
case LV_SCROLL_SNAP_END:
x = scroll_obj->coords.x2 - lv_obj_get_style_pad_right(scroll_obj, 0);
break;
default:
break;
}
int32_t d;
d = find_snap_point_x(scroll_obj, x + 1, LV_COORD_MAX, 0);
if(d == LV_COORD_MAX) *has_end_snap = false;
d = find_snap_point_x(scroll_obj, LV_COORD_MIN, x - 1, 0);
if(d == LV_COORD_MAX) *has_start_snap = false;
}
else {
int32_t y = 0;
switch(snap) {
case LV_SCROLL_SNAP_CENTER: {
int32_t pad_top = lv_obj_get_style_pad_top(scroll_obj, 0);
int32_t pad_bottom = lv_obj_get_style_pad_bottom(scroll_obj, 0);
y = scroll_obj->coords.y1;
y += (lv_area_get_height(&scroll_obj->coords) - pad_top - pad_bottom) / 2;
y += pad_top;
}
break;
case LV_SCROLL_SNAP_START:
y = scroll_obj->coords.y1 + lv_obj_get_style_pad_top(scroll_obj, 0);
break;
case LV_SCROLL_SNAP_END:
y = scroll_obj->coords.y2 - lv_obj_get_style_pad_bottom(scroll_obj, 0);
break;
default:
break;
}
int32_t d;
d = find_snap_point_y(scroll_obj, y + 1, LV_COORD_MAX, 0);
if(d == LV_COORD_MAX) *has_end_snap = false;
d = find_snap_point_y(scroll_obj, LV_COORD_MIN, y - 1, 0);
if(d == LV_COORD_MAX) *has_start_snap = false;
}
}