[gtk+/wip/garnacho/window-dragging: 2/2] gtkwindow: Move window dragging to a standalone drag gesture



commit dcbe35f735dc3dbaa627463ae73b6d52c8a13179
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Feb 25 20:34:12 2015 +0100

    gtkwindow: Move window dragging to a standalone drag gesture
    
    The gesture is hooked to the capture phase, so it works for buttons in
    header bars and whatnot. In order to be friendly to the widget it is
    capturing events from, an ugly hack is in place to avoid capturing
    events when the target widget has a gesture that would consume motion
    events.

 gtk/gtkwidget.c        |   29 ++++++++++++
 gtk/gtkwidgetprivate.h |    2 +
 gtk/gtkwindow.c        |  111 +++++++++++++++++++++++++++++++++---------------
 3 files changed, 108 insertions(+), 34 deletions(-)
---
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 11307c6..0146d57 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -17278,3 +17278,32 @@ _gtk_widget_list_controllers (GtkWidget           *widget,
 
   return retval;
 }
+
+gboolean
+_gtk_widget_consumes_motion (GtkWidget *widget)
+{
+  EventControllerData *data;
+  GtkWidgetPrivate *priv;
+  gboolean consumes_motion = FALSE;
+  GList *l;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  priv = widget->priv;
+
+  for (l = priv->event_controllers; l; l = l->next)
+    {
+      data = l->data;
+
+      if (data->controller == NULL)
+        continue;
+
+      if (!GTK_IS_GESTURE_SINGLE (data->controller))
+        consumes_motion = TRUE;
+      else if (GTK_IS_GESTURE_DRAG (data->controller) ||
+               GTK_IS_GESTURE_SWIPE (data->controller))
+        consumes_motion = TRUE;
+    }
+
+  return consumes_motion;
+}
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 48c9f8c..676d6e8 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -160,6 +160,8 @@ void              _gtk_widget_remove_controller            (GtkWidget
                                                             GtkEventController  *controller);
 GList *           _gtk_widget_list_controllers             (GtkWidget           *widget,
                                                             GtkPropagationPhase  phase);
+gboolean          _gtk_widget_consumes_motion              (GtkWidget           *widget);
+
 gboolean          gtk_widget_has_tick_callback             (GtkWidget *widget);
 
 void              gtk_widget_set_csd_input_shape           (GtkWidget            *widget,
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 54305d5..cb84095 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -233,11 +233,10 @@ struct _GtkWindowPrivate
   guint    fullscreen                : 1;
   guint    tiled                     : 1;
 
-  guint    drag_possible             : 1;
-
   guint    use_subsurface            : 1;
 
   GtkGesture *multipress_gesture;
+  GtkGesture *drag_gesture;
 
   GdkWindow *hardcoded_window;
 };
