Subject: Re: Drop Shadow Effect for GTK2
To: None <tech-pkg@NetBSD.org>
From: Joel CARNAT <joel@carnat.net>
List: tech-pkg
Date: 08/02/2004 16:03:34
--a8Wt8u1KmwUX3Y2C
Content-Type: text/plain; charset=iso-8859-15
Content-Disposition: inline
humpf... now, the attachment is attached :)
SHA1 (patch-menushadow) = 1536b03553c9c94348cf20abc8df86a25ae6d884
On Mon, Aug 02 2004 - 16:00, Joel CARNAT wrote:
> Hi,
>
> Find attached a patch that enables drop shadow on GTK2 menu.
>
> It's taken from http://www.xfce.org/gtkmenu-shadow/index.html
> Tested on NetBSD-2.0-BETA/i386 with gtk+-2.4.2 (pkgsrc-2004Q2 branch)
>
> It's pure eye-candy, but I love that effects.
> Maybe we can have it by default :)
>
> Cheers,
> Jo
>
> PS: I didn't subscribe to this ML, please Cc me for any direct remarks.
--a8Wt8u1KmwUX3Y2C
Content-Type: text/plain; charset=iso-8859-15
Content-Disposition: attachment; filename=patch-menushadow
--- gtk/gtkmenu.c.orig 2004-06-04 16:15:20.000000000 +0200
+++ gtk/gtkmenu.c 2004-08-02 15:37:19.000000000 +0200
@@ -50,4 +50,5 @@
#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
@@ -91,4 +92,9 @@
gint n_rows;
gint n_columns;
+
+ /* Shadow patch addon */
+ GdkPixbuf *east, *south;
+ GdkWindow *east_shadow, *south_shadow;
+ guint32 timeout_id;
};
@@ -123,4 +129,58 @@
};
+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);
@@ -237,4 +297,7 @@
GtkMenuPrivate *priv = (GtkMenuPrivate *)data;
+ if (priv->timeout_id > 0)
+ g_source_remove (priv->timeout_id);
+
g_free (priv->heights);
@@ -457,4 +520,305 @@
}
+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)
@@ -687,4 +1051,18 @@
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));
+
}
@@ -893,4 +1271,13 @@
priv->have_layout = FALSE;
+
+ /* Shadow patch */
+ priv->east_shadow = NULL;
+ priv->south_shadow = NULL;
+
+ priv->east = NULL;
+ priv->south = NULL;
+
+ priv->timeout_id = 0;
}
@@ -959,4 +1346,5 @@
GtkMenuPrivate *private = gtk_menu_get_private (menu);
+ shadow_remove_timeout(GTK_WIDGET(menu));
if (menu->torn_off)
{
@@ -1390,4 +1778,5 @@
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));
}
@@ -1400,5 +1789,5 @@
g_return_if_fail (GTK_IS_MENU (menu));
-
+
menu_shell = GTK_MENU_SHELL (menu);
private = gtk_menu_get_private (menu);
@@ -1465,4 +1854,5 @@
gtk_grab_remove (GTK_WIDGET (menu));
+ shadow_remove_timeout(GTK_WIDGET (menu));
menu_grab_transfer_window_destroy (menu);
}
@@ -1929,8 +2319,14 @@
{
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);
}
}
@@ -1943,4 +2339,5 @@
gint border_width;
GtkMenu *menu;
+ GtkMenuPrivate *private;
GtkWidget *child;
GList *children;
@@ -1950,4 +2347,5 @@
menu = GTK_MENU (widget);
+ private = gtk_menu_get_private (menu);
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
@@ -2020,4 +2418,23 @@
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);
}
@@ -2082,8 +2499,10 @@
{
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);
@@ -2097,4 +2516,13 @@
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);
}
@@ -2251,6 +2679,13 @@
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)
{
@@ -2349,5 +2784,5 @@
}
}
- }
+ }
}
@@ -2436,5 +2871,14 @@
arrow_size, arrow_size);
}
- }
+ }
+ 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);
+ }
}
--a8Wt8u1KmwUX3Y2C--