diff options
author | Michael Natterer <mitch@imendio.com> | 2007-04-27 14:49:37 +0000 |
---|---|---|
committer | Michael Natterer <mitch@src.gnome.org> | 2007-04-27 14:49:37 +0000 |
commit | 533d3fcc76b39fc4c71afae9c2a31e8147851b8c (patch) | |
tree | e920ae1729522ef4580cca5134faa28bd64deb5e | |
parent | 069d78ed31fc1c0a9cb7621d27fe10d7b974315b (diff) | |
download | gtk+-533d3fcc76b39fc4c71afae9c2a31e8147851b8c.tar.gz |
Merged heavily modified patch from maemo-gtk which enables opening and
2007-04-27 Michael Natterer <mitch@imendio.com>
Merged heavily modified patch from maemo-gtk which enables opening
and closing submenus on click, and introduces some usability
changes when gtk-touchscreen-mode is enabled (bug #128968):
* gtk/gtkmenushell.c (struct GtkMenuShellPrivate): added boolean
"activated_submenu" to indicate that the current mouse operation
(click or drag) has opened a submenu.
(gtk_menu_shell_button_press): pop up submenus without delay
and record the fact in "activated_submenu".
(gtk_menu_shell_button_release): if a submenu was explicitely
opened, or not opened by this release's button_press, or enough
time has passed since timeout-opening it, close the submenu here.
(gtk_menu_shell_enter_notify): when entering a menu item with
any mouse button pressed, open its submenu.
(gtk_real_menu_shell_move_current): in touchsreen mode, close the
submenu when moving the focus away from it via keyboard-navigation.
* gtk/gtkmenuitem.[ch] (_gtk_menu_item_popup_submenu): added
parameter "gboolean with_delay" so GtkMenuShell can control this
for the different scenarios of submenu showing.
(_gtk_menu_item_popdown_submenu): new function. also needed by
GtkMenuShell for closing submenus on click.
Renamed internal function gtk_menu_item_select_timeout() to
gtk_menu_item_popup_timeout().
(gtk_menu_item_real_popup_submenu): new utility function which
does the actual popup and records the exact time of the popup when
the menu was timeout-opened (using g_get_current_time()).
(gtk_real_menu_item_select): don't add the popup timeout when in
touchscreen mode.
* gtk/gtkmenu.c (gtk_menu_popup): in touchscreen mode, select the
first item of every opened menu.
svn path=/trunk/; revision=17659
-rw-r--r-- | ChangeLog | 43 | ||||
-rw-r--r-- | gtk/gtkmenu.c | 13 | ||||
-rw-r--r-- | gtk/gtkmenuitem.c | 221 | ||||
-rw-r--r-- | gtk/gtkmenuitem.h | 4 | ||||
-rw-r--r-- | gtk/gtkmenushell.c | 275 |
5 files changed, 383 insertions, 173 deletions
@@ -1,3 +1,46 @@ +2007-04-27 Michael Natterer <mitch@imendio.com> + + Merged heavily modified patch from maemo-gtk which enables opening + and closing submenus on click, and introduces some usability + changes when gtk-touchscreen-mode is enabled (bug #128968): + + * gtk/gtkmenushell.c (struct GtkMenuShellPrivate): added boolean + "activated_submenu" to indicate that the current mouse operation + (click or drag) has opened a submenu. + + (gtk_menu_shell_button_press): pop up submenus without delay + and record the fact in "activated_submenu". + + (gtk_menu_shell_button_release): if a submenu was explicitely + opened, or not opened by this release's button_press, or enough + time has passed since timeout-opening it, close the submenu here. + + (gtk_menu_shell_enter_notify): when entering a menu item with + any mouse button pressed, open its submenu. + + (gtk_real_menu_shell_move_current): in touchsreen mode, close the + submenu when moving the focus away from it via keyboard-navigation. + + * gtk/gtkmenuitem.[ch] (_gtk_menu_item_popup_submenu): added + parameter "gboolean with_delay" so GtkMenuShell can control this + for the different scenarios of submenu showing. + + (_gtk_menu_item_popdown_submenu): new function. also needed by + GtkMenuShell for closing submenus on click. + + Renamed internal function gtk_menu_item_select_timeout() to + gtk_menu_item_popup_timeout(). + + (gtk_menu_item_real_popup_submenu): new utility function which + does the actual popup and records the exact time of the popup when + the menu was timeout-opened (using g_get_current_time()). + + (gtk_real_menu_item_select): don't add the popup timeout when in + touchscreen mode. + + * gtk/gtkmenu.c (gtk_menu_popup): in touchscreen mode, select the + first item of every opened menu. + 2007-04-26 Matthias Clasen <mclasen@redhat.com> * gtk/gtkfilechooserentry.c: Append a '/' to directory names diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index 9c92b04401..71de7d7c2e 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -1429,6 +1429,19 @@ gtk_menu_popup (GtkMenu *menu, gtk_menu_scroll_to (menu, menu->scroll_offset); + /* if no item is selected, select the first one */ + if (!menu_shell->active_menu_item) + { + gboolean touchscreen_mode; + + g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), + "gtk-touchscreen-mode", &touchscreen_mode, + NULL); + + if (touchscreen_mode) + gtk_menu_shell_select_first (menu_shell, TRUE); + } + /* Once everything is set up correctly, map the toplevel window on the screen. */ diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c index 03855d6999..7d9a7e963e 100644 --- a/gtk/gtkmenuitem.c +++ b/gtk/gtkmenuitem.c @@ -79,7 +79,7 @@ static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item, static gboolean gtk_menu_item_mnemonic_activate (GtkWidget *widget, gboolean group_cycling); -static gint gtk_menu_item_select_timeout (gpointer data); +static gint gtk_menu_item_popup_timeout (gpointer data); static void gtk_menu_item_position_menu (GtkMenu *menu, gint *x, gint *y, @@ -875,72 +875,28 @@ gtk_menu_item_expose (GtkWidget *widget, return FALSE; } -static gint -get_popup_delay (GtkMenuItem *menu_item) -{ - GtkWidget *parent = GTK_WIDGET (menu_item)->parent; - - if (GTK_IS_MENU_SHELL (parent)) - { - return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (parent)); - } - else - { - gint popup_delay; - - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_item)), - "gtk-menu-popup-delay", &popup_delay, - NULL); - - return popup_delay; - } -} - static void gtk_real_menu_item_select (GtkItem *item) { GtkMenuItem *menu_item; + gboolean touchscreen_mode; g_return_if_fail (GTK_IS_MENU_ITEM (item)); menu_item = GTK_MENU_ITEM (item); - if (menu_item->submenu && + g_object_get (gtk_widget_get_settings (GTK_WIDGET (item)), + "gtk-touchscreen-mode", &touchscreen_mode, + NULL); + + if (!touchscreen_mode && + menu_item->submenu && (!GTK_WIDGET_MAPPED (menu_item->submenu) || GTK_MENU (menu_item->submenu)->tearoff_active)) { - gint popup_delay; - - if (menu_item->timer) - { - g_source_remove (menu_item->timer); - menu_item->timer = 0; - popup_delay = 0; - } - else - popup_delay = get_popup_delay (menu_item); - - if (popup_delay > 0) - { - GdkEvent *event = gtk_get_current_event (); - - menu_item->timer = gdk_threads_add_timeout (popup_delay, - gtk_menu_item_select_timeout, - menu_item); - if (event && - event->type != GDK_BUTTON_PRESS && - event->type != GDK_ENTER_NOTIFY) - menu_item->timer_from_keypress = TRUE; - else - menu_item->timer_from_keypress = FALSE; - - if (event) - gdk_event_free (event); - } - else - _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item)); + _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), TRUE); } - + gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT); gtk_widget_queue_draw (GTK_WIDGET (menu_item)); } @@ -955,15 +911,7 @@ gtk_real_menu_item_deselect (GtkItem *item) menu_item = GTK_MENU_ITEM (item); if (menu_item->submenu) - { - if (menu_item->timer) - { - g_source_remove (menu_item->timer); - menu_item->timer = 0; - } - else - gtk_menu_popdown (GTK_MENU (menu_item->submenu)); - } + _gtk_menu_item_popdown_submenu (GTK_WIDGET (menu_item)); gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL); gtk_widget_queue_draw (GTK_WIDGET (menu_item)); @@ -987,7 +935,6 @@ gtk_menu_item_mnemonic_activate (GtkWidget *widget, return TRUE; } - static void gtk_real_menu_item_activate_item (GtkMenuItem *menu_item) { @@ -1009,13 +956,14 @@ gtk_real_menu_item_activate_item (GtkMenuItem *menu_item) _gtk_menu_shell_activate (menu_shell); - gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget); - _gtk_menu_item_popup_submenu (widget); + gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget); + _gtk_menu_item_popup_submenu (widget, FALSE); gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE); } } } + static void gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item, gint *requisition) @@ -1034,8 +982,53 @@ gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item, menu_item->toggle_size = allocation; } +static void +gtk_menu_item_real_popup_submenu (GtkWidget *widget, + gboolean remember_exact_time) +{ + GtkMenuItem *menu_item = GTK_MENU_ITEM (widget); + + if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu)) + { + gboolean take_focus; + + take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (widget->parent)); + gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (menu_item->submenu), + take_focus); + + if (remember_exact_time) + { + GTimeVal *popup_time = g_new0 (GTimeVal, 1); + + g_get_current_time (popup_time); + + g_object_set_data_full (G_OBJECT (menu_item->submenu), + "gtk-menu-exact-popup-time", popup_time, + (GDestroyNotify) g_free); + } + else + { + g_object_set_data (G_OBJECT (menu_item->submenu), + "gtk-menu-exact-popup-time", NULL); + } + + gtk_menu_popup (GTK_MENU (menu_item->submenu), + widget->parent, + widget, + gtk_menu_item_position_menu, + menu_item, + GTK_MENU_SHELL (widget->parent)->button, + 0); + } + + /* Enable themeing of the parent menu item depending on whether + * its submenu is shown or not. + */ + gtk_widget_queue_draw (widget); +} + static gint -gtk_menu_item_select_timeout (gpointer data) +gtk_menu_item_popup_timeout (gpointer data) { GtkMenuItem *menu_item; GtkWidget *parent; @@ -1047,40 +1040,98 @@ gtk_menu_item_select_timeout (gpointer data) if ((GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->active) || (GTK_IS_MENU (parent) && GTK_MENU (parent)->torn_off)) { - _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item)); + gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), TRUE); if (menu_item->timer_from_keypress && menu_item->submenu) GTK_MENU_SHELL (menu_item->submenu)->ignore_enter = TRUE; } + menu_item->timer = 0; + return FALSE; } +static gint +get_popup_delay (GtkWidget *widget) +{ + if (GTK_IS_MENU_SHELL (widget->parent)) + { + return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (widget->parent)); + } + else + { + gint popup_delay; + + g_object_get (gtk_widget_get_settings (widget), + "gtk-menu-popup-delay", &popup_delay, + NULL); + + return popup_delay; + } +} + +void +_gtk_menu_item_popup_submenu (GtkWidget *widget, + gboolean with_delay) +{ + GtkMenuItem *menu_item = GTK_MENU_ITEM (widget); + + if (menu_item->timer) + { + g_source_remove (menu_item->timer); + menu_item->timer = 0; + with_delay = FALSE; + } + + if (with_delay) + { + gint popup_delay = get_popup_delay (widget); + + if (popup_delay > 0) + { + GdkEvent *event = gtk_get_current_event (); + + menu_item->timer = gdk_threads_add_timeout (popup_delay, + gtk_menu_item_popup_timeout, + menu_item); + + if (event && + event->type != GDK_BUTTON_PRESS && + event->type != GDK_ENTER_NOTIFY) + menu_item->timer_from_keypress = TRUE; + else + menu_item->timer_from_keypress = FALSE; + + if (event) + gdk_event_free (event); + + return; + } + } + + gtk_menu_item_real_popup_submenu (widget, FALSE); +} + void -_gtk_menu_item_popup_submenu (GtkWidget *widget) +_gtk_menu_item_popdown_submenu (GtkWidget *widget) { GtkMenuItem *menu_item; menu_item = GTK_MENU_ITEM (widget); - if (menu_item->timer) - g_source_remove (menu_item->timer); - menu_item->timer = 0; - - if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu)) + if (menu_item->submenu) { - gboolean take_focus; + g_object_set_data (G_OBJECT (menu_item->submenu), + "gtk-menu-exact-popup-time", NULL); - take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (widget->parent)); - gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (menu_item->submenu), - take_focus); + if (menu_item->timer) + { + g_source_remove (menu_item->timer); + menu_item->timer = 0; + } + else + gtk_menu_popdown (GTK_MENU (menu_item->submenu)); - gtk_menu_popup (GTK_MENU (menu_item->submenu), - widget->parent, - widget, - gtk_menu_item_position_menu, - menu_item, - GTK_MENU_SHELL (widget->parent)->button, - 0); + gtk_widget_queue_draw (widget); } } diff --git a/gtk/gtkmenuitem.h b/gtk/gtkmenuitem.h index e76087b601..84e9f43d2e 100644 --- a/gtk/gtkmenuitem.h +++ b/gtk/gtkmenuitem.h @@ -119,7 +119,9 @@ void _gtk_menu_item_refresh_accel_path (GtkMenuItem *menu_item, GtkAccelGroup *accel_group, gboolean group_changed); gboolean _gtk_menu_item_is_selectable (GtkWidget *menu_item); -void _gtk_menu_item_popup_submenu (GtkWidget *menu_item); +void _gtk_menu_item_popup_submenu (GtkWidget *menu_item, + gboolean with_delay); +void _gtk_menu_item_popdown_submenu (GtkWidget *menu_item); #ifndef GTK_DISABLE_DEPRECATED #define gtk_menu_item_right_justify(menu_item) gtk_menu_item_set_right_justified ((menu_item), TRUE) diff --git a/gtk/gtkmenushell.c b/gtk/gtkmenushell.c index 9862b5b64f..6d9ab94995 100644 --- a/gtk/gtkmenushell.c +++ b/gtk/gtkmenushell.c @@ -32,6 +32,7 @@ #include "gtkkeyhash.h" #include "gtkmain.h" #include "gtkmarshalers.h" +#include "gtkmenu.h" #include "gtkmenubar.h" #include "gtkmenuitem.h" #include "gtkmenushell.h" @@ -132,7 +133,8 @@ struct _GtkMenuShellPrivate GtkMnemonicHash *mnemonic_hash; GtkKeyHash *key_hash; - gboolean take_focus; + guint take_focus : 1; + guint activated_submenu : 1; }; static void gtk_menu_shell_set_property (GObject *object, @@ -365,6 +367,7 @@ gtk_menu_shell_init (GtkMenuShell *menu_shell) priv->mnemonic_hash = NULL; priv->key_hash = NULL; priv->take_focus = TRUE; + priv->activated_submenu = FALSE; } static void @@ -520,39 +523,48 @@ gtk_menu_shell_button_press (GtkWidget *widget, GtkMenuShell *menu_shell; GtkWidget *menu_item; - g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - if (event->type != GDK_BUTTON_PRESS) return FALSE; menu_shell = GTK_MENU_SHELL (widget); if (menu_shell->parent_menu_shell) + return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event); + + menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event); + + if (menu_item && _gtk_menu_item_is_selectable (menu_item) && + menu_item != GTK_MENU_SHELL (menu_item->parent)->active_menu_item) { - return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event); + /* 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_item->parent)->submenu_placement != GTK_TOP_BOTTOM) + { + gtk_menu_shell_select_item (GTK_MENU_SHELL (menu_item->parent), menu_item); + } } - else if (!menu_shell->active || !menu_shell->button) + + if (!menu_shell->active || !menu_shell->button) { _gtk_menu_shell_activate (menu_shell); - - menu_shell->button = event->button; - menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event); + menu_shell->button = event->button; - if (menu_item && _gtk_menu_item_is_selectable (menu_item)) - { - if ((menu_item->parent == widget) && - (menu_item != menu_shell->active_menu_item)) - { - if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM) - { - menu_shell->activate_time = event->time; - } - - gtk_menu_shell_select_item (menu_shell, menu_item); - } - } + if (menu_item && _gtk_menu_item_is_selectable (menu_item) && + menu_item->parent == widget && + menu_item != menu_shell->active_menu_item) + { + if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM) + { + menu_shell->activate_time = event->time; + gtk_menu_shell_select_item (menu_shell, menu_item); + } + } } else { @@ -564,6 +576,18 @@ gtk_menu_shell_button_press (GtkWidget *widget, } } + if (menu_item && _gtk_menu_item_is_selectable (menu_item) && + GTK_MENU_ITEM (menu_item)->submenu != NULL && + !GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (menu_item)->submenu)) + { + GtkMenuShellPrivate *priv; + + _gtk_menu_item_popup_submenu (menu_item, FALSE); + + priv = GTK_MENU_SHELL_GET_PRIVATE (menu_item->parent); + priv->activated_submenu = TRUE; + } + return TRUE; } @@ -591,16 +615,14 @@ static gint gtk_menu_shell_button_release (GtkWidget *widget, GdkEventButton *event) { - GtkMenuShell *menu_shell; - GtkWidget *menu_item; - gint deactivate; - - g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); + GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget); + GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (widget); - menu_shell = GTK_MENU_SHELL (widget); if (menu_shell->active) { + GtkWidget *menu_item; + gboolean deactivate = TRUE; + if (menu_shell->button && (event->button != menu_shell->button)) { menu_shell->button = 0; @@ -611,63 +633,108 @@ gtk_menu_shell_button_release (GtkWidget *widget, menu_shell->button = 0; menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event); - deactivate = TRUE; - if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT) - { - if (menu_item && (menu_shell->active_menu_item == menu_item) && - _gtk_menu_item_is_selectable (menu_item)) - { - if (GTK_MENU_ITEM (menu_item)->submenu == NULL) - { - gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE); - return TRUE; - } - else if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM) - { - gtk_menu_item_select (GTK_MENU_ITEM (menu_item)); - return TRUE; - } - } - else if (menu_item && - !_gtk_menu_item_is_selectable (menu_item) && - GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM) - { - deactivate = FALSE; - } - else if (menu_shell->parent_menu_shell) - { - menu_shell->active = TRUE; - gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event); - return TRUE; - } + { + if (menu_item && (menu_shell->active_menu_item == menu_item) && + _gtk_menu_item_is_selectable (menu_item)) + { + GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->submenu; - /* If we ended up on an item with a submenu, leave the menu up. - */ - if (menu_item && (menu_shell->active_menu_item == menu_item) && - GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM) - { - deactivate = FALSE; - } - } + if (submenu == NULL) + { + gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE); + + deactivate = FALSE; + } + else if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM || + priv->activated_submenu) + { + gint popdown_delay; + GTimeVal *popup_time; + gint64 usec_since_popup = 0; + + g_object_get (gtk_widget_get_settings (widget), + "gtk-menu-popdown-delay", &popdown_delay, + NULL); + + popup_time = g_object_get_data (G_OBJECT (submenu), + "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 (submenu), + "gtk-menu-exact-popup-time", NULL); + } + + /* only close the submenu on click if we opened the + * menu explicitely (usec_since_popup == 0) or + * enough time has passed since it was opened by + * GtkMenuItem's timeout (usec_since_popup > delay). + */ + if (!priv->activated_submenu && + (usec_since_popup == 0 || + usec_since_popup > popdown_delay * 1000)) + { + _gtk_menu_item_popdown_submenu (menu_item); + } + else + { + gtk_menu_item_select (GTK_MENU_ITEM (menu_item)); + } + + deactivate = FALSE; + } + } + else if (menu_item && + !_gtk_menu_item_is_selectable (menu_item) && + GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM) + { + deactivate = FALSE; + } + else if (menu_shell->parent_menu_shell) + { + menu_shell->active = TRUE; + gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event); + deactivate = FALSE; + } + + /* If we ended up on an item with a submenu, leave the menu up. + */ + if (menu_item && (menu_shell->active_menu_item == menu_item) && + GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM) + { + deactivate = FALSE; + } + } else /* a very fast press-release */ - { - /* We only ever want to prevent deactivation on the first + { + /* 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. - */ - menu_shell->activate_time = 0; - deactivate = FALSE; - } - + * 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. + */ + menu_shell->activate_time = 0; + deactivate = FALSE; + } + if (deactivate) - { - gtk_menu_shell_deactivate (menu_shell); - g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0); - } + { + gtk_menu_shell_deactivate (menu_shell); + g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0); + } + + priv->activated_submenu = FALSE; } return TRUE; @@ -706,7 +773,6 @@ gtk_menu_shell_enter_notify (GtkWidget *widget, GdkEventCrossing *event) { GtkMenuShell *menu_shell; - GtkWidget *menu_item; g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); @@ -715,6 +781,8 @@ gtk_menu_shell_enter_notify (GtkWidget *widget, if (menu_shell->active) { + GtkWidget *menu_item; + menu_item = gtk_get_event_widget ((GdkEvent*) event); if (!menu_item || @@ -733,6 +801,27 @@ gtk_menu_shell_enter_notify (GtkWidget *widget, (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT)) { gtk_menu_shell_select_item (menu_shell, menu_item); + + /* If any mouse button is down, and there is a submenu + * that is not yet visible, activate it. It's sufficient + * to check for any button's mask (not only the one + * matching menu_shell->button), because there is no + * situation a mouse button could be pressed while + * entering a menu item where we wouldn't want to show + * its submenu. + */ + if ((event->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON2_MASK)) && + GTK_MENU_ITEM (menu_item)->submenu != NULL && + !GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (menu_item)->submenu)) + { + GtkMenuShellPrivate *priv; + + priv = GTK_MENU_SHELL_GET_PRIVATE (menu_item->parent); + + _gtk_menu_item_popup_submenu (menu_item, TRUE); + + priv->activated_submenu = TRUE; + } } } else if (menu_shell->parent_menu_shell) @@ -1169,7 +1258,7 @@ gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell) if (menu_item->submenu) { - _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item)); + _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), FALSE); gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE); if (GTK_MENU_SHELL (menu_item->submenu)->active_menu_item) return TRUE; @@ -1179,8 +1268,8 @@ gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell) } static void -gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell, - GtkMenuDirectionType direction) +gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell, + GtkMenuDirectionType direction) { GtkMenuShell *parent_menu_shell = NULL; gboolean had_selection; @@ -1195,6 +1284,19 @@ gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell, case GTK_MENU_DIR_PARENT: if (parent_menu_shell) { + gboolean touchscreen_mode; + + g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)), + "gtk-touchscreen-mode", &touchscreen_mode, + NULL); + + if (touchscreen_mode) + { + /* close menu when returning from submenu. */ + _gtk_menu_item_popdown_submenu (GTK_MENU (menu_shell)->parent_menu_item); + break; + } + if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement == GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement) gtk_menu_shell_deselect (menu_shell); @@ -1276,13 +1378,12 @@ gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell, if (menu_shell->active_menu_item && _gtk_menu_item_is_selectable (menu_shell->active_menu_item)) { - if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL) gtk_menu_shell_activate_item (menu_shell, menu_shell->active_menu_item, force_hide); else - _gtk_menu_item_popup_submenu (menu_shell->active_menu_item); + _gtk_menu_item_popup_submenu (menu_shell->active_menu_item, FALSE); } } |