[gnome-shell] Implement -st-shadow for StWidget



commit 2dfe113a4251ebfd89931dcaff45374748958193
Author: Florian Müllner <fmuellner src gnome org>
Date:   Sat Nov 21 04:19:56 2009 +0100

    Implement -st-shadow for StWidget
    
    Add support for a new -st-shadow property, which is based loosely
    on the CSS3 box-shadow property:
    http://www.css3.info/preview/box-shadow/
    
    It defers from the specification as follows:
    
     * no multiple shadows
     * the optional color argument may be placed anywhere
     * the shape is not determined by the widget's bounding box,
       but by the background-image property
    
    https://bugzilla.gnome.org/show_bug.cgi?id=603691

 src/Makefile-st.am         |    4 +
 src/st/st-shadow-texture.c |  274 ++++++++++++++++++++++++++++++++++++++++++++
 src/st/st-shadow-texture.h |   30 +++++
 src/st/st-shadow.c         |   89 ++++++++++++++
 src/st/st-shadow.h         |   42 +++++++
 src/st/st-theme-node.c     |   89 ++++++++++++++
 src/st/st-theme-node.h     |    2 +
 src/st/st-widget.c         |   86 ++++++++++++++-
 8 files changed, 614 insertions(+), 2 deletions(-)
---
diff --git a/src/Makefile-st.am b/src/Makefile-st.am
index bd7d15b..bf44016 100644
--- a/src/Makefile-st.am
+++ b/src/Makefile-st.am
@@ -83,6 +83,8 @@ st_source_h =					\
     st/st-scrollable.h				\
     st/st-scroll-bar.h				\
     st/st-scroll-view.h				\
+    st/st-shadow-texture.h                      \
+    st/st-shadow.h                              \
     st/st-subtexture.h				\
     st/st-table.h				\
     st/st-table-child.h				\
@@ -122,6 +124,8 @@ st_source_c =					\
     st/st-scrollable.c				\
     st/st-scroll-bar.c				\
     st/st-scroll-view.c				\
+    st/st-shadow-texture.c                      \
+    st/st-shadow.c                              \
     st/st-subtexture.c				\
     st/st-table.c				\
     st/st-table-child.c				\
