diff options
author | Thomas Martitz <kugel@rockbox.org> | 2012-04-08 13:15:39 +0200 |
---|---|---|
committer | Thomas Martitz <kugel@rockbox.org> | 2012-04-08 13:16:54 +0200 |
commit | 688302a3b3cee640c268ba767b469fec5bfa2ca8 (patch) | |
tree | 33f8aecb42dc8e8adfa015f0e272faba344f2f96 | |
parent | a12b2a5df96233465a2811f9b056e15d7ff30ee2 (diff) | |
download | rockbox-688302a3b3cee640c268ba767b469fec5bfa2ca8.tar.gz rockbox-688302a3b3cee640c268ba767b469fec5bfa2ca8.zip |
touchscreen: Rewrite kinetic scrolling using a state machine.
The old code was very confusing. The scrolling modes need to be handled
differently, thus a state machine makes sense. Should fix numerious
glitches and be easier to maintain.
NOTE: Behavior is still a bit glitchy with an SBS in use, because the skin
engine sees the touch events earlier than the list code.
Change-Id: I4ccead359c81de0d0fc3dea636fe2cb3a28d1bc6
-rw-r--r-- | apps/gui/bitmap/list.c | 416 |
1 files changed, 213 insertions, 203 deletions
diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c index 2f13d8ba53..05a7f066af 100644 --- a/apps/gui/bitmap/list.c +++ b/apps/gui/bitmap/list.c | |||
@@ -48,17 +48,8 @@ | |||
48 | static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS]; | 48 | static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS]; |
49 | 49 | ||
50 | #ifdef HAVE_TOUCHSCREEN | 50 | #ifdef HAVE_TOUCHSCREEN |
51 | /* difference in pixels between draws, above it means enough to start scrolling */ | ||
52 | #define SCROLL_BEGIN_THRESHOLD 3 | ||
53 | |||
54 | static enum { | ||
55 | SCROLL_NONE, /* no scrolling */ | ||
56 | SCROLL_BAR, /* scroll by using the scrollbar */ | ||
57 | SCROLL_SWIPE, /* scroll by wiping over the screen */ | ||
58 | SCROLL_KINETIC, /* state after releasing swipe */ | ||
59 | } scroll_mode; | ||
60 | |||
61 | static int y_offset; | 51 | static int y_offset; |
52 | static bool hide_selection = true; | ||
62 | #endif | 53 | #endif |
63 | 54 | ||
64 | int gui_list_get_item_offset(struct gui_synclist * gui_list, int item_width, | 55 | int gui_list_get_item_offset(struct gui_synclist * gui_list, int item_width, |
@@ -90,6 +81,17 @@ void gui_synclist_scroll_stop(struct gui_synclist *lists) | |||
90 | Note: This image is flipped horizontally when the language is a | 81 | Note: This image is flipped horizontally when the language is a |
91 | right-to-left one (Hebrew, Arabic) | 82 | right-to-left one (Hebrew, Arabic) |
92 | */ | 83 | */ |
84 | |||
85 | static int list_icon_width(enum screen_type screen) | ||
86 | { | ||
87 | return get_icon_width(screen) + ICON_PADDING * 2; | ||
88 | } | ||
89 | |||
90 | static int list_icon_height(enum screen_type screen) | ||
91 | { | ||
92 | return get_icon_height(screen); | ||
93 | } | ||
94 | |||
93 | static bool draw_title(struct screen *display, struct gui_synclist *list) | 95 | static bool draw_title(struct screen *display, struct gui_synclist *list) |
94 | { | 96 | { |
95 | const int screen = display->screen_type; | 97 | const int screen = display->screen_type; |
@@ -108,9 +110,9 @@ static bool draw_title(struct screen *display, struct gui_synclist *list) | |||
108 | { | 110 | { |
109 | struct viewport title_icon = *title_text_vp; | 111 | struct viewport title_icon = *title_text_vp; |
110 | 112 | ||
111 | title_icon.width = get_icon_width(screen) + ICON_PADDING * 2; | 113 | title_icon.width = list_icon_width(screen); |
112 | title_icon.y += (title_icon.height - get_icon_height(screen)) / 2; | 114 | title_icon.y += (title_icon.height - list_icon_height(screen)) / 2; |
113 | title_icon.height = get_icon_height(screen); | 115 | title_icon.height = list_icon_height(screen); |
114 | if (VP_IS_RTL(&title_icon)) | 116 | if (VP_IS_RTL(&title_icon)) |
115 | { | 117 | { |
116 | title_icon.x += title_text_vp->width - title_icon.width; | 118 | title_icon.x += title_text_vp->width - title_icon.width; |
@@ -141,7 +143,7 @@ void list_draw(struct screen *display, struct gui_synclist *list) | |||
141 | int start, end, line_height, style, item_offset, i; | 143 | int start, end, line_height, style, item_offset, i; |
142 | const int screen = display->screen_type; | 144 | const int screen = display->screen_type; |
143 | const int list_start_item = list->start_item[screen]; | 145 | const int list_start_item = list->start_item[screen]; |
144 | const int icon_width = get_icon_width(screen) + ICON_PADDING; | 146 | const int icon_width = list_icon_width(screen); |
145 | const bool scrollbar_in_left = (global_settings.scrollbar == SCROLLBAR_LEFT); | 147 | const bool scrollbar_in_left = (global_settings.scrollbar == SCROLLBAR_LEFT); |
146 | const bool show_cursor = !global_settings.cursor_style && | 148 | const bool show_cursor = !global_settings.cursor_style && |
147 | list->show_selection_marker; | 149 | list->show_selection_marker; |
@@ -236,7 +238,7 @@ void list_draw(struct screen *display, struct gui_synclist *list) | |||
236 | list_icons.x += list_text_vp->width + ICON_PADDING; | 238 | list_icons.x += list_text_vp->width + ICON_PADDING; |
237 | else | 239 | else |
238 | list_text_vp->x += list_icons.width + ICON_PADDING; | 240 | list_text_vp->x += list_icons.width + ICON_PADDING; |
239 | icon_yoffset = (line_height - get_icon_height(screen)) / 2; | 241 | icon_yoffset = (line_height - list_icon_height(screen)) / 2; |
240 | } | 242 | } |
241 | 243 | ||
242 | for (i=start; i<end && i<list->nb_items; i++) | 244 | for (i=start; i<end && i<list->nb_items; i++) |
@@ -274,7 +276,7 @@ void list_draw(struct screen *display, struct gui_synclist *list) | |||
274 | if( | 276 | if( |
275 | #ifdef HAVE_TOUCHSCREEN | 277 | #ifdef HAVE_TOUCHSCREEN |
276 | /* don't draw it during scrolling */ | 278 | /* don't draw it during scrolling */ |
277 | scroll_mode == SCROLL_NONE && | 279 | !hide_selection && |
278 | #endif | 280 | #endif |
279 | i >= list->selected_item | 281 | i >= list->selected_item |
280 | && i < list->selected_item + list->selected_size | 282 | && i < list->selected_item + list->selected_size |
@@ -338,7 +340,7 @@ void list_draw(struct screen *display, struct gui_synclist *list) | |||
338 | display->set_viewport(&list_icons); | 340 | display->set_viewport(&list_icons); |
339 | if (list->callback_get_item_icon != NULL) | 341 | if (list->callback_get_item_icon != NULL) |
340 | { | 342 | { |
341 | int xoff = show_cursor ? get_icon_width(screen) + ICON_PADDING : 0; | 343 | int xoff = show_cursor ? list_icon_width(screen) : 0; |
342 | screen_put_iconxy(display, xoff, | 344 | screen_put_iconxy(display, xoff, |
343 | line*line_height + draw_offset + icon_yoffset, | 345 | line*line_height + draw_offset + icon_yoffset, |
344 | list->callback_get_item_icon(i, list->data)); | 346 | list->callback_get_item_icon(i, list->data)); |
@@ -360,13 +362,15 @@ void list_draw(struct screen *display, struct gui_synclist *list) | |||
360 | #if defined(HAVE_TOUCHSCREEN) | 362 | #if defined(HAVE_TOUCHSCREEN) |
361 | /* This needs to be fixed if we ever get more than 1 touchscreen on a target. */ | 363 | /* This needs to be fixed if we ever get more than 1 touchscreen on a target. */ |
362 | 364 | ||
363 | static bool released = false; | 365 | /* difference in pixels between draws, above it means enough to start scrolling */ |
366 | #define SCROLL_BEGIN_THRESHOLD 3 | ||
364 | 367 | ||
365 | /* Used for kinetic scrolling as we need to know the last position to | 368 | static enum { |
366 | * recognize the scroll direction. | 369 | SCROLL_NONE, /* no scrolling */ |
367 | * This gets reset to 0 at the end of scrolling | 370 | SCROLL_BAR, /* scroll by using the scrollbar */ |
368 | */ | 371 | SCROLL_SWIPE, /* scroll by wiping over the screen */ |
369 | static int last_position=0; | 372 | SCROLL_KINETIC, /* state after releasing swipe */ |
373 | } scroll_mode; | ||
370 | 374 | ||
371 | static int scrollbar_scroll(struct gui_synclist * gui_list, | 375 | static int scrollbar_scroll(struct gui_synclist * gui_list, |
372 | int y) | 376 | int y) |
@@ -486,6 +490,7 @@ void _gui_synclist_stop_kinetic_scrolling(void) | |||
486 | if (scroll_mode == SCROLL_KINETIC) | 490 | if (scroll_mode == SCROLL_KINETIC) |
487 | kinetic_force_stop(); | 491 | kinetic_force_stop(); |
488 | scroll_mode = SCROLL_NONE; | 492 | scroll_mode = SCROLL_NONE; |
493 | hide_selection = false; | ||
489 | } | 494 | } |
490 | /* | 495 | /* |
491 | * returns false if scrolling should be stopped entirely | 496 | * returns false if scrolling should be stopped entirely |
@@ -497,11 +502,12 @@ void _gui_synclist_stop_kinetic_scrolling(void) | |||
497 | 502 | ||
498 | static int scroll_begin_threshold; | 503 | static int scroll_begin_threshold; |
499 | static int threshold_accumulation; | 504 | static int threshold_accumulation; |
500 | static bool swipe_scroll(struct gui_synclist * gui_list, int line_height, int difference) | 505 | static bool swipe_scroll(struct gui_synclist * gui_list, int difference) |
501 | { | 506 | { |
502 | /* fixme */ | 507 | /* fixme */ |
503 | const enum screen_type screen = screens[SCREEN_MAIN].screen_type; | 508 | const enum screen_type screen = screens[SCREEN_MAIN].screen_type; |
504 | const int nb_lines = viewport_get_nb_lines(&list_text[screen]); | 509 | const int nb_lines = viewport_get_nb_lines(&list_text[screen]); |
510 | const int line_height = gui_list->parent[0]->line_height; | ||
505 | 511 | ||
506 | if (UNLIKELY(scroll_begin_threshold == 0)) | 512 | if (UNLIKELY(scroll_begin_threshold == 0)) |
507 | scroll_begin_threshold = touchscreen_get_scroll_threshold(); | 513 | scroll_begin_threshold = touchscreen_get_scroll_threshold(); |
@@ -566,13 +572,12 @@ static int kinetic_callback(struct timeout *tmo) | |||
566 | return 0; | 572 | return 0; |
567 | 573 | ||
568 | struct cb_data *data = (struct cb_data*)tmo->data; | 574 | struct cb_data *data = (struct cb_data*)tmo->data; |
569 | int line_height = data->list->parent[0]->line_height; | ||
570 | /* ds = v*dt */ | 575 | /* ds = v*dt */ |
571 | int pixel_diff = data->velocity * RELOAD_INTERVAL / HZ; | 576 | int pixel_diff = data->velocity * RELOAD_INTERVAL / HZ; |
572 | /* remember signedness to detect stopping */ | 577 | /* remember signedness to detect stopping */ |
573 | int old_sign = SIGN(data->velocity); | 578 | int old_sign = SIGN(data->velocity); |
574 | /* advance the list */ | 579 | /* advance the list */ |
575 | if (!swipe_scroll(data->list, line_height, pixel_diff)) | 580 | if (!swipe_scroll(data->list, pixel_diff)) |
576 | { | 581 | { |
577 | /* nothing to scroll? */ | 582 | /* nothing to scroll? */ |
578 | data->velocity = 0; | 583 | data->velocity = 0; |
@@ -587,6 +592,8 @@ static int kinetic_callback(struct timeout *tmo) | |||
587 | data->velocity = 0; | 592 | data->velocity = 0; |
588 | } | 593 | } |
589 | 594 | ||
595 | /* let get_action() timeout, which loads to a | ||
596 | * gui_synclist_draw() call from the main thread */ | ||
590 | queue_post(&button_queue, BUTTON_REDRAW, 0); | 597 | queue_post(&button_queue, BUTTON_REDRAW, 0); |
591 | /* stop if the velocity hit or crossed zero */ | 598 | /* stop if the velocity hit or crossed zero */ |
592 | if (!data->velocity) | 599 | if (!data->velocity) |
@@ -594,8 +601,6 @@ static int kinetic_callback(struct timeout *tmo) | |||
594 | kinetic_stats_reset(); | 601 | kinetic_stats_reset(); |
595 | return 0; | 602 | return 0; |
596 | } | 603 | } |
597 | /* let get_action() timeout, which loads to a | ||
598 | * gui_synclist_draw() call from the main thread */ | ||
599 | return RELOAD_INTERVAL; /* cancel or reload */ | 604 | return RELOAD_INTERVAL; /* cancel or reload */ |
600 | } | 605 | } |
601 | 606 | ||
@@ -628,208 +633,213 @@ static bool kinetic_setup_scroll(struct gui_synclist *list) | |||
628 | return false; | 633 | return false; |
629 | } | 634 | } |
630 | 635 | ||
631 | unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list) | 636 | #define OUTSIDE 0 |
632 | { | 637 | #define TITLE_TEXT (1<<0) |
633 | short x, y; | 638 | #define TITLE_ICON (1<<1) |
634 | const enum screen_type screen = SCREEN_MAIN; | 639 | #define SCROLLBAR (1<<2) |
635 | struct viewport *parent = gui_list->parent[screen]; | 640 | #define LIST_TEXT (1<<3) |
636 | const int button = action_get_touchscreen_press_in_vp(&x, &y, parent); | 641 | #define LIST_ICON (1<<4) |
637 | const int list_start_item = gui_list->start_item[screen]; | ||
638 | const int line_height = gui_list->parent[screen]->line_height; | ||
639 | const struct viewport *list_text_vp = &list_text[screen]; | ||
640 | const bool old_released = released; | ||
641 | const bool show_title = list_display_title(gui_list, screen); | ||
642 | const bool show_cursor = !global_settings.cursor_style && | ||
643 | gui_list->show_selection_marker; | ||
644 | const bool on_title_clicked = show_title && y < line_height && (button&BUTTON_REL); | ||
645 | const bool cancelled_kinetic = (scroll_mode == SCROLL_KINETIC | ||
646 | && button != ACTION_NONE && button != ACTION_UNKNOWN | ||
647 | && !is_kinetic_over()); | ||
648 | int icon_width = 0; | ||
649 | int line, list_width = list_text_vp->width; | ||
650 | 642 | ||
651 | released = (button&BUTTON_REL) != 0; | 643 | #define TITLE (TITLE_TEXT|TITLE_ICON) |
652 | 644 | #define LIST (LIST_TEXT|LIST_ICON) | |
653 | if (button == ACTION_NONE || button == ACTION_UNKNOWN) | ||
654 | { | ||
655 | /* this happens when we hit edges of the list while kinetic scrolling, | ||
656 | * but not when manually cancelling */ | ||
657 | if (scroll_mode == SCROLL_KINETIC) | ||
658 | return ACTION_REDRAW; | ||
659 | return ACTION_NONE; | ||
660 | } | ||
661 | 645 | ||
662 | /* x and y are relative to parent */ | 646 | static int get_click_location(struct gui_synclist *list, int x, int y) |
663 | if (gui_list->callback_get_item_icon != NULL) | 647 | { |
664 | icon_width += get_icon_width(screen); | 648 | int screen = SCREEN_MAIN; |
665 | if (show_cursor) | 649 | struct viewport *parent, *title, *text; |
666 | icon_width += get_icon_width(screen); | 650 | int retval = OUTSIDE; |
667 | 651 | ||
668 | if (on_title_clicked) | 652 | parent = list->parent[screen]; |
653 | if (viewport_point_within_vp(parent, x, y)) | ||
669 | { | 654 | { |
670 | if (scroll_mode == SCROLL_NONE || is_kinetic_over()) | 655 | /* see if the title was clicked */ |
656 | title = &title_text[screen]; | ||
657 | if (viewport_point_within_vp(title, x, y)) | ||
658 | retval = TITLE_TEXT; | ||
659 | /* check the icon too */ | ||
660 | if (list->title_icon != Icon_NOICON && global_settings.show_icons) | ||
661 | { | ||
662 | int width = list_icon_width(screen); | ||
663 | struct viewport vp = *title; | ||
664 | if (VP_IS_RTL(&vp)) | ||
665 | vp.x += vp.width; | ||
666 | else | ||
667 | vp.x -= width; | ||
668 | vp.width = width; | ||
669 | if (viewport_point_within_vp(&vp, x, y)) | ||
670 | retval = TITLE_ICON; | ||
671 | } | ||
672 | /* check scrollbar. assume it's shown, if it isn't it will be handled | ||
673 | * later */ | ||
674 | if (retval == OUTSIDE) | ||
671 | { | 675 | { |
672 | if (x < icon_width) | 676 | bool on_scrollbar_clicked; |
677 | int adj_x = x - parent->x; | ||
678 | switch (global_settings.scrollbar) | ||
673 | { | 679 | { |
674 | /* Top left corner is GO_TO_ROOT */ | 680 | case SCROLLBAR_LEFT: |
675 | if (button == BUTTON_REL) | 681 | on_scrollbar_clicked = adj_x <= SCROLLBAR_WIDTH; break; |
676 | return ACTION_STD_MENU; | 682 | case SCROLLBAR_RIGHT: |
677 | else if (button == (BUTTON_REPEAT|BUTTON_REL)) | 683 | on_scrollbar_clicked = adj_x > (title->x + title->width - SCROLLBAR_WIDTH); break; |
678 | return ACTION_STD_CONTEXT; | 684 | default: |
679 | return ACTION_NONE; | 685 | on_scrollbar_clicked = false; break; |
680 | } | 686 | } |
681 | else /* click on title text is cancel */ | 687 | if (on_scrollbar_clicked) |
682 | if (button == BUTTON_REL) | 688 | retval = SCROLLBAR; |
683 | return ACTION_STD_CANCEL; | ||
684 | } | 689 | } |
685 | /* do this after the above so the scrolling stops without | 690 | if (retval == OUTSIDE) |
686 | * going back in the list with the same touch */ | ||
687 | if (scroll_mode == SCROLL_KINETIC) | ||
688 | { | 691 | { |
689 | kinetic_force_stop(); | 692 | text = &list_text[screen]; |
690 | scroll_mode = SCROLL_NONE; | 693 | if (viewport_point_within_vp(text, x, y)) |
694 | retval = LIST_TEXT; | ||
695 | else /* if all fails, it must be on the list icons */ | ||
696 | retval = LIST_ICON; | ||
691 | } | 697 | } |
692 | } | 698 | } |
693 | else /* list area clicked (or not released) */ | 699 | return retval; |
694 | { | 700 | } |
695 | const int actual_y = y - (show_title ? line_height : 0); | ||
696 | bool on_scrollbar_clicked; | ||
697 | switch (global_settings.scrollbar) | ||
698 | { | ||
699 | case SCROLLBAR_LEFT: | ||
700 | on_scrollbar_clicked = x <= SCROLLBAR_WIDTH; break; | ||
701 | case SCROLLBAR_RIGHT: | ||
702 | on_scrollbar_clicked = x > (icon_width + list_width); break; | ||
703 | default: | ||
704 | on_scrollbar_clicked = false; break; | ||
705 | } | ||
706 | /* conditions for scrollbar scrolling: | ||
707 | * * pen is on the scrollbar | ||
708 | * AND scrollbar is on the right (left case is handled above) | ||
709 | * OR * pen is in the somewhere else but we did scrollbar scrolling before | ||
710 | * | ||
711 | * scrollbar scrolling must end if the pen is released | ||
712 | * scrollbar scrolling must not happen if we're currently scrolling | ||
713 | * via swiping the screen | ||
714 | **/ | ||
715 | |||
716 | if (!released && scroll_mode != SCROLL_SWIPE && | ||
717 | (on_scrollbar_clicked || scroll_mode == SCROLL_BAR)) | ||
718 | { | ||
719 | if (scroll_mode == SCROLL_KINETIC) | ||
720 | kinetic_force_stop(); | ||
721 | scroll_mode = SCROLL_BAR; | ||
722 | return scrollbar_scroll(gui_list, y); | ||
723 | } | ||
724 | 701 | ||
725 | /* |--------------------------------------------------------| | 702 | unsigned gui_synclist_do_touchscreen(struct gui_synclist * list) |
726 | * | Description of the touchscreen list interface: | | 703 | { |
727 | * |--------------------------------------------------------| | 704 | enum screen_type screen; |
728 | * | Pressing an item will select it and "enter" it. | | 705 | struct viewport *parent; |
729 | * | | | 706 | short x, y; |
730 | * | Pressing and holding your pen down will scroll through | | 707 | int action, adj_y, line, line_height, list_start_item; |
731 | * | the list of items. | | 708 | bool recurse; |
732 | * | | | 709 | static int last_y; |
733 | * | Pressing and holding your pen down on a single item | | 710 | |
734 | * | will bring up the context menu of it. | | 711 | screen = SCREEN_MAIN; |
735 | * |--------------------------------------------------------| | 712 | parent = list->parent[screen]; |
736 | */ | 713 | line_height = list->parent[screen]->line_height; |
737 | if (actual_y > 0 || button & BUTTON_REPEAT) | 714 | list_start_item = list->start_item[screen]; |
715 | /* start with getting the action code and finding the click location */ | ||
716 | action = action_get_touchscreen_press(&x, &y); | ||
717 | adj_y = y - parent->y; | ||
718 | |||
719 | /* selection needs to be corrected if items are only partially visible */ | ||
720 | line = (adj_y - y_offset) / line_height; | ||
721 | if (list_display_title(list, screen)) | ||
722 | line -= 1; /* adjust for the list title */ | ||
723 | |||
724 | /* some defaults before running the state machine */ | ||
725 | recurse = false; | ||
726 | hide_selection = true; | ||
727 | |||
728 | switch (scroll_mode) | ||
729 | { | ||
730 | case SCROLL_NONE: | ||
738 | { | 731 | { |
739 | /* selection needs to be corrected if an items are only | 732 | list->selected_item = list_start_item+line; |
740 | * partially visible */ | 733 | gui_synclist_speak_item(list); |
741 | line = (actual_y - y_offset) / line_height; | 734 | if (!last_y) |
742 | 735 | { /* first run. register adj_y and re-run (will then take the else case) */ | |
743 | if (cancelled_kinetic) | 736 | last_y = adj_y; |
744 | { | 737 | recurse = true; |
745 | kinetic_force_stop(); | ||
746 | scroll_mode = SCROLL_SWIPE; | ||
747 | } | ||
748 | |||
749 | /* Pressed below the list*/ | ||
750 | if (list_start_item + line >= gui_list->nb_items) | ||
751 | { | ||
752 | /* don't collect last_position outside of the list area | ||
753 | * it'd break selecting after such a situation */ | ||
754 | last_position = 0; | ||
755 | return ACTION_NONE; | ||
756 | } | 738 | } |
757 | 739 | else | |
758 | if (button & BUTTON_REPEAT && scroll_mode == SCROLL_NONE) | ||
759 | { | ||
760 | /* held a single line for a while, bring up the context menu */ | ||
761 | gui_synclist_select_item(gui_list, list_start_item + line); | ||
762 | /* don't sent context repeatedly */ | ||
763 | action_wait_for_release(); | ||
764 | last_position = 0; | ||
765 | return ACTION_STD_CONTEXT; | ||
766 | } | ||
767 | if (released && !cancelled_kinetic) | ||
768 | { | 740 | { |
769 | /* Pen was released anywhere on the screen */ | 741 | int click_loc = get_click_location(list, x, y); |
770 | last_position = 0; | 742 | if (action == BUTTON_TOUCHSCREEN) |
771 | if (scroll_mode == SCROLL_NONE) | ||
772 | { | 743 | { |
773 | /* select current line */ | 744 | /* if not scrolling, the user is trying to select */ |
774 | gui_synclist_select_item(gui_list, list_start_item + line); | 745 | int diff = adj_y - last_y; |
775 | return ACTION_STD_OK; | 746 | if ((click_loc & LIST) && swipe_scroll(list, diff)) |
747 | scroll_mode = SCROLL_SWIPE; | ||
748 | else if (click_loc & SCROLLBAR) | ||
749 | scroll_mode = SCROLL_BAR; | ||
750 | |||
751 | hide_selection = click_loc & SCROLLBAR; | ||
776 | } | 752 | } |
777 | else | 753 | else if (action == BUTTON_REPEAT) |
778 | { | 754 | { |
779 | /* we were scrolling | 755 | if (click_loc & LIST) |
780 | * -> reset scrolling but do nothing else */ | ||
781 | if (scroll_mode == SCROLL_SWIPE) | ||
782 | { | 756 | { |
783 | if (kinetic_setup_scroll(gui_list)) | 757 | /* held a single line for a while, bring up the context menu */ |
784 | scroll_mode = SCROLL_KINETIC; | 758 | gui_synclist_select_item(list, list_start_item + line); |
759 | /* don't sent context repeatedly */ | ||
760 | action_wait_for_release(); | ||
761 | last_y = 0; | ||
762 | return ACTION_STD_CONTEXT; | ||
785 | } | 763 | } |
786 | if (scroll_mode != SCROLL_KINETIC) | ||
787 | scroll_mode = SCROLL_NONE; | ||
788 | return ACTION_NONE; | ||
789 | } | 764 | } |
790 | } | 765 | else if (action & BUTTON_REL) |
791 | else | ||
792 | { /* pen is on the screen */ | ||
793 | bool redraw = false, result = false; | ||
794 | /* beginning of list interaction denoted by release in | ||
795 | * the previous call */ | ||
796 | if (old_released || is_kinetic_over()) | ||
797 | { | 766 | { |
798 | scroll_mode = SCROLL_NONE; | 767 | last_y = 0; |
799 | redraw = true; | 768 | if (click_loc & LIST) |
800 | } | 769 | { /* release on list item enters it */ |
801 | 770 | gui_synclist_select_item(list, list_start_item + line); | |
802 | /* select current item; gui_synclist_select_item() | 771 | return ACTION_STD_OK; |
803 | * is not called because it has side effects that | 772 | } |
804 | * disturb kinetic scrolling */ | 773 | else if (click_loc & TITLE_TEXT) |
805 | gui_list->selected_item = list_start_item+line; | 774 | { /* clicking the title goes one level up (cancel) */ |
806 | gui_synclist_speak_item(gui_list); | 775 | return ACTION_STD_CANCEL; |
807 | if (last_position == 0) | 776 | } |
808 | { | 777 | else if (click_loc & TITLE_ICON) |
809 | redraw = true; | 778 | { /* clicking the title icon goes back to the root */ |
810 | last_position = actual_y; | 779 | return ACTION_STD_MENU; |
811 | } | 780 | } |
812 | else | ||
813 | { | ||
814 | /* record speed data in case we do kinetic scrolling */ | ||
815 | int diff = actual_y - last_position; | ||
816 | kinetic_stats_collect(diff); | ||
817 | result = swipe_scroll(gui_list, line_height, diff); | ||
818 | } | 781 | } |
819 | 782 | } | |
820 | /* Start scrolling once the pen is moved without | 783 | break; |
821 | * releasing it inbetween */ | 784 | } |
822 | if (result) | 785 | case SCROLL_SWIPE: |
786 | { | ||
787 | /* when swipe scrolling, we accept outside presses as well and | ||
788 | * grab the entire screen (i.e. click_loc does not matter) */ | ||
789 | int diff = adj_y - last_y; | ||
790 | kinetic_stats_collect(diff); | ||
791 | if (swipe_scroll(list, diff)) | ||
792 | { | ||
793 | /* letting the pen go enters kinetic scrolling */ | ||
794 | if ((action & BUTTON_REL)) | ||
823 | { | 795 | { |
824 | redraw = true; | 796 | if (kinetic_setup_scroll(list)) |
825 | scroll_mode = SCROLL_SWIPE; | 797 | scroll_mode = SCROLL_KINETIC; |
798 | else | ||
799 | scroll_mode = SCROLL_NONE; | ||
826 | } | 800 | } |
827 | last_position = actual_y; | ||
828 | |||
829 | return redraw ? ACTION_REDRAW:ACTION_NONE; | ||
830 | } | 801 | } |
802 | else if (action & BUTTON_REL) | ||
803 | { | ||
804 | scroll_mode = SCROLL_NONE; | ||
805 | } | ||
806 | break; | ||
807 | } | ||
808 | case SCROLL_KINETIC: | ||
809 | { | ||
810 | /* during kinetic scrolling we need to handle cancellation. | ||
811 | * This state is actually only entered upon end of it as this | ||
812 | * function is not called during the animation. */ | ||
813 | if (!is_kinetic_over()) | ||
814 | { /* a) the user touched the screen (manual cancellation) */ | ||
815 | kinetic_force_stop(); | ||
816 | scroll_mode = SCROLL_SWIPE; | ||
817 | } | ||
818 | else | ||
819 | { /* b) kinetic scrolling stopped on its own */ | ||
820 | /* need to re-run this with SCROLL_NONE since otherwise | ||
821 | * the next touch is not detected correctly */ | ||
822 | scroll_mode = SCROLL_NONE; | ||
823 | recurse = true; | ||
824 | } | ||
825 | break; | ||
826 | } | ||
827 | case SCROLL_BAR: | ||
828 | { | ||
829 | /* similarly to swipe scroll, using the scrollbar grabs | ||
830 | * focus so the click location is irrelevant */ | ||
831 | scrollbar_scroll(list, adj_y); | ||
832 | if (action & BUTTON_REL) | ||
833 | scroll_mode = SCROLL_NONE; | ||
834 | break; | ||
831 | } | 835 | } |
832 | } | 836 | } |
833 | return ACTION_REDRAW; | 837 | |
838 | /* register y position unless forcefully reset to 0 */ | ||
839 | if (last_y) | ||
840 | last_y = adj_y; | ||
841 | |||
842 | return recurse ? gui_synclist_do_touchscreen(list) : ACTION_REDRAW; | ||
834 | } | 843 | } |
844 | |||
835 | #endif | 845 | #endif |