1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 by Jonathan Gordon
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 /* This file contains the code to draw the list widget on BITMAP LCDs. */
35 #include "screen_access.h"
37 #include "scrollbar.h"
42 #include "statusbar-skinned.h"
45 #define ICON_PADDING 1
47 /* these are static to make scrolling work */
48 static struct viewport list_text
[NB_SCREENS
], title_text
[NB_SCREENS
];
50 #ifdef HAVE_TOUCHSCREEN
52 static bool hide_selection
= true;
55 int gui_list_get_item_offset(struct gui_synclist
* gui_list
, int item_width
,
56 int text_pos
, struct screen
* display
,
58 bool list_display_title(struct gui_synclist
*list
, enum screen_type screen
);
60 void gui_synclist_scroll_stop(struct gui_synclist
*lists
)
64 screens
[i
].scroll_stop(&list_text
[i
]);
65 screens
[i
].scroll_stop(&title_text
[i
]);
66 screens
[i
].scroll_stop(lists
->parent
[i
]);
71 internal screen layout:
73 |TI| title | TI is title icon
77 | | | items | I - icons
81 Note: This image is flipped horizontally when the language is a
82 right-to-left one (Hebrew, Arabic)
85 static int list_icon_width(enum screen_type screen
)
87 return get_icon_width(screen
) + ICON_PADDING
* 2;
90 static int list_icon_height(enum screen_type screen
)
92 return get_icon_height(screen
);
95 static bool draw_title(struct screen
*display
, struct gui_synclist
*list
)
97 const int screen
= display
->screen_type
;
98 int style
= STYLE_DEFAULT
;
99 struct viewport
*title_text_vp
= &title_text
[screen
];
101 if (sb_set_title_text(list
->title
, list
->title_icon
, screen
))
102 return false; /* the sbs is handling the title */
103 display
->scroll_stop(title_text_vp
);
104 if (!list_display_title(list
, screen
))
106 *title_text_vp
= *(list
->parent
[screen
]);
107 title_text_vp
->height
= title_text_vp
->line_height
;
109 if (list
->title_icon
!= Icon_NOICON
&& global_settings
.show_icons
)
111 struct viewport title_icon
= *title_text_vp
;
113 title_icon
.width
= list_icon_width(screen
);
114 title_icon
.y
+= (title_icon
.height
- list_icon_height(screen
)) / 2;
115 title_icon
.height
= list_icon_height(screen
);
116 if (VP_IS_RTL(&title_icon
))
118 title_icon
.x
+= title_text_vp
->width
- title_icon
.width
;
122 title_text_vp
->x
+= title_icon
.width
;
124 title_text_vp
->width
-= title_icon
.width
;
126 display
->set_viewport(&title_icon
);
127 screen_put_iconxy(display
, 0, 0, list
->title_icon
);
129 #ifdef HAVE_LCD_COLOR
130 if (list
->title_color
>= 0)
132 style
|= (STYLE_COLORED
|list
->title_color
);
135 display
->set_viewport(title_text_vp
);
136 display
->puts_scroll_style(0, 0, list
->title
, style
);
140 void list_draw(struct screen
*display
, struct gui_synclist
*list
)
142 struct viewport list_icons
;
143 int start
, end
, line_height
, style
, item_offset
, i
;
144 const int screen
= display
->screen_type
;
145 const int list_start_item
= list
->start_item
[screen
];
146 const int icon_width
= list_icon_width(screen
);
147 const bool scrollbar_in_left
= (global_settings
.scrollbar
== SCROLLBAR_LEFT
);
148 const bool show_cursor
= !global_settings
.cursor_style
&&
149 list
->show_selection_marker
;
150 struct viewport
*parent
= (list
->parent
[screen
]);
151 #ifdef HAVE_LCD_COLOR
152 unsigned char cur_line
= 0;
154 int icon_yoffset
= 0; /* to center the icon */
156 struct viewport
*list_text_vp
= &list_text
[screen
];
158 line_height
= parent
->line_height
;
159 display
->set_viewport(parent
);
160 display
->clear_viewport();
161 display
->scroll_stop(list_text_vp
);
162 *list_text_vp
= *parent
;
163 list_text_vp
->line_height
= line_height
;
164 if ((show_title
= draw_title(display
, list
)))
166 int title_height
= title_text
[screen
].height
;
167 list_text_vp
->y
+= title_height
;
168 list_text_vp
->height
-= title_height
;
171 const int nb_lines
= viewport_get_nb_lines(list_text_vp
);
173 start
= list_start_item
;
174 end
= start
+ nb_lines
;
176 #ifdef HAVE_TOUCHSCREEN
177 if (list
->selected_item
== 0 || (list
->nb_items
< nb_lines
))
178 y_offset
= 0; /* reset in case it's a new list */
180 int draw_offset
= y_offset
;
181 /* draw some extra items to not have empty lines at the top and bottom */
184 /* make it negative for more consistent apparence when switching
186 draw_offset
-= line_height
;
190 else if (y_offset
< 0)
193 #define draw_offset 0
196 /* draw the scrollbar if its needed */
197 if (global_settings
.scrollbar
&& nb_lines
< list
->nb_items
)
199 struct viewport vp
= *list_text_vp
;
200 vp
.width
= SCROLLBAR_WIDTH
;
201 vp
.height
= line_height
* nb_lines
;
203 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
204 if (scrollbar_in_left
)
205 list_text_vp
->x
+= SCROLLBAR_WIDTH
;
207 vp
.x
+= list_text_vp
->width
;
208 display
->set_viewport(&vp
);
209 gui_scrollbar_draw(display
,
210 (scrollbar_in_left
? 0: 1), 0, SCROLLBAR_WIDTH
-1, vp
.height
,
211 list
->nb_items
, list_start_item
, list_start_item
+ nb_lines
,
216 /* shift everything a bit in relation to the title... */
217 if (!VP_IS_RTL(list_text_vp
) && scrollbar_in_left
)
219 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
220 list_text_vp
->x
+= SCROLLBAR_WIDTH
;
222 else if (VP_IS_RTL(list_text_vp
) && !scrollbar_in_left
)
224 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
228 /* setup icon placement */
229 list_icons
= *list_text_vp
;
230 int icon_count
= (list
->callback_get_item_icon
!= NULL
) ? 1 : 0;
235 list_icons
.width
= icon_width
* icon_count
;
236 list_text_vp
->width
-= list_icons
.width
+ ICON_PADDING
;
237 if (VP_IS_RTL(&list_icons
))
238 list_icons
.x
+= list_text_vp
->width
+ ICON_PADDING
;
240 list_text_vp
->x
+= list_icons
.width
+ ICON_PADDING
;
241 icon_yoffset
= (line_height
- list_icon_height(screen
)) / 2;
244 for (i
=start
; i
<end
&& i
<list
->nb_items
; i
++)
247 unsigned const char *s
;
248 char entry_buffer
[MAX_PATH
];
249 unsigned char *entry_name
;
251 int line
= i
- start
;
252 s
= list
->callback_get_item_name(i
, list
->data
, entry_buffer
,
253 sizeof(entry_buffer
));
254 entry_name
= P2STR(s
);
255 display
->set_viewport(list_text_vp
);
256 style
= STYLE_DEFAULT
;
257 /* position the string at the correct offset place */
259 display
->getstringsize(entry_name
, &item_width
, &h
);
260 item_offset
= gui_list_get_item_offset(list
, item_width
, text_pos
,
261 display
, list_text_vp
);
263 #ifdef HAVE_LCD_COLOR
264 /* if the list has a color callback */
265 if (list
->callback_get_item_color
)
267 int color
= list
->callback_get_item_color(i
, list
->data
);
268 /* if color selected */
271 style
|= STYLE_COLORED
|color
;
275 /* draw the selected line */
277 #ifdef HAVE_TOUCHSCREEN
278 /* don't draw it during scrolling */
281 i
>= list
->selected_item
282 && i
< list
->selected_item
+ list
->selected_size
283 && list
->show_selection_marker
)
284 {/* The selected item must be displayed scrolling */
285 if (global_settings
.cursor_style
== 1
286 #ifdef HAVE_REMOTE_LCD
287 /* the global_settings.cursor_style check is here to make
288 * sure if they want the cursor instead of bar it will work
290 || (display
->depth
< 16 && global_settings
.cursor_style
)
294 /* Display inverted-line-style */
295 style
= STYLE_INVERT
;
297 #ifdef HAVE_LCD_COLOR
298 else if (global_settings
.cursor_style
== 2)
300 /* Display colour line selector */
301 style
= STYLE_COLORBAR
;
303 else if (global_settings
.cursor_style
== 3)
305 /* Display gradient line selector */
306 style
= STYLE_GRADIENT
;
308 /* Make the lcd driver know how many lines the gradient should
309 cover and current line number */
310 /* number of selected lines */
311 style
|= NUMLN_PACK(list
->selected_size
);
312 /* current line number, zero based */
313 style
|= CURLN_PACK(cur_line
);
317 /* if the text is smaller than the viewport size */
318 if (item_offset
> item_width
- (list_text_vp
->width
- text_pos
))
321 display
->puts_style_xyoffset(0, line
, entry_name
,
322 style
, item_offset
, draw_offset
);
326 display
->puts_scroll_style_xyoffset(0, line
, entry_name
,
327 style
, item_offset
, draw_offset
);
332 if (list
->scroll_all
)
333 display
->puts_scroll_style_xyoffset(0, line
, entry_name
,
334 style
, item_offset
, draw_offset
);
336 display
->puts_style_xyoffset(0, line
, entry_name
,
337 style
, item_offset
, draw_offset
);
340 display
->set_viewport(&list_icons
);
341 if (list
->callback_get_item_icon
!= NULL
)
343 int xoff
= show_cursor
? list_icon_width(screen
) : 0;
344 screen_put_iconxy(display
, xoff
,
345 line
*line_height
+ draw_offset
+ icon_yoffset
,
346 list
->callback_get_item_icon(i
, list
->data
));
349 if (show_cursor
&& i
>= list
->selected_item
&&
350 i
< list
->selected_item
+ list
->selected_size
)
352 screen_put_iconxy(display
, 0,
353 line
*line_height
+ draw_offset
+ icon_yoffset
,
357 display
->set_viewport(parent
);
358 display
->update_viewport();
359 display
->set_viewport(NULL
);
362 #if defined(HAVE_TOUCHSCREEN)
363 /* This needs to be fixed if we ever get more than 1 touchscreen on a target. */
365 /* difference in pixels between draws, above it means enough to start scrolling */
366 #define SCROLL_BEGIN_THRESHOLD 3
369 SCROLL_NONE
, /* no scrolling */
370 SCROLL_BAR
, /* scroll by using the scrollbar */
371 SCROLL_SWIPE
, /* scroll by wiping over the screen */
372 SCROLL_KINETIC
, /* state after releasing swipe */
375 static int scrollbar_scroll(struct gui_synclist
* gui_list
,
378 const int screen
= screens
[SCREEN_MAIN
].screen_type
;
379 const int nb_lines
= viewport_get_nb_lines(&list_text
[screen
]);
381 if (nb_lines
< gui_list
->nb_items
)
383 /* scrollbar scrolling is still line based */
385 int scrollbar_size
= nb_lines
*gui_list
->parent
[screen
]->line_height
;
386 int actual_y
= y
- list_text
[screen
].y
;
388 int new_selection
= (actual_y
* gui_list
->nb_items
)
391 int start_item
= new_selection
- nb_lines
/2;
394 else if(start_item
> gui_list
->nb_items
- nb_lines
)
395 start_item
= gui_list
->nb_items
- nb_lines
;
397 gui_list
->start_item
[screen
] = start_item
;
399 return ACTION_REDRAW
;
405 /* kinetic scrolling, based on
407 * v = a*t + v0 and ds = v*dt
409 * In each (fixed interval) timeout, the list is advanced by ds, then
410 * the v is reduced by a.
411 * This way we get a linear and smooth deceleration of the scrolling
413 * As v is the difference of distance per time unit, v is passed (as
414 * pixels moved since the last call) to the scrolling function which takes
415 * care of the pixel accurate drawing
417 * v0 is dertermined by averaging the last 4 movements of the list
418 * (the pixel and time difference is used to compute each v)
420 * influenced by http://stechz.com/tag/kinetic/
421 * We take the easy and smooth first approach (until section "Drawbacks"),
422 * since its drawbacks don't apply for us since our timers seem to be
423 * relatively accurate
427 #define SIGN(a) ((a) < 0 ? -1 : 1)
428 /* these could possibly be configurable */
429 /* the lower the smoother */
430 #define RELOAD_INTERVAL (HZ/25)
431 /* the higher the earler the list stops */
432 #define DECELERATION (1000*RELOAD_INTERVAL/HZ)
434 /* this array holds data to compute the initial velocity v0 */
435 static struct kinetic_info
{
439 static size_t cur_idx
;
441 static struct cb_data
{
442 struct gui_synclist
*list
; /* current list */
443 int velocity
; /* in pixel/s */
446 /* data member points to the above struct */
447 static struct timeout kinetic_tmo
;
449 static bool is_kinetic_over(void)
451 return !cb_data
.velocity
&& (scroll_mode
== SCROLL_KINETIC
);
455 * collect data about how fast the list is moved in order to compute
456 * the initial velocity from it later */
457 static void kinetic_stats_collect(const int difference
)
459 static long last_tick
;
460 /* collect velocity statistics */
461 kinetic_data
[cur_idx
].difference
= difference
;
462 kinetic_data
[cur_idx
].ticks
= current_tick
- last_tick
;
464 last_tick
= current_tick
;
466 if (cur_idx
>= ARRAYLEN(kinetic_data
))
467 cur_idx
= 0; /* rewind the index */
471 * resets the statistic */
472 static void kinetic_stats_reset(void)
474 memset(kinetic_data
, 0, sizeof(kinetic_data
));
478 /* cancels all currently active kinetic scrolling */
479 static void kinetic_force_stop(void)
481 timeout_cancel(&kinetic_tmo
);
482 kinetic_stats_reset();
485 /* helper for gui/list.c to cancel scrolling if a normal button event comes
486 * through dpad or keyboard or whatever */
487 void _gui_synclist_stop_kinetic_scrolling(void)
490 if (scroll_mode
== SCROLL_KINETIC
)
491 kinetic_force_stop();
492 scroll_mode
= SCROLL_NONE
;
493 hide_selection
= false;
496 * returns false if scrolling should be stopped entirely
498 * otherwise it returns true even if it didn't actually scroll,
499 * but scrolling mode shouldn't be changed
503 static int scroll_begin_threshold
;
504 static int threshold_accumulation
;
505 static bool swipe_scroll(struct gui_synclist
* gui_list
, int difference
)
508 const enum screen_type screen
= screens
[SCREEN_MAIN
].screen_type
;
509 const int nb_lines
= viewport_get_nb_lines(&list_text
[screen
]);
510 const int line_height
= gui_list
->parent
[0]->line_height
;
512 if (UNLIKELY(scroll_begin_threshold
== 0))
513 scroll_begin_threshold
= touchscreen_get_scroll_threshold();
515 /* make selecting items easier */
516 threshold_accumulation
+= abs(difference
);
517 if (threshold_accumulation
< scroll_begin_threshold
&& scroll_mode
== SCROLL_NONE
)
520 threshold_accumulation
= 0;
522 /* does the list even scroll? if no, return but still show
523 * the caller that we would scroll */
524 if (nb_lines
>= gui_list
->nb_items
)
527 const int old_start
= gui_list
->start_item
[screen
];
528 int new_start_item
= -1;
531 /* don't scroll at the edges of the list */
532 if ((old_start
== 0 && difference
> 0)
533 || (old_start
== (gui_list
->nb_items
- nb_lines
) && difference
< 0))
536 gui_list
->start_item
[screen
] = old_start
;
537 return scroll_mode
!= SCROLL_KINETIC
; /* stop kinetic at the edges */
540 /* add up y_offset over time and translate to lines
541 * if scrolled enough */
542 y_offset
+= difference
;
543 if (abs(y_offset
) > line_height
)
545 line_diff
= y_offset
/line_height
;
546 y_offset
-= line_diff
* line_height
;
551 int selection_offset
= gui_list
->selected_item
- old_start
;
552 new_start_item
= old_start
- line_diff
;
553 /* check if new_start_item is bigger than list item count */
554 if(new_start_item
> gui_list
->nb_items
- nb_lines
)
555 new_start_item
= gui_list
->nb_items
- nb_lines
;
556 /* set new_start_item to 0 if it's negative */
557 if(new_start_item
< 0)
560 gui_list
->start_item
[screen
] = new_start_item
;
561 /* keep selected item in sync */
562 gui_list
->selected_item
= new_start_item
+ selection_offset
;
568 static int kinetic_callback(struct timeout
*tmo
)
570 /* cancel if screen was pressed */
571 if (scroll_mode
!= SCROLL_KINETIC
)
574 struct cb_data
*data
= (struct cb_data
*)tmo
->data
;
576 int pixel_diff
= data
->velocity
* RELOAD_INTERVAL
/ HZ
;
577 /* remember signedness to detect stopping */
578 int old_sign
= SIGN(data
->velocity
);
579 /* advance the list */
580 if (!swipe_scroll(data
->list
, pixel_diff
))
582 /* nothing to scroll? */
587 /* decelerate by a fixed amount
588 * decrementing v0 over time by the deceleration is
589 * equivalent to computing v = a*t + v0 */
590 data
->velocity
-= SIGN(data
->velocity
)*DECELERATION
;
591 if (SIGN(data
->velocity
) != old_sign
)
595 /* let get_action() timeout, which loads to a
596 * gui_synclist_draw() call from the main thread */
597 queue_post(&button_queue
, BUTTON_REDRAW
, 0);
598 /* stop if the velocity hit or crossed zero */
601 kinetic_stats_reset();
604 return RELOAD_INTERVAL
; /* cancel or reload */
608 * computes the initial velocity v0 and sets up the timer */
609 static bool kinetic_setup_scroll(struct gui_synclist
*list
)
611 /* compute initial velocity */
612 int i
, _i
, v0
, len
= ARRAYLEN(kinetic_data
);
613 for(i
= 0, _i
= 0, v0
= 0; i
< len
; i
++)
615 if (kinetic_data
[i
].ticks
> 0)
617 v0
+= kinetic_data
[i
].difference
*HZ
/kinetic_data
[i
].ticks
;
629 cb_data
.velocity
= v0
;
630 timeout_register(&kinetic_tmo
, kinetic_callback
, RELOAD_INTERVAL
, (intptr_t)&cb_data
);
637 #define TITLE_TEXT (1<<0)
638 #define TITLE_ICON (1<<1)
639 #define SCROLLBAR (1<<2)
640 #define LIST_TEXT (1<<3)
641 #define LIST_ICON (1<<4)
643 #define TITLE (TITLE_TEXT|TITLE_ICON)
644 #define LIST (LIST_TEXT|LIST_ICON)
646 static int get_click_location(struct gui_synclist
*list
, int x
, int y
)
648 int screen
= SCREEN_MAIN
;
649 struct viewport
*parent
, *title
, *text
;
650 int retval
= OUTSIDE
;
652 parent
= list
->parent
[screen
];
653 if (viewport_point_within_vp(parent
, x
, y
))
655 /* see if the title was clicked */
656 title
= &title_text
[screen
];
657 if (viewport_point_within_vp(title
, x
, y
))
659 /* check the icon too */
660 if (list
->title_icon
!= Icon_NOICON
&& global_settings
.show_icons
)
662 int width
= list_icon_width(screen
);
663 struct viewport vp
= *title
;
669 if (viewport_point_within_vp(&vp
, x
, y
))
672 /* check scrollbar. assume it's shown, if it isn't it will be handled
674 if (retval
== OUTSIDE
)
676 bool on_scrollbar_clicked
;
677 int adj_x
= x
- parent
->x
;
678 switch (global_settings
.scrollbar
)
681 on_scrollbar_clicked
= adj_x
<= SCROLLBAR_WIDTH
; break;
682 case SCROLLBAR_RIGHT
:
683 on_scrollbar_clicked
= adj_x
> (title
->x
+ title
->width
- SCROLLBAR_WIDTH
); break;
685 on_scrollbar_clicked
= false; break;
687 if (on_scrollbar_clicked
)
690 if (retval
== OUTSIDE
)
692 text
= &list_text
[screen
];
693 if (viewport_point_within_vp(text
, x
, y
))
695 else /* if all fails, it must be on the list icons */
702 unsigned gui_synclist_do_touchscreen(struct gui_synclist
* list
)
704 enum screen_type screen
;
705 struct viewport
*parent
;
707 int action
, adj_y
, line
, line_height
, list_start_item
;
709 static int last_y
= -1;
711 screen
= SCREEN_MAIN
;
712 parent
= list
->parent
[screen
];
713 line_height
= list
->parent
[screen
]->line_height
;
714 list_start_item
= list
->start_item
[screen
];
715 /* start with getting the action code and finding the click location */
716 action
= action_get_touchscreen_press(&x
, &y
);
717 adj_y
= y
- parent
->y
;
720 /* some defaults before running the state machine */
722 hide_selection
= true;
729 { /* first run. register adj_y and re-run (will then take the else case) */
735 int click_loc
= get_click_location(list
, x
, y
);
736 line
= 0; /* silence gcc 'used uninitialized' warning */
737 if (click_loc
& LIST
)
739 /* selection needs to be corrected if items are only partially visible */
740 line
= (adj_y
- y_offset
) / line_height
;
741 if (list_display_title(list
, screen
))
742 line
-= 1; /* adjust for the list title */
743 if (line
>= list
->nb_items
)
745 list
->selected_item
= list_start_item
+line
;
746 gui_synclist_speak_item(list
);
748 if (action
== BUTTON_TOUCHSCREEN
)
750 /* if not scrolling, the user is trying to select */
751 int diff
= adj_y
- last_y
;
752 if ((click_loc
& LIST
) && swipe_scroll(list
, diff
))
753 scroll_mode
= SCROLL_SWIPE
;
754 else if (click_loc
& SCROLLBAR
)
755 scroll_mode
= SCROLL_BAR
;
757 /* only show selection bar if clicking the list */
758 hide_selection
= click_loc
& (SCROLLBAR
|TITLE
);
760 else if (action
== BUTTON_REPEAT
)
762 if (click_loc
& LIST
)
764 /* held a single line for a while, bring up the context menu */
765 gui_synclist_select_item(list
, list_start_item
+ line
);
766 /* don't sent context repeatedly */
767 action_wait_for_release();
769 return ACTION_STD_CONTEXT
;
772 else if (action
& BUTTON_REL
)
775 if (click_loc
& LIST
)
776 { /* release on list item enters it */
777 gui_synclist_select_item(list
, list_start_item
+ line
);
778 return ACTION_STD_OK
;
780 else if (click_loc
& TITLE_TEXT
)
781 { /* clicking the title goes one level up (cancel) */
782 return ACTION_STD_CANCEL
;
784 else if (click_loc
& TITLE_ICON
)
785 { /* clicking the title icon goes back to the root */
786 return ACTION_STD_MENU
;
794 /* when swipe scrolling, we accept outside presses as well and
795 * grab the entire screen (i.e. click_loc does not matter) */
796 int diff
= adj_y
- last_y
;
797 kinetic_stats_collect(diff
);
798 if (swipe_scroll(list
, diff
))
800 /* letting the pen go enters kinetic scrolling */
801 if ((action
& BUTTON_REL
))
803 if (kinetic_setup_scroll(list
))
804 scroll_mode
= SCROLL_KINETIC
;
806 scroll_mode
= SCROLL_NONE
;
809 else if (action
& BUTTON_REL
)
810 scroll_mode
= SCROLL_NONE
;
812 if (scroll_mode
== SCROLL_NONE
)
818 /* during kinetic scrolling we need to handle cancellation.
819 * This state is actually only entered upon end of it as this
820 * function is not called during the animation. */
821 if (!is_kinetic_over())
822 { /* a) the user touched the screen (manual cancellation) */
823 kinetic_force_stop();
824 if (get_click_location(list
, x
, y
) & SCROLLBAR
)
825 scroll_mode
= SCROLL_BAR
;
827 scroll_mode
= SCROLL_SWIPE
;
830 { /* b) kinetic scrolling stopped on its own */
831 /* need to re-run this with SCROLL_NONE since otherwise
832 * the next touch is not detected correctly */
833 scroll_mode
= SCROLL_NONE
;
840 /* similarly to swipe scroll, using the scrollbar grabs
841 * focus so the click location is irrelevant */
842 scrollbar_scroll(list
, adj_y
);
843 if (action
& BUTTON_REL
)
844 scroll_mode
= SCROLL_NONE
;
849 /* register y position unless forcefully reset to 1- */
853 return recurse
? gui_synclist_do_touchscreen(list
) : ACTION_REDRAW
;