From ba72235b66ee956977ad6e5c7a8510f9f23a3517 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 9 Mar 2011 10:06:44 +0100 Subject: [PATCH] Add ClutterSwipeAction and ClutterGestureAction To allow actors to handle gestures in a more organized way. http://bugzilla.clutter-project.org/show_bug.cgi?id=2585 --- clutter/Makefile.am | 4 + clutter/clutter-gesture-action.c | 500 ++++++++++++++++++++++++++ clutter/clutter-gesture-action.h | 120 +++++++ clutter/clutter-marshal.list | 1 + clutter/clutter-swipe-action.c | 249 +++++++++++++ clutter/clutter-swipe-action.h | 117 ++++++ clutter/clutter.h | 2 + tests/interactive/Makefile.am | 1 + tests/interactive/test-swipe-action.c | 109 ++++++ 9 files changed, 1103 insertions(+) create mode 100644 clutter/clutter-gesture-action.c create mode 100644 clutter/clutter-gesture-action.h create mode 100644 clutter/clutter-swipe-action.c create mode 100644 clutter/clutter-swipe-action.h create mode 100644 tests/interactive/test-swipe-action.c diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 476a38d2d..7702a0e5e 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -95,6 +95,7 @@ source_h = \ $(srcdir)/clutter-fixed-layout.h \ $(srcdir)/clutter-flow-layout.h \ $(srcdir)/clutter-frame-source.h \ + $(srcdir)/clutter-gesture-action.h \ $(srcdir)/clutter-group.h \ $(srcdir)/clutter-input-device.h \ $(srcdir)/clutter-interval.h \ @@ -118,6 +119,7 @@ source_h = \ $(srcdir)/clutter-shader.h \ $(srcdir)/clutter-shader-effect.h \ $(srcdir)/clutter-shader-types.h \ + $(srcdir)/clutter-swipe-action.h \ $(srcdir)/clutter-snap-constraint.h \ $(srcdir)/clutter-stage.h \ $(srcdir)/clutter-stage-manager.h \ @@ -178,6 +180,7 @@ source_c = \ $(srcdir)/clutter-flatten-effect.c \ $(srcdir)/clutter-flow-layout.c \ $(srcdir)/clutter-frame-source.c \ + $(srcdir)/clutter-gesture-action.c \ $(srcdir)/clutter-group.c \ $(srcdir)/clutter-input-device.c \ $(srcdir)/clutter-interval.c \ @@ -202,6 +205,7 @@ source_c = \ $(srcdir)/clutter-shader.c \ $(srcdir)/clutter-shader-effect.c \ $(srcdir)/clutter-shader-types.c \ + $(srcdir)/clutter-swipe-action.c \ $(srcdir)/clutter-snap-constraint.c \ $(srcdir)/clutter-stage.c \ $(srcdir)/clutter-stage-manager.c \ diff --git a/clutter/clutter-gesture-action.c b/clutter/clutter-gesture-action.c new file mode 100644 index 000000000..1ab84eabe --- /dev/null +++ b/clutter/clutter-gesture-action.c @@ -0,0 +1,500 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 Intel Corporation. + * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Tomeu Vizoso + */ + +/** + * SECTION:clutter-gesture-action + * @Title: ClutterGestureAction + * @Short_Description: Action for gesture gestures + * + * #ClutterGestureAction is a sub-class of #ClutterAction that implements + * the logic for recognizing gesture gestures. It listens for low level events + * such as #ClutterButtonEvent and #ClutterMotionEvent on the stage to raise + * the signals #ClutterGestureAction::gesture-begin, #ClutterGestureAction::gesture-motion and + * #ClutterGestureAction::gesture-end. + * + * To use #ClutterGestureAction you just need to apply it to a #ClutterActor + * using clutter_actor_add_action() and connect to the signals: + * + * |[ + * ClutterAction *action = clutter_gesture_action_new (); + * + * clutter_actor_add_action (actor, action); + * + * g_signal_connect (action, "gesture-begin", G_CALLBACK (on_gesture_begin), NULL); + * g_signal_connect (action, "gesture-motion", G_CALLBACK (on_gesture_motion), NULL); + * g_signal_connect (action, "gesture-end", G_CALLBACK (on_gesture_end), NULL); + * ]| + * + * Since: 1.8 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-gesture-action.h" + +#include "clutter-debug.h" +#include "clutter-enum-types.h" +#include "clutter-marshal.h" +#include "clutter-private.h" + +struct _ClutterGestureActionPrivate +{ + ClutterActor *stage; + + guint actor_capture_id; + gulong stage_capture_id; + + gboolean in_drag; + + gfloat press_x, press_y; + gfloat last_motion_x, last_motion_y; + gfloat release_x, release_y; +}; + +enum +{ + GESTURE_BEGIN, + GESTURE_PROGRESS, + GESTURE_END, + GESTURE_CANCEL, + + LAST_SIGNAL +}; + +static guint gesture_signals[LAST_SIGNAL] = { 0, }; + +G_DEFINE_ABSTRACT_TYPE (ClutterGestureAction, clutter_gesture_action, + CLUTTER_TYPE_ACTION); + +static gboolean +signal_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer user_data) +{ + gboolean continue_emission; + + continue_emission = g_value_get_boolean (handler_return); + g_value_set_boolean (return_accu, continue_emission); + + return continue_emission; +} + +static void +cancel_gesture (ClutterGestureAction *action) +{ + ClutterGestureActionPrivate *priv = action->priv; + ClutterActor *actor; + + priv->in_drag = FALSE; + + g_signal_handler_disconnect (priv->stage, priv->stage_capture_id); + priv->stage_capture_id = 0; + + actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); + g_signal_emit (action, gesture_signals[GESTURE_CANCEL], 0, actor); +} + +static gboolean +stage_captured_event_cb (ClutterActor *stage, + ClutterEvent *event, + ClutterGestureAction *action) +{ + ClutterGestureActionPrivate *priv = action->priv; + ClutterActor *actor; + gboolean return_value; + + actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); + + switch (clutter_event_type (event)) + { + case CLUTTER_MOTION: + { + clutter_event_get_coords (event, &priv->last_motion_x, + &priv->last_motion_y); + + if (!clutter_actor_transform_stage_point (actor, + priv->last_motion_x, + priv->last_motion_y, + NULL, NULL)) + return FALSE; + + if (!priv->in_drag) + { + gint drag_threshold; + ClutterSettings *settings = clutter_settings_get_default (); + + g_object_get (settings, + "dnd-drag-threshold", &drag_threshold, + NULL); + + if ((ABS (priv->press_y - priv->last_motion_y) >= drag_threshold) || + (ABS (priv->press_x - priv->last_motion_x) >= drag_threshold)) + { + priv->in_drag = TRUE; + + g_signal_emit (action, gesture_signals[GESTURE_BEGIN], 0, actor, + &return_value); + if (!return_value) + cancel_gesture (action); + } + else + return FALSE; + } + + g_signal_emit (action, gesture_signals[GESTURE_PROGRESS], 0, actor, + &return_value); + if (!return_value) + cancel_gesture (action); + } + break; + + case CLUTTER_BUTTON_RELEASE: + { + clutter_event_get_coords (event, &priv->release_x, &priv->release_y); + + g_signal_handler_disconnect (priv->stage, priv->stage_capture_id); + priv->stage_capture_id = 0; + + if (priv->in_drag) + { + priv->in_drag = FALSE; + g_signal_emit (action, gesture_signals[GESTURE_END], 0, actor); + } + } + break; + + default: + break; + } + + return FALSE; +} + +static gboolean +actor_captured_event_cb (ClutterActor *actor, + ClutterEvent *event, + ClutterGestureAction *action) +{ + ClutterGestureActionPrivate *priv = action->priv; + + if (clutter_event_type (event) != CLUTTER_BUTTON_PRESS) + return FALSE; + + if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action))) + return FALSE; + + clutter_event_get_coords (event, &priv->press_x, &priv->press_y); + + if (priv->stage == NULL) + priv->stage = clutter_actor_get_stage (actor); + + priv->stage_capture_id = g_signal_connect_after (priv->stage, + "captured-event", + G_CALLBACK (stage_captured_event_cb), + action); + + return FALSE; +} + +static void +clutter_gesture_action_set_actor (ClutterActorMeta *meta, + ClutterActor *actor) +{ + ClutterGestureActionPrivate *priv = CLUTTER_GESTURE_ACTION (meta)->priv; + ClutterActorMetaClass *meta_class = + CLUTTER_ACTOR_META_CLASS (clutter_gesture_action_parent_class); + + if (priv->actor_capture_id != 0) + { + ClutterActor *old_actor = clutter_actor_meta_get_actor (meta); + + g_signal_handler_disconnect (old_actor, priv->actor_capture_id); + priv->actor_capture_id = 0; + } + + if (priv->stage_capture_id != 0) + { + g_signal_handler_disconnect (priv->stage, priv->stage_capture_id); + priv->stage_capture_id = 0; + priv->stage = NULL; + } + + if (actor != NULL) + priv->actor_capture_id = g_signal_connect (actor, "captured-event", + G_CALLBACK (actor_captured_event_cb), + meta); + + meta_class->set_actor (meta, actor); +} + +static void +clutter_gesture_action_class_init (ClutterGestureActionClass *klass) +{ + ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ClutterGestureActionPrivate)); + + meta_class->set_actor = clutter_gesture_action_set_actor; + + /** + * ClutterGestureAction::gesture_begin: + * @action: the #ClutterGestureAction that emitted the signal + * @actor: the #ClutterActor attached to the @action + * + * The ::gesture_begin signal is emitted when the #ClutterActor to which + * a #ClutterGestureAction has been applied starts receiving a gesture. + */ + gesture_signals[GESTURE_BEGIN] = + g_signal_new (I_("gesture-begin"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_begin), + signal_accumulator, NULL, + _clutter_marshal_BOOLEAN__OBJECT, + G_TYPE_BOOLEAN, 1, + CLUTTER_TYPE_ACTOR); + + /** + * ClutterGestureAction::gesture-progress: + * @action: the #ClutterGestureAction that emitted the signal + * @actor: the #ClutterActor attached to the @action + * + * The ::gesture-progress signal is emitted for each motion event after + * the #ClutterGestureAction::gesture-begin signal has been emitted. + */ + gesture_signals[GESTURE_PROGRESS] = + g_signal_new (I_("gesture-progress"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_progress), + signal_accumulator, NULL, + _clutter_marshal_BOOLEAN__OBJECT, + G_TYPE_BOOLEAN, 1, + CLUTTER_TYPE_ACTOR); + + /** + * ClutterGestureAction::gesture-end: + * @action: the #ClutterGestureAction that emitted the signal + * @actor: the #ClutterActor attached to the @action + * + * The ::gesture-end signal is emitted at the end of the gesture gesture, + * when the pointer's button is released + * + * This signal is emitted if and only if the #ClutterGestureAction::gesture-begin + * signal has been emitted first. + * + * Since: 1.8 + */ + gesture_signals[GESTURE_END] = + g_signal_new (I_("gesture-end"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_end), + NULL, NULL, + _clutter_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + CLUTTER_TYPE_ACTOR); + + /** + * ClutterGestureAction::gesture-cancel: + * @action: the #ClutterGestureAction that emitted the signal + * @actor: the #ClutterActor attached to the @action + * + * The ::gesture-cancel signal is emitted when the ongoing gesture gets + * cancelled. + * + * This signal is emitted if and only if the #ClutterGestureAction::gesture-begin + * signal has been emitted first. + * + * Since: 1.8 + */ + gesture_signals[GESTURE_CANCEL] = + g_signal_new (I_("gesture-cancel"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_cancel), + NULL, NULL, + _clutter_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + CLUTTER_TYPE_ACTOR); +} + +static void +clutter_gesture_action_init (ClutterGestureAction *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_GESTURE_ACTION, + ClutterGestureActionPrivate); +} + +/** + * clutter_gesture_action_new: + * + * Creates a new #ClutterGestureAction instance + * + * Return value: the newly created #ClutterGestureAction + * + * Since: 1.8 + */ +ClutterAction * +clutter_gesture_action_new (void) +{ + return g_object_new (CLUTTER_TYPE_GESTURE_ACTION, NULL); +} + +/** + * clutter_gesture_action_get_press_coords: + * @action: a #ClutterGestureAction + * @device: id of the device we are interested in + * @press_x: (out): return location for the press event's X coordinate + * @press_y: (out): return location for the press event's Y coordinate + * + * Retrieves the coordinates, in stage space, of the press event + * that started the dragging for an specific pointer device + * + * Since: 1.8 + */ +void +clutter_gesture_action_get_press_coords (ClutterGestureAction *action, + guint device, + gfloat *press_x, + gfloat *press_y) +{ + g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); + + if (device != 0) + { + g_warning ("Multi-device support not yet implemented"); + return; + } + + if (press_x) + *press_x = action->priv->press_x; + + if (press_y) + *press_y = action->priv->press_y; +} + +/** + * clutter_gesture_action_get_motion_coords: + * @action: a #ClutterGestureAction + * @motion_x: (out): return location for the latest motion + * event's X coordinate + * @motion_y: (out): return location for the latest motion + * event's Y coordinate + * + * Retrieves the coordinates, in stage space, of the latest motion + * event during the dragging + * + * Since: 1.8 + */ +void +clutter_gesture_action_get_motion_coords (ClutterGestureAction *action, + guint device, + gfloat *motion_x, + gfloat *motion_y) +{ + g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); + + if (device != 0) + { + g_warning ("Multi-device support not yet implemented"); + return; + } + + if (motion_x) + *motion_x = action->priv->last_motion_x; + + if (motion_y) + *motion_y = action->priv->last_motion_y; +} + +/** + * clutter_gesture_action_get_release_coords: + * @action: a #ClutterGestureAction + * @release_x: (out): return location for the X coordinate of the last release + * @release_y: (out): return location for the Y coordinate of the last release + * + * Retrieves the coordinates, in stage space, of the point where the pointer + * device was last released. + * + * Since: 1.8 + */ +void +clutter_gesture_action_get_release_coords (ClutterGestureAction *action, + guint device, + gfloat *release_x, + gfloat *release_y) +{ + g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); + + if (device != 0) + { + g_warning ("Multi-device support not yet implemented"); + return; + } + + if (release_x) + *release_x = action->priv->release_x; + + if (release_y) + *release_y = action->priv->release_y; +} + +/** + * clutter_gesture_action_set_required_devices: + * @action: a #ClutterGestureAction + * @n_required_devices: the number of pointer devices that are to be tracked + * + * Sets the number of pointer devices that are to be tracked by this gesture. + * + * Since: 1.8 + */ +void +clutter_gesture_action_set_required_devices (ClutterGestureAction *action, + guint n_required_devices) +{ + if (n_required_devices != 1) + { + g_warning ("Multi-device support not yet implemented"); + return; + } +} + +/** + * clutter_gesture_action_get_required_devices: + * @action: a #ClutterGestureAction + * + * Returns the number of devices tracked by this gesture. + * + * Since: 1.8 + */ +guint +clutter_gesture_action_get_required_devices (ClutterGestureAction *action) +{ + return 1; +} diff --git a/clutter/clutter-gesture-action.h b/clutter/clutter-gesture-action.h new file mode 100644 index 000000000..e1f3efaa2 --- /dev/null +++ b/clutter/clutter-gesture-action.h @@ -0,0 +1,120 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 Intel Corporation. + * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Tomeu Vizoso + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_GESTURE_ACTION_H__ +#define __CLUTTER_GESTURE_ACTION_H__ + +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_GESTURE_ACTION (clutter_gesture_action_get_type ()) +#define CLUTTER_GESTURE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GESTURE_ACTION, ClutterGestureAction)) +#define CLUTTER_IS_GESTURE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GESTURE_ACTION)) +#define CLUTTER_GESTURE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_GESTURE_ACTION, ClutterGestureActionClass)) +#define CLUTTER_IS_GESTURE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_GESTURE_ACTION)) +#define CLUTTER_GESTURE_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_GESTURE_ACTION, ClutterGestureActionClass)) + +typedef struct _ClutterGestureAction ClutterGestureAction; +typedef struct _ClutterGestureActionPrivate ClutterGestureActionPrivate; +typedef struct _ClutterGestureActionClass ClutterGestureActionClass; + +/** + * ClutterGestureAction: + * + * The ClutterGestureAction structure contains + * only private data and should be accessed using the provided API + * + * Since: 1.8 + */ +struct _ClutterGestureAction +{ + /*< private >*/ + ClutterAction parent_instance; + + ClutterGestureActionPrivate *priv; +}; + +/** + * ClutterGestureActionClass: + * @gesture_begin: class handler for the #ClutterGestureAction::gesture-begin signal + * + * Since: 1.8 + */ +struct _ClutterGestureActionClass +{ + /*< private >*/ + ClutterActionClass parent_class; + + /*< public >*/ + gboolean (* gesture_begin) (ClutterGestureAction *action, + ClutterActor *actor); + gboolean (* gesture_progress) (ClutterGestureAction *action, + ClutterActor *actor); + void (* gesture_end) (ClutterGestureAction *action, + ClutterActor *actor); + void (* gesture_cancel) (ClutterGestureAction *action, + ClutterActor *actor); + + /*< private >*/ + void (* _clutter_gesture_action1) (void); + void (* _clutter_gesture_action2) (void); + void (* _clutter_gesture_action3) (void); + void (* _clutter_gesture_action4) (void); + void (* _clutter_gesture_action5) (void); + void (* _clutter_gesture_action6) (void); + void (* _clutter_gesture_action7) (void); +}; + +GType clutter_gesture_action_get_type (void) G_GNUC_CONST; + +ClutterAction *clutter_gesture_action_new (void); + +void clutter_gesture_action_get_press_coords (ClutterGestureAction *action, + guint device, + gfloat *press_x, + gfloat *press_y); + +void clutter_gesture_action_get_motion_coords (ClutterGestureAction *action, + guint device, + gfloat *motion_x, + gfloat *motion_y); + +void clutter_gesture_action_get_release_coords (ClutterGestureAction *action, + guint device, + gfloat *release_x, + gfloat *release_y); + +void clutter_gesture_action_set_required_devices (ClutterGestureAction *action, + guint n_required_devices); +guint clutter_gesture_action_get_required_devices (ClutterGestureAction *action); + +G_END_DECLS + +#endif /* __CLUTTER_GESTURE_ACTION_H__ */ diff --git a/clutter/clutter-marshal.list b/clutter/clutter-marshal.list index 563508ffc..f9a604b6b 100644 --- a/clutter/clutter-marshal.list +++ b/clutter/clutter-marshal.list @@ -1,6 +1,7 @@ BOOLEAN:BOXED BOOLEAN:OBJECT,ENUM BOOLEAN:STRING,UINT,FLAGS +BOOLEAN:OBJECT BOXED:UINT,UINT DOUBLE:VOID UINT:VOID diff --git a/clutter/clutter-swipe-action.c b/clutter/clutter-swipe-action.c new file mode 100644 index 000000000..98b575f5e --- /dev/null +++ b/clutter/clutter-swipe-action.c @@ -0,0 +1,249 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 Intel Corporation. + * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Tomeu Vizoso + * + * Based on ClutterDragAction, written by: + * Emmanuele Bassi + */ + +/** + * SECTION:clutter-swipe-action + * @Title: ClutterSwipeAction + * @Short_Description: Action for swipe gestures + * + * #ClutterSwipeAction is a sub-class of #ClutterAction that implements + * the logic for recognizing swipe gestures. It listens for low level events + * such as #ClutterButtonEvent and #ClutterMotionEvent on the stage to raise + * the signals #ClutterSwipeAction::swipe-begin, #ClutterSwipeAction::swipe-motion and + * #ClutterSwipeAction::swipe-end. + * + * To use #ClutterSwipeAction you just need to apply it to a #ClutterActor + * using clutter_actor_add_action() and connect to the signals: + * + * |[ + * ClutterAction *action = clutter_swipe_action_new (); + * + * clutter_actor_add_action (actor, action); + * + * g_signal_connect (action, "swipe-begin", G_CALLBACK (on_swipe_begin), NULL); + * g_signal_connect (action, "swipe-motion", G_CALLBACK (on_swipe_motion), NULL); + * g_signal_connect (action, "swipe-end", G_CALLBACK (on_swipe_end), NULL); + * ]| + * + * Since: 1.8 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-swipe-action.h" + +#include "clutter-debug.h" +#include "clutter-enum-types.h" +#include "clutter-marshal.h" +#include "clutter-private.h" + +struct _ClutterSwipeActionPrivate +{ + ClutterSwipeDirection h_direction; + ClutterSwipeDirection v_direction; + gfloat last_motion_x; + gfloat last_motion_y; +}; + +enum +{ + SWEPT, + + LAST_SIGNAL +}; + +static guint swipe_signals[LAST_SIGNAL] = { 0, }; + +G_DEFINE_TYPE (ClutterSwipeAction, clutter_swipe_action, + CLUTTER_TYPE_GESTURE_ACTION); + +static gboolean +gesture_begin (ClutterGestureAction *action, + ClutterActor *actor) +{ + ClutterSwipeActionPrivate *priv = CLUTTER_SWIPE_ACTION (action)->priv; + + priv->h_direction = 0; + priv->v_direction = 0; + + return TRUE; +} + +static gboolean +gesture_progress (ClutterGestureAction *action, + ClutterActor *actor) +{ + ClutterSwipeActionPrivate *priv = CLUTTER_SWIPE_ACTION (action)->priv; + gfloat press_x, press_y; + gfloat motion_x, motion_y; + gfloat delta_x, delta_y; + ClutterSwipeDirection h_direction = 0, v_direction = 0; + ClutterSettings *settings = clutter_settings_get_default (); + gint drag_threshold; + + clutter_gesture_action_get_press_coords (action, + 0, + &press_x, + &press_y); + + clutter_gesture_action_get_motion_coords (action, + 0, + &motion_x, + &motion_y); + + delta_x = press_x - motion_x; + delta_y = press_y - motion_y; + + g_object_get (settings, + "dnd-drag-threshold", &drag_threshold, + NULL); + + if (delta_x >= drag_threshold) + h_direction = CLUTTER_SWIPE_DIRECTION_RIGHT; + else if (delta_x < -drag_threshold) + h_direction = CLUTTER_SWIPE_DIRECTION_LEFT; + + if (delta_y >= drag_threshold) + v_direction = CLUTTER_SWIPE_DIRECTION_DOWN; + else if (delta_y < -drag_threshold) + v_direction = CLUTTER_SWIPE_DIRECTION_UP; + + /* cancel gesture on direction reversal */ + + if (priv->h_direction == 0) + priv->h_direction = h_direction; + + if (priv->v_direction == 0) + priv->v_direction = v_direction; + + if (priv->h_direction != h_direction) + return FALSE; + + if (priv->v_direction != v_direction) + return FALSE; + + return TRUE; +} + + +static void +gesture_end (ClutterGestureAction *action, + ClutterActor *actor) +{ + gfloat press_x, press_y; + gfloat release_x, release_y; + ClutterSwipeDirection direction = 0; + ClutterSettings *settings = clutter_settings_get_default (); + gint drag_threshold; + + clutter_gesture_action_get_press_coords (CLUTTER_GESTURE_ACTION (action), + 0, + &press_x, &press_y); + + clutter_gesture_action_get_release_coords (CLUTTER_GESTURE_ACTION (action), + 0, + &release_x, &release_y); + + g_object_get (settings, + "dnd-drag-threshold", &drag_threshold, + NULL); + + if (release_x - press_x > drag_threshold) + direction |= CLUTTER_SWIPE_DIRECTION_RIGHT; + else if (press_x - release_x > drag_threshold) + direction |= CLUTTER_SWIPE_DIRECTION_LEFT; + + if (release_y - press_y > drag_threshold) + direction |= CLUTTER_SWIPE_DIRECTION_DOWN; + else if (press_y - release_y > drag_threshold) + direction |= CLUTTER_SWIPE_DIRECTION_UP; + + g_signal_emit (action, swipe_signals[SWEPT], 0, actor, direction); +} + +static void +clutter_swipe_action_class_init (ClutterSwipeActionClass *klass) +{ + ClutterGestureActionClass *gesture_class = + CLUTTER_GESTURE_ACTION_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ClutterSwipeActionPrivate)); + + gesture_class->gesture_begin = gesture_begin; + gesture_class->gesture_progress = gesture_progress; + gesture_class->gesture_end = gesture_end; + + /** + * ClutterSwipeAction::swept: + * @action: the #ClutterSwipeAction that emitted the signal + * @actor: the #ClutterActor attached to the @action + * @direction: the main direction of the swipe gesture + * + * The ::swept signal is emitted when a swipe gesture is recognized on the + * attached actor. + * + * Since: 1.8 + */ + swipe_signals[SWEPT] = + g_signal_new (I_("swept"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterSwipeActionClass, swept), + NULL, NULL, + _clutter_marshal_VOID__OBJECT_INT, + G_TYPE_NONE, 2, + CLUTTER_TYPE_ACTOR, + G_TYPE_INT); +} + +static void +clutter_swipe_action_init (ClutterSwipeAction *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_SWIPE_ACTION, + ClutterSwipeActionPrivate); + + clutter_gesture_action_set_required_devices (CLUTTER_GESTURE_ACTION (self), + 1); +} + +/** + * clutter_swipe_action_new: + * + * Creates a new #ClutterSwipeAction instance + * + * Return value: the newly created #ClutterSwipeAction + * + * Since: 1.8 + */ +ClutterAction * +clutter_swipe_action_new (void) +{ + return g_object_new (CLUTTER_TYPE_SWIPE_ACTION, NULL); +} diff --git a/clutter/clutter-swipe-action.h b/clutter/clutter-swipe-action.h new file mode 100644 index 000000000..ed9246de2 --- /dev/null +++ b/clutter/clutter-swipe-action.h @@ -0,0 +1,117 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 Intel Corporation. + * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Tomeu Vizoso + * + * Based on ClutterDragAction, written by: + * Emmanuele Bassi + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_SWIPE_ACTION_H__ +#define __CLUTTER_SWIPE_ACTION_H__ + +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_SWIPE_ACTION (clutter_swipe_action_get_type ()) +#define CLUTTER_SWIPE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SWIPE_ACTION, ClutterSwipeAction)) +#define CLUTTER_IS_SWIPE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SWIPE_ACTION)) +#define CLUTTER_SWIPE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SWIPE_ACTION, ClutterSwipeActionClass)) +#define CLUTTER_IS_SWIPE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SWIPE_ACTION)) +#define CLUTTER_SWIPE_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_SWIPE_ACTION, ClutterSwipeActionClass)) + +typedef struct _ClutterSwipeAction ClutterSwipeAction; +typedef struct _ClutterSwipeActionPrivate ClutterSwipeActionPrivate; +typedef struct _ClutterSwipeActionClass ClutterSwipeActionClass; + +/** + * ClutterSwipeDirection: + * @CLUTTER_SWIPE_DIRECTION_UP: Upwards swipe gesture + * @CLUTTER_SWIPE_DIRECTION_DOWN: Downwards swipe gesture + * @CLUTTER_SWIPE_DIRECTION_LEFT: Leftwards swipe gesture + * @CLUTTER_SWIPE_DIRECTION_RIGHT: Rightwards swipe gesture + * + * The main direction of the swipe gesture + * + * Since: 1.8 + */ +typedef enum { /*< prefix=CLUTTER_SWIPE >*/ + CLUTTER_SWIPE_DIRECTION_UP = 1 << 0, + CLUTTER_SWIPE_DIRECTION_DOWN = 1 << 1, + CLUTTER_SWIPE_DIRECTION_LEFT = 1 << 2, + CLUTTER_SWIPE_DIRECTION_RIGHT = 1 << 3 +} ClutterSwipeDirection; + +/** + * ClutterSwipeAction: + * + * The ClutterSwipeAction structure contains + * only private data and should be accessed using the provided API + * + * Since: 1.8 + */ +struct _ClutterSwipeAction +{ + /*< private >*/ + ClutterGestureAction parent_instance; + + ClutterSwipeActionPrivate *priv; +}; + +/** + * ClutterSwipeActionClass: + * @swept: class handler for the #ClutterSwipeAction::swept signal + * + * Since: 1.8 + */ +struct _ClutterSwipeActionClass +{ + /*< private >*/ + ClutterGestureActionClass parent_class; + + /*< public >*/ + void (* swept) (ClutterSwipeAction *action, + ClutterActor *actor, + ClutterSwipeDirection direction); + + /*< private >*/ + void (* _clutter_swipe_action1) (void); + void (* _clutter_swipe_action2) (void); + void (* _clutter_swipe_action3) (void); + void (* _clutter_swipe_action4) (void); + void (* _clutter_swipe_action5) (void); + void (* _clutter_swipe_action6) (void); + void (* _clutter_swipe_action7) (void); +}; + +GType clutter_swipe_action_get_type (void) G_GNUC_CONST; + +ClutterAction *clutter_swipe_action_new (void); + +G_END_DECLS + +#endif /* __CLUTTER_SWIPE_ACTION_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 3729cf8eb..d64a9b722 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -73,6 +73,7 @@ #include "clutter-fixed-layout.h" #include "clutter-flow-layout.h" #include "clutter-frame-source.h" +#include "clutter-gesture-action.h" #include "clutter-group.h" #include "clutter-input-device.h" #include "clutter-interval.h" @@ -95,6 +96,7 @@ #include "clutter-shader.h" #include "clutter-shader-effect.h" #include "clutter-shader-types.h" +#include "clutter-swipe-action.h" #include "clutter-snap-constraint.h" #include "clutter-stage.h" #include "clutter-stage-manager.h" diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 5c04bf61e..38c748cfc 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -52,6 +52,7 @@ UNIT_TESTS = \ test-drag.c \ test-constraints.c \ test-scrolling.c \ + test-swipe-action.c \ test-cogl-point-sprites.c \ test-table-layout.c \ test-path-constraint.c \ diff --git a/tests/interactive/test-swipe-action.c b/tests/interactive/test-swipe-action.c new file mode 100644 index 000000000..2837554e5 --- /dev/null +++ b/tests/interactive/test-swipe-action.c @@ -0,0 +1,109 @@ +#include +#include + +static guint VERTICAL = 0; +static guint HORIZONTAL = 1; +static guint BOTH = 2; + +static void +swept_cb (ClutterSwipeAction *action, + ClutterActor *actor, + ClutterSwipeDirection direction, + guint axis) +{ + gchar *direction_str = ""; + + if (axis == HORIZONTAL && + (direction == CLUTTER_SWIPE_DIRECTION_UP || + direction == CLUTTER_SWIPE_DIRECTION_DOWN)) + return; + else if (axis == VERTICAL && + (direction == CLUTTER_SWIPE_DIRECTION_LEFT || + direction == CLUTTER_SWIPE_DIRECTION_RIGHT)) + return; + + if (direction & CLUTTER_SWIPE_DIRECTION_UP) + direction_str = g_strconcat (direction_str, " up", NULL); + + if (direction & CLUTTER_SWIPE_DIRECTION_DOWN) + direction_str = g_strconcat (direction_str, " down", NULL); + + if (direction & CLUTTER_SWIPE_DIRECTION_LEFT) + direction_str = g_strconcat (direction_str, " left", NULL); + + if (direction & CLUTTER_SWIPE_DIRECTION_RIGHT) + direction_str = g_strconcat (direction_str, " right", NULL); + + g_debug ("swept_cb '%s'%s", clutter_actor_get_name (actor), direction_str); +} + +static gboolean +gesture_progress_cb (ClutterSwipeAction *action, + ClutterActor *actor, + gpointer user_data) +{ + return TRUE; +} + +static void +gesture_cancel_cb (ClutterSwipeAction *action, + ClutterActor *actor, + gpointer user_data) +{ + g_debug ("gesture_cancel_cb '%s'", clutter_actor_get_name (actor)); +} + +static void +attach_action (ClutterActor *actor, guint axis) +{ + ClutterAction *action; + + action = g_object_new (CLUTTER_TYPE_SWIPE_ACTION, NULL); + clutter_actor_add_action (actor, action); + g_signal_connect (action, "swept", G_CALLBACK (swept_cb), (gpointer) axis); + g_signal_connect (action, "gesture-progress", G_CALLBACK (gesture_progress_cb), NULL); + g_signal_connect (action, "gesture-cancel", G_CALLBACK (gesture_cancel_cb), NULL); +} + +G_MODULE_EXPORT int +test_swipe_action_main (int argc, char *argv[]) +{ + ClutterActor *stage, *rect; + + if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) + return 1; + + stage = clutter_stage_get_default (); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Swipe action test"); + clutter_actor_set_size (stage, 640, 480); + + rect = clutter_rectangle_new_with_color (CLUTTER_COLOR_Red); + clutter_actor_set_name (rect, "Vertical swipes"); + clutter_actor_set_size (rect, 150, 150); + clutter_actor_set_position (rect, 10, 100); + clutter_actor_set_reactive (rect, TRUE); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); + attach_action (rect, VERTICAL); + + rect = clutter_rectangle_new_with_color (CLUTTER_COLOR_Blue); + clutter_actor_set_name (rect, "Horizontal swipes"); + clutter_actor_set_size (rect, 150, 150); + clutter_actor_set_position (rect, 170, 100); + clutter_actor_set_reactive (rect, TRUE); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); + attach_action (rect, HORIZONTAL); + + rect = clutter_rectangle_new_with_color (CLUTTER_COLOR_Green); + clutter_actor_set_name (rect, "All swipes"); + clutter_actor_set_size (rect, 150, 150); + clutter_actor_set_position (rect, 330, 100); + clutter_actor_set_reactive (rect, TRUE); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); + attach_action (rect, BOTH); + + clutter_actor_show_all (stage); + + clutter_main (); + + return EXIT_SUCCESS; +}