Re: gtk2 drop shadow menu



On Sat, 2003-12-27 at 19:38, Ryan wrote: 
> Hi,
> 
> Bit of a gtk newbie here sorry if the question is offensive by nature. 
> Is there a patch for gtk2-menu-dropshadows that's compatible with gtk+
> 2.4 (2.3)?  How do (or can) I enable this option when building gtk2?  My
> favorite theme is quite white and some additional separation would be
> nice (yeah I know about freedesktop.org ;-)
> 
> thanks,
> -ry

You should not attempt to create a new thread by replying to old
messages.

But yes, there is a patch for gtk+ that adds menu dropshadows - I use it
on my system here.  As it's only 16K I have also attached it FYI.  The
source was www.breakmygentoo.net, although I don't know where it
originally surfaced.
I think it seems a little sluggish to me, but looks quite neat.

-- 
Tom Wesley
--- gtk.orig/gtkmenu.c	Tue May 20 21:00:52 2003
+++ gtk/gtkmenu.c	Fri Jun 20 00:59:41 2003
@@ -48,6 +48,7 @@
 
 #define DEFAULT_POPUP_DELAY    225
 #define DEFAULT_POPDOWN_DELAY  1000
+#define DEFAULT_SHADOW_DELAY   50
 
 #define NAVIGATION_REGION_OVERSHOOT 50  /* How much the navigation region
 					 * extends below the submenu
@@ -74,6 +75,9 @@
   gboolean have_position;
   gint x;
   gint y;
+  GdkPixbuf *east, *south;
+  GdkWindow *east_shadow, *south_shadow;
+  guint32 timeout_id;
 };
 
 enum {
@@ -86,6 +90,60 @@
   PROP_TEAROFF_TITLE
 };
 
+enum side {
+  EAST_SIDE,
+  SOUTH_SIDE
+};
+
+const double shadow_strip_l[5] = {
+ .937, .831, .670, .478, .180
+};
+
+const double bottom_left_corner[25] = {
+  1.00, .682, .423, .333, .258,
+  1.00, .898, .800, .682, .584, 
+  1.00, .937, .874, .800, .737, 
+  1.00, .968, .937, .898, .866,
+  1.00, .988, .976, .960, .945
+};
+
+const double bottom_right_corner[25] = {
+  .258, .584, .737, .866, .945,
+  .584, .682, .800, .898, .960,
+  .737, .800, .874, .937, .976,
+  .866, .898, .937, .968, .988,
+  .945, .960, .976, .988, .996 
+};
+
+const double top_right_corner[25] = {
+  1.00, 1.00, 1.00, 1.00, 1.00, 
+  .686, .898, .937, .968, .988, 
+  .423, .803, .874, .937, .976, 
+  .333, .686, .800, .898, .960,
+  .258, .584, .737, .866, .945
+};
+
+const double top_left_corner[25] = {
+  .988, .968, .937, .898, .498,
+  .976, .937, .874, .803, .423, 
+  .960, .898, .800, .686, .333, 
+  .945, .866, .737, .584, .258,
+  .941, .847, .698, .521, .215
+}; 
+
+static GdkPixbuf *get_pixbuf               (GtkMenu *menu,
+					    int x,
+					    int y,
+					    int width,
+					    int height);
+static void     shadow_paint               (GtkWidget *widget,
+					    GdkRectangle *area, 
+					    enum side shadow);
+static void     pixbuf_add_shadow          (GdkPixbuf *pb,
+					    enum side shadow);
+static gboolean map_shadow_windows         (gpointer data);
+static void     shadow_add_timeout         (GtkWidget *widget);
+static void     shadow_remove_timeout      (GtkWidget *widget);
 static void     gtk_menu_class_init        (GtkMenuClass     *klass);
 static void     gtk_menu_init              (GtkMenu          *menu);
 static void     gtk_menu_set_property      (GObject      *object,
@@ -181,6 +239,17 @@
 
 static guint menu_signals[LAST_SIGNAL] = { 0 };
 
+static void
+free_private_data (gpointer data)
+{
+  GtkMenuPrivate *private = (GtkMenuPrivate *) data;
+
+  if (private->timeout_id > 0) 
+    g_source_remove (private->timeout_id);
+  
+  g_free (data);
+}
+
 GtkMenuPrivate *
 gtk_menu_get_private (GtkMenu *menu)
 {
@@ -198,7 +267,7 @@
       private->have_position = FALSE;
       
       g_object_set_qdata_full (G_OBJECT (menu), private_quark,
-			       private, g_free);
+			       private, free_private_data);
     }
 
   return private;
