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
;
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
];
159 line_height
= parent
->line_height
;
160 display
->set_viewport(parent
);
161 display
->clear_viewport();
162 display
->scroll_stop(list_text_vp
);
163 *list_text_vp
= *parent
;
164 list_text_vp
->line_height
= line_height
;
165 if ((show_title
= draw_title(display
, list
)))
167 int title_height
= title_text
[screen
].height
;
168 list_text_vp
->y
+= title_height
;
169 list_text_vp
->height
-= title_height
;
172 const int nb_lines
= viewport_get_nb_lines(list_text_vp
);
174 start
= list_start_item
;
175 end
= start
+ nb_lines
;
177 #ifdef HAVE_TOUCHSCREEN
178 if (list
->selected_item
== 0 || (list
->nb_items
< nb_lines
))
179 y_offset
= 0; /* reset in case it's a new list */
181 int draw_offset
= y_offset
;
182 /* draw some extra items to not have empty lines at the top and bottom */
185 /* make it negative for more consistent apparence when switching
187 draw_offset
-= line_height
;
191 else if (y_offset
< 0)
194 #define draw_offset 0
197 /* draw the scrollbar if its needed */
198 if (global_settings
.scrollbar
&& nb_lines
< list
->nb_items
)
200 struct viewport vp
= *list_text_vp
;
201 vp
.width
= SCROLLBAR_WIDTH
;
202 vp
.height
= line_height
* nb_lines
;
204 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
205 if (scrollbar_in_left
)
206 list_text_vp
->x
+= SCROLLBAR_WIDTH
;
208 vp
.x
+= list_text_vp
->width
;
209 display
->set_viewport(&vp
);
210 gui_scrollbar_draw(display
,
211 (scrollbar_in_left
? 0: 1), 0, SCROLLBAR_WIDTH
-1, vp
.height
,
212 list
->nb_items
, list_start_item
, list_start_item
+ nb_lines
,
217 /* shift everything a bit in relation to the title... */
218 if (!VP_IS_RTL(list_text_vp
) && scrollbar_in_left
)
220 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
221 list_text_vp
->x
+= SCROLLBAR_WIDTH
;
223 else if (VP_IS_RTL(list_text_vp
) && !scrollbar_in_left
)
225 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
229 /* setup icon placement */
230 list_icons
= *list_text_vp
;
231 int icon_count
= (list
->callback_get_item_icon
!= NULL
) ? 1 : 0;
236 list_icons
.width
= icon_width
* icon_count
;
237 list_text_vp
->width
-= list_icons
.width
+ ICON_PADDING
;
238 if (VP_IS_RTL(&list_icons
))
239 list_icons
.x
+= list_text_vp
->width
+ ICON_PADDING
;
241 list_text_vp
->x
+= list_icons
.width
+ ICON_PADDING
;
242 icon_yoffset
= (line_height
- list_icon_height(screen
)) / 2;
245 for (i
=start
; i
<end
&& i
<list
->nb_items
; i
++)
248 unsigned const char *s
;
249 char entry_buffer
[MAX_PATH
];
250 unsigned char *entry_name
;
252 int line
= i
- start
;
254 s
= list
->callback_get_item_name(i
, list
->data
, entry_buffer
,
255 sizeof(entry_buffer
));
256 entry_name
= P2STR(s
);
258 while (*entry_name
== '\t')
266 indent
*= icon_width
;
268 indent
*= display
->getcharwidth();
270 list_icons
.x
+= indent
;
271 list_text_vp
->x
+= indent
;
272 list_text_vp
->width
-= indent
;
275 display
->set_viewport(list_text_vp
);
276 style
= STYLE_DEFAULT
;
277 /* position the string at the correct offset place */
279 display
->getstringsize(entry_name
, &item_width
, &h
);
280 item_offset
= gui_list_get_item_offset(list
, item_width
, text_pos
,
281 display
, list_text_vp
);
283 #ifdef HAVE_LCD_COLOR
284 /* if the list has a color callback */
285 if (list
->callback_get_item_color
)
287 int color
= list
->callback_get_item_color(i
, list
->data
);
288 /* if color selected */
291 style
|= STYLE_COLORED
|color
;
295 /* draw the selected line */
297 #ifdef HAVE_TOUCHSCREEN
298 /* don't draw it during scrolling */
301 i
>= list
->selected_item
302 && i
< list
->selected_item
+ list
->selected_size
303 && list
->show_selection_marker
)
304 {/* The selected item must be displayed scrolling */
305 if (global_settings
.cursor_style
== 1
306 #ifdef HAVE_REMOTE_LCD
307 /* the global_settings.cursor_style check is here to make
308 * sure if they want the cursor instead of bar it will work
310 || (display
->depth
< 16 && global_settings
.cursor_style
)
314 /* Display inverted-line-style */
315 style
= STYLE_INVERT
;
317 #ifdef HAVE_LCD_COLOR
318 else if (global_settings
.cursor_style
== 2)
320 /* Display colour line selector */
321 style
= STYLE_COLORBAR
;
323 else if (global_settings
.cursor_style
== 3)
325 /* Display gradient line selector */
326 style
= STYLE_GRADIENT
;
328 /* Make the lcd driver know how many lines the gradient should
329 cover and current line number */
330 /* number of selected lines */
331 style
|= NUMLN_PACK(list
->selected_size
);
332 /* current line number, zero based */
333 style
|= CURLN_PACK(cur_line
);
337 /* if the text is smaller than the viewport size */
338 if (item_offset
> item_width
- (list_text_vp
->width
- text_pos
))
341 display
->puts_style_xyoffset(0, line
, entry_name
,
342 style
, item_offset
, draw_offset
);
346 display
->puts_scroll_style_xyoffset(0, line
, entry_name
,
347 style
, item_offset
, draw_offset
);
352 if (list
->scroll_all
)
353 display
->puts_scroll_style_xyoffset(0, line
, entry_name
,
354 style
, item_offset
, draw_offset
);
356 display
->puts_style_xyoffset(0, line
, entry_name
,
357 style
, item_offset
, draw_offset
);
360 display
->set_viewport(&list_icons
);
361 if (list
->callback_get_item_icon
!= NULL
)
363 int xoff
= show_cursor
? list_icon_width(screen
) : 0;
364 screen_put_iconxy(display
, xoff
,
365 line
*line_height
+ draw_offset
+ icon_yoffset
,
366 list
->callback_get_item_icon(i
, list
->data
));
369 if (show_cursor
&& i
>= list
->selected_item
&&
370 i
< list
->selected_item
+ list
->selected_size
)
372 screen_put_iconxy(display
, 0,
373 line
*line_height
+ draw_offset
+ icon_yoffset
,
378 list_icons
.x
-= indent
;
379 list_text_vp
->x
-= indent
;
380 list_text_vp
->width
+= indent
;
383 display
->set_viewport(parent
);
384 display
->update_viewport();
385 display
->set_viewport(NULL
);
388 #if defined(HAVE_TOUCHSCREEN)
389 /* This needs to be fixed if we ever get more than 1 touchscreen on a target. */
391 /* difference in pixels between draws, above it means enough to start scrolling */
392 #define SCROLL_BEGIN_THRESHOLD 3
395 SCROLL_NONE
, /* no scrolling */
396 SCROLL_BAR
, /* scroll by using the scrollbar */
397 SCROLL_SWIPE
, /* scroll by wiping over the screen */
398 SCROLL_KINETIC
, /* state after releasing swipe */
401 static int scrollbar_scroll(struct gui_synclist
* gui_list
,
404 const int screen
= screens
[SCREEN_MAIN
].screen_type
;
405 const int nb_lines
= viewport_get_nb_lines(&list_text
[screen
]);
407 if (nb_lines
< gui_list
->nb_items
)
409 /* scrollbar scrolling is still line based */
411 int scrollbar_size
= nb_lines
*gui_list
->parent
[screen
]->line_height
;
412 int actual_y
= y
- list_text
[screen
].y
;
414 int new_selection
= (actual_y
* gui_list
->nb_items
)
417 int start_item
= new_selection
- nb_lines
/2;
420 else if(start_item
> gui_list
->nb_items
- nb_lines
)
421 start_item
= gui_list
->nb_items
- nb_lines
;
423 gui_list
->start_item
[screen
] = start_item
;
425 return ACTION_REDRAW
;
431 /* kinetic scrolling, based on
433 * v = a*t + v0 and ds = v*dt
435 * In each (fixed interval) timeout, the list is advanced by ds, then
436 * the v is reduced by a.
437 * This way we get a linear and smooth deceleration of the scrolling
439 * As v is the difference of distance per time unit, v is passed (as
440 * pixels moved since the last call) to the scrolling function which takes
441 * care of the pixel accurate drawing
443 * v0 is dertermined by averaging the last 4 movements of the list
444 * (the pixel and time difference is used to compute each v)
446 * influenced by http://stechz.com/tag/kinetic/
447 * We take the easy and smooth first approach (until section "Drawbacks"),
448 * since its drawbacks don't apply for us since our timers seem to be
449 * relatively accurate
453 #define SIGN(a) ((a) < 0 ? -1 : 1)
454 /* these could possibly be configurable */
455 /* the lower the smoother */
456 #define RELOAD_INTERVAL (HZ/25)
457 /* the higher the earler the list stops */
458 #define DECELERATION (1000*RELOAD_INTERVAL/HZ)
460 /* this array holds data to compute the initial velocity v0 */
461 static struct kinetic_info
{
465 static size_t cur_idx
;
467 static struct cb_data
{
468 struct gui_synclist
*list
; /* current list */
469 int velocity
; /* in pixel/s */
472 /* data member points to the above struct */
473 static struct timeout kinetic_tmo
;
475 static bool is_kinetic_over(void)
477 return !cb_data
.velocity
&& (scroll_mode
== SCROLL_KINETIC
);
481 * collect data about how fast the list is moved in order to compute
482 * the initial velocity from it later */
483 static void kinetic_stats_collect(const int difference
)
485 static long last_tick
;
486 /* collect velocity statistics */
487 kinetic_data
[cur_idx
].difference
= difference
;
488 kinetic_data
[cur_idx
].ticks
= current_tick
- last_tick
;
490 last_tick
= current_tick
;
492 if (cur_idx
>= ARRAYLEN(kinetic_data
))
493 cur_idx
= 0; /* rewind the index */
497 * resets the statistic */
498 static void kinetic_stats_reset(void)
500 memset(kinetic_data
, 0, sizeof(kinetic_data
));
504 /* cancels all currently active kinetic scrolling */
505 static void kinetic_force_stop(void)
507 timeout_cancel(&kinetic_tmo
);
508 kinetic_stats_reset();
511 /* helper for gui/list.c to cancel scrolling if a normal button event comes
512 * through dpad or keyboard or whatever */
513 void _gui_synclist_stop_kinetic_scrolling(void)
516 if (scroll_mode
== SCROLL_KINETIC
)
517 kinetic_force_stop();
518 scroll_mode
= SCROLL_NONE
;
519 hide_selection
= false;
522 * returns false if scrolling should be stopped entirely
524 * otherwise it returns true even if it didn't actually scroll,
525 * but scrolling mode shouldn't be changed
529 static int scroll_begin_threshold
;
530 static int threshold_accumulation
;
531 static bool swipe_scroll(struct gui_synclist
* gui_list
, int difference
)
534 const enum screen_type screen
= screens
[SCREEN_MAIN
].screen_type
;
535 const int nb_lines
= viewport_get_nb_lines(&list_text
[screen
]);
536 const int line_height
= gui_list
->parent
[0]->line_height
;
538 if (UNLIKELY(scroll_begin_threshold
== 0))
539 scroll_begin_threshold
= touchscreen_get_scroll_threshold();
541 /* make selecting items easier */
542 threshold_accumulation
+= abs(difference
);
543 if (threshold_accumulation
< scroll_begin_threshold
&& scroll_mode
== SCROLL_NONE
)
546 threshold_accumulation
= 0;
548 /* does the list even scroll? if no, return but still show
549 * the caller that we would scroll */
550 if (nb_lines
>= gui_list
->nb_items
)
553 const int old_start
= gui_list
->start_item
[screen
];
554 int new_start_item
= -1;
557 /* don't scroll at the edges of the list */
558 if ((old_start
== 0 && difference
> 0)
559 || (old_start
== (gui_list
->nb_items
- nb_lines
) && difference
< 0))
562 gui_list
->start_item
[screen
] = old_start
;
563 return scroll_mode
!= SCROLL_KINETIC
; /* stop kinetic at the edges */
566 /* add up y_offset over time and translate to lines
567 * if scrolled enough */
568 y_offset
+= difference
;
569 if (abs(y_offset
) > line_height
)
571 line_diff
= y_offset
/line_height
;
572 y_offset
-= line_diff
* line_height
;
577 int selection_offset
= gui_list
->selected_item
- old_start
;
578 new_start_item
= old_start
- line_diff
;
579 /* check if new_start_item is bigger than list item count */
580 if(new_start_item
> gui_list
->nb_items
- nb_lines
)
581 new_start_item
= gui_list
->nb_items
- nb_lines
;
582 /* set new_start_item to 0 if it's negative */
583 if(new_start_item
< 0)
586 gui_list
->start_item
[screen
] = new_start_item
;
587 /* keep selected item in sync */
588 gui_list
->selected_item
= new_start_item
+ selection_offset
;
594 static int kinetic_callback(struct timeout
*tmo
)
596 /* cancel if screen was pressed */
597 if (scroll_mode
!= SCROLL_KINETIC
)
600 struct cb_data
*data
= (struct cb_data
*)tmo
->data
;
602 int pixel_diff
= data
->velocity
* RELOAD_INTERVAL
/ HZ
;
603 /* remember signedness to detect stopping */
604 int old_sign
= SIGN(data
->velocity
);
605 /* advance the list */
606 if (!swipe_scroll(data
->list
, pixel_diff
))
608 /* nothing to scroll? */
613 /* decelerate by a fixed amount
614 * decrementing v0 over time by the deceleration is
615 * equivalent to computing v = a*t + v0 */
616 data
->velocity
-= SIGN(data
->velocity
)*DECELERATION
;
617 if (SIGN(data
->velocity
) != old_sign
)
621 /* let get_action() timeout, which loads to a
622 * gui_synclist_draw() call from the main thread */
623 queue_post(&button_queue
, BUTTON_REDRAW
, 0);
624 /* stop if the velocity hit or crossed zero */
627 kinetic_stats_reset();
630 return RELOAD_INTERVAL
; /* cancel or reload */
634 * computes the initial velocity v0 and sets up the timer */
635 static bool kinetic_setup_scroll(struct gui_synclist
*list
)
637 /* compute initial velocity */
638 int i
, _i
, v0
, len
= ARRAYLEN(kinetic_data
);
639 for(i
= 0, _i
= 0, v0
= 0; i
< len
; i
++)
641 if (kinetic_data
[i
].ticks
> 0)
643 v0
+= kinetic_data
[i
].difference
*HZ
/kinetic_data
[i
].ticks
;
655 cb_data
.velocity
= v0
;
656 timeout_register(&kinetic_tmo
, kinetic_callback
, RELOAD_INTERVAL
, (intptr_t)&cb_data
);
663 #define TITLE_TEXT (1<<0)
664 #define TITLE_ICON (1<<1)
665 #define SCROLLBAR (1<<2)
666 #define LIST_TEXT (1<<3)
667 #define LIST_ICON (1<<4)
669 #define TITLE (TITLE_TEXT|TITLE_ICON)
670 #define LIST (LIST_TEXT|LIST_ICON)
672 static int get_click_location(struct gui_synclist
*list
, int x
, int y
)
674 int screen
= SCREEN_MAIN
;
675 struct viewport
*parent
, *title
, *text
;
676 int retval
= OUTSIDE
;
678 parent
= list
->parent
[screen
];
679 if (viewport_point_within_vp(parent
, x
, y
))
681 /* see if the title was clicked */
682 title
= &title_text
[screen
];
683 if (viewport_point_within_vp(title
, x
, y
))
685 /* check the icon too */
686 if (list
->title_icon
!= Icon_NOICON
&& global_settings
.show_icons
)
688 int width
= list_icon_width(screen
);
689 struct viewport vp
= *title
;
695 if (viewport_point_within_vp(&vp
, x
, y
))
698 /* check scrollbar. assume it's shown, if it isn't it will be handled
700 if (retval
== OUTSIDE
)
702 bool on_scrollbar_clicked
;
703 int adj_x
= x
- parent
->x
;
704 switch (global_settings
.scrollbar
)
707 on_scrollbar_clicked
= adj_x
<= SCROLLBAR_WIDTH
; break;
708 case SCROLLBAR_RIGHT
:
709 on_scrollbar_clicked
= adj_x
> (title
->x
+ title
->width
- SCROLLBAR_WIDTH
); break;
711 on_scrollbar_clicked
= false; break;
713 if (on_scrollbar_clicked
)
716 if (retval
== OUTSIDE
)
718 text
= &list_text
[screen
];
719 if (viewport_point_within_vp(text
, x
, y
))
721 else /* if all fails, it must be on the list icons */
728 unsigned gui_synclist_do_touchscreen(struct gui_synclist
* list
)
730 enum screen_type screen
;
731 struct viewport
*parent
;
733 int action
, adj_x
, adj_y
, line
, line_height
, list_start_item
;
735 static int last_y
= -1;
737 screen
= SCREEN_MAIN
;
738 parent
= list
->parent
[screen
];
739 line_height
= list
->parent
[screen
]->line_height
;
740 list_start_item
= list
->start_item
[screen
];
741 /* start with getting the action code and finding the click location */
742 action
= action_get_touchscreen_press(&x
, &y
);
743 adj_x
= x
- parent
->x
;
744 adj_y
= y
- parent
->y
;
747 /* some defaults before running the state machine */
749 hide_selection
= false;
756 { /* first run. register adj_y and re-run (will then take the else case) */
762 int click_loc
= get_click_location(list
, x
, y
);
763 line
= 0; /* silence gcc 'used uninitialized' warning */
764 if (click_loc
& LIST
)
766 if(!skinlist_get_item(&screens
[screen
], list
, adj_x
, adj_y
, &line
))
768 /* selection needs to be corrected if items are only partially visible */
769 line
= (adj_y
- y_offset
) / line_height
;
770 if (list_display_title(list
, screen
))
771 line
-= 1; /* adjust for the list title */
773 if (line
>= list
->nb_items
)
775 list
->selected_item
= list_start_item
+line
;
777 gui_synclist_speak_item(list
);
779 if (action
== BUTTON_TOUCHSCREEN
)
781 /* if not scrolling, the user is trying to select */
782 int diff
= adj_y
- last_y
;
783 if ((click_loc
& LIST
) && swipe_scroll(list
, diff
))
784 scroll_mode
= SCROLL_SWIPE
;
785 else if (click_loc
& SCROLLBAR
)
786 scroll_mode
= SCROLL_BAR
;
788 else if (action
== BUTTON_REPEAT
)
790 if (click_loc
& LIST
)
792 /* held a single line for a while, bring up the context menu */
793 gui_synclist_select_item(list
, list_start_item
+ line
);
794 /* don't sent context repeatedly */
795 action_wait_for_release();
797 return ACTION_STD_CONTEXT
;
800 else if (action
& BUTTON_REL
)
803 if (click_loc
& LIST
)
804 { /* release on list item enters it */
805 gui_synclist_select_item(list
, list_start_item
+ line
);
806 return ACTION_STD_OK
;
808 else if (click_loc
& TITLE_TEXT
)
809 { /* clicking the title goes one level up (cancel) */
810 return ACTION_STD_CANCEL
;
812 else if (click_loc
& TITLE_ICON
)
813 { /* clicking the title icon goes back to the root */
814 return ACTION_STD_MENU
;
822 /* when swipe scrolling, we accept outside presses as well and
823 * grab the entire screen (i.e. click_loc does not matter) */
824 int diff
= adj_y
- last_y
;
825 hide_selection
= true;
826 kinetic_stats_collect(diff
);
827 if (swipe_scroll(list
, diff
))
829 /* letting the pen go enters kinetic scrolling */
830 if ((action
& BUTTON_REL
))
832 if (kinetic_setup_scroll(list
))
834 hide_selection
= true;
835 scroll_mode
= SCROLL_KINETIC
;
838 scroll_mode
= SCROLL_NONE
;
841 else if (action
& BUTTON_REL
)
842 scroll_mode
= SCROLL_NONE
;
844 if (scroll_mode
== SCROLL_NONE
)
850 /* during kinetic scrolling we need to handle cancellation.
851 * This state is actually only entered upon end of it as this
852 * function is not called during the animation. */
853 if (!is_kinetic_over())
854 { /* a) the user touched the screen (manual cancellation) */
855 kinetic_force_stop();
856 if (get_click_location(list
, x
, y
) & SCROLLBAR
)
857 scroll_mode
= SCROLL_BAR
;
859 scroll_mode
= SCROLL_SWIPE
;
862 { /* b) kinetic scrolling stopped on its own */
863 /* need to re-run this with SCROLL_NONE since otherwise
864 * the next touch is not detected correctly */
865 scroll_mode
= SCROLL_NONE
;
872 hide_selection
= true;
873 /* similarly to swipe scroll, using the scrollbar grabs
874 * focus so the click location is irrelevant */
875 scrollbar_scroll(list
, adj_y
);
876 if (action
& BUTTON_REL
)
877 scroll_mode
= SCROLL_NONE
;
882 /* register y position unless forcefully reset to 1- */
886 return recurse
? gui_synclist_do_touchscreen(list
) : ACTION_REDRAW
;