[gtk+/gestures: 48/117] widget: Improve button press emulation on sequence denied



commit 92f7d0166eb0b01106f026631fd830ead1e7cdc8
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Mar 26 10:25:09 2014 +0100

    widget: Improve button press emulation on sequence denied
    
    Ensure that state being set on pointer emulating touches actually
    gets propagated properly on widgets with gestures that only handle
    pointer events.

 gtk/gtkgesture.c        |   37 ++++++++++++++
 gtk/gtkgestureprivate.h |    4 ++
 gtk/gtkwidget.c         |  126 ++++++++++++++++++++++++++++++++++++++--------
 3 files changed, 145 insertions(+), 22 deletions(-)
---
diff --git a/gtk/gtkgesture.c b/gtk/gtkgesture.c
index 0f0fb9a..c6bd658 100644
--- a/gtk/gtkgesture.c
+++ b/gtk/gtkgesture.c
@@ -1161,3 +1161,40 @@ _gtk_gesture_handled_sequence_press (GtkGesture       *gesture,
 
   return data->press_handled;
 }
+
+gboolean
+_gtk_gesture_get_pointer_emulating_sequence (GtkGesture        *gesture,
+                                             GdkEventSequence **sequence)
+{
+  GtkGesturePrivate *priv;
+  GdkEventSequence *seq;
+  GHashTableIter iter;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  g_hash_table_iter_init (&iter, priv->points);
+
+  while (g_hash_table_iter_next (&iter, (gpointer*) &seq, (gpointer*) &data))
+    {
+      switch (data->event->type)
+        {
+        case GDK_TOUCH_BEGIN:
+        case GDK_TOUCH_UPDATE:
+        case GDK_TOUCH_END:
+          if (!data->event->touch.emulating_pointer)
+            continue;
+          /* Fall through */
+        case GDK_BUTTON_PRESS:
+        case GDK_BUTTON_RELEASE:
+        case GDK_MOTION_NOTIFY:
+          *sequence = seq;
+          return TRUE;
+        default:
+          break;
+        }
+    }
+
+  return FALSE;
+}
diff --git a/gtk/gtkgestureprivate.h b/gtk/gtkgestureprivate.h
index d60eaf1..ae4a9c4 100644
--- a/gtk/gtkgestureprivate.h
+++ b/gtk/gtkgestureprivate.h
@@ -27,6 +27,10 @@ G_BEGIN_DECLS
 gboolean _gtk_gesture_handled_sequence_press (GtkGesture       *gesture,
                                               GdkEventSequence *sequence);
 
+gboolean _gtk_gesture_get_pointer_emulating_sequence
+                                             (GtkGesture        *gesture,
+                                              GdkEventSequence **sequence);
+
 G_END_DECLS
 
 #endif /* __GTK_GESTURE_PRIVATE_H__ */
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 6da5e38..7c8c28b 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -4086,26 +4086,60 @@ _gtk_widget_emulate_press (GtkWidget      *widget,
   gdk_event_free (press);
 }
 
