forked from AuroraMiddleware/gtk
gtkmenushell: Port to GtkGesture
We still need to poke the current event at places, but this is better than the ::event vfunc.
This commit is contained in:
parent
a5414bc404
commit
7bfc3a5c74
@ -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)
|
||||
event = gtk_get_current_event ();
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (gdk_event_get_grab_surface (event, &surface))
|
||||
{
|
||||
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)
|
||||
|
||||
g_object_unref (event);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!priv->active || !priv->button)
|
||||
{
|
||||
gboolean initially_active = priv->active;
|
||||
guint button;
|
||||
guint32 time;
|
||||
|
||||
button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
|
||||
time = gdk_event_get_time (event);
|
||||
|
||||
priv->button = button;
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (priv->parent_menu_shell &&
|
||||
(time - GTK_MENU_SHELL (priv->parent_menu_shell)->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
|
||||
*/
|
||||
GTK_MENU_SHELL (priv->parent_menu_shell)->priv->activate_time = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->active)
|
||||
{
|
||||
GtkWidget *menu_item;
|
||||
guint button = priv->button;
|
||||
|
||||
priv->button = 0;
|
||||
|
||||
if (button && (new_button != button) && priv->parent_menu_shell)
|
||||
{
|
||||
gtk_menu_shell_deactivate_and_emit_done (gtk_menu_shell_get_toplevel_shell (menu_shell));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((time - priv->activate_time) <= MENU_SHELL_TIMEOUT)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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) == widget)
|
||||
if (menu_item)
|
||||
{
|
||||
if (menu_item != menu_shell->priv->active_menu_item)
|
||||
GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->priv->submenu;
|
||||
GtkWidget *parent_menu_item_shell = gtk_widget_get_parent (menu_item);
|
||||
|
||||
if (!_gtk_menu_item_is_selectable (GTK_WIDGET (menu_item)))
|
||||
return;
|
||||
|
||||
if (submenu == NULL)
|
||||
{
|
||||
/* 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).
|
||||
gtk_menu_shell_activate_item (menu_shell, GTK_WIDGET (menu_item), TRUE);
|
||||
return;
|
||||
}
|
||||
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))
|
||||
{
|
||||
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 (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
|
||||
gtk_menu_shell_select_item (menu_shell, menu_item);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
priv->button = button;
|
||||
|
||||
if (menu_item)
|
||||
{
|
||||
if (_gtk_menu_item_is_selectable (menu_item) &&
|
||||
gtk_widget_get_parent (menu_item) == widget &&
|
||||
menu_item != priv->active_menu_item)
|
||||
if (!activated_submenu &&
|
||||
(usec_since_popup == 0 ||
|
||||
usec_since_popup > MENU_POPDOWN_DELAY * 1000))
|
||||
{
|
||||
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);
|
||||
}
|
||||
_gtk_menu_item_popdown_submenu (menu_item);
|
||||
}
|
||||
}
|
||||
else if (!initially_active)
|
||||
{
|
||||
gtk_menu_shell_deactivate (menu_shell);
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
else if (gdk_event_get_event_type (event) == GDK_BUTTON_RELEASE)
|
||||
{
|
||||
GtkMenuShell *parent_shell = GTK_MENU_SHELL (priv->parent_menu_shell);
|
||||
gboolean activated_submenu = FALSE;
|
||||
guint new_button;
|
||||
guint32 time;
|
||||
|
||||
gdk_event_get_button (event, &new_button);
|
||||
time = gdk_event_get_time (event);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (priv->parent_menu_shell &&
|
||||
(time - GTK_MENU_SHELL (priv->parent_menu_shell)->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
|
||||
*/
|
||||
GTK_MENU_SHELL (priv->parent_menu_shell)->priv->activate_time = 0;
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
|
||||
if (priv->active)
|
||||
{
|
||||
GtkWidget *menu_item;
|
||||
guint button = priv->button;
|
||||
|
||||
priv->button = 0;
|
||||
|
||||
if (button && (new_button != button) && priv->parent_menu_shell)
|
||||
{
|
||||
gtk_menu_shell_deactivate_and_emit_done (gtk_menu_shell_get_toplevel_shell (menu_shell));
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
|
||||
if ((time - priv->activate_time) <= MENU_SHELL_TIMEOUT)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
menu_item = gtk_get_event_target_with_type (event, GTK_TYPE_MENU_ITEM);
|
||||
|
||||
if (menu_item)
|
||||
{
|
||||
GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->priv->submenu;
|
||||
GtkWidget *parent_menu_item_shell = gtk_widget_get_parent (menu_item);
|
||||
|
||||
if (!_gtk_menu_item_is_selectable (GTK_WIDGET (menu_item)))
|
||||
return GDK_EVENT_STOP;
|
||||
|
||||
if (submenu == NULL)
|
||||
else
|
||||
{
|
||||
gtk_menu_shell_activate_item (menu_shell, GTK_WIDGET (menu_item), TRUE);
|
||||
return GDK_EVENT_STOP;
|
||||
gtk_menu_item_select (GTK_MENU_ITEM (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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_menu_shell_deactivate_and_emit_done (gtk_menu_shell_get_toplevel_shell (menu_shell));
|
||||
}
|
||||
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user