[gtk+/gestures] paned: Use GtkGesture to handle handle dragging



commit 3b8b76be89d5567c6bc85fca68cf4ac018a53e64
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon May 19 19:58:54 2014 +0200

    paned: Use GtkGesture to handle handle dragging
    
    Dragging is all handled by a GtkGesturePan now, matching the
    paned orientation.
    
    On touch events, a wider area is listened for, so touch events
    don't need to be as accurate to initiate dragging, if no dragging
    is truly initiated in this case, events are just forwarded for
    child widgets to handle.

 gtk/gtkpaned.c |  287 ++++++++++++++++++++++++++------------------------------
 1 files changed, 134 insertions(+), 153 deletions(-)
---
diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c
index 6a0b6e5..fe28801 100644
--- a/gtk/gtkpaned.c
+++ b/gtk/gtkpaned.c
@@ -110,10 +110,11 @@ struct _GtkPanedPrivate
   GtkOrientation  orientation;
 
   GdkCursorType  cursor_type;
-  GdkDevice     *grab_device;
   GdkRectangle   handle_pos;
   GdkWindow     *handle;
 
+  GtkGesture    *pan_gesture;
+
   gint          child1_size;
   gint          drag_pos;
   gint          last_allocation;
@@ -121,16 +122,14 @@ struct _GtkPanedPrivate
   gint          min_position;
   gint          original_position;
 
-  guint32       grab_time;
-
   guint         handle_prelit : 1;
-  guint         in_drag       : 1;
   guint         in_recursion  : 1;
   guint         child1_resize : 1;
   guint         child1_shrink : 1;
   guint         child2_resize : 1;
   guint         child2_shrink : 1;
   guint         position_set  : 1;
