[gtk+/wip/events: 9/16] gtk: Add event trackers to event handling code



commit f5f5532ca7b04c39b77518223e46311d540693b3
Author: Benjamin Otte <otte redhat com>
Date:   Tue Mar 6 23:35:54 2012 +0100

    gtk: Add event trackers to event handling code
    
    Event trackers run before any widget event handling functions. Event
    recognizers run per-widget before any widget event handlers.

 gtk/Makefile.am                 |    2 +
 gtk/gtkmain.c                   |   16 +++
 gtk/gtksequencetracker.c        |  259 +++++++++++++++++++++++++++++++++++++++
 gtk/gtksequencetrackerprivate.h |   52 ++++++++
 gtk/gtkwidget.c                 |   27 ++++
 5 files changed, 356 insertions(+), 0 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 8ee0a5f..e36267a 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -488,6 +488,7 @@ gtk_private_h_sources =		\
 	gtksearchengine.h	\
 	gtksearchenginesimple.h	\
 	gtkselectionprivate.h	\
+	gtksequencetrackerprivate.h \
 	gtksettingsprivate.h	\
 	gtkshadowprivate.h      \
 	gtksizegroup-private.h	\
@@ -749,6 +750,7 @@ gtk_base_c_sources = 		\
 	gtkseparator.c		\
 	gtkseparatormenuitem.c	\
 	gtkseparatortoolitem.c	\
+	gtksequencetracker.c	\
 	gtksettings.c		\
 	gtksizegroup.c		\
 	gtksizerequest.c	\
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index 65df934..847c4d4 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -114,6 +114,7 @@
 #include "gtkclipboard.h"
 #include "gtkdebug.h"
 #include "gtkdnd.h"
+#include "gtkeventtrackerprivate.h"
 #include "gtkmain.h"
 #include "gtkmenu.h"
 #include "gtkmodules.h"
@@ -1647,6 +1648,9 @@ gtk_main_do_event (GdkEvent *event)
     case GDK_WINDOW_STATE:
     case GDK_GRAB_BROKEN:
     case GDK_DAMAGE:
+      if (_gtk_event_trackers_invoke (event))
+        break;
+
       if (!_gtk_widget_captured_event (event_widget, event))
         gtk_widget_event (event_widget, event);
       break;
@@ -1656,6 +1660,9 @@ gtk_main_do_event (GdkEvent *event)
     case GDK_2BUTTON_PRESS:
     case GDK_3BUTTON_PRESS:
     case GDK_TOUCH_BEGIN:
+      if (_gtk_event_trackers_invoke (event))
+        break;
+
       if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
         gtk_propagate_event (grab_widget, event);
       break;
@@ -1709,11 +1716,17 @@ gtk_main_do_event (GdkEvent *event)
     case GDK_TOUCH_UPDATE:
     case GDK_TOUCH_END:
     case GDK_TOUCH_CANCEL:
+      if (_gtk_event_trackers_invoke (event))
+        break;
+
       if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
         gtk_propagate_event (grab_widget, event);
       break;
 
     case GDK_ENTER_NOTIFY:
