[gtk+] Break out press-and-hold code as its own object



commit 912ad3b6989c9206fea95afe9a0fd03f8413db08
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Mar 3 23:42:54 2012 -0500

    Break out press-and-hold code as its own object
    
    https://bugzilla.gnome.org/show_bug.cgi?id=671057

 gtk/Makefile.am              |    2 +
 gtk/gtkcolorplane.c          |   58 +++++++++++
 gtk/gtkcolorswatch.c         |   95 ++++++------------
 gtk/gtkpressandhold.c        |  226 ++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkpressandholdprivate.h |   68 +++++++++++++
 5 files changed, 383 insertions(+), 66 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 9f4f818..bb60b3d 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -470,6 +470,7 @@ gtk_private_h_sources =		\
 	gtkorientableprivate.h	\
 	gtkpango.h		\
 	gtkpathbar.h		\
+	gtkpressandholdprivate.h \
 	gtkprintoperation-private.h \
 	gtkprintutils.h		\
 	gtkprivate.h		\
@@ -707,6 +708,7 @@ gtk_base_c_sources = 		\
 	gtkpango.c		\
 	gtkpapersize.c		\
 	gtkpathbar.c		\
+	gtkpressandhold.c	\
 	gtkprintcontext.c	\
 	gtkprintoperation.c	\
 	gtkprintoperationpreview.c \
diff --git a/gtk/gtkcolorplane.c b/gtk/gtkcolorplane.c
index c8b2d71..c1ca898 100644
--- a/gtk/gtkcolorplane.c
+++ b/gtk/gtkcolorplane.c
@@ -22,6 +22,7 @@
 #include "gtkaccessible.h"
 #include "gtkadjustment.h"
 #include "gtkcolorutils.h"
+#include "gtkpressandholdprivate.h"
 #include "gtkintl.h"
 
 struct _GtkColorPlanePrivate
@@ -32,6 +33,8 @@ struct _GtkColorPlanePrivate
 
   cairo_surface_t *surface;
   gboolean in_drag;
+
+  GtkPressAndHold *press_and_hold;
 };
 
 G_DEFINE_TYPE (GtkColorPlane, gtk_color_plane, GTK_TYPE_DRAWING_AREA)
@@ -286,6 +289,58 @@ plane_motion_notify (GtkWidget      *widget,
 }
 
 static void
+hold_action (GtkPressAndHold *pah,
+             gint             x,
+             gint             y,
+             GtkColorPlane   *plane)
+{
+  gboolean handled;
+
+  g_signal_emit_by_name (plane, "popup-menu", &handled);
+}
+
+static void
+tap_action (GtkPressAndHold *pah,
+            gint             x,
+            gint             y,
+            GtkColorPlane   *plane)
+{
+  update_color (plane, x, y);
+}
+
+static gboolean
+plane_touch (GtkWidget     *widget,
+             GdkEventTouch *event)
+{
+  GtkColorPlane *plane = GTK_COLOR_PLANE (widget);
+
+  if (!plane->priv->press_and_hold)
+    {
+      gint drag_threshold;
+
+      g_object_get (gtk_widget_get_settings (widget),
+                    "gtk-dnd-drag-threshold", &drag_threshold,
+                    NULL);
+
+      plane->priv->press_and_hold = gtk_press_and_hold_new ();
+
+      g_object_set (plane->priv->press_and_hold,
+                    "drag-threshold", drag_threshold,
+                    "hold-time", 1000,
+                    NULL);
+
+      g_signal_connect (plane->priv->press_and_hold, "hold",
+                        G_CALLBACK (hold_action), plane);
+      g_signal_connect (plane->priv->press_and_hold, "tap",
+                        G_CALLBACK (tap_action), plane);
+    }
+
+  gtk_press_and_hold_process_event (plane->priv->press_and_hold, (GdkEvent *)event);
+
+  return TRUE;
+}
+
+static void
 sv_move (GtkColorPlane *plane,
          gdouble        ds,
          gdouble        dv)
@@ -406,6 +461,8 @@ plane_finalize (GObject *object)
   g_clear_object (&plane->priv->s_adj);
   g_clear_object (&plane->priv->v_adj);
 
+  g_clear_object (&plane->priv->press_and_hold);
+
   G_OBJECT_CLASS (gtk_color_plane_parent_class)->finalize (object);
 }
 
@@ -424,6 +481,7 @@ gtk_color_plane_class_init (GtkColorPlaneClass *class)
   widget_class->motion_notify_event = plane_motion_notify;
   widget_class->grab_broken_event = plane_grab_broken;
   widget_class->key_press_event = plane_key_press;
