[gnome-shell] Implement box-shadow for StWidget



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

    Implement box-shadow for StWidget
    
    Add support for 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 the element's other CSS properties - right now,
       only for background-images a shadow is drawn; it may be
       extended to more properties, if desired.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=603691

 src/Makefile-st.am         |    4 +
 src/st/st-box-shadow.c     |   89 +++++++++++++++
 src/st/st-box-shadow.h     |   42 +++++++
 src/st/st-shadow-texture.c |  256 ++++++++++++++++++++++++++++++++++++++++++++
 src/st/st-shadow-texture.h |   28 +++++
 src/st/st-theme-node.c     |   90 ++++++++++++++++
 src/st/st-theme-node.h     |    2 +
 src/st/st-widget.c         |   90 +++++++++++++++-
 8 files changed, 599 insertions(+), 2 deletions(-)
---
diff --git a/src/Makefile-st.am b/src/Makefile-st.am
index bd7d15b..b93bf35 100644
--- a/src/Makefile-st.am
+++ b/src/Makefile-st.am
@@ -75,6 +75,7 @@ st_source_h =					\
     st/st-clickable.h           \
     st/st-clipboard.h				\
     st/st-drawing-area.h        \
+    st/st-box-shadow.h                          \
     st/st-entry.h				\
     st/st-im-text.h				\
     st/st-label.h				\
@@ -83,6 +84,7 @@ st_source_h =					\
     st/st-scrollable.h				\
     st/st-scroll-bar.h				\
     st/st-scroll-view.h				\
+    st/st-shadow-texture.h                      \
     st/st-subtexture.h				\
     st/st-table.h				\
     st/st-table-child.h				\
@@ -114,6 +116,7 @@ st_source_c =					\
     st/st-clickable.c           \
     st/st-clipboard.c				\
     st/st-drawing-area.c        \
+    st/st-box-shadow.c                          \
     st/st-entry.c				\
     st/st-im-text.c				\
     st/st-label.c				\
@@ -122,6 +125,7 @@ st_source_c =					\
     st/st-scrollable.c				\
     st/st-scroll-bar.c				\
     st/st-scroll-view.c				\
+    st/st-shadow-texture.c                      \
     st/st-subtexture.c				\
     st/st-table.c				\
     st/st-table-child.c				\
