1
0
Fork 0
forked from len0rd/rockbox

touchscreen: improved pixelwise scrolling in lists

Scrolling via the scrollbar is now a bit smoother -- it's
now drawn pixelwise, and dragging it will scroll the list
pixelwise instead of rigidly snapping to items.

Several other general UX issues were fixed:

- List and scrollbar now occupy the full viewport height,
  to maximize use of screen space.
- Fixed issue with last item in the list suddenly appearing
  or disappearing while scrolling.
- Prevented scrolling into blank space after the last item.

Change-Id: Ib279ac87ec2f2ffc8834c19ff0af45286e2d6d4d
This commit is contained in:
Aidan MacDonald 2021-11-20 02:04:04 +00:00
parent 44acbc6629
commit 871572b6c3

View file

@ -169,6 +169,12 @@ void list_draw(struct screen *display, struct gui_synclist *list)
end = start + nb_lines; end = start + nb_lines;
#ifdef HAVE_TOUCHSCREEN #ifdef HAVE_TOUCHSCREEN
/* y_pos needs to be clamped now since it can overflow the maximum
* in some cases, and we have no easy way to prevent this beforehand */
int max_y_pos = list->nb_items * linedes.height - list_text[screen].height;
if (max_y_pos > 0 && list->y_pos > max_y_pos)
list->y_pos = max_y_pos;
int draw_offset = list_start_item * linedes.height - list->y_pos; 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 */ /* draw some extra items to not have empty lines at the top and bottom */
if (draw_offset > 0) if (draw_offset > 0)
@ -179,7 +185,16 @@ void list_draw(struct screen *display, struct gui_synclist *list)
if (start > 0) if (start > 0)
start--; start--;
} }
else if (draw_offset < 0) else if (draw_offset < 0) {
if(end < list->nb_items)
end++;
}
/* If the viewport is not an exact multiple of the line height, then
* there will be space for one more partial line. */
int spare_space = list_text_vp->height - linedes.height * nb_lines;
if(nb_lines < list->nb_items && spare_space > 0 && end < list->nb_items)
if(end < list->nb_items)
end++; end++;
#else #else
#define draw_offset 0 #define draw_offset 0
@ -193,17 +208,32 @@ void list_draw(struct screen *display, struct gui_synclist *list)
{ {
struct viewport vp = *list_text_vp; struct viewport vp = *list_text_vp;
vp.width = SCROLLBAR_WIDTH; vp.width = SCROLLBAR_WIDTH;
#ifndef HAVE_TOUCHSCREEN
/* touchscreens must use full viewport height
* due to pixelwise rendering */
vp.height = linedes.height * nb_lines; vp.height = linedes.height * nb_lines;
#endif
list_text_vp->width -= SCROLLBAR_WIDTH; list_text_vp->width -= SCROLLBAR_WIDTH;
if (scrollbar_in_right) if (scrollbar_in_right)
vp.x += list_text_vp->width; vp.x += list_text_vp->width;
else /* left */ else /* left */
list_text_vp->x += SCROLLBAR_WIDTH; list_text_vp->x += SCROLLBAR_WIDTH;
struct viewport *last = display->set_viewport(&vp); struct viewport *last = display->set_viewport(&vp);
#ifndef HAVE_TOUCHSCREEN
/* button targets go itemwise */
int scrollbar_items = list->nb_items;
int scrollbar_min = list_start_item;
int scrollbar_max = list_start_item + nb_lines;
#else
/* touchscreens use pixelwise scrolling */
int scrollbar_items = list->nb_items * linedes.height;
int scrollbar_min = list->y_pos;
int scrollbar_max = list->y_pos + list_text_vp->height;
#endif
gui_scrollbar_draw(display, gui_scrollbar_draw(display,
(scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height, (scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height,
list->nb_items, list_start_item, list_start_item + nb_lines, scrollbar_items, scrollbar_min, scrollbar_max, VERTICAL);
VERTICAL);
display->set_viewport(last); display->set_viewport(last);
} }
/* shift everything a bit in relation to the title */ /* shift everything a bit in relation to the title */
@ -362,19 +392,26 @@ static int scrollbar_scroll(struct gui_synclist * gui_list, int y)
if (nb_lines < gui_list->nb_items) if (nb_lines < gui_list->nb_items)
{ {
/* scrollbar scrolling is still line based */ const int line_height = gui_list->line_height[screen];
int scrollbar_size = nb_lines * gui_list->line_height[screen];
/* try to position the center of the scrollbar at the touch point */
int scrollbar_size = list_text[screen].height;
int actual_y = y - list_text[screen].y; int actual_y = y - list_text[screen].y;
int new_selection = (actual_y * gui_list->nb_items) / scrollbar_size; int new_y_pos = (actual_y * gui_list->nb_items * line_height) / scrollbar_size;
int new_start = (actual_y * gui_list->nb_items) / scrollbar_size;
int start_item = new_selection - nb_lines/2; new_start -= nb_lines / 2;
if(start_item < 0) new_y_pos -= (nb_lines * line_height) / 2;
start_item = 0; if(new_start < 0) {
else if(start_item > gui_list->nb_items - nb_lines) new_start = 0;
start_item = gui_list->nb_items - nb_lines; new_y_pos = 0;
} else if(new_start > gui_list->nb_items - nb_lines) {
new_start = gui_list->nb_items - nb_lines;
new_y_pos = new_start * line_height;
}
gui_list->start_item[screen] = start_item; gui_list->start_item[screen] = new_start;
gui_list->y_pos = start_item * gui_list->line_height[screen]; gui_list->y_pos = new_y_pos;
return ACTION_REDRAW; return ACTION_REDRAW;
} }
@ -509,6 +546,7 @@ static bool swipe_scroll(struct gui_synclist * gui_list, int difference)
const int old_start = gui_list->start_item[screen]; const int old_start = gui_list->start_item[screen];
int new_start_item = -1; int new_start_item = -1;
int line_diff = 0; int line_diff = 0;
int max_y_pos = gui_list->nb_items * line_height - list_text[screen].height;
/* Track whether we hit the end of the list for sake of kinetic scroll */ /* Track whether we hit the end of the list for sake of kinetic scroll */
bool hit_end = true; bool hit_end = true;
@ -517,8 +555,8 @@ static bool swipe_scroll(struct gui_synclist * gui_list, int difference)
gui_list->y_pos -= difference; gui_list->y_pos -= difference;
if(gui_list->y_pos < 0) if(gui_list->y_pos < 0)
gui_list->y_pos = 0; gui_list->y_pos = 0;
else if(gui_list->y_pos > (gui_list->nb_items - nb_lines) * line_height) else if(gui_list->y_pos > max_y_pos)
gui_list->y_pos = (gui_list->nb_items - nb_lines) * line_height; gui_list->y_pos = max_y_pos;
else else
hit_end = false; hit_end = false;