+  widget_class->touch_event= plane_touch;
 
   g_type_class_add_private (class, sizeof (GtkColorPlanePrivate));
 }
diff --git a/gtk/gtkcolorswatch.c b/gtk/gtkcolorswatch.c
index c75ef2d..810112d 100644
--- a/gtk/gtkcolorswatch.c
+++ b/gtk/gtkcolorswatch.c
@@ -28,20 +28,12 @@
 #include "gtkmenu.h"
 #include "gtkmenuitem.h"
 #include "gtkmenushell.h"
+#include "gtkpressandholdprivate.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
 #include "a11y/gtkcolorswatchaccessible.h"
 
 
-typedef struct {
-  GtkWidget *widget;
-
-  GdkEventSequence *sequence;
-  guint press_and_hold_id;
-  gint start_x;
-  gint start_y;
-} GtkPressAndHoldData;
-
 struct _GtkColorSwatchPrivate
 {
   GdkRGBA color;
@@ -54,7 +46,7 @@ struct _GtkColorSwatchPrivate
 
   GdkWindow *event_window;
 
-  GtkPressAndHoldData *press_and_hold;
+  GtkPressAndHold *press_and_hold;
 };
 
 enum
@@ -531,80 +523,52 @@ swatch_button_release (GtkWidget      *widget,
 }
 
 static void
-swatch_press_and_hold_cancel (GtkWidget           *widget,
-                              GtkPressAndHoldData *data)
+hold_action (GtkPressAndHold *pah,
+             gint             x,
+             gint             y,
+             GtkColorSwatch  *swatch)
 {
-  if (data->press_and_hold_id)
-    {
-      g_source_remove (data->press_and_hold_id);
-      data->press_and_hold_id = 0;
-    }
-  
-  data->sequence = NULL;
+  emit_customize (swatch);
 }
 
 static void
-swatch_press_and_hold_free (GtkPressAndHoldData *data)
+tap_action (GtkPressAndHold *pah,
+            gint             x,
+            gint             y,
+            GtkColorSwatch  *swatch)
 {
-  swatch_press_and_hold_cancel (data->widget, data);
-  g_slice_free (GtkPressAndHoldData, data);
-}
-
-static gboolean
-swatch_press_and_hold_action (gpointer data)
-{
-  GtkPressAndHoldData *pah = data;
-
-  emit_customize (GTK_COLOR_SWATCH (pah->widget));
-  swatch_press_and_hold_cancel (pah->widget, pah);
-
-  return G_SOURCE_REMOVE;
+  swatch_primary_action (swatch);
 }
 
-
 static gboolean
 swatch_touch (GtkWidget     *widget,
               GdkEventTouch *event)
 {
   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
-  GtkPressAndHoldData *data;
 
   if (!swatch->priv->press_and_hold)
-    swatch->priv->press_and_hold = g_slice_new0 (GtkPressAndHoldData);
+    {
+      gint drag_threshold;
 
-  data = swatch->priv->press_and_hold;
+      g_object_get (gtk_widget_get_settings (widget),
+                    "gtk-dnd-drag-threshold", &drag_threshold,
+                    NULL);
 
-  /* We're already tracking a different touch, ignore */
-  if (data->sequence != NULL && data->sequence != event->sequence)
-    return TRUE;
+      swatch->priv->press_and_hold = gtk_press_and_hold_new ();
 
-  if (event->type == GDK_TOUCH_BEGIN)
-    {
-      data->widget = widget;
-      data->sequence = event->sequence;
-      data->start_x = event->x;
-      data->start_y = event->y;
+      g_object_set (swatch->priv->press_and_hold,
+                    "drag-threshold", drag_threshold,
+                    "hold-time", 1000,
+                    NULL);
 
-      data->press_and_hold_id =
-          gdk_threads_add_timeout (1000, swatch_press_and_hold_action, data);
-    }
-  else if (event->type == GDK_TOUCH_UPDATE)
-    {
-      if (gtk_drag_check_threshold (widget,
-                                    data->start_x, data->start_y,
-                                    event->x, event->y))
-        swatch_press_and_hold_cancel (widget, data);
-    }
-  else if (event->type == GDK_TOUCH_END)
-    {
-      swatch_press_and_hold_cancel (widget, data);
-      swatch_primary_action (swatch);
-    }
-  else if (event->type == GDK_TOUCH_CANCEL)
-    {
-      swatch_press_and_hold_cancel (widget, data);
+      g_signal_connect (swatch->priv->press_and_hold, "hold",
+                        G_CALLBACK (hold_action), swatch);
+      g_signal_connect (swatch->priv->press_and_hold, "tap",
+                        G_CALLBACK (tap_action), swatch);
     }
 
+  gtk_press_and_hold_process_event (swatch->priv->press_and_hold, (GdkEvent *)event);
+
   return TRUE;
 }
 
@@ -759,8 +723,7 @@ swatch_finalize (GObject *object)
   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
 
   g_free (swatch->priv->icon);
-  if (swatch->priv->press_and_hold)
-    swatch_press_and_hold_free (swatch->priv->press_and_hold);
+  g_clear_object (&swatch->priv->press_and_hold);
 
   G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object);
 }
