[gtk+/gestures] switch: Use GtkGesture to handle input events



commit 14f94e73945d0e42e9e8371233299ee2a4e62e9f
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon May 19 16:08:56 2014 +0200

    switch: Use GtkGesture to handle input events
    
    A pan gesture is used to handle switch dragging, which is only triggered
    by horizontal panning movements. A multipress gesture handles the cases
    where clicking without dragging happens, just toggling the switch.

 gtk/gtkswitch.c |  255 +++++++++++++++++++++++++-----------------------------
 1 files changed, 118 insertions(+), 137 deletions(-)
---
diff --git a/gtk/gtkswitch.c b/gtk/gtkswitch.c
index e038192..3330ea9 100644
--- a/gtk/gtkswitch.c
+++ b/gtk/gtkswitch.c
@@ -62,6 +62,9 @@ struct _GtkSwitchPrivate
   GtkAction *action;
   GtkActionHelper *action_helper;
 
+  GtkGesture *pan_gesture;
+  GtkGesture *multipress_gesture;
+
   gint handle_x;
   gint offset;
   gint drag_start;
@@ -69,8 +72,6 @@ struct _GtkSwitchPrivate
 
   guint state                 : 1;
   guint is_active             : 1;
-  guint is_dragging           : 1;
-  guint in_press              : 1;
   guint in_switch             : 1;
   guint use_action_appearance : 1;
 };
@@ -110,171 +111,131 @@ G_DEFINE_TYPE_WITH_CODE (GtkSwitch, gtk_switch, GTK_TYPE_WIDGET,
                                                 gtk_switch_activatable_interface_init));
 G_GNUC_END_IGNORE_DEPRECATIONS;
 
-static gboolean
-gtk_switch_button_press (GtkWidget      *widget,
-                         GdkEventButton *event)
+static void
+gtk_switch_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
+                                       gint                  n_press,
+                                       gdouble               x,
+                                       gdouble               y,
+                                       GtkSwitch            *sw)
 {
-  GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
+  GtkSwitchPrivate *priv = sw->priv;
   GtkAllocation allocation;
 
-  /* Don't handle extra mouse buttons events, let them bubble up */
-  if (event->button > 5)
-    return GDK_EVENT_PROPAGATE;
-
-  gtk_widget_get_allocation (widget, &allocation);
+  gtk_widget_get_allocation (GTK_WIDGET (sw), &allocation);
+  gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
 
   if (priv->is_active)
-    {
-      /* if the event occurred in the "off" area, then this
-       * is a direct toggle
-       */
-      if (event->x <= allocation.width / 2)
-        {
-          priv->in_press = TRUE;
-          return GDK_EVENT_STOP;
-        }
-
-      priv->offset = event->x - allocation.width / 2;
-    }
+    priv->offset = allocation.width / 2;
   else
-    {
-      /* if the event occurred in the "on" area, then this
-       * is a direct toggle
-       */
-      if (event->x >= allocation.width / 2)
-        {
-          priv->in_press = TRUE;
-          return GDK_EVENT_STOP;
-        }
-
-      priv->offset = event->x;
-    }
-
-  priv->drag_start = event->x;
-
-  g_object_get (gtk_widget_get_settings (widget),
-                "gtk-dnd-drag-threshold", &priv->drag_threshold,
-                NULL);
+    priv->offset = 0;
 
-  return GDK_EVENT_STOP;
+  /* If the press didn't happen in the draggable handle,
+   * cancel the pan gesture right away
+   */
+  if ((priv->is_active && x <= allocation.width / 2) ||
+      (!priv->is_active && x > allocation.width / 2))
+    gtk_gesture_set_state (priv->pan_gesture, GTK_EVENT_SEQUENCE_DENIED);
 }
 