@@ -231,6 +300,307 @@
   return menu_type;
 }
 
+static GdkPixbuf *
+get_pixbuf (GtkMenu *menu,
+	    int x,
+	    int y,
+	    int width,
+	    int height)
+{
+  GdkPixbuf *dest, *src;
+  GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET(menu));
+  GdkWindow *root = gdk_screen_get_root_window (screen);
+  gint screen_height = gdk_screen_get_height (screen);
+  gint screen_width = gdk_screen_get_width (screen);
+  gint original_width = width;
+  gint original_height = height;
+    
+  if (x < 0) 
+    {
+      width += x;
+      x = 0;
+    }
+
+  if (y < 0) 
+    {
+      height += y;
+      y = 0;
+    }
+
+  if (x + width > screen_width) 
+    {
+      width = screen_width - x;
+    }
+
+  if (y + height > screen_height) 
+    {
+      height = screen_height - y;
+    }
+
+  if (width <= 0 || height <= 0)
+    return NULL;
+
+  dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 
+                         original_width, original_height);
+  src = gdk_pixbuf_get_from_drawable (NULL, root, NULL, x, y, 0, 0, 
+                                      width, height);
+  gdk_pixbuf_copy_area (src, 0, 0, width, height, dest, 0, 0);
+
+  g_object_unref (G_OBJECT (src));
+  
+  return dest;
+}
+
+static void
+shadow_paint(GtkWidget *widget, GdkRectangle *area, enum side shadow)
+{
+  GtkMenu *menu = GTK_MENU (widget);
+  GtkMenuPrivate *private = gtk_menu_get_private (menu);
+  gint width, height;
+  GdkGC *gc = widget->style->black_gc;
+
+  switch (shadow) 
+    {
+      case EAST_SIDE:
+	if (private->east != NULL)
+	  {
+	    if (area)
+	      gdk_gc_set_clip_rectangle (gc, area);
+
+	    width = gdk_pixbuf_get_width (private->east);
+	    height = gdk_pixbuf_get_height (private->east);
+
+	    gdk_draw_pixbuf (private->east_shadow, gc, private->east, 0, 0, 0, 0,
+			     width, height, GDK_RGB_DITHER_NONE, 0, 0);
+
+	    if (area)
+	      gdk_gc_set_clip_rectangle (gc, NULL);
+	  }
+	break;
+      case SOUTH_SIDE:
+	if (private->south != NULL)
+	  {
+	    if (area)
+	      gdk_gc_set_clip_rectangle (gc, area);
+
+	    width = gdk_pixbuf_get_width (private->south);
+	    height = gdk_pixbuf_get_height (private->south);
+
+	    gdk_draw_pixbuf (private->south_shadow, gc, private->south, 0, 0, 0, 0,
+			     width, height, GDK_RGB_DITHER_NONE, 0, 0);
+
+	    if (area)
+	      gdk_gc_set_clip_rectangle (gc, NULL);
+	  }
+	break;
+      default:
+	break;
+    }
+}
+
+static void
+pixbuf_add_shadow (GdkPixbuf *pb,
+	           enum side shadow)
+{
+  gint width, rowstride, height;
+  gint i;
+  guchar *pixels, *p;
+
+  width = gdk_pixbuf_get_width (pb);
+  height = gdk_pixbuf_get_height (pb);
+  rowstride = gdk_pixbuf_get_rowstride (pb);
+  pixels = gdk_pixbuf_get_pixels (pb);
+
+  switch (shadow) 
+    {
+      case EAST_SIDE:
+	if (height > 5) 
+	  {
+	    for (i = 0; i < width; i++) 
+	      {
+		gint j, k;
+
+		p = pixels + (i * rowstride);
+		for (j = 0, k = 0; j < 3 * width; j += 3, k++) 
+		  {
+		    p[j] = (guchar) (p[j] * top_right_corner [i * width + k]);
+		    p[j + 1] = (guchar) (p[j + 1] * top_right_corner [i * width + k]);
+		    p[j + 2] = (guchar) (p[j + 2] * top_right_corner [i * width + k]);
+		  }
+	      }
+
+	    i = 5;
+	  } 
+	else 
+	  {
+	    i = 0;
+	  }
+
+	for (;i < height; i++) 
+	  {
+	    gint j, k;
+
+	    p = pixels + (i * rowstride);
+	    for (j = 0, k = 0; j < 3 * width; j += 3, k++) 
+	      {
+		p[j] = (guchar) (p[j] * shadow_strip_l[width - 1 - k]);
+		p[j + 1] = (guchar) (p[j + 1] * shadow_strip_l[width - 1 - k]);
+		p[j + 2] = (guchar) (p[j + 2] * shadow_strip_l[width - 1 - k]);
+	      }
+	  }
+	break;
+
+      case SOUTH_SIDE:
+	for (i = 0; i < height; i++) 
+	  {
+	    gint j, k;
+
+	    p = pixels + (i * rowstride);
+	    for (j = 0, k = 0; j < 3 * height; j += 3, k++) 
+	      {
+
+		p[j] = (guchar) (p[j] * bottom_left_corner[i * height + k]);
+		p[j + 1] = (guchar) (p[j + 1] * bottom_left_corner[i * height + k]);
+		p[j + 2] = (guchar) (p[j + 2] * bottom_left_corner[i * height + k]);
+	      }
+
+	    p = pixels + (i * rowstride) + 3 * height;
+	    for (j = 0, k = 0; j < (width * 3) - (6 * height); j += 3, k++)
+	      {
+		p[j] = (guchar) (p[j] * bottom_right_corner [i * height]);
+		p[j + 1] = (guchar) (p[j + 1] * bottom_right_corner [i * height]);
+		p[j + 2] = (guchar) (p[j + 2] * bottom_right_corner [i * height]);
+	      }
+
+	    p = pixels + (i * rowstride) + ((width * 3) - (3 * height));
+	    for (j = 0, k = 0; j < 3 * height; j += 3, k++) 
+	      {
+		p[j] = (guchar) (p[j] * bottom_right_corner[i * height + k]);
+		p[j + 1] = (guchar) (p[j + 1] * bottom_right_corner[i * height + k]);
+		p[j + 2] = (guchar) (p[j + 2] * bottom_right_corner[i * height + k]);
+	      }
+	  }
+	break;
+
+      default:
+	break;
+    }
+}
+
+static gboolean
+map_shadow_windows (gpointer data)
+{
+  GtkMenu *menu = GTK_MENU (data);
+  GtkMenuPrivate *private = gtk_menu_get_private (menu);
+  GtkWidget *widget = GTK_WIDGET (data);
+  GdkPixbuf *pixbuf;
+
+  pixbuf = get_pixbuf (menu, 
+		       private->x + widget->allocation.width, private->y,
+		       5, widget->allocation.height);
+  if (pixbuf != NULL) 
+    {
+      pixbuf_add_shadow (pixbuf, EAST_SIDE);
+      if (private->east != NULL) 
+	{
+	  g_object_unref (G_OBJECT (private->east));
+	}
+      private->east = pixbuf;
+    }
+  
+  pixbuf = get_pixbuf (menu, 
+		       private->x, private->y + widget->allocation.height,
+		       widget->allocation.width + 5, 5);
+  if (pixbuf != NULL) 
+    {
+      pixbuf_add_shadow (pixbuf, SOUTH_SIDE);
+      if (private->south != NULL) 
+	{
+	  g_object_unref (G_OBJECT (private->south));
+	}
+      private->south = pixbuf;
+    }
+
+  gdk_window_move_resize (private->east_shadow, 
+			  private->x + widget->allocation.width, private->y, 
+			  5, widget->allocation.height);
+
+  gdk_window_move_resize (private->south_shadow, 
+			  private->x, private->y + widget->allocation.height, 
+			  widget->allocation.width + 5, 5);
+
+  gdk_window_show (private->east_shadow);
+  gdk_window_show (private->south_shadow);
+
+  shadow_paint(widget, NULL, EAST_SIDE);
+  shadow_paint(widget, NULL, SOUTH_SIDE);
+
+  private->timeout_id = 0;
+  return FALSE;
+}
+
+static void
+shadow_add_timeout(GtkWidget *widget)
+{
+  GtkMenuPrivate *private = gtk_menu_get_private (GTK_MENU (widget));
+  gboolean menu_shadow;
+  gint shadow_delay;
+
+  if (private->have_position)
+    {
+      g_object_get (G_OBJECT (gtk_widget_get_settings (widget)),
+		"gtk-menu-drop-shadow", &menu_shadow, NULL);
+
+      if (menu_shadow)
+	{
+	  if (private->timeout_id > 0) 
+	    {
+	      g_source_remove (private->timeout_id);
+	    } 
+
+
+	  g_object_get (G_OBJECT (gtk_widget_get_settings (widget)),
+			"gtk-menu-shadow-delay", &shadow_delay,
+			NULL);
+
+	  private->timeout_id = g_timeout_add (shadow_delay, map_shadow_windows, widget);
+	}
+    }
+}
+
+static void
+shadow_remove_timeout (GtkWidget *widget)
+{
+  GtkMenu *menu = GTK_MENU (widget);
+  GtkMenuPrivate *private = gtk_menu_get_private (menu);
+
+  if (private->timeout_id > 0) 
+    {
+      g_source_remove (private->timeout_id);
+      private->timeout_id = 0;
+    } 
+  else 
+    {
+      if (private->east_shadow)
+	gdk_window_hide (private->east_shadow);
+	
+      if (private->south_shadow)
+	gdk_window_hide (private->south_shadow);
+
+      if (private->east)
+        {
+          g_object_unref (G_OBJECT (private->east));
+          private->east = NULL;
+        }
+
+      if (private->south)
+        {
+          g_object_unref (G_OBJECT (private->south));
+          private->south = NULL;
+        }
+    }
+}
+
 static void
 gtk_menu_class_init (GtkMenuClass *class)
 {
@@ -397,6 +767,20 @@
 						   DEFAULT_POPDOWN_DELAY,
 						   G_PARAM_READWRITE));
 						   