diff --git a/gtk/gtkpressandhold.c b/gtk/gtkpressandhold.c
new file mode 100644
index 0000000..aed0095
--- /dev/null
+++ b/gtk/gtkpressandhold.c
@@ -0,0 +1,226 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * 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/>.
+ */
+
+#include "config.h"
+
+#include "gdk.h"
+#include "gtkpressandholdprivate.h"
+#include "gtkintl.h"
+#include "gtkprivate.h"
+
+struct _GtkPressAndHoldPrivate
+{
+  gint hold_time;
+  gint drag_threshold;
+
+  GdkEventSequence *sequence;
+  guint timeout;
+  gint start_x;
+  gint start_y;
+  gint x;
+  gint y;
+};
+
+enum
+{
+  PROP_ZERO,
+  PROP_HOLD_TIME,
+  PROP_DRAG_THRESHOLD
+};
+
+enum
+{
+  HOLD,
+  TAP,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (GtkPressAndHold, gtk_press_and_hold, G_TYPE_OBJECT)
+
+static void
+gtk_press_and_hold_init (GtkPressAndHold *pah)
+{
+  pah->priv = G_TYPE_INSTANCE_GET_PRIVATE (pah,
+                                           GTK_TYPE_PRESS_AND_HOLD,
+                                           GtkPressAndHoldPrivate);
+
+  pah->priv->hold_time = 1000;
+  pah->priv->drag_threshold = 8;
+}
+
+static void
+press_and_hold_finalize (GObject *object)
+{
+  GtkPressAndHold *pah = GTK_PRESS_AND_HOLD (object);
+
+  if (pah->priv->timeout)
+    g_source_remove (pah->priv->timeout);
+
+  G_OBJECT_CLASS (gtk_press_and_hold_parent_class)->finalize (object);
+}
+
+static void
+press_and_hold_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  GtkPressAndHold *pah = GTK_PRESS_AND_HOLD (object);
+
+  switch (prop_id)
+    {
+    case PROP_HOLD_TIME:
+      g_value_set_int (value, pah->priv->hold_time);
+      break;
+    case PROP_DRAG_THRESHOLD:
+      g_value_set_int (value, pah->priv->drag_threshold);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+press_and_hold_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  GtkPressAndHold *pah = GTK_PRESS_AND_HOLD (object);
+
+  switch (prop_id)
+    {
+    case PROP_HOLD_TIME:
+      pah->priv->hold_time = g_value_get_int (value);
+      break;
+    case PROP_DRAG_THRESHOLD:
+      pah->priv->hold_time = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_press_and_hold_class_init (GtkPressAndHoldClass *class)
+{
+  GObjectClass *object_class = (GObjectClass *)class;
+
+  object_class->get_property = press_and_hold_get_property;
+  object_class->set_property = press_and_hold_set_property;
+  object_class->finalize = press_and_hold_finalize;
+
+  signals[HOLD] =
+    g_signal_new ("hold",
+                  GTK_TYPE_PRESS_AND_HOLD,
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (GtkPressAndHoldClass, hold),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
+
+  signals[TAP] =
+    g_signal_new ("tap",
+                  GTK_TYPE_PRESS_AND_HOLD,
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (GtkPressAndHoldClass, tap),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
+
+  g_object_class_install_property (object_class, PROP_HOLD_TIME,
+      g_param_spec_int ("hold-time", P_("Hold Time"), P_("Hold Time (in milliseconds)"),
+                        0, G_MAXINT, 1000, GTK_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class, PROP_DRAG_THRESHOLD,
+      g_param_spec_int ("drag-threshold", P_("Drag Threshold"), P_("Drag Threshold (in pixels)"),
+                        1, G_MAXINT, 8, GTK_PARAM_READWRITE));
+
+  g_type_class_add_private (object_class, sizeof (GtkPressAndHoldPrivate));
+}
+
+static void
+press_and_hold_cancel (GtkPressAndHold *pah)
+{
+  GtkPressAndHoldPrivate *priv = pah->priv;
+
+  if (priv->timeout)
+    g_source_remove (priv->timeout);
+
+  priv->timeout = 0;
+  priv->sequence = NULL;
+}
+
+static gboolean
+hold_action (gpointer data)
+{
+  GtkPressAndHold *pah = data;
+  GtkPressAndHoldPrivate *priv = pah->priv;
+
+  press_and_hold_cancel (pah);
+
+  g_signal_emit (pah, signals[HOLD], 0, priv->x, priv->y);
+
+  return G_SOURCE_REMOVE;
+}
+
+void
+gtk_press_and_hold_process_event (GtkPressAndHold *pah,
+                                  GdkEvent        *event)
+{
+  GtkPressAndHoldPrivate *priv = pah->priv;
+
+  /* We're already tracking a different touch, ignore */
+  if (priv->sequence != NULL && priv->sequence != event->touch.sequence)
+    return;
+
+  priv->x = event->touch.x;
+  priv->y = event->touch.y;
+
+  if (event->type == GDK_TOUCH_BEGIN)
+    {
+      priv->sequence = event->touch.sequence;
+      priv->start_x = priv->x;
+      priv->start_y = priv->y;
+
+      priv->timeout =
+          gdk_threads_add_timeout (priv->hold_time, hold_action, pah);
+    }
+  else if (event->type == GDK_TOUCH_UPDATE)
+    {
+      if (ABS (priv->x - priv->start_x) > priv->drag_threshold ||
+          ABS (priv->y - priv->start_y) > priv->drag_threshold)
+        press_and_hold_cancel (pah);
+    }
+  else if (event->type == GDK_TOUCH_END)
+    {
+      press_and_hold_cancel (pah);
+      g_signal_emit (pah, signals[TAP], 0, priv->x, priv->y);
+    }
+  else if (event->type == GDK_TOUCH_CANCEL)
+    {
+      press_and_hold_cancel (pah);
+    }
+}
+
+GtkPressAndHold *
+gtk_press_and_hold_new (void)
+{
+  return (GtkPressAndHold *) g_object_new (GTK_TYPE_PRESS_AND_HOLD, NULL);
+}
diff --git a/gtk/gtkpressandholdprivate.h b/gtk/gtkpressandholdprivate.h
new file mode 100644
index 0000000..ccccca2
--- /dev/null
+++ b/gtk/gtkpressandholdprivate.h
@@ -0,0 +1,68 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * 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/>.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_PRESS_AND_HOLD_H__
+#define __GTK_PRESS_AND_HOLD_H__
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRESS_AND_HOLD                  (gtk_press_and_hold_get_type ())
+#define GTK_PRESS_AND_HOLD(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRESS_AND_HOLD, GtkPressAndHold))
+#define GTK_PRESS_AND_HOLD_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRESS_AND_HOLD, GtkPressAndHoldClass))
+#define GTK_IS_PRESS_AND_HOLD(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRESS_AND_HOLD))
+#define GTK_IS_PRESS_AND_HOLD_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRESS_AND_HOLD))
+#define GTK_PRESS_AND_HOLD_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRESS_AND_HOLD, GtkPressAndHoldClass))
+
+
+typedef struct _GtkPressAndHold        GtkPressAndHold;
+typedef struct _GtkPressAndHoldClass   GtkPressAndHoldClass;
+typedef struct _GtkPressAndHoldPrivate GtkPressAndHoldPrivate;
+
+struct _GtkPressAndHold
+{
+  GObject parent;
+
+  /*< private >*/
+  GtkPressAndHoldPrivate *priv;
+};
+
+struct _GtkPressAndHoldClass
+{
+  GObjectClass parent_class;
+
+  void ( * hold) (GtkPressAndHold *pah, gint x, gint y);
+  void ( * tap)  (GtkPressAndHold *pah, gint x, gint y);
+};
+
+
+G_GNUC_INTERNAL
+GType             gtk_press_and_hold_get_type      (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+GtkPressAndHold * gtk_press_and_hold_new           (void);
+
+G_GNUC_INTERNAL
+void              gtk_press_and_hold_process_event (GtkPressAndHold *pah,
+                                                    GdkEvent        *event);
+
+G_END_DECLS
+
+#endif /* __GTK_PRESS_AND_HOLD_H__ */



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