From 14e0cbe2d35215fb299e84c193d3438dfbcc7a88 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 16 Jun 2010 13:14:01 +0200 Subject: [PATCH] Get rid of navigation region in GtkMenu This completes the move to get rid of using a GdkRegion for the navigation region and the only user of gdk_region_polygon(). We keep track of the triangle and compute in/out points ourselves now. Unfortunately the DRAW_STAYUP_TRIANGLES debugging code doesn't work using cairo, so I removed it completely. --- gtk/gtkmenu.c | 148 +++++++++++++++++++++++++++----------------------- gtk/gtkmenu.h | 2 +- 2 files changed, 82 insertions(+), 68 deletions(-) diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index 3835e7300a..c9b23827a7 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -94,6 +94,12 @@ struct _GtkMenuPrivate GtkStateType lower_arrow_state; GtkStateType upper_arrow_state; + /* navigation region */ + int navigation_x; + int navigation_y; + int navigation_width; + int navigation_height; + guint have_layout : 1; guint seen_item_enter : 1; guint have_position : 1; @@ -3325,6 +3331,16 @@ definitely_within_item (GtkWidget *widget, check_threshold (widget, 0, h - 1, x, y); } +static gboolean +gtk_menu_has_navigation_triangle (GtkMenu *menu) +{ + GtkMenuPrivate *priv; + + priv = gtk_menu_get_private (menu); + + return priv->navigation_height && priv->navigation_width; +} + static gboolean gtk_menu_motion_notify (GtkWidget *widget, GdkEventMotion *event) @@ -3366,7 +3382,7 @@ gtk_menu_motion_notify (GtkWidget *widget, if (definitely_within_item (menu_item, event->x, event->y)) menu_shell->activate_time = 0; - need_enter = (menu->navigation_region != NULL || menu_shell->ignore_enter); + need_enter = (gtk_menu_has_navigation_triangle (menu) || menu_shell->ignore_enter); /* Check to see if we are within an active submenu's navigation region */ @@ -4066,11 +4082,13 @@ gtk_menu_leave_notify (GtkWidget *widget, static void gtk_menu_stop_navigating_submenu (GtkMenu *menu) { - if (menu->navigation_region) - { - gdk_region_destroy (menu->navigation_region); - menu->navigation_region = NULL; - } + GtkMenuPrivate *priv = gtk_menu_get_private (menu); + + priv->navigation_x = 0; + priv->navigation_y = 0; + priv->navigation_width = 0; + priv->navigation_height = 0; + if (menu->navigation_timeout) { g_source_remove (menu->navigation_timeout); @@ -4119,50 +4137,49 @@ gtk_menu_navigating_submenu (GtkMenu *menu, gint event_x, gint event_y) { - if (menu->navigation_region) + GtkMenuPrivate *priv; + int width, height; + + if (!gtk_menu_has_navigation_triangle (menu)) + return FALSE; + + priv = gtk_menu_get_private (menu); + width = priv->navigation_width; + height = priv->navigation_height; + + /* check if x/y are in the triangle spanned by the navigation parameters */ + + /* 1) Move the coordinates so the triangle starts at 0,0 */ + event_x -= priv->navigation_x; + event_y -= priv->navigation_y; + + /* 2) Ensure both legs move along the positive axis */ + if (width < 0) { - if (gdk_region_point_in (menu->navigation_region, event_x, event_y)) - return TRUE; - else - { - gtk_menu_stop_navigating_submenu (menu); - return FALSE; - } + event_x = -event_x; + width = -width; + } + if (height < 0) + { + event_y = -event_y; + height = -height; + } + + /* 3) Check that the given coordinate is inside the triangle. The formula + * is a transformed form of this formula: x/w + y/h <= 1 + */ + if (event_x >= 0 && event_y >= 0 && + event_x * height + event_y * width <= width * height) + { + return TRUE; + } + else + { + gtk_menu_stop_navigating_submenu (menu); + return FALSE; } - return FALSE; } -#undef DRAW_STAY_UP_TRIANGLE - -#ifdef DRAW_STAY_UP_TRIANGLE - -static void -draw_stay_up_triangle (GdkWindow *window, - GdkRegion *region) -{ - /* Draw ugly color all over the stay-up triangle */ - GdkColor ugly_color = { 0, 50000, 10000, 10000 }; - GdkGCValues gc_values; - GdkGC *ugly_gc; - GdkRectangle clipbox; - - gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS; - ugly_gc = gdk_gc_new_with_values (window, &gc_values, 0 | GDK_GC_SUBWINDOW); - gdk_gc_set_rgb_fg_color (ugly_gc, &ugly_color); - gdk_gc_set_clip_region (ugly_gc, region); - - gdk_region_get_clipbox (region, &clipbox); - - gdk_draw_rectangle (window, - ugly_gc, - TRUE, - clipbox.x, clipbox.y, - clipbox.width, clipbox.height); - - g_object_unref (ugly_gc); -} -#endif - static void gtk_menu_set_submenu_navigation_region (GtkMenu *menu, GtkMenuItem *menu_item, @@ -4174,13 +4191,15 @@ gtk_menu_set_submenu_navigation_region (GtkMenu *menu, gint submenu_bottom = 0; gint width = 0; gint height = 0; - GdkPoint point[3]; GtkWidget *event_widget; GtkMenuPopdownData *popdown_data; + GtkMenuPrivate *priv; g_return_if_fail (menu_item->submenu != NULL); g_return_if_fail (event != NULL); + priv = gtk_menu_get_private (menu); + event_widget = gtk_get_event_widget ((GdkEvent*) event); gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top); @@ -4197,43 +4216,43 @@ gtk_menu_set_submenu_navigation_region (GtkMenu *menu, gtk_menu_stop_navigating_submenu (menu); + /* The navigation region is the triangle closest to the x/y + * location of the rectangle. This is why the width or height + * can be negative. + */ + if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT) { /* right */ - point[0].x = event->x_root; - point[1].x = submenu_left; + priv->navigation_x = submenu_left; + priv->navigation_width = event->x_root - submenu_left; } else { /* left */ - point[0].x = event->x_root + 1; - point[1].x = submenu_right; + priv->navigation_x = submenu_right; + priv->navigation_width = event->x_root - submenu_right; } if (event->y < 0) { /* top */ - point[0].y = event->y_root; - point[1].y = submenu_top - NAVIGATION_REGION_OVERSHOOT; + priv->navigation_y = event->y_root; + priv->navigation_height = submenu_top - event->y_root - NAVIGATION_REGION_OVERSHOOT; - if (point[0].y <= submenu_top) + if (priv->navigation_height >= 0) return; } else { /* bottom */ - point[0].y = event->y_root + 1; - point[1].y = submenu_bottom + NAVIGATION_REGION_OVERSHOOT; + priv->navigation_y = event->y_root; + priv->navigation_height = submenu_bottom - event->y_root + NAVIGATION_REGION_OVERSHOOT; - if (point[0].y >= submenu_bottom) + if (priv->navigation_height <= 0) return; } - point[2].x = point[1].x; - point[2].y = point[0].y; - - menu->navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE); - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), "gtk-menu-popdown-delay", &popdown_delay, NULL); @@ -4247,11 +4266,6 @@ gtk_menu_set_submenu_navigation_region (GtkMenu *menu, gtk_menu_stop_navigating_submenu_cb, popdown_data, (GDestroyNotify) g_free); - -#ifdef DRAW_STAY_UP_TRIANGLE - draw_stay_up_triangle (gdk_get_default_root_window(), - menu->navigation_region); -#endif } } diff --git a/gtk/gtkmenu.h b/gtk/gtkmenu.h index 0929472af7..4e4746fc06 100644 --- a/gtk/gtkmenu.h +++ b/gtk/gtkmenu.h @@ -92,7 +92,7 @@ struct _GtkMenu /* When a submenu of this menu is popped up, motion in this * region is ignored */ - GdkRegion *GSEAL (navigation_region); + GdkRegion *GSEAL (navigation_region); /* unused */ guint GSEAL (navigation_timeout); guint GSEAL (needs_destruction_ref_count) : 1;