+  gtk_settings_install_property (g_param_spec_boolean ("gtk-menu-drop-shadow",
+						       _("Display menu drop-shadow"),
+						       _("Whether menu drop-shadow should be displayed"),
+						       TRUE,
+						       G_PARAM_READWRITE));
+
+  gtk_settings_install_property (g_param_spec_int ("gtk-menu-shadow-delay",
+						   _("Delay before drop-shadow appear"),
+						   _("Minimum time before drop-shadow appear under the menu"),
+						   0,
+						   G_MAXINT,
+						   DEFAULT_SHADOW_DELAY,
+						   G_PARAM_READWRITE));
+
 }
 
 
@@ -488,6 +872,8 @@
 static void
 gtk_menu_init (GtkMenu *menu)
 {
+  GtkMenuPrivate *private = gtk_menu_get_private (menu);
+
   menu->parent_menu_item = NULL;
   menu->old_active_menu_item = NULL;
   menu->accel_group = NULL;
@@ -532,6 +918,14 @@
   menu->upper_arrow_prelight = FALSE;
   menu->lower_arrow_prelight = FALSE;
   
+  private->east_shadow = NULL;
+  private->south_shadow = NULL;
+  
+  private->east = NULL;
+  private->south = NULL;
+  
+  private->timeout_id = 0;
+
   MENU_NEEDS_RESIZE (menu) = TRUE;
 }
 
