[gtk+] Redo csd window-dragging



commit 2232430a5afca69cf9403af851ed4fe55558da79
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Jan 12 21:01:43 2014 -0500

    Redo csd window-dragging
    
    The window-dragging code had a number of issues: The code was
    starting a drag on every button press, never bothering to cancel
    them. This leads to the odd hand cursor occurring between the two
    clicks to maximize. We relied on GDK's multi-click detection, which
    gives us triple-clicks when we really want sequences of double-clicks.
    Lastly, we didn't propery restrict double-click handling to the primary
    button, so e.g. if you had a window on an empty workspace, double-right
    click on the titlebar would maximize it, which is not intended.
    
    This commit solves all three problem by a doing our own double-click
    detection, and only starting a drag when the pointer goes out of
    'double-click range'. We change the way dragging is implemented for
    menubars and toolbars to just letting events bubble up, so they
    get the same behaviour as the titlebar. To make this work, we
    have to select for pointer motion events in a few more places.

 gtk/gtkmenushell.c     |    8 +-
 gtk/gtktoolbar.c       |    3 +-
 gtk/gtkwindow.c        |  267 +++++++++++++++++++++++++++++------------------
 gtk/gtkwindowprivate.h |    3 -
 4 files changed, 170 insertions(+), 111 deletions(-)
---
diff --git a/gtk/gtkmenushell.c b/gtk/gtkmenushell.c
index 3bbceee..1838695 100644
--- a/gtk/gtkmenushell.c
+++ b/gtk/gtkmenushell.c
@@ -605,6 +605,7 @@ gtk_menu_shell_realize (GtkWidget *widget)
   attributes.event_mask |= (GDK_EXPOSURE_MASK |
                             GDK_BUTTON_PRESS_MASK |
                             GDK_BUTTON_RELEASE_MASK |
+                            GDK_POINTER_MOTION_MASK |
                             GDK_KEY_PRESS_MASK |
                             GDK_ENTER_NOTIFY_MASK |
                             GDK_LEAVE_NOTIFY_MASK);
@@ -648,9 +649,6 @@ gtk_menu_shell_button_press (GtkWidget      *widget,
   GtkWidget *menu_item;
   GtkWidget *parent;
 
-  if (event->type == GDK_2BUTTON_PRESS)
-    return _gtk_window_handle_button_press_for_widget (widget, event);
-
   if (event->type != GDK_BUTTON_PRESS)
     return FALSE;
 
@@ -706,8 +704,8 @@ gtk_menu_shell_button_press (GtkWidget      *widget,
         {
           if (!initially_active)
             {
-              if (_gtk_window_handle_button_press_for_widget (widget, event))
-                gtk_menu_shell_deactivate (menu_shell);
+              gtk_menu_shell_deactivate (menu_shell);
+              return FALSE;
             }
         }
     }
diff --git a/gtk/gtktoolbar.c b/gtk/gtktoolbar.c
index 46c4ca1..20ed218 100644
--- a/gtk/gtktoolbar.c
+++ b/gtk/gtktoolbar.c
@@ -833,6 +833,7 @@ gtk_toolbar_realize (GtkWidget *widget)
   attributes.event_mask = gtk_widget_get_events (widget);
   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
                            GDK_BUTTON_RELEASE_MASK |
+                           GDK_POINTER_MOTION_MASK |
                            GDK_ENTER_NOTIFY_MASK |
                            GDK_LEAVE_NOTIFY_MASK);
 
@@ -2692,7 +2693,7 @@ gtk_toolbar_button_press (GtkWidget      *toolbar,
       return return_value;
     }
 
-  return _gtk_window_handle_button_press_for_widget (toolbar, event);
+  return FALSE;
 }
 
 static gboolean
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index c8fb6c0..3966c38 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -221,6 +221,11 @@ struct _GtkWindowPrivate
   guint    fullscreen                : 1;
   guint    tiled                     : 1;
 
