touchscreen: Improved kinetic scrolling in lists
rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]>
| Newsgroups | gmane.comp.systems.archos.rockbox.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit 0ca22b9a71f5a5c3b89bb3f36d08d0c7e4b6c111 Author: Aidan MacDonald <[email protected]> Date: Fri Nov 25 14:13:29 2022 +0000 touchscreen: Improved kinetic scrolling in lists Kinetic scrolling now accelerates when repeatedly swiping over the screen, so you can scroll through large lists easily without using the scrollbar. Press & hold to slow down and stop. Menu items can no longer be selected during a kinetic scroll event, to reduce the chance of accidental selections. Change-Id: I05b7d5fe6681a9edf53096b1fa265442fb31bf33 diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c index 74bdea2f64..be0267fcc5 100644 --- a/apps/gui/bitmap/list.c +++ b/apps/gui/bitmap/list.c @@ -543,12 +543,22 @@ static int swipe_scroll(struct gui_synclist *gui_list, int delta) /* these could possibly be configurable */ /* the lower the smoother */ #define RELOAD_INTERVAL (HZ/25) -/* the higher the earler the list stops */ -#define DECELERATION (1000*RELOAD_INTERVAL/HZ) +/* deceleration factors: new velocity = P * old velocity * - K */ +#define DECEL_P (RELOAD_INTERVAL * 1) / (2 * HZ) +#define DECEL_K (RELOAD_INTERVAL * 2 * LCD_HEIGHT / HZ) +/* fast deceleration when the screen is pressed */ +#define FINGER_DECEL_P (RELOAD_INTERVAL * 2) / (1 * HZ) +#define FINGER_DECEL_K (RELOAD_INTERVAL * (LCD_HEIGHT * 4) / HZ) +/* finger deceleration does not kick in until the touch duration exceeds this */ +#define FINGER_DECEL_GRACE (HZ/4) +/* fraction used to multiply gesture velocity before accumulating it */ +#define VELOCITY_RESIST 3 / 4 struct kinetic_cb_data { struct gui_synclist *list; int velocity; + int subpixel_accum; + long finger_tick; }; struct kinetic { @@ -563,7 +573,11 @@ static struct gesture_vel list_gvel; static void kinetic_stop_scrolling(struct kinetic *k, struct gui_synclist *list) { if (k->cb_data.list == list) + { + k->cb_data.subpixel_accum = 0; + k->cb_data.velocity = 0; timeout_cancel(&k->tmo); + } } /* helper for gui/list.c to cancel scrolling if a normal button event comes */ @@ -586,7 +600,11 @@ static int kinetic_callback(struct timeout *tmo) return 0; /* ds = v*dt */ - int pixel_diff = data->velocity * RELOAD_INTERVAL / HZ; + data->subpixel_accum += 100 * data->velocity * RELOAD_INTERVAL / HZ; + + int pixel_diff = data->subpixel_accum / 100; + data->subpixel_accum %= 100; + int action = swipe_scroll(list, pixel_diff); if (action == ACTION_REDRAW) { @@ -594,14 +612,33 @@ static int kinetic_callback(struct timeout *tmo) button_queue_post(BUTTON_REDRAW, 0); } - /* apply deceleration */ - int old_sign = SIGN(data->velocity); - data->velocity -= SIGN(data->velocity) * DECELERATION; - if (SIGN(data->velocity) != old_sign) + /* calculate and apply deceleration */ + int sign = SIGN(data->velocity); + int absvel = abs(data->velocity); + int decel; + + if (data->finger_tick != 0 && + !TIME_BEFORE(current_tick, data->finger_tick + FINGER_DECEL_GRACE)) + { + decel = data->velocity * FINGER_DECEL_P + sign * FINGER_DECEL_K; + } + else + { + decel = data->velocity * DECEL_P + sign * DECEL_K; + } + + /* + * Ensure velocity is smoothly reduced to zero to avoid jerkiness. + */ + if (absvel < 3) data->velocity = 0; + else if (absvel < sign*decel) + data->velocity = data->velocity / 3; + else + data->velocity -= decel; /* stop scrolling if we didn't move, it means we hit the end */ - if (list->y_pos == list->scroll_base_y) + if (list->y_pos == list->scroll_base_y && pixel_diff != 0) data->velocity = 0; else /* update base y since our scroll distance doesn't accumulate. */ @@ -625,7 +662,7 @@ static bool kinetic_start_scrolling(struct kinetic *k, struct gui_synclist *list return false; k->cb_data.list = list; - k->cb_data.velocity = yvel; + k->cb_data.velocity += yvel * VELOCITY_RESIST; list->scroll_mode = SCROLL_KINETIC; list->scroll_base_y = list->y_pos; @@ -728,6 +765,11 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist *list) int action = ACTION_NONE; int click_loc; + if (action_gesture_is_pressed()) + kinetic.cb_data.finger_tick = gevent.start_tick; + else + kinetic.cb_data.finger_tick = 0; + switch (gevent.id) { case GESTURE_NONE: @@ -737,7 +779,15 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist *list) case GESTURE_TAP: case GESTURE_LONG_PRESS: - _gui_synclist_stop_kinetic_scrolling(list); + /* In kinetic mode taps and presses only decelerate scrolling. + * User needs to wait until scrolling stops to select an item. */ + if (list->scroll_mode == SCROLL_KINETIC) + { + if (gevent.id != GESTURE_NONE) + gesture_vel_reset(&list_gvel); + break; + } + click_loc = get_click_location(list, gevent.x, gevent.y); if (click_loc & LIST) { @@ -789,9 +839,7 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist *list) break; case GESTURE_DRAGSTART: - _gui_synclist_stop_kinetic_scrolling(list); gesture_vel_reset(&list_gvel); - list->scroll_base_y = list->y_pos; /* fallthrough */ case GESTURE_DRAG: @@ -799,7 +847,9 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist *list) if (list->scroll_mode == SCROLL_NONE) { + list->scroll_base_y = list->y_pos; click_loc = get_click_location(list, gevent.ox, gevent.oy); + if (click_loc & SCROLLBAR) list->scroll_mode = SCROLL_BAR; else if (click_loc & LIST) @@ -810,15 +860,14 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist *list) action = scrollbar_scroll(list, gevent.y); else if (list->scroll_mode == SCROLL_SWIPE) action = swipe_scroll(list, gevent.y - gevent.oy); - break; case GESTURE_RELEASE: - if (list->scroll_mode != SCROLL_SWIPE || - !kinetic_start_scrolling(&kinetic, list)) - { + if (list->scroll_mode == SCROLL_BAR) + list->scroll_mode = SCROLL_NONE; + else if(!kinetic_start_scrolling(&kinetic, list) && + list->scroll_mode != SCROLL_KINETIC) list->scroll_mode = SCROLL_NONE; - } action_gesture_reset(); action = ACTION_REDRAW; -- rockbox-cvs mailing list [email protected] https://lists.haxx.se/mailman/listinfo/rockbox-cvs