@@ -594,6 +988,7 @@
 menu_change_screen (GtkMenu   *menu,
 		    GdkScreen *new_screen)
 {
+  shadow_remove_timeout(GTK_WIDGET(menu));
   if (menu->torn_off)
     {
       gtk_window_set_screen (GTK_WINDOW (menu->tearoff_window), new_screen);
@@ -994,6 +1389,7 @@
   if (xgrab_shell == widget)
     popup_grab_on_window (widget->window, activate_time); /* Should always succeed */
 
+  shadow_add_timeout(GTK_WIDGET (menu));
   gtk_grab_add (GTK_WIDGET (menu));
 }
 
@@ -1004,7 +1400,7 @@
   GtkMenuShell *menu_shell;
 
   g_return_if_fail (GTK_IS_MENU (menu));
-  
+
   menu_shell = GTK_MENU_SHELL (menu);
   private = gtk_menu_get_private (menu);
   
@@ -1068,6 +1464,7 @@
   menu_shell->have_xgrab = FALSE;
   gtk_grab_remove (GTK_WIDGET (menu));
 
+  shadow_remove_timeout(GTK_WIDGET (menu));
   menu_grab_transfer_window_destroy (menu);
 }
 
@@ -1473,10 +1870,16 @@
   if (GTK_WIDGET_REALIZED (widget))
     {
       GtkMenu *menu = GTK_MENU (widget);
+      GtkMenuPrivate *private;
       
+      private = gtk_menu_get_private (menu);
+
       gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL);
       gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL);
       gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+