diff --git a/src/st/st-shadow-texture.c b/src/st/st-shadow-texture.c
new file mode 100644
index 0000000..57090ba
--- /dev/null
+++ b/src/st/st-shadow-texture.c
@@ -0,0 +1,274 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include <math.h>
+#include <string.h>
+
+#include "st-shadow-texture.h"
+
+/**
+ * SECTION: st-shadow-texture
+ * @short_description: a class for creating soft shadow textures
+ *
+ * #StShadowTexture is a #ClutterTexture holding a soft shadow texture for
+ * another #ClutterActor.
+ * It is used to implement the box-shadow property in StWidget and should
+ * not be used stand-alone.
+ */
+
+struct _StShadowTexture {
+  ClutterTexture parent;
+
+  CoglColor color;
+  gdouble sigma;
+  gdouble blur_radius;
+};
+
+struct _StShadowTextureClass {
+  ClutterTextureClass parent_class;
+};
+
+G_DEFINE_TYPE (StShadowTexture, st_shadow_texture, CLUTTER_TYPE_TEXTURE);
+
+static gdouble *
+calculate_gaussian_kernel (gdouble sigma, guint n_values)
+{
+  gdouble *ret, sum;
+  gdouble exp_divisor;
+  gint half, i;
+
+  g_return_val_if_fail ((int) sigma > 0, NULL);
+
+  half = n_values / 2;
+
+  ret = g_malloc (n_values * sizeof (gdouble));
+  sum = 0.0;
+
+  exp_divisor = 2 * sigma * sigma;
+
+  /* n_values of 1D Gauss function */
+  for (i = 0; i < n_values; i++)
+    {
+      ret[i] = exp (-(i - half) * (i - half) / exp_divisor);
+      sum += ret[i];
+    }
+
+  /* normalize */
+  for (i = 0; i < n_values; i++)
+    ret[i] /= sum;
+
+  return ret;
+}
+
+static void
+st_shadow_texture_create_shadow (StShadowTexture *st,
+                                 ClutterActor    *actor)
+{
+  CoglHandle  texture, material;
+  guchar     *pixels_in, *pixels_out;
+  gint        width_in, height_in, rowstride_in;
+  gint        width_out, height_out, rowstride_out;
+
+  g_return_if_fail (ST_IS_SHADOW_TEXTURE (st));
+
+  /* Right now we only deal with actors of type ClutterTexture.
+     It would be nice to extend this to generic actors with some
+     clutter_texture_new_from_actor magic in the future */
+  g_return_if_fail (CLUTTER_IS_TEXTURE (actor));
+
+  texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (actor));
+  if (texture == COGL_INVALID_HANDLE)
+    return;
+
+  width_in  = cogl_texture_get_width  (texture);
+  height_in = cogl_texture_get_height (texture);
+  rowstride_in = (width_in + 3) & ~3;
+
+  pixels_in  = g_malloc0 (rowstride_in * height_in);
+
+  cogl_texture_get_data (texture, COGL_PIXEL_FORMAT_A_8,
+                         rowstride_in, pixels_in);
+  cogl_texture_unref (texture);
+
+  if ((guint) st->blur_radius == 0)
+    {
+      width_out  = width_in;
+      height_out = height_in;
+      rowstride_out = rowstride_in;
+      pixels_out = g_memdup (pixels_in, rowstride_out * height_out);
+    }
+  else
+    {
+      gdouble *kernel;
+      guchar  *line;
+      gint     n_values, half;
+      gint     x_in, y_in, x_out, y_out, i;
+
+      n_values = (gint) 5 * st->sigma;
+      half = n_values / 2;
+
+      width_out  = width_in  + 2 * half;
+      height_out = height_in + 2 * half;
+      rowstride_out = (width_out + 3) & ~3;
+
+      pixels_out = g_malloc0 (rowstride_out * height_out);
+      line       = g_malloc0 (rowstride_out);
+
+      kernel = calculate_gaussian_kernel (st->sigma, n_values);
+
+      /* vertical blur */
+      for (x_in = 0; x_in < width_in; x_in++)
+        for (y_out = 0; y_out < height_out; y_out++)
+          {
+            guchar *pixel_in, *pixel_out;
+            gint i0, i1;
+
+            y_in = y_out - half;
+
+            /* We read from the source at 'y = y_in + i - half'; clamp the
+             * full i range [0, n_values) so that y is in [0, height_in).
+             */
+            i0 = MAX (half - y_in, 0);
+            i1 = MIN (height_in + half - y_in, n_values);
+
+            pixel_in  =  pixels_in + (y_in + i0 - half) * rowstride_in + x_in;
+            pixel_out =  pixels_out + y_out * rowstride_out + (x_in + half);
+
+            for (i = i0; i < i1; i++)
+              {
+                *pixel_out += *pixel_in * kernel[i];
+                pixel_in += rowstride_in;
+              }
+          }
+
+      /* horizontal blur */
+      for (y_out = 0; y_out < height_out; y_out++)
+        {
+          memcpy (line, pixels_out + y_out * rowstride_out, rowstride_out);
+
+          for (x_out = 0; x_out < width_out; x_out++)
+            {
+              gint i0, i1;
+              guchar *pixel_out, *pixel_in;
+
+              /* We read from the source at 'x = x_out + i - half'; clamp the
+               * full i range [0, n_values) so that x is in [0, width_out).
+               */
+              i0 = MAX (half - x_out, 0);
+              i1 = MIN (width_out + half - x_out, n_values);
+
+              pixel_in  = line + x_out + i0 - half;
+              pixel_out = pixels_out + rowstride_out * y_out + x_out;
+
+              *pixel_out = 0;
+              for (i = i0; i < i1; i++)
+                {
+                  *pixel_out += *pixel_in * kernel[i];
+                  pixel_in++;
+                }
+            }
+        }
+      g_free (kernel);
+      g_free (line);
+    }
+
+  material = cogl_material_new ();
+  texture = cogl_texture_new_from_data (width_out,
+                                        height_out,
+                                        COGL_TEXTURE_NONE,
+                                        COGL_PIXEL_FORMAT_A_8,
+                                        COGL_PIXEL_FORMAT_A_8,
+                                        rowstride_out,
+                                        pixels_out);
+
+  cogl_material_set_layer_combine_constant (material, 0, &st->color);
+  cogl_material_set_layer (material, 0, texture);
+
+  /* We ignore the material color, which encodes the overall opacity of the
+   * actor, so setting an ancestor of the shadow to partially opaque won't
+   * work. The easiest way to fix this would be to override paint(). */
+
+  cogl_material_set_layer_combine (material, 0,
+                                   "RGBA = MODULATE (CONSTANT, TEXTURE[A])",
+                                   NULL);
+
+  clutter_texture_set_cogl_material (CLUTTER_TEXTURE (st), material);
+
+  cogl_texture_unref  (texture);
+  cogl_material_unref (material);
+
+  g_free (pixels_in);
+  g_free (pixels_out);
+}
+
+
+/**
+ * st_shadow_texture_adjust_allocation:
+ * @shadow: a #StShadowTexture
+ * @allocation: the original allocation of @shadow
+ *
+ * Adjust @allocation to account for size change caused by blurrimg
+ */
+void
+st_shadow_texture_adjust_allocation (StShadowTexture *shadow,
+                                     ClutterActorBox *allocation)
+{
+  g_return_if_fail (ST_IS_SHADOW_TEXTURE (shadow));
+  g_return_if_fail (allocation != NULL);
+
+  allocation->x1 -= shadow->blur_radius;
+  allocation->y1 -= shadow->blur_radius;
+  allocation->x2 += shadow->blur_radius;
+  allocation->y2 += shadow->blur_radius;
+}
+
+
+/**
+ * st_shadow_texture_new:
+ * @actor: the original actor
+ * @color: (allow-none): the shadow color
+ * @blur: the shadow's blur radius
+ *
+ * Create a shadow texture for @actor. When %NULL is passed for @color, it
+ * defaults to fully opaque black.
+ *
+ * Returns: a new #ClutterActor holding a shadow texture for @actor
+ */
+ClutterActor *
+st_shadow_texture_new (ClutterActor *actor,
+                       ClutterColor *color,
+                       gdouble       blur)
+{
+  StShadowTexture *st = g_object_new (ST_TYPE_SHADOW_TEXTURE, NULL);
+
+  if (color)
+    {
+      cogl_color_set_from_4ub (&st->color,
+                               color->red,  color->green,
+                               color->blue, color->alpha);
+      cogl_color_premultiply (&st->color);
+    }
+
+  st->blur_radius = blur;
+  /* we use an approximation of the sigma - blur radius relationship used
+     in Firefox for doing SVG blurs; see
+     http://mxr.mozilla.org/mozilla-central/source/gfx/thebes/src/gfxBlur.cpp#280
+  */
+  st->sigma = blur / 1.9;
+
+  st_shadow_texture_create_shadow (st, actor);
+
+  return CLUTTER_ACTOR (st);
+}
+
+static void
+st_shadow_texture_init (StShadowTexture *st)
+{
+  st->sigma  = 0.0;
+  st->blur_radius = 0.0;
+
+  cogl_color_set_from_4ub (&st->color, 0x0, 0x0, 0x0, 0xff);
+}
+
+static void
+st_shadow_texture_class_init (StShadowTextureClass *klass)
+{
+}
diff --git a/src/st/st-shadow-texture.h b/src/st/st-shadow-texture.h
new file mode 100644
index 0000000..3f5b28a
--- /dev/null
+++ b/src/st/st-shadow-texture.h
@@ -0,0 +1,30 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __ST_SHADOW_TEXTURE__
+#define __ST_SHADOW_TEXTURE__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+typedef struct _StShadowTexture      StShadowTexture;
+typedef struct _StShadowTextureClass StShadowTextureClass;
+
+#define ST_TYPE_SHADOW_TEXTURE            (st_shadow_texture_get_type ())
+#define ST_SHADOW_TEXTURE(object)         (G_TYPE_CHECK_INSTANCE_CAST((object),ST_TYPE_SHADOW_TEXTURE, StShadowTexture))
+#define ST_IS_SHADOW_TEXTURE(object)      (G_TYPE_CHECK_INSTANCE_TYPE((object),ST_TYPE_SHADOW_TEXTURE))
+#define ST_SHADOW_TEXTURE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),    ST_TYPE_SHADOW_TEXTURE, StShadowTextureClass))
+#define ST_IS_SHADOW_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),    ST_TYPE_SHADOW_TEXTURE))
+#define ST_SHADOW_TEXTURE_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), ST_TYPE_SHADOW_TEXTURE, StShadowTextureClass))
+
+GType st_shadow_texture_get_type (void) G_GNUC_CONST;
+
+ClutterActor *st_shadow_texture_new (ClutterActor *actor,
+                                     ClutterColor *color,
+                                     gdouble       blur_radius);
+
+void st_shadow_texture_adjust_allocation (StShadowTexture *shadow,
+                                          ClutterActorBox *allocation);
+
+G_END_DECLS
+
+#endif /* __ST_SHADOW_TEXTURE__ */
diff --git a/src/st/st-shadow.c b/src/st/st-shadow.c
new file mode 100644
index 0000000..0f11a6c
--- /dev/null
+++ b/src/st/st-shadow.c
@@ -0,0 +1,89 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
+#include "st-shadow.h"
+
+/**
+ * SECTION: st-shadow
+ * @short_description: Boxed type for -st-shadow attributes
+ *
+ * #StShadow is a boxed type for storing attributes of the -st-shadow
+ * property, modelled liberally after the CSS3 box-shadow property.
+ * See http://www.css3.info/preview/box-shadow/
+ *
+ */
+
+/**
+ * st_shadow_new:
+ * @color: shadow's color
+ * @xoffset: horizontal offset
+ * @yoffset: vertical offset
+ * @blur: blur radius
+ *
+ * Creates a new #StShadow
+ *
+ * Returns: the newly allocated shadow. Use st_shadow_free() when done
+ */
+StShadow *
+st_shadow_new (ClutterColor *color,
+               gdouble       xoffset,
+               gdouble       yoffset,
+               gdouble       blur)
+{
+  StShadow *shadow;
+
+  shadow = g_slice_new (StShadow);
+
+  shadow->color   = *color;
+  shadow->xoffset = xoffset;
+  shadow->yoffset = yoffset;
+  shadow->blur    = blur;
+
+  return shadow;
+}
+
+/**
+ * st_shadow_copy:
+ * @shadow: a #StShadow
+ *
+ * Makes a copy of @shadow.
+ *
+ * Returns: an allocated copy of @shadow - the result must be freed with
+ *          st_shadow_free() when done
+ */
+StShadow *
+st_shadow_copy (const StShadow *shadow)
+{
+  g_return_val_if_fail (shadow != NULL, NULL);
+
+  return g_slice_dup (StShadow, shadow);
+}
+
+/**
+ * st_shadow_free:
+ * @shadow: a #StShadow
+ *
+ * Frees the shadow structure created with st_shadow_new() or
+ * st_shadow_copy()
+ */
+void
+st_shadow_free (StShadow *shadow)
+{
+  g_return_if_fail (shadow != NULL);
+
+  g_slice_free (StShadow, shadow);
+}
+
+GType
+st_shadow_get_type (void)
+{
+  static GType _st_shadow_type = 0;
+
+  if (G_UNLIKELY (_st_shadow_type == 0))
+    _st_shadow_type =
+        g_boxed_type_register_static ("StShadow",
+                                      (GBoxedCopyFunc) st_shadow_copy,
+                                      (GBoxedFreeFunc) st_shadow_free);
+
+  return _st_shadow_type;
+}
diff --git a/src/st/st-shadow.h b/src/st/st-shadow.h
new file mode 100644
index 0000000..cf5bffb
--- /dev/null
+++ b/src/st/st-shadow.h
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __ST_SHADOW__
+#define __ST_SHADOW__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_SHADOW              (st_shadow_get_type ())
+
+typedef struct _StShadow StShadow;
+
+/**
+ * StShadow:
+ * @color: shadow's color
+ * @xoffset: horizontal offset - positive values mean placement to the right,
+ *           negative values placement to the left of the element.
+ * @yoffset: vertical offset - positive values mean placement below, negative
+ *           values placement above the element.
+ * @blur: shadow's blur radius - a value of 0.0 will result in a hard shadow.
+ *
+ * Attributes of the -st-shadow property.
+ */
+struct _StShadow {
+    ClutterColor color;
+    gdouble      xoffset;
+    gdouble      yoffset;
+    gdouble      blur;
+};
+
+GType     st_shadow_get_type (void) G_GNUC_CONST;
+
+StShadow *st_shadow_new      (ClutterColor   *color,
+                              gdouble         xoffset,
+                              gdouble         yoffset,
+                              gdouble         blur);
+StShadow *st_shadow_copy     (const StShadow *shadow);
+void      st_shadow_free     (StShadow       *shadow);
+
+G_END_DECLS
+
+#endif /* __ST_SHADOW__ */
diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c
index fb14c68..f8998e8 100644
--- a/src/st/st-theme-node.c
+++ b/src/st/st-theme-node.c
@@ -39,6 +39,7 @@ struct _StThemeNode {
 
   char *background_image;
   StBorderImage *border_image;
+  StShadow *shadow;
 
   GType element_type;
   char *element_id;
@@ -57,6 +58,7 @@ struct _StThemeNode {
   guint background_computed : 1;
   guint foreground_computed : 1;
   guint border_image_computed : 1;
+  guint shadow_computed : 1;
   guint link_type : 2;
 };
 
@@ -118,6 +120,12 @@ st_theme_node_finalize (GObject *object)
       node->border_image = NULL;
     }
 
