gtkmenushell: Port to GtkGesture
authorCarlos Garnacho <carlosg@gnome.org>
Thu, 7 Jun 2018 13:32:47 +0000 (15:32 +0200)
committerCarlos Garnacho <carlosg@gnome.org>
Thu, 21 Jun 2018 10:50:58 +0000 (12:50 +0200)
We still need to poke the current event at places, but this is
better than the ::event vfunc.

gtk/gtkmenushell.c

index 8873345ffa715424fc3f5188485314121c7764f3..f9c497b8e532adb5a04de93d5b931d492753b76b 100644 (file)
@@ -76,6 +76,7 @@
 #include "gtkwindow.h"
 #include "gtkwindowprivate.h"
 #include "gtkeventcontrollerkey.h"
+#include "gtkgesturemultipress.h"
 
 #include "a11y/gtkmenushellaccessible.h"
 
@@ -124,8 +125,20 @@ static gboolean gtk_menu_shell_key_press     (GtkEventControllerKey *key,
                                               GtkWidget             *widget);
 static void gtk_menu_shell_display_changed   (GtkWidget         *widget,
                                               GdkDisplay        *previous_display);
-static gboolean gtk_menu_shell_event         (GtkWidget         *widget,
-                                              GdkEvent          *event);
+static void multi_press_pressed  (GtkGestureMultiPress *gesture,
+                                  gint                  n_press,
+                                  gdouble               x,
+                                  gdouble               y,
+                                  GtkMenuShell         *menu_shell);
+static void multi_press_released (GtkGestureMultiPress *gesture,
+                                  gint                  n_press,
+                                  gdouble               x,
+                                  gdouble               y,
+                                  GtkMenuShell         *menu_shell);
+static void multi_press_stopped  (GtkGestureMultiPress *gesture,
+                                  GtkMenuShell         *menu_shell);
+
+
 static void gtk_menu_shell_add               (GtkContainer      *container,
                                               GtkWidget         *widget);
 static void gtk_menu_shell_remove            (GtkContainer      *container,
@@ -180,7 +193,6 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass)
   object_class->finalize = gtk_menu_shell_finalize;
   object_class->dispose = gtk_menu_shell_dispose;
 
-  widget_class->event = gtk_menu_shell_event;
   widget_class->display_changed = gtk_menu_shell_display_changed;
 
   container_class->add = gtk_menu_shell_add;
@@ -422,6 +434,15 @@ gtk_menu_shell_init (GtkMenuShell *menu_shell)
   gtk_widget_add_controller (widget, controller);
 
   gtk_widget_set_has_surface (widget, FALSE);
+
+  controller = GTK_EVENT_CONTROLLER (gtk_gesture_multi_press_new ());
+  g_signal_connect (controller, "pressed",
+                    G_CALLBACK (multi_press_pressed), menu_shell);
+  g_signal_connect (controller, "released",
+                    G_CALLBACK (multi_press_released), menu_shell);
+  g_signal_connect (controller, "stopped",
+                    G_CALLBACK (multi_press_stopped), menu_shell);
+  gtk_widget_add_controller (widget, controller);
 }
 
 static void
@@ -605,217 +626,238 @@ gtk_menu_shell_get_toplevel_shell (GtkMenuShell *menu_shell)
   return menu_shell;
 }
 
