summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-08-22 01:46:28 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-08-22 14:57:54 +0000
commitd1653bc4d89592f07206ad8f5b711fcf7d58a92d (patch)
treeb721d43277b8c3ebdd49aca524507c7b10bd053f
parent569b165cff2330c5c7dfd6b1aa175008729b4414 (diff)
downloadrockbox-d1653bc4d8.tar.gz
rockbox-d1653bc4d8.zip
touchscreen: fix smooth scrolling in lists
This fixes those annoying jumps that happen when you hit the end of a list while scrolling. Change-Id: I2e41111f9415dce1692b52a2600e7ce77c8f0291
-rw-r--r--apps/gui/bitmap/list.c57
-rw-r--r--apps/gui/list.c12
-rw-r--r--apps/gui/list.h6
3 files changed, 47 insertions, 28 deletions
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