mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-12-07 05:35:02 -05:00
touchscreen: Port list code to gesture API
Make use of the new gesture API to overhaul list touch support. This should fix most of the annoyances with touch navigation in menus and make the touchscreen easier to use. Change-Id: Ied300947fcf755e2810e9348496ed86eaf620669
This commit is contained in:
parent
e2363b0e2c
commit
67233114f7
3 changed files with 242 additions and 374 deletions
|
|
@ -46,13 +46,19 @@
|
||||||
#define ICON_PADDING 1
|
#define ICON_PADDING 1
|
||||||
#define ICON_PADDING_S "1"
|
#define ICON_PADDING_S "1"
|
||||||
|
|
||||||
|
#ifdef HAVE_TOUCHSCREEN
|
||||||
|
/* used in gui_synclist->scroll_mode */
|
||||||
|
enum {
|
||||||
|
SCROLL_NONE = 0, /* no scrolling */
|
||||||
|
SCROLL_BAR, /* scroll by using the scrollbar */
|
||||||
|
SCROLL_SWIPE, /* scroll by wiping over the screen */
|
||||||
|
SCROLL_KINETIC, /* state after releasing swipe */
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/* these are static to make scrolling work */
|
/* these are static to make scrolling work */
|
||||||
static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS];
|
static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS];
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREEN
|
|
||||||
static bool hide_selection;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* list-private helpers from the generic list.c (move to header?) */
|
/* list-private helpers from the generic list.c (move to header?) */
|
||||||
int gui_list_get_item_offset(struct gui_synclist * gui_list, int item_width,
|
int gui_list_get_item_offset(struct gui_synclist * gui_list, int item_width,
|
||||||
int text_pos, struct screen * display,
|
int text_pos, struct screen * display,
|
||||||
|
|
@ -363,7 +369,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
|
||||||
if(
|
if(
|
||||||
#ifdef HAVE_TOUCHSCREEN
|
#ifdef HAVE_TOUCHSCREEN
|
||||||
/* don't draw it during scrolling */
|
/* don't draw it during scrolling */
|
||||||
!hide_selection &&
|
list->scroll_mode == SCROLL_NONE &&
|
||||||
#endif
|
#endif
|
||||||
i >= list->selected_item
|
i >= list->selected_item
|
||||||
&& i < list->selected_item + list->selected_size)
|
&& i < list->selected_item + list->selected_size)
|
||||||
|
|
@ -448,43 +454,51 @@ void list_draw(struct screen *display, struct gui_synclist *list)
|
||||||
#if defined(HAVE_TOUCHSCREEN)
|
#if defined(HAVE_TOUCHSCREEN)
|
||||||
/* This needs to be fixed if we ever get more than 1 touchscreen on a target. */
|
/* This needs to be fixed if we ever get more than 1 touchscreen on a target. */
|
||||||
|
|
||||||
/* difference in pixels between draws, above it means enough to start scrolling */
|
static void do_touch_scroll(struct gui_synclist *gui_list, int new_y_pos)
|
||||||
#define SCROLL_BEGIN_THRESHOLD 3
|
|
||||||
|
|
||||||
static enum {
|
|
||||||
SCROLL_NONE, /* no scrolling */
|
|
||||||
SCROLL_BAR, /* scroll by using the scrollbar */
|
|
||||||
SCROLL_SWIPE, /* scroll by wiping over the screen */
|
|
||||||
SCROLL_KINETIC, /* state after releasing swipe */
|
|
||||||
} scroll_mode;
|
|
||||||
|
|
||||||
static int scrollbar_scroll(struct gui_synclist * gui_list, int y)
|
|
||||||
{
|
{
|
||||||
const int screen = screens[SCREEN_MAIN].screen_type;
|
if (new_y_pos < 0)
|
||||||
|
new_y_pos = 0;
|
||||||
|
|
||||||
|
int line_height = gui_list->line_height[SCREEN_MAIN];
|
||||||
|
int new_start = new_y_pos / line_height;
|
||||||
|
int nb_lines = list_get_nb_lines(gui_list, SCREEN_MAIN);
|
||||||
|
if (new_start > gui_list->nb_items - nb_lines)
|
||||||
|
{
|
||||||
|
new_start = gui_list->nb_items - nb_lines;
|
||||||
|
new_y_pos = new_start * line_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_item = new_start + nb_lines/2;
|
||||||
|
if (gui_list->selected_size > 1)
|
||||||
|
new_item -= new_item % gui_list->selected_size;
|
||||||
|
|
||||||
|
gui_list->selected_item = new_item;
|
||||||
|
gui_list->start_item[SCREEN_MAIN] = new_start;
|
||||||
|
gui_list->y_pos = new_y_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle touch scrolling using the scrollbar. Pass the screen y-coordinate
|
||||||
|
* of the current touch position. */
|
||||||
|
static int scrollbar_scroll(struct gui_synclist *gui_list, int y)
|
||||||
|
{
|
||||||
|
const enum screen_type screen = SCREEN_MAIN;
|
||||||
const int nb_lines = list_get_nb_lines(gui_list, screen);
|
const int nb_lines = list_get_nb_lines(gui_list, screen);
|
||||||
|
|
||||||
if (nb_lines < gui_list->nb_items)
|
if (nb_lines < gui_list->nb_items)
|
||||||
{
|
{
|
||||||
const int line_height = gui_list->line_height[screen];
|
const int line_height = gui_list->line_height[screen];
|
||||||
|
int bar_height = list_text[screen].height;
|
||||||
|
int bar_y = y - list_text[screen].y;
|
||||||
|
|
||||||
/* try to position the center of the scrollbar at the touch point */
|
if (bar_y < 0)
|
||||||
int scrollbar_size = list_text[screen].height;
|
bar_y = 0;
|
||||||
int actual_y = y - list_text[screen].y;
|
else if(bar_y >= bar_height)
|
||||||
int new_y_pos = (actual_y * gui_list->nb_items * line_height) / scrollbar_size;
|
bar_y = bar_height - 1;
|
||||||
int new_start = (actual_y * gui_list->nb_items) / scrollbar_size;
|
|
||||||
|
|
||||||
new_start -= nb_lines / 2;
|
int new_y_pos = (bar_y * gui_list->nb_items * line_height) / bar_height;
|
||||||
new_y_pos -= (nb_lines * line_height) / 2;
|
new_y_pos -= (nb_lines * line_height) / 2;
|
||||||
if(new_start < 0) {
|
|
||||||
new_start = 0;
|
|
||||||
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] = new_start;
|
do_touch_scroll(gui_list, new_y_pos);
|
||||||
gui_list->y_pos = new_y_pos;
|
|
||||||
|
|
||||||
return ACTION_REDRAW;
|
return ACTION_REDRAW;
|
||||||
}
|
}
|
||||||
|
|
@ -492,6 +506,21 @@ static int scrollbar_scroll(struct gui_synclist * gui_list, int y)
|
||||||
return ACTION_NONE;
|
return ACTION_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle swipe scrolling on a list. 'delta' is the distance to scroll
|
||||||
|
* relative to gui_list->scroll_base_y, which should be set up at the
|
||||||
|
* beginning of scrolling. */
|
||||||
|
static int swipe_scroll(struct gui_synclist *gui_list, int delta)
|
||||||
|
{
|
||||||
|
/* nothing to do if the list does not scroll */
|
||||||
|
const int nb_lines = list_get_nb_lines(gui_list, SCREEN_MAIN);
|
||||||
|
if (nb_lines >= gui_list->nb_items)
|
||||||
|
return ACTION_NONE;
|
||||||
|
|
||||||
|
do_touch_scroll(gui_list, gui_list->scroll_base_y - delta);
|
||||||
|
|
||||||
|
return ACTION_REDRAW;
|
||||||
|
}
|
||||||
|
|
||||||
/* kinetic scrolling, based on
|
/* kinetic scrolling, based on
|
||||||
*
|
*
|
||||||
* v = a*t + v0 and ds = v*dt
|
* v = a*t + v0 and ds = v*dt
|
||||||
|
|
@ -513,7 +542,6 @@ static int scrollbar_scroll(struct gui_synclist * gui_list, int y)
|
||||||
* relatively accurate
|
* relatively accurate
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#define SIGN(a) ((a) < 0 ? -1 : 1)
|
#define SIGN(a) ((a) < 0 ? -1 : 1)
|
||||||
/* these could possibly be configurable */
|
/* these could possibly be configurable */
|
||||||
/* the lower the smoother */
|
/* the lower the smoother */
|
||||||
|
|
@ -521,217 +549,92 @@ static int scrollbar_scroll(struct gui_synclist * gui_list, int y)
|
||||||
/* the higher the earler the list stops */
|
/* the higher the earler the list stops */
|
||||||
#define DECELERATION (1000*RELOAD_INTERVAL/HZ)
|
#define DECELERATION (1000*RELOAD_INTERVAL/HZ)
|
||||||
|
|
||||||
/* this array holds data to compute the initial velocity v0 */
|
struct kinetic_cb_data {
|
||||||
static struct kinetic_info {
|
struct gui_synclist *list;
|
||||||
int difference;
|
int velocity;
|
||||||
long ticks;
|
};
|
||||||
} kinetic_data[4];
|
|
||||||
static size_t cur_idx;
|
|
||||||
|
|
||||||
static struct cb_data {
|
struct kinetic {
|
||||||
struct gui_synclist *list; /* current list */
|
/* callback for performing the scroll animation */
|
||||||
int velocity; /* in pixel/s */
|
struct kinetic_cb_data cb_data;
|
||||||
} cb_data;
|
struct timeout tmo;
|
||||||
|
};
|
||||||
|
|
||||||
/* data member points to the above struct */
|
static struct kinetic kinetic;
|
||||||
static struct timeout kinetic_tmo;
|
static struct gesture_vel list_gvel;
|
||||||
|
|
||||||
static bool is_kinetic_over(void)
|
static void kinetic_stop_scrolling(struct kinetic *k, struct gui_synclist *list)
|
||||||
{
|
{
|
||||||
return !cb_data.velocity && (scroll_mode == SCROLL_KINETIC);
|
if (k->cb_data.list == list)
|
||||||
|
timeout_cancel(&k->tmo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* helper for gui/list.c to cancel scrolling if a normal button event comes */
|
||||||
* collect data about how fast the list is moved in order to compute
|
void _gui_synclist_stop_kinetic_scrolling(struct gui_synclist *list)
|
||||||
* the initial velocity from it later */
|
|
||||||
static void kinetic_stats_collect(const int difference)
|
|
||||||
{
|
{
|
||||||
static long last_tick;
|
if (list->scroll_mode == SCROLL_KINETIC)
|
||||||
/* collect velocity statistics */
|
|
||||||
kinetic_data[cur_idx].difference = difference;
|
|
||||||
kinetic_data[cur_idx].ticks = current_tick - last_tick;
|
|
||||||
|
|
||||||
last_tick = current_tick;
|
|
||||||
cur_idx += 1;
|
|
||||||
if (cur_idx >= ARRAYLEN(kinetic_data))
|
|
||||||
cur_idx = 0; /* rewind the index */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* resets the statistic */
|
|
||||||
static void kinetic_stats_reset(void)
|
|
||||||
{
|
|
||||||
memset(kinetic_data, 0, sizeof(kinetic_data));
|
|
||||||
cur_idx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cancels all currently active kinetic scrolling */
|
|
||||||
static void kinetic_force_stop(void)
|
|
||||||
{
|
|
||||||
timeout_cancel(&kinetic_tmo);
|
|
||||||
kinetic_stats_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* helper for gui/list.c to cancel scrolling if a normal button event comes
|
|
||||||
* through dpad or keyboard or whatever */
|
|
||||||
void _gui_synclist_stop_kinetic_scrolling(struct gui_synclist * gui_list)
|
|
||||||
{
|
|
||||||
const enum screen_type screen = screens[SCREEN_MAIN].screen_type;
|
|
||||||
gui_list->y_pos = gui_list->start_item[screen] * gui_list->line_height[screen];
|
|
||||||
|
|
||||||
if (scroll_mode == SCROLL_KINETIC)
|
|
||||||
kinetic_force_stop();
|
|
||||||
scroll_mode = SCROLL_NONE;
|
|
||||||
hide_selection = false;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* returns false if scrolling should be stopped entirely
|
|
||||||
*
|
|
||||||
* otherwise it returns true even if it didn't actually scroll,
|
|
||||||
* but scrolling mode shouldn't be changed
|
|
||||||
**/
|
|
||||||
|
|
||||||
|
|
||||||
static int scroll_begin_threshold;
|
|
||||||
static int threshold_accumulation;
|
|
||||||
static bool swipe_scroll(struct gui_synclist * gui_list, int difference)
|
|
||||||
{
|
|
||||||
/* fixme */
|
|
||||||
const enum screen_type screen = screens[SCREEN_MAIN].screen_type;
|
|
||||||
const int nb_lines = list_get_nb_lines(gui_list, screen);
|
|
||||||
const int line_height = gui_list->line_height[screen];
|
|
||||||
|
|
||||||
if (UNLIKELY(scroll_begin_threshold == 0))
|
|
||||||
scroll_begin_threshold = touchscreen_get_scroll_threshold();
|
|
||||||
|
|
||||||
/* make selecting items easier */
|
|
||||||
threshold_accumulation += abs(difference);
|
|
||||||
if (threshold_accumulation < scroll_begin_threshold && scroll_mode == SCROLL_NONE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
threshold_accumulation = 0;
|
|
||||||
|
|
||||||
/* does the list even scroll? if no, return but still show
|
|
||||||
* the caller that we would scroll */
|
|
||||||
if (nb_lines >= gui_list->nb_items)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const int old_start = gui_list->start_item[screen];
|
|
||||||
int new_start_item = -1;
|
|
||||||
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 */
|
|
||||||
bool hit_end = true;
|
|
||||||
|
|
||||||
/* Move the y position and clamp it (funny things happen otherwise...) */
|
|
||||||
gui_list->y_pos -= difference;
|
|
||||||
if(gui_list->y_pos < 0)
|
|
||||||
gui_list->y_pos = 0;
|
|
||||||
else if(gui_list->y_pos > max_y_pos)
|
|
||||||
gui_list->y_pos = max_y_pos;
|
|
||||||
else
|
|
||||||
hit_end = false;
|
|
||||||
|
|
||||||
/* Get the list y position. When pos_y differs by a line height or more,
|
|
||||||
* we need to scroll the list by adjusting the start item accordingly */
|
|
||||||
int cur_y = gui_list->start_item[screen] * line_height;
|
|
||||||
int diff_y = cur_y - gui_list->y_pos;
|
|
||||||
if (abs(diff_y) >= line_height)
|
|
||||||
{
|
{
|
||||||
line_diff = diff_y/line_height;
|
kinetic_stop_scrolling(&kinetic, list);
|
||||||
|
list->scroll_mode = SCROLL_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(line_diff != 0)
|
|
||||||
{
|
|
||||||
int selection_offset = gui_list->selected_item - old_start;
|
|
||||||
new_start_item = old_start - line_diff;
|
|
||||||
/* check if new_start_item is bigger than list item count */
|
|
||||||
if(new_start_item > gui_list->nb_items - nb_lines)
|
|
||||||
new_start_item = gui_list->nb_items - nb_lines;
|
|
||||||
/* set new_start_item to 0 if it's negative */
|
|
||||||
if(new_start_item < 0)
|
|
||||||
new_start_item = 0;
|
|
||||||
|
|
||||||
gui_list->start_item[screen] = new_start_item;
|
|
||||||
/* keep selected item in sync */
|
|
||||||
gui_list->selected_item = new_start_item + selection_offset;
|
|
||||||
if(gui_list->selected_size > 1)
|
|
||||||
gui_list->selected_item -= (gui_list->selected_item % gui_list->selected_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(hit_end)
|
|
||||||
return scroll_mode != SCROLL_KINETIC;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kinetic_callback(struct timeout *tmo)
|
static int kinetic_callback(struct timeout *tmo)
|
||||||
{
|
{
|
||||||
/* cancel if screen was pressed */
|
struct kinetic_cb_data *data = (struct kinetic_cb_data*)tmo->data;
|
||||||
if (scroll_mode != SCROLL_KINETIC)
|
struct gui_synclist *list = data->list;
|
||||||
|
|
||||||
|
/* deal with cancellation */
|
||||||
|
if (list->scroll_mode != SCROLL_KINETIC)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
struct cb_data *data = (struct cb_data*)tmo->data;
|
|
||||||
/* ds = v*dt */
|
/* ds = v*dt */
|
||||||
int pixel_diff = data->velocity * RELOAD_INTERVAL / HZ;
|
int pixel_diff = data->velocity * RELOAD_INTERVAL / HZ;
|
||||||
/* remember signedness to detect stopping */
|
int action = swipe_scroll(list, pixel_diff);
|
||||||
int old_sign = SIGN(data->velocity);
|
if (action == ACTION_REDRAW)
|
||||||
/* advance the list */
|
|
||||||
if (!swipe_scroll(data->list, pixel_diff))
|
|
||||||
{
|
{
|
||||||
/* nothing to scroll? */
|
/* force the list to redraw */
|
||||||
data->velocity = 0;
|
button_queue_post(BUTTON_REDRAW, 0);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
/* apply deceleration */
|
||||||
/* decelerate by a fixed amount
|
int old_sign = SIGN(data->velocity);
|
||||||
* decrementing v0 over time by the deceleration is
|
data->velocity -= SIGN(data->velocity) * DECELERATION;
|
||||||
* equivalent to computing v = a*t + v0 */
|
|
||||||
data->velocity -= SIGN(data->velocity)*DECELERATION;
|
|
||||||
if (SIGN(data->velocity) != old_sign)
|
if (SIGN(data->velocity) != old_sign)
|
||||||
data->velocity = 0;
|
data->velocity = 0;
|
||||||
}
|
|
||||||
|
|
||||||
/* let get_action() timeout, which loads to a
|
/* stop scrolling if we didn't move, it means we hit the end */
|
||||||
* gui_synclist_draw() call from the main thread */
|
if (list->y_pos == list->scroll_base_y)
|
||||||
button_queue_post(BUTTON_REDRAW, 0);
|
data->velocity = 0;
|
||||||
/* stop if the velocity hit or crossed zero */
|
else
|
||||||
if (!data->velocity)
|
/* update base y since our scroll distance doesn't accumulate. */
|
||||||
|
list->scroll_base_y = list->y_pos;
|
||||||
|
|
||||||
|
if (data->velocity == 0)
|
||||||
{
|
{
|
||||||
kinetic_stats_reset();
|
list->scroll_mode = SCROLL_NONE;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return RELOAD_INTERVAL; /* cancel or reload */
|
|
||||||
|
return RELOAD_INTERVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Computes the initial velocity v0 and sets up the timer */
|
||||||
* computes the initial velocity v0 and sets up the timer */
|
static bool kinetic_start_scrolling(struct kinetic *k, struct gui_synclist *list)
|
||||||
static bool kinetic_setup_scroll(struct gui_synclist *list)
|
|
||||||
{
|
{
|
||||||
/* compute initial velocity */
|
int xvel, yvel;
|
||||||
int i, _i, v0, len = ARRAYLEN(kinetic_data);
|
gesture_vel_get(&list_gvel, &xvel, &yvel);
|
||||||
for(i = 0, _i = 0, v0 = 0; i < len; i++)
|
if (yvel == 0)
|
||||||
{ /* in pixel/s */
|
|
||||||
if (kinetic_data[i].ticks > 0)
|
|
||||||
{
|
|
||||||
v0 += kinetic_data[i].difference*HZ/kinetic_data[i].ticks;
|
|
||||||
_i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_i > 0)
|
|
||||||
v0 /= _i;
|
|
||||||
else
|
|
||||||
v0 = 0;
|
|
||||||
|
|
||||||
if (v0 != 0)
|
|
||||||
{
|
|
||||||
cb_data.list = list;
|
|
||||||
cb_data.velocity = v0;
|
|
||||||
timeout_register(&kinetic_tmo, kinetic_callback, RELOAD_INTERVAL, (intptr_t)&cb_data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
k->cb_data.list = list;
|
||||||
|
k->cb_data.velocity = yvel;
|
||||||
|
|
||||||
|
list->scroll_mode = SCROLL_KINETIC;
|
||||||
|
list->scroll_base_y = list->y_pos;
|
||||||
|
timeout_register(&k->tmo, kinetic_callback, RELOAD_INTERVAL,
|
||||||
|
(intptr_t)&k->cb_data);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define OUTSIDE 0
|
#define OUTSIDE 0
|
||||||
|
|
@ -746,18 +649,20 @@ static bool kinetic_setup_scroll(struct gui_synclist *list)
|
||||||
|
|
||||||
static int get_click_location(struct gui_synclist *list, int x, int y)
|
static int get_click_location(struct gui_synclist *list, int x, int y)
|
||||||
{
|
{
|
||||||
int screen = SCREEN_MAIN;
|
const enum screen_type screen = SCREEN_MAIN;
|
||||||
struct viewport *parent, *title, *text;
|
|
||||||
int retval = OUTSIDE;
|
int retval = OUTSIDE;
|
||||||
|
|
||||||
parent = list->parent[screen];
|
struct viewport *parent = list->parent[screen];
|
||||||
|
struct viewport *text = &list_text[screen];
|
||||||
|
struct viewport *title = &title_text[screen];
|
||||||
|
|
||||||
if (viewport_point_within_vp(parent, x, y))
|
if (viewport_point_within_vp(parent, x, y))
|
||||||
{
|
{
|
||||||
/* see if the title was clicked */
|
/* see if the title was clicked */
|
||||||
title = &title_text[screen];
|
|
||||||
if (viewport_point_within_vp(title, x, y))
|
if (viewport_point_within_vp(title, x, y))
|
||||||
retval = TITLE_TEXT;
|
retval = TITLE_TEXT;
|
||||||
/* check the icon too */
|
|
||||||
|
/* check the title icon */
|
||||||
if (list->title_icon != Icon_NOICON && list->show_icons)
|
if (list->title_icon != Icon_NOICON && list->show_icons)
|
||||||
{
|
{
|
||||||
int width = list_icon_width(screen);
|
int width = list_icon_width(screen);
|
||||||
|
|
@ -770,12 +675,12 @@ static int get_click_location(struct gui_synclist *list, int x, int y)
|
||||||
if (viewport_point_within_vp(&vp, x, y))
|
if (viewport_point_within_vp(&vp, x, y))
|
||||||
retval = TITLE_ICON;
|
retval = TITLE_ICON;
|
||||||
}
|
}
|
||||||
/* check scrollbar. assume it's shown, if it isn't it will be handled
|
|
||||||
* later */
|
/* check scrollbar if shown */
|
||||||
if (retval == OUTSIDE)
|
if (retval == OUTSIDE)
|
||||||
{
|
{
|
||||||
bool on_scrollbar_clicked;
|
bool on_scrollbar_clicked;
|
||||||
int adj_x = x - parent->x;
|
int adj_x = x - text->x;
|
||||||
switch (list->scrollbar)
|
switch (list->scrollbar)
|
||||||
{
|
{
|
||||||
case SCROLLBAR_OFF:
|
case SCROLLBAR_OFF:
|
||||||
|
|
@ -787,12 +692,14 @@ static int get_click_location(struct gui_synclist *list, int x, int y)
|
||||||
on_scrollbar_clicked = adj_x <= SCROLLBAR_WIDTH;
|
on_scrollbar_clicked = adj_x <= SCROLLBAR_WIDTH;
|
||||||
break;
|
break;
|
||||||
case SCROLLBAR_RIGHT:
|
case SCROLLBAR_RIGHT:
|
||||||
on_scrollbar_clicked = adj_x > (title->x + title->width - SCROLLBAR_WIDTH);
|
on_scrollbar_clicked = adj_x > (text->x + text->width - SCROLLBAR_WIDTH);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (on_scrollbar_clicked)
|
if (on_scrollbar_clicked)
|
||||||
retval = SCROLLBAR;
|
retval = SCROLLBAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check the text area */
|
||||||
if (retval == OUTSIDE)
|
if (retval == OUTSIDE)
|
||||||
{
|
{
|
||||||
text = &list_text[screen];
|
text = &list_text[screen];
|
||||||
|
|
@ -805,170 +712,126 @@ static int get_click_location(struct gui_synclist *list, int x, int y)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned gui_synclist_do_touchscreen(struct gui_synclist * list)
|
unsigned gui_synclist_do_touchscreen(struct gui_synclist *list)
|
||||||
{
|
{
|
||||||
enum screen_type screen;
|
struct touchevent tevent;
|
||||||
struct viewport *parent;
|
struct gesture_event gevent;
|
||||||
short x, y;
|
|
||||||
int action, adj_x, adj_y, line, line_height, list_start_item;
|
|
||||||
bool recurse;
|
|
||||||
static bool initial_touch = true;
|
|
||||||
static int last_y;
|
|
||||||
|
|
||||||
screen = SCREEN_MAIN;
|
action_get_touch_event(&tevent);
|
||||||
parent = list->parent[screen];
|
if (!action_gesture_get_event(&gevent))
|
||||||
line_height = list->line_height[screen];
|
return ACTION_NONE;
|
||||||
list_start_item = list->start_item[screen];
|
|
||||||
/* start with getting the action code and finding the click location */
|
|
||||||
action = action_get_touchscreen_press(&x, &y);
|
|
||||||
adj_x = x - parent->x;
|
|
||||||
adj_y = y - parent->y;
|
|
||||||
|
|
||||||
|
const enum screen_type screen = SCREEN_MAIN;
|
||||||
/* some defaults before running the state machine */
|
struct viewport *list_vp = list->parent[screen];
|
||||||
recurse = false;
|
int adj_x = gevent.x - list_vp->x;
|
||||||
hide_selection = false;
|
int adj_y = gevent.y - list_vp->y;
|
||||||
|
int line_height = list->line_height[screen];
|
||||||
switch (scroll_mode)
|
int start_item = list->start_item[screen];
|
||||||
{
|
int start_y = start_item * line_height;
|
||||||
case SCROLL_NONE:
|
int action = ACTION_NONE;
|
||||||
{
|
|
||||||
int click_loc;
|
int click_loc;
|
||||||
if (initial_touch)
|
|
||||||
{
|
|
||||||
/* on the first touch last_y has to be reset to avoid
|
|
||||||
* glitches with touches from long ago */
|
|
||||||
last_y = adj_y;
|
|
||||||
initial_touch = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
line = 0; /* silence gcc 'used uninitialized' warning */
|
switch (gevent.id)
|
||||||
click_loc = get_click_location(list, x, y);
|
{
|
||||||
|
case GESTURE_NONE:
|
||||||
|
if (!action_gesture_is_pressed())
|
||||||
|
break;
|
||||||
|
/* fallthrough */
|
||||||
|
|
||||||
|
case GESTURE_TAP:
|
||||||
|
case GESTURE_LONG_PRESS:
|
||||||
|
_gui_synclist_stop_kinetic_scrolling(list);
|
||||||
|
click_loc = get_click_location(list, gevent.x, gevent.y);
|
||||||
if (click_loc & LIST)
|
if (click_loc & LIST)
|
||||||
{
|
{
|
||||||
|
int line;
|
||||||
if(!skinlist_get_item(&screens[screen], list, adj_x, adj_y, &line))
|
if(!skinlist_get_item(&screens[screen], list, adj_x, adj_y, &line))
|
||||||
{
|
{
|
||||||
/* selection needs to be corrected if items are only partially visible */
|
line = (adj_y - (start_y - list->y_pos)) / line_height;
|
||||||
int cur_y = list->start_item[screen] * line_height;
|
|
||||||
line = (adj_y - (cur_y - list->y_pos)) / line_height;
|
|
||||||
if (list_display_title(list, screen))
|
if (list_display_title(list, screen))
|
||||||
line -= 1; /* adjust for the list title */
|
line -= 1;
|
||||||
}
|
}
|
||||||
if (list_start_item+line >= list->nb_items)
|
|
||||||
return ACTION_NONE;
|
|
||||||
list->selected_item = list_start_item+line;
|
|
||||||
if(list->selected_size > 1)
|
|
||||||
list->selected_item -= (list->selected_item % list->selected_size);
|
|
||||||
|
|
||||||
|
int new_item = start_item + line;
|
||||||
|
if (new_item < list->nb_items)
|
||||||
|
{
|
||||||
|
if (list->selected_size > 1)
|
||||||
|
new_item -= new_item % list->selected_size;
|
||||||
|
|
||||||
|
list->selected_item = new_item;
|
||||||
gui_synclist_speak_item(list);
|
gui_synclist_speak_item(list);
|
||||||
|
|
||||||
|
if (gevent.id == GESTURE_TAP)
|
||||||
|
action = ACTION_STD_OK;
|
||||||
|
else if (gevent.id == GESTURE_LONG_PRESS)
|
||||||
|
action = ACTION_STD_CONTEXT;
|
||||||
|
else
|
||||||
|
action = ACTION_REDRAW;
|
||||||
}
|
}
|
||||||
if (action == BUTTON_TOUCHSCREEN)
|
}
|
||||||
|
else if ((click_loc & TITLE) && gevent.id == GESTURE_TAP)
|
||||||
{
|
{
|
||||||
/* if not scrolling, the user is trying to select */
|
if (click_loc & TITLE_TEXT)
|
||||||
int diff = adj_y - last_y;
|
action = ACTION_STD_CANCEL;
|
||||||
if ((click_loc & LIST) && swipe_scroll(list, diff))
|
|
||||||
scroll_mode = SCROLL_SWIPE;
|
|
||||||
else if (click_loc & SCROLLBAR)
|
|
||||||
scroll_mode = SCROLL_BAR;
|
|
||||||
}
|
|
||||||
else if (action == BUTTON_REPEAT)
|
|
||||||
{
|
|
||||||
if (click_loc & LIST)
|
|
||||||
{
|
|
||||||
/* held a single line for a while, bring up the context menu */
|
|
||||||
gui_synclist_select_item(list, list->selected_item);
|
|
||||||
/* don't sent context repeatedly */
|
|
||||||
action_wait_for_release();
|
|
||||||
initial_touch = true;
|
|
||||||
return ACTION_STD_CONTEXT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (action & BUTTON_REL)
|
|
||||||
{
|
|
||||||
initial_touch = true;
|
|
||||||
if (click_loc & LIST)
|
|
||||||
{ /* release on list item enters it */
|
|
||||||
gui_synclist_select_item(list, list->selected_item);
|
|
||||||
return ACTION_STD_OK;
|
|
||||||
}
|
|
||||||
else if (click_loc & TITLE_TEXT)
|
|
||||||
{ /* clicking the title goes one level up (cancel) */
|
|
||||||
return ACTION_STD_CANCEL;
|
|
||||||
}
|
|
||||||
else if (click_loc & TITLE_ICON)
|
else if (click_loc & TITLE_ICON)
|
||||||
{ /* clicking the title icon goes back to the root */
|
action = ACTION_STD_MENU;
|
||||||
return ACTION_STD_MENU;
|
|
||||||
}
|
}
|
||||||
}
|
else if (gevent.id != GESTURE_NONE && (click_loc & SCROLLBAR))
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SCROLL_SWIPE:
|
|
||||||
{
|
{
|
||||||
/* when swipe scrolling, we accept outside presses as well and
|
action = scrollbar_scroll(list, gevent.y);
|
||||||
* grab the entire screen (i.e. click_loc does not matter) */
|
|
||||||
int diff = adj_y - last_y;
|
|
||||||
hide_selection = true;
|
|
||||||
kinetic_stats_collect(diff);
|
|
||||||
if (swipe_scroll(list, diff))
|
|
||||||
{
|
|
||||||
/* letting the pen go enters kinetic scrolling */
|
|
||||||
if ((action & BUTTON_REL))
|
|
||||||
{
|
|
||||||
if (kinetic_setup_scroll(list))
|
|
||||||
{
|
|
||||||
hide_selection = true;
|
|
||||||
scroll_mode = SCROLL_KINETIC;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scroll_mode = SCROLL_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (action & BUTTON_REL)
|
|
||||||
scroll_mode = SCROLL_NONE;
|
|
||||||
|
|
||||||
if (scroll_mode == SCROLL_NONE)
|
/* allow long press to become a motion event */
|
||||||
initial_touch = true;
|
if (gevent.id != GESTURE_TAP)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SCROLL_KINETIC:
|
|
||||||
{
|
|
||||||
/* during kinetic scrolling we need to handle cancellation.
|
|
||||||
* This state is actually only entered upon end of it as this
|
|
||||||
* function is not called during the animation. */
|
|
||||||
if (!is_kinetic_over())
|
|
||||||
{ /* a) the user touched the screen (manual cancellation) */
|
|
||||||
kinetic_force_stop();
|
|
||||||
if (get_click_location(list, x, y) & SCROLLBAR)
|
|
||||||
scroll_mode = SCROLL_BAR;
|
|
||||||
else
|
|
||||||
scroll_mode = SCROLL_SWIPE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ /* b) kinetic scrolling stopped on its own */
|
|
||||||
/* need to re-run this with SCROLL_NONE since otherwise
|
|
||||||
* the next touch is not detected correctly */
|
|
||||||
scroll_mode = SCROLL_NONE;
|
|
||||||
recurse = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SCROLL_BAR:
|
|
||||||
{
|
|
||||||
hide_selection = true;
|
|
||||||
/* similarly to swipe scroll, using the scrollbar grabs
|
|
||||||
* focus so the click location is irrelevant */
|
|
||||||
scrollbar_scroll(list, y);
|
|
||||||
if (action & BUTTON_REL)
|
|
||||||
scroll_mode = SCROLL_NONE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* register y position unless forcefully reset */
|
if (action != ACTION_REDRAW)
|
||||||
if (!initial_touch)
|
gui_synclist_select_item(list, list->selected_item);
|
||||||
last_y = adj_y;
|
if (gevent.id != GESTURE_NONE)
|
||||||
|
action_gesture_reset();
|
||||||
|
break;
|
||||||
|
|
||||||
return recurse ? gui_synclist_do_touchscreen(list) : ACTION_REDRAW;
|
case GESTURE_DRAGSTART:
|
||||||
|
_gui_synclist_stop_kinetic_scrolling(list);
|
||||||
|
gesture_vel_reset(&list_gvel);
|
||||||
|
list->scroll_base_y = list->y_pos;
|
||||||
|
/* fallthrough */
|
||||||
|
|
||||||
|
case GESTURE_DRAG:
|
||||||
|
gesture_vel_process(&list_gvel, &tevent);
|
||||||
|
|
||||||
|
if (list->scroll_mode == SCROLL_NONE)
|
||||||
|
{
|
||||||
|
click_loc = get_click_location(list, gevent.ox, gevent.oy);
|
||||||
|
if (click_loc & SCROLLBAR)
|
||||||
|
list->scroll_mode = SCROLL_BAR;
|
||||||
|
else if (click_loc & LIST)
|
||||||
|
list->scroll_mode = SCROLL_SWIPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list->scroll_mode == SCROLL_BAR)
|
||||||
|
action = scrollbar_scroll(list, gevent.y);
|
||||||
|
else if (list->scroll_mode == SCROLL_SWIPE)
|
||||||
|
action = swipe_scroll(list, gevent.y - gevent.oy);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GESTURE_RELEASE:
|
||||||
|
if (list->scroll_mode != SCROLL_SWIPE ||
|
||||||
|
!kinetic_start_scrolling(&kinetic, list))
|
||||||
|
{
|
||||||
|
list->scroll_mode = SCROLL_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
action_gesture_reset();
|
||||||
|
action = ACTION_REDRAW;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,8 @@ void gui_synclist_init(struct gui_synclist * gui_list,
|
||||||
gui_synclist_init_display_settings(gui_list);
|
gui_synclist_init_display_settings(gui_list);
|
||||||
#ifdef HAVE_TOUCHSCREEN
|
#ifdef HAVE_TOUCHSCREEN
|
||||||
gui_list->y_pos = 0;
|
gui_list->y_pos = 0;
|
||||||
|
gui_list->scroll_base_y = 0;
|
||||||
|
gui_list->scroll_mode = 0;
|
||||||
#endif
|
#endif
|
||||||
FOR_NB_SCREENS(i)
|
FOR_NB_SCREENS(i)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#define _GUI_LIST_H_
|
#define _GUI_LIST_H_
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "action.h"
|
||||||
#include "icon.h"
|
#include "icon.h"
|
||||||
#include "screen_access.h"
|
#include "screen_access.h"
|
||||||
#include "skin_engine/skin_engine.h"
|
#include "skin_engine/skin_engine.h"
|
||||||
|
|
@ -155,10 +156,6 @@ struct gui_synclist
|
||||||
int nb_items;
|
int nb_items;
|
||||||
int selected_item;
|
int selected_item;
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREEN
|
|
||||||
/* absolute Y coordinate, used for smooth scrolling */
|
|
||||||
int y_pos;
|
|
||||||
#endif
|
|
||||||
int start_item[NB_SCREENS]; /* the item that is displayed at the top of the screen */
|
int start_item[NB_SCREENS]; /* the item that is displayed at the top of the screen */
|
||||||
/* the number of lines that are selected at the same time */
|
/* the number of lines that are selected at the same time */
|
||||||
int selected_size;
|
int selected_size;
|
||||||
|
|
@ -185,6 +182,12 @@ struct gui_synclist
|
||||||
struct list_selection_color *selection_color;
|
struct list_selection_color *selection_color;
|
||||||
#endif
|
#endif
|
||||||
struct viewport *parent[NB_SCREENS];
|
struct viewport *parent[NB_SCREENS];
|
||||||
|
|
||||||
|
#ifdef HAVE_TOUCHSCREEN
|
||||||
|
int y_pos; /* absolute Y coordinate, used for smooth scrolling */
|
||||||
|
int scroll_base_y; /* used for swipe scrolling */
|
||||||
|
int scroll_mode; /* see apps/gui/bitmap/list.c */
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue