diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c index db8e0504d7..ff0f5a29c1 100644 --- a/apps/gui/bitmap/list.c +++ b/apps/gui/bitmap/list.c @@ -50,7 +50,6 @@ static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS]; #ifdef HAVE_TOUCHSCREEN -static int y_offset; static bool hide_selection; #endif @@ -170,12 +169,9 @@ void list_draw(struct screen *display, struct gui_synclist *list) end = start + nb_lines; #ifdef HAVE_TOUCHSCREEN - if (list->selected_item == 0 || (list->nb_items < nb_lines)) - y_offset = 0; /* reset in case it's a new list */ - - int draw_offset = y_offset; + int draw_offset = list_start_item * linedes.height - list->y_pos; /* draw some extra items to not have empty lines at the top and bottom */ - if (y_offset > 0) + if (draw_offset > 0) { /* make it negative for more consistent apparence when switching * directions */ @@ -183,7 +179,7 @@ void list_draw(struct screen *display, struct gui_synclist *list) if (start > 0) start--; } - else if (y_offset < 0) + else if (draw_offset < 0) end++; #else #define draw_offset 0 @@ -367,7 +363,6 @@ static int scrollbar_scroll(struct gui_synclist * gui_list, int y) if (nb_lines < gui_list->nb_items) { /* scrollbar scrolling is still line based */ - y_offset = 0; int scrollbar_size = nb_lines * gui_list->line_height[screen]; int actual_y = y - list_text[screen].y; int new_selection = (actual_y * gui_list->nb_items) / scrollbar_size; @@ -379,6 +374,7 @@ static int scrollbar_scroll(struct gui_synclist * gui_list, int y) start_item = gui_list->nb_items - nb_lines; gui_list->start_item[screen] = start_item; + gui_list->y_pos = start_item * gui_list->line_height[screen]; return ACTION_REDRAW; } @@ -468,9 +464,11 @@ static void kinetic_force_stop(void) /* helper for gui/list.c to cancel scrolling if a normal button event comes * through dpad or keyboard or whatever */ -void _gui_synclist_stop_kinetic_scrolling(void) +void _gui_synclist_stop_kinetic_scrolling(struct gui_synclist * gui_list) { - y_offset = 0; + const enum screen_type screen = screens[SCREEN_MAIN].screen_type; + gui_list->y_pos = gui_list->start_item[screen] * gui_list->line_height[screen]; + if (scroll_mode == SCROLL_KINETIC) kinetic_force_stop(); scroll_mode = SCROLL_NONE; @@ -512,22 +510,25 @@ static bool swipe_scroll(struct gui_synclist * gui_list, int difference) int new_start_item = -1; int line_diff = 0; - /* don't scroll at the edges of the list */ - if ((old_start == 0 && difference > 0) - || (old_start == (gui_list->nb_items - nb_lines) && difference < 0)) - { - y_offset = 0; - gui_list->start_item[screen] = old_start; - return scroll_mode != SCROLL_KINETIC; /* stop kinetic at the edges */ - } + /* Track whether we hit the end of the list for sake of kinetic scroll */ + bool hit_end = true; - /* add up y_offset over time and translate to lines - * if scrolled enough */ - y_offset += difference; - if (abs(y_offset) > line_height) + /* Move the y position and clamp it (funny things happen otherwise...) */ + gui_list->y_pos -= difference; + if(gui_list->y_pos < 0) + gui_list->y_pos = 0; + else if(gui_list->y_pos > (gui_list->nb_items - nb_lines) * line_height) + gui_list->y_pos = (gui_list->nb_items - nb_lines) * line_height; + else + hit_end = false; + + /* Get the list y position. When pos_y differs by a line height or more, + * we need to scroll the list by adjusting the start item accordingly */ + int cur_y = gui_list->start_item[screen] * line_height; + int diff_y = cur_y - gui_list->y_pos; + if (abs(diff_y) >= line_height) { - line_diff = y_offset/line_height; - y_offset -= line_diff * line_height; + line_diff = diff_y/line_height; } if(line_diff != 0) @@ -548,7 +549,10 @@ static bool swipe_scroll(struct gui_synclist * gui_list, int difference) gui_list->selected_item -= (gui_list->selected_item % gui_list->selected_size); } - return true; + if(hit_end) + return scroll_mode != SCROLL_KINETIC; + else + return true; } static int kinetic_callback(struct timeout *tmo) @@ -729,7 +733,8 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * list) if(!skinlist_get_item(&screens[screen], list, adj_x, adj_y, &line)) { /* selection needs to be corrected if items are only partially visible */ - line = (adj_y - y_offset) / line_height; + int cur_y = list->start_item[screen] * line_height; + line = (adj_y - (cur_y - list->y_pos)) / line_height; if (list_display_title(list, screen)) line -= 1; /* adjust for the list title */ } diff --git a/apps/gui/list.c b/apps/gui/list.c index 139dbaac18..13a850bd7b 100644 --- a/apps/gui/list.c +++ b/apps/gui/list.c @@ -152,6 +152,9 @@ void gui_synclist_init(struct gui_synclist * gui_list, gui_list->callback_speak_item = NULL; gui_list->nb_items = 0; gui_list->selected_item = 0; +#ifdef HAVE_TOUCHSCREEN + gui_list->y_pos = 0; +#endif FOR_NB_SCREENS(i) { gui_list->start_item[i] = 0; @@ -282,6 +285,9 @@ static void gui_list_put_selection_on_screen(struct gui_synclist * gui_list, gui_list->start_item[screen] = bottom; else gui_list->start_item[screen] = new_start_item; +#ifdef HAVE_TOUCHSCREEN + gui_list->y_pos = gui_list->start_item[SCREEN_MAIN] * gui_list->line_height[SCREEN_MAIN]; +#endif } static void edge_beep(struct gui_synclist * gui_list, bool wrap) @@ -417,6 +423,10 @@ static void gui_list_select_at_offset(struct gui_synclist * gui_list, gui_list->selected_size); gui_list->selected_item = gui_list->start_item[i] + nb_lines; } + +#ifdef HAVE_TOUCHSCREEN + gui_list->y_pos = gui_list->start_item[SCREEN_MAIN] * gui_list->line_height[SCREEN_MAIN]; +#endif } return; } @@ -667,7 +677,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists, action = *actionptr = gui_synclist_do_touchscreen(lists); else if (action > ACTION_TOUCHSCREEN_MODE) /* cancel kinetic if we got a normal button event */ - _gui_synclist_stop_kinetic_scrolling(); + _gui_synclist_stop_kinetic_scrolling(lists); #endif /* Disable the skin redraw callback */ diff --git a/apps/gui/list.h b/apps/gui/list.h index 3a613d0a67..64ff3e3fdd 100644 --- a/apps/gui/list.h +++ b/apps/gui/list.h @@ -118,6 +118,10 @@ struct gui_synclist bool scroll_all; int nb_items; int selected_item; +#ifdef HAVE_TOUCHSCREEN + /* absolute Y coordinate, used for smooth scrolling */ + int y_pos; +#endif int start_item[NB_SCREENS]; /* the item that is displayed at the top of the screen */ /* the number of lines that are selected at the same time */ int selected_size; @@ -229,7 +233,7 @@ int skinlist_get_line_count(enum screen_type screen, struct gui_synclist *list); /* this needs to be fixed if we ever get more than 1 touchscreen on a target */ extern unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list); /* only for private use in gui/list.c */ -extern void _gui_synclist_stop_kinetic_scrolling(void); +extern void _gui_synclist_stop_kinetic_scrolling(struct gui_synclist * gui_list); #endif /* If the list has a pending postponed scheduled announcement, that