+static const GdkEvent *
+_gtk_widget_get_last_event (GtkWidget        *widget,
+                            GdkEventSequence *sequence)
+{
+  GtkWidgetPrivate *priv = widget->priv;
+  EventControllerData *data;
+  const GdkEvent *event;
+  GList *l;
+
+  for (l = priv->event_controllers; l; l = l->next)
+    {
+      data = l->data;
+      event = gtk_gesture_get_last_event (data->controller, sequence);
+      if (event)
+        return event;
+    }
+
+  return NULL;
+}
+
 static gboolean
 _gtk_widget_set_sequence_state_internal (GtkWidget             *widget,
                                          GdkEventSequence      *sequence,
-                                         GtkEventSequenceState  state)
+                                         GtkEventSequenceState  state,
+                                         gboolean               emulates_pointer)
 {
+  gboolean retval, sequence_handled, handled = FALSE;
   GtkWidgetPrivate *priv = widget->priv;
-  const GdkEvent *mimic_event = NULL;
-  gboolean retval, handled = FALSE;
+  const GdkEvent *mimic_event;
+  gboolean send_event = FALSE;
   EventControllerData *data;
+  GdkEventSequence *seq;
   GList *l;
 
+  if (!priv->event_controllers && state != GTK_EVENT_SEQUENCE_CLAIMED)
+    return TRUE;
+
+  mimic_event = _gtk_widget_get_last_event (widget, sequence);
+
   for (l = priv->event_controllers; l; l = l->next)
     {
+      seq = sequence;
       data = l->data;
 
-      if (!GTK_IS_GESTURE (data->controller))
+      if (seq && emulates_pointer &&
+          !gtk_gesture_handles_sequence (data->controller, seq))
+        seq = NULL;
+
+      if (!gtk_gesture_handles_sequence (data->controller, seq))
         continue;
 
-      retval = gtk_gesture_set_sequence_state (GTK_GESTURE (data->controller),
-                                               sequence, state);
+      sequence_handled =
+        _gtk_gesture_handled_sequence_press (data->controller, seq);
+      retval = gtk_gesture_set_sequence_state (data->controller, seq, state);
       handled |= retval;
 
       /* If the sequence goes denied, check whether this is a controller attached
@@ -4113,56 +4147,103 @@ _gtk_widget_set_sequence_state_internal (GtkWidget             *widget,
        * 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 &&
+      if (retval && sequence_handled &&
           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);
+          state == GTK_EVENT_SEQUENCE_DENIED)
+        send_event = TRUE;
     }
 
-  if (mimic_event)
+  if (send_event && mimic_event)
     _gtk_widget_emulate_press (widget, mimic_event);
 
   return handled;
 }
 
 static gboolean
-_gtk_widget_cancel_sequence (GtkWidget *widget,
-                             GdkEventSequence *sequence)
+_gtk_widget_cancel_sequence (GtkWidget        *widget,
+                             GdkEventSequence *sequence,
+                             gboolean          emulates_pointer)
 {
   GtkWidgetPrivate *priv = widget->priv;
   EventControllerData *data;
   gboolean handled = FALSE;
+  GdkEventSequence *seq;
   GList *l;
 
   for (l = priv->event_controllers; l; l = l->next)
     {
+      seq = sequence;
       data = l->data;
 
-      if (!GTK_IS_GESTURE (data->controller))
+      if (seq && emulates_pointer &&
+          !gtk_gesture_handles_sequence (data->controller, seq))
+        seq = NULL;
+
+      if (!gtk_gesture_handles_sequence (data->controller, seq))
         continue;
 
-      handled |= gtk_gesture_cancel_sequence (GTK_GESTURE (data->controller),
-                                              sequence);
+      handled |=
+        gtk_gesture_cancel_sequence (data->controller, seq);
     }
 
   return handled;
 }
 
 static gboolean
+_gtk_widget_get_emulating_sequence (GtkWidget         *widget,
+                                    GdkEventSequence **sequence)
+{
+  GtkWidgetPrivate *priv = widget->priv;
+  EventControllerData *data;
+  GList *l;
+
+  for (l = priv->event_controllers; l; l = l->next)
+    {
+      data = l->data;
+
+      if (_gtk_gesture_get_pointer_emulating_sequence (data->controller,
+                                                       sequence))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
 gtk_widget_real_sequence_state_changed (GtkWidget             *widget,
                                         GtkWidget             *changed_widget,
                                         GdkEventSequence      *sequence,
                                         GtkEventSequenceState  state)
 {
   GtkEventSequenceState changed_state;
+  gboolean emulates_pointer = FALSE;
+  const GdkEvent *last_event;
+
+  if (!sequence)
+    {
+      if (!_gtk_widget_get_emulating_sequence (widget, &sequence))
+        return FALSE;
+    }
+  else
+    {
+      last_event = _gtk_widget_get_last_event (changed_widget, sequence);
+
+      if (!last_event)
+        return FALSE;
+
+      if ((last_event->type == GDK_TOUCH_BEGIN ||
+           last_event->type == GDK_TOUCH_UPDATE ||
+           last_event->type == GDK_TOUCH_END) &&
+          last_event->touch.emulating_pointer)
+        emulates_pointer = TRUE;
+    }
 
   if (widget == changed_widget)
-    return _gtk_widget_set_sequence_state_internal (widget, sequence, state);
-  else if (gtk_widget_is_ancestor (widget, changed_widget))
-    return _gtk_widget_cancel_sequence (widget, sequence);
+    return _gtk_widget_set_sequence_state_internal (widget, sequence,
+                                                    state, emulates_pointer);
+  else if (state == GTK_EVENT_SEQUENCE_CLAIMED &&
+           gtk_widget_is_ancestor (widget, changed_widget))
+    return _gtk_widget_cancel_sequence (widget, sequence, emulates_pointer);
   else
     {
       changed_state = gtk_widget_get_sequence_state (changed_widget, sequence);
@@ -4170,7 +4251,8 @@ gtk_widget_real_sequence_state_changed (GtkWidget             *widget,
       if (state == GTK_EVENT_SEQUENCE_CLAIMED &&
           changed_state == GTK_EVENT_SEQUENCE_CLAIMED)
         return _gtk_widget_set_sequence_state_internal (widget, sequence,
-                                                        GTK_EVENT_SEQUENCE_DENIED);
+                                                        GTK_EVENT_SEQUENCE_DENIED,
+                                                        emulates_pointer);
     }
 
   return FALSE;


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