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

scroll: add animation and elastic scroll

This commit is contained in:
Gabor Kiss-Vamosi 2020-07-02 13:04:44 +02:00
parent e7648f1c00
commit b091b676fe
3 changed files with 179 additions and 94 deletions

View File

@ -1245,15 +1245,20 @@ static void indev_click_focus(lv_indev_proc_t * proc)
*/ */
static void indev_drag(lv_indev_proc_t * proc) static void indev_drag(lv_indev_proc_t * proc)
{ {
lv_obj_t * drag_obj = get_dragged_obj(proc->types.pointer.act_obj);
bool drag_just_started = false;
if(drag_obj == NULL) return; bool scrollable = true;
if(lv_obj_get_drag(drag_obj) == false) return; /*Get which object to drad/scroll*/
lv_obj_t * target_obj = get_dragged_obj(proc->types.pointer.act_obj);
if(target_obj == NULL) return;
lv_drag_dir_t dirs = LV_DRAG_DIR_VER; //scrollable ? lv_obj_get_scroll_dir(target_obj) : lv_obj_get_drag_dir(target_obj);
lv_drag_dir_t allowed_dirs = lv_obj_get_drag_dir(drag_obj); bool just_started = false;
/*Get the coordinates of the object and modify them*/
lv_coord_t act_x = lv_obj_get_x(target_obj);
lv_coord_t act_y = lv_obj_get_y(target_obj);
/*Count the movement by drag*/ /*Count the movement by drag*/
if(proc->types.pointer.drag_limit_out == 0) { if(proc->types.pointer.drag_limit_out == 0) {
@ -1263,15 +1268,15 @@ static void indev_drag(lv_indev_proc_t * proc)
/*Enough move?*/ /*Enough move?*/
bool hor_en = false; bool hor_en = false;
bool ver_en = false; bool ver_en = false;
if(allowed_dirs == LV_DRAG_DIR_HOR || allowed_dirs == LV_DRAG_DIR_BOTH) { if(dirs == LV_DRAG_DIR_HOR || dirs == LV_DRAG_DIR_BOTH) {
hor_en = true; hor_en = true;
} }
if(allowed_dirs == LV_DRAG_DIR_VER || allowed_dirs == LV_DRAG_DIR_BOTH) { if(dirs == LV_DRAG_DIR_VER || dirs == LV_DRAG_DIR_BOTH) {
ver_en = true; ver_en = true;
} }
if(allowed_dirs == LV_DRAG_DIR_ONE) { if(dirs == LV_DRAG_DIR_ONE) {
if(LV_MATH_ABS(proc->types.pointer.drag_sum.x) > LV_MATH_ABS(proc->types.pointer.drag_sum.y)) { if(LV_MATH_ABS(proc->types.pointer.drag_sum.x) > LV_MATH_ABS(proc->types.pointer.drag_sum.y)) {
hor_en = true; hor_en = true;
} }
@ -1284,102 +1289,77 @@ static void indev_drag(lv_indev_proc_t * proc)
if((hor_en && LV_MATH_ABS(proc->types.pointer.drag_sum.x) >= indev_act->driver.drag_limit) || if((hor_en && LV_MATH_ABS(proc->types.pointer.drag_sum.x) >= indev_act->driver.drag_limit) ||
(ver_en && LV_MATH_ABS(proc->types.pointer.drag_sum.y) >= indev_act->driver.drag_limit)) { (ver_en && LV_MATH_ABS(proc->types.pointer.drag_sum.y) >= indev_act->driver.drag_limit)) {
proc->types.pointer.drag_limit_out = 1; proc->types.pointer.drag_limit_out = 1;
drag_just_started = true; just_started = true;
if(dirs == LV_DRAG_DIR_ONE) {
proc->types.pointer.drag_dir = hor_en ? LV_DRAG_DIR_HOR : LV_DRAG_DIR_VER;
} else {
proc->types.pointer.drag_dir = dirs;
}
/*The was no move due to drag limit. Compensate it now.*/
if(!hor_en) proc->types.pointer.drag_sum.x = 0;
if(!ver_en) proc->types.pointer.drag_sum.y = 0;
act_x += proc->types.pointer.drag_sum.x;
act_y += proc->types.pointer.drag_sum.y;
} }
} }
/*If the drag limit is exceeded handle the dragging*/ /*If the drag limit is exceeded handle the dragging*/
if(proc->types.pointer.drag_limit_out != 0) { if(proc->types.pointer.drag_limit_out != 0) {
/*Set new position if the vector is not zero*/ /*Set new position or scroll if the vector is not zero*/
if(proc->types.pointer.vect.x != 0 || proc->types.pointer.vect.y != 0) { if(proc->types.pointer.vect.x != 0 || proc->types.pointer.vect.y != 0) {
lv_coord_t prev_x = drag_obj->coords.x1; lv_coord_t prev_x = target_obj->coords.x1;
lv_coord_t prev_y = drag_obj->coords.y1; lv_coord_t prev_y = target_obj->coords.y1;
lv_coord_t prev_par_w = lv_obj_get_width(lv_obj_get_parent(drag_obj));
lv_coord_t prev_par_h = lv_obj_get_height(lv_obj_get_parent(drag_obj));
/*Get the coordinates of the object and modify them*/ /*Move the object. `drag_sum` is zerod out for *disabled direction*/
lv_coord_t act_x = lv_obj_get_x(drag_obj); if(proc->types.pointer.drag_sum.x) act_x += proc->types.pointer.vect.x;
lv_coord_t act_y = lv_obj_get_y(drag_obj); if(proc->types.pointer.drag_sum.y) act_y += proc->types.pointer.vect.y;
if(allowed_dirs == LV_DRAG_DIR_BOTH) {
if(drag_just_started) {
proc->types.pointer.drag_dir = LV_DRAG_DIR_BOTH;
act_x += proc->types.pointer.drag_sum.x;
act_y += proc->types.pointer.drag_sum.y;
}
}
else if(allowed_dirs == LV_DRAG_DIR_HOR) {
if(drag_just_started) {
proc->types.pointer.drag_dir = LV_DRAG_DIR_HOR;
proc->types.pointer.drag_sum.y = 0;
act_x += proc->types.pointer.drag_sum.x;
}
}
else if(allowed_dirs == LV_DRAG_DIR_VER) {
if(drag_just_started) {
proc->types.pointer.drag_dir = LV_DRAG_DIR_VER;
proc->types.pointer.drag_sum.x = 0;
act_y += proc->types.pointer.drag_sum.y;
}
}
else if(allowed_dirs == LV_DRAG_DIR_ONE) {
if(drag_just_started) {
if(LV_MATH_ABS(proc->types.pointer.drag_sum.x) > LV_MATH_ABS(proc->types.pointer.drag_sum.y)) {
proc->types.pointer.drag_dir = LV_DRAG_DIR_HOR;
proc->types.pointer.drag_sum.y = 0;
act_x += proc->types.pointer.drag_sum.x;
}
else {
proc->types.pointer.drag_dir = LV_DRAG_DIR_VER;
proc->types.pointer.drag_sum.x = 0;
act_y += proc->types.pointer.drag_sum.y;
}
}
}
/*Move the object*/
if(allowed_dirs == LV_DRAG_DIR_HOR ||
allowed_dirs == LV_DRAG_DIR_BOTH ||
(allowed_dirs == LV_DRAG_DIR_ONE &&
LV_MATH_ABS(proc->types.pointer.drag_sum.x) > LV_MATH_ABS(proc->types.pointer.drag_sum.y))) {
act_x += proc->types.pointer.vect.x;
}
if(allowed_dirs == LV_DRAG_DIR_VER ||
allowed_dirs == LV_DRAG_DIR_BOTH ||
(allowed_dirs == LV_DRAG_DIR_ONE &&
LV_MATH_ABS(proc->types.pointer.drag_sum.x) < LV_MATH_ABS(proc->types.pointer.drag_sum.y))) {
act_y += proc->types.pointer.vect.y;
}
uint16_t inv_buf_size = uint16_t inv_buf_size =
lv_disp_get_inv_buf_size(indev_act->driver.disp); /*Get the number of currently invalidated areas*/ lv_disp_get_inv_buf_size(indev_act->driver.disp); /*Get the number of currently invalidated areas*/
lv_obj_set_pos(drag_obj, act_x, act_y); if(scrollable) {
proc->types.pointer.drag_in_prog = 1; lv_area_t child_box;
lv_obj_get_children_box(target_obj, &child_box);
/*If the object didn't moved then clear the invalidated areas*/ lv_coord_t diff_y = proc->types.pointer.vect.y;
if(drag_obj->coords.x1 == prev_x && drag_obj->coords.y1 == prev_y) { if(target_obj->scroll.y > 0 && diff_y > 0) {
/*In a special case if the object is moved on a page and diff_y = diff_y / 2;
* the scrollable has fit == true and the object is dragged of the page then
* while its coordinate is not changing only the parent's size is reduced */
lv_coord_t act_par_w = lv_obj_get_width(lv_obj_get_parent(drag_obj));
lv_coord_t act_par_h = lv_obj_get_height(lv_obj_get_parent(drag_obj));
if(act_par_w == prev_par_w && act_par_h == prev_par_h) {
uint16_t new_inv_buf_size = lv_disp_get_inv_buf_size(indev_act->driver.disp);
_lv_disp_pop_from_inv_buf(indev_act->driver.disp, new_inv_buf_size - inv_buf_size);
} }
if(child_box.y2 < target_obj->coords.y2 && diff_y < 0) {
diff_y = diff_y / 2;
}
lv_obj_scroll_by(target_obj, 0, diff_y, LV_ANIM_OFF);
} else {
lv_obj_set_pos(target_obj, act_x, act_y);
} }
/*Set the drag in progress flag*/ proc->types.pointer.drag_in_prog = 1;
/*Send the drag begin signal on first move*/ //
if(drag_just_started) { // /*If the object didn't moved then clear the invalidated areas*/
drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_BEGIN, indev_act); // if(target_obj->coords.x1 == prev_x && target_obj->coords.y1 == prev_y) {
if(indev_reset_check(proc)) return; // /*In a special case if the object is moved on a page and
// * the scrollable has fit == true and the object is dragged of the page then
lv_event_send(drag_obj, LV_EVENT_DRAG_BEGIN, NULL); // * while its coordinate is not changing only the parent's size is reduced */
if(indev_reset_check(proc)) return; // lv_coord_t act_par_w = lv_obj_get_width(lv_obj_get_parent(target_obj));
} // lv_coord_t act_par_h = lv_obj_get_height(lv_obj_get_parent(target_obj));
// if(act_par_w == prev_par_w && act_par_h == prev_par_h) {
// uint16_t new_inv_buf_size = lv_disp_get_inv_buf_size(indev_act->driver.disp);
// _lv_disp_pop_from_inv_buf(indev_act->driver.disp, new_inv_buf_size - inv_buf_size);
// }
// }
//
// /*Set the drag in progress flag*/
// /*Send the drag begin signal on first move*/
// if(just_started) {
// target_obj->signal_cb(target_obj, LV_SIGNAL_DRAG_BEGIN, indev_act);
// if(indev_reset_check(proc)) return;
//
// lv_event_send(target_obj, LV_EVENT_DRAG_BEGIN, NULL);
// if(indev_reset_check(proc)) return;
// }
} }
} }
@ -1466,12 +1446,16 @@ static void indev_drag_throw(lv_indev_proc_t * proc)
*/ */
static lv_obj_t * get_dragged_obj(lv_obj_t * obj) static lv_obj_t * get_dragged_obj(lv_obj_t * obj)
{ {
return obj;
if(obj == NULL) return NULL; if(obj == NULL) return NULL;
lv_obj_t * drag_obj = obj; lv_obj_t * drag_obj = obj;
while(lv_obj_get_drag_parent(drag_obj) != false && drag_obj != NULL) { while(lv_obj_get_drag_parent(drag_obj) != false && drag_obj != NULL) {
drag_obj = lv_obj_get_parent(drag_obj); drag_obj = lv_obj_get_parent(drag_obj);
} }
if(lv_obj_get_drag(drag_obj) == false) return NULL;
return drag_obj; return drag_obj;
} }

View File

@ -96,6 +96,8 @@ static void trans_anim_start_cb(lv_anim_t * a);
static void trans_anim_ready_cb(lv_anim_t * a); static void trans_anim_ready_cb(lv_anim_t * a);
static void opa_scale_anim(lv_obj_t * obj, lv_anim_value_t v); static void opa_scale_anim(lv_obj_t * obj, lv_anim_value_t v);
static void fade_in_anim_ready(lv_anim_t * a); static void fade_in_anim_ready(lv_anim_t * a);
static void scroll_anim_x_cb(lv_obj_t * obj, lv_anim_value_t v);
static void scroll_anim_y_cb(lv_obj_t * obj, lv_anim_value_t v);
#endif #endif
static void lv_event_mark_deleted(lv_obj_t * obj); static void lv_event_mark_deleted(lv_obj_t * obj);
static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find); static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
@ -1074,6 +1076,7 @@ void lv_obj_scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
obj->scroll.y += y; obj->scroll.y += y;
refresh_children_position(obj, x, y); refresh_children_position(obj, x, y);
lv_obj_invalidate(obj);
} }
/** /**
* Moves all children with horizontally or vertically. * Moves all children with horizontally or vertically.
@ -1084,10 +1087,37 @@ void lv_obj_scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
*/ */
void lv_obj_scroll_by(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en) void lv_obj_scroll_by(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en)
{ {
obj->scroll.x += x;
obj->scroll.y += y;
refresh_children_position(obj, x, y); if(x == 0 && y == 0) return;
if(anim_en == LV_ANIM_ON) {
lv_disp_t * d = lv_obj_get_disp(obj);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
if(x) {
lv_anim_set_time(&a, lv_anim_speed_to_time(lv_disp_get_hor_res(d), 0, y));
lv_anim_set_values(&a, obj->scroll.x, obj->scroll.x + x);
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t) scroll_anim_x_cb);
lv_anim_start(&a);
}
if(y) {
lv_anim_set_time(&a, lv_anim_speed_to_time(lv_disp_get_ver_res(d), 0, y));
lv_anim_set_values(&a, obj->scroll.y, obj->scroll.y + y);
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t) scroll_anim_y_cb);
lv_anim_start(&a);
}
} else {
lv_obj_scroll_by_raw(obj, x, y);
}
} }
/** /**
@ -1118,7 +1148,7 @@ void lv_obj_scroll_to_x(lv_obj_t * obj, lv_coord_t x, lv_anim_enable_t anim_en)
*/ */
void lv_obj_scroll_to_y(lv_obj_t * obj, lv_coord_t y, lv_anim_enable_t anim_en) void lv_obj_scroll_to_y(lv_obj_t * obj, lv_coord_t y, lv_anim_enable_t anim_en)
{ {
// refresh_children_position(obj, x, y); lv_obj_scroll_by(obj, 0, y - obj->scroll.y, anim_en);
} }
/** /**
@ -2125,6 +2155,37 @@ void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * cords_p)
lv_area_copy(cords_p, &obj->coords); lv_area_copy(cords_p, &obj->coords);
} }
/**
* Get a bounding which includes all the the children.
* `margin` of the children also taken into account and makes the box larger (if positive)
* @param obj pointer to an object
* @param coords pointer to an area to store the coordinates
* @return LV_RES_INV: `obj` has no children and `coords` can't be set; `LV_RES_OK`: success
*/
lv_res_t lv_obj_get_children_box(const lv_obj_t * obj, lv_area_t * coords)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
coords->x1 = LV_COORD_MAX;
coords->y1 = LV_COORD_MAX;
coords->x2 = LV_COORD_MIN;
coords->y2 = LV_COORD_MIN;
lv_obj_t * child = lv_obj_get_child(obj, NULL);
if(child == NULL) return LV_RES_INV;
while(child) {
coords->x1 = LV_MATH_MIN(coords->x1, child->coords.x1 - lv_obj_get_style_margin_left(child, LV_OBJ_PART_MAIN));
coords->y1 = LV_MATH_MIN(coords->y1, child->coords.y1 - lv_obj_get_style_margin_top(child, LV_OBJ_PART_MAIN));
coords->x2 = LV_MATH_MAX(coords->x2, child->coords.x2 + lv_obj_get_style_margin_right(child, LV_OBJ_PART_MAIN));
coords->y2 = LV_MATH_MAX(coords->y2, child->coords.y2 + lv_obj_get_style_margin_bottom(child, LV_OBJ_PART_MAIN));
child = lv_obj_get_child(obj, child);
}
return LV_RES_OK;
}
/** /**
* Reduce area retried by `lv_obj_get_coords()` the get graphically usable area of an object. * Reduce area retried by `lv_obj_get_coords()` the get graphically usable area of an object.
* (Without the size of the border or other extra graphical elements) * (Without the size of the border or other extra graphical elements)
@ -2761,6 +2822,18 @@ lv_drag_dir_t lv_obj_get_drag_dir(const lv_obj_t * obj)
return obj->drag_dir; return obj->drag_dir;
} }
/**
* Get the directions an object can be scrolled
* @param obj pointer to an object
* @return bitwise OR of allowed directions an object can be dragged in
*/
lv_drag_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->scroll_dir;
}
/** /**
* Get the drag throw enable attribute of an object * Get the drag throw enable attribute of an object
* @param obj pointer to an object * @param obj pointer to an object
@ -3738,6 +3811,17 @@ static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
/*Return 'invalid' if the child change signal is not enabled*/ /*Return 'invalid' if the child change signal is not enabled*/
if(lv_obj_is_protected(obj, LV_PROTECT_CHILD_CHG) != false) res = LV_RES_INV; if(lv_obj_is_protected(obj, LV_PROTECT_CHILD_CHG) != false) res = LV_RES_INV;
} }
else if(sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED) {
lv_area_t child_box;
lv_obj_get_children_box(obj, &child_box);
if(obj->scroll.y > 0) {
lv_obj_scroll_to_y(obj, 0, LV_ANIM_ON);
}
else if(child_box.y2 < obj->coords.y2) {
lv_obj_scroll_by(obj, 0, -(child_box.y2 - obj->coords.y2), LV_ANIM_ON);
}
}
else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) { else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) {
lv_coord_t d = lv_obj_get_draw_rect_ext_pad_size(obj, LV_OBJ_PART_MAIN); lv_coord_t d = lv_obj_get_draw_rect_ext_pad_size(obj, LV_OBJ_PART_MAIN);
obj->ext_draw_pad = LV_MATH_MAX(obj->ext_draw_pad, d); obj->ext_draw_pad = LV_MATH_MAX(obj->ext_draw_pad, d);
@ -4278,6 +4362,15 @@ static void fade_in_anim_ready(lv_anim_t * a)
lv_style_remove_prop(lv_obj_get_local_style(a->var, LV_OBJ_PART_MAIN), LV_STYLE_OPA_SCALE); lv_style_remove_prop(lv_obj_get_local_style(a->var, LV_OBJ_PART_MAIN), LV_STYLE_OPA_SCALE);
} }
static void scroll_anim_x_cb(lv_obj_t * obj, lv_anim_value_t v)
{
}
static void scroll_anim_y_cb(lv_obj_t * obj, lv_anim_value_t v)
{
lv_obj_scroll_by_raw(obj, 0, v - obj->scroll.y);
}
#endif #endif
static void lv_event_mark_deleted(lv_obj_t * obj) static void lv_event_mark_deleted(lv_obj_t * obj)