+  guint    drag_possible             : 1;
+
+  gint     button_press_x;
+  gint     button_press_y;
+  guint32  button_press_time;
 };
 
 enum {
@@ -389,6 +394,10 @@ static gint gtk_window_key_release_event  (GtkWidget         *widget,
                                           GdkEventKey       *event);
 static gint gtk_window_button_press_event (GtkWidget         *widget,
                                            GdkEventButton    *event);
+static gint gtk_window_button_release_event (GtkWidget         *widget,
+                                             GdkEventButton    *event);
+static gint gtk_window_motion_notify_event (GtkWidget         *widget,
+                                            GdkEventMotion    *event);
 static gint gtk_window_focus_in_event     (GtkWidget         *widget,
                                           GdkEventFocus     *event);
 static gint gtk_window_focus_out_event    (GtkWidget         *widget,
@@ -663,6 +672,8 @@ gtk_window_class_init (GtkWindowClass *klass)
   widget_class->key_release_event = gtk_window_key_release_event;
   widget_class->focus_in_event = gtk_window_focus_in_event;
   widget_class->button_press_event = gtk_window_button_press_event;
+  widget_class->button_release_event = gtk_window_button_release_event;
+  widget_class->motion_notify_event = gtk_window_motion_notify_event;
   widget_class->focus_out_event = gtk_window_focus_out_event;
   widget_class->focus = gtk_window_focus;
   widget_class->move_focus = gtk_window_move_focus;
@@ -5738,6 +5749,8 @@ gtk_window_realize (GtkWidget *widget)
   attributes.event_mask = gtk_widget_get_events (widget);
   attributes.event_mask |= (GDK_EXPOSURE_MASK |
                            GDK_BUTTON_PRESS_MASK |
+                           GDK_BUTTON_RELEASE_MASK |
+                           GDK_BUTTON_MOTION_MASK |
                            GDK_KEY_PRESS_MASK |
                            GDK_KEY_RELEASE_MASK |
                            GDK_ENTER_NOTIFY_MASK |
@@ -7233,6 +7246,78 @@ get_active_region_type (GtkWindow *window, GdkEventAny *event, gint x, gint y)
   return GTK_WINDOW_REGION_CONTENT;
 }
 
+static inline gboolean
+in_double_click_range (GtkWidget      *widget,
+                       GdkEventButton *event)
+{
+  GtkWindowPrivate *priv = GTK_WINDOW (widget)->priv;
+  gint double_click_time;
+  gint double_click_distance;
+
+  g_object_get (gtk_widget_get_settings (widget),
+                "gtk-double-click-time", &double_click_time,
+                "gtk-double-click-distance", &double_click_distance,
+                NULL);
+
+  if (event->time < priv->button_press_time + double_click_time &&
+      ABS (event->x_root - priv->button_press_x) <= double_click_distance &&
+      ABS (event->y_root - priv->button_press_y) <= double_click_distance)
+    return TRUE;
+
+  return FALSE;
+}
+
+static gint
+gtk_window_motion_notify_event (GtkWidget      *widget,
+                                GdkEventMotion *event)
+{
+  GtkWindowPrivate *priv = GTK_WINDOW (widget)->priv;
+  GtkWidget *src;
+  gboolean window_drag;
+
+  if (!priv->drag_possible)
+    return FALSE;
+
+  gdk_window_get_user_data (event->window, (gpointer *)&src);
+  if (src && src != widget)
+    {
+      gtk_widget_style_get (GTK_WIDGET (src),
+                            "window-dragging", &window_drag,
+                            NULL);
+      if (!window_drag)
+        {
+          priv->drag_possible = FALSE;
+          return FALSE;
+        }
+    }
+
+  if (!in_double_click_range (widget, (GdkEventButton *)event))
+    {
+      gdk_window_begin_move_drag_for_device (gtk_widget_get_window (widget),
+                                             gdk_event_get_device ((GdkEvent *)event),
+                                             GDK_BUTTON_PRIMARY,
+                                             event->x_root,
+                                             event->y_root,
+                                             event->time);
+      priv->drag_possible = FALSE;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gint
+gtk_window_button_release_event (GtkWidget      *widget,
+                                 GdkEventButton *event)
+{
+  GtkWindow *window = GTK_WINDOW (widget);
+  GtkWindowPrivate *priv = window->priv;
+
+  priv->drag_possible = FALSE;
+
+  return FALSE;
+}
+
 static gint
 gtk_window_button_press_event (GtkWidget      *widget,
                                GdkEventButton *event)
@@ -7241,9 +7326,22 @@ gtk_window_button_press_event (GtkWidget      *widget,
   GtkWindowPrivate *priv = window->priv;
   GdkWindowEdge edge;
   GdkWindow *gdk_window;
+  gint x, y;
+  GtkWidget *src;
+  GtkWindowRegion region;
+  gboolean window_drag = FALSE;
 
   gdk_window = gtk_widget_get_window (widget);
 
+  /* We do our own double-click detection, so we ignore
+   * GDK_2BUTTON_PRESS and GDK_3BUTTON_PRESS events
+   */
+  if (event->type != GDK_BUTTON_PRESS)
+    return FALSE;
+
+  if (priv->fullscreen)
+    return FALSE;
+
   if (event->window == priv->grip_window)
     {
       if (get_drag_edge (widget, &edge))
@@ -7257,82 +7355,87 @@ gtk_window_button_press_event (GtkWidget      *widget,
 
       return TRUE;
     }
-  else if (!priv->fullscreen)
+
+  gdk_window_get_user_data (event->window, (gpointer *)&src);
+  if (src && src != widget)
     {
-      gint x, y;
-      GtkWidget *src;
-      GtkWindowRegion region;
+      gtk_widget_style_get (GTK_WIDGET (src),
+                            "window-dragging", &window_drag,
+                            NULL);
+      gtk_widget_translate_coordinates (src, widget, event->x, event->y, &x, &y);
+    }
+  else
+    {
+      x = event->x;
+      y = event->y;
+    }
 
-      gdk_window_get_user_data (event->window, (gpointer *)&src);
-      if (src && src != widget)
+  region = get_active_region_type (window, (GdkEventAny*)event, x, y);
+
+  if (event->button == GDK_BUTTON_PRIMARY)
+    {
+      if (in_double_click_range (widget, event))
         {
-          gtk_widget_translate_coordinates (src, widget, event->x, event->y, &x, &y);
+          switch (region)
+            {
+            case GTK_WINDOW_REGION_CONTENT:
+              if (!window_drag) /* do nothing */
+                break;
+              /* fall thru */
+            case GTK_WINDOW_REGION_TITLE:
+              _gtk_window_toggle_maximized (window);
+              return TRUE;
+            default:
+              break;
+            }
         }
       else
         {
-          x = event->x;
-          y = event->y;
-        }
-      region = get_active_region_type (window, (GdkEventAny*)event, x, y);
-
-      if (event->type == GDK_BUTTON_PRESS)
-        {
-          if (event->button == GDK_BUTTON_PRIMARY)
+          switch (region)
             {
-              switch (region)
-                {
-                case GTK_WINDOW_REGION_CONTENT:
-                  /* do nothing */
-                  break;
-                case GTK_WINDOW_REGION_TITLE:
-                case GTK_WINDOW_REGION_EDGE:
-                   gdk_window_begin_move_drag_for_device (gdk_window,
-                                                          gdk_event_get_device ((GdkEvent *) event),
-                                                          event->button,
-                                                          event->x_root,
-                                                          event->y_root,
-                                                          event->time);
-                  return TRUE;
+            case GTK_WINDOW_REGION_CONTENT:
+              if (!window_drag)
+                break;
+              /* fall thru */
+
+            case GTK_WINDOW_REGION_TITLE:
+            case GTK_WINDOW_REGION_EDGE:
+              priv->drag_possible = TRUE;
+              priv->button_press_x = event->x_root;
+              priv->button_press_y = event->y_root;
+              priv->button_press_time = event->time;
+              return TRUE;
 
-                default:
-                  if (!priv->maximized)
-                    {
-                      gdk_window_begin_resize_drag_for_device (gdk_window,
-                                                               (GdkWindowEdge)region,
-                                                               gdk_event_get_device ((GdkEvent *) event),
-                                                               event->button,
-                                                               event->x_root,
-                                                               event->y_root,
-                                                               event->time);
-                      return TRUE;
-                    }
-                  break;
-                }
-            }
-          else if (event->button == GDK_BUTTON_SECONDARY)
-            {
-              if (region == GTK_WINDOW_REGION_TITLE)
+            default:
+              if (!priv->maximized)
                 {
-                  gtk_window_do_popup (window, event);
-                  return TRUE;
-                }
-            }
-          else if (event->button == GDK_BUTTON_MIDDLE)
-            {
-              if (region == GTK_WINDOW_REGION_TITLE)
-                {
-                  gdk_window_lower (gdk_window);
+                  gdk_window_begin_resize_drag_for_device (gdk_window,
+                                                           (GdkWindowEdge)region,
+                                                           gdk_event_get_device ((GdkEvent *) event),
+                                                           event->button,
+                                                           event->x_root,
+                                                           event->y_root,
+                                                           event->time);
                   return TRUE;
                 }
+              break;
             }
         }
-      else if (event->type == GDK_2BUTTON_PRESS)
+    }
+  else if (event->button == GDK_BUTTON_SECONDARY)
+    {
+      if (region == GTK_WINDOW_REGION_TITLE)
         {
-          if (region == GTK_WINDOW_REGION_TITLE)
-            {
-              _gtk_window_toggle_maximized (window);
-              return TRUE;
-            }
+          gtk_window_do_popup (window, event);
+          return TRUE;
+        }
+    }
+  else if (event->button == GDK_BUTTON_MIDDLE)
+    {
+      if (region == GTK_WINDOW_REGION_TITLE)
+        {
+          gdk_window_lower (gdk_window);
+          return TRUE;
         }
     }
 
@@ -11518,46 +11621,6 @@ ensure_state_flag_backdrop (GtkWidget *widget)
   gtk_widget_queue_draw (widget);
 }
 
-gboolean
-_gtk_window_handle_button_press_for_widget (GtkWidget      *widget,
-                                            GdkEventButton *event)
-{
-  gboolean window_drag = FALSE;
-  GtkWindow *window;
-
-  gtk_widget_style_get (GTK_WIDGET (widget),
-                        "window-dragging", &window_drag,
-                        NULL);
-
-  if (!window_drag)
-    return FALSE;
-
-  if (event->button != GDK_BUTTON_PRIMARY)
-    return FALSE;
-
-  window = GTK_WINDOW (gtk_widget_get_toplevel (widget));
-
-  switch (event->type)
-    {
-    case GDK_BUTTON_PRESS:
-      gtk_window_begin_move_drag (window,
-                                  event->button,
-                                  event->x_root,
-                                  event->y_root,
-                                  event->time);
-      return TRUE;
-
-    case GDK_2BUTTON_PRESS:
-      _gtk_window_toggle_maximized (window);
-      return TRUE;
-
-    default:
-      break;
-    }
-
-  return FALSE;
-}
-
 void
 _gtk_window_get_shadow_width (GtkWindow *window,
                               GtkBorder *border)
diff --git a/gtk/gtkwindowprivate.h b/gtk/gtkwindowprivate.h
index 8826782..bfe7ecc 100644
--- a/gtk/gtkwindowprivate.h
+++ b/gtk/gtkwindowprivate.h
@@ -65,9 +65,6 @@ void            _gtk_window_set_allocation         (GtkWindow           *window,
                                                     const GtkAllocation *allocation,
                                                     GtkAllocation       *allocation_out);
 
-gboolean       _gtk_window_handle_button_press_for_widget (GtkWidget      *widget,
-                                                           GdkEventButton *event);
-
 typedef void (*GtkWindowKeysForeachFunc) (GtkWindow      *window,
                                           guint           keyval,
                                           GdkModifierType modifiers,


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