diff --git a/src/st/st-box-shadow.c b/src/st/st-box-shadow.c
new file mode 100644
index 0000000..5fc6a73
--- /dev/null
+++ b/src/st/st-box-shadow.c
@@ -0,0 +1,89 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
+#include "st-box-shadow.h"
+
+/**
+ * SECTION: st-box-shadow
+ * @short_description: Boxed type for box-shadow attributes
+ *
+ * #StBoxShadow is a boxed type for storing attributes of the CSS3 box-shadow
+ * property.
+ * See http://www.css3.info/preview/box-shadow/
+ *
+ */
+
+/**
+ * st_box_shadow_new:
+ * @color: shadow's color
+ * @xoffset: horizontal offset
+ * @yoffset: vertical offset
+ * @blur: blur radius
+ *
+ * Creates a new #StBoxShadow
+ *
+ * Returns: the newly allocated shadow. Use st_box_shadow_free() when done
+ */
+StBoxShadow *
+st_box_shadow_new (ClutterColor *color,
+                   gdouble       xoffset,
+                   gdouble       yoffset,
+                   gdouble       blur)
+{
+  StBoxShadow *shadow;
+
+  shadow = g_slice_new (StBoxShadow);
+
+  shadow->color   = *color;
+  shadow->xoffset = xoffset;
+  shadow->yoffset = yoffset;
+  shadow->blur    = blur;
+
+  return shadow;
+}
+
+/**
+ * st_box_shadow_copy:
+ * @shadow: a #StBoxShadow
+ *
+ * Makes a copy of @shadow.
+ *
+ * Returns: an allocated copy of @shadow - the result must be freed with
+ *          st_box_shadow_free() when done
+ */
+StBoxShadow *
+st_box_shadow_copy (const StBoxShadow *shadow)
+{
+  g_return_val_if_fail (shadow != NULL, NULL);
+
+  return g_slice_dup (StBoxShadow, shadow);
+}
+
+/**
+ * st_box_shadow_free:
+ * @shadow: a #StBoxShadow
+ *
+ * Frees the shadow structure created with st_box_shadow_new() or
+ * st_box_shadow_copy()
+ */
+void
+st_box_shadow_free (StBoxShadow *shadow)
+{
+  g_return_if_fail (shadow != NULL);
+
+  g_slice_free (StBoxShadow, shadow);
+}
+
+GType
+st_box_shadow_get_type (void)
+{
+  static GType _st_box_shadow_type = 0;
+
+  if (G_UNLIKELY (_st_box_shadow_type == 0))
+    _st_box_shadow_type =
+        g_boxed_type_register_static ("StBoxShadow",
+                                      (GBoxedCopyFunc) st_box_shadow_copy,
+                                      (GBoxedFreeFunc) st_box_shadow_free);
+
+  return _st_box_shadow_type;
+}
diff --git a/src/st/st-box-shadow.h b/src/st/st-box-shadow.h
new file mode 100644
index 0000000..579e6f2
--- /dev/null
+++ b/src/st/st-box-shadow.h
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __ST_BOX_SHADOW__
+#define __ST_BOX_SHADOW__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_BOX_SHADOW              (st_box_shadow_get_type ())
+
+typedef struct _StBoxShadow StBoxShadow;
+
+/**
+ * StBoxShadow:
+ * @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 box-shadow property.
+ */
+struct _StBoxShadow {
+    ClutterColor color;
+    gdouble      xoffset;
+    gdouble      yoffset;
+    gdouble      blur;
+};
+
+GType        st_box_shadow_get_type (void) G_GNUC_CONST;
+
+StBoxShadow *st_box_shadow_new      (ClutterColor      *color,
+                                     gdouble            xoffset,
+                                     gdouble            yoffset,
+                                     gdouble            blur);
+StBoxShadow *st_box_shadow_copy     (const StBoxShadow *shadow);
+void         st_box_shadow_free     (StBoxShadow       *shadow);
+
+G_END_DECLS
+
+#endif /* __ST_BOX_SHADOW__ */
diff --git a/src/st/st-shadow-texture.c b/src/st/st-shadow-texture.c
new file mode 100644
index 0000000..842197e
--- /dev/null
+++ b/src/st/st-shadow-texture.c
@@ -0,0 +1,256 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include <math.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;
+
+  guint blur_window;
+};
+
+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 gboolean
+st_shadow_texture_create_shadow (StShadowTexture *st,
+                                 ClutterActor *actor,
+                                 GError **error)
+{
+  CoglHandle  texture, material;
+  guchar     *pixels_in, *pixels_out;
+  gint        width_in, height_in;
+  gint        width_out, height_out;
+  gboolean    success = FALSE;
+
+  g_return_val_if_fail (ST_IS_SHADOW_TEXTURE (st), FALSE);
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  /* 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_val_if_fail (CLUTTER_IS_TEXTURE (actor), FALSE);
+
+  texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (actor));
+  if (texture == COGL_INVALID_HANDLE)
+    return FALSE;
+
+  width_in  = cogl_texture_get_width  (texture);
+  height_in = cogl_texture_get_height (texture);
+
+  pixels_in  = g_malloc0 (width_in * height_in);
+
+  cogl_texture_get_data (texture, COGL_PIXEL_FORMAT_A_8, width_in, pixels_in);
+  cogl_texture_unref (texture);
+
+  if ((guint) st->sigma  == 0)
+    {
+      width_out  = width_in;
+      height_out = height_in;
+      pixels_out = g_memdup (pixels_in, width_out * height_out);
+    }
+  else
+    {
+      gdouble *kernel;
+      gint     n_values;
+      gint     x, y, i;
+
+      n_values = 2 * st->blur_window + 1;
+
+      width_out  = width_in  + n_values;
+      height_out = height_in + n_values;
+
+      pixels_out = g_malloc0 (width_out * height_out);
+
+      kernel = calculate_gaussian_kernel (st->sigma, n_values);
+
+      /* vertical blur */
+      for (x = 0; x < width_in; x++)
+        for (y = 0; y < height_out; y++)
+          {
+            gint index = y * width_out + x + st->blur_window;
+
+            for (i = 0; i < n_values; i++)
+              {
+                gint _y = y + i - n_values;
+                gint _index = _y * width_in + x;
+
+                if (_y >= 0 && _y <= height_in)
+                  pixels_out[index] += pixels_in[_index] * kernel[i];
+              }
+          }
+
+      /* horizontal blur */
+      for (y = 0; y < height_out; y++)
+        {
+            guchar *line = g_memdup (pixels_out + y * width_out, width_out);
+
+            for (x = 0; x < width_out; x++)
+              {
+                gint index = x + y * width_out;
+
+                pixels_out[index] = 0;
+                for (i = 0; i < n_values; i++)
+                  {
+                    gint _x = x + i - st->blur_window;
+
+                    if (_x > 0 && _x < width_out)
+                      pixels_out[index] += line[_x] * kernel[i];
+                  }
+              }
+
+            g_free (line);
+        }
+      g_free (kernel);
+    }
+
+  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,
+                                        width_out,
+                                        pixels_out);
+
+  cogl_material_set_layer_combine_constant (material, 0, &st->color);
+  cogl_material_set_layer (material, 0, texture);
+
+  success = cogl_material_set_layer_combine (material, 0,
+                                             "RGBA = MODULATE (CONSTANT,TEXTURE[A])",
+                                             error);
+
+  if (success)
+    {
+      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);
+
+  return success;
+}
+
+
+/**
+ * st_shadow_texture_get_blur_window_size:
+ * @shadow: a #StShadowTexture
+ *
+ * Get the size of the window used in the convulsion
+ *
+ * Returns: the blur window size
+ */
+guint
+st_shadow_texture_get_blur_window_size (StShadowTexture *shadow)
+{
+  g_return_val_if_fail (ST_IS_SHADOW_TEXTURE (shadow), 0);
+
+  return shadow->blur_window;
+}
+
+
+/**
+ * st_shadow_texture_new:
+ * @actor: the original actor
+ * @color: (allow-none): the shadow color
+ * @sigma: 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       sigma)
+{
+  GError *error = NULL;
+  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);
+  st->sigma = sigma;
+  st->blur_window = (guint) (5 * sigma - 2.5);
+
+  if (!st_shadow_texture_create_shadow (st, actor, &error))
+    {
+      if (error)
+        {
+          g_warning ("Failed to initialize shadow: %s", error->message);
+          g_error_free (error);
+        }
+      else
+        g_warning ("Failed to initialize shadow.");
+
+      g_object_unref (st);
+
+      return NULL;
+    }
+
+  return CLUTTER_ACTOR (st);
+}
+
+static void
+st_shadow_texture_init (StShadowTexture *st)
+{
+  st->sigma  = 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..5e2ebf4
--- /dev/null
+++ b/src/st/st-shadow-texture.h
@@ -0,0 +1,28 @@
+/* -*- 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 sigma);
+guint st_shadow_texture_get_blur_window_size (StShadowTexture *shadow);
+
+G_END_DECLS
+
+#endif /* __ST_SHADOW_TEXTURE__ */
diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c
index 29acbd8..e9f2585 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;
+  StBoxShadow   *box_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 box_shadow_computed : 1;
   guint link_type : 2;
 };
 