+  guint         panning       : 1;
 };
 
 enum {
@@ -209,16 +208,8 @@ static gboolean gtk_paned_enter                 (GtkWidget        *widget,
                                                 GdkEventCrossing *event);
 static gboolean gtk_paned_leave                 (GtkWidget        *widget,
                                                 GdkEventCrossing *event);
-static gboolean gtk_paned_button_press          (GtkWidget        *widget,
-                                                GdkEventButton   *event);
-static gboolean gtk_paned_button_release        (GtkWidget        *widget,
-                                                GdkEventButton   *event);
-static gboolean gtk_paned_motion                (GtkWidget        *widget,
-                                                GdkEventMotion   *event);
 static gboolean gtk_paned_focus                 (GtkWidget        *widget,
                                                 GtkDirectionType  direction);
-static gboolean gtk_paned_grab_broken           (GtkWidget          *widget,
-                                                GdkEventGrabBroken *event);
 static void     gtk_paned_add                   (GtkContainer     *container,
                                                 GtkWidget        *widget);
 static void     gtk_paned_remove                (GtkContainer     *container,
@@ -252,9 +243,10 @@ static gboolean gtk_paned_cancel_position       (GtkPaned         *paned);
 static gboolean gtk_paned_toggle_handle_focus   (GtkPaned         *paned);
 
 static GType    gtk_paned_child_type            (GtkContainer     *container);
-static void     gtk_paned_grab_notify           (GtkWidget        *widget,
-                                                gboolean          was_grabbed);
 
+static void     update_drag                     (GtkPaned         *paned,
+                                                 int               xpos,
+                                                 int               ypos);
 
 G_DEFINE_TYPE_WITH_CODE (GtkPaned, gtk_paned, GTK_TYPE_CONTAINER,
                          G_ADD_PRIVATE (GtkPaned)
@@ -316,11 +308,6 @@ gtk_paned_class_init (GtkPanedClass *class)
   widget_class->focus = gtk_paned_focus;
   widget_class->enter_notify_event = gtk_paned_enter;
   widget_class->leave_notify_event = gtk_paned_leave;
-  widget_class->button_press_event = gtk_paned_button_press;
-  widget_class->button_release_event = gtk_paned_button_release;
-  widget_class->motion_notify_event = gtk_paned_motion;
-  widget_class->grab_broken_event = gtk_paned_grab_broken;
-  widget_class->grab_notify = gtk_paned_grab_notify;
   widget_class->state_flags_changed = gtk_paned_state_flags_changed;
 
   container_class->add = gtk_paned_add;
@@ -667,10 +654,106 @@ gtk_paned_child_type (GtkContainer *container)
     return G_TYPE_NONE;
 }
 
+static gboolean
+initiates_touch_drag (GtkPaned *paned,
+                      gdouble   start_x,
+                      gdouble   start_y)
+{
+  gint handle_size, handle_pos, drag_pos;
+  GtkPanedPrivate *priv = paned->priv;
+  GtkAllocation allocation;
+
+#define TOUCH_EXTRA_AREA_WIDTH 50
+  gtk_widget_get_allocation (GTK_WIDGET (paned), &allocation);
+  gtk_widget_style_get (GTK_WIDGET (paned), "handle-size", &handle_size, NULL);
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      handle_pos = priv->handle_pos.x - allocation.x;
+      drag_pos = start_x;
+    }
+  else
+    {
+      handle_pos = priv->handle_pos.y - allocation.y;
+      drag_pos = start_y;
+    }
+
+  if (drag_pos < handle_pos - TOUCH_EXTRA_AREA_WIDTH ||
+      drag_pos > handle_pos + handle_size + TOUCH_EXTRA_AREA_WIDTH)
+    return FALSE;
+
+#undef TOUCH_EXTRA_AREA_WIDTH
+
+  return TRUE;
+}
+
+static void
+pan_gesture_drag_begin_cb (GtkGestureDrag *gesture,
+                           gdouble         start_x,
+                           gdouble         start_y,
+                           GtkPaned       *paned)
+{
+  GtkPanedPrivate *priv = paned->priv;
+  GdkEventSequence *sequence;
+  GtkAllocation allocation;
+  const GdkEvent *event;
+
+  sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+  event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
+  gtk_widget_get_allocation (GTK_WIDGET (paned), &allocation);
+  paned->priv->panning = FALSE;
+
+  if (event->any.window == priv->handle ||
+      (event->type == GDK_TOUCH_BEGIN &&
+       initiates_touch_drag (paned, start_x, start_y)))
+    {
+      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+        priv->drag_pos = start_x - (priv->handle_pos.x - allocation.x);
+      else
+        priv->drag_pos = start_y - (priv->handle_pos.y - allocation.y);
+
+      gtk_gesture_set_state (GTK_GESTURE (gesture),
+                             GTK_EVENT_SEQUENCE_CLAIMED);
+    }
+  else
+    {
+      gtk_gesture_set_state (GTK_GESTURE (gesture),
+                             GTK_EVENT_SEQUENCE_DENIED);
+    }
+}
+
+static void
+pan_gesture_pan_cb (GtkGesturePan   *gesture,
+                    GtkPanDirection  direction,
+                    gdouble          offset,
+                    GtkPaned        *paned)
+{
+  gdouble start_x, start_y, offset_x, offset_y;
+
+  paned->priv->panning = TRUE;
+
+  gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (gesture),
+                               &start_x, &start_y);
+  gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture),
+                               &offset_x, &offset_y);
+  update_drag (paned, start_x + offset_x, start_y + offset_y);
+}
+
+static void
+pan_gesture_drag_end_cb (GtkGestureDrag *gesture,
+                         gdouble         offset_x,
+                         gdouble         offset_y,
+                         GtkPaned       *paned)
+{
+  if (!paned->priv->panning)
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+}
+
 static void
 gtk_paned_init (GtkPaned *paned)
 {
   GtkPanedPrivate *priv;
+  GtkGesture *gesture;
 
   gtk_widget_set_has_window (GTK_WIDGET (paned), FALSE);
   gtk_widget_set_can_focus (GTK_WIDGET (paned), TRUE);
@@ -694,7 +777,6 @@ gtk_paned_init (GtkPaned *paned)
   priv->handle_pos.height = 5;
   priv->position_set = FALSE;
   priv->last_allocation = -1;
-  priv->in_drag = FALSE;
 
   priv->last_child1_focus = NULL;
   priv->last_child2_focus = NULL;
@@ -705,7 +787,17 @@ gtk_paned_init (GtkPaned *paned)
   priv->handle_pos.x = -1;
   priv->handle_pos.y = -1;
 
-  priv->drag_pos = -1;
+  gesture = gtk_gesture_pan_new (GTK_WIDGET (paned),
+                                 GTK_PAN_ORIENTATION_HORIZONTAL);
+  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
+  g_signal_connect (gesture, "drag-begin",
+                    G_CALLBACK (pan_gesture_drag_begin_cb), paned);
+  g_signal_connect (gesture, "pan",
+                    G_CALLBACK (pan_gesture_pan_cb), paned);
+  g_signal_connect (gesture, "drag-end",
+                    G_CALLBACK (pan_gesture_drag_end_cb), paned);
+  gtk_gesture_attach (gesture, GTK_PHASE_CAPTURE);
+  priv->pan_gesture = gesture;
 }
 
 static void
