diff --git a/apps/gui/skin_engine/skin_engine.c b/apps/gui/skin_engine/skin_engine.c index f54d1bc751..bc2f599b4e 100644 --- a/apps/gui/skin_engine/skin_engine.c +++ b/apps/gui/skin_engine/skin_engine.c @@ -119,6 +119,7 @@ static void gui_skin_reset(struct gui_skin *skin) skin->data.tree = -1; #ifdef HAVE_TOUCHSCREEN skin->data.touchregions = -1; + gesture_reset(&skin->data.gesture); #endif #ifdef HAVE_SKIN_VARIABLES skin->data.skinvars = -1; diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c index bdf57fbf8d..c2d597d329 100644 --- a/apps/gui/skin_engine/skin_parser.c +++ b/apps/gui/skin_engine/skin_parser.c @@ -1284,7 +1284,6 @@ static int parse_progressbar_tag(struct skin_element* element, region->allow_while_locked = false; region->press_length = PRESS; region->last_press = -1; - region->armed = false; region->bar = PTRTOSKINOFFSET(skin_buffer, pb); region->label = PTRTOSKINOFFSET(skin_buffer, NULL); @@ -1781,13 +1780,13 @@ static int parse_touchregion(struct skin_element *element, p++; region->wvp = PTRTOSKINOFFSET(skin_buffer, curr_vp); - region->armed = false; region->reverse_bar = false; region->value = 0; region->last_press = -1; region->press_length = PRESS; region->allow_while_locked = false; region->bar = PTRTOSKINOFFSET(skin_buffer, NULL); + action = get_param_text(element, p++); /* figure out the action */ diff --git a/apps/gui/skin_engine/skin_touchsupport.c b/apps/gui/skin_engine/skin_touchsupport.c index 4479b4754d..7eeed6ecb4 100644 --- a/apps/gui/skin_engine/skin_touchsupport.c +++ b/apps/gui/skin_engine/skin_touchsupport.c @@ -37,16 +37,7 @@ /** Disarms all touchregions. */ void skin_disarm_touchregions(struct gui_wps *gwps) { - struct wps_data *data = gwps->data; - char* skin_buffer = get_skin_buffer(data); - struct skin_token_list *regions = SKINOFFSETTOPTR(skin_buffer, data->touchregions); - while (regions) - { - struct wps_token *token = SKINOFFSETTOPTR(skin_buffer, regions->token); - struct touchregion *region = SKINOFFSETTOPTR(skin_buffer, token->value.data); - region->armed = false; - regions = SKINOFFSETTOPTR(skin_buffer, regions->next); - } + gesture_reset(&gwps->data->gesture); } /* Get the touched action. @@ -56,272 +47,293 @@ void skin_disarm_touchregions(struct gui_wps *gwps) int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset) { struct wps_data *data = gwps->data; - int returncode = ACTION_NONE; - short x,y; - short vx, vy; - int type = action_get_touchscreen_press(&x, &y); - struct skin_viewport *wvp; - struct touchregion *r, *temp = NULL; char* skin_buffer = get_skin_buffer(data); - bool repeated = (type == BUTTON_REPEAT); - bool released = (type == BUTTON_REL); - bool pressed = (type == BUTTON_TOUCHSCREEN); - struct skin_token_list *regions = SKINOFFSETTOPTR(skin_buffer, data->touchregions); - bool needs_repeat; + struct touchregion *region = NULL; + struct touchevent tevent; + struct gesture_event gevent; - while (regions) + action_get_touch_event(&tevent); + gesture_process(&data->gesture, &tevent); + + struct skin_token_list *regions = SKINOFFSETTOPTR(skin_buffer, data->touchregions); + for (; regions && !region; + regions = SKINOFFSETTOPTR(skin_buffer, regions->next)) { struct wps_token *token = SKINOFFSETTOPTR(skin_buffer, regions->token); - r = SKINOFFSETTOPTR(skin_buffer, token->value.data); - wvp = SKINOFFSETTOPTR(skin_buffer, r->wvp); + struct touchregion *r = SKINOFFSETTOPTR(skin_buffer, token->value.data); + struct skin_viewport *wvp = SKINOFFSETTOPTR(skin_buffer, r->wvp); + /* make sure this region's viewport is visible */ - if (wvp->hidden_flags&VP_DRAW_HIDDEN) - { - regions = SKINOFFSETTOPTR(skin_buffer, regions->next); + if (wvp->hidden_flags & VP_DRAW_HIDDEN) continue; - } + + /* unless it's allow_while_locked, ignore the region if locked + * (this is a special skin engine lock, different from softlock) */ if (data->touchscreen_locked && (r->action != ACTION_TOUCH_SOFTLOCK && !r->allow_while_locked)) - { - regions = SKINOFFSETTOPTR(skin_buffer, regions->next); continue; - } - needs_repeat = r->press_length != PRESS; - /* check if it's inside this viewport */ - if (viewport_point_within_vp(&(wvp->vp), x, y)) - { /* reposition the touch inside the viewport since touchregions - * are relative to a preceding viewport */ - vx = x - wvp->vp.x; - vy = y - wvp->vp.y; - /* project touches in the padding region so they clamp to the - * edge of the region instead */ - if(r->x - r->wpad <= vx && vx < r->x) - vx = r->x; - else if(r->x + r->width <= vx && vx < r->x + r->width + r->wpad) - vx = r->x + r->width - 1; - if(r->y - r->hpad <= vy && vy < r->y) - vy = r->y; - else if(r->y + r->height <= vy && vy < r->y + r->height + r->hpad) - vy = r->y + r->height - 1; + /* check for a gesture inside the region's parent viewport */ + if (!gesture_get_event_in_vp(&data->gesture, &gevent, &wvp->vp)) + continue; - /* now see if the point is inside this region */ - if (vx >= r->x && vx < r->x+r->width && - vy >= r->y && vy < r->y+r->height) + /* project touches inside the padding box to the region edge */ + if (r->x - r->wpad <= gevent.ox && gevent.ox < r->x) + gevent.ox = r->x; + else if (r->x + r->width <= gevent.ox && gevent.ox < r->x + r->width + r->wpad) + gevent.ox = r->x + r->width - 1; + + if (r->y - r->hpad <= gevent.y && gevent.y < r->y) + gevent.oy = r->y; + else if (r->y + r->height <= gevent.oy && gevent.oy < r->y + r->height + r->hpad) + gevent.oy = r->y + r->height - 1; + + /* ignore anything outside of the region */ + if (gevent.ox < r->x || gevent.ox >= r->x + r->width) + continue; + if (gevent.oy < r->y || gevent.oy >= r->y + r->height) + continue; + + /* convert coordinates to region-relative and clamp */ + gevent.x -= r->x; + gevent.y -= r->y; + + switch (r->action) + { + case ACTION_TOUCH_SCROLLBAR: + case ACTION_TOUCH_VOLUME: + case ACTION_TOUCH_SETTING: + /* we need something press-like */ + if (!(gevent.id == GESTURE_TAP || + gevent.id == GESTURE_LONG_PRESS || + gevent.id == GESTURE_HOLD || + gevent.id == GESTURE_DRAGSTART || + gevent.id == GESTURE_DRAG)) + break; + + if (edge_offset) { - /* reposition the touch within the area */ - vx -= r->x; - vy -= r->y; + struct progressbar *bar = SKINOFFSETTOPTR(skin_buffer, r->bar); + bool reverse = r->reverse_bar || (bar && bar->invert_fill_direction); + int pos, lim; - switch(r->action) - { - case ACTION_TOUCH_SCROLLBAR: - case ACTION_TOUCH_VOLUME: - case ACTION_TOUCH_SETTING: - if (edge_offset) - { - struct progressbar *bar = - SKINOFFSETTOPTR(skin_buffer, r->bar); - if(r->width > r->height) { - if(r->width > 1) - *edge_offset = vx*1000/(r->width - 1); - else - *edge_offset = 0; - } else { - /* vertical bars are bottom-up by default */ - if(r->height > 1) - *edge_offset = 1000 - vy*1000/(r->height - 1); - else - *edge_offset = 0; - } - - if (r->reverse_bar || (bar && bar->invert_fill_direction)) - *edge_offset = 1000 - *edge_offset; - } - temp = r; - returncode = r->action; - r->last_press = current_tick; - break; - default: - if (r->armed && ((repeated && needs_repeat) || - (released && !needs_repeat))) - { - returncode = r->action; - temp = r; - } - if (pressed) - { - r->armed = true; - r->last_press = current_tick; - } - break; + if (r->width > r->height) { + /* left to right by default */ + pos = MIN(MAX(0, gevent.x), r->width - 1); + lim = r->width; + } else { + /* bottom up by default, so we need to add a reversal */ + pos = MIN(MAX(0, gevent.y), r->height - 1); + lim = r->height; + reverse = !reverse; } + + if (lim > 1) + *edge_offset = pos * 1000 / (lim - 1); + else + *edge_offset = 0; + + if (reverse) + *edge_offset = 1000 - *edge_offset; } + + region = r; + break; + + default: + if (r->press_length == PRESS) + { + if (gevent.id != GESTURE_TAP) + break; + } + else if (r->press_length == LONG_PRESS) + { + if (gevent.id != GESTURE_LONG_PRESS) + break; + } + else /* REPEAT */ + { + /* for repeat regions we allow dragging inside the region */ + if (gevent.id != GESTURE_HOLD && + gevent.id != GESTURE_DRAGSTART && + gevent.id != GESTURE_DRAG) + break; + + if (gevent.x < 0 || gevent.x >= r->width || + gevent.y < 0 || gevent.y >= r->height) + break; + } + + /* gesture is OK */ + region = r; + break; } - regions = SKINOFFSETTOPTR(skin_buffer, regions->next); } - /* On release, all regions are disarmed. */ - if (released) - skin_disarm_touchregions(gwps); - if (temp && temp->press_length == LONG_PRESS) - temp->armed = false; + /* no region - pass the event upward */ + if (!region) + return ACTION_TOUCHSCREEN; - if (returncode != ACTION_NONE) + int action = region->action; + region->last_press = tevent.tick; + + if (global_settings.party_mode) { - if (global_settings.party_mode) + switch (action) { - switch (returncode) - { - case ACTION_WPS_PLAY: - case ACTION_WPS_SKIPPREV: - case ACTION_WPS_SKIPNEXT: - case ACTION_WPS_STOP: - returncode = ACTION_NONE; - break; - default: - break; - } - } - switch (returncode) - { - case ACTION_TOUCH_SOFTLOCK: - data->touchscreen_locked = !data->touchscreen_locked; - returncode = ACTION_NONE; - break; - case ACTION_WPS_PLAY: - if (!audio_status()) - { - if ( global_status.resume_index != -1 ) - { - if (playlist_resume() != -1) - { - playlist_start(global_status.resume_index, - global_status.resume_elapsed, - global_status.resume_offset); - } - } - else - { - splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); - } - } - else - { - wps_do_playpause(false); - } - returncode = ACTION_REDRAW; - break; - case ACTION_WPS_SKIPPREV: - audio_prev(); - returncode = ACTION_REDRAW; - break; - case ACTION_WPS_SKIPNEXT: - audio_next(); - returncode = ACTION_REDRAW; - break; - case ACTION_WPS_STOP: - audio_stop(); - returncode = ACTION_REDRAW; - break; - case ACTION_SETTINGS_INC: - case ACTION_SETTINGS_DEC: - { - const struct settings_list *setting = - temp->setting_data.setting; - option_select_next_val(setting, - returncode == ACTION_SETTINGS_DEC, - true); - returncode = ACTION_REDRAW; - } + case ACTION_WPS_PLAY: + case ACTION_WPS_SKIPPREV: + case ACTION_WPS_SKIPNEXT: + case ACTION_WPS_STOP: + action = ACTION_NONE; break; - case ACTION_SETTINGS_SET: - { - struct touchsetting *data = &temp->setting_data; - const struct settings_list *s = data->setting; - void (*f)(int) = NULL; - switch (s->flags&F_T_MASK) - { - case F_T_CUSTOM: - s->custom_setting - ->load_from_cfg(s->setting, SKINOFFSETTOPTR(skin_buffer, data->value.text)); - break; - case F_T_INT: - case F_T_UINT: - *(int*)s->setting = data->value.number; - if ((s->flags & F_T_SOUND) == F_T_SOUND) - sound_set(s->sound_setting->setting, data->value.number); - else if (s->flags&F_CHOICE_SETTING) - f = s->choice_setting->option_callback; - else if (s->flags&F_TABLE_SETTING) - f = s->table_setting->option_callback; - else - f = s->int_setting->option_callback; - - if (f) - f(data->value.number); - break; - case F_T_BOOL: - *(bool*)s->setting = data->value.number ? true : false; - if (s->bool_setting->option_callback) - s->bool_setting - ->option_callback(data->value.number ? true : false); - break; - } - returncode = ACTION_REDRAW; - } - break; - case ACTION_TOUCH_MUTE: - { - const int min_vol = sound_min(SOUND_VOLUME); - if (global_status.volume == min_vol) - global_status.volume = temp->value; - else - { - temp->value = global_status.volume; - global_status.volume = min_vol; - } - setvol(); - returncode = ACTION_REDRAW; - } - break; - case ACTION_TOUCH_SHUFFLE: /* toggle shuffle mode */ - { - global_settings.playlist_shuffle = - !global_settings.playlist_shuffle; - replaygain_update(); - if (global_settings.playlist_shuffle) - playlist_randomise(NULL, current_tick, true); - else - playlist_sort(NULL, true); - returncode = ACTION_REDRAW; - } - break; - case ACTION_TOUCH_REPMODE: /* cycle the repeat mode setting */ - { - const struct settings_list *rep_setting = - find_setting(&global_settings.repeat_mode); - option_select_next_val(rep_setting, false, true); - audio_flush_and_reload_tracks(); - returncode = ACTION_REDRAW; - } - break; - case ACTION_TOUCH_SETTING: - { - struct progressbar *bar = - SKINOFFSETTOPTR(skin_buffer, temp->bar); - if (bar && edge_offset) - { - int val, count; - get_setting_info_for_bar(bar->setting, bar->setting_offset, &count, &val); - val = *edge_offset * count / 1000; - update_setting_value_from_touch(bar->setting, bar->setting_offset, val); - } - } + default: break; } - return returncode; } - return ACTION_TOUCHSCREEN; + switch (action) + { + case ACTION_TOUCH_SOFTLOCK: + data->touchscreen_locked = !data->touchscreen_locked; + action = ACTION_NONE; + break; + + case ACTION_WPS_PLAY: + if (!audio_status()) + { + if ( global_status.resume_index != -1 ) + { + if (playlist_resume() != -1) + { + playlist_start(global_status.resume_index, + global_status.resume_elapsed, + global_status.resume_offset); + } + } + else + { + splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); + } + } + else + { + wps_do_playpause(false); + } + + action = ACTION_REDRAW; + break; + + case ACTION_WPS_SKIPPREV: + audio_prev(); + action = ACTION_REDRAW; + break; + + case ACTION_WPS_SKIPNEXT: + audio_next(); + action = ACTION_REDRAW; + break; + + case ACTION_WPS_STOP: + audio_stop(); + action = ACTION_REDRAW; + break; + + case ACTION_SETTINGS_INC: + case ACTION_SETTINGS_DEC: + { + const struct settings_list *setting = region->setting_data.setting; + bool decrement = (action == ACTION_SETTINGS_DEC); + option_select_next_val(setting, decrement, true); + action = ACTION_REDRAW; + } break; + + case ACTION_SETTINGS_SET: + { + struct touchsetting *data = ®ion->setting_data; + const struct settings_list *s = data->setting; + void (*f)(int) = NULL; + switch (s->flags & F_T_MASK) + { + case F_T_CUSTOM: + s->custom_setting + ->load_from_cfg(s->setting, SKINOFFSETTOPTR(skin_buffer, data->value.text)); + break; + case F_T_INT: + case F_T_UINT: + *(int*)s->setting = data->value.number; + if ((s->flags & F_T_SOUND) == F_T_SOUND) + sound_set(s->sound_setting->setting, data->value.number); + else if (s->flags&F_CHOICE_SETTING) + f = s->choice_setting->option_callback; + else if (s->flags&F_TABLE_SETTING) + f = s->table_setting->option_callback; + else + f = s->int_setting->option_callback; + + if (f) + f(data->value.number); + break; + case F_T_BOOL: + *(bool*)s->setting = data->value.number ? true : false; + if (s->bool_setting->option_callback) + s->bool_setting + ->option_callback(data->value.number ? true : false); + break; + } + + action = ACTION_REDRAW; + } break; + + case ACTION_TOUCH_MUTE: + { + const int min_vol = sound_min(SOUND_VOLUME); + if (global_status.volume == min_vol) + global_status.volume = region->value; + else + { + region->value = global_status.volume; + global_status.volume = min_vol; + } + + setvol(); + action = ACTION_REDRAW; + } break; + + case ACTION_TOUCH_SHUFFLE: + global_settings.playlist_shuffle = !global_settings.playlist_shuffle; + replaygain_update(); + if (global_settings.playlist_shuffle) + playlist_randomise(NULL, current_tick, true); + else + playlist_sort(NULL, true); + + action = ACTION_REDRAW; + break; + + case ACTION_TOUCH_REPMODE: + { + const struct settings_list *rep_setting = + find_setting(&global_settings.repeat_mode); + option_select_next_val(rep_setting, false, true); + audio_flush_and_reload_tracks(); + action = ACTION_REDRAW; + } break; + + case ACTION_TOUCH_SETTING: + { + struct progressbar *bar = SKINOFFSETTOPTR(skin_buffer, region->bar); + if (bar && edge_offset) + { + int val, count; + get_setting_info_for_bar(bar->setting, 0, &count, &val); + val = *edge_offset * count / 1000; + update_setting_value_from_touch(bar->setting, 0, val); + } + + action = ACTION_NONE; + } break; + } + + return action; } diff --git a/apps/gui/skin_engine/wps_internals.h b/apps/gui/skin_engine/wps_internals.h index 67fc711eee..a6d9d2b472 100644 --- a/apps/gui/skin_engine/wps_internals.h +++ b/apps/gui/skin_engine/wps_internals.h @@ -27,6 +27,7 @@ #include "tag_table.h" #include "skin_parser.h" +#include "gesture.h" #ifndef __PCTOOL__ #include "core_alloc.h" #endif @@ -218,8 +219,6 @@ struct touchregion { int16_t hpad; /* padding to height */ bool reverse_bar; /* if true 0% is the left or top */ bool allow_while_locked; - bool armed; /* A region is armed on press. Only armed regions are triggered - on repeat or release. */ enum { PRESS, /* quick press only */ LONG_PRESS, /* Long press without repeat */ @@ -371,6 +370,7 @@ struct wps_data #ifdef HAVE_TOUCHSCREEN bool touchscreen_locked; OFFSETTYPE(struct skin_token_list *) touchregions; + struct gesture gesture; #endif #ifdef HAVE_ALBUMART OFFSETTYPE(struct skin_albumart *) albumart;