-static gboolean
-gtk_switch_motion (GtkWidget      *widget,
-                   GdkEventMotion *event)
+static void
+gtk_switch_multipress_gesture_released (GtkGestureMultiPress *gesture,
+                                        gint                  n_press,
+                                        gdouble               x,
+                                        gdouble               y,
+                                        GtkSwitch            *sw)
 {
-  GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
-
-  /* if this is a direct toggle we don't handle motion */
-  if (priv->in_press)
-    return GDK_EVENT_PROPAGATE;
-
-  if (ABS (event->x - priv->drag_start) < priv->drag_threshold)
-    return GDK_EVENT_STOP;
-
-  if (event->state & GDK_BUTTON1_MASK)
-    {
-      gint position = event->x - priv->offset;
-      GtkAllocation allocation;
-      GtkStyleContext *context;
-      GtkStateFlags state;
-      GtkBorder padding;
-      gint width;
-
-      context = gtk_widget_get_style_context (widget);
-      state = gtk_widget_get_state_flags (widget);
-
-      gtk_style_context_save (context);
-      gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
-      gtk_style_context_get_padding (context, state, &padding);
-      gtk_style_context_restore (context);
-      
-      gtk_widget_get_allocation (widget, &allocation);
-
-      width = allocation.width;
-
-      /* constrain the handle within the trough width */
-      if (position > (width / 2) - padding.right)
-        priv->handle_x = width / 2 - padding.right;
-      else if (position < padding.left)
-        priv->handle_x = 0;
-      else
-        priv->handle_x = position;
-
-      priv->is_dragging = TRUE;
+  GtkSwitchPrivate *priv = sw->priv;
+  GdkEventSequence *sequence;
+  GtkAllocation allocation;
 
-      /* we need to redraw the handle */
-      gtk_widget_queue_draw (widget);
+  gtk_widget_get_allocation (GTK_WIDGET (sw), &allocation);
 
-      return GDK_EVENT_STOP;
-    }
+  sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
 
-  return GDK_EVENT_PROPAGATE;
+  if (priv->in_switch &&
+      gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
+    gtk_switch_set_active (sw, !priv->is_active);
 }
 
-static gboolean
-gtk_switch_button_release (GtkWidget      *widget,
-                           GdkEventButton *event)
+static void
+gtk_switch_pan_gesture_pan (GtkGesturePan   *gesture,
+                            GtkPanDirection  direction,
+                            gdouble          offset,
+                            GtkSwitch       *sw)
 {
-  GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
+  GtkWidget *widget = GTK_WIDGET (sw);
+  GtkSwitchPrivate *priv = sw->priv;
   GtkAllocation allocation;
+  GtkStyleContext *context;
+  GtkStateFlags state;
+  GtkBorder padding;
+  gint width, position;
 
-  /* Don't handle extra mouse buttons events, let them bubble up */
-  if (event->button > 5)
-    return GDK_EVENT_PROPAGATE;
+  if (direction == GTK_PAN_DIRECTION_LEFT)
+    offset = -offset;
+
+  gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+  position = priv->offset + offset;
+
+  context = gtk_widget_get_style_context (widget);
+  state = gtk_widget_get_state_flags (widget);
+
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
+  gtk_style_context_get_padding (context, state, &padding);
+  gtk_style_context_restore (context);
 
   gtk_widget_get_allocation (widget, &allocation);
 
-  /* dragged toggles have a "soft" grab, so we don't care whether we
-   * are in the switch or not when the button is released; we do care
-   * for direct toggles, instead
-   */
-  if (!priv->is_dragging && !priv->in_switch)
-    return GDK_EVENT_PROPAGATE;
+  width = allocation.width;
 
-  /* direct toggle */
-  if (priv->in_press)
-    {
-      priv->in_press = FALSE;
-      gtk_switch_set_active (GTK_SWITCH (widget), !priv->is_active);
+  /* constrain the handle within the trough width */
+  if (position > (width / 2) - padding.right)
+    priv->handle_x = width / 2 - padding.right;
+  else if (position < padding.left)
+    priv->handle_x = 0;
+  else
+    priv->handle_x = position;
 
-      return GDK_EVENT_STOP;
-    }
+  /* we need to redraw the handle */
+  gtk_widget_queue_draw (widget);
+}
 
-  /* toggle the switch if the handle was clicked but a drag had not been
-   * initiated */
-  if (!priv->is_dragging && !priv->in_press)
-    {
-      gtk_switch_set_active (GTK_SWITCH (widget), !priv->is_active);
+static void
+gtk_switch_pan_gesture_drag_end (GtkGestureDrag *gesture,
+                                 gdouble         x,
+                                 gdouble         y,
+                                 GtkSwitch      *sw)
+{
+  GtkSwitchPrivate *priv = sw->priv;
+  GdkEventSequence *sequence;
+  GtkAllocation allocation;
+  gboolean active = FALSE;
 
-      return GDK_EVENT_STOP;
-    }
+  sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
 
-  /* dragged toggle */
-  if (priv->is_dragging)
+  if (gtk_gesture_get_sequence_state (GTK_GESTURE (gesture), sequence) == GTK_EVENT_SEQUENCE_CLAIMED)
     {
-      priv->is_dragging = FALSE;
+      gtk_widget_get_allocation (GTK_WIDGET (sw), &allocation);
 
       /* if half the handle passed the middle of the switch, then we
        * consider it to be on
        */
       if ((priv->handle_x + (allocation.width / 4)) >= (allocation.width / 2))
-        {
-          gtk_switch_set_active (GTK_SWITCH (widget), TRUE);
-          priv->handle_x = allocation.width / 2;
-        }
-      else
-        {
-          gtk_switch_set_active (GTK_SWITCH (widget), FALSE);
-          priv->handle_x = 0;
-        }
-
-      gtk_widget_queue_draw (widget);
-
-      return GDK_EVENT_STOP;
+        active = TRUE;
     }
+  else if (!gtk_gesture_handles_sequence (priv->multipress_gesture, sequence))
+    active = priv->is_active;
+  else
+    return;
+
+  if (active)
+    priv->handle_x = allocation.width / 2;
+  else
+    priv->handle_x = 0;
 
-  return GDK_EVENT_PROPAGATE;
+  gtk_switch_set_active (sw, active);
+  gtk_widget_queue_draw (GTK_WIDGET (sw));
 }
 
 static gboolean
@@ -593,7 +554,7 @@ gtk_switch_draw (GtkWidget *widget,
 
   g_object_unref (layout);
 
-  if (priv->is_dragging)
+  if (gtk_gesture_is_recognized (priv->pan_gesture))
     handle.x = x + priv->handle_x;
   else if (priv->is_active)
     handle.x = x + width - handle.width;
@@ -869,9 +830,6 @@ gtk_switch_class_init (GtkSwitchClass *klass)
   widget_class->map = gtk_switch_map;
   widget_class->unmap = gtk_switch_unmap;
   widget_class->draw = gtk_switch_draw;
-  widget_class->button_press_event = gtk_switch_button_press;
-  widget_class->button_release_event = gtk_switch_button_release;
-  widget_class->motion_notify_event = gtk_switch_motion;
   widget_class->enter_notify_event = gtk_switch_enter;
   widget_class->leave_notify_event = gtk_switch_leave;
 
@@ -952,10 +910,33 @@ gtk_switch_class_init (GtkSwitchClass *klass)
 static void
 gtk_switch_init (GtkSwitch *self)
 {
+  GtkGesture *gesture;
+
   self->priv = gtk_switch_get_instance_private (self);
   self->priv->use_action_appearance = TRUE;
   gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
   gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
+
+  gesture = gtk_gesture_multi_press_new (GTK_WIDGET (self));
+  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
+  gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (gesture), TRUE);
+  g_signal_connect (gesture, "pressed",
+                    G_CALLBACK (gtk_switch_multipress_gesture_pressed), self);
+  g_signal_connect (gesture, "released",
+                    G_CALLBACK (gtk_switch_multipress_gesture_released), self);
+  gtk_gesture_attach (gesture, GTK_PHASE_TARGET);
+  self->priv->multipress_gesture = gesture;
+
+  gesture = gtk_gesture_pan_new (GTK_WIDGET (self),
+                                 GTK_PAN_ORIENTATION_HORIZONTAL);
+  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
+  gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (gesture), TRUE);
+  g_signal_connect (gesture, "pan",
+                    G_CALLBACK (gtk_switch_pan_gesture_pan), self);
+  g_signal_connect (gesture, "drag-end",
+                    G_CALLBACK (gtk_switch_pan_gesture_drag_end), self);
+  gtk_gesture_attach (gesture, GTK_PHASE_TARGET);
+  self->priv->pan_gesture = gesture;
 }
 
 /**


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