@@ -724,9 +816,17 @@ gtk_paned_set_property (GObject        *object,
       _gtk_orientable_set_style_classes (GTK_ORIENTABLE (paned));
 
       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
-        priv->cursor_type = GDK_SB_H_DOUBLE_ARROW;
+        {
+          priv->cursor_type = GDK_SB_H_DOUBLE_ARROW;
+          gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (priv->pan_gesture),
+                                           GTK_PAN_ORIENTATION_HORIZONTAL);
+        }
       else
-        priv->cursor_type = GDK_SB_V_DOUBLE_ARROW;
+        {
+          priv->cursor_type = GDK_SB_V_DOUBLE_ARROW;
+          gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (priv->pan_gesture),
+                                           GTK_PAN_ORIENTATION_VERTICAL);
+        }
 
       /* state_flags_changed updates the cursor */
       gtk_paned_state_flags_changed (GTK_WIDGET (paned), 0);
@@ -866,6 +966,9 @@ gtk_paned_finalize (GObject *object)
   gtk_paned_set_saved_focus (paned, NULL);
   gtk_paned_set_first_paned (paned, NULL);
 
+  gtk_gesture_detach (paned->priv->pan_gesture);
+  g_clear_object (&paned->priv->pan_gesture);
+
   G_OBJECT_CLASS (gtk_paned_parent_class)->finalize (object);
 }
 
@@ -1634,10 +1737,9 @@ is_rtl (GtkPaned *paned)
 }
 
 static void