-static gboolean
-gtk_menu_shell_event (GtkWidget *widget,
-                      GdkEvent  *event)
+static void
+multi_press_stopped (GtkGestureMultiPress *gesture,
+                     GtkMenuShell         *menu_shell)
 {
-  GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
   GtkMenuShellPrivate *priv = menu_shell->priv;
   GdkSurface *surface;
+  GdkEvent *event;
 
-  if (gdk_event_get_event_type (event) == GDK_GRAB_BROKEN)
-    {
-      gdk_event_get_grab_surface (event, &surface);
+  event = gtk_get_current_event ();
+  if (!event)
+    return;
 
+  if (gdk_event_get_grab_surface (event, &surface))
+    {
       if (priv->have_xgrab && surface == NULL)
         {
           /* Unset the active menu item so gtk_menu_popdown() doesn't see it. */
           gtk_menu_shell_deselect (menu_shell);
           gtk_menu_shell_deactivate_and_emit_done (menu_shell);
         }
-
-      return GDK_EVENT_STOP;
     }
-  else if (gdk_event_get_event_type (event) == GDK_BUTTON_PRESS)
-    {
-      GtkWidget *menu_item;
 
-      menu_item = gtk_get_event_target_with_type (event, GTK_TYPE_MENU_ITEM);
+  g_object_unref (event);
+}
 
-      if (menu_item &&
-          _gtk_menu_item_is_selectable (menu_item) &&
-          gtk_widget_get_parent (menu_item) == widget)
-        {
-          if (menu_item != menu_shell->priv->active_menu_item)
-            {
-              /*  select the menu item *before* activating the shell, so submenus
-               *  which might be open are closed the friendly way. If we activate
-               *  (and thus grab) this menu shell first, we might get grab_broken
-               *  events which will close the entire menu hierarchy. Selecting the
-               *  menu item also fixes up the state as if enter_notify() would
-               *  have run before (which normally selects the item).
-               */
-              if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
-                gtk_menu_shell_select_item (menu_shell, menu_item);
-            }
+static void
+multi_press_pressed (GtkGestureMultiPress *gesture,
+                     gint                  n_press,
+                     gdouble               x,
+                     gdouble               y,
+                     GtkMenuShell         *menu_shell)
+{
+  GtkMenuShellPrivate *priv = menu_shell->priv;
+  GtkWidget *menu_item;
+  GdkEvent *event;
 
-          if (GTK_MENU_ITEM (menu_item)->priv->submenu != NULL &&
-              !gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu))
-            {
-              _gtk_menu_item_popup_submenu (menu_item, FALSE);
-              priv->activated_submenu = TRUE;
-            }
+  event = gtk_get_current_event ();
+  menu_item = gtk_get_event_target_with_type (event, GTK_TYPE_MENU_ITEM);
+
+  if (menu_item &&
+      _gtk_menu_item_is_selectable (menu_item) &&
+      gtk_widget_get_parent (menu_item) == GTK_WIDGET (menu_shell))
+    {
+      if (menu_item != menu_shell->priv->active_menu_item)
+        {
+          /*  select the menu item *before* activating the shell, so submenus
+           *  which might be open are closed the friendly way. If we activate
+           *  (and thus grab) this menu shell first, we might get grab_broken
+           *  events which will close the entire menu hierarchy. Selecting the
+           *  menu item also fixes up the state as if enter_notify() would
+           *  have run before (which normally selects the item).
+           */
+          if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
+            gtk_menu_shell_select_item (menu_shell, menu_item);
         }
 
-      if (!priv->active || !priv->button)
+      if (GTK_MENU_ITEM (menu_item)->priv->submenu != NULL &&
+          !gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu))
         {
-          gboolean initially_active = priv->active;
-          guint button;
-          guint32 time;
+          _gtk_menu_item_popup_submenu (menu_item, FALSE);
+          priv->activated_submenu = TRUE;
+        }
+    }
+
+  if (!priv->active || !priv->button)
+    {
+      gboolean initially_active = priv->active;
+      guint button;
+      guint32 time;
 
-          gdk_event_get_button (event, &button);
-          time = gdk_event_get_time (event);
+      button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+      time = gdk_event_get_time (event);
 
-          priv->button = button;
+      priv->button = button;
 
-          if (menu_item)
+      if (menu_item)
+        {
+          if (_gtk_menu_item_is_selectable (menu_item) &&
+              gtk_widget_get_parent (menu_item) == GTK_WIDGET (menu_shell) &&
+              menu_item != priv->active_menu_item)
             {
-              if (_gtk_menu_item_is_selectable (menu_item) &&
-                  gtk_widget_get_parent (menu_item) == widget &&
-                  menu_item != priv->active_menu_item)
-                {
-                  gtk_menu_shell_activate (menu_shell);
+              gtk_menu_shell_activate (menu_shell);
 
-                  if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
-                    {
-                      priv->activate_time = time;
-                      gtk_menu_shell_select_item (menu_shell, menu_item);
-                    }
+              if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
+                {
+                  priv->activate_time = time;
+                  gtk_menu_shell_select_item (menu_shell, menu_item);
                 }
             }
-          else if (!initially_active)
-            {
-              gtk_menu_shell_deactivate (menu_shell);
-              return GDK_EVENT_STOP;
-            }
         }
+      else if (!initially_active)
+        {
+          gtk_menu_shell_deactivate (menu_shell);
+          gtk_gesture_set_state (GTK_GESTURE (gesture),
+                                 GTK_EVENT_SEQUENCE_CLAIMED);
+        }
+    }
+
+  g_object_unref (event);
+}
+
+static void
+multi_press_released (GtkGestureMultiPress *gesture,
+                      gint                  n_press,
+                      gdouble               x,
+                      gdouble               y,
+                      GtkMenuShell         *menu_shell)
+{
+  GtkMenuShellPrivate *priv = menu_shell->priv;
+  GtkMenuShell *parent_shell = GTK_MENU_SHELL (priv->parent_menu_shell);
+  gboolean activated_submenu = FALSE;
+  guint new_button;
+  guint32 time;
+  GdkEvent *event;
+
+  event = gtk_get_current_event ();
+  new_button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+  time = gdk_event_get_time (event);
 
-      return GDK_EVENT_PROPAGATE;
+  gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+
+  if (parent_shell)
+    {
+      /* If a submenu was just activated, it is its shell which is receiving
+       * the button release event. In this case, we must check the parent
+       * shell to know about the submenu state.
+       */
+      activated_submenu = parent_shell->priv->activated_submenu;
+      parent_shell->priv->activated_submenu = FALSE;
     }
-  else if (gdk_event_get_event_type (event) == GDK_BUTTON_RELEASE)
+
+  if (priv->parent_menu_shell &&
+      (time - GTK_MENU_SHELL (priv->parent_menu_shell)->priv->activate_time) < MENU_SHELL_TIMEOUT)
     {
-      GtkMenuShell *parent_shell = GTK_MENU_SHELL (priv->parent_menu_shell);
-      gboolean activated_submenu = FALSE;
-      guint new_button;
-      guint32 time;
+      /* The button-press originated in the parent menu bar and we are
+       * a pop-up menu. It was a quick press-and-release so we don't want
+       * to activate an item but we leave the popup in place instead.
+       * https://bugzilla.gnome.org/show_bug.cgi?id=703069
+       */
+      GTK_MENU_SHELL (priv->parent_menu_shell)->priv->activate_time = 0;
+      return;
+    }
 
-      gdk_event_get_button (event, &new_button);
-      time = gdk_event_get_time (event);
+  if (priv->active)
+    {
+      GtkWidget *menu_item;
+      guint button = priv->button;
 
-      if (parent_shell)
+      priv->button = 0;
+
+      if (button && (new_button != button) && priv->parent_menu_shell)
         {
-          /* If a submenu was just activated, it is its shell which is receiving
-           * the button release event. In this case, we must check the parent
-           * shell to know about the submenu state.
-           */
-          activated_submenu = parent_shell->priv->activated_submenu;
-          parent_shell->priv->activated_submenu = FALSE;
+          gtk_menu_shell_deactivate_and_emit_done (gtk_menu_shell_get_toplevel_shell (menu_shell));
+          return;
         }
 
-      if (priv->parent_menu_shell &&
-          (time - GTK_MENU_SHELL (priv->parent_menu_shell)->priv->activate_time) < MENU_SHELL_TIMEOUT)
+      if ((time - priv->activate_time) <= MENU_SHELL_TIMEOUT)
         {
-          /* The button-press originated in the parent menu bar and we are
-           * a pop-up menu. It was a quick press-and-release so we don't want
-           * to activate an item but we leave the popup in place instead.
-           * https://bugzilla.gnome.org/show_bug.cgi?id=703069
+          /* We only ever want to prevent deactivation on the first
+           * press/release. Setting the time to zero is a bit of a
+           * hack, since we could be being triggered in the first
+           * few fractions of a second after a server time wraparound.
+           * the chances of that happening are ~1/10^6, without
+           * serious harm if we lose.
            */
-          GTK_MENU_SHELL (priv->parent_menu_shell)->priv->activate_time = 0;
-          return GDK_EVENT_STOP;
+          priv->activate_time = 0;
+          return;
         }
 
-      if (priv->active)
+      menu_item = gtk_get_event_target_with_type (event, GTK_TYPE_MENU_ITEM);
+
+      if (menu_item)
         {
-          GtkWidget *menu_item;
-          guint button = priv->button;
+          GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->priv->submenu;
+          GtkWidget *parent_menu_item_shell = gtk_widget_get_parent (menu_item);
 
-          priv->button = 0;
+          if (!_gtk_menu_item_is_selectable (GTK_WIDGET (menu_item)))
+            return;
 
-          if (button && (new_button != button) && priv->parent_menu_shell)
+          if (submenu == NULL)
             {
-              gtk_menu_shell_deactivate_and_emit_done (gtk_menu_shell_get_toplevel_shell (menu_shell));
-              return GDK_EVENT_STOP;
+              gtk_menu_shell_activate_item (menu_shell, GTK_WIDGET (menu_item), TRUE);
+              return;
             }
-
-          if ((time - priv->activate_time) <= MENU_SHELL_TIMEOUT)
+          else if (GTK_MENU_SHELL (parent_menu_item_shell)->priv->parent_menu_shell &&
+                   (activated_submenu ||
+                    GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM))
             {
-              /* We only ever want to prevent deactivation on the first
-               * press/release. Setting the time to zero is a bit of a
-               * hack, since we could be being triggered in the first
-               * few fractions of a second after a server time wraparound.
-               * the chances of that happening are ~1/10^6, without
-               * serious harm if we lose.
-               */
-              priv->activate_time = 0;
-              return GDK_EVENT_STOP;
-            }
+              GTimeVal *popup_time;
+              gint64 usec_since_popup = 0;
 
-          menu_item = gtk_get_event_target_with_type (event, GTK_TYPE_MENU_ITEM);
+              popup_time = g_object_get_data (G_OBJECT (menu_shell),
+                                              "gtk-menu-exact-popup-time");
+              if (popup_time)
+                {
+                  GTimeVal current_time;
 
-          if (menu_item)
-            {
-              GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->priv->submenu;
-              GtkWidget *parent_menu_item_shell = gtk_widget_get_parent (menu_item);
+                  g_get_current_time (&current_time);
 
-              if (!_gtk_menu_item_is_selectable (GTK_WIDGET (menu_item)))
-                return GDK_EVENT_STOP;
+                  usec_since_popup = ((gint64) current_time.tv_sec * 1000 * 1000 +
+                                      (gint64) current_time.tv_usec -
+                                      (gint64) popup_time->tv_sec * 1000 * 1000 -
+                                      (gint64) popup_time->tv_usec);
+
+                  g_object_set_data (G_OBJECT (menu_shell),
+                                     "gtk-menu-exact-popup-time", NULL);
+                }
 
-              if (submenu == NULL)
+              /* Only close the submenu on click if we opened the
+               * menu explicitly (usec_since_popup == 0) or
+               * enough time has passed since it was opened by
+               * GtkMenuItem's timeout (usec_since_popup > delay).
+               */
+              if (!activated_submenu &&
+                  (usec_since_popup == 0 ||
+                   usec_since_popup > MENU_POPDOWN_DELAY * 1000))
                 {
-                  gtk_menu_shell_activate_item (menu_shell, GTK_WIDGET (menu_item), TRUE);
-                  return GDK_EVENT_STOP;
+                  _gtk_menu_item_popdown_submenu (menu_item);
                 }
-              else if (GTK_MENU_SHELL (parent_menu_item_shell)->priv->parent_menu_shell &&
-                       (activated_submenu ||
-                       GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM))
+              else
                 {
-                  GTimeVal *popup_time;
-                  gint64 usec_since_popup = 0;
-
-                  popup_time = g_object_get_data (G_OBJECT (menu_shell),
-                                                  "gtk-menu-exact-popup-time");
-                  if (popup_time)
-                    {
-                      GTimeVal current_time;
-
-                      g_get_current_time (&current_time);
-
-                      usec_since_popup = ((gint64) current_time.tv_sec * 1000 * 1000 +
-                                          (gint64) current_time.tv_usec -
-                                          (gint64) popup_time->tv_sec * 1000 * 1000 -
-                                          (gint64) popup_time->tv_usec);
-
-                      g_object_set_data (G_OBJECT (menu_shell),
-                                         "gtk-menu-exact-popup-time", NULL);
-                    }
-
-                  /* Only close the submenu on click if we opened the
-                   * menu explicitly (usec_since_popup == 0) or
-                   * enough time has passed since it was opened by
-                   * GtkMenuItem's timeout (usec_since_popup > delay).
-                   */
-                  if (!activated_submenu &&
-                      (usec_since_popup == 0 ||
-                       usec_since_popup > MENU_POPDOWN_DELAY * 1000))
-                    {
-                      _gtk_menu_item_popdown_submenu (menu_item);
-                    }
-                  else
-                    {
-                      gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
-                    }
-
-                  return GDK_EVENT_STOP;
+                  gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
                 }
-            }
 
-          gtk_menu_shell_deactivate_and_emit_done (gtk_menu_shell_get_toplevel_shell (menu_shell));
+              return;
+            }
         }
 
-      return GDK_EVENT_STOP;
+      gtk_menu_shell_deactivate_and_emit_done (gtk_menu_shell_get_toplevel_shell (menu_shell));
     }
 
-  return GDK_EVENT_PROPAGATE;
+  g_object_unref (event);
 }
 
 void