summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2010-10-31 11:11:46 +0000
committerThomas Martitz <kugel@rockbox.org>2010-10-31 11:11:46 +0000
commitb673ae2c46129d6994b61a351a1567c56f4aef82 (patch)
tree4253dd1daa90384e3f87443f497453c265cbbb0f /apps
parenteaff333bf526225cfca84cd686fe5332b852e506 (diff)
downloadrockbox-b673ae2c46129d6994b61a351a1567c56f4aef82.tar.gz
rockbox-b673ae2c46129d6994b61a351a1567c56f4aef82.zip
FS#11686 - Kinetic list scrolling for touchscreen
This adds kinetic scrolling to to lists on touchscreen targets and RaaA, like all other OSes on touchscreens have. It's only enabled in the absolute point mode, so for non-touchscreen and in grid mode nothing changes. Kinetic scrolling means that the list keeps scrolling (but is decelerating) after you leave the touchscreen with the finger. Redraw interval and deceleration is hardcoded for now but could be made configurable if desired. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28408 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/gui/bitmap/list.c299
-rw-r--r--apps/gui/list.c39
-rw-r--r--apps/gui/list.h4
3 files changed, 275 insertions, 67 deletions
diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c
index ec12ee5367..d78f005e6a 100644
--- a/apps/gui/bitmap/list.c
+++ b/apps/gui/bitmap/list.c
@@ -52,9 +52,10 @@ static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS];
52#define SCROLL_BEGIN_THRESHOLD 3 52#define SCROLL_BEGIN_THRESHOLD 3
53 53
54static enum { 54static enum {
55 SCROLL_NONE, /* no scrolling */ 55 SCROLL_NONE, /* no scrolling */
56 SCROLL_BAR, /* scroll by using the scrollbar */ 56 SCROLL_BAR, /* scroll by using the scrollbar */
57 SCROLL_SWIPE, /* scroll by wiping over the screen */ 57 SCROLL_SWIPE, /* scroll by wiping over the screen */
58 SCROLL_KINETIC, /* state after releasing swipe */
58} scroll_mode; 59} scroll_mode;
59 60
60static int y_offset; 61static int y_offset;
@@ -162,12 +163,13 @@ void list_draw(struct screen *display, struct gui_synclist *list)
162 list_text_vp->height -= line_height; 163 list_text_vp->height -= line_height;
163 } 164 }
164 165
166 const int nb_lines = viewport_get_nb_lines(list_text_vp);
165 167
166 start = list_start_item; 168 start = list_start_item;
167 end = start + viewport_get_nb_lines(list_text_vp); 169 end = start + nb_lines;
168 170
169#ifdef HAVE_TOUCHSCREEN 171#ifdef HAVE_TOUCHSCREEN
170 if (list->selected_item == 0) 172 if (list->selected_item == 0 || (list->nb_items < nb_lines))
171 y_offset = 0; /* reset in case it's a new list */ 173 y_offset = 0; /* reset in case it's a new list */
172 174
173 int draw_offset = y_offset; 175 int draw_offset = y_offset;
@@ -187,12 +189,11 @@ void list_draw(struct screen *display, struct gui_synclist *list)
187#endif 189#endif
188 190
189 /* draw the scrollbar if its needed */ 191 /* draw the scrollbar if its needed */
190 if (global_settings.scrollbar && 192 if (global_settings.scrollbar && nb_lines < list->nb_items)
191 viewport_get_nb_lines(list_text_vp) < list->nb_items)
192 { 193 {
193 struct viewport vp = *list_text_vp; 194 struct viewport vp = *list_text_vp;
194 vp.width = SCROLLBAR_WIDTH; 195 vp.width = SCROLLBAR_WIDTH;
195 vp.height = line_height * viewport_get_nb_lines(list_text_vp); 196 vp.height = line_height * nb_lines;
196 vp.x = parent->x; 197 vp.x = parent->x;
197 list_text_vp->width -= SCROLLBAR_WIDTH; 198 list_text_vp->width -= SCROLLBAR_WIDTH;
198 if (scrollbar_in_left) 199 if (scrollbar_in_left)
@@ -202,7 +203,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
202 display->set_viewport(&vp); 203 display->set_viewport(&vp);
203 gui_scrollbar_draw(display, 204 gui_scrollbar_draw(display,
204 (scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height, 205 (scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height,
205 list->nb_items, list_start_item, list_start_item + end-start, 206 list->nb_items, list_start_item, list_start_item + nb_lines,
206 VERTICAL); 207 VERTICAL);
207 } 208 }
208 else if (show_title) 209 else if (show_title)
@@ -359,8 +360,7 @@ static bool released = false;
359 */ 360 */
360static int last_position=0; 361static int last_position=0;
361 362
362 363static int scrollbar_scroll(struct gui_synclist * gui_list,
363static int gui_synclist_touchscreen_scrollbar(struct gui_synclist * gui_list,
364 int y) 364 int y)
365{ 365{
366 const int screen = screens[SCREEN_MAIN].screen_type; 366 const int screen = screens[SCREEN_MAIN].screen_type;
@@ -391,25 +391,115 @@ static int gui_synclist_touchscreen_scrollbar(struct gui_synclist * gui_list,
391 return ACTION_NONE; 391 return ACTION_NONE;
392} 392}
393 393
394/* kinetic scrolling, based on
395 *
396 * v = a*t + v0 and ds = v*dt
397 *
398 * In each (fixed interval) timeout, the list is advanced by ds, then
399 * the v is reduced by a.
400 * This way we get a linear and smooth deceleration of the scrolling
401 *
402 * As v is the difference of distance per time unit, v is passed (as
403 * pixels moved since the last call) to the scrolling function which takes
404 * care of the pixel accurate drawing
405 *
406 * v0 is dertermined by averaging the last 4 movements of the list
407 * (the pixel and time difference is used to compute each v)
408 *
409 * influenced by http://stechz.com/tag/kinetic/
410 * We take the easy and smooth first approach (until section "Drawbacks"),
411 * since its drawbacks don't apply for us since our timers seem to be
412 * relatively accurate
413 */
414
415
416#define SIGN(a) ((a) < 0 ? -1 : 1)
417/* these could possibly be configurable */
418/* the lower the smoother */
419#define RELOAD_INTERVAL (HZ/25)
420/* the higher the earler the list stops */
421#define DECELERATION (1000*RELOAD_INTERVAL/HZ)
422
423/* this array holds data to compute the initial velocity v0 */
424static struct kinetic_info {
425 int difference;
426 long ticks;
427} kinetic_data[4];
428static size_t cur_idx;
429
430static struct cb_data {
431 struct gui_synclist *list; /* current list */
432 int velocity; /* in pixel/s */
433} cb_data;
434
435/* data member points to the above struct */
436static struct timeout kinetic_tmo;
437
438static bool is_kinetic_over(void)
439{
440 return !cb_data.velocity && (scroll_mode == SCROLL_KINETIC);
441}
442
443/*
444 * collect data about how fast the list is moved in order to compute
445 * the initial velocity from it later */
446static void kinetic_stats_collect(const int difference)
447{
448 static long last_tick;
449 /* collect velocity statistics */
450 kinetic_data[cur_idx].difference = difference;
451 kinetic_data[cur_idx].ticks = current_tick - last_tick;
452
453 last_tick = current_tick;
454 cur_idx += 1;
455 if (cur_idx >= ARRAYLEN(kinetic_data))
456 cur_idx = 0; /* rewind the index */
457}
458
394/* 459/*
395 * returns the number of pixel scrolled since the last call 460 * resets the statistic */
461static void kinetic_stats_reset(void)
462{
463 memset(kinetic_data, 0, sizeof(kinetic_data));
464 cur_idx = 0;
465}
466
467/* cancels all currently active kinetic scrolling */
468static void kinetic_force_stop(void)
469{
470 timeout_cancel(&kinetic_tmo);
471 kinetic_stats_reset();
472}
473
474/* helper for gui/list.c to cancel scrolling if a normal button event comes
475 * through dpad or keyboard or whatever */
476void _gui_synclist_stop_kinetic_scrolling(void)
477{
478 y_offset = 0;
479 if (scroll_mode == SCROLL_KINETIC)
480 kinetic_force_stop();
481 scroll_mode = SCROLL_NONE;
482}
483/*
484 * returns false if scrolling should be stopped entirely
485 *
486 * otherwise it returns true even if it didn't actually scroll,
487 * but scrolling mode shouldn't be changed
396 **/ 488 **/
397static int gui_synclist_touchscreen_scrolling(struct gui_synclist * gui_list, int line_height, int position) 489static bool swipe_scroll(struct gui_synclist * gui_list, int line_height, int difference)
398{ 490{
399 /* fixme */ 491 /* fixme */
400 const enum screen_type screen = screens[SCREEN_MAIN].screen_type; 492 const enum screen_type screen = screens[SCREEN_MAIN].screen_type;
401 const int nb_lines = viewport_get_nb_lines(&list_text[screen]); 493 const int nb_lines = viewport_get_nb_lines(&list_text[screen]);
402 /* in pixels */
403 const int difference = position - last_position;
404 494
405 /* make selecting items easier */ 495 /* make selecting items easier */
406 if (abs(difference) < SCROLL_BEGIN_THRESHOLD && scroll_mode == SCROLL_NONE) 496 if (abs(difference) < SCROLL_BEGIN_THRESHOLD && scroll_mode == SCROLL_NONE)
407 return 0; 497 return false;
408 498
409 /* does the list even scroll? if no, return but still show 499 /* does the list even scroll? if no, return but still show
410 * the caller that we would scroll */ 500 * the caller that we would scroll */
411 if (nb_lines >= gui_list->nb_items) 501 if (nb_lines >= gui_list->nb_items)
412 return difference; 502 return true;
413 503
414 const int old_start = gui_list->start_item[screen]; 504 const int old_start = gui_list->start_item[screen];
415 int new_start_item = -1; 505 int new_start_item = -1;
@@ -420,7 +510,8 @@ static int gui_synclist_touchscreen_scrolling(struct gui_synclist * gui_list, in
420 || (old_start == (gui_list->nb_items - nb_lines) && difference < 0)) 510 || (old_start == (gui_list->nb_items - nb_lines) && difference < 0))
421 { 511 {
422 y_offset = 0; 512 y_offset = 0;
423 return difference; 513 gui_list->start_item[screen] = old_start;
514 return scroll_mode != SCROLL_KINETIC; /* stop kinetic at the edges */
424 } 515 }
425 516
426 /* add up y_offset over time and translate to lines 517 /* add up y_offset over time and translate to lines
@@ -434,6 +525,7 @@ static int gui_synclist_touchscreen_scrolling(struct gui_synclist * gui_list, in
434 525
435 if(line_diff != 0) 526 if(line_diff != 0)
436 { 527 {
528 int selection_offset = gui_list->selected_item - old_start;
437 new_start_item = old_start - line_diff; 529 new_start_item = old_start - line_diff;
438 /* check if new_start_item is bigger than list item count */ 530 /* check if new_start_item is bigger than list item count */
439 if(new_start_item > gui_list->nb_items - nb_lines) 531 if(new_start_item > gui_list->nb_items - nb_lines)
@@ -441,10 +533,82 @@ static int gui_synclist_touchscreen_scrolling(struct gui_synclist * gui_list, in
441 /* set new_start_item to 0 if it's negative */ 533 /* set new_start_item to 0 if it's negative */
442 if(new_start_item < 0) 534 if(new_start_item < 0)
443 new_start_item = 0; 535 new_start_item = 0;
536
444 gui_list->start_item[screen] = new_start_item; 537 gui_list->start_item[screen] = new_start_item;
538 /* keep selected item in sync */
539 gui_list->selected_item = new_start_item + selection_offset;
540 }
541
542 return true;
543}
544
545static int kinetic_callback(struct timeout *tmo)
546{
547 /* cancel if screen was pressed */
548 if (scroll_mode != SCROLL_KINETIC)
549 return 0;
550
551 struct cb_data *data = (struct cb_data*)tmo->data;
552 int line_height = font_get(data->list->parent[0]->font)->height;
553 /* ds = v*dt */
554 int pixel_diff = data->velocity * RELOAD_INTERVAL / HZ;
555 /* remember signedness to detect stopping */
556 int old_sign = SIGN(data->velocity);
557 /* advance the list */
558 if (!swipe_scroll(data->list, line_height, pixel_diff))
559 {
560 /* nothing to scroll? */
561 data->velocity = 0;
562 }
563 else
564 {
565 /* decelerate by a fixed amount
566 * decrementing v0 over time by the deceleration is
567 * equivalent to computing v = a*t + v0 */
568 data->velocity -= SIGN(data->velocity)*DECELERATION;
569 if (SIGN(data->velocity) != old_sign)
570 data->velocity = 0;
571 }
572
573 queue_post(&button_queue, BUTTON_TOUCHSCREEN, 0);
574 /* stop if the velocity hit or crossed zero */
575 if (!data->velocity)
576 {
577 kinetic_stats_reset();
578 return 0;
579 }
580 /* let get_action() timeout, which loads to a
581 * gui_synclist_draw() call from the main thread */
582 return RELOAD_INTERVAL; /* cancel or reload */
583}
584
585/*
586 * computes the initial velocity v0 and sets up the timer */
587static bool kinetic_setup_scroll(struct gui_synclist *list)
588{
589 /* compute initial velocity */
590 int i, _i, v0, len = ARRAYLEN(kinetic_data);
591 for(i = 0, _i = 0, v0 = 0; i < len; i++)
592 { /* in pixel/s */
593 if (kinetic_data[i].ticks > 0)
594 {
595 v0 += kinetic_data[i].difference*HZ/kinetic_data[i].ticks;
596 _i++;
597 }
445 } 598 }
599 if (_i > 0)
600 v0 /= _i;
601 else
602 v0 = 0;
446 603
447 return difference; 604 if (v0 != 0)
605 {
606 cb_data.list = list;
607 cb_data.velocity = v0;
608 timeout_register(&kinetic_tmo, kinetic_callback, RELOAD_INTERVAL, (intptr_t)&cb_data);
609 return true;
610 }
611 return false;
448} 612}
449 613
450unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list) 614unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
@@ -460,12 +624,23 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
460 const bool show_title = list_display_title(gui_list, screen); 624 const bool show_title = list_display_title(gui_list, screen);
461 const bool show_cursor = !global_settings.cursor_style && 625 const bool show_cursor = !global_settings.cursor_style &&
462 gui_list->show_selection_marker; 626 gui_list->show_selection_marker;
463 const bool on_title_clicked = show_title && y < line_height; 627 const bool on_title_clicked = show_title && y < line_height && (button&BUTTON_REL);
628 const bool cancelled_kinetic = (scroll_mode == SCROLL_KINETIC
629 && button != ACTION_NONE && button != ACTION_UNKNOWN
630 && !is_kinetic_over());
464 int icon_width = 0; 631 int icon_width = 0;
465 int line, list_width = list_text_vp->width; 632 int line, list_width = list_text_vp->width;
466 633
634 released = (button&BUTTON_REL) != 0;
635
467 if (button == ACTION_NONE || button == ACTION_UNKNOWN) 636 if (button == ACTION_NONE || button == ACTION_UNKNOWN)
637 {
638 /* this happens when we hit edges of the list while kinetic scrolling,
639 * but not when manually cancelling */
640 if (scroll_mode == SCROLL_KINETIC)
641 return ACTION_REDRAW;
468 return ACTION_NONE; 642 return ACTION_NONE;
643 }
469 644
470 /* x and y are relative to info_vp */ 645 /* x and y are relative to info_vp */
471 if (gui_list->callback_get_item_icon != NULL) 646 if (gui_list->callback_get_item_icon != NULL)
@@ -473,27 +648,32 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
473 if (show_cursor) 648 if (show_cursor)
474 icon_width += get_icon_width(screen); 649 icon_width += get_icon_width(screen);
475 650
476 released = (button&BUTTON_REL) != 0;
477
478 if (button == BUTTON_NONE)
479 return ACTION_NONE;
480
481 if (on_title_clicked) 651 if (on_title_clicked)
482 { 652 {
483 if (x < icon_width) 653 if (scroll_mode == SCROLL_NONE || is_kinetic_over())
654 {
655 if (x < icon_width)
656 {
657 /* Top left corner is GO_TO_ROOT */
658 if (button == BUTTON_REL)
659 return ACTION_STD_MENU;
660 else if (button == (BUTTON_REPEAT|BUTTON_REL))
661 return ACTION_STD_CONTEXT;
662 return ACTION_NONE;
663 }
664 else /* click on title text is cancel */
665 if (button == BUTTON_REL)
666 return ACTION_STD_CANCEL;
667 }
668 /* do this after the above so the scrolling stops without
669 * going back in the list with the same touch */
670 if (scroll_mode == SCROLL_KINETIC)
484 { 671 {
485 /* Top left corner is GO_TO_ROOT */ 672 kinetic_force_stop();
486 if (button == BUTTON_REL) 673 scroll_mode = SCROLL_NONE;
487 return ACTION_STD_MENU;
488 else if (button == (BUTTON_REPEAT|BUTTON_REL))
489 return ACTION_STD_CONTEXT;
490 return ACTION_NONE;
491 } 674 }
492 else /* click on title text is cancel */
493 if (button == BUTTON_REL && scroll_mode == SCROLL_NONE)
494 return ACTION_STD_CANCEL;
495 } 675 }
496 else /* list area clicked */ 676 else /* list area clicked (or not released) */
497 { 677 {
498 const int actual_y = y - (show_title ? line_height : 0); 678 const int actual_y = y - (show_title ? line_height : 0);
499 bool on_scrollbar_clicked; 679 bool on_scrollbar_clicked;
@@ -516,11 +696,13 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
516 * via swiping the screen 696 * via swiping the screen
517 **/ 697 **/
518 698
519 if (!released && scroll_mode < SCROLL_SWIPE && 699 if (!released && scroll_mode != SCROLL_SWIPE &&
520 (on_scrollbar_clicked || scroll_mode == SCROLL_BAR)) 700 (on_scrollbar_clicked || scroll_mode == SCROLL_BAR))
521 { 701 {
702 if (scroll_mode == SCROLL_KINETIC)
703 kinetic_force_stop();
522 scroll_mode = SCROLL_BAR; 704 scroll_mode = SCROLL_BAR;
523 return gui_synclist_touchscreen_scrollbar(gui_list, y); 705 return scrollbar_scroll(gui_list, y);
524 } 706 }
525 707
526 /* |--------------------------------------------------------| 708 /* |--------------------------------------------------------|
@@ -540,7 +722,13 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
540 /* selection needs to be corrected if an items are only 722 /* selection needs to be corrected if an items are only
541 * partially visible */ 723 * partially visible */
542 line = (actual_y - y_offset) / line_height; 724 line = (actual_y - y_offset) / line_height;
543 725
726 if (cancelled_kinetic)
727 {
728 kinetic_force_stop();
729 scroll_mode = SCROLL_SWIPE;
730 }
731
544 /* Pressed below the list*/ 732 /* Pressed below the list*/
545 if (list_start_item + line >= gui_list->nb_items) 733 if (list_start_item + line >= gui_list->nb_items)
546 { 734 {
@@ -550,7 +738,7 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
550 return ACTION_NONE; 738 return ACTION_NONE;
551 } 739 }
552 740
553 if (released) 741 if (released && !cancelled_kinetic)
554 { 742 {
555 /* Pen was released anywhere on the screen */ 743 /* Pen was released anywhere on the screen */
556 last_position = 0; 744 last_position = 0;
@@ -571,29 +759,41 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
571 { 759 {
572 /* we were scrolling 760 /* we were scrolling
573 * -> reset scrolling but do nothing else */ 761 * -> reset scrolling but do nothing else */
574 scroll_mode = SCROLL_NONE; 762 if (scroll_mode == SCROLL_SWIPE)
763 {
764 if (kinetic_setup_scroll(gui_list))
765 scroll_mode = SCROLL_KINETIC;
766 }
767 if (scroll_mode != SCROLL_KINETIC)
768 scroll_mode = SCROLL_NONE;
575 return ACTION_NONE; 769 return ACTION_NONE;
576 } 770 }
577 } 771 }
578 else 772 else
579 { /* pen is on the screen */ 773 { /* pen is on the screen */
580 int result = 0; 774 bool redraw = false, result = false;
581 bool redraw = false;
582
583 /* beginning of list interaction denoted by release in 775 /* beginning of list interaction denoted by release in
584 * the previous call */ 776 * the previous call */
585 if (old_released) 777 if (old_released || is_kinetic_over())
586 { 778 {
587 scroll_mode = SCROLL_NONE; 779 scroll_mode = SCROLL_NONE;
588 redraw = true; 780 redraw = true;
589 } 781 }
590 782
591 /* select current item */ 783 /* select current item; gui_synclist_select_item()
592 gui_synclist_select_item(gui_list, list_start_item+line); 784 * is not called because it has side effects that
785 * disturb kinetic scrolling */
786 gui_list->selected_item = list_start_item+line;
787 gui_synclist_speak_item(gui_list);
593 if (last_position == 0) 788 if (last_position == 0)
594 last_position = actual_y; 789 last_position = actual_y;
595 else 790 else
596 result = gui_synclist_touchscreen_scrolling(gui_list, line_height, actual_y); 791 {
792 /* record speed data in case we do kinetic scrolling */
793 int diff = actual_y - last_position;
794 kinetic_stats_collect(diff);
795 result = swipe_scroll(gui_list, line_height, diff);
796 }
597 797
598 /* Start scrolling once the pen is moved without 798 /* Start scrolling once the pen is moved without
599 * releasing it inbetween */ 799 * releasing it inbetween */
@@ -602,13 +802,12 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
602 redraw = true; 802 redraw = true;
603 scroll_mode = SCROLL_SWIPE; 803 scroll_mode = SCROLL_SWIPE;
604 } 804 }
605
606 last_position = actual_y; 805 last_position = actual_y;
607 806
608 return redraw ? ACTION_REDRAW:ACTION_NONE; 807 return redraw ? ACTION_REDRAW:ACTION_NONE;
609 } 808 }
610 } 809 }
611 } 810 }
612 return ACTION_NONE; 811 return ACTION_REDRAW;
613} 812}
614#endif 813#endif
diff --git a/apps/gui/list.c b/apps/gui/list.c
index 70b04c4313..61738fd454 100644
--- a/apps/gui/list.c
+++ b/apps/gui/list.c
@@ -550,6 +550,7 @@ static void gui_synclist_scroll_left(struct gui_synclist * lists)
550} 550}
551#endif /* HAVE_LCD_BITMAP */ 551#endif /* HAVE_LCD_BITMAP */
552 552
553
553bool gui_synclist_do_button(struct gui_synclist * lists, 554bool gui_synclist_do_button(struct gui_synclist * lists,
554 int *actionptr, enum list_wrap wrap) 555 int *actionptr, enum list_wrap wrap)
555{ 556{
@@ -563,32 +564,38 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
563#else 564#else
564 static int next_item_modifier = 1; 565 static int next_item_modifier = 1;
565 static int last_accel_tick = 0; 566 static int last_accel_tick = 0;
566 if (global_settings.list_accel_start_delay)
567 {
568 int start_delay = global_settings.list_accel_start_delay * (HZ/2);
569 int accel_wait = global_settings.list_accel_wait * HZ/2;
570 567
571 if (get_action_statuscode(NULL)&ACTION_REPEAT) 568 if (action != ACTION_TOUCHSCREEN)
569 {
570 if (global_settings.list_accel_start_delay)
572 { 571 {
573 if (!last_accel_tick) 572 int start_delay = global_settings.list_accel_start_delay * (HZ/2);
574 last_accel_tick = current_tick + start_delay; 573 int accel_wait = global_settings.list_accel_wait * HZ/2;
575 else if (TIME_AFTER(current_tick, last_accel_tick + accel_wait)) 574
575 if (get_action_statuscode(NULL)&ACTION_REPEAT)
576 { 576 {
577 last_accel_tick = current_tick; 577 if (!last_accel_tick)
578 next_item_modifier++; 578 last_accel_tick = current_tick + start_delay;
579 else if (TIME_AFTER(current_tick, last_accel_tick + accel_wait))
580 {
581 last_accel_tick = current_tick;
582 next_item_modifier++;
583 }
584 }
585 else if (last_accel_tick)
586 {
587 next_item_modifier = 1;
588 last_accel_tick = 0;
579 } 589 }
580 }
581 else if (last_accel_tick)
582 {
583 next_item_modifier = 1;
584 last_accel_tick = 0;
585 } 590 }
586 } 591 }
587#endif 592#endif
588
589#if defined(HAVE_TOUCHSCREEN) 593#if defined(HAVE_TOUCHSCREEN)
590 if (action == ACTION_TOUCHSCREEN) 594 if (action == ACTION_TOUCHSCREEN)
591 action = *actionptr = gui_synclist_do_touchscreen(lists); 595 action = *actionptr = gui_synclist_do_touchscreen(lists);
596 else if (action > ACTION_TOUCHSCREEN_MODE)
597 /* cancel kinetic if we got a normal button event */
598 _gui_synclist_stop_kinetic_scrolling();
592#endif 599#endif
593 600
594 switch (wrap) 601 switch (wrap)
diff --git a/apps/gui/list.h b/apps/gui/list.h
index 6deac2de0d..84673d866c 100644
--- a/apps/gui/list.h
+++ b/apps/gui/list.h
@@ -176,7 +176,9 @@ extern bool gui_synclist_do_button(struct gui_synclist * lists,
176 176
177#if defined(HAVE_TOUCHSCREEN) 177#if defined(HAVE_TOUCHSCREEN)
178/* this needs to be fixed if we ever get more than 1 touchscreen on a target */ 178/* this needs to be fixed if we ever get more than 1 touchscreen on a target */
179unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list); 179extern unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list);
180/* only for private use in gui/list.c */
181extern void _gui_synclist_stop_kinetic_scrolling(void);
180#endif 182#endif
181 183
182/* If the list has a pending postponed scheduled announcement, that 184/* If the list has a pending postponed scheduled announcement, that