-update_drag (GtkPaned         *paned,
-             /* relative to priv->handle */
-             int               xpos,
-             int               ypos)
+update_drag (GtkPaned *paned,
+             int       xpos,
+             int       ypos)
 {
   GtkPanedPrivate *priv = paned->priv;
   GtkAllocation allocation;
@@ -1650,13 +1752,9 @@ update_drag (GtkPaned         *paned,
   gdk_window_get_position (priv->handle, &x, &y);
   gtk_widget_get_allocation (widget, &allocation);
   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
-    {
-      pos = xpos + x - allocation.x;
-    }
+    pos = xpos;
   else
-    {
-      pos = ypos + y - allocation.y;
-    }
+    pos = ypos;
 
   pos -= priv->drag_pos;
 
@@ -1686,9 +1784,7 @@ gtk_paned_enter (GtkWidget        *widget,
   GtkPaned *paned = GTK_PANED (widget);
   GtkPanedPrivate *priv = paned->priv;
 
-  if (priv->in_drag)
-    update_drag (paned, event->x, event->y);
-  else
+  if (!gtk_gesture_is_active (priv->pan_gesture))
     {
       priv->handle_prelit = TRUE;
       gtk_widget_queue_draw_area (widget,
@@ -1708,9 +1804,7 @@ gtk_paned_leave (GtkWidget        *widget,
   GtkPaned *paned = GTK_PANED (widget);
   GtkPanedPrivate *priv = paned->priv;
 
-  if (priv->in_drag)
-    update_drag (paned, event->x, event->y);
-  else
+  if (!gtk_gesture_is_active (priv->pan_gesture))
     {
       priv->handle_prelit = FALSE;
       gtk_widget_queue_draw_area (widget,
@@ -1741,86 +1835,6 @@ gtk_paned_focus (GtkWidget        *widget,
   return retval;
 }
 
-static gboolean
-gtk_paned_button_press (GtkWidget      *widget,
-                       GdkEventButton *event)
-{
-  GtkPaned *paned = GTK_PANED (widget);
-  GtkPanedPrivate *priv = paned->priv;
-
-  if (!priv->in_drag &&
-      (event->window == priv->handle) && (event->button == GDK_BUTTON_PRIMARY))
-    {
-      /* We need a server grab here, not gtk_grab_add(), since
-       * we don't want to pass events on to the widget's children */
-      if (gdk_device_grab (event->device,
-                           priv->handle,
-                           GDK_OWNERSHIP_WINDOW, FALSE,
-                           GDK_BUTTON1_MOTION_MASK
-                           | GDK_BUTTON_RELEASE_MASK
-                           | GDK_ENTER_NOTIFY_MASK
-                           | GDK_LEAVE_NOTIFY_MASK,
-                           NULL, event->time) != GDK_GRAB_SUCCESS)
-       return FALSE;
-
-      priv->in_drag = TRUE;
-      priv->grab_time = event->time;
-      priv->grab_device = event->device;
-
-      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
-       priv->drag_pos = event->x;
-      else
-       priv->drag_pos = event->y;
-
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-static gboolean
-gtk_paned_grab_broken (GtkWidget          *widget,
-                      GdkEventGrabBroken *event)
-{
-  GtkPaned *paned = GTK_PANED (widget);
-  GtkPanedPrivate *priv = paned->priv;
-
-  priv->in_drag = FALSE;
-  priv->drag_pos = -1;
-  priv->position_set = TRUE;
-
-  return TRUE;
-}
-
-static void
-stop_drag (GtkPaned *paned)
-{
-  GtkPanedPrivate *priv = paned->priv;
-
-  priv->in_drag = FALSE;
-  priv->drag_pos = -1;
-  priv->position_set = TRUE;
-
-  gdk_device_ungrab (priv->grab_device,
-                     priv->grab_time);
-  priv->grab_device = NULL;
-}
-
-static void
-gtk_paned_grab_notify (GtkWidget *widget,
-                      gboolean   was_grabbed)
-{
-  GtkPaned *paned = GTK_PANED (widget);
-  GtkPanedPrivate *priv = paned->priv;
-  GdkDevice *grab_device;
-
-  grab_device = priv->grab_device;
-
-  if (priv->in_drag && grab_device &&
-      gtk_widget_device_is_shadowed (widget, grab_device))
-    stop_drag (paned);
-}
-
 static void
 gtk_paned_state_flags_changed (GtkWidget     *widget,
                                GtkStateFlags  previous_state)
@@ -1844,39 +1858,6 @@ gtk_paned_state_flags_changed (GtkWidget     *widget,
     }
 }
 
-static gboolean
-gtk_paned_button_release (GtkWidget      *widget,
-                         GdkEventButton *event)
-{
-  GtkPaned *paned = GTK_PANED (widget);
-  GtkPanedPrivate *priv = paned->priv;
-
-  if (priv->in_drag && (event->button == GDK_BUTTON_PRIMARY))
-    {
-      stop_drag (paned);
-
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-static gboolean
-gtk_paned_motion (GtkWidget      *widget,
-                 GdkEventMotion *event)
-{
-  GtkPaned *paned = GTK_PANED (widget);
-  GtkPanedPrivate *priv = paned->priv;
-
-  if (priv->in_drag)
-    {
-      update_drag (paned, event->x, event->y);
-      return TRUE;
-    }
-  
-  return FALSE;
-}
-
 /**
  * gtk_paned_new:
  * @orientation: the paned’s orientation.
@@ -2848,4 +2829,4 @@ gtk_paned_get_handle_window (GtkPaned *paned)
   g_return_val_if_fail (GTK_IS_PANED (paned), NULL);
 
   return paned->priv->handle;
-}
\ No newline at end of file
+}


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