diff options
author | Owen Taylor <otaylor@redhat.com> | 2000-09-02 02:43:50 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2000-09-02 02:43:50 +0000 |
commit | e4229e9c4bd815717d1f9056a53c3389d4b20122 (patch) | |
tree | d5210323b1e245c81c03c706665bb9e51500e399 | |
parent | 6e3a2369b3703653389d2a123a882f3cb632abd9 (diff) | |
download | gtk+-e4229e9c4bd815717d1f9056a53c3389d4b20122.tar.gz |
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.
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | ChangeLog.pre-2-0 | 11 | ||||
-rw-r--r-- | ChangeLog.pre-2-10 | 11 | ||||
-rw-r--r-- | ChangeLog.pre-2-2 | 11 | ||||
-rw-r--r-- | ChangeLog.pre-2-4 | 11 | ||||
-rw-r--r-- | ChangeLog.pre-2-6 | 11 | ||||
-rw-r--r-- | ChangeLog.pre-2-8 | 11 | ||||
-rw-r--r-- | TODO.xml | 2 | ||||
-rw-r--r-- | gtk/gtkmenu.c | 324 | ||||
-rw-r--r-- | gtk/gtkmenu.h | 6 |
10 files changed, 376 insertions, 33 deletions
@@ -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> * gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index f18fe87bdd..d9e467747e 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -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> * gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index f18fe87bdd..d9e467747e 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -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> * gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index f18fe87bdd..d9e467747e 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -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> * gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index f18fe87bdd..d9e467747e 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -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> * gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index f18fe87bdd..d9e467747e 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -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> * gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index f18fe87bdd..d9e467747e 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -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> * gdk/x11/gdkwindow-x11.c (gdk_window_move): Fix bug where @@ -441,7 +441,7 @@ <contact>gtk-devel-list@gnome.org</contact> </entry> - <entry size="small" status="40%" target="2.0"> + <entry size="small" status="99%" target="2.0"> <title>Improve Submenu Navigation</title> <description> <p> diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index 7a8f64d075..9342242d37 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -38,6 +38,9 @@ #define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_GET_CLASS (w) #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; struct _GtkMenuAttachData @@ -47,23 +50,37 @@ struct _GtkMenuAttachData }; -static void gtk_menu_class_init (GtkMenuClass *klass); -static void gtk_menu_init (GtkMenu *menu); -static void gtk_menu_destroy (GtkObject *object); -static void gtk_menu_realize (GtkWidget *widget); -static void gtk_menu_size_request (GtkWidget *widget, - GtkRequisition *requisition); -static void gtk_menu_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); -static void gtk_menu_paint (GtkWidget *widget); -static void gtk_menu_draw (GtkWidget *widget, - GdkRectangle *area); -static gint gtk_menu_expose (GtkWidget *widget, - GdkEventExpose *event); -static gint gtk_menu_key_press (GtkWidget *widget, - GdkEventKey *event); -static gint gtk_menu_motion_notify (GtkWidget *widget, - GdkEventMotion *event); +static void gtk_menu_class_init (GtkMenuClass *klass); +static void gtk_menu_init (GtkMenu *menu); +static void gtk_menu_destroy (GtkObject *object); +static void gtk_menu_realize (GtkWidget *widget); +static void gtk_menu_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_menu_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void gtk_menu_paint (GtkWidget *widget); +static void gtk_menu_draw (GtkWidget *widget, + GdkRectangle *area); +static gboolean gtk_menu_expose (GtkWidget *widget, + GdkEventExpose *event); +static gboolean gtk_menu_key_press (GtkWidget *widget, + GdkEventKey *event); +static gboolean gtk_menu_motion_notify (GtkWidget *widget, + 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_show_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 GQuark quark_uline_accel_group = 0; - GtkType 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->show_all = gtk_menu_show_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->deactivate = gtk_menu_deactivate; @@ -156,7 +174,7 @@ gtk_menu_class_init (GtkMenuClass *class) GTK_MENU_DIR_CHILD); } -static gint +static gboolean gtk_menu_window_event (GtkWidget *window, GdkEvent *event, GtkWidget *menu) @@ -227,6 +245,8 @@ gtk_menu_destroy (GtkObject *object) if (data) gtk_menu_detach (menu); + gtk_menu_stop_navigating_submenu (menu); + gtk_menu_set_accel_group (menu, NULL); if (menu->old_active_menu_item) @@ -524,6 +544,8 @@ gtk_menu_popdown (GtkMenu *menu) menu_shell->active = FALSE; menu_shell->ignore_enter = FALSE; + gtk_menu_stop_navigating_submenu (menu); + if (menu_shell->active_menu_item) { if (menu->old_active_menu_item) @@ -558,7 +580,7 @@ gtk_menu_popdown (GtkMenu *menu) } else gtk_widget_hide (GTK_WIDGET (menu)); - + menu_shell->have_xgrab = FALSE; gtk_grab_remove (GTK_WIDGET (menu)); } @@ -978,7 +1000,7 @@ gtk_menu_draw (GtkWidget *widget, } } -static gint +static gboolean gtk_menu_expose (GtkWidget *widget, GdkEventExpose *event) { @@ -1016,7 +1038,7 @@ gtk_menu_expose (GtkWidget *widget, return FALSE; } -static gint +static gboolean gtk_menu_key_press (GtkWidget *widget, GdkEventKey *event) { @@ -1029,6 +1051,8 @@ gtk_menu_key_press (GtkWidget *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)) return TRUE; @@ -1102,19 +1126,48 @@ gtk_menu_key_press (GtkWidget *widget, return TRUE; } -static gint +static gboolean gtk_menu_motion_notify (GtkWidget *widget, GdkEventMotion *event) { - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_MENU (widget), FALSE); + GtkWidget *menu_item; + GtkMenu *menu; + GtkMenuShell *menu_shell; + + 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'. + */ - if (GTK_MENU_SHELL (widget)->ignore_enter) - GTK_MENU_SHELL (widget)->ignore_enter = FALSE; - else + 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); if (event->x >= 0 && event->x < width && event->y >= 0 && event->y < height) @@ -1125,15 +1178,223 @@ gtk_menu_motion_notify (GtkWidget *widget, send_event.crossing.window = event->window; send_event.crossing.time = event->time; send_event.crossing.send_event = TRUE; - - gtk_widget_event (widget, &send_event); + send_event.crossing.x_root = event->x_root; + 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; +} + +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 gtk_menu_deactivate (GtkMenuShell *menu_shell) { GtkWidget *parent; @@ -1150,7 +1411,6 @@ gtk_menu_deactivate (GtkMenuShell *menu_shell) gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent)); } - static void gtk_menu_position (GtkMenu *menu) { diff --git a/gtk/gtkmenu.h b/gtk/gtkmenu.h index 7f0420daac..741a37ea54 100644 --- a/gtk/gtkmenu.h +++ b/gtk/gtkmenu.h @@ -74,6 +74,12 @@ struct _GtkMenu GtkWidget *toplevel; 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 torn_off : 1; }; |