[gtk+/gestures: 2/8] Add GtkGesture



commit 58a075346e43c5c574cd59cbc7e4443540e61bc1
Author: Carlos Garnacho <carlos lanedo com>
Date:   Thu Jan 17 20:59:07 2013 +0100

    Add GtkGesture
    
    This a more specific abstract type that handles one or multiple
    streams of pointer/touch events.

 gtk/Makefile.am        |    2 +
 gtk/gtk.h              |    1 +
 gtk/gtkgesture.c       |  453 ++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkgesture.h       |   79 +++++++++
 gtk/gtkmarshalers.list |    1 +
 5 files changed, 536 insertions(+), 0 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index bb692ec..35148b3 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -257,6 +257,7 @@ gtk_public_h_sources = 		\
 	gtkfontchooserdialog.h  \
 	gtkfontchooserwidget.h	\
 	gtkframe.h		\
+	gtkgesture.h		\
 	gtkgrid.h		\
 	gtkiconfactory.h	\
 	gtkicontheme.h		\
@@ -728,6 +729,7 @@ gtk_base_c_sources = 		\
 	gtkfontchooserutils.c	\
 	gtkfontchooserwidget.c	\
 	gtkframe.c		\
+	gtkgesture.c		\
 	gtkgrid.c		\
 	gtkhsla.c		\
 	gtkiconcache.c		\
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 27a61df..02b192f 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -108,6 +108,7 @@
 #include <gtk/gtkfontchooserdialog.h>
 #include <gtk/gtkfontchooserwidget.h>
 #include <gtk/gtkframe.h>
+#include <gtk/gtkgesture.h>
 #include <gtk/gtkgrid.h>
 #include <gtk/gtkiconfactory.h>
 #include <gtk/gtkicontheme.h>
