From 76719a836bfca9625af1c81c29da0f1574322661 Mon Sep 17 00:00:00 2001 From: Daniel Zaoui Date: Tue, 2 Jul 2013 10:17:54 +0300 Subject: [PATCH] Gesture Layer: implementation of 'tap + longpressed' sequence Callbacks can be set: - at the start of the sequence, i.e at the start of the single tap - at the end of the sequence, i.e when mouse up occurs on long press - when longpress is detected, i.e when mouse is still down during longpress --- legacy/elementary/src/lib/Makefile.am | 1 + .../lib/elm_gesture_layer_extra_gestures.c | 238 ++++++++++++++++++ .../src/lib/elm_gesture_layer_legacy.h | 37 +++ 3 files changed, 276 insertions(+) create mode 100644 legacy/elementary/src/lib/elm_gesture_layer_extra_gestures.c diff --git a/legacy/elementary/src/lib/Makefile.am b/legacy/elementary/src/lib/Makefile.am index 44dc6b0bf5..ac5cc33e24 100644 --- a/legacy/elementary/src/lib/Makefile.am +++ b/legacy/elementary/src/lib/Makefile.am @@ -419,6 +419,7 @@ elm_frame.c \ elm_gengrid.c \ elm_genlist.c \ elm_gesture_layer.c \ +elm_gesture_layer_extra_gestures.c \ elm_glview.c \ elm_grid.c \ elm_hover.c \ diff --git a/legacy/elementary/src/lib/elm_gesture_layer_extra_gestures.c b/legacy/elementary/src/lib/elm_gesture_layer_extra_gestures.c new file mode 100644 index 0000000000..97468a3076 --- /dev/null +++ b/legacy/elementary/src/lib/elm_gesture_layer_extra_gestures.c @@ -0,0 +1,238 @@ +#include +#include + +//#define DEBUGON +#ifdef DEBUGON +# define gl_debug(x...) fprintf(stderr, __FILE__": " x) +#else +# define gl_debug(x...) do { } while (0) +#endif + +struct _Func_Data +{ + EINA_INLIST; + void *user_data; /**< Holds user data to CB (like sd) */ + Elm_Gesture_Event_Cb cb; +}; +typedef struct _Func_Data Func_Data; + +struct _Tap_Longpress_Info +{ + Evas_Object *obj; + Eina_Inlist *cbs[ELM_GESTURE_STATE_ABORT + 1]; /**< Callback info (Func_Data) for states */ + void *data; + + Ecore_Timer *timer_between_taps; + unsigned int nb_taps_on_single : 7; + Eina_Bool long_tap_started : 1; +}; + +typedef struct _Tap_Longpress_Info Tap_Longpress_Info; + +static Evas_Event_Flags +_cb_call(Tap_Longpress_Info *info, Elm_Gesture_State state, void *event_info) +{ + Evas_Event_Flags flags = EVAS_EVENT_FLAG_NONE; + Func_Data *cb_info; + EINA_INLIST_FOREACH(info->cbs[state], cb_info) + flags |= cb_info->cb(cb_info->user_data, event_info); + return flags; +} + +static Evas_Event_Flags +_tap_long_single_tap_start_cb(void *data, void *event_info) +{ + Tap_Longpress_Info *info = data; + Evas_Event_Flags flags = EVAS_EVENT_FLAG_NONE; + if (!info->nb_taps_on_single) + { + gl_debug("\n%s\n", __FUNCTION__); + _cb_call(info, ELM_GESTURE_STATE_START, event_info); + } + return flags; +} + +static Evas_Event_Flags +_tap_long_single_tap_abort_cb(void *data, void *event_info EINA_UNUSED) +{ + Tap_Longpress_Info *info = data; + Evas_Event_Flags flags = EVAS_EVENT_FLAG_NONE; + if (!info->long_tap_started) + { + gl_debug("%s\n", __FUNCTION__); + _cb_call(info, ELM_GESTURE_STATE_ABORT, NULL); + info->nb_taps_on_single = 0; + } + return flags; +} + +static Eina_Bool +_tap_long_timeout(void *data) +{ + gl_debug("%s\n", __FUNCTION__); + Tap_Longpress_Info *info = data; + _tap_long_single_tap_abort_cb(info, NULL); + info->timer_between_taps = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static Evas_Event_Flags +_tap_long_single_tap_end_cb(void *data, void *event_info) +{ + gl_debug("%s\n", __FUNCTION__); + Tap_Longpress_Info *info = data; + Evas_Event_Flags flags = EVAS_EVENT_FLAG_NONE; + double timeout_between_taps = elm_gesture_layer_double_tap_timeout_get(info->obj); + info->timer_between_taps = ecore_timer_add(timeout_between_taps, + _tap_long_timeout, info); + info->nb_taps_on_single = ((Elm_Gesture_Taps_Info *)event_info)->n; + return flags; +} + +static Evas_Event_Flags +_tap_long_long_tap_start_cb(void *data, void *event_info EINA_UNUSED) +{ + Tap_Longpress_Info *info = data; + Evas_Event_Flags flags = EVAS_EVENT_FLAG_NONE; + if (info->nb_taps_on_single && info->timer_between_taps) + { + gl_debug("%s\n", __FUNCTION__); + info->long_tap_started = EINA_TRUE; + ecore_timer_del(info->timer_between_taps); + info->timer_between_taps = NULL; + } + return flags; +} + +static Evas_Event_Flags +_tap_long_long_tap_abort_cb(void *data, void *event_info EINA_UNUSED) +{ + Tap_Longpress_Info *info = data; + Evas_Event_Flags flags = EVAS_EVENT_FLAG_NONE; + if (info->long_tap_started) + { + gl_debug("%s\n", __FUNCTION__); + _cb_call(info, ELM_GESTURE_STATE_ABORT, NULL); + info->nb_taps_on_single = 0; + info->long_tap_started = EINA_FALSE; + } + return flags; +} + +static Evas_Event_Flags +_tap_long_long_tap_move_cb(void *data, void *event_info) +{ + Tap_Longpress_Info *info = data; + Evas_Event_Flags flags = EVAS_EVENT_FLAG_NONE; + if (info->long_tap_started) + { + if (((Elm_Gesture_Taps_Info *)event_info)->n != info->nb_taps_on_single) + { + _tap_long_long_tap_abort_cb(info, NULL); + } + else + { + gl_debug("%s\n", __FUNCTION__); + _cb_call(info, ELM_GESTURE_STATE_MOVE, event_info); + } + } + return flags; +} + +static Evas_Event_Flags +_tap_long_long_tap_end_cb(void *data, void *event_info) +{ + Tap_Longpress_Info *info = data; + Evas_Event_Flags flags = EVAS_EVENT_FLAG_NONE; + if (info->long_tap_started) + { + gl_debug("%s\n", __FUNCTION__); + _cb_call(info, ELM_GESTURE_STATE_END, event_info); + info->long_tap_started = EINA_FALSE; + info->nb_taps_on_single = 0; + } + return flags; +} + +static void +_object_delete(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + Tap_Longpress_Info *info = evas_object_data_get(obj, "Tap-Longpress"); + if (info) + { + Eina_Inlist *itr; + Func_Data *cb_info; + int state; + for (state = ELM_GESTURE_STATE_START; state <= ELM_GESTURE_STATE_ABORT; state++) + { + EINA_INLIST_FOREACH_SAFE(info->cbs[state], itr, cb_info) + { + info->cbs[state] = eina_inlist_remove( + info->cbs[state], EINA_INLIST_GET(cb_info)); + free(cb_info); + } + } + elm_gesture_layer_cb_del(obj, ELM_GESTURE_N_TAPS, ELM_GESTURE_STATE_START, _tap_long_single_tap_start_cb, info); + elm_gesture_layer_cb_del(obj, ELM_GESTURE_N_TAPS, ELM_GESTURE_STATE_ABORT, _tap_long_single_tap_abort_cb, info); + elm_gesture_layer_cb_del(obj, ELM_GESTURE_N_TAPS, ELM_GESTURE_STATE_END, _tap_long_single_tap_end_cb, info); + elm_gesture_layer_cb_del(obj, ELM_GESTURE_N_LONG_TAPS, ELM_GESTURE_STATE_START, _tap_long_long_tap_start_cb, info); + elm_gesture_layer_cb_del(obj, ELM_GESTURE_N_LONG_TAPS, ELM_GESTURE_STATE_MOVE, _tap_long_long_tap_move_cb, info); + elm_gesture_layer_cb_del(obj, ELM_GESTURE_N_LONG_TAPS, ELM_GESTURE_STATE_ABORT, _tap_long_long_tap_abort_cb, info); + elm_gesture_layer_cb_del(obj, ELM_GESTURE_N_LONG_TAPS, ELM_GESTURE_STATE_END, _tap_long_long_tap_end_cb, info); + evas_object_data_del(obj, "Tap-Longpress"); + free(info); + } +} + +EAPI void elm_gesture_layer_tap_longpress_cb_add(Evas_Object *obj, Elm_Gesture_State state, Elm_Gesture_Event_Cb cb, void *data) +{ + Tap_Longpress_Info *info = evas_object_data_get(obj, "Tap-Longpress"); + if (!info) + { + info = calloc(1, sizeof(*info)); + info->obj = obj; + elm_gesture_layer_cb_add(obj, ELM_GESTURE_N_TAPS, ELM_GESTURE_STATE_START, _tap_long_single_tap_start_cb, info); + elm_gesture_layer_cb_add(obj, ELM_GESTURE_N_TAPS, ELM_GESTURE_STATE_ABORT, _tap_long_single_tap_abort_cb, info); + elm_gesture_layer_cb_add(obj, ELM_GESTURE_N_TAPS, ELM_GESTURE_STATE_END, _tap_long_single_tap_end_cb, info); + elm_gesture_layer_cb_add(obj, ELM_GESTURE_N_LONG_TAPS, ELM_GESTURE_STATE_START, _tap_long_long_tap_start_cb, info); + elm_gesture_layer_cb_add(obj, ELM_GESTURE_N_LONG_TAPS, ELM_GESTURE_STATE_MOVE, _tap_long_long_tap_move_cb, info); + elm_gesture_layer_cb_add(obj, ELM_GESTURE_N_LONG_TAPS, ELM_GESTURE_STATE_ABORT, _tap_long_long_tap_abort_cb, info); + elm_gesture_layer_cb_add(obj, ELM_GESTURE_N_LONG_TAPS, ELM_GESTURE_STATE_END, _tap_long_long_tap_end_cb, info); + evas_object_data_set(obj, "Tap-Longpress", info); + evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _object_delete, NULL); + } + + Func_Data *cb_info = calloc(1, sizeof(*cb_info)); + if (!cb_info) return; + cb_info->cb = cb; + cb_info->user_data = data; + info->cbs[state] = eina_inlist_append(info->cbs[state], + EINA_INLIST_GET(cb_info)); +} + +EAPI void elm_gesture_layer_tap_longpress_cb_del(Evas_Object *obj, Elm_Gesture_State state, Elm_Gesture_Event_Cb cb, void *data) +{ + Tap_Longpress_Info *info = evas_object_data_get(obj, "Tap-Longpress"); + if (!info) return; + + Eina_Inlist *itr; + Func_Data *cb_info; + EINA_INLIST_FOREACH_SAFE(info->cbs[state], itr, cb_info) + { + if (cb_info->cb == cb && cb_info->user_data == data) + { + info->cbs[state] = eina_inlist_remove( + info->cbs[state], EINA_INLIST_GET(cb_info)); + free(cb_info); + break; + } + } + if (!info->cbs[ELM_GESTURE_STATE_START] && + !info->cbs[ELM_GESTURE_STATE_MOVE] && + !info->cbs[ELM_GESTURE_STATE_END] && + !info->cbs[ELM_GESTURE_STATE_ABORT]) + { + _object_delete(NULL, NULL, obj, NULL); + } +} + diff --git a/legacy/elementary/src/lib/elm_gesture_layer_legacy.h b/legacy/elementary/src/lib/elm_gesture_layer_legacy.h index b3005ded58..d643e03142 100644 --- a/legacy/elementary/src/lib/elm_gesture_layer_legacy.h +++ b/legacy/elementary/src/lib/elm_gesture_layer_legacy.h @@ -168,3 +168,40 @@ EAPI void elm_gesture_layer_tap_finger_size_set(Evas_Object *obj, Evas_Coord sz) * */ EAPI Evas_Coord elm_gesture_layer_tap_finger_size_get(const Evas_Object *obj); + +/** + * @since 1.8 + * This function adds a callback called during Tap + Long Tap sequence. + * + * @param state state for the callback to add. + * @param cb callback pointer + * @param data user data for the callback. + * + * The callbacks will be called as followed: + * - start cbs on single tap start + * - move cbs on long press move + * - end cbs on long press end + * - abort cbs whenever in the sequence. The event info will be NULL, because it + * can be triggered from multiple events (timer expired, abort single/long taps). + * + * You can remove the callbacks by using elm_gesture_layer_tap_longpress_cb_del. + * + * @see elm_gesture_layer_tap_longpress_cb_del + */ +EAPI void elm_gesture_layer_tap_longpress_cb_add(Evas_Object *obj, Elm_Gesture_State state, Elm_Gesture_Event_Cb cb, void *data); + +/** + * @since 1.8 + * This function removes a callback called during Tap + Long Tap sequence. + * + * @param state state for the callback to add. + * @param cb callback pointer + * @param data user data for the callback. + * + * The internal data used for the sequence will be freed ONLY when all the + * callbacks added via elm_gesture_layer_tap_longpress_cb_add are removed by + * this function. + * + * @see elm_gesture_layer_tap_longpress_cb_add + */ +EAPI void elm_gesture_layer_tap_longpress_cb_del(Evas_Object *obj, Elm_Gesture_State state, Elm_Gesture_Event_Cb cb, void *data);