diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-11-20 02:04:04 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-11-24 19:03:16 -0500 |
commit | 871572b6c3a3125f4958cdc1c4b6d101da60324e (patch) | |
tree | e0b31a0dc06bd1b6d09fb310da7e290c2eed4703 /apps | |
parent | 44acbc66291da6a8ade8571b73a10e34341a622b (diff) | |
download | rockbox-871572b6c3a3125f4958cdc1c4b6d101da60324e.tar.gz rockbox-871572b6c3a3125f4958cdc1c4b6d101da60324e.zip |
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
Diffstat (limited to 'apps')
-rw-r--r-- | apps/gui/bitmap/list.c | 74 |
1 files changed, 56 insertions, 18 deletions
diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c index ff0f5a29c1..194f4c008b 100644 --- a/apps/gui/bitmap/list.c +++ b/apps/gui/bitmap/list.c | |||
@@ -169,6 +169,12 @@ void list_draw(struct screen *display, struct gui_synclist *list) | |||
169 | end = start + nb_lines; | 169 | end = start + nb_lines; |
170 | 170 | ||
171 | #ifdef HAVE_TOUCHSCREEN | 171 | #ifdef HAVE_TOUCHSCREEN |
172 | /* y_pos needs to be clamped now since it can overflow the maximum | ||
173 | * in some cases, and we have no easy way to prevent this beforehand */ | ||
174 | int max_y_pos = list->nb_items * linedes.height - list_text[screen].height; | ||
175 | if (max_y_pos > 0 && list->y_pos > max_y_pos) | ||
176 | list->y_pos = max_y_pos; | ||
177 | |||
172 | int draw_offset = list_start_item * linedes.height - list->y_pos; | 178 | int draw_offset = list_start_item * linedes.height - list->y_pos; |
173 | /* draw some extra items to not have empty lines at the top and bottom */ | 179 | /* draw some extra items to not have empty lines at the top and bottom */ |
174 | if (draw_offset > 0) | 180 | if (draw_offset > 0) |
@@ -179,8 +185,17 @@ void list_draw(struct screen *display, struct gui_synclist *list) | |||
179 | if (start > 0) | 185 | if (start > 0) |
180 | start--; | 186 | start--; |
181 | } | 187 | } |
182 | else if (draw_offset < 0) | 188 | else if (draw_offset < 0) { |
183 | end++; | 189 | if(end < list->nb_items) |
190 | end++; | ||
191 | } | ||
192 | |||
193 | /* If the viewport is not an exact multiple of the line height, then | ||
194 | * there will be space for one more partial line. */ | ||
195 | int spare_space = list_text_vp->height - linedes.height * nb_lines; | ||
196 | if(nb_lines < list->nb_items && spare_space > 0 && end < list->nb_items) | ||
197 | if(end < list->nb_items) | ||
198 | end++; | ||
184 | #else | 199 | #else |
185 | #define draw_offset 0 | 200 | #define draw_offset 0 |
186 | #endif | 201 | #endif |
@@ -193,17 +208,32 @@ void list_draw(struct screen *display, struct gui_synclist *list) | |||
193 | { | 208 | { |
194 | struct viewport vp = *list_text_vp; | 209 | struct viewport vp = *list_text_vp; |
195 | vp.width = SCROLLBAR_WIDTH; | 210 | vp.width = SCROLLBAR_WIDTH; |
211 | #ifndef HAVE_TOUCHSCREEN | ||
212 | /* touchscreens must use full viewport height | ||
213 | * due to pixelwise rendering */ | ||
196 | vp.height = linedes.height * nb_lines; | 214 | vp.height = linedes.height * nb_lines; |
215 | #endif | ||
197 | list_text_vp->width -= SCROLLBAR_WIDTH; | 216 | list_text_vp->width -= SCROLLBAR_WIDTH; |
198 | if (scrollbar_in_right) | 217 | if (scrollbar_in_right) |
199 | vp.x += list_text_vp->width; | 218 | vp.x += list_text_vp->width; |
200 | else /* left */ | 219 | else /* left */ |
201 | list_text_vp->x += SCROLLBAR_WIDTH; | 220 | list_text_vp->x += SCROLLBAR_WIDTH; |
202 | struct viewport *last = display->set_viewport(&vp); | 221 | struct viewport *last = display->set_viewport(&vp); |
222 | |||
223 | #ifndef HAVE_TOUCHSCREEN | ||
224 | /* button targets go itemwise */ | ||
225 | int scrollbar_items = list->nb_items; | ||
226 | int scrollbar_min = list_start_item; | ||
227 | int scrollbar_max = list_start_item + nb_lines; | ||
228 | #else | ||
229 | /* touchscreens use pixelwise scrolling */ | ||
230 | int scrollbar_items = list->nb_items * linedes.height; | ||
231 | int scrollbar_min = list->y_pos; | ||
232 | int scrollbar_max = list->y_pos + list_text_vp->height; | ||
233 | #endif | ||
203 | gui_scrollbar_draw(display, | 234 | gui_scrollbar_draw(display, |
204 | (scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height, | 235 | (scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height, |
205 | list->nb_items, list_start_item, list_start_item + nb_lines, | 236 | scrollbar_items, scrollbar_min, scrollbar_max, VERTICAL); |
206 | VERTICAL); | ||
207 | display->set_viewport(last); | 237 | display->set_viewport(last); |
208 | } | 238 | } |
209 | /* shift everything a bit in relation to the title */ | 239 | /* shift everything a bit in relation to the title */ |
@@ -360,21 +390,28 @@ static int scrollbar_scroll(struct gui_synclist * gui_list, int y) | |||
360 | const int screen = screens[SCREEN_MAIN].screen_type; | 390 | const int screen = screens[SCREEN_MAIN].screen_type; |
361 | const int nb_lines = list_get_nb_lines(gui_list, screen); | 391 | const int nb_lines = list_get_nb_lines(gui_list, screen); |
362 | 392 | ||
363 | if (nb_lines < gui_list->nb_items) | 393 | if (nb_lines < gui_list->nb_items) |
364 | { | 394 | { |
365 | /* scrollbar scrolling is still line based */ | 395 | const int line_height = gui_list->line_height[screen]; |
366 | int scrollbar_size = nb_lines * gui_list->line_height[screen]; | ||
367 | int actual_y = y - list_text[screen].y; | ||
368 | int new_selection = (actual_y * gui_list->nb_items) / scrollbar_size; | ||
369 | 396 | ||
370 | int start_item = new_selection - nb_lines/2; | 397 | /* try to position the center of the scrollbar at the touch point */ |
371 | if(start_item < 0) | 398 | int scrollbar_size = list_text[screen].height; |
372 | start_item = 0; | 399 | int actual_y = y - list_text[screen].y; |
373 | else if(start_item > gui_list->nb_items - nb_lines) | 400 | int new_y_pos = (actual_y * gui_list->nb_items * line_height) / scrollbar_size; |
374 | start_item = gui_list->nb_items - nb_lines; | 401 | int new_start = (actual_y * gui_list->nb_items) / scrollbar_size; |
402 | |||
403 | new_start -= nb_lines / 2; | ||
404 | new_y_pos -= (nb_lines * line_height) / 2; | ||
405 | if(new_start < 0) { | ||
406 | new_start = 0; | ||
407 | new_y_pos = 0; | ||
408 | } else if(new_start > gui_list->nb_items - nb_lines) { | ||
409 | new_start = gui_list->nb_items - nb_lines; | ||
410 | new_y_pos = new_start * line_height; | ||
411 | } | ||
375 | 412 | ||
376 | gui_list->start_item[screen] = start_item; | 413 | gui_list->start_item[screen] = new_start; |
377 | gui_list->y_pos = start_item * gui_list->line_height[screen]; | 414 | gui_list->y_pos = new_y_pos; |
378 | 415 | ||
379 | return ACTION_REDRAW; | 416 | return ACTION_REDRAW; |
380 | } | 417 | } |
@@ -509,6 +546,7 @@ static bool swipe_scroll(struct gui_synclist * gui_list, int difference) | |||
509 | const int old_start = gui_list->start_item[screen]; | 546 | const int old_start = gui_list->start_item[screen]; |
510 | int new_start_item = -1; | 547 | int new_start_item = -1; |
511 | int line_diff = 0; | 548 | int line_diff = 0; |
549 | int max_y_pos = gui_list->nb_items * line_height - list_text[screen].height; | ||
512 | 550 | ||
513 | /* Track whether we hit the end of the list for sake of kinetic scroll */ | 551 | /* Track whether we hit the end of the list for sake of kinetic scroll */ |
514 | bool hit_end = true; | 552 | bool hit_end = true; |
@@ -517,8 +555,8 @@ static bool swipe_scroll(struct gui_synclist * gui_list, int difference) | |||
517 | gui_list->y_pos -= difference; | 555 | gui_list->y_pos -= difference; |
518 | if(gui_list->y_pos < 0) | 556 | if(gui_list->y_pos < 0) |
519 | gui_list->y_pos = 0; | 557 | gui_list->y_pos = 0; |
520 | else if(gui_list->y_pos > (gui_list->nb_items - nb_lines) * line_height) | 558 | else if(gui_list->y_pos > max_y_pos) |
521 | gui_list->y_pos = (gui_list->nb_items - nb_lines) * line_height; | 559 | gui_list->y_pos = max_y_pos; |
522 | else | 560 | else |
523 | hit_end = false; | 561 | hit_end = false; |
524 | 562 | ||