#include "gtkwindow.h"
#include "gtkwindowprivate.h"
#include "gtkeventcontrollerkey.h"
+#include "gtkgesturemultipress.h"
#include "a11y/gtkmenushellaccessible.h"
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,
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;
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
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 (¤t_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 (¤t_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