[gtk+/gestures: 19/20] widget: Propagate touch/button press events when a sequence is denied



commit 0d5b26b4fa477ceeaec8c138411efe968dc5f679
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Mar 3 20:47:43 2014 +0100

    widget: Propagate touch/button press events when a sequence is denied
    
    If the captured touch begin or button press event have been consumed
    for the given sequence, propagate it upwards if the sequence goes from
    claimed to denied, so the widgets on the way to the event widget receive
    a coherent event stream now that they're going to receive events.

 gtk/gtkwidget.c |  100 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 97 insertions(+), 3 deletions(-)
---
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index f053614..07a4d02 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -65,6 +65,7 @@
 #include "gtktypebuiltins.h"
 #include "a11y/gtkwidgetaccessible.h"
 #include "gtkapplicationprivate.h"
+#include "gtkgestureprivate.h"
 
 /* for the use of round() */
 #include "fallback-c89.c"
@@ -3999,14 +4000,90 @@ gtk_widget_get_property (GObject         *object,
     }
 }
 
+static void
+_gtk_widget_emulate_press (GtkWidget      *widget,
+                           const GdkEvent *event)
+{
+  GtkWidget *event_widget, *next_child, *parent;
+  GdkEvent *press;
+
+  event_widget = gtk_get_event_widget ((GdkEvent *) event);
+
+  if (event_widget == widget)
+    return;
+
+  if (event->type == GDK_TOUCH_BEGIN ||
+      event->type == GDK_TOUCH_UPDATE ||
+      event->type == GDK_TOUCH_END)
+    {
+      press = gdk_event_copy (event);
+      press->type = GDK_TOUCH_BEGIN;
+    }
+  else if (event->type == GDK_BUTTON_PRESS ||
+           event->type == GDK_BUTTON_RELEASE)
+    {
+      press = gdk_event_copy (event);
+      press->type = GDK_BUTTON_PRESS;
+    }
+  else if (event->type == GDK_MOTION_NOTIFY)
+    {
+      press = gdk_event_new (GDK_BUTTON_PRESS);
+      press->button.window = g_object_ref (event->motion.window);
+      press->button.time = event->motion.time;
+      press->button.x = event->motion.x;
+      press->button.y = event->motion.y;
+      press->button.x_root = event->motion.x_root;
+      press->button.y_root = event->motion.y_root;
+      press->button.state = event->motion.state;
+
+      press->button.axes = g_memdup (event->motion.axes,
+                                     sizeof (gdouble) *
+                                     gdk_device_get_n_axes (event->motion.device));
+
+      if (event->motion.state & GDK_BUTTON3_MASK)
+        press->button.button = 3;
+      else if (event->motion.state & GDK_BUTTON2_MASK)
+        press->button.button = 2;
+      else
+        {
+          if ((event->motion.state & GDK_BUTTON1_MASK) == 0)
+            g_critical ("Guessing button number 1 on generated button press event");
+
+          press->button.button = 1;
+        }
+
+      gdk_event_set_device (press, gdk_event_get_device (event));
+      gdk_event_set_source_device (press, gdk_event_get_source_device (event));
+    }
+  else
+    return;
+
+  press->any.send_event = TRUE;
+  next_child = event_widget;
+  parent = gtk_widget_get_parent (next_child);
+
+  while (parent != widget)
+    {
+      next_child = parent;
+      parent = gtk_widget_get_parent (parent);
+    }
+
+  /* Perform propagation state starting from the next child in the chain */
+  if (!_gtk_propagate_captured_event (event_widget, press, next_child))
+    gtk_propagate_event (event_widget, press);
+
+  gdk_event_free (press);
+}
+
 static gboolean
 _gtk_widget_set_sequence_state_internal (GtkWidget             *widget,
                                          GdkEventSequence      *sequence,
                                          GtkEventSequenceState  state)
 {
   GtkWidgetPrivate *priv = widget->priv;
+  const GdkEvent *mimic_event = NULL;
+  gboolean retval, handled = FALSE;
   EventControllerData *data;
-  gboolean handled = FALSE;
   GList *l;
 
   for (l = priv->event_controllers; l; l = l->next)
@@ -4016,10 +4093,27 @@ _gtk_widget_set_sequence_state_internal (GtkWidget             *widget,
       if (!GTK_IS_GESTURE (data->controller))
         continue;
 
-      handled |= gtk_gesture_set_sequence_state (GTK_GESTURE (data->controller),
-                                                 sequence, state);
+      retval = gtk_gesture_set_sequence_state (GTK_GESTURE (data->controller),
+                                               sequence, state);
+      handled |= retval;
+
+      /* If the sequence goes denied, check whether this is a controller attached
+       * to the capture phase, that additionally handled the button/touch press (ie.
+       * it was consumed), the corresponding press will be emulated for widgets
+       * beneath, so the widgets beneath get a coherent stream of events from now on.
+       */
+      if (retval && !mimic_event &&
+          data->propagation_phase == GTK_PHASE_CAPTURE &&
+          state == GTK_EVENT_SEQUENCE_DENIED &&
+          _gtk_gesture_handled_sequence_press (GTK_GESTURE (data->controller),
+                                               sequence))
+        mimic_event = gtk_gesture_get_last_event (GTK_GESTURE (data->controller),
+                                                  sequence);
     }
 
+  if (mimic_event)
+    _gtk_widget_emulate_press (widget, mimic_event);
+
   return handled;
 }
 


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