diff --git a/gtk/gtkgesture.c b/gtk/gtkgesture.c
new file mode 100644
index 0000000..45b3e36
--- /dev/null
+++ b/gtk/gtkgesture.c
@@ -0,0 +1,453 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2012, One Laptop Per Child.
+ *
+ * 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(s): Carlos Garnacho <carlos lanedo com>
+ */
+#include "config.h"
+#include <gtk/gtkgesture.h>
+#include "gtkprivate.h"
+#include "gtkmarshalers.h"
+#include "gtkintl.h"
+
+typedef struct _PointData PointData;
+
+enum {
+  PROP_N_POINTS = 1
+};
+
+enum {
+  CHECK,
+  BEGIN,
+  END,
+  UPDATE,
+  N_SIGNALS
+};
+
+struct _PointData
+{
+  GdkPoint point;
+  GdkEventSequence *sequence;
+  guint32 evtime;
+};
+
+struct _GtkGesturePriv
+{
+  GHashTable *points;
+  gint n_points;
+  guint recognized : 1;
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+G_DEFINE_ABSTRACT_TYPE (GtkGesture, gtk_gesture, GTK_TYPE_EVENT_CONTROLLER)
+
+static void
+gtk_gesture_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  GtkGesturePriv *priv = GTK_GESTURE (object)->_priv;
+
+  switch (prop_id)
+    {
+    case PROP_N_POINTS:
+      g_value_set_int (value, priv->n_points);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_gesture_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  GtkGesturePriv *priv = GTK_GESTURE (object)->_priv;
+
+  switch (prop_id)
+    {
+    case PROP_N_POINTS:
+      priv->n_points = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_gesture_finalize (GObject *object)
+{
+  GtkGesturePriv *priv = GTK_GESTURE (object)->_priv;
+
+  g_hash_table_destroy (priv->points);
+
+  G_OBJECT_CLASS (gtk_gesture_parent_class)->finalize (object);
+}
+
+static gboolean
+gtk_gesture_check_impl (GtkGesture *gesture)
+{
+  return TRUE;
+}
+
+static void
+_gtk_gesture_set_recognized (GtkGesture *gesture,
+                             gboolean    recognized)
+{
+  if (gesture->_priv->recognized == recognized)
+    return;
+
+  gesture->_priv->recognized = recognized;
+
+  if (recognized)
+    g_signal_emit (gesture, signals[BEGIN], 0);
+  else
+    g_signal_emit (gesture, signals[END], 0);
+}
+
+static gboolean
+_gtk_gesture_do_check (GtkGesture *gesture)
+{
+  gboolean retval;
+
+  g_signal_emit (G_OBJECT (gesture), signals[CHECK], 0, &retval);
+
+  return retval;
+}
+
+static void
+_gtk_gesture_check_recognized (GtkGesture *gesture)
+{
+  GtkGesturePriv *priv = gesture->_priv;
+  gint current_n_points;
+
+  current_n_points = g_hash_table_size (priv->points);
+
+  if (priv->recognized && current_n_points != priv->n_points)
+    _gtk_gesture_set_recognized (gesture, FALSE);
+  else if (!priv->recognized &&
+           current_n_points == priv->n_points &&
+           _gtk_gesture_do_check (gesture))
+    _gtk_gesture_set_recognized (gesture, TRUE);
+}
+
+static gboolean
+_gtk_gesture_update_point (GtkGesture *gesture,
+                           GdkEvent   *event,
+                           gboolean    add)
+{
+  GdkEventSequence *sequence;
+  GtkGesturePriv *priv;
+  PointData *data;
+  guint32 evtime;
+  gdouble x, y;
+
+  if (!gdk_event_get_coords (event, &x, &y))
+    return FALSE;
+
+  priv = gesture->_priv;
+  sequence = gdk_event_get_event_sequence (event);
+  evtime = gdk_event_get_time (event);
+
+  if (!g_hash_table_lookup_extended (priv->points, sequence,
+                                     NULL, (gpointer *) &data))
+    {
+      if (!add)
+        return FALSE;
+
+      data = g_new0 (PointData, 1);
+      g_hash_table_insert (priv->points, sequence, data);
+    }
+
+  data->point.x = x;
+  data->point.y = y;
+  data->sequence = sequence;
+  data->evtime = evtime;
+
+  return TRUE;
+}
+
+static gboolean
+gtk_gesture_handle_event (GtkEventController *controller,
+                          GdkEvent           *event)
+{
+  GtkGesture *gesture = GTK_GESTURE (controller);
+  GtkGesturePriv *priv = gesture->_priv;
+  GdkEventSequence *sequence;
+
+  sequence = gdk_event_get_event_sequence (event);
+
+  switch (event->type)
+    {
+    case GDK_TOUCH_BEGIN:
+    case GDK_BUTTON_PRESS:
+      _gtk_gesture_update_point (gesture, event, TRUE);
+      _gtk_gesture_check_recognized (gesture);
+      break;
+    case GDK_TOUCH_END:
+    case GDK_BUTTON_RELEASE:
+      if (_gtk_gesture_update_point (gesture, event, FALSE))
+        {
+          if (priv->recognized)
+            {
+              g_signal_emit (gesture, signals[UPDATE], 0, sequence);
+              _gtk_gesture_set_recognized (gesture, FALSE);
+            }
+
+          g_hash_table_remove (priv->points, sequence);
+        }
+      break;
+    case GDK_TOUCH_UPDATE:
+    case GDK_MOTION_NOTIFY:
+      if (_gtk_gesture_update_point (gesture, event, FALSE))
+        {
+          _gtk_gesture_check_recognized (gesture);
+
+          if (priv->recognized)
+            g_signal_emit (gesture, signals[UPDATE], 0, sequence);
+        }
+      break;
+    default:
+      break;
+    }
+
+  return FALSE;
+}
+
+static void
+gtk_gesture_class_init (GtkGestureClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
+
+  object_class->get_property = gtk_gesture_get_property;
+  object_class->set_property = gtk_gesture_set_property;
+  object_class->finalize = gtk_gesture_finalize;
+
+  controller_class->handle_event = gtk_gesture_handle_event;
+
+  klass->check = gtk_gesture_check_impl;
+
+  g_object_class_install_property (object_class,
+                                   PROP_N_POINTS,
+                                   g_param_spec_int ("n-points",
+                                                     P_("Number of points"),
+                                                     P_("Number of points needed "
+                                                        "to trigger the gesture"),
+                                                     1, G_MAXINT, 1,
+                                                     GTK_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT_ONLY));
+
+  signals[CHECK] = g_signal_new ("check",
+                                 G_TYPE_FROM_CLASS (klass),
+                                 G_SIGNAL_RUN_LAST,
+                                 G_STRUCT_OFFSET (GtkGestureClass, check),
+                                 g_signal_accumulator_true_handled, NULL,
+                                 _gtk_marshal_BOOLEAN__VOID,
+                                 G_TYPE_BOOLEAN, 0);
+  signals[BEGIN] = g_signal_new ("begin",
+                                 G_TYPE_FROM_CLASS (klass),
+                                 G_SIGNAL_RUN_LAST,
+                                 G_STRUCT_OFFSET (GtkGestureClass, begin),
+                                 NULL, NULL,
+                                 g_cclosure_marshal_VOID__VOID,
+                                 G_TYPE_NONE, 0);
+  signals[END] = g_signal_new ("end",
+                               G_TYPE_FROM_CLASS (klass),
+                               G_SIGNAL_RUN_LAST,
+                               G_STRUCT_OFFSET (GtkGestureClass, end),
+                               NULL, NULL,
+                               g_cclosure_marshal_VOID__VOID,
+                               G_TYPE_NONE, 0);
+  signals[UPDATE] = g_signal_new ("update",
+                                  G_TYPE_FROM_CLASS (klass),
+                                  G_SIGNAL_RUN_LAST,
+                                  G_STRUCT_OFFSET (GtkGestureClass, update),
+                                  NULL, NULL,
+                                  g_cclosure_marshal_VOID__POINTER,
+                                  G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+  g_type_class_add_private (klass, sizeof (GtkGesturePriv));
+}
+
+static void
+gtk_gesture_init (GtkGesture *gesture)
+{
+  GtkGesturePriv *priv;
+
+  gesture->_priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (gesture,
+                                                       GTK_TYPE_GESTURE,
+                                                       GtkGesturePriv);
+  priv->points = g_hash_table_new_full (NULL, NULL, NULL,
+                                        (GDestroyNotify) g_free);
+}
+
+/**
+ * gtk_gesture_get_sequences:
+ * @gesture: a #GtkGesture
+ *
+ * Returns the list of #GdkEventSequence<!-- -->s currently being interpreted
+ * by @gesture
+ *
+ * Returns: (transfer container) (element-type:Gdk.EventSequence): A list
+ *          of #GdkEventSequence<!-- -->s, the list elements are owned by GTK+
+ *          and must not be freed or modified, the list itself must be deleted
+ *          through g_list_free()
+ **/
+GList *
+gtk_gesture_get_sequences (GtkGesture *gesture)
+{
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
+
+  return g_hash_table_get_keys (gesture->_priv->points);
+}
+
+/**
+ * gtk_gesture_get_point:
+ * @gesture: a #GtkGesture
+ * @sequence: (allow-none): a #GdkEventSequence, or %NULL for pointer events
+ * @x: (out) (allow-none): return location for X axis of the sequence coordinates
+ * @y: (out) (allow-none): return location for Y axis of the sequence coordinates
+ *
+ * If @sequence is currently being interpreted by @gesture, this
+ * function returns %TRUE and fills in @x and @y with the last coordinates
+ * stored for that event sequence.
+ *
+ * Returns: %TRUE if @sequence is currently interpreted
+ *
+ * Since: 3.8
+ **/
+gboolean
+gtk_gesture_get_point (GtkGesture       *gesture,
+                       GdkEventSequence *sequence,
+                       gdouble          *x,
+                       gdouble          *y)
+{
+  GtkGesturePriv *priv;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gesture->_priv;
+
+  if (!g_hash_table_lookup_extended (priv->points, sequence,
+                                     NULL, (gpointer *) &data))
+    return FALSE;
+
+  if (x)
+    *x = data->point.x;
+
+  if (y)
+    *y = data->point.y;
+
+  return TRUE;
+}
+
+/**
+ * gtk_gesture_get_last_update_time:
+ * @gesture: a #GtkGesture
+ * @sequence: (allow-none): a #GdkEventSequence, or %NULL for pointer events
+ * @evtime: (out) (allow-none): return location for last update time
+ *
+ * If @sequence is being interpreted by @gesture, this function
+ * returns %TRUE and fills @evtime with the last event time it
+ * received from that @sequence.
+ *
+ * Returns: %TRUE if @sequence is currently interpreted
+ *
+ * Since: 3.8
+ **/
+gboolean
+gtk_gesture_get_last_update_time (GtkGesture       *gesture,
+                                  GdkEventSequence *sequence,
+                                  guint32          *evtime)
+{
+  GtkGesturePriv *priv;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gesture->_priv;
+
+  if (!g_hash_table_lookup_extended (priv->points, sequence,
+                                     NULL, (gpointer *) &data))
+    return FALSE;
+
+  if (evtime)
+    *evtime = data->evtime;
+
+  return TRUE;
+};
+
+gboolean
+gtk_gesture_get_bounding_box (GtkGesture            *gesture,
+                              cairo_rectangle_int_t *rect)
+{
+  GtkGesturePriv *priv = gesture->_priv;
+  gdouble x1, y1, x2, y2;
+  GHashTableIter iter;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+  g_return_val_if_fail (rect != NULL, FALSE);
+
+  if (g_hash_table_size (priv->points) == 0)
+    return FALSE;
+
+  x1 = y1 = G_MAXDOUBLE;
+  x2 = y2 = -G_MAXDOUBLE;
+
+  g_hash_table_iter_init (&iter, priv->points);
+
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
+    {
+      x1 = MIN (x1, data->point.x);
+      y1 = MIN (y1, data->point.y);
+      x2 = MAX (x2, data->point.x);
+      y2 = MAX (y2, data->point.y);
+    }
+
+  rect->x = x1;
+  rect->y = y1;
+  rect->width = x2 - x1;
+  rect->height = y2 - y1;
+
+  return TRUE;
+}
+
+/**
+ * gtk_gesture_is_active:
+ * @gesture: a #GtkGesture
+ *
+ * Returns %TRUE if the gesture is currently active.
+ * A gesture is active if there are as many interacting
+ * touch sequences as required by @gesture, and @gesture
+ * is interpreting those to potentially trigger an action.
+ *
+ * Returns: %TRUE if gesture is active.
+ *
+ * Since: 3.8
+ **/
+gboolean
+gtk_gesture_is_active (GtkGesture *gesture)
+{
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  return gesture->_priv->recognized;
+}
diff --git a/gtk/gtkgesture.h b/gtk/gtkgesture.h
new file mode 100644
index 0000000..9ea44fb
--- /dev/null
+++ b/gtk/gtkgesture.h
@@ -0,0 +1,79 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2012, One Laptop Per Child.
+ *
+ * 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(s): Carlos Garnacho <carlos lanedo com>
+ */
+#ifndef __GTK_GESTURE_H__
+#define __GTK_GESTURE_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkeventcontroller.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_GESTURE         (gtk_gesture_get_type ())
+#define GTK_GESTURE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_GESTURE, GtkGesture))
+#define GTK_GESTURE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_GESTURE, GtkGestureClass))
+#define GTK_IS_GESTURE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_GESTURE))
+#define GTK_IS_GESTURE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_GESTURE))
+#define GTK_GESTURE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_GESTURE, GtkGestureClass))
+
+typedef struct _GtkGesture GtkGesture;
+typedef struct _GtkGestureClass GtkGestureClass;
+typedef struct _GtkGesturePriv GtkGesturePriv;
+
+struct _GtkGesture
+{
+  GtkEventController parent_instance;
+
+  /*< private >*/
+  GtkGesturePriv *_priv;
+};
+
+struct _GtkGestureClass
+{
+  GtkEventControllerClass parent_class;
+
+  gboolean (* check)  (GtkGesture       *gesture);
+
+  void     (* begin)  (GtkGesture       *gesture);
+  void     (* update) (GtkGesture       *gesture,
+                       GdkEventSequence *sequence);
+  void     (* end)    (GtkGesture       *gesture);
+};
+
+GType      gtk_gesture_get_type             (void) G_GNUC_CONST;
+
+GList    * gtk_gesture_get_sequences        (GtkGesture            *gesture);
+
+gboolean   gtk_gesture_get_point            (GtkGesture            *gesture,
+                                             GdkEventSequence      *sequence,
+                                             gdouble               *x,
+                                             gdouble               *y);
+gboolean   gtk_gesture_get_last_update_time (GtkGesture            *gesture,
+                                             GdkEventSequence      *sequence,
+                                             guint32               *evtime);
+gboolean   gtk_gesture_get_bounding_box     (GtkGesture            *gesture,
+                                             cairo_rectangle_int_t *rect);
+gboolean   gtk_gesture_is_active            (GtkGesture            *gesture);
+
+
+G_END_DECLS
+
+#endif /* __GTK_GESTURE_H__ */
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index 14d0519..e19a0d0 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -52,6 +52,7 @@ OBJECT:VOID
 STRING:DOUBLE
 STRING:STRING
 VOID:DOUBLE
+VOID:DOUBLE,DOUBLE
 VOID:BOOLEAN
 VOID:BOOLEAN,BOOLEAN,BOOLEAN
 VOID:BOXED



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