[clutter: 1/8] Add ClutterSwipeAction and ClutterGestureAction



commit ba72235b66ee956977ad6e5c7a8510f9f23a3517
Author: Tomeu Vizoso <tomeu vizoso collabora co uk>
Date:   Wed Mar 9 10:06:44 2011 +0100

    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(+), 0 deletions(-)
---
diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index 476a38d..7702a0e 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 0000000..1ab84ea
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Tomeu Vizoso <tomeu vizoso collabora co uk>
+ */
+
+/**
+ * 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 0000000..e1f3efa
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Tomeu Vizoso <tomeu vizoso collabora co uk>
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <clutter/clutter.h> can be included directly."
+#endif
+
+#ifndef __CLUTTER_GESTURE_ACTION_H__
+#define __CLUTTER_GESTURE_ACTION_H__
+
+#include <clutter/clutter-action.h>
+
+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 <structname>ClutterGestureAction</structname> 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 563508f..f9a604b 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 0000000..98b575f
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Tomeu Vizoso <tomeu vizoso collabora co uk>
+ *
+ * Based on ClutterDragAction, written by:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+/**
+ * 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 0000000..ed9246d
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Tomeu Vizoso <tomeu vizoso collabora co uk>
+ *
+ * Based on ClutterDragAction, written by:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <clutter/clutter.h> can be included directly."
+#endif
+
+#ifndef __CLUTTER_SWIPE_ACTION_H__
+#define __CLUTTER_SWIPE_ACTION_H__
+
+#include <clutter/clutter-gesture-action.h>
+
+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 <structname>ClutterSwipeAction</structname> 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 3729cf8..d64a9b7 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 5c04bf6..38c748c 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 0000000..2837554
--- /dev/null
+++ b/tests/interactive/test-swipe-action.c
@@ -0,0 +1,109 @@
+#include <stdlib.h>
+#include <clutter/clutter.h>
+
+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;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]