+      gdk_window_set_back_pixmap (private->east_shadow, NULL, FALSE);
+      gdk_window_set_back_pixmap (private->south_shadow, NULL, FALSE);
     }
 }
 
@@ -1487,12 +1890,14 @@
   gint attributes_mask;
   gint border_width;
   GtkMenu *menu;
+  GtkMenuPrivate *private;
   GtkWidget *child;
   GList *children;
-
+  
   g_return_if_fail (GTK_IS_MENU (widget));
 
   menu = GTK_MENU (widget);
+  private = gtk_menu_get_private (menu);
   
   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
   
@@ -1558,6 +1963,25 @@
 
   gdk_window_show (menu->bin_window);
   gdk_window_show (menu->view_window);
+
+  /* Drop shadow */
+
+  attributes.window_type = GDK_WINDOW_TEMP;
+  attributes.override_redirect = TRUE;
+
+  attributes_mask = GDK_WA_NOREDIR | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+  
+  /* East drop shadow */
+  private->east_shadow = gdk_window_new (gtk_widget_get_root_window (widget), 
+                                         &attributes, attributes_mask);
+  gdk_window_set_user_data (private->east_shadow, menu);
+  gdk_window_set_back_pixmap (private->east_shadow, NULL, FALSE);
+  
+  /* South drop shadow */
+  private->south_shadow = gdk_window_new (gtk_widget_get_root_window (widget), 
+                                          &attributes, attributes_mask);
+  gdk_window_set_user_data (private->south_shadow, menu);
+  gdk_window_set_back_pixmap (private->south_shadow, NULL, FALSE);
 }
 
 static gboolean 
@@ -1620,10 +2044,12 @@
 gtk_menu_unrealize (GtkWidget *widget)
 {
   GtkMenu *menu;
+  GtkMenuPrivate *private;
 
   g_return_if_fail (GTK_IS_MENU (widget));
 
   menu = GTK_MENU (widget);
+  private = gtk_menu_get_private (menu);
 
   menu_grab_transfer_window_destroy (menu);
 
@@ -1635,6 +2061,15 @@
   gdk_window_destroy (menu->bin_window);
   menu->bin_window = NULL;
 
+  /* Shadows */
+  gdk_window_set_user_data (private->east_shadow, NULL);
+  gdk_window_destroy (private->east_shadow);
+  private->east_shadow = NULL;
+
+  gdk_window_set_user_data (private->south_shadow, NULL);
+  gdk_window_destroy (private->south_shadow);
+  private->south_shadow = NULL;
+  
   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
 }
 
@@ -1753,8 +2188,15 @@
 			      y,
 			      width,
 			      height);
-    }
 
+      if (GTK_WIDGET_MAPPED (widget))
+	{
+	  /* Remap the shadows as the menu size has changed */
+	  shadow_remove_timeout(widget);
+	  shadow_add_timeout(widget);
+	}
+    }
+    
   if (menu_shell->children)
     {
       child_allocation.x = 0;
@@ -1908,7 +2350,16 @@
 			   MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2,
 			   MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2);
 	}
-    }
+      } 
+    else 
+      {
+	GtkMenuPrivate *private = gtk_menu_get_private (menu);
+
+	if (event->window == private->east_shadow)
+	  shadow_paint(widget, &event->area, EAST_SIDE);
+	else if (event->window == private->south_shadow) 
+	  shadow_paint(widget, &event->area, SOUTH_SIDE);
+      }
 }
 
 static gboolean

Attachment: signature.asc
Description: This is a digitally signed message part



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