[gtk+] Get rid of navigation region in GtkMenu



commit 14e0cbe2d35215fb299e84c193d3438dfbcc7a88
Author: Benjamin Otte <otte redhat com>
Date:   Wed Jun 16 13:14:01 2010 +0200

    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 |  146 +++++++++++++++++++++++++++++++--------------------------
 gtk/gtkmenu.h |    2 +-
 2 files changed, 81 insertions(+), 67 deletions(-)
---
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index 3835e73..c9b2382 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;
@@ -3326,6 +3332,16 @@ definitely_within_item (GtkWidget *widget,
 }
 
 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,49 +4137,48 @@ 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;
-}
+  GtkMenuPrivate *priv;
+  int width, height;
 
-#undef DRAW_STAY_UP_TRIANGLE
+  if (!gtk_menu_has_navigation_triangle (menu))
+    return FALSE;
 
-#ifdef DRAW_STAY_UP_TRIANGLE
+  priv = gtk_menu_get_private (menu);
+  width = priv->navigation_width;
+  height = priv->navigation_height;
 
-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);
+  /* 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)
+    {
+      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;
+    }
 }
-#endif
 
 static void
 gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
@@ -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 0929472..4e4746f 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;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]