+  if (node->shadow)
+    {
+      st_shadow_free (node->shadow);
+      node->shadow = NULL;
+    }
+
   if (node->background_image)
     g_free (node->background_image);
 
@@ -2116,6 +2124,87 @@ st_theme_node_get_border_image (StThemeNode *node)
   return NULL;
 }
 
+/**
+ * st_theme_node_get_shadow:
+ * @node: a #StThemeNode
+ *
+ * Gets the value for the -st-shadow style property
+ *
+ * Return value: (transfer none): the node's shadow, or %NULL
+ *   if node has no shadow
+ */
+StShadow *
+st_theme_node_get_shadow (StThemeNode *node)
+{
+  int i;
+
+  if (node->shadow_computed)
+    return node->shadow;
+
+  node->shadow = NULL;
+  node->shadow_computed = TRUE;
+
+  ensure_properties (node);
+
+  for (i = node->n_properties - 1; i >= 0; i--)
+    {
+      CRDeclaration *decl = node->properties[i];
+
+      if (strcmp (decl->property->stryng->str, "-st-shadow") == 0)
+        {
+          /* Set value for width/color/blur in any order */
+          CRTerm *term;
+          ClutterColor color = { 0x0, 0x0, 0x0, 0xff };
+          gdouble xoffset = 0.;
+          gdouble yoffset = 0.;
+          gdouble blur = 0.;
+          int n_offsets = 0;
+
+          for (term = decl->value; term; term = term->next)
+            {
+              GetFromTermResult result;
+
+              if (term->type == TERM_NUMBER)
+                {
+                  gdouble value;
+                  gdouble multiplier;
+
+                  multiplier = (term->unary_op == MINUS_UOP) ? -1. : 1.;
+                  result = get_length_from_term (node, term, FALSE, &value);
+                  if (result != VALUE_NOT_FOUND)
+                    {
+                      switch (n_offsets++)
+                        {
+                        case 0:
+                          xoffset = multiplier * value;
+                          break;
+                        case 1:
+                          yoffset = multiplier * value;
+                          break;
+                        case 2:
+                          if (multiplier < 0)
+                              g_warning ("Negative blur values are "
+                                         "not allowed");
+                          blur = value;
+                        }
+                      continue;
+                    }
+                }
+
+              result = get_color_from_term (node, term, &color);
+              if (result != VALUE_NOT_FOUND)
+                {
+                  continue;
+                }
+            }
+          node->shadow = st_shadow_new (&color, xoffset, yoffset, blur);
+
+          return node->shadow;
+        }
+    }
+  return NULL;
+}
+
 static float
 get_width_inc (StThemeNode *node)
 {
diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h
index 0f92214..31fb08e 100644
--- a/src/st/st-theme-node.h
+++ b/src/st/st-theme-node.h
@@ -4,6 +4,7 @@
 
 #include <clutter/clutter.h>
 #include "st-border-image.h"
+#include "st-shadow.h"
 
 G_BEGIN_DECLS
 
@@ -142,6 +143,7 @@ StTextDecoration st_theme_node_get_text_decoration (StThemeNode *node);
 const PangoFontDescription *st_theme_node_get_font (StThemeNode *node);
 
 StBorderImage *st_theme_node_get_border_image (StThemeNode *node);
+StShadow      *st_theme_node_get_shadow       (StThemeNode *node);
 
 /* Helpers for get_preferred_width()/get_preferred_height() ClutterActor vfuncs */
 void st_theme_node_adjust_for_height       (StThemeNode  *node,
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
index ba2c7a6..023be7d 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -37,6 +37,7 @@
 
 #include "st-marshal.h"
 #include "st-private.h"
+#include "st-shadow-texture.h"
 #include "st-texture-cache.h"
 #include "st-texture-frame.h"
 #include "st-theme-context.h"
@@ -57,11 +58,15 @@ struct _StWidgetPrivate
 
   ClutterActor *border_image;
   ClutterActor *background_image;
+  ClutterActor *background_image_shadow;
   ClutterColor  bg_color;
 
   StGradientType bg_gradient_type;
   ClutterColor  bg_gradient_end;
 
+  gdouble       shadow_xoffset;
+  gdouble       shadow_yoffset;
+
   gboolean      is_stylable : 1;
   gboolean      has_tooltip : 1;
   gboolean      is_style_dirty : 1;
@@ -227,6 +232,12 @@ st_widget_dispose (GObject *gobject)
       priv->border_image = NULL;
     }
 
+  if (priv->background_image_shadow)
+    {
+      clutter_actor_unparent (priv->background_image_shadow);
+      priv->background_image_shadow = NULL;
+    }
+
   if (priv->tooltip)
     {
       ClutterContainer *parent;
@@ -356,6 +367,28 @@ st_widget_allocate (ClutterActor          *actor,
           frame_box.y2 = frame_box.y1 + h;
         }
 
+        if (priv->background_image_shadow)
+          {
+            StShadowTexture *shadow;
+            ClutterActorBox  shadow_box;
+
+            shadow_box.x1 = frame_box.x1 + priv->shadow_xoffset;
+            shadow_box.y1 = frame_box.y1 + priv->shadow_yoffset;
+            shadow_box.x2 = frame_box.x2 + priv->shadow_xoffset;
+            shadow_box.y2 = frame_box.y2 + priv->shadow_yoffset;
+
+            /* The shadow texture is larger than the original image due
+               to blurring, so we let it adjust its size.
+               When the original image has been scaled, this will change
+               the effective blur radius - we ignore this for now. */
+            shadow = ST_SHADOW_TEXTURE (priv->background_image_shadow);
+            st_shadow_texture_adjust_allocation (shadow, &shadow_box);
+
+            clutter_actor_allocate (priv->background_image_shadow,
+                                    &shadow_box, flags);
+          }
+
+
       clutter_actor_allocate (CLUTTER_ACTOR (priv->background_image),
                               &frame_box,
                               flags);
@@ -497,7 +530,11 @@ st_widget_paint (ClutterActor *self)
   klass->draw_background (ST_WIDGET (self));
 
   if (priv->background_image != NULL)
-    clutter_actor_paint (priv->background_image);
+    {
+      if (priv->background_image_shadow)
+        clutter_actor_paint (priv->background_image_shadow);
+      clutter_actor_paint (priv->background_image);
+    }
 }
 
 static void
@@ -527,6 +564,9 @@ st_widget_map (ClutterActor *actor)
 
   st_widget_ensure_style ((StWidget*) actor);
 
+  if (priv->background_image_shadow)
+    clutter_actor_map (priv->background_image_shadow);
+
   if (priv->border_image)
     clutter_actor_map (priv->border_image);
 
@@ -544,6 +584,9 @@ st_widget_unmap (ClutterActor *actor)
 
   CLUTTER_ACTOR_CLASS (st_widget_parent_class)->unmap (actor);
 
+  if (priv->background_image_shadow)
+    clutter_actor_unmap (priv->background_image_shadow);
+
   if (priv->border_image)
     clutter_actor_unmap (priv->border_image);
 
@@ -659,6 +702,7 @@ st_widget_real_style_changed (StWidget *self)
   StWidgetPrivate *priv = ST_WIDGET (self)->priv;
   StThemeNode *theme_node;
   StBorderImage *border_image;
+  StShadow *shadow;
   StTextureCache *texture_cache;
   ClutterTexture *texture;
   const char *bg_file = NULL;
@@ -706,6 +750,12 @@ st_widget_real_style_changed (StWidget *self)
       has_changed = TRUE;
     }
 
