diff options
-rw-r--r-- | apps/gui/bitmap/list.c | 299 | ||||
-rw-r--r-- | apps/gui/list.c | 39 | ||||
-rw-r--r-- | apps/gui/list.h | 4 | ||||
-rw-r--r-- | firmware/export/config.h | 6 |
4 files changed, 281 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 | ||
54 | static enum { | 54 | static 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 | ||
60 | static int y_offset; | 61 | static 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 | */ |
360 | static int last_position=0; | 361 | static int last_position=0; |
361 | 362 | ||
362 | 363 | static int scrollbar_scroll(struct gui_synclist * gui_list, | |
363 | static 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 */ | ||
424 | static struct kinetic_info { | ||
425 | int difference; | ||
426 | long ticks; | ||
427 | } kinetic_data[4]; | ||
428 | static size_t cur_idx; | ||
429 | |||
430 | static 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 */ | ||
436 | static struct timeout kinetic_tmo; | ||
437 | |||
438 | static 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 */ | ||
446 | static 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 */ |
461 | static 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 */ | ||
468 | static 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 */ | ||
476 | void _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 | **/ |
397 | static int gui_synclist_touchscreen_scrolling(struct gui_synclist * gui_list, int line_height, int position) | 489 | static 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 | |||
545 | static 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 */ | ||
587 | static 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 | ||
450 | unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list) | 614 | unsigned 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 | |||
553 | bool gui_synclist_do_button(struct gui_synclist * lists, | 554 | bool 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 */ |
179 | unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list); | 179 | extern unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list); |
180 | /* only for private use in gui/list.c */ | ||
181 | extern 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 |
diff --git a/firmware/export/config.h b/firmware/export/config.h index 8892600ece..d005aae976 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h | |||
@@ -901,6 +901,12 @@ Lyre prototype 1 */ | |||
901 | #endif | 901 | #endif |
902 | #endif /* HAVE_HEADPHONE_DETECTION */ | 902 | #endif /* HAVE_HEADPHONE_DETECTION */ |
903 | 903 | ||
904 | #ifdef HAVE_TOUCHSCREEN | ||
905 | /* Timeout objects required for kinetic list scrolling */ | ||
906 | #undef INCLUDE_TIMEOUT_API | ||
907 | #define INCLUDE_TIMEOUT_API | ||
908 | #endif /* HAVE_TOUCHSCREEN */ | ||
909 | |||
904 | #if defined(HAVE_USB_CHARGING_ENABLE) && defined(HAVE_USBSTACK) | 910 | #if defined(HAVE_USB_CHARGING_ENABLE) && defined(HAVE_USBSTACK) |
905 | /* USB charging support in the USB stack requires timeout objects */ | 911 | /* USB charging support in the USB stack requires timeout objects */ |
906 | #ifndef INCLUDE_TIMEOUT_API | 912 | #ifndef INCLUDE_TIMEOUT_API |