Apply patch from Nils Barth and David Santiago to improve submenu
Fri Sep 1 22:39:07 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkmenu.[ch] TODO.xml: Apply patch from Nils Barth and David Santiago to improve submenu navigation. The patch does this by creating a triangular region from the point where the pointer leaves the menu to the submenu. While the pointer is in that region and a timeout has not expired, events that would cause the active submenu to change are ignored.
This commit is contained in:
11
ChangeLog
11
ChangeLog
@ -1,3 +1,14 @@
|
|||||||
|
Fri Sep 1 22:39:07 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
|
* gtk/gtkmenu.[ch] TODO.xml: Apply patch from
|
||||||
|
Nils Barth and David Santiago to improve submenu
|
||||||
|
navigation. The patch does this by creating a triangular
|
||||||
|
region from the point where the pointer leaves the
|
||||||
|
menu to the submenu. While the pointer is in
|
||||||
|
that region and a timeout has not expired, events
|
||||||
|
that would cause the active submenu to change are
|
||||||
|
ignored.
|
||||||
|
|
||||||
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
Fri Sep 1 22:39:07 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
|
* gtk/gtkmenu.[ch] TODO.xml: Apply patch from
|
||||||
|
Nils Barth and David Santiago to improve submenu
|
||||||
|
navigation. The patch does this by creating a triangular
|
||||||
|
region from the point where the pointer leaves the
|
||||||
|
menu to the submenu. While the pointer is in
|
||||||
|
that region and a timeout has not expired, events
|
||||||
|
that would cause the active submenu to change are
|
||||||
|
ignored.
|
||||||
|
|
||||||
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
Fri Sep 1 22:39:07 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
|
* gtk/gtkmenu.[ch] TODO.xml: Apply patch from
|
||||||
|
Nils Barth and David Santiago to improve submenu
|
||||||
|
navigation. The patch does this by creating a triangular
|
||||||
|
region from the point where the pointer leaves the
|
||||||
|
menu to the submenu. While the pointer is in
|
||||||
|
that region and a timeout has not expired, events
|
||||||
|
that would cause the active submenu to change are
|
||||||
|
ignored.
|
||||||
|
|
||||||
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
Fri Sep 1 22:39:07 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
|
* gtk/gtkmenu.[ch] TODO.xml: Apply patch from
|
||||||
|
Nils Barth and David Santiago to improve submenu
|
||||||
|
navigation. The patch does this by creating a triangular
|
||||||
|
region from the point where the pointer leaves the
|
||||||
|
menu to the submenu. While the pointer is in
|
||||||
|
that region and a timeout has not expired, events
|
||||||
|
that would cause the active submenu to change are
|
||||||
|
ignored.
|
||||||
|
|
||||||
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
Fri Sep 1 22:39:07 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
|
* gtk/gtkmenu.[ch] TODO.xml: Apply patch from
|
||||||
|
Nils Barth and David Santiago to improve submenu
|
||||||
|
navigation. The patch does this by creating a triangular
|
||||||
|
region from the point where the pointer leaves the
|
||||||
|
menu to the submenu. While the pointer is in
|
||||||
|
that region and a timeout has not expired, events
|
||||||
|
that would cause the active submenu to change are
|
||||||
|
ignored.
|
||||||
|
|
||||||
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
Fri Sep 1 22:39:07 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
|
* gtk/gtkmenu.[ch] TODO.xml: Apply patch from
|
||||||
|
Nils Barth and David Santiago to improve submenu
|
||||||
|
navigation. The patch does this by creating a triangular
|
||||||
|
region from the point where the pointer leaves the
|
||||||
|
menu to the submenu. While the pointer is in
|
||||||
|
that region and a timeout has not expired, events
|
||||||
|
that would cause the active submenu to change are
|
||||||
|
ignored.
|
||||||
|
|
||||||
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
Fri Sep 1 22:39:07 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
|
* gtk/gtkmenu.[ch] TODO.xml: Apply patch from
|
||||||
|
Nils Barth and David Santiago to improve submenu
|
||||||
|
navigation. The patch does this by creating a triangular
|
||||||
|
region from the point where the pointer leaves the
|
||||||
|
menu to the submenu. While the pointer is in
|
||||||
|
that region and a timeout has not expired, events
|
||||||
|
that would cause the active submenu to change are
|
||||||
|
ignored.
|
||||||
|
|
||||||
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
Fri Sep 1 15:34:46 2000 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
* gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where
|
||||||
|
2
TODO.xml
2
TODO.xml
@ -441,7 +441,7 @@
|
|||||||
<contact>gtk-devel-list@gnome.org</contact>
|
<contact>gtk-devel-list@gnome.org</contact>
|
||||||
</entry>
|
</entry>
|
||||||
|
|
||||||
<entry size="small" status="40%" target="2.0">
|
<entry size="small" status="99%" target="2.0">
|
||||||
<title>Improve Submenu Navigation</title>
|
<title>Improve Submenu Navigation</title>
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
|
328
gtk/gtkmenu.c
328
gtk/gtkmenu.c
@ -38,6 +38,9 @@
|
|||||||
#define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_GET_CLASS (w)
|
#define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_GET_CLASS (w)
|
||||||
#define MENU_NEEDS_RESIZE(m) GTK_MENU_SHELL (m)->menu_flag
|
#define MENU_NEEDS_RESIZE(m) GTK_MENU_SHELL (m)->menu_flag
|
||||||
|
|
||||||
|
#define SUBMENU_NAV_REGION_PADDING 2
|
||||||
|
#define SUBMENU_NAV_HYSTERESIS_TIMEOUT 333
|
||||||
|
|
||||||
typedef struct _GtkMenuAttachData GtkMenuAttachData;
|
typedef struct _GtkMenuAttachData GtkMenuAttachData;
|
||||||
|
|
||||||
struct _GtkMenuAttachData
|
struct _GtkMenuAttachData
|
||||||
@ -47,23 +50,37 @@ struct _GtkMenuAttachData
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static void gtk_menu_class_init (GtkMenuClass *klass);
|
static void gtk_menu_class_init (GtkMenuClass *klass);
|
||||||
static void gtk_menu_init (GtkMenu *menu);
|
static void gtk_menu_init (GtkMenu *menu);
|
||||||
static void gtk_menu_destroy (GtkObject *object);
|
static void gtk_menu_destroy (GtkObject *object);
|
||||||
static void gtk_menu_realize (GtkWidget *widget);
|
static void gtk_menu_realize (GtkWidget *widget);
|
||||||
static void gtk_menu_size_request (GtkWidget *widget,
|
static void gtk_menu_size_request (GtkWidget *widget,
|
||||||
GtkRequisition *requisition);
|
GtkRequisition *requisition);
|
||||||
static void gtk_menu_size_allocate (GtkWidget *widget,
|
static void gtk_menu_size_allocate (GtkWidget *widget,
|
||||||
GtkAllocation *allocation);
|
GtkAllocation *allocation);
|
||||||
static void gtk_menu_paint (GtkWidget *widget);
|
static void gtk_menu_paint (GtkWidget *widget);
|
||||||
static void gtk_menu_draw (GtkWidget *widget,
|
static void gtk_menu_draw (GtkWidget *widget,
|
||||||
GdkRectangle *area);
|
GdkRectangle *area);
|
||||||
static gint gtk_menu_expose (GtkWidget *widget,
|
static gboolean gtk_menu_expose (GtkWidget *widget,
|
||||||
GdkEventExpose *event);
|
GdkEventExpose *event);
|
||||||
static gint gtk_menu_key_press (GtkWidget *widget,
|
static gboolean gtk_menu_key_press (GtkWidget *widget,
|
||||||
GdkEventKey *event);
|
GdkEventKey *event);
|
||||||
static gint gtk_menu_motion_notify (GtkWidget *widget,
|
static gboolean gtk_menu_motion_notify (GtkWidget *widget,
|
||||||
GdkEventMotion *event);
|
GdkEventMotion *event);
|
||||||
|
static gboolean gtk_menu_enter_notify (GtkWidget *widget,
|
||||||
|
GdkEventCrossing *event);
|
||||||
|
static gboolean gtk_menu_leave_notify (GtkWidget *widget,
|
||||||
|
GdkEventCrossing *event);
|
||||||
|
|
||||||
|
static void gtk_menu_stop_navigating_submenu (GtkMenu *menu);
|
||||||
|
static gboolean gtk_menu_stop_navigating_submenu_cb (gpointer user_data);
|
||||||
|
static gboolean gtk_menu_navigating_submenu (GtkMenu *menu,
|
||||||
|
gint event_x,
|
||||||
|
gint event_y);
|
||||||
|
static void gtk_menu_set_submenu_navigation_region (GtkMenu *menu,
|
||||||
|
GtkMenuItem *menu_item,
|
||||||
|
GdkEventCrossing *event);
|
||||||
|
|
||||||
static void gtk_menu_deactivate (GtkMenuShell *menu_shell);
|
static void gtk_menu_deactivate (GtkMenuShell *menu_shell);
|
||||||
static void gtk_menu_show_all (GtkWidget *widget);
|
static void gtk_menu_show_all (GtkWidget *widget);
|
||||||
static void gtk_menu_hide_all (GtkWidget *widget);
|
static void gtk_menu_hide_all (GtkWidget *widget);
|
||||||
@ -76,7 +93,6 @@ static GtkMenuShellClass *parent_class = NULL;
|
|||||||
static const gchar *attach_data_key = "gtk-menu-attach-data";
|
static const gchar *attach_data_key = "gtk-menu-attach-data";
|
||||||
static GQuark quark_uline_accel_group = 0;
|
static GQuark quark_uline_accel_group = 0;
|
||||||
|
|
||||||
|
|
||||||
GtkType
|
GtkType
|
||||||
gtk_menu_get_type (void)
|
gtk_menu_get_type (void)
|
||||||
{
|
{
|
||||||
@ -129,6 +145,8 @@ gtk_menu_class_init (GtkMenuClass *class)
|
|||||||
widget_class->motion_notify_event = gtk_menu_motion_notify;
|
widget_class->motion_notify_event = gtk_menu_motion_notify;
|
||||||
widget_class->show_all = gtk_menu_show_all;
|
widget_class->show_all = gtk_menu_show_all;
|
||||||
widget_class->hide_all = gtk_menu_hide_all;
|
widget_class->hide_all = gtk_menu_hide_all;
|
||||||
|
widget_class->enter_notify_event = gtk_menu_enter_notify;
|
||||||
|
widget_class->leave_notify_event = gtk_menu_leave_notify;
|
||||||
|
|
||||||
menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
|
menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
|
||||||
menu_shell_class->deactivate = gtk_menu_deactivate;
|
menu_shell_class->deactivate = gtk_menu_deactivate;
|
||||||
@ -156,7 +174,7 @@ gtk_menu_class_init (GtkMenuClass *class)
|
|||||||
GTK_MENU_DIR_CHILD);
|
GTK_MENU_DIR_CHILD);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gboolean
|
||||||
gtk_menu_window_event (GtkWidget *window,
|
gtk_menu_window_event (GtkWidget *window,
|
||||||
GdkEvent *event,
|
GdkEvent *event,
|
||||||
GtkWidget *menu)
|
GtkWidget *menu)
|
||||||
@ -227,6 +245,8 @@ gtk_menu_destroy (GtkObject *object)
|
|||||||
if (data)
|
if (data)
|
||||||
gtk_menu_detach (menu);
|
gtk_menu_detach (menu);
|
||||||
|
|
||||||
|
gtk_menu_stop_navigating_submenu (menu);
|
||||||
|
|
||||||
gtk_menu_set_accel_group (menu, NULL);
|
gtk_menu_set_accel_group (menu, NULL);
|
||||||
|
|
||||||
if (menu->old_active_menu_item)
|
if (menu->old_active_menu_item)
|
||||||
@ -524,6 +544,8 @@ gtk_menu_popdown (GtkMenu *menu)
|
|||||||
menu_shell->active = FALSE;
|
menu_shell->active = FALSE;
|
||||||
menu_shell->ignore_enter = FALSE;
|
menu_shell->ignore_enter = FALSE;
|
||||||
|
|
||||||
|
gtk_menu_stop_navigating_submenu (menu);
|
||||||
|
|
||||||
if (menu_shell->active_menu_item)
|
if (menu_shell->active_menu_item)
|
||||||
{
|
{
|
||||||
if (menu->old_active_menu_item)
|
if (menu->old_active_menu_item)
|
||||||
@ -558,7 +580,7 @@ gtk_menu_popdown (GtkMenu *menu)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
gtk_widget_hide (GTK_WIDGET (menu));
|
gtk_widget_hide (GTK_WIDGET (menu));
|
||||||
|
|
||||||
menu_shell->have_xgrab = FALSE;
|
menu_shell->have_xgrab = FALSE;
|
||||||
gtk_grab_remove (GTK_WIDGET (menu));
|
gtk_grab_remove (GTK_WIDGET (menu));
|
||||||
}
|
}
|
||||||
@ -978,7 +1000,7 @@ gtk_menu_draw (GtkWidget *widget,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gboolean
|
||||||
gtk_menu_expose (GtkWidget *widget,
|
gtk_menu_expose (GtkWidget *widget,
|
||||||
GdkEventExpose *event)
|
GdkEventExpose *event)
|
||||||
{
|
{
|
||||||
@ -1016,7 +1038,7 @@ gtk_menu_expose (GtkWidget *widget,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gboolean
|
||||||
gtk_menu_key_press (GtkWidget *widget,
|
gtk_menu_key_press (GtkWidget *widget,
|
||||||
GdkEventKey *event)
|
GdkEventKey *event)
|
||||||
{
|
{
|
||||||
@ -1029,6 +1051,8 @@ gtk_menu_key_press (GtkWidget *widget,
|
|||||||
|
|
||||||
menu_shell = GTK_MENU_SHELL (widget);
|
menu_shell = GTK_MENU_SHELL (widget);
|
||||||
|
|
||||||
|
gtk_menu_stop_navigating_submenu (GTK_MENU (widget));
|
||||||
|
|
||||||
if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
|
if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
@ -1102,19 +1126,48 @@ gtk_menu_key_press (GtkWidget *widget,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gboolean
|
||||||
gtk_menu_motion_notify (GtkWidget *widget,
|
gtk_menu_motion_notify (GtkWidget *widget,
|
||||||
GdkEventMotion *event)
|
GdkEventMotion *event)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (widget != NULL, FALSE);
|
GtkWidget *menu_item;
|
||||||
g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
|
GtkMenu *menu;
|
||||||
|
GtkMenuShell *menu_shell;
|
||||||
if (GTK_MENU_SHELL (widget)->ignore_enter)
|
|
||||||
GTK_MENU_SHELL (widget)->ignore_enter = FALSE;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gint width, height;
|
|
||||||
|
|
||||||
|
gboolean need_enter;
|
||||||
|
|
||||||
|
/* We received the event for one of two reasons:
|
||||||
|
*
|
||||||
|
* a) We are the active menu, and did gtk_grab_add()
|
||||||
|
* b) The widget is a child of ours, and the event was propagated
|
||||||
|
*
|
||||||
|
* Since for computation of navigation regions, we want the menu which
|
||||||
|
* is the parent of the menu item, for a), we need to find that menu,
|
||||||
|
* which may be different from 'widget'.
|
||||||
|
*/
|
||||||
|
|
||||||
|
menu_item = gtk_get_event_widget ((GdkEvent*) event);
|
||||||
|
if (!menu_item || !GTK_IS_MENU_ITEM (menu_item) || !GTK_WIDGET_IS_SENSITIVE (menu_item) ||
|
||||||
|
!GTK_IS_MENU (menu_item->parent))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
menu_shell = GTK_MENU_SHELL (menu_item->parent);
|
||||||
|
menu = GTK_MENU (menu_shell);
|
||||||
|
|
||||||
|
need_enter = (menu->navigation_region != NULL || menu_shell->ignore_enter);
|
||||||
|
|
||||||
|
/* Check to see if we are within an active submenu's navigation region
|
||||||
|
*/
|
||||||
|
if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (need_enter)
|
||||||
|
{
|
||||||
|
/* The menu is now sensitive to enter events on its items, but
|
||||||
|
* was previously sensitive. So we fake an enter event.
|
||||||
|
*/
|
||||||
|
gint width, height;
|
||||||
|
|
||||||
gdk_window_get_size (event->window, &width, &height);
|
gdk_window_get_size (event->window, &width, &height);
|
||||||
if (event->x >= 0 && event->x < width &&
|
if (event->x >= 0 && event->x < width &&
|
||||||
event->y >= 0 && event->y < height)
|
event->y >= 0 && event->y < height)
|
||||||
@ -1125,14 +1178,222 @@ gtk_menu_motion_notify (GtkWidget *widget,
|
|||||||
send_event.crossing.window = event->window;
|
send_event.crossing.window = event->window;
|
||||||
send_event.crossing.time = event->time;
|
send_event.crossing.time = event->time;
|
||||||
send_event.crossing.send_event = TRUE;
|
send_event.crossing.send_event = TRUE;
|
||||||
|
send_event.crossing.x_root = event->x_root;
|
||||||
gtk_widget_event (widget, &send_event);
|
send_event.crossing.y_root = event->y_root;
|
||||||
|
send_event.crossing.x = event->x;
|
||||||
|
send_event.crossing.y = event->y;
|
||||||
|
|
||||||
|
/* We send the event to 'widget', the currently active menu,
|
||||||
|
* instead of 'menu', the menu that the pointer is in. This
|
||||||
|
* will ensure that the event will be ignored unless the
|
||||||
|
* menuitem is a child of the active menu or some parent
|
||||||
|
* menu of the active menu.
|
||||||
|
*/
|
||||||
|
return gtk_widget_event (widget, &send_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu_shell->ignore_enter = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gtk_menu_enter_notify (GtkWidget *widget,
|
||||||
|
GdkEventCrossing *event)
|
||||||
|
{
|
||||||
|
GtkWidget *menu_item;
|
||||||
|
|
||||||
|
/* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
|
||||||
|
* will not correspond to the event widget's parent. Check to see
|
||||||
|
* if we are in the parent's navigation region.
|
||||||
|
*/
|
||||||
|
menu_item = gtk_get_event_widget ((GdkEvent*) event);
|
||||||
|
if (menu_item && GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (menu_item->parent) &&
|
||||||
|
gtk_menu_navigating_submenu (GTK_MENU (menu_item->parent), event->x_root, event->y_root))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return GTK_WIDGET_CLASS (parent_class)->enter_notify_event (widget, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gtk_menu_leave_notify (GtkWidget *widget,
|
||||||
|
GdkEventCrossing *event)
|
||||||
|
{
|
||||||
|
GtkMenuShell *menu_shell;
|
||||||
|
GtkMenu *menu;
|
||||||
|
GtkMenuItem *menu_item;
|
||||||
|
GtkWidget *event_widget;
|
||||||
|
|
||||||
|
menu = GTK_MENU (widget);
|
||||||
|
menu_shell = GTK_MENU_SHELL (widget);
|
||||||
|
|
||||||
|
if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
event_widget = gtk_get_event_widget ((GdkEvent*) event);
|
||||||
|
|
||||||
|
if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
menu_item = GTK_MENU_ITEM (event_widget);
|
||||||
|
|
||||||
|
/* Here we check to see if we're leaving an active menu item with a submenu,
|
||||||
|
* in which case we enter submenu navigation mode.
|
||||||
|
*/
|
||||||
|
if (menu_shell->active_menu_item != NULL
|
||||||
|
&& menu_item->submenu != NULL
|
||||||
|
&& menu_item->submenu_placement == GTK_LEFT_RIGHT)
|
||||||
|
{
|
||||||
|
if (menu_item->submenu->window != NULL)
|
||||||
|
{
|
||||||
|
gtk_menu_set_submenu_navigation_region (menu, menu_item, event);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gtk_menu_stop_navigating_submenu (GtkMenu *menu)
|
||||||
|
{
|
||||||
|
if (menu->navigation_region)
|
||||||
|
{
|
||||||
|
gdk_region_destroy (menu->navigation_region);
|
||||||
|
menu->navigation_region = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu->navigation_timeout)
|
||||||
|
{
|
||||||
|
gtk_timeout_remove (menu->navigation_timeout);
|
||||||
|
menu->navigation_timeout = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When the timeout is elapsed, the navigation region is destroyed
|
||||||
|
* and the menuitem under the pointer (if any) is selected.
|
||||||
|
*/
|
||||||
|
static gboolean
|
||||||
|
gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
GdkEventCrossing send_event;
|
||||||
|
|
||||||
|
GtkMenu *menu = user_data;
|
||||||
|
GdkWindow *child_window;
|
||||||
|
|
||||||
|
gtk_menu_stop_navigating_submenu (menu);
|
||||||
|
|
||||||
|
if (GTK_WIDGET_REALIZED (menu))
|
||||||
|
{
|
||||||
|
child_window = gdk_window_get_pointer (GTK_WIDGET (menu)->window, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if (child_window)
|
||||||
|
{
|
||||||
|
send_event.window = child_window;
|
||||||
|
send_event.type = GDK_ENTER_NOTIFY;
|
||||||
|
send_event.time = GDK_CURRENT_TIME; /* Bogus */
|
||||||
|
send_event.send_event = TRUE;
|
||||||
|
|
||||||
|
GTK_WIDGET_CLASS (parent_class)->enter_notify_event (GTK_WIDGET (menu), &send_event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gtk_menu_navigating_submenu (GtkMenu *menu,
|
||||||
|
gint event_x,
|
||||||
|
gint event_y)
|
||||||
|
{
|
||||||
|
if (menu->navigation_region)
|
||||||
|
{
|
||||||
|
if (gdk_region_point_in (menu->navigation_region, event_x, event_y))
|
||||||
|
return TRUE;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gtk_menu_stop_navigating_submenu (menu);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gtk_menu_set_submenu_navigation_region (GtkMenu *menu,
|
||||||
|
GtkMenuItem *menu_item,
|
||||||
|
GdkEventCrossing *event)
|
||||||
|
{
|
||||||
|
gint submenu_left = 0;
|
||||||
|
gint submenu_right = 0;
|
||||||
|
gint submenu_top = 0;
|
||||||
|
gint submenu_bottom = 0;
|
||||||
|
gint width = 0;
|
||||||
|
gint height = 0;
|
||||||
|
GdkPoint point[3];
|
||||||
|
GtkWidget *event_widget;
|
||||||
|
|
||||||
|
g_return_if_fail (menu_item->submenu != NULL);
|
||||||
|
g_return_if_fail (event != NULL);
|
||||||
|
|
||||||
|
event_widget = gtk_get_event_widget ((GdkEvent*) event);
|
||||||
|
|
||||||
|
gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top);
|
||||||
|
gdk_window_get_size (menu_item->submenu->window, &width, &height);
|
||||||
|
submenu_right = submenu_left + width;
|
||||||
|
submenu_bottom = submenu_top + height;
|
||||||
|
|
||||||
|
gdk_window_get_size (event_widget->window, &width, &height);
|
||||||
|
|
||||||
|
if (event->x >= 0 && event->x < width)
|
||||||
|
{
|
||||||
|
/* Set navigation region */
|
||||||
|
/* We fudge/give a little padding in case the user
|
||||||
|
* ``misses the vertex'' of the triangle/is off by a pixel or two.
|
||||||
|
*/
|
||||||
|
if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
|
||||||
|
point[0].x = event->x_root - SUBMENU_NAV_REGION_PADDING;
|
||||||
|
else
|
||||||
|
point[0].x = event->x_root + SUBMENU_NAV_REGION_PADDING;
|
||||||
|
|
||||||
|
/* Exiting the top or bottom? */
|
||||||
|
if (event->y < 0)
|
||||||
|
{ /* top */
|
||||||
|
point[0].y = event->y_root + SUBMENU_NAV_REGION_PADDING;
|
||||||
|
point[1].y = submenu_top;
|
||||||
|
|
||||||
|
if (point[0].y <= point[1].y)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* bottom */
|
||||||
|
point[0].y = event->y_root - SUBMENU_NAV_REGION_PADDING;
|
||||||
|
point[1].y = submenu_bottom;
|
||||||
|
|
||||||
|
if (point[0].y >= point[1].y)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Submenu is to the left or right? */
|
||||||
|
if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
|
||||||
|
point[1].x = submenu_left; /* right */
|
||||||
|
else
|
||||||
|
point[1].x = submenu_right; /* left */
|
||||||
|
|
||||||
|
point[2].x = point[1].x;
|
||||||
|
point[2].y = point[0].y;
|
||||||
|
|
||||||
|
if (menu->navigation_region)
|
||||||
|
gdk_region_destroy (menu->navigation_region);
|
||||||
|
|
||||||
|
menu->navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE);
|
||||||
|
|
||||||
|
menu->navigation_timeout = gtk_timeout_add (SUBMENU_NAV_HYSTERESIS_TIMEOUT,
|
||||||
|
gtk_menu_stop_navigating_submenu_cb, menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_menu_deactivate (GtkMenuShell *menu_shell)
|
gtk_menu_deactivate (GtkMenuShell *menu_shell)
|
||||||
{
|
{
|
||||||
@ -1150,7 +1411,6 @@ gtk_menu_deactivate (GtkMenuShell *menu_shell)
|
|||||||
gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
|
gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_menu_position (GtkMenu *menu)
|
gtk_menu_position (GtkMenu *menu)
|
||||||
{
|
{
|
||||||
|
@ -74,6 +74,12 @@ struct _GtkMenu
|
|||||||
GtkWidget *toplevel;
|
GtkWidget *toplevel;
|
||||||
GtkWidget *tearoff_window;
|
GtkWidget *tearoff_window;
|
||||||
|
|
||||||
|
/* When a submenu of this menu is popped up, motion in this
|
||||||
|
* region is ignored
|
||||||
|
*/
|
||||||
|
GdkRegion *navigation_region;
|
||||||
|
guint navigation_timeout;
|
||||||
|
|
||||||
guint needs_destruction_ref_count : 1;
|
guint needs_destruction_ref_count : 1;
|
||||||
guint torn_off : 1;
|
guint torn_off : 1;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user