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:
parent
20bfb4855b
commit
51a3d93852
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user