Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

touchscreen: Port list code to gesture API

Make use of the new gesture API to overhaul list touch support.
This should fix most of the annoyances with touch navigation in
menus and make the touchscreen easier to use.

Change-Id: Ied300947fcf755e2810e9348496ed86eaf620669

authored by

Aidan MacDonald and committed by
Solomon Peachy
67233114 e2363b0e

+236 -368
+227 -364
apps/gui/bitmap/list.c
··· 46 46 #define ICON_PADDING 1 47 47 #define ICON_PADDING_S "1" 48 48 49 - /* these are static to make scrolling work */ 50 - static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS]; 51 - 52 49 #ifdef HAVE_TOUCHSCREEN 53 - static bool hide_selection; 50 + /* used in gui_synclist->scroll_mode */ 51 + enum { 52 + SCROLL_NONE = 0, /* no scrolling */ 53 + SCROLL_BAR, /* scroll by using the scrollbar */ 54 + SCROLL_SWIPE, /* scroll by wiping over the screen */ 55 + SCROLL_KINETIC, /* state after releasing swipe */ 56 + }; 54 57 #endif 58 + 59 + /* these are static to make scrolling work */ 60 + static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS]; 55 61 56 62 /* list-private helpers from the generic list.c (move to header?) */ 57 63 int gui_list_get_item_offset(struct gui_synclist * gui_list, int item_width, ··· 363 369 if( 364 370 #ifdef HAVE_TOUCHSCREEN 365 371 /* don't draw it during scrolling */ 366 - !hide_selection && 372 + list->scroll_mode == SCROLL_NONE && 367 373 #endif 368 374 i >= list->selected_item 369 375 && i < list->selected_item + list->selected_size) ··· 448 454 #if defined(HAVE_TOUCHSCREEN) 449 455 /* This needs to be fixed if we ever get more than 1 touchscreen on a target. */ 450 456 451 - /* difference in pixels between draws, above it means enough to start scrolling */ 452 - #define SCROLL_BEGIN_THRESHOLD 3 457 + static void do_touch_scroll(struct gui_synclist *gui_list, int new_y_pos) 458 + { 459 + if (new_y_pos < 0) 460 + new_y_pos = 0; 461 + 462 + int line_height = gui_list->line_height[SCREEN_MAIN]; 463 + int new_start = new_y_pos / line_height; 464 + int nb_lines = list_get_nb_lines(gui_list, SCREEN_MAIN); 465 + if (new_start > gui_list->nb_items - nb_lines) 466 + { 467 + new_start = gui_list->nb_items - nb_lines; 468 + new_y_pos = new_start * line_height; 469 + } 470 + 471 + int new_item = new_start + nb_lines/2; 472 + if (gui_list->selected_size > 1) 473 + new_item -= new_item % gui_list->selected_size; 453 474 454 - static enum { 455 - SCROLL_NONE, /* no scrolling */ 456 - SCROLL_BAR, /* scroll by using the scrollbar */ 457 - SCROLL_SWIPE, /* scroll by wiping over the screen */ 458 - SCROLL_KINETIC, /* state after releasing swipe */ 459 - } scroll_mode; 475 + gui_list->selected_item = new_item; 476 + gui_list->start_item[SCREEN_MAIN] = new_start; 477 + gui_list->y_pos = new_y_pos; 478 + } 460 479 461 - static int scrollbar_scroll(struct gui_synclist * gui_list, int y) 480 + /* Handle touch scrolling using the scrollbar. Pass the screen y-coordinate 481 + * of the current touch position. */ 482 + static int scrollbar_scroll(struct gui_synclist *gui_list, int y) 462 483 { 463 - const int screen = screens[SCREEN_MAIN].screen_type; 484 + const enum screen_type screen = SCREEN_MAIN; 464 485 const int nb_lines = list_get_nb_lines(gui_list, screen); 465 486 466 487 if (nb_lines < gui_list->nb_items) 467 488 { 468 489 const int line_height = gui_list->line_height[screen]; 490 + int bar_height = list_text[screen].height; 491 + int bar_y = y - list_text[screen].y; 469 492 470 - /* try to position the center of the scrollbar at the touch point */ 471 - int scrollbar_size = list_text[screen].height; 472 - int actual_y = y - list_text[screen].y; 473 - int new_y_pos = (actual_y * gui_list->nb_items * line_height) / scrollbar_size; 474 - int new_start = (actual_y * gui_list->nb_items) / scrollbar_size; 493 + if (bar_y < 0) 494 + bar_y = 0; 495 + else if(bar_y >= bar_height) 496 + bar_y = bar_height - 1; 475 497 476 - new_start -= nb_lines / 2; 498 + int new_y_pos = (bar_y * gui_list->nb_items * line_height) / bar_height; 477 499 new_y_pos -= (nb_lines * line_height) / 2; 478 - if(new_start < 0) { 479 - new_start = 0; 480 - new_y_pos = 0; 481 - } else if(new_start > gui_list->nb_items - nb_lines) { 482 - new_start = gui_list->nb_items - nb_lines; 483 - new_y_pos = new_start * line_height; 484 - } 485 500 486 - gui_list->start_item[screen] = new_start; 487 - gui_list->y_pos = new_y_pos; 501 + do_touch_scroll(gui_list, new_y_pos); 488 502 489 503 return ACTION_REDRAW; 490 504 } ··· 492 506 return ACTION_NONE; 493 507 } 494 508 509 + /* Handle swipe scrolling on a list. 'delta' is the distance to scroll 510 + * relative to gui_list->scroll_base_y, which should be set up at the 511 + * beginning of scrolling. */ 512 + static int swipe_scroll(struct gui_synclist *gui_list, int delta) 513 + { 514 + /* nothing to do if the list does not scroll */ 515 + const int nb_lines = list_get_nb_lines(gui_list, SCREEN_MAIN); 516 + if (nb_lines >= gui_list->nb_items) 517 + return ACTION_NONE; 518 + 519 + do_touch_scroll(gui_list, gui_list->scroll_base_y - delta); 520 + 521 + return ACTION_REDRAW; 522 + } 523 + 495 524 /* kinetic scrolling, based on 496 525 * 497 526 * v = a*t + v0 and ds = v*dt ··· 513 542 * relatively accurate 514 543 */ 515 544 516 - 517 545 #define SIGN(a) ((a) < 0 ? -1 : 1) 518 546 /* these could possibly be configurable */ 519 547 /* the lower the smoother */ ··· 521 549 /* the higher the earler the list stops */ 522 550 #define DECELERATION (1000*RELOAD_INTERVAL/HZ) 523 551 524 - /* this array holds data to compute the initial velocity v0 */ 525 - static struct kinetic_info { 526 - int difference; 527 - long ticks; 528 - } kinetic_data[4]; 529 - static size_t cur_idx; 530 - 531 - static struct cb_data { 532 - struct gui_synclist *list; /* current list */ 533 - int velocity; /* in pixel/s */ 534 - } cb_data; 552 + struct kinetic_cb_data { 553 + struct gui_synclist *list; 554 + int velocity; 555 + }; 535 556 536 - /* data member points to the above struct */ 537 - static struct timeout kinetic_tmo; 557 + struct kinetic { 558 + /* callback for performing the scroll animation */ 559 + struct kinetic_cb_data cb_data; 560 + struct timeout tmo; 561 + }; 538 562 539 - static bool is_kinetic_over(void) 540 - { 541 - return !cb_data.velocity && (scroll_mode == SCROLL_KINETIC); 542 - } 543 - 544 - /* 545 - * collect data about how fast the list is moved in order to compute 546 - * the initial velocity from it later */ 547 - static void kinetic_stats_collect(const int difference) 548 - { 549 - static long last_tick; 550 - /* collect velocity statistics */ 551 - kinetic_data[cur_idx].difference = difference; 552 - kinetic_data[cur_idx].ticks = current_tick - last_tick; 553 - 554 - last_tick = current_tick; 555 - cur_idx += 1; 556 - if (cur_idx >= ARRAYLEN(kinetic_data)) 557 - cur_idx = 0; /* rewind the index */ 558 - } 559 - 560 - /* 561 - * resets the statistic */ 562 - static void kinetic_stats_reset(void) 563 - { 564 - memset(kinetic_data, 0, sizeof(kinetic_data)); 565 - cur_idx = 0; 566 - } 567 - 568 - /* cancels all currently active kinetic scrolling */ 569 - static void kinetic_force_stop(void) 570 - { 571 - timeout_cancel(&kinetic_tmo); 572 - kinetic_stats_reset(); 573 - } 563 + static struct kinetic kinetic; 564 + static struct gesture_vel list_gvel; 574 565 575 - /* helper for gui/list.c to cancel scrolling if a normal button event comes 576 - * through dpad or keyboard or whatever */ 577 - void _gui_synclist_stop_kinetic_scrolling(struct gui_synclist * gui_list) 566 + static void kinetic_stop_scrolling(struct kinetic *k, struct gui_synclist *list) 578 567 { 579 - const enum screen_type screen = screens[SCREEN_MAIN].screen_type; 580 - gui_list->y_pos = gui_list->start_item[screen] * gui_list->line_height[screen]; 581 - 582 - if (scroll_mode == SCROLL_KINETIC) 583 - kinetic_force_stop(); 584 - scroll_mode = SCROLL_NONE; 585 - hide_selection = false; 568 + if (k->cb_data.list == list) 569 + timeout_cancel(&k->tmo); 586 570 } 587 - /* 588 - * returns false if scrolling should be stopped entirely 589 - * 590 - * otherwise it returns true even if it didn't actually scroll, 591 - * but scrolling mode shouldn't be changed 592 - **/ 593 571 594 - 595 - static int scroll_begin_threshold; 596 - static int threshold_accumulation; 597 - static bool swipe_scroll(struct gui_synclist * gui_list, int difference) 572 + /* helper for gui/list.c to cancel scrolling if a normal button event comes */ 573 + void _gui_synclist_stop_kinetic_scrolling(struct gui_synclist *list) 598 574 { 599 - /* fixme */ 600 - const enum screen_type screen = screens[SCREEN_MAIN].screen_type; 601 - const int nb_lines = list_get_nb_lines(gui_list, screen); 602 - const int line_height = gui_list->line_height[screen]; 603 - 604 - if (UNLIKELY(scroll_begin_threshold == 0)) 605 - scroll_begin_threshold = touchscreen_get_scroll_threshold(); 606 - 607 - /* make selecting items easier */ 608 - threshold_accumulation += abs(difference); 609 - if (threshold_accumulation < scroll_begin_threshold && scroll_mode == SCROLL_NONE) 610 - return false; 611 - 612 - threshold_accumulation = 0; 613 - 614 - /* does the list even scroll? if no, return but still show 615 - * the caller that we would scroll */ 616 - if (nb_lines >= gui_list->nb_items) 617 - return true; 618 - 619 - const int old_start = gui_list->start_item[screen]; 620 - int new_start_item = -1; 621 - int line_diff = 0; 622 - int max_y_pos = gui_list->nb_items * line_height - list_text[screen].height; 623 - 624 - /* Track whether we hit the end of the list for sake of kinetic scroll */ 625 - bool hit_end = true; 626 - 627 - /* Move the y position and clamp it (funny things happen otherwise...) */ 628 - gui_list->y_pos -= difference; 629 - if(gui_list->y_pos < 0) 630 - gui_list->y_pos = 0; 631 - else if(gui_list->y_pos > max_y_pos) 632 - gui_list->y_pos = max_y_pos; 633 - else 634 - hit_end = false; 635 - 636 - /* Get the list y position. When pos_y differs by a line height or more, 637 - * we need to scroll the list by adjusting the start item accordingly */ 638 - int cur_y = gui_list->start_item[screen] * line_height; 639 - int diff_y = cur_y - gui_list->y_pos; 640 - if (abs(diff_y) >= line_height) 641 - { 642 - line_diff = diff_y/line_height; 643 - } 644 - 645 - if(line_diff != 0) 575 + if (list->scroll_mode == SCROLL_KINETIC) 646 576 { 647 - int selection_offset = gui_list->selected_item - old_start; 648 - new_start_item = old_start - line_diff; 649 - /* check if new_start_item is bigger than list item count */ 650 - if(new_start_item > gui_list->nb_items - nb_lines) 651 - new_start_item = gui_list->nb_items - nb_lines; 652 - /* set new_start_item to 0 if it's negative */ 653 - if(new_start_item < 0) 654 - new_start_item = 0; 655 - 656 - gui_list->start_item[screen] = new_start_item; 657 - /* keep selected item in sync */ 658 - gui_list->selected_item = new_start_item + selection_offset; 659 - if(gui_list->selected_size > 1) 660 - gui_list->selected_item -= (gui_list->selected_item % gui_list->selected_size); 577 + kinetic_stop_scrolling(&kinetic, list); 578 + list->scroll_mode = SCROLL_NONE; 661 579 } 662 - 663 - if(hit_end) 664 - return scroll_mode != SCROLL_KINETIC; 665 - else 666 - return true; 667 580 } 668 581 669 582 static int kinetic_callback(struct timeout *tmo) 670 583 { 671 - /* cancel if screen was pressed */ 672 - if (scroll_mode != SCROLL_KINETIC) 584 + struct kinetic_cb_data *data = (struct kinetic_cb_data*)tmo->data; 585 + struct gui_synclist *list = data->list; 586 + 587 + /* deal with cancellation */ 588 + if (list->scroll_mode != SCROLL_KINETIC) 673 589 return 0; 674 590 675 - struct cb_data *data = (struct cb_data*)tmo->data; 676 591 /* ds = v*dt */ 677 592 int pixel_diff = data->velocity * RELOAD_INTERVAL / HZ; 678 - /* remember signedness to detect stopping */ 679 - int old_sign = SIGN(data->velocity); 680 - /* advance the list */ 681 - if (!swipe_scroll(data->list, pixel_diff)) 593 + int action = swipe_scroll(list, pixel_diff); 594 + if (action == ACTION_REDRAW) 682 595 { 683 - /* nothing to scroll? */ 684 - data->velocity = 0; 596 + /* force the list to redraw */ 597 + button_queue_post(BUTTON_REDRAW, 0); 685 598 } 599 + 600 + /* apply deceleration */ 601 + int old_sign = SIGN(data->velocity); 602 + data->velocity -= SIGN(data->velocity) * DECELERATION; 603 + if (SIGN(data->velocity) != old_sign) 604 + data->velocity = 0; 605 + 606 + /* stop scrolling if we didn't move, it means we hit the end */ 607 + if (list->y_pos == list->scroll_base_y) 608 + data->velocity = 0; 686 609 else 687 - { 688 - /* decelerate by a fixed amount 689 - * decrementing v0 over time by the deceleration is 690 - * equivalent to computing v = a*t + v0 */ 691 - data->velocity -= SIGN(data->velocity)*DECELERATION; 692 - if (SIGN(data->velocity) != old_sign) 693 - data->velocity = 0; 694 - } 610 + /* update base y since our scroll distance doesn't accumulate. */ 611 + list->scroll_base_y = list->y_pos; 695 612 696 - /* let get_action() timeout, which loads to a 697 - * gui_synclist_draw() call from the main thread */ 698 - button_queue_post(BUTTON_REDRAW, 0); 699 - /* stop if the velocity hit or crossed zero */ 700 - if (!data->velocity) 613 + if (data->velocity == 0) 701 614 { 702 - kinetic_stats_reset(); 615 + list->scroll_mode = SCROLL_NONE; 703 616 return 0; 704 617 } 705 - return RELOAD_INTERVAL; /* cancel or reload */ 618 + 619 + return RELOAD_INTERVAL; 706 620 } 707 621 708 - /* 709 - * computes the initial velocity v0 and sets up the timer */ 710 - static bool kinetic_setup_scroll(struct gui_synclist *list) 622 + /* Computes the initial velocity v0 and sets up the timer */ 623 + static bool kinetic_start_scrolling(struct kinetic *k, struct gui_synclist *list) 711 624 { 712 - /* compute initial velocity */ 713 - int i, _i, v0, len = ARRAYLEN(kinetic_data); 714 - for(i = 0, _i = 0, v0 = 0; i < len; i++) 715 - { /* in pixel/s */ 716 - if (kinetic_data[i].ticks > 0) 717 - { 718 - v0 += kinetic_data[i].difference*HZ/kinetic_data[i].ticks; 719 - _i++; 720 - } 721 - } 722 - if (_i > 0) 723 - v0 /= _i; 724 - else 725 - v0 = 0; 625 + int xvel, yvel; 626 + gesture_vel_get(&list_gvel, &xvel, &yvel); 627 + if (yvel == 0) 628 + return false; 629 + 630 + k->cb_data.list = list; 631 + k->cb_data.velocity = yvel; 726 632 727 - if (v0 != 0) 728 - { 729 - cb_data.list = list; 730 - cb_data.velocity = v0; 731 - timeout_register(&kinetic_tmo, kinetic_callback, RELOAD_INTERVAL, (intptr_t)&cb_data); 732 - return true; 733 - } 734 - return false; 633 + list->scroll_mode = SCROLL_KINETIC; 634 + list->scroll_base_y = list->y_pos; 635 + timeout_register(&k->tmo, kinetic_callback, RELOAD_INTERVAL, 636 + (intptr_t)&k->cb_data); 637 + return true; 735 638 } 736 639 737 640 #define OUTSIDE 0 ··· 746 649 747 650 static int get_click_location(struct gui_synclist *list, int x, int y) 748 651 { 749 - int screen = SCREEN_MAIN; 750 - struct viewport *parent, *title, *text; 652 + const enum screen_type screen = SCREEN_MAIN; 751 653 int retval = OUTSIDE; 752 654 753 - parent = list->parent[screen]; 655 + struct viewport *parent = list->parent[screen]; 656 + struct viewport *text = &list_text[screen]; 657 + struct viewport *title = &title_text[screen]; 658 + 754 659 if (viewport_point_within_vp(parent, x, y)) 755 660 { 756 661 /* see if the title was clicked */ 757 - title = &title_text[screen]; 758 662 if (viewport_point_within_vp(title, x, y)) 759 663 retval = TITLE_TEXT; 760 - /* check the icon too */ 664 + 665 + /* check the title icon */ 761 666 if (list->title_icon != Icon_NOICON && list->show_icons) 762 667 { 763 668 int width = list_icon_width(screen); ··· 770 675 if (viewport_point_within_vp(&vp, x, y)) 771 676 retval = TITLE_ICON; 772 677 } 773 - /* check scrollbar. assume it's shown, if it isn't it will be handled 774 - * later */ 678 + 679 + /* check scrollbar if shown */ 775 680 if (retval == OUTSIDE) 776 681 { 777 682 bool on_scrollbar_clicked; 778 - int adj_x = x - parent->x; 683 + int adj_x = x - text->x; 779 684 switch (list->scrollbar) 780 685 { 781 686 case SCROLLBAR_OFF: ··· 787 692 on_scrollbar_clicked = adj_x <= SCROLLBAR_WIDTH; 788 693 break; 789 694 case SCROLLBAR_RIGHT: 790 - on_scrollbar_clicked = adj_x > (title->x + title->width - SCROLLBAR_WIDTH); 695 + on_scrollbar_clicked = adj_x > (text->x + text->width - SCROLLBAR_WIDTH); 791 696 break; 792 697 } 793 698 if (on_scrollbar_clicked) 794 699 retval = SCROLLBAR; 795 700 } 701 + 702 + /* check the text area */ 796 703 if (retval == OUTSIDE) 797 704 { 798 705 text = &list_text[screen]; ··· 805 712 return retval; 806 713 } 807 714 808 - unsigned gui_synclist_do_touchscreen(struct gui_synclist * list) 715 + unsigned gui_synclist_do_touchscreen(struct gui_synclist *list) 809 716 { 810 - enum screen_type screen; 811 - struct viewport *parent; 812 - short x, y; 813 - int action, adj_x, adj_y, line, line_height, list_start_item; 814 - bool recurse; 815 - static bool initial_touch = true; 816 - static int last_y; 817 - 818 - screen = SCREEN_MAIN; 819 - parent = list->parent[screen]; 820 - line_height = list->line_height[screen]; 821 - list_start_item = list->start_item[screen]; 822 - /* start with getting the action code and finding the click location */ 823 - action = action_get_touchscreen_press(&x, &y); 824 - adj_x = x - parent->x; 825 - adj_y = y - parent->y; 717 + struct touchevent tevent; 718 + struct gesture_event gevent; 826 719 720 + action_get_touch_event(&tevent); 721 + if (!action_gesture_get_event(&gevent)) 722 + return ACTION_NONE; 827 723 828 - /* some defaults before running the state machine */ 829 - recurse = false; 830 - hide_selection = false; 724 + const enum screen_type screen = SCREEN_MAIN; 725 + struct viewport *list_vp = list->parent[screen]; 726 + int adj_x = gevent.x - list_vp->x; 727 + int adj_y = gevent.y - list_vp->y; 728 + int line_height = list->line_height[screen]; 729 + int start_item = list->start_item[screen]; 730 + int start_y = start_item * line_height; 731 + int action = ACTION_NONE; 732 + int click_loc; 831 733 832 - switch (scroll_mode) 734 + switch (gevent.id) 833 735 { 834 - case SCROLL_NONE: 736 + case GESTURE_NONE: 737 + if (!action_gesture_is_pressed()) 738 + break; 739 + /* fallthrough */ 740 + 741 + case GESTURE_TAP: 742 + case GESTURE_LONG_PRESS: 743 + _gui_synclist_stop_kinetic_scrolling(list); 744 + click_loc = get_click_location(list, gevent.x, gevent.y); 745 + if (click_loc & LIST) 835 746 { 836 - int click_loc; 837 - if (initial_touch) 747 + int line; 748 + if(!skinlist_get_item(&screens[screen], list, adj_x, adj_y, &line)) 838 749 { 839 - /* on the first touch last_y has to be reset to avoid 840 - * glitches with touches from long ago */ 841 - last_y = adj_y; 842 - initial_touch = false; 750 + line = (adj_y - (start_y - list->y_pos)) / line_height; 751 + if (list_display_title(list, screen)) 752 + line -= 1; 843 753 } 844 754 845 - line = 0; /* silence gcc 'used uninitialized' warning */ 846 - click_loc = get_click_location(list, x, y); 847 - if (click_loc & LIST) 755 + int new_item = start_item + line; 756 + if (new_item < list->nb_items) 848 757 { 849 - if(!skinlist_get_item(&screens[screen], list, adj_x, adj_y, &line)) 850 - { 851 - /* selection needs to be corrected if items are only partially visible */ 852 - int cur_y = list->start_item[screen] * line_height; 853 - line = (adj_y - (cur_y - list->y_pos)) / line_height; 854 - if (list_display_title(list, screen)) 855 - line -= 1; /* adjust for the list title */ 856 - } 857 - if (list_start_item+line >= list->nb_items) 858 - return ACTION_NONE; 859 - list->selected_item = list_start_item+line; 860 - if(list->selected_size > 1) 861 - list->selected_item -= (list->selected_item % list->selected_size); 758 + if (list->selected_size > 1) 759 + new_item -= new_item % list->selected_size; 862 760 761 + list->selected_item = new_item; 863 762 gui_synclist_speak_item(list); 763 + 764 + if (gevent.id == GESTURE_TAP) 765 + action = ACTION_STD_OK; 766 + else if (gevent.id == GESTURE_LONG_PRESS) 767 + action = ACTION_STD_CONTEXT; 768 + else 769 + action = ACTION_REDRAW; 864 770 } 865 - if (action == BUTTON_TOUCHSCREEN) 866 - { 867 - /* if not scrolling, the user is trying to select */ 868 - int diff = adj_y - last_y; 869 - if ((click_loc & LIST) && swipe_scroll(list, diff)) 870 - scroll_mode = SCROLL_SWIPE; 871 - else if (click_loc & SCROLLBAR) 872 - scroll_mode = SCROLL_BAR; 873 - } 874 - else if (action == BUTTON_REPEAT) 875 - { 876 - if (click_loc & LIST) 877 - { 878 - /* held a single line for a while, bring up the context menu */ 879 - gui_synclist_select_item(list, list->selected_item); 880 - /* don't sent context repeatedly */ 881 - action_wait_for_release(); 882 - initial_touch = true; 883 - return ACTION_STD_CONTEXT; 884 - } 885 - } 886 - else if (action & BUTTON_REL) 887 - { 888 - initial_touch = true; 889 - if (click_loc & LIST) 890 - { /* release on list item enters it */ 891 - gui_synclist_select_item(list, list->selected_item); 892 - return ACTION_STD_OK; 893 - } 894 - else if (click_loc & TITLE_TEXT) 895 - { /* clicking the title goes one level up (cancel) */ 896 - return ACTION_STD_CANCEL; 897 - } 898 - else if (click_loc & TITLE_ICON) 899 - { /* clicking the title icon goes back to the root */ 900 - return ACTION_STD_MENU; 901 - } 902 - } 903 - break; 771 + } 772 + else if ((click_loc & TITLE) && gevent.id == GESTURE_TAP) 773 + { 774 + if (click_loc & TITLE_TEXT) 775 + action = ACTION_STD_CANCEL; 776 + else if (click_loc & TITLE_ICON) 777 + action = ACTION_STD_MENU; 904 778 } 905 - case SCROLL_SWIPE: 779 + else if (gevent.id != GESTURE_NONE && (click_loc & SCROLLBAR)) 906 780 { 907 - /* when swipe scrolling, we accept outside presses as well and 908 - * grab the entire screen (i.e. click_loc does not matter) */ 909 - int diff = adj_y - last_y; 910 - hide_selection = true; 911 - kinetic_stats_collect(diff); 912 - if (swipe_scroll(list, diff)) 913 - { 914 - /* letting the pen go enters kinetic scrolling */ 915 - if ((action & BUTTON_REL)) 916 - { 917 - if (kinetic_setup_scroll(list)) 918 - { 919 - hide_selection = true; 920 - scroll_mode = SCROLL_KINETIC; 921 - } 922 - else 923 - scroll_mode = SCROLL_NONE; 924 - } 925 - } 926 - else if (action & BUTTON_REL) 927 - scroll_mode = SCROLL_NONE; 781 + action = scrollbar_scroll(list, gevent.y); 928 782 929 - if (scroll_mode == SCROLL_NONE) 930 - initial_touch = true; 931 - break; 783 + /* allow long press to become a motion event */ 784 + if (gevent.id != GESTURE_TAP) 785 + break; 932 786 } 933 - case SCROLL_KINETIC: 787 + 788 + if (action != ACTION_REDRAW) 789 + gui_synclist_select_item(list, list->selected_item); 790 + if (gevent.id != GESTURE_NONE) 791 + action_gesture_reset(); 792 + break; 793 + 794 + case GESTURE_DRAGSTART: 795 + _gui_synclist_stop_kinetic_scrolling(list); 796 + gesture_vel_reset(&list_gvel); 797 + list->scroll_base_y = list->y_pos; 798 + /* fallthrough */ 799 + 800 + case GESTURE_DRAG: 801 + gesture_vel_process(&list_gvel, &tevent); 802 + 803 + if (list->scroll_mode == SCROLL_NONE) 934 804 { 935 - /* during kinetic scrolling we need to handle cancellation. 936 - * This state is actually only entered upon end of it as this 937 - * function is not called during the animation. */ 938 - if (!is_kinetic_over()) 939 - { /* a) the user touched the screen (manual cancellation) */ 940 - kinetic_force_stop(); 941 - if (get_click_location(list, x, y) & SCROLLBAR) 942 - scroll_mode = SCROLL_BAR; 943 - else 944 - scroll_mode = SCROLL_SWIPE; 945 - } 946 - else 947 - { /* b) kinetic scrolling stopped on its own */ 948 - /* need to re-run this with SCROLL_NONE since otherwise 949 - * the next touch is not detected correctly */ 950 - scroll_mode = SCROLL_NONE; 951 - recurse = true; 952 - } 953 - break; 805 + click_loc = get_click_location(list, gevent.ox, gevent.oy); 806 + if (click_loc & SCROLLBAR) 807 + list->scroll_mode = SCROLL_BAR; 808 + else if (click_loc & LIST) 809 + list->scroll_mode = SCROLL_SWIPE; 954 810 } 955 - case SCROLL_BAR: 811 + 812 + if (list->scroll_mode == SCROLL_BAR) 813 + action = scrollbar_scroll(list, gevent.y); 814 + else if (list->scroll_mode == SCROLL_SWIPE) 815 + action = swipe_scroll(list, gevent.y - gevent.oy); 816 + 817 + break; 818 + 819 + case GESTURE_RELEASE: 820 + if (list->scroll_mode != SCROLL_SWIPE || 821 + !kinetic_start_scrolling(&kinetic, list)) 956 822 { 957 - hide_selection = true; 958 - /* similarly to swipe scroll, using the scrollbar grabs 959 - * focus so the click location is irrelevant */ 960 - scrollbar_scroll(list, y); 961 - if (action & BUTTON_REL) 962 - scroll_mode = SCROLL_NONE; 963 - break; 823 + list->scroll_mode = SCROLL_NONE; 964 824 } 965 - } 966 825 967 - /* register y position unless forcefully reset */ 968 - if (!initial_touch) 969 - last_y = adj_y; 826 + action_gesture_reset(); 827 + action = ACTION_REDRAW; 828 + break; 970 829 971 - return recurse ? gui_synclist_do_touchscreen(list) : ACTION_REDRAW; 830 + default: 831 + break; 832 + } 833 + 834 + return action; 972 835 } 973 836 974 837 #endif
+2
apps/gui/list.c
··· 163 163 gui_synclist_init_display_settings(gui_list); 164 164 #ifdef HAVE_TOUCHSCREEN 165 165 gui_list->y_pos = 0; 166 + gui_list->scroll_base_y = 0; 167 + gui_list->scroll_mode = 0; 166 168 #endif 167 169 FOR_NB_SCREENS(i) 168 170 {
+7 -4
apps/gui/list.h
··· 23 23 #define _GUI_LIST_H_ 24 24 25 25 #include "config.h" 26 + #include "action.h" 26 27 #include "icon.h" 27 28 #include "screen_access.h" 28 29 #include "skin_engine/skin_engine.h" ··· 155 156 int nb_items; 156 157 int selected_item; 157 158 158 - #ifdef HAVE_TOUCHSCREEN 159 - /* absolute Y coordinate, used for smooth scrolling */ 160 - int y_pos; 161 - #endif 162 159 int start_item[NB_SCREENS]; /* the item that is displayed at the top of the screen */ 163 160 /* the number of lines that are selected at the same time */ 164 161 int selected_size; ··· 185 182 struct list_selection_color *selection_color; 186 183 #endif 187 184 struct viewport *parent[NB_SCREENS]; 185 + 186 + #ifdef HAVE_TOUCHSCREEN 187 + int y_pos; /* absolute Y coordinate, used for smooth scrolling */ 188 + int scroll_base_y; /* used for swipe scrolling */ 189 + int scroll_mode; /* see apps/gui/bitmap/list.c */ 190 + #endif 188 191 }; 189 192 190 193