View File

@ -228,7 +228,8 @@ typedef struct _lv_obj_t {
uint8_t gesture_parent : 1; /**< 1: Parent will be gesture instead*/ uint8_t gesture_parent : 1; /**< 1: Parent will be gesture instead*/
uint8_t focus_parent : 1; /**< 1: Parent will be focused instead*/ uint8_t focus_parent : 1; /**< 1: Parent will be focused instead*/
lv_drag_dir_t drag_dir : 3; /**< Which directions the object can be dragged in */ lv_drag_dir_t drag_dir : 3; /**< In which directions the object can be dragged */
lv_drag_dir_t scroll_dir : 3; /**< In which directions the object can be scrolled */
lv_bidi_dir_t base_dir : 2; /**< Base direction of texts related to this object */ lv_bidi_dir_t base_dir : 2; /**< Base direction of texts related to this object */
#if LV_USE_GROUP != 0 #if LV_USE_GROUP != 0
@ -1223,6 +1224,13 @@ bool lv_obj_get_drag(const lv_obj_t * obj);
*/ */
lv_drag_dir_t lv_obj_get_drag_dir(const lv_obj_t * obj); lv_drag_dir_t lv_obj_get_drag_dir(const lv_obj_t * obj);
/**
* Get the directions an object can be scrolled
* @param obj pointer to an object
* @return bitwise OR of allowed directions an object can be dragged in
*/
lv_drag_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj);
/** /**
* Get the drag throw enable attribute of an object * Get the drag throw enable attribute of an object
* @param obj pointer to an object * @param obj pointer to an object