@@ -1435,8 +1434,10 @@ multipress_gesture_pressed_cb (GtkGestureMultiPress *gesture,
   if (!event)
     return;
 
+  if (n_press > 1)
+    gtk_gesture_set_state (priv->drag_gesture, GTK_EVENT_SEQUENCE_DENIED);
+
   region = get_active_region_type (window, (GdkEventAny*) event, x, y);
-  priv->drag_possible = FALSE;
 
   if (button == GDK_BUTTON_SECONDARY && region == GTK_WINDOW_REGION_TITLE)
     {
@@ -1476,8 +1477,6 @@ multipress_gesture_pressed_cb (GtkGestureMultiPress *gesture,
     case GTK_WINDOW_REGION_TITLE:
       if (n_press == 2)
         gtk_window_titlebar_action (window, event, button, n_press);
-      if (n_press == 1)
-        priv->drag_possible = TRUE;
 
       if (gtk_widget_has_grab (widget))
         gtk_gesture_set_sequence_state (GTK_GESTURE (gesture),
@@ -1502,41 +1501,73 @@ multipress_gesture_pressed_cb (GtkGestureMultiPress *gesture,
 }
 
 static void
-multipress_gesture_stopped_cb (GtkGestureMultiPress *gesture,
-                               GtkWindow            *window)
+drag_gesture_begin_cb (GtkGestureDrag *gesture,
+                       gdouble         x,
+                       gdouble         y,
+                       GtkWindow      *window)
 {
   GdkEventSequence *sequence;
+  GtkWindowRegion region;
   GtkWindowPrivate *priv;
   const GdkEvent *event;
-  gdouble x, y;
+  GtkWidget *event_widget;
 
-  if (!gtk_gesture_is_active (GTK_GESTURE (gesture)))
+  sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+  event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
+
+  if (!event)
     return;
 
-  /* The gesture is active, but stopped, so a too long
-   * press happened, or one drifting out of the threshold
-   */
+  event_widget = gtk_get_event_widget (event);
+
+  if (event_widget != window &&
+      _gtk_widget_consumes_motion (event_widget))
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+  else
+    {
+      region = get_active_region_type (window, (GdkEventAny*) event, x, y);
+
+      if (region != GTK_WINDOW_REGION_TITLE)
+        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+    }
+}
+
+static void
+drag_gesture_update_cb (GtkGestureDrag *gesture,
+                        gdouble         offset_x,
+                        gdouble         offset_y,
+                        GtkWindow      *window)
+{
+  gint double_click_distance;
+  GtkWindowPrivate *priv;
+  GtkSettings *settings;
+
   priv = gtk_window_get_instance_private (window);
-  sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
-  event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
-  gtk_gesture_get_point (GTK_GESTURE (gesture), sequence, &x, &y);
+  settings = gtk_widget_get_settings (GTK_WIDGET (window));
+  g_object_get (settings,
+                "gtk-double-click-distance", &double_click_distance,
+                NULL);
 
-  if (priv->drag_possible)
+  if (ABS (offset_x) > double_click_distance ||
+      ABS (offset_y) > double_click_distance)
     {
-      gdouble x_root, y_root;
+      gdouble start_x, start_y;
+      gint x_root, y_root;
+
+      gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+
+      gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
+      gdk_window_get_root_coords (gtk_widget_get_window (GTK_WIDGET (window)),
+                                  start_x, start_y, &x_root, &y_root);
 
-      gtk_gesture_set_sequence_state (GTK_GESTURE (gesture),
-                                      sequence, GTK_EVENT_SEQUENCE_CLAIMED);
-      gdk_event_get_root_coords (event, &x_root, &y_root);
       gdk_window_begin_move_drag_for_device (gtk_widget_get_window (GTK_WIDGET (window)),
-                                             gdk_event_get_device ((GdkEvent*) event),
-                                             GDK_BUTTON_PRIMARY,
+                                             gtk_gesture_get_device (GTK_GESTURE (gesture)),
+                                             gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE 
(gesture)),
                                              x_root, y_root,
-                                             gdk_event_get_time (event));
-    }
+                                             gtk_get_current_event_time ());
 
-  gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
-  priv->drag_possible = FALSE;
+      gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
+    }
 }
 
 static void
@@ -1632,8 +1663,14 @@ gtk_window_constructed (GObject *object)
                                                   GTK_PHASE_NONE);
       g_signal_connect (priv->multipress_gesture, "pressed",
                         G_CALLBACK (multipress_gesture_pressed_cb), object);
-      g_signal_connect (priv->multipress_gesture, "stopped",
-                        G_CALLBACK (multipress_gesture_stopped_cb), object);
+
+      priv->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (object));
+      gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->drag_gesture),
+                                                  GTK_PHASE_CAPTURE);
+      g_signal_connect (priv->drag_gesture, "drag-begin",
+                        G_CALLBACK (drag_gesture_begin_cb), object);
+      g_signal_connect (priv->drag_gesture, "drag-update",
+                        G_CALLBACK (drag_gesture_update_cb), object);
     }
 }
 
@@ -7766,8 +7803,10 @@ get_active_region_type (GtkWindow *window, GdkEventAny *event, gint x, gint y)
 
 static gboolean
 gtk_window_handle_wm_event (GtkWindow *window,
-                            GdkEvent  *event)
+                            GdkEvent  *event,
+                            gboolean   run_drag)
 {
+  gboolean retval = GDK_EVENT_PROPAGATE;
   GtkWindowPrivate *priv;
 
   if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE ||
@@ -7776,12 +7815,16 @@ gtk_window_handle_wm_event (GtkWindow *window,
     {
       priv = window->priv;
 
+      if (run_drag && priv->drag_gesture)
+        retval |= gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (priv->drag_gesture),
+                                                     (const GdkEvent*) event);
+
       if (priv->multipress_gesture)
-        return gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (priv->multipress_gesture),
-                                                  (const GdkEvent*) event);
+        retval |= gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (priv->multipress_gesture),
+                                                     (const GdkEvent*) event);
     }
 
-  return GDK_EVENT_PROPAGATE;
+  return retval;
 }
 
 gboolean
@@ -7808,7 +7851,7 @@ _gtk_window_check_handle_wm_event (GdkEvent *event)
   if (gtk_widget_event (widget, event))
     return GDK_EVENT_STOP;
 
-  return gtk_window_handle_wm_event (GTK_WINDOW (widget), event);
+  return gtk_window_handle_wm_event (GTK_WINDOW (widget), event, TRUE);
 }
 
 static gboolean
@@ -7816,7 +7859,7 @@ gtk_window_event (GtkWidget *widget,
                   GdkEvent  *event)
 {
   if (widget != gtk_get_event_widget (event))
-    return gtk_window_handle_wm_event (GTK_WINDOW (widget), event);
+    return gtk_window_handle_wm_event (GTK_WINDOW (widget), event, FALSE);
 
   return GDK_EVENT_PROPAGATE;
 }


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