+      if (_gtk_event_trackers_invoke (event))
+        break;
+
       if (event->crossing.detail != GDK_NOTIFY_VIRTUAL &&
           event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL)
         _gtk_widget_set_device_window (event_widget,
@@ -1725,6 +1738,9 @@ gtk_main_do_event (GdkEvent *event)
       break;
 
     case GDK_LEAVE_NOTIFY:
+      if (_gtk_event_trackers_invoke (event))
+        break;
+
       if (event->crossing.detail != GDK_NOTIFY_VIRTUAL &&
           event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL)
         _gtk_widget_set_device_window (event_widget,
diff --git a/gtk/gtksequencetracker.c b/gtk/gtksequencetracker.c
new file mode 100644
index 0000000..bfc5f64
--- /dev/null
+++ b/gtk/gtksequencetracker.c
@@ -0,0 +1,259 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2012 Benjamin Otte <otte gnome org>
+ *
+ * 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 "gtksequencetrackerprivate.h"
+
+#include <math.h>
+
+#define GTK_SEQUENCE_TRACKER_HISTORY_SIZE 8
+
+typedef struct _GtkSequenceTrackerClass GtkSequenceTrackerClass;
+struct _GtkSequenceTrackerClass {
+  gboolean      (* update)              (GtkSequenceTracker *tracker,
+                                         GdkEvent           *event,
+                                         double             *x,
+                                         double             *y);
+};
+
+struct _GtkSequenceTracker {
+  GtkSequenceTrackerClass *klass;
+
+  struct {
+    guint                  time;
+    GtkMovementDirection   dir;
+    double                 x;           /* x position in pixels - will be compared to start_x */
+    double                 y;           /* y position in pixels - will be compared to start_y */
+  }                        history[GTK_SEQUENCE_TRACKER_HISTORY_SIZE];
+  guint                    history_index; /* current item */
+
+  GdkEventSequence        *sequence;    /* sequence we're tracking */
+  double                   start_x;     /* NOT screen location, but in device coordinates */
+  double                   start_y;     /* NOT screen location, but in device coordinates */
+};
+
+/* MOUSE */
+
+/* FIXME */
+
+/* TOUCHSCREEN */
+
+/* FIXME */
+
+/* TOUCHPAD */
+
+static gboolean
+gtk_sequence_tracker_touchpad_update (GtkSequenceTracker *tracker,
+                                      GdkEvent           *event,
+                                      double             *x,
+                                      double             *y)
+{
+  GdkDevice *device = gdk_event_get_device (event);
+
+  switch (event->type)
+  {
+    case GDK_TOUCH_BEGIN:
+      tracker->sequence = gdk_event_get_event_sequence (event);
+      /* fall through */
+    case GDK_TOUCH_UPDATE:
+    case GDK_TOUCH_END:
+      if (tracker->sequence != gdk_event_get_event_sequence (event))
+        return FALSE;
+
+      if (gdk_device_get_axis (device,
+                               event->touch.axes,
+                               GDK_AXIS_X_RELATIVE,
+                               x))
+        {
+          *x *= gdk_screen_get_width (gdk_display_get_default_screen (gdk_device_get_display (device)));
+        }
+      else
+        {
+          g_warning ("Could not query X value");
+          *x = 0;
+        }
+
+      if (gdk_device_get_axis (device,
+                               event->touch.axes,
+                               GDK_AXIS_Y_RELATIVE,
+                               y))
+        {
+          *y *= gdk_screen_get_height (gdk_display_get_default_screen (gdk_device_get_display (device)));
+        }
+      else
+        {
+          g_warning ("Could not query Y value");
+          *y = 0;
+        }
+
+      return TRUE;
+    default:
+      return FALSE;
+  }
+}
+
+GtkSequenceTrackerClass GTK_SEQUENCE_TRACKER_CLASS_TOUCHPAD = {
+  gtk_sequence_tracker_touchpad_update
+};
+
+/* API */
+
+GtkSequenceTracker *
+_gtk_sequence_tracker_new (GdkEvent *event)
+{
+  GtkSequenceTrackerClass *klass;
+  GtkSequenceTracker *tracker;
+  guint i, time;
+
+  switch (event->type)
+    {
+      case GDK_TOUCH_BEGIN:
+        if (gdk_device_get_source (gdk_event_get_source_device (event)) == GDK_SOURCE_TOUCHPAD)
+          klass = &GTK_SEQUENCE_TRACKER_CLASS_TOUCHPAD;
+        else
+          return NULL;
+        break;
+      default:
+        return NULL;
+    }
+
+  tracker = g_slice_new0 (GtkSequenceTracker);
+  tracker->klass = klass;
+
+  if (!tracker->klass->update (tracker, event, &tracker->start_x, &tracker->start_y))
+    {
+      g_assert_not_reached ();
+      return NULL;
+    }
+  time = gdk_event_get_time (event);
+
+  for (i = 0; i < GTK_SEQUENCE_TRACKER_HISTORY_SIZE; i++)
+    {
+      tracker->history[i].time = time;
+      tracker->history[i].x = tracker->start_x;
+      tracker->history[i].y = tracker->start_y;
+      tracker->history[i].dir = GTK_DIR_ANY;
+    }
+  tracker->history_index = 0;
+
+  return tracker;
+}
+
+void
+_gtk_sequence_tracker_free (GtkSequenceTracker *tracker)
+{
+  g_return_if_fail (tracker != NULL);
+
+  g_slice_free (GtkSequenceTracker, tracker);
+}
+
+static GtkMovementDirection
+gtk_sequence_tracker_compute_direction (double dx,
+                                        double dy)
+{
+  double r;
+  int i1, i2;
+
+  if (fabs (dx) < 2 && fabs (dy) < 2)
+    {
+      GtkMovementDirection dir = GTK_DIR_ANY;
+
+      if (dx <= 1)
+        dir &= GTK_DIR_SOUTH | GTK_DIR_SOUTH_WEST | GTK_DIR_WEST | GTK_DIR_NORTH_WEST | GTK_DIR_NORTH;
+      else if (dx >= 1)
+        dir &= GTK_DIR_SOUTH | GTK_DIR_SOUTH_EAST | GTK_DIR_EAST | GTK_DIR_NORTH_EAST | GTK_DIR_NORTH;
+
+      if (dy <= 1)
+        dir &= GTK_DIR_WEST | GTK_DIR_NORTH_WEST | GTK_DIR_NORTH | GTK_DIR_NORTH_EAST | GTK_DIR_EAST;
+      else
+        dir &= GTK_DIR_WEST | GTK_DIR_SOUTH_WEST | GTK_DIR_SOUTH | GTK_DIR_SOUTH_EAST | GTK_DIR_EAST;
+      
+      return dir;
+  }
+  
+  r = atan2(dy, dx);
+
+  /* Add 360Â to avoid r become negative since C has no well-defined
+   * modulo for such cases. */
+  r += 2 * G_PI;
+  
+  /* Divide by 45Â to get the octant number,  e.g.
+   *          0 <= r <= 1 is [0-45]Â
+   *          1 <= r <= 2 is [45-90]Â
+   *          etc. */
+  r /= G_PI / 4;
+
+  /* This intends to flag 2 directions (45 degrees),
+   * except on very well-aligned coordinates. */
+  i1 = (int) (r + 0.1) % 8;
+  i2 = (int) (r + 0.9) % 8;
+  if (i1 < 0 || i1 > 7 || i2 < 0 || i2 > 7)
+    return GTK_DIR_ANY;
+  
+  return (1 << i1 | 1 << i2);
+}
+
+gboolean
+_gtk_sequence_tracker_update (GtkSequenceTracker *tracker,
+                              GdkEvent           *event)
+{
+  double x, y, dx, dy;
+
+  g_return_val_if_fail (tracker != NULL, FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  if (!tracker->klass->update (tracker, event, &x, &y))
+    return FALSE;
+
+  dx = x - tracker->history[tracker->history_index].x;
+  dy = y - tracker->history[tracker->history_index].y;
+  
+  tracker->history_index = (tracker->history_index + 1) % GTK_SEQUENCE_TRACKER_HISTORY_SIZE;
+  tracker->history[tracker->history_index].time = gdk_event_get_time (event);
+  tracker->history[tracker->history_index].x = x;
+  tracker->history[tracker->history_index].y = y;
+  tracker->history[tracker->history_index].dir = gtk_sequence_tracker_compute_direction (dx, dy);
+
+  return TRUE;
+}
+
+double
+_gtk_sequence_tracker_get_x_offset (GtkSequenceTracker *tracker)
+{
+  g_return_val_if_fail (tracker != NULL, 0);
+
+  return tracker->history[tracker->history_index].x
+    - tracker->start_x;
+}
+
+double
+_gtk_sequence_tracker_get_y_offset (GtkSequenceTracker *tracker)
+{
+  g_return_val_if_fail (tracker != NULL, 0);
+
+  return tracker->history[tracker->history_index].y
+    - tracker->start_y;
+}
+
+GtkMovementDirection
+_gtk_sequence_tracker_get_direction (GtkSequenceTracker *tracker)
+{
+  g_return_val_if_fail (tracker != NULL, GTK_DIR_ANY);
+
+  return tracker->history[tracker->history_index].dir;
+}
diff --git a/gtk/gtksequencetrackerprivate.h b/gtk/gtksequencetrackerprivate.h
new file mode 100644
index 0000000..5ad632f
--- /dev/null
+++ b/gtk/gtksequencetrackerprivate.h
@@ -0,0 +1,52 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2012 Benjamin Otte <otte gnome org>
+ *
+ * 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/>.
+ */
+
+#ifndef __GTK_SEQUENCE_TRACKER_PRIVATE_H__
+#define __GTK_SEQUENCE_TRACKER_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  GTK_DIR_EAST       = (1 << 0),
+  GTK_DIR_SOUTH_EAST = (1 << 1),
+  GTK_DIR_SOUTH      = (1 << 2),
+  GTK_DIR_SOUTH_WEST = (1 << 3),
+  GTK_DIR_WEST       = (1 << 4),
+  GTK_DIR_NORTH_WEST = (1 << 5),
+  GTK_DIR_NORTH      = (1 << 6),
+  GTK_DIR_NORTH_EAST = (1 << 7),
+  GTK_DIR_ANY        = (1 << 8) - 1,
+} GtkMovementDirection;
+
+typedef struct _GtkSequenceTracker GtkSequenceTracker;
+
+GtkSequenceTracker *  _gtk_sequence_tracker_new                 (GdkEvent           *event);
+void                  _gtk_sequence_tracker_free                (GtkSequenceTracker *tracker);
+
+gboolean              _gtk_sequence_tracker_update              (GtkSequenceTracker *tracker,
+                                                                 GdkEvent           *event);
+
+double                _gtk_sequence_tracker_get_x_offset        (GtkSequenceTracker *tracker);
+double                _gtk_sequence_tracker_get_y_offset        (GtkSequenceTracker *tracker);
+
+GtkMovementDirection  _gtk_sequence_tracker_get_direction       (GtkSequenceTracker *tracker);
+
+G_END_DECLS
+
+#endif /* __GTK_SEQUENCE_TRACKER_PRIVATE_H__ */
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 0beea08..99a58de 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -6282,6 +6282,31 @@ event_window_is_still_viewable (GdkEvent *event)
     }
 }
 
+static gboolean
+gtk_widget_invoke_recognizers (GtkWidget *widget,
+                               GdkEvent  *event)
+{
+  GtkWidgetClass *klass = GTK_WIDGET_GET_CLASS (widget);
+  GtkWidgetClassPrivate *priv = klass->priv;
+  guint i;
+  gboolean eat_event = FALSE;
+
+  if (priv->recognizers == NULL)
+    return FALSE;
+
+  g_object_ref (widget);
+
+  for (i = 0; i < priv->recognizers->len; i++)
+    {
+      GtkEventRecognizer *recognizer = g_ptr_array_index (priv->recognizers, i);
+      _gtk_event_recognizer_recognize (recognizer, widget, event);
+    }
+
+  g_object_unref (widget);
+
+  return eat_event;
+}
+
 static gint
 gtk_widget_event_internal (GtkWidget *widget,
 			   GdkEvent  *event)
@@ -6296,6 +6321,8 @@ gtk_widget_event_internal (GtkWidget *widget,
   if (!event_window_is_still_viewable (event))
     return TRUE;
 
+  gtk_widget_invoke_recognizers (widget, event);
+
   g_object_ref (widget);
 
   g_signal_emit (widget, widget_signals[EVENT], 0, event, &return_val);



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