+  if (priv->background_image_shadow)
+    {
+      clutter_actor_unparent (priv->background_image_shadow);
+      priv->background_image_shadow = NULL;
+    }
+
   if (priv->border_image)
     {
       clutter_actor_unparent (priv->border_image);
@@ -877,7 +927,39 @@ st_widget_real_style_changed (StWidget *self)
       relayout_needed = TRUE;
     }
 
-  /* If there are any properties above that need to cause a relayout thay
+  /* CSS based drop shadows
+   *
+   * Drop shadows in ST are modelled after the CSS3 box-shadow property;
+   * see http://www.css3.info/preview/box-shadow/ for a detailed description.
+   *
+   * While the syntax of the property is mostly identical - we do not support
+   * multiple shadows and allow for a more liberal placement of the color
+   * parameter - its interpretation defers significantly in that the shadow's
+   * shape is not determined by the bounding box, but by the CSS background
+   * image (we could exend this in the future to take other CSS properties
+   * like boder and background color into account).
+   */
+  shadow = st_theme_node_get_shadow (theme_node);
+  if (shadow != NULL)
+    {
+      priv->shadow_xoffset = shadow->xoffset;
+      priv->shadow_yoffset = shadow->yoffset;
+
+      if (priv->background_image)
+        {
+          priv->background_image_shadow =
+              st_shadow_texture_new (priv->background_image,
+                                     &shadow->color,
+                                     shadow->blur);
+
+          clutter_actor_set_parent (priv->background_image_shadow,
+                                    CLUTTER_ACTOR (self));
+          has_changed = TRUE;
+          relayout_needed = TRUE;
+        }
+    }
+
+  /* If there are any properties above that need to cause a relayout they
    * should set this flag.
    */
   if (has_changed)



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