@@ -118,6 +120,12 @@ st_theme_node_finalize (GObject *object)
       node->border_image = NULL;
     }
 
+  if (node->box_shadow)
+    {
+      st_box_shadow_free (node->box_shadow);
+      node->box_shadow = NULL;
+    }
+
   if (node->background_image)
     g_free (node->background_image);
 
@@ -2113,6 +2121,88 @@ st_theme_node_get_border_image (StThemeNode *node)
   return NULL;
 }
 
+/**
+ * st_theme_node_get_box_shadow:
+ * @node: a #StThemeNode
+ *
+ * Gets the value for the box-shadow style property
+ *
+ * Return value: (transfer none): the box shadow, or %NULL
+ *   if there is no box shadow.
+ */
+StBoxShadow *
+st_theme_node_get_box_shadow (StThemeNode *node)
+{
+  int i;
+
+  if (node->box_shadow_computed)
+    return node->box_shadow;
+
+  node->box_shadow = NULL;
+  node->box_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, "box-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->box_shadow = st_box_shadow_new (&color,
+                                                xoffset, yoffset, blur);
+
+          return node->box_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..b46a75e 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-box-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);
+StBoxShadow   *st_theme_node_get_box_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..29a8c32 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,30 @@ st_widget_allocate (ClutterActor          *actor,
           frame_box.y2 = frame_box.y1 + h;
         }
 
+        if (priv->background_image_shadow)
+          {
+            StShadowTexture *shadow;
+            ClutterActorBox  shadow_box;
+            gdouble blur_size;
+
+            /* To avoid sharp edges of the blurred texture, the shadow texture
+               is larger than the original texture - on each side, blur_window_size
+               pixels are added, which we have to consider for the allocation box.
+               A blur_size of the entire blur_window_size will make the shadow quite
+               massive though, so we use a fraction.
+               A factor of .5 seems to work reasonably well. */
+            shadow = ST_SHADOW_TEXTURE (priv->background_image_shadow);
+            blur_size = .5 * st_shadow_texture_get_blur_window_size (shadow);
+
+            shadow_box.x1 = frame_box.x1 - blur_size + priv->shadow_xoffset;
+            shadow_box.y1 = frame_box.y1 - blur_size + priv->shadow_yoffset;
+            shadow_box.x2 = frame_box.x2 + blur_size + priv->shadow_xoffset;
+            shadow_box.y2 = frame_box.y2 + blur_size + priv->shadow_yoffset;
+
+            clutter_actor_allocate (priv->background_image_shadow, &shadow_box, flags);
+          }
+
+
       clutter_actor_allocate (CLUTTER_ACTOR (priv->background_image),
                               &frame_box,
                               flags);
@@ -497,7 +532,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 +566,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 +586,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 +704,7 @@ st_widget_real_style_changed (StWidget *self)
   StWidgetPrivate *priv = ST_WIDGET (self)->priv;
   StThemeNode *theme_node;
   StBorderImage *border_image;
+  StBoxShadow  *box_shadow;
   StTextureCache *texture_cache;
   ClutterTexture *texture;
   const char *bg_file = NULL;
@@ -706,6 +752,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 +929,41 @@ 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 the element's other CSS properties.
+   *
+   * Note that the current implementation only takes the background-image property into
+   * account though.
+   */
+  box_shadow = st_theme_node_get_box_shadow (theme_node);
+  if (box_shadow != NULL)
+    {
+      priv->shadow_xoffset = box_shadow->xoffset;
+      priv->shadow_yoffset = box_shadow->yoffset;
+
+      if (priv->background_image)
+        priv->background_image_shadow =
+            st_shadow_texture_new (priv->background_image,
+                                   &box_shadow->color,
+                                   box_shadow->blur);
+
+      if (priv->background_image_shadow)
+        {
+           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]