summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-11-20 02:04:04 +0000
committerAidan MacDonald <amachronic@protonmail.com>2021-11-24 19:03:16 -0500
commit871572b6c3a3125f4958cdc1c4b6d101da60324e (patch)
treee0b31a0dc06bd1b6d09fb310da7e290c2eed4703
parent44acbc66291da6a8ade8571b73a10e34341a622b (diff)
downloadrockbox-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
-rw-r--r--apps/gui/bitmap/list.c74
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