diff --git a/src/Makefile-st.am b/src/Makefile-st.am index 706c58840..6b06688b0 100644 --- a/src/Makefile-st.am +++ b/src/Makefile-st.am @@ -83,13 +83,11 @@ 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 \ st/st-texture-cache.h \ - st/st-texture-frame.h \ st/st-theme.h \ st/st-theme-context.h \ st/st-theme-node.h \ @@ -109,7 +107,8 @@ BUILT_SOURCES += st.h st_source_private_h = \ st/st-private.h \ st/st-table-private.h \ - st/st-theme-private.h + st/st-theme-private.h \ + st/st-theme-node-private.h # please, keep this sorted alphabetically st_source_c = \ @@ -130,16 +129,15 @@ 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 \ st/st-texture-cache.c \ - st/st-texture-frame.c \ st/st-theme.c \ st/st-theme-context.c \ st/st-theme-node.c \ + st/st-theme-node-drawing.c \ st/st-tooltip.c \ st/st-widget.c \ $(NULL) diff --git a/src/st/st-button.c b/src/st/st-button.c index 2b8d7574e..7fbc101d8 100644 --- a/src/st/st-button.c +++ b/src/st/st-button.c @@ -45,7 +45,6 @@ #include "st-button.h" #include "st-marshal.h" -#include "st-texture-frame.h" #include "st-texture-cache.h" #include "st-private.h" diff --git a/src/st/st-shadow-texture.c b/src/st/st-shadow-texture.c deleted file mode 100644 index 04b6b90f7..000000000 --- a/src/st/st-shadow-texture.c +++ /dev/null @@ -1,273 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -#include -#include - -#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); - - 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_handle_unref (texture); - cogl_handle_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 deleted file mode 100644 index 3f5b28a25..000000000 --- a/src/st/st-shadow-texture.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -#ifndef __ST_SHADOW_TEXTURE__ -#define __ST_SHADOW_TEXTURE__ - -#include - -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-texture-frame.c b/src/st/st-texture-frame.c deleted file mode 100644 index 45b4bf9d2..000000000 --- a/src/st/st-texture-frame.c +++ /dev/null @@ -1,621 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * st-texture-frame.h: Expandible texture actor - * - * Copyright 2007 OpenedHand - * Copyright 2009 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU Lesser General Public License, - * version 2.1, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - * more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * Boston, MA 02111-1307, USA. - * - */ - -/** - * SECTION:st-texture-frame - * @short_description: Stretch a texture to fit the entire allocation - * - * #StTextureFrame - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "st-texture-frame.h" -#include "st-private.h" - -enum -{ - PROP_0, - - PROP_PARENT_TEXTURE, - - PROP_TOP, - PROP_RIGHT, - PROP_BOTTOM, - PROP_LEFT -}; - -G_DEFINE_TYPE (StTextureFrame, st_texture_frame, CLUTTER_TYPE_ACTOR); - -#define ST_TEXTURE_FRAME_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFramePrivate)) - -struct _StTextureFramePrivate -{ - ClutterTexture *parent_texture; - - gfloat top; - gfloat right; - gfloat bottom; - gfloat left; -}; - -static void -st_texture_frame_get_preferred_width (ClutterActor *self, - gfloat for_height, - gfloat *min_width_p, - gfloat *natural_width_p) -{ - StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv; - - if (G_UNLIKELY (priv->parent_texture == NULL)) - { - if (min_width_p) - *min_width_p = 0; - - if (natural_width_p) - *natural_width_p = 0; - } - else - { - ClutterActorClass *klass; - - /* by directly querying the parent texture's class implementation - * we are going around any override mechanism the parent texture - * might have in place, and we ask directly for the original - * preferred width - */ - klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture); - klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture), - for_height, - min_width_p, - natural_width_p); - } -} - -static void -st_texture_frame_get_preferred_height (ClutterActor *self, - gfloat for_width, - gfloat *min_height_p, - gfloat *natural_height_p) -{ - StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv; - - if (G_UNLIKELY (priv->parent_texture == NULL)) - { - if (min_height_p) - *min_height_p = 0; - - if (natural_height_p) - *natural_height_p = 0; - } - else - { - ClutterActorClass *klass; - - /* by directly querying the parent texture's class implementation - * we are going around any override mechanism the parent texture - * might have in place, and we ask directly for the original - * preferred height - */ - klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture); - klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture), - for_width, - min_height_p, - natural_height_p); - } -} - -static void -st_texture_frame_paint (ClutterActor *self) -{ - StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv; - CoglHandle cogl_texture = COGL_INVALID_HANDLE; - CoglHandle cogl_material = COGL_INVALID_HANDLE; - ClutterActorBox box = { 0, }; - gfloat width, height; - gfloat tex_width, tex_height; - gfloat ex, ey; - gfloat tx1, ty1, tx2, ty2; - guint8 opacity; - - /* no need to paint stuff if we don't have a texture */ - if (G_UNLIKELY (priv->parent_texture == NULL)) - return; - - /* parent texture may have been hidden, so need to make sure it gets - * realized - */ - if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture)) - clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture)); - - cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture); - if (cogl_texture == COGL_INVALID_HANDLE) - return; - cogl_material = clutter_texture_get_cogl_material (priv->parent_texture); - if (cogl_material == COGL_INVALID_HANDLE) - return; - - tex_width = cogl_texture_get_width (cogl_texture); - tex_height = cogl_texture_get_height (cogl_texture); - - clutter_actor_get_allocation_box (self, &box); - width = box.x2 - box.x1; - height = box.y2 - box.y1; - - tx1 = priv->left / tex_width; - tx2 = (tex_width - priv->right) / tex_width; - ty1 = priv->top / tex_height; - ty2 = (tex_height - priv->bottom) / tex_height; - - ex = width - priv->right; - if (ex < 0) - ex = priv->right; /* FIXME ? */ - - ey = height - priv->bottom; - if (ey < 0) - ey = priv->bottom; /* FIXME ? */ - - opacity = clutter_actor_get_paint_opacity (self); - - /* Paint using the parent texture's material. It should already have - the cogl texture set as the first layer */ - /* NB: for correct blending we need set a preumultiplied color here: */ - cogl_material_set_color4ub (cogl_material, - opacity, opacity, opacity, opacity); - cogl_set_source (cogl_material); - - { - GLfloat rectangles[] = - { - /* top left corner */ - 0, 0, priv->left, priv->top, - 0.0, 0.0, - tx1, ty1, - - /* top middle */ - priv->left, 0, ex, priv->top, - tx1, 0.0, - tx2, ty1, - - /* top right */ - ex, 0, width, priv->top, - tx2, 0.0, - 1.0, ty1, - - /* mid left */ - 0, priv->top, priv->left, ey, - 0.0, ty1, - tx1, ty2, - - /* center */ - priv->left, priv->top, ex, ey, - tx1, ty1, - tx2, ty2, - - /* mid right */ - ex, priv->top, width, ey, - tx2, ty1, - 1.0, ty2, - - /* bottom left */ - 0, ey, priv->left, height, - 0.0, ty2, - tx1, 1.0, - - /* bottom center */ - priv->left, ey, ex, height, - tx1, ty2, - tx2, 1.0, - - /* bottom right */ - ex, ey, width, height, - tx2, ty2, - 1.0, 1.0 - }; - - cogl_rectangles_with_texture_coords (rectangles, 9); - } -} - -static inline void -st_texture_frame_set_frame_internal (StTextureFrame *frame, - gfloat top, - gfloat right, - gfloat bottom, - gfloat left) -{ - StTextureFramePrivate *priv = frame->priv; - GObject *gobject = G_OBJECT (frame); - gboolean changed = FALSE; - - g_object_freeze_notify (gobject); - - if (priv->top != top) - { - priv->top = top; - g_object_notify (gobject, "top"); - changed = TRUE; - } - - if (priv->right != right) - { - priv->right = right; - g_object_notify (gobject, "right"); - changed = TRUE; - } - - if (priv->bottom != bottom) - { - priv->bottom = bottom; - g_object_notify (gobject, "bottom"); - changed = TRUE; - } - - if (priv->left != left) - { - priv->left = left; - g_object_notify (gobject, "left"); - changed = TRUE; - } - - if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (frame)); - - g_object_thaw_notify (gobject); -} - -static void -st_texture_frame_set_property (GObject *gobject, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - StTextureFrame *frame = ST_TEXTURE_FRAME (gobject); - StTextureFramePrivate *priv = frame->priv; - - switch (prop_id) - { - case PROP_PARENT_TEXTURE: - st_texture_frame_set_parent_texture (frame, - g_value_get_object (value)); - break; - - case PROP_TOP: - st_texture_frame_set_frame_internal (frame, - g_value_get_float (value), - priv->right, - priv->bottom, - priv->left); - break; - - case PROP_RIGHT: - st_texture_frame_set_frame_internal (frame, - priv->top, - g_value_get_float (value), - priv->bottom, - priv->left); - break; - - case PROP_BOTTOM: - st_texture_frame_set_frame_internal (frame, - priv->top, - priv->right, - g_value_get_float (value), - priv->left); - break; - - case PROP_LEFT: - st_texture_frame_set_frame_internal (frame, - priv->top, - priv->right, - priv->bottom, - g_value_get_float (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - break; - } -} - -static void -st_texture_frame_get_property (GObject *gobject, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - StTextureFramePrivate *priv = ST_TEXTURE_FRAME (gobject)->priv; - - switch (prop_id) - { - case PROP_PARENT_TEXTURE: - g_value_set_object (value, priv->parent_texture); - break; - - case PROP_LEFT: - g_value_set_float (value, priv->left); - break; - - case PROP_TOP: - g_value_set_float (value, priv->top); - break; - - case PROP_RIGHT: - g_value_set_float (value, priv->right); - break; - - case PROP_BOTTOM: - g_value_set_float (value, priv->bottom); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - break; - } -} - -static void -st_texture_frame_dispose (GObject *gobject) -{ - StTextureFramePrivate *priv = ST_TEXTURE_FRAME (gobject)->priv; - - if (priv->parent_texture) - { - g_object_unref (priv->parent_texture); - priv->parent_texture = NULL; - } - - G_OBJECT_CLASS (st_texture_frame_parent_class)->dispose (gobject); -} - -static void -st_texture_frame_class_init (StTextureFrameClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - GParamSpec *pspec; - - g_type_class_add_private (gobject_class, sizeof (StTextureFramePrivate)); - - actor_class->get_preferred_width = - st_texture_frame_get_preferred_width; - actor_class->get_preferred_height = - st_texture_frame_get_preferred_height; - actor_class->paint = st_texture_frame_paint; - - gobject_class->set_property = st_texture_frame_set_property; - gobject_class->get_property = st_texture_frame_get_property; - gobject_class->dispose = st_texture_frame_dispose; - - pspec = g_param_spec_object ("parent-texture", - "Parent Texture", - "The parent ClutterTexture", - CLUTTER_TYPE_TEXTURE, - ST_PARAM_READWRITE | - G_PARAM_CONSTRUCT); - g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec); - - pspec = g_param_spec_float ("left", - "Left", - "Left offset", - 0, G_MAXFLOAT, - 0, - ST_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_LEFT, pspec); - - pspec = g_param_spec_float ("top", - "Top", - "Top offset", - 0, G_MAXFLOAT, - 0, - ST_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_TOP, pspec); - - pspec = g_param_spec_float ("bottom", - "Bottom", - "Bottom offset", - 0, G_MAXFLOAT, - 0, - ST_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec); - - pspec = g_param_spec_float ("right", - "Right", - "Right offset", - 0, G_MAXFLOAT, - 0, - ST_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_RIGHT, pspec); -} - -static void -st_texture_frame_init (StTextureFrame *self) -{ - StTextureFramePrivate *priv; - - self->priv = priv = ST_TEXTURE_FRAME_GET_PRIVATE (self); -} - -/** - * st_texture_frame_new: - * @texture: a #ClutterTexture or %NULL - * @left: left margin preserving its content - * @top: top margin preserving its content - * @right: right margin preserving its content - * @bottom: bottom margin preserving its content - * - * A #StTextureFrame is a specialized texture that efficiently clones - * an area of the given @texture while keeping preserving portions of the - * same texture. - * - * A #StTextureFrame can be used to make a rectangular texture fit a - * given size without stretching its borders. - * - * Return value: the newly created #StTextureFrame - */ -ClutterActor* -st_texture_frame_new (ClutterTexture *texture, - gfloat top, - gfloat right, - gfloat bottom, - gfloat left) -{ - g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL); - - return g_object_new (ST_TYPE_TEXTURE_FRAME, - "parent-texture", texture, - "top", top, - "right", right, - "bottom", bottom, - "left", left, - NULL); -} - -/** - * st_texture_frame_get_parent_texture: - * @frame: A #StTextureFrame - * - * Return the texture used by the #StTextureFrame - * - * Returns: (transfer none): a #ClutterTexture owned by the #StTextureFrame - */ -ClutterTexture * -st_texture_frame_get_parent_texture (StTextureFrame *frame) -{ - g_return_val_if_fail (ST_IS_TEXTURE_FRAME (frame), NULL); - - return frame->priv->parent_texture; -} - -/** - * st_texture_frame_set_parent_texture: - * @frame: A #StTextureFrame - * @texture: A #ClutterTexture - * - * Set the #ClutterTexture used by this #StTextureFrame - * - */ -void -st_texture_frame_set_parent_texture (StTextureFrame *frame, - ClutterTexture *texture) -{ - StTextureFramePrivate *priv; - gboolean was_visible; - - g_return_if_fail (ST_IS_TEXTURE_FRAME (frame)); - g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture)); - - priv = frame->priv; - - was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame); - - if (priv->parent_texture == texture) - return; - - if (priv->parent_texture) - { - g_object_unref (priv->parent_texture); - priv->parent_texture = NULL; - - if (was_visible) - clutter_actor_hide (CLUTTER_ACTOR (frame)); - } - - if (texture) - { - priv->parent_texture = g_object_ref_sink (texture); - - if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture)) - clutter_actor_show (CLUTTER_ACTOR (frame)); - } - - clutter_actor_queue_relayout (CLUTTER_ACTOR (frame)); - - g_object_notify (G_OBJECT (frame), "parent-texture"); -} - -/** - * st_texture_frame_set_frame: - * @frame: A #StTextureFrame - * @top: width of the top slice - * @right: width of the right slice - * @bottom: width of the bottom slice - * @left: width of the left slice - * - * Set the slice lines of the specified frame. The slices are calculated as - * widths from the edge of the frame. - * - */ -void -st_texture_frame_set_frame (StTextureFrame *frame, - gfloat top, - gfloat right, - gfloat bottom, - gfloat left) -{ - g_return_if_fail (ST_IS_TEXTURE_FRAME (frame)); - - st_texture_frame_set_frame_internal (frame, top, right, bottom, left); -} - -/** - * st_texture_frame_get_frame: - * @frame: A #StTextureFrame - * @top: width of the top slice - * @right: width of the right slice - * @bottom: width of the bottom slice - * @left: width of the left slice - * - * Retrieve the current slice lines from the specified frame. - * - */ -void -st_texture_frame_get_frame (StTextureFrame *frame, - gfloat *top, - gfloat *right, - gfloat *bottom, - gfloat *left) -{ - StTextureFramePrivate *priv; - - g_return_if_fail (ST_IS_TEXTURE_FRAME (frame)); - - priv = frame->priv; - - if (top) - *top = priv->top; - - if (right) - *right = priv->right; - - if (bottom) - *bottom = priv->bottom; - - if (left) - *left = priv->left; -} diff --git a/src/st/st-texture-frame.h b/src/st/st-texture-frame.h deleted file mode 100644 index a4e4463a6..000000000 --- a/src/st/st-texture-frame.h +++ /dev/null @@ -1,94 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * st-texture-frame.h: Expandible texture actor - * - * Copyright 2007, 2008 OpenedHand Ltd - * Copyright 2009 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU Lesser General Public License, - * version 2.1, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - * more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * Boston, MA 02111-1307, USA. - * - */ - -#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) -#error "Only can be included directly.h" -#endif - -#ifndef __ST_TEXTURE_FRAME_H__ -#define __ST_TEXTURE_FRAME_H__ - -#include - -G_BEGIN_DECLS - -#define ST_TYPE_TEXTURE_FRAME (st_texture_frame_get_type ()) -#define ST_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFrame)) -#define ST_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TEXTURE_FRAME, StTextureFrameClass)) -#define ST_IS_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TEXTURE_FRAME)) -#define ST_IS_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TEXTURE_FRAME)) -#define ST_TEXTURE_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFrameClass)) - -typedef struct _StTextureFrame StTextureFrame; -typedef struct _StTextureFramePrivate StTextureFramePrivate; -typedef struct _StTextureFrameClass StTextureFrameClass; - -/** - * StTextureFrame: - * - * The contents of this structure are private and should only be accessed - * through the public API. - */ -struct _StTextureFrame -{ - /*< private >*/ - ClutterActor parent_instance; - - StTextureFramePrivate *priv; -}; - -struct _StTextureFrameClass -{ - ClutterActorClass parent_class; - - /* padding for future expansion */ - void (*_clutter_box_1) (void); - void (*_clutter_box_2) (void); - void (*_clutter_box_3) (void); - void (*_clutter_box_4) (void); -}; - -GType st_texture_frame_get_type (void) G_GNUC_CONST; - -ClutterActor * st_texture_frame_new (ClutterTexture *texture, - gfloat top, - gfloat right, - gfloat bottom, - gfloat left); -void st_texture_frame_set_parent_texture (StTextureFrame *frame, - ClutterTexture *texture); -ClutterTexture *st_texture_frame_get_parent_texture (StTextureFrame *frame); -void st_texture_frame_set_frame (StTextureFrame *frame, - gfloat top, - gfloat right, - gfloat bottom, - gfloat left); -void st_texture_frame_get_frame (StTextureFrame *frame, - gfloat *top, - gfloat *right, - gfloat *bottom, - gfloat *left); - -G_END_DECLS - -#endif /* __ST_TEXTURE_FRAME_H__ */ diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c new file mode 100644 index 000000000..374a55c34 --- /dev/null +++ b/src/st/st-theme-node-drawing.c @@ -0,0 +1,1097 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* Drawing for StWidget. + + Copyright (C) 2009,2010 Red Hat, Inc. + + Contains code derived from: + rectangle.c: Rounded rectangle. + Copyright (C) 2008 litl, LLC. + st-shadow-texture.c: a class for creating soft shadow texture + Copyright (C) 2009 Florian Müllner + st-texture-frame.h: Expandible texture actor + Copyright 2007 OpenedHand + Copyright 2009 Intel Corporation. + + The St is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The St is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the St; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., 59 Temple Place - + Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +#include "st-theme-private.h" +#include "st-theme-context.h" +#include "st-texture-cache.h" +#include "st-theme-node-private.h" + +/***** + * Shadows + *****/ + +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 CoglHandle +create_shadow_material (StThemeNode *node, + CoglHandle src_texture) +{ + CoglColor color; + CoglHandle material; + CoglHandle texture; + StShadow *shadow_spec; + guchar *pixels_in, *pixels_out; + gint width_in, height_in, rowstride_in; + gint width_out, height_out, rowstride_out; + float sigma; + + shadow_spec = st_theme_node_get_shadow (node); + if (!shadow_spec) + return COGL_INVALID_HANDLE; + + /* 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 + */ + sigma = shadow_spec->blur / 1.9; + + width_in = cogl_texture_get_width (src_texture); + height_in = cogl_texture_get_height (src_texture); + rowstride_in = (width_in + 3) & ~3; + + pixels_in = g_malloc0 (rowstride_in * height_in); + + cogl_texture_get_data (src_texture, COGL_PIXEL_FORMAT_A_8, + rowstride_in, pixels_in); + + if ((guint) shadow_spec->blur == 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 * 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 (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); + } + + 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); + + g_free (pixels_in); + g_free (pixels_out); + + material = cogl_material_new (); + + cogl_color_set_from_4ub (&color, + shadow_spec->color.red, shadow_spec->color.green, + shadow_spec->color.blue, shadow_spec->color.alpha); + cogl_color_premultiply (&color); + + cogl_material_set_layer_combine_constant (material, 0, &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 not set the combine or constant color + * here, then in paint set the material color to the shadow_spec_color * + * paint_opacity.*/ + cogl_material_set_layer_combine (material, 0, + "RGBA = MODULATE (CONSTANT, TEXTURE[A])", + NULL); + + + cogl_handle_unref (texture); + + return material; +} + + +/**** + * Rounded corners + ****/ + +typedef struct { + ClutterColor color; + ClutterColor border_color_1; + ClutterColor border_color_2; + guint radius; + guint border_width_1; + guint border_width_2; +} StCornerSpec; + +static CoglHandle +create_corner_texture (StCornerSpec *corner) +{ + CoglHandle texture; + cairo_t *cr; + cairo_surface_t *surface; + guint rowstride; + guint8 *data; + guint size; + guint max_border_width; + + max_border_width = MAX(corner->border_width_2, corner->border_width_1); + size = 2 * MAX(max_border_width, corner->radius); + rowstride = size * 4; + data = g_new0 (guint8, size * rowstride); + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + size, size, + rowstride); + cr = cairo_create (surface); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_scale (cr, size, size); + + /* TODO support nonuniform border widths */ + + if (corner->border_width_1 < corner->radius) + { + double internal_radius = 0.5 * (1.0 - (double) corner->border_width_1 / corner->radius); + + if (corner->border_width_1 != 0) + { + cairo_set_source_rgba (cr, + corner->border_color_1.red / 255., + corner->border_color_1.green / 255., + corner->border_color_1.blue / 255., + corner->border_color_1.alpha / 255.); + + cairo_arc (cr, 0.5, 0.5, 0.5, 0, 2 * M_PI); + cairo_fill (cr); + } + + cairo_set_source_rgba (cr, + corner->color.red / 255., + corner->color.green / 255., + corner->color.blue / 255., + corner->color.alpha / 255.); + cairo_arc (cr, 0.5, 0.5, internal_radius, 0, 2 * M_PI); + cairo_fill (cr); + } + else + { + double radius; + + radius = (gdouble)corner->radius / corner->border_width_1; + + cairo_set_source_rgba (cr, + corner->border_color_1.red / 255., + corner->border_color_1.green / 255., + corner->border_color_1.blue / 255., + corner->border_color_1.alpha / 255.); + + cairo_arc (cr, radius, radius, radius, M_PI, 3 * M_PI / 2); + cairo_line_to (cr, 1.0 - radius, 0.0); + cairo_arc (cr, 1.0 - radius, radius, radius, 3 * M_PI / 2, 2 * M_PI); + cairo_line_to (cr, 1.0, 1.0 - radius); + cairo_arc (cr, 1.0 - radius, 1.0 - radius, radius, 0, M_PI / 2); + cairo_line_to (cr, radius, 1.0); + cairo_arc (cr, radius, 1.0 - radius, radius, M_PI / 2, M_PI); + cairo_fill (cr); + } + cairo_destroy (cr); + + cairo_surface_destroy (surface); + + texture = cogl_texture_new_from_data (size, size, + COGL_TEXTURE_NONE, +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + COGL_PIXEL_FORMAT_BGRA_8888_PRE, +#else + COGL_PIXEL_FORMAT_ARGB_8888_PRE, +#endif + COGL_PIXEL_FORMAT_ANY, + rowstride, + data); + g_free (data); + g_assert (texture != COGL_INVALID_HANDLE); + + return texture; +} + +static char * +corner_to_string (StCornerSpec *corner) +{ + return g_strdup_printf ("st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u", + corner->color.red, corner->color.blue, corner->color.green, corner->color.alpha, + corner->border_color_1.red, corner->border_color_1.green, corner->border_color_1.blue, corner->border_color_1.alpha, + corner->border_color_2.red, corner->border_color_2.green, corner->border_color_2.blue, corner->border_color_2.alpha, + corner->radius, + corner->border_width_1, + corner->border_width_2); +} + +typedef struct { + StThemeNode *node; + StCornerSpec *corner; +} LoadCornerData; + +static CoglHandle +load_corner (StTextureCache *cache, + const char *key, + void *datap, + GError **error) +{ + LoadCornerData *data = datap; + + return create_corner_texture (data->corner); +} + +/* To match the CSS specification, we want the border to look like it was + * drawn over the background. But actually drawing the border over the + * background will produce slightly bad antialiasing at the edges, so + * compute the effective border color instead. + */ +#define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8) +#define MULT(c,a) NORM(c*a) + +static void +premultiply (ClutterColor *color) +{ + guint t; + color->red = MULT (color->red, color->alpha); + color->green = MULT (color->green, color->alpha); + color->blue = MULT (color->blue, color->alpha); +} + +static void +unpremultiply (ClutterColor *color) +{ + if (color->alpha != 0) + { + color->red = (color->red * 255 + 127) / color->alpha; + color->green = (color->green * 255 + 127) / color->alpha; + color->blue = (color->blue * 255 + 127) / color->alpha; + } +} + +static void +over (const ClutterColor *source, + const ClutterColor *destination, + ClutterColor *result) +{ + guint t; + ClutterColor src = *source; + ClutterColor dst = *destination; + + premultiply (&src); + premultiply (&dst); + + result->alpha = src.alpha + NORM ((255 - src.alpha) * dst.alpha); + result->red = src.red + NORM ((255 - src.alpha) * dst.red); + result->green = src.green + NORM ((255 - src.alpha) * dst.green); + result->blue = src.blue + NORM ((255 - src.alpha) * dst.blue); + + unpremultiply (result); +} + +static CoglHandle +st_theme_node_lookup_corner (StThemeNode *node, + StCorner corner_id) +{ + CoglHandle texture; + char *key; + StTextureCache *cache; + StCornerSpec corner; + LoadCornerData data; + + if (node->border_radius[corner_id] == 0) + return COGL_INVALID_HANDLE; + + cache = st_texture_cache_get_default (); + + corner.radius = node->border_radius[corner_id]; + corner.color = node->background_color; + + switch (corner_id) + { + case ST_CORNER_TOPLEFT: + corner.border_width_1 = node->border_width[ST_SIDE_TOP]; + corner.border_width_2 = node->border_width[ST_SIDE_LEFT]; + over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1); + over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2); + break; + case ST_CORNER_TOPRIGHT: + corner.border_width_1 = node->border_width[ST_SIDE_TOP]; + corner.border_width_2 = node->border_width[ST_SIDE_RIGHT]; + over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1); + over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2); + break; + case ST_CORNER_BOTTOMRIGHT: + corner.border_width_1 = node->border_width[ST_SIDE_BOTTOM]; + corner.border_width_2 = node->border_width[ST_SIDE_RIGHT]; + over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1); + over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2); + break; + case ST_CORNER_BOTTOMLEFT: + corner.border_width_1 = node->border_width[ST_SIDE_BOTTOM]; + corner.border_width_2 = node->border_width[ST_SIDE_LEFT]; + over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1); + over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2); + break; + } + + key = corner_to_string (&corner); + + data.node = node; + data.corner = &corner; + texture = st_texture_cache_load (cache, key, ST_TEXTURE_CACHE_POLICY_NONE, load_corner, &data, NULL); + + g_free (key); + + return texture; +} + +static void +get_background_position (StThemeNode *self, + const ClutterActorBox *allocation, + ClutterActorBox *result) +{ + gfloat w, h; + + result->x1 = result->y1 = 0; + result->x2 = allocation->x2 - allocation->x1; + result->y2 = allocation->y2 - allocation->y1; + + w = cogl_texture_get_width (self->background_texture); + h = cogl_texture_get_height (self->background_texture); + + /* scale the background into the allocated bounds */ + if (w > result->x2 || h > result->y2) + { + gint new_h, new_w, offset; + gint box_w, box_h; + + box_w = (int) result->x2; + box_h = (int) result->y2; + + /* scale to fit */ + new_h = (int)((h / w) * ((gfloat) box_w)); + new_w = (int)((w / h) * ((gfloat) box_h)); + + if (new_h > box_h) + { + /* center for new width */ + offset = ((box_w) - new_w) * 0.5; + result->x1 = offset; + result->x2 = offset + new_w; + + result->y2 = box_h; + } + else + { + /* center for new height */ + offset = ((box_h) - new_h) * 0.5; + result->y1 = offset; + result->y2 = offset + new_h; + + result->x2 = box_w; + } + } + else + { + /* center the background on the widget */ + result->x1 = (int)(((allocation->x2 - allocation->x1) / 2) - (w / 2)); + result->y1 = (int)(((allocation->y2 - allocation->y1) / 2) - (h / 2)); + result->x2 = result->x1 + w; + result->y2 = result->y1 + h; + } +} + +/* Use of this function marks code which doesn't support + * non-uniform widths and/or colors. + */ +static gboolean +get_arbitrary_border (StThemeNode *node, + int *width, + ClutterColor *color) +{ + int w; + + w = st_theme_node_get_border_width (node, ST_SIDE_TOP); + if (w > 0) + { + if (width) + *width = w; + if (color) + st_theme_node_get_border_color (node, ST_SIDE_TOP, color); + return TRUE; + } + + if (width) + *width = 0; + return FALSE; +} + +static CoglHandle +st_theme_node_render_gradient (StThemeNode *node) +{ + CoglHandle texture; + int radius[4], i; + cairo_t *cr; + cairo_surface_t *surface; + cairo_pattern_t *pattern; + gboolean round_border = FALSE; + ClutterColor border_color; + int border_width; + guint rowstride; + guchar *data; + + rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, node->alloc_width); + data = g_new0 (guchar, node->alloc_height * rowstride); + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + node->alloc_width, + node->alloc_height, + rowstride); + cr = cairo_create (surface); + + /* TODO - support non-uniform border colors and widths */ + get_arbitrary_border (node, &border_width, &border_color); + + for (i = 0; i < 4; i++) + { + radius[i] = st_theme_node_get_border_radius (node, i); + if (radius[i] > 0) + round_border = TRUE; + } + + if (node->background_gradient_type == ST_GRADIENT_VERTICAL) + pattern = cairo_pattern_create_linear (0, 0, 0, node->alloc_height); + else if (node->background_gradient_type == ST_GRADIENT_HORIZONTAL) + pattern = cairo_pattern_create_linear (0, 0, node->alloc_width, 0); + else + { + gdouble cx, cy; + + cx = node->alloc_width / 2.; + cy = node->alloc_height / 2.; + pattern = cairo_pattern_create_radial (cx, cy, 0, cx, cy, MIN (cx, cy)); + } + + cairo_pattern_add_color_stop_rgba (pattern, 0, + node->background_color.red / 255., + node->background_color.green / 255., + node->background_color.blue / 255., + node->background_color.alpha / 255.); + cairo_pattern_add_color_stop_rgba (pattern, 1, + node->background_gradient_end.red / 255., + node->background_gradient_end.green / 255., + node->background_gradient_end.blue / 255., + node->background_gradient_end.alpha / 255.); + + if (round_border) + { + if (radius[ST_CORNER_TOPLEFT] > 0) + cairo_arc (cr, + radius[ST_CORNER_TOPLEFT], + radius[ST_CORNER_TOPLEFT], + radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2); + else + cairo_move_to (cr, 0, 0); + cairo_line_to (cr, node->alloc_width - radius[ST_CORNER_TOPRIGHT], 0); + if (radius[ST_CORNER_TOPRIGHT] > 0) + cairo_arc (cr, + node->alloc_width - radius[ST_CORNER_TOPRIGHT], + radius[ST_CORNER_TOPRIGHT], + radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI); + cairo_line_to (cr, node->alloc_width, node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT]); + if (radius[ST_CORNER_BOTTOMRIGHT]) + cairo_arc (cr, + node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT], + node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT], + radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2); + cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], node->alloc_height); + if (radius[ST_CORNER_BOTTOMLEFT]) + cairo_arc (cr, + radius[ST_CORNER_BOTTOMLEFT], + node->alloc_height - radius[ST_CORNER_BOTTOMLEFT], + radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI); + cairo_close_path (cr); + } + else + cairo_rectangle (cr, 0, 0, node->alloc_width, node->alloc_height); + + if (border_width > 0) + { + cairo_path_t *path; + + path = cairo_copy_path (cr); + cairo_set_source_rgba (cr, + border_color.red / 255., + border_color.green / 255., + border_color.blue / 255., + border_color.alpha / 255.); + cairo_fill (cr); + + cairo_translate (cr, border_width, border_width); + cairo_scale (cr, + (gdouble)(node->alloc_width - 2 * border_width) / node->alloc_width, + (gdouble)(node->alloc_height - 2 * border_width) / node->alloc_height); + cairo_append_path (cr, path); + cairo_path_destroy (path); + } + + cairo_set_source (cr, pattern); + cairo_fill (cr); + + cairo_pattern_destroy (pattern); + + texture = cogl_texture_new_from_data (node->alloc_width, node->alloc_height, + COGL_TEXTURE_NONE, +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + COGL_PIXEL_FORMAT_RGBA_8888_PRE, +#elif G_BYTE_ORDER == G_BIG_ENDIAN + COGL_PIXEL_FORMAT_ARGB_8888_PRE, +#else + COGL_PIXEL_FORMAT_ANY, +#error unknown endianness type +#endif + COGL_PIXEL_FORMAT_ANY, + rowstride, + data); + + cairo_destroy (cr); + cairo_surface_destroy (surface); + g_free (data); + + return texture; +} + +void +_st_theme_node_free_drawing_state (StThemeNode *node) +{ + if (node->background_texture != COGL_INVALID_HANDLE) + cogl_handle_unref (node->background_texture); + if (node->shadow_material != COGL_INVALID_HANDLE) + cogl_handle_unref (node->shadow_material); + if (node->border_texture != COGL_INVALID_HANDLE) + cogl_handle_unref (node->border_texture); + + _st_theme_node_init_drawing_state (node); +} + +void +_st_theme_node_init_drawing_state (StThemeNode *node) +{ + node->background_texture = COGL_INVALID_HANDLE; + node->shadow_material = COGL_INVALID_HANDLE; + node->border_texture = COGL_INVALID_HANDLE; +} + +static void +st_theme_node_render_resources (StThemeNode *node, + float width, + float height) +{ + StTextureCache *texture_cache; + StBorderImage *border_image; + const char *background_image; + + texture_cache = st_texture_cache_get_default (); + + /* FIXME - need to separate this into things that need to be recomputed on + * geometry change versus things that can be cached regardless, such as + * a background image. + */ + _st_theme_node_free_drawing_state (node); + + node->alloc_width = width; + node->alloc_height = height; + + _st_theme_node_ensure_background (node); + _st_theme_node_ensure_geometry (node); + + /* Load referenced images from disk and draw anything we need with cairo now */ + + border_image = st_theme_node_get_border_image (node); + if (border_image) + { + const char *filename; + + filename = st_border_image_get_filename (border_image); + + node->border_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, filename); + } + else if (node->background_gradient_type != ST_GRADIENT_NONE) + { + node->border_texture = st_theme_node_render_gradient (node); + } + + background_image = st_theme_node_get_background_image (node); + if (background_image != NULL) + { + StShadow *shadow_spec; + + node->background_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, background_image); + + shadow_spec = st_theme_node_get_shadow (node); + if (shadow_spec) + { + node->shadow_material = create_shadow_material (node, node->background_texture); + } + } + + node->corner_texture = st_theme_node_lookup_corner (node, ST_CORNER_TOPLEFT); +} + +static void +paint_texture_with_opacity (CoglHandle texture, + ClutterActorBox *box, + guint8 paint_opacity) +{ + if (paint_opacity == 255) + { + /* Minor: optimization use the default material if we can */ + cogl_set_source_texture (texture); + cogl_rectangle (box->x1, box->y1, box->x2, box->y2); + return; + } + + CoglHandle material; + + material = cogl_material_new (); + cogl_material_set_layer (material, 0, texture); + cogl_material_set_color4ub (material, + paint_opacity, paint_opacity, paint_opacity, paint_opacity); + + cogl_set_source (material); + cogl_rectangle (box->x1, box->y1, box->x2, box->y2); + + cogl_handle_unref (material); +} + +static void +st_theme_node_paint_borders (StThemeNode *node, + const ClutterActorBox *box, + guint8 paint_opacity) + +{ + float width, height; + int border_width; + int border_radius; + int max_width_radius; + ClutterColor border_color; + CoglHandle material; + + width = box->x2 - box->x1; + height = box->y2 - box->y1; + + get_arbitrary_border (node, &border_width, &border_color); + border_radius = node->border_radius[ST_CORNER_TOPLEFT]; + + max_width_radius = MAX(border_width, border_radius); + + /* borders */ + if (border_width > 0) + { + ClutterColor effective_border; + + over (&border_color, &node->background_color, &effective_border); + + cogl_set_source_color4ub (effective_border.red, + effective_border.green, + effective_border.blue, + paint_opacity * effective_border.alpha / 255); + + if (border_radius > 0) /* skip corners */ + { + /* NORTH */ + cogl_rectangle (max_width_radius, 0, + width - max_width_radius, border_width); + + /* EAST */ + cogl_rectangle (width - border_width, max_width_radius, + width, height - max_width_radius); + + /* SOUTH */ + cogl_rectangle (max_width_radius, height - border_width, + width - max_width_radius, height); + + /* WEST */ + cogl_rectangle (0, max_width_radius, + border_width, height - max_width_radius); + } + else /* include corners */ + { + /* NORTH */ + cogl_rectangle (0, 0, + width, border_width); + + /* EAST */ + cogl_rectangle (width - border_width, border_width, + width, height - border_width); + + /* SOUTH */ + cogl_rectangle (0, height - border_width, + width, height); + + /* WEST */ + cogl_rectangle (0, border_width, + border_width, height - border_width); + } + } + + /* corners */ + if (node->corner_texture != COGL_INVALID_HANDLE) + { + material = cogl_material_new (); + cogl_material_set_layer (material, 0, node->corner_texture); + cogl_material_set_color4ub (material, + paint_opacity, paint_opacity, paint_opacity, paint_opacity); + + cogl_set_source (material); + + cogl_rectangle_with_texture_coords (0, 0, max_width_radius, max_width_radius, 0, 0, 0.5, 0.5); + cogl_rectangle_with_texture_coords (width - max_width_radius, 0, width, max_width_radius, 0.5, 0, 1, 0.5); + cogl_rectangle_with_texture_coords (width - max_width_radius, height - max_width_radius, width, height, 0.5, 0.5, 1, 1); + cogl_rectangle_with_texture_coords (0, height - max_width_radius, max_width_radius, height, 0, 0.5, 0.5, 1); + + cogl_handle_unref (material); + } + + /* background color */ + cogl_set_source_color4ub (node->background_color.red, + node->background_color.green, + node->background_color.blue, + paint_opacity * node->background_color.alpha / 255); + + if (border_radius > border_width) + { + /* Once we've drawn the borders and corners, if the corners are bigger + * the the border width, the remaining area is shaped like + * + * ######## + * ########## + * ########## + * ######## + * + * We draw it in 3 pieces - first the top and bottom, then the main + * rectangle + */ + cogl_rectangle (border_radius, border_width, + width - border_radius, border_radius); + cogl_rectangle (border_radius, height - border_radius, + width - border_radius, height - border_width); + } + + cogl_rectangle (border_width, max_width_radius, + width - border_width, height - max_width_radius); +} + +static void +st_theme_node_paint_sliced_border_image (StThemeNode *node, + const ClutterActorBox *box, + guint8 paint_opacity) +{ + gfloat ex, ey; + gfloat tx1, ty1, tx2, ty2; + gint border_left, border_right, border_top, border_bottom; + float img_width, img_height; + StBorderImage *border_image; + CoglHandle material; + + border_image = st_theme_node_get_border_image (node); + g_assert (border_image != NULL); + + st_border_image_get_borders (border_image, + &border_left, &border_right, &border_top, &border_bottom); + + img_width = cogl_texture_get_width (node->border_texture); + img_height = cogl_texture_get_height (node->border_texture); + + tx1 = border_left / img_width; + tx2 = (img_width - border_right) / img_width; + ty1 = border_top / img_height; + ty2 = (img_height - border_bottom) / img_height; + + ex = node->alloc_width - border_right; + if (ex < 0) + ex = border_right; /* FIXME ? */ + + ey = node->alloc_height - border_bottom; + if (ey < 0) + ey = border_bottom; /* FIXME ? */ + + material = cogl_material_new (); + cogl_material_set_layer (material, 0, node->border_texture); + cogl_material_set_color4ub (material, + paint_opacity, paint_opacity, paint_opacity, paint_opacity); + + cogl_set_source (material); + + { + GLfloat rectangles[] = + { + /* top left corner */ + 0, 0, border_left, border_top, + 0.0, 0.0, + tx1, ty1, + + /* top middle */ + border_left, 0, ex, border_top, + tx1, 0.0, + tx2, ty1, + + /* top right */ + ex, 0, node->alloc_width, border_top, + tx2, 0.0, + 1.0, ty1, + + /* mid left */ + 0, border_top, border_left, ey, + 0.0, ty1, + tx1, ty2, + + /* center */ + border_left, border_top, ex, ey, + tx1, ty1, + tx2, ty2, + + /* mid right */ + ex, border_top, node->alloc_width, ey, + tx2, ty1, + 1.0, ty2, + + /* bottom left */ + 0, ey, border_left, node->alloc_height, + 0.0, ty2, + tx1, 1.0, + + /* bottom center */ + border_left, ey, ex, node->alloc_height, + tx1, ty2, + tx2, 1.0, + + /* bottom right */ + ex, ey, node->alloc_width, node->alloc_height, + tx2, ty2, + 1.0, 1.0 + }; + + cogl_rectangles_with_texture_coords (rectangles, 9); + } + + cogl_handle_unref (material); +} + +void +st_theme_node_paint (StThemeNode *node, + const ClutterActorBox *box, + guint8 paint_opacity) +{ + float width, height; + ClutterActorBox allocation; + + /* Some things take an ActorBox, some things just width/height */ + width = box->x2 - box->x1; + height = box->y2 - box->y1; + allocation.x1 = allocation.y1 = 0; + allocation.x2 = width; + allocation.y2 = height; + + if (node->alloc_width != width || node->alloc_height != height) + st_theme_node_render_resources (node, width, height); + + /* Rough notes about the relationship of borders and backgrounds in CSS3; + * see http://www.w3.org/TR/css3-background/ for more accurate details. + * + * - Things are drawn in 4 layers, from the bottom: + * Background color + * Background image + * Border color or border image + * Content + * - The background color, gradient and image extend to and are clipped by + * the edge of the border area, so will be rounded if the border is + * rounded. (CSS3 background-clip property modifies this) + * - The border image replaces what would normally be drawn by the border + * - The border image is not clipped by a rounded border-radius + * - The border radius rounds the background even if the border is + * zero width or a border image is being used. + * + * Deviations from the above as implemented here: + * - Nonuniform border widths combined with a non-zero border radius result + * in the border radius being ignored + * - The combination of border image and a non-zero border radius is + * not supported; the background color will be drawn with square + * corners. + * - The combination of border image and a background gradient is not + * supported; the background will be drawn as a solid color + * - The background image is drawn above the border color or image, + * not below it. + * - We don't clip the background image to the (rounded) border area. + * + * The first three allow us to always draw with no more than a single + * border_image and a single background image above it. + */ + + if (node->border_texture != COGL_INVALID_HANDLE) + { + /* Gradients and border images are mutually exclusive at this time */ + if (node->background_gradient_type != ST_GRADIENT_NONE) + paint_texture_with_opacity (node->border_texture, &allocation, paint_opacity); + else + st_theme_node_paint_sliced_border_image (node, &allocation, paint_opacity); + } + else + st_theme_node_paint_borders (node, box, paint_opacity); + + if (node->background_texture != COGL_INVALID_HANDLE) + { + ClutterActorBox background_box; + + get_background_position (node, &allocation, &background_box); + + /* 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). + */ + if (node->shadow_material != COGL_INVALID_HANDLE) + { + StShadow *shadow_spec; + ClutterActorBox shadow_box; + + shadow_spec = node->shadow; + + shadow_box.x1 = background_box.x1 + shadow_spec->xoffset - shadow_spec->blur; + shadow_box.y1 = background_box.y1 + shadow_spec->yoffset - shadow_spec->blur; + shadow_box.x2 = background_box.x2 + shadow_spec->xoffset + shadow_spec->blur; + shadow_box.y2 = background_box.y2 + shadow_spec->yoffset + shadow_spec->blur; + + cogl_material_set_color4ub (node->shadow_material, + paint_opacity, paint_opacity, paint_opacity, paint_opacity); + + cogl_set_source (node->shadow_material); + cogl_rectangle_with_texture_coords (shadow_box.x1, shadow_box.y1, shadow_box.x2, shadow_box.y2, + 0, 0, 1, 1); + } + + paint_texture_with_opacity (node->background_texture, &background_box, paint_opacity); + } +} diff --git a/src/st/st-theme-node-private.h b/src/st/st-theme-node-private.h new file mode 100644 index 000000000..6074d5a0c --- /dev/null +++ b/src/st/st-theme-node-private.h @@ -0,0 +1,86 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +#ifndef __ST_THEME_NODE_PRIVATE_H__ +#define __ST_THEME_NODE_PRIVATE_H__ + +#include + +#include "st-theme-node.h" + +G_BEGIN_DECLS + +struct _StThemeNode { + GObject parent; + + StThemeContext *context; + StThemeNode *parent_node; + StTheme *theme; + + PangoFontDescription *font_desc; + + ClutterColor background_color; + /* If gradient is set, then background_color is the gradient start */ + StGradientType background_gradient_type; + ClutterColor background_gradient_end; + + ClutterColor foreground_color; + ClutterColor border_color[4]; + + int border_width[4]; + int border_radius[4]; + guint padding[4]; + + int width; + int height; + int min_width; + int min_height; + int max_width; + int max_height; + + char *background_image; + StBorderImage *border_image; + StShadow *shadow; + + GType element_type; + char *element_id; + char *element_class; + char *pseudo_class; + char *inline_style; + + CRDeclaration **properties; + int n_properties; + + /* We hold onto these separately so we can destroy them on finalize */ + CRDeclaration *inline_properties; + + guint properties_computed : 1; + guint geometry_computed : 1; + guint background_computed : 1; + guint foreground_computed : 1; + guint border_image_computed : 1; + guint shadow_computed : 1; + guint link_type : 2; + + /* Graphics state */ + float alloc_width; + float alloc_height; + + CoglHandle shadow_material; + CoglHandle background_texture; + CoglHandle border_texture; + CoglHandle corner_texture; +}; + +struct _StThemeNodeClass { + GObjectClass parent_class; + +}; + +void _st_theme_node_ensure_background (StThemeNode *node); +void _st_theme_node_ensure_geometry (StThemeNode *node); + +void _st_theme_node_init_drawing_state (StThemeNode *node); +void _st_theme_node_free_drawing_state (StThemeNode *node); + +G_END_DECLS + +#endif /* __ST_THEME_NODE_PRIVATE_H__ */ diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c index 5492a9475..1b8b4d0b2 100644 --- a/src/st/st-theme-node.c +++ b/src/st/st-theme-node.c @@ -5,70 +5,12 @@ #include "st-theme-private.h" #include "st-theme-context.h" -#include "st-theme-node.h" +#include "st-theme-node-private.h" static void st_theme_node_init (StThemeNode *node); static void st_theme_node_class_init (StThemeNodeClass *klass); static void st_theme_node_finalize (GObject *object); -struct _StThemeNode { - GObject parent; - - StThemeContext *context; - StThemeNode *parent_node; - StTheme *theme; - - PangoFontDescription *font_desc; - - ClutterColor background_color; - /* If gradient is set, then background_color is the gradient start */ - StGradientType background_gradient_type; - ClutterColor background_gradient_end; - - ClutterColor foreground_color; - ClutterColor border_color[4]; - - int border_width[4]; - int border_radius[4]; - guint padding[4]; - - int width; - int height; - int min_width; - int min_height; - int max_width; - int max_height; - - char *background_image; - StBorderImage *border_image; - StShadow *shadow; - - GType element_type; - char *element_id; - char *element_class; - char *pseudo_class; - char *inline_style; - - CRDeclaration **properties; - int n_properties; - - /* We hold onto these separately so we can destroy them on finalize */ - CRDeclaration *inline_properties; - - guint properties_computed : 1; - guint geometry_computed : 1; - guint background_computed : 1; - guint foreground_computed : 1; - guint border_image_computed : 1; - guint shadow_computed : 1; - guint link_type : 2; -}; - -struct _StThemeNodeClass { - GObjectClass parent_class; - -}; - static const ClutterColor BLACK_COLOR = { 0, 0, 0, 0xff }; static const ClutterColor TRANSPARENT_COLOR = { 0, 0, 0, 0 }; @@ -77,6 +19,7 @@ G_DEFINE_TYPE (StThemeNode, st_theme_node, G_TYPE_OBJECT) static void st_theme_node_init (StThemeNode *node) { + _st_theme_node_init_drawing_state (node); } static void @@ -131,6 +74,8 @@ st_theme_node_finalize (GObject *object) if (node->background_image) g_free (node->background_image); + _st_theme_node_free_drawing_state (node); + G_OBJECT_CLASS (st_theme_node_parent_class)->finalize (object); } @@ -259,7 +204,7 @@ st_theme_node_get_pseudo_class (StThemeNode *node) return node->pseudo_class; } -static void +void ensure_properties (StThemeNode *node) { if (!node->properties_computed) @@ -1108,8 +1053,8 @@ do_size_property (StThemeNode *node, get_length_from_term_int (node, decl->value, FALSE, node_value); } -static void -ensure_geometry (StThemeNode *node) +void +_st_theme_node_ensure_geometry (StThemeNode *node) { int i, j; @@ -1192,7 +1137,7 @@ st_theme_node_get_border_width (StThemeNode *node, g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.); g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); return node->border_width[side]; } @@ -1204,7 +1149,7 @@ st_theme_node_get_border_radius (StThemeNode *node, g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.); g_return_val_if_fail (corner >= ST_CORNER_TOPLEFT && corner <= ST_CORNER_BOTTOMLEFT, 0.); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); return node->border_radius[corner]; } @@ -1214,7 +1159,7 @@ st_theme_node_get_width (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); return node->width; } @@ -1223,7 +1168,7 @@ st_theme_node_get_height (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); return node->height; } @@ -1232,7 +1177,7 @@ st_theme_node_get_min_width (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); return node->min_width; } @@ -1241,7 +1186,7 @@ st_theme_node_get_min_height (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); return node->min_height; } @@ -1250,7 +1195,7 @@ st_theme_node_get_max_width (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); return node->max_width; } @@ -1259,7 +1204,7 @@ st_theme_node_get_max_height (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); return node->max_height; } @@ -1281,8 +1226,8 @@ get_background_color_from_term (StThemeNode *node, return result; } -static void -ensure_background (StThemeNode *node) +void +_st_theme_node_ensure_background (StThemeNode *node) { int i; @@ -1429,7 +1374,7 @@ st_theme_node_get_background_color (StThemeNode *node, { g_return_if_fail (ST_IS_THEME_NODE (node)); - ensure_background (node); + _st_theme_node_ensure_background (node); *color = node->background_color; } @@ -1439,7 +1384,7 @@ st_theme_node_get_background_image (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); - ensure_background (node); + _st_theme_node_ensure_background (node); return node->background_image; } @@ -1500,7 +1445,7 @@ st_theme_node_get_background_gradient (StThemeNode *node, { g_return_if_fail (ST_IS_THEME_NODE (node)); - ensure_background (node); + _st_theme_node_ensure_background (node); *type = node->background_gradient_type; if (*type != ST_GRADIENT_NONE) @@ -1518,7 +1463,7 @@ st_theme_node_get_border_color (StThemeNode *node, g_return_if_fail (ST_IS_THEME_NODE (node)); g_return_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); *color = node->border_color[side]; } @@ -1530,7 +1475,7 @@ st_theme_node_get_padding (StThemeNode *node, g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.); g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); return node->padding[side]; } @@ -2353,7 +2298,7 @@ st_theme_node_adjust_preferred_width (StThemeNode *node, g_return_if_fail (ST_IS_THEME_NODE (node)); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); width_inc = get_width_inc (node); @@ -2420,7 +2365,7 @@ st_theme_node_adjust_preferred_height (StThemeNode *node, g_return_if_fail (ST_IS_THEME_NODE (node)); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); height_inc = get_height_inc (node); @@ -2461,7 +2406,7 @@ st_theme_node_get_content_box (StThemeNode *node, g_return_if_fail (ST_IS_THEME_NODE (node)); - ensure_geometry (node); + _st_theme_node_ensure_geometry (node); avail_width = allocation->x2 - allocation->x1; avail_height = allocation->y2 - allocation->y1; @@ -2501,8 +2446,8 @@ st_theme_node_geometry_equal (StThemeNode *node, { StSide side; - ensure_geometry (node); - ensure_geometry (other); + _st_theme_node_ensure_geometry (node); + _st_theme_node_ensure_geometry (other); for (side = ST_SIDE_TOP; side <= ST_SIDE_LEFT; side++) { diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h index 87c6884ee..2bc5e7ab4 100644 --- a/src/st/st-theme-node.h +++ b/src/st/st-theme-node.h @@ -171,6 +171,11 @@ void st_theme_node_get_content_box (StThemeNode *node, gboolean st_theme_node_geometry_equal (StThemeNode *node, StThemeNode *other); +void st_theme_node_paint (StThemeNode *node, + const ClutterActorBox *box, + guint8 paint_opacity); + + G_END_DECLS #endif /* __ST_THEME_NODE_H__ */ diff --git a/src/st/st-widget.c b/src/st/st-widget.c index 92e2d9a43..dba5a040e 100644 --- a/src/st/st-widget.c +++ b/src/st/st-widget.c @@ -38,9 +38,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" #include "st-tooltip.h" @@ -59,7 +57,6 @@ struct _StWidgetPrivate ClutterActor *border_image; ClutterActor *background_image; - ClutterActor *background_image_shadow; ClutterColor bg_color; guint border_width; @@ -68,6 +65,7 @@ struct _StWidgetPrivate StGradientType bg_gradient_type; ClutterColor bg_gradient_end; + ClutterActorBox background_allocation; gdouble shadow_xoffset; gdouble shadow_yoffset; @@ -125,7 +123,6 @@ G_DEFINE_ABSTRACT_TYPE (StWidget, st_widget, CLUTTER_TYPE_ACTOR); static void st_widget_recompute_style (StWidget *widget, StThemeNode *old_theme_node); -static void st_widget_redraw_gradient (StWidget *widget); static void st_widget_set_property (GObject *gobject, @@ -254,12 +251,6 @@ 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->theme_node) { g_object_unref (priv->theme_node); @@ -295,18 +286,20 @@ st_widget_finalize (GObject *gobject) G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject); } + static void st_widget_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { - StWidgetPrivate *priv = ST_WIDGET (actor)->priv; + StWidget *self = ST_WIDGET (actor); + StWidgetPrivate *priv = self->priv; StThemeNode *theme_node; ClutterActorClass *klass; ClutterGeometry area; ClutterVertex in_v, out_v; - theme_node = st_widget_get_theme_node ((StWidget*) actor); + theme_node = st_widget_get_theme_node (self); klass = CLUTTER_ACTOR_CLASS (st_widget_parent_class); klass->allocate (actor, box, flags); @@ -327,249 +320,20 @@ st_widget_allocate (ClutterActor *actor, st_tooltip_set_tip_area (priv->tooltip, &area); } - - - - if (priv->border_image && priv->bg_gradient_type == ST_GRADIENT_NONE) - { - ClutterActorBox frame_box; - - frame_box.x1 = frame_box.y1 = 0; - frame_box.x2 = box->x2 - box->x1; - frame_box.y2 = box->y2 - box->y1; - - clutter_actor_allocate (CLUTTER_ACTOR (priv->border_image), - &frame_box, - flags); - } - else if (priv->bg_gradient_type != ST_GRADIENT_NONE) - { - guint width, old_width, - height, old_height; - ClutterActorBox frame_box; - - frame_box.x1 = frame_box.y1 = 0; - frame_box.x2 = box->x2 - box->x1; - frame_box.y2 = box->y2 - box->y1; - - width = (guint)(0.5 + frame_box.x2); - height = (guint)(0.5 + frame_box.y2); - - clutter_cairo_texture_get_surface_size (CLUTTER_CAIRO_TEXTURE (priv->border_image), - &old_width, &old_height); - - if (width > 0 && height > 0 && - (old_width != width || old_height != height)) - { - - clutter_cairo_texture_set_surface_size (CLUTTER_CAIRO_TEXTURE (priv->border_image), - width, height); - st_widget_redraw_gradient ((StWidget*) actor); - } - clutter_actor_allocate (CLUTTER_ACTOR (priv->border_image), - &frame_box, - flags); - } - - if (priv->background_image) - { - ClutterActorBox frame_box; - gfloat w, h; - - frame_box.x1 = frame_box.y1 = 0; - frame_box.x2 = box->x2 - box->x1; - frame_box.y2 = box->y2 - box->y1; - - clutter_actor_get_size (CLUTTER_ACTOR (priv->background_image), &w, &h); - - /* scale the background into the allocated bounds */ - if (w > frame_box.x2 || h > frame_box.y2) - { - gint new_h, new_w, offset; - gint box_w, box_h; - - box_w = (int) frame_box.x2; - box_h = (int) frame_box.y2; - - /* scale to fit */ - new_h = (int)((h / w) * ((gfloat) box_w)); - new_w = (int)((w / h) * ((gfloat) box_h)); - - if (new_h > box_h) - { - /* center for new width */ - offset = ((box_w) - new_w) * 0.5; - frame_box.x1 = offset; - frame_box.x2 = offset + new_w; - - frame_box.y2 = box_h; - } - else - { - /* center for new height */ - offset = ((box_h) - new_h) * 0.5; - frame_box.y1 = offset; - frame_box.y2 = offset + new_h; - - frame_box.x2 = box_w; - } - - } - else - { - /* center the background on the widget */ - frame_box.x1 = (int)(((box->x2 - box->x1) / 2) - (w / 2)); - frame_box.y1 = (int)(((box->y2 - box->y1) / 2) - (h / 2)); - frame_box.x2 = frame_box.x1 + w; - 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); - } } static void -st_widget_real_draw_background (StWidget *self) +st_widget_paint (ClutterActor *actor) { - StWidgetPrivate *priv = self->priv; - ClutterActor *actor = CLUTTER_ACTOR (self); - ClutterActorBox allocation = { 0, }; - gfloat w, h; - guint8 opacity; + StWidget *self = ST_WIDGET (actor); + StThemeNode *theme_node; + ClutterActorBox allocation; + + theme_node = st_widget_get_theme_node (self); clutter_actor_get_allocation_box (actor, &allocation); - w = allocation.x2 - allocation.x1; - h = allocation.y2 - allocation.y1; - opacity = clutter_actor_get_paint_opacity (actor); - - /* Default implementation just draws the background - * colour and the image on top - */ - if (priv->draw_bg_color) - { - ClutterColor bg_color = priv->bg_color; - - bg_color.alpha = opacity * bg_color.alpha / 255; - - cogl_set_source_color4ub (bg_color.red, - bg_color.green, - bg_color.blue, - bg_color.alpha); - cogl_rectangle (0, 0, w, h); - } - - if (priv->draw_border_internal) - { - StThemeNode *node = st_widget_get_theme_node (self); - int side; - double border_top, border_right, border_bottom, border_left; - - border_top = st_theme_node_get_border_width (node, ST_SIDE_TOP); - border_right = st_theme_node_get_border_width (node, ST_SIDE_RIGHT); - border_bottom = st_theme_node_get_border_width (node, ST_SIDE_BOTTOM); - border_left = st_theme_node_get_border_width (node, ST_SIDE_LEFT); - - for (side = 0; side < 4; side++) - { - ClutterColor color; - - switch (side) - { - case ST_SIDE_TOP: - if (border_top <= 0) - continue; - break; - case ST_SIDE_RIGHT: - if (border_right <= 0) - continue; - break; - case ST_SIDE_BOTTOM: - if (border_bottom <= 0) - continue; - break; - case ST_SIDE_LEFT: - if (border_left <= 0) - continue; - break; - } - - st_theme_node_get_border_color (node, side, &color); - - color.alpha = (color.alpha * opacity) / 0xff; - - cogl_set_source_color4ub (color.red, - color.green, - color.blue, - color.alpha); - - /* Note top and bottom extend to the ends, left/right - * are constrained by them. See comment above about CSS - * conformance. - */ - switch (side) - { - case ST_SIDE_TOP: - cogl_rectangle (0, 0, - w, border_top); - break; - case ST_SIDE_RIGHT: - cogl_rectangle (w - border_right, border_top, - w, h - border_bottom); - break; - case ST_SIDE_BOTTOM: - cogl_rectangle (0, h - border_bottom, - w, h); - break; - case ST_SIDE_LEFT: - cogl_rectangle (0, border_top, - border_left, h - border_bottom); - break; - } - } - } - - if (priv->border_image) - clutter_actor_paint (priv->border_image); -} - -static void -st_widget_paint (ClutterActor *self) -{ - StWidgetPrivate *priv = ST_WIDGET (self)->priv; - - st_widget_real_draw_background (ST_WIDGET (self)); - - if (priv->background_image != NULL) - { - if (priv->background_image_shadow) - clutter_actor_paint (priv->background_image_shadow); - clutter_actor_paint (priv->background_image); - } + st_theme_node_paint (theme_node, &allocation, clutter_actor_get_paint_opacity (actor)); } static void @@ -599,9 +363,6 @@ 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); @@ -619,9 +380,6 @@ 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); @@ -632,125 +390,6 @@ st_widget_unmap (ClutterActor *actor) clutter_actor_unmap ((ClutterActor *) priv->tooltip); } -static void -st_widget_redraw_gradient (StWidget *widget) -{ - ClutterCairoTexture *texture; - ClutterColor *start, *end; - StWidgetPrivate *priv; - guint width, height; - guint radius[4], i; - cairo_t *cr; - cairo_pattern_t *pattern; - gboolean round_border = FALSE; - - if (widget->priv->bg_gradient_type == ST_GRADIENT_NONE) - return; - - texture = CLUTTER_CAIRO_TEXTURE (widget->priv->border_image); - priv = widget->priv; - start = &widget->priv->bg_color; - end = &widget->priv->bg_gradient_end; - - for (i = 0; i < 4; i++) - { - radius[i] = st_theme_node_get_border_radius (priv->theme_node, i); - if (radius[i] > 0) - round_border = TRUE; - } - - clutter_cairo_texture_get_surface_size (texture, &width, &height); - clutter_cairo_texture_clear (texture); - cr = clutter_cairo_texture_create (texture); - - if (priv->bg_gradient_type == ST_GRADIENT_VERTICAL) - pattern = cairo_pattern_create_linear (0, 0, 0, height); - else if (priv->bg_gradient_type == ST_GRADIENT_HORIZONTAL) - pattern = cairo_pattern_create_linear (0, 0, width, 0); - else - { - gdouble cx, cy; - - cx = width / 2.; - cy = height / 2.; - pattern = cairo_pattern_create_radial (cx, cy, 0, cx, cy, MIN (cx, cy)); - } - - cairo_pattern_add_color_stop_rgba (pattern, 0, - start->red / 255., - start->green / 255., - start->blue / 255., - start->alpha / 255.); - cairo_pattern_add_color_stop_rgba (pattern, 1, - end->red / 255., - end->green / 255., - end->blue / 255., - end->alpha / 255.); - - if (round_border) - { - if (radius[ST_CORNER_TOPLEFT] > 0) - cairo_arc (cr, - radius[ST_CORNER_TOPLEFT], - radius[ST_CORNER_TOPLEFT], - radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2); - else - cairo_move_to (cr, 0, 0); - cairo_line_to (cr, width - radius[ST_CORNER_TOPRIGHT], 0); - if (radius[ST_CORNER_TOPRIGHT] > 0) - cairo_arc (cr, - width - radius[ST_CORNER_TOPRIGHT], - radius[ST_CORNER_TOPRIGHT], - radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI); - cairo_line_to (cr, width, height - radius[ST_CORNER_BOTTOMRIGHT]); - if (radius[ST_CORNER_BOTTOMRIGHT]) - cairo_arc (cr, - width - radius[ST_CORNER_BOTTOMRIGHT], - height - radius[ST_CORNER_BOTTOMRIGHT], - radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2); - cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], height); - if (radius[ST_CORNER_BOTTOMLEFT]) - cairo_arc (cr, - radius[ST_CORNER_BOTTOMLEFT], - height - radius[ST_CORNER_BOTTOMLEFT], - radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI); - cairo_close_path (cr); - } - else - cairo_rectangle (cr, 0, 0, width, height); - - if (priv->border_width > 0) - { - guint8 opacity; - gdouble effective_alpha; - cairo_path_t *path; - - path = cairo_copy_path (cr); - opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (widget)); - effective_alpha = priv->border_color.alpha * opacity / (255. * 255.); - - cairo_set_source_rgba (cr, - priv->border_color.red / 255., - priv->border_color.green / 255., - priv->border_color.blue / 255., - effective_alpha); - cairo_fill (cr); - - cairo_translate (cr, priv->border_width, priv->border_width); - cairo_scale (cr, - (gdouble)(width - 2 * priv->border_width) / width, - (gdouble)(height - 2 * priv->border_width) / height); - cairo_append_path (cr, path); - cairo_path_destroy (path); - } - - cairo_set_source (cr, pattern); - cairo_fill (cr); - - cairo_pattern_destroy (pattern); - cairo_destroy (cr); -} - static void notify_children_of_style_change (ClutterContainer *container); static void @@ -776,270 +415,12 @@ static void 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; - gboolean relayout_needed = FALSE; - gboolean has_changed = FALSE; - ClutterColor color; - guint border_radius = 0; - StGradientType gradient; - ClutterColor gradient_end; - StSide side; - StCorner corner; - gboolean uniform_border_width; /* application has request this widget is not stylable */ if (!priv->is_stylable) return; - theme_node = st_widget_get_theme_node (self); - - st_theme_node_get_background_gradient (theme_node, &gradient, &color, &gradient_end); - - if (gradient == ST_GRADIENT_NONE) - { - st_theme_node_get_background_color (theme_node, &color); - if (gradient != priv->bg_gradient_type || - !clutter_color_equal (&color, &priv->bg_color)) - { - priv->bg_gradient_type = gradient; - priv->bg_color = color; - priv->draw_bg_color = color.alpha != 0; - has_changed = TRUE; - } - } - else if (gradient != priv->bg_gradient_type || - !clutter_color_equal (&color, &priv->bg_color) || - !clutter_color_equal (&gradient_end, &priv->bg_gradient_end)) - { - priv->bg_gradient_type = gradient; - priv->bg_color = color; - priv->bg_gradient_end = gradient_end; - priv->draw_bg_color = TRUE; - 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); - priv->border_image = NULL; - } - - if (priv->background_image) - { - clutter_actor_unparent (priv->background_image); - priv->background_image = NULL; - } - - texture_cache = st_texture_cache_get_default (); - - - /* Rough notes about the relationship of borders and backgrounds in CSS3; - * see http://www.w3.org/TR/css3-background/ for more accurate details. - * - * - Things are drawn in 4 layers, from the bottom: - * Background color - * Background image - * Border color or border image - * Content - * - The background color, gradient and image extend to and are clipped by - * the edge of the border area, so will be rounded if the border is - * rounded. (CSS3 background-clip property modifies this) - * - The border image replaces what would normally be drawn by the border - * - The border image is not clipped by a rounded border-radius - * - The border radius rounds the background even if the border is - * zero width or a border image is being used. - * - * Deviations from the above as implemented here: - * - Nonuniform border widths combined with a non-zero border radius result - * in the border radius being ignored - * - The combination of border image and a non-zero border radius is - * not supported; the background color will be drawn with square - * corners. - * - The combination of border image and a background gradient is not - * supported; the background will be drawn as a solid color - * - The background image is drawn above the border color or image, - * not below it. - * - We don't clip the background image to the (rounded) border area. - * - * The first three allow us to always draw with no more than a single - * border_image and a single background image above it. - */ - - /* Check whether all border widths are the same. Also, acquire the - * first nonzero border width as well as the border color. - */ - uniform_border_width = TRUE; - priv->border_width = st_theme_node_get_border_width (theme_node, - ST_SIDE_TOP); - if (priv->border_width > 0.5) - priv->border_width = (int)(0.5 + priv->border_width); - for (side = 0; side < 4; side++) - { - double width = st_theme_node_get_border_width (theme_node, side); - if (width > 0.5) - width = (int)(0.5 + width); - if (width > 0) - { - priv->border_width = width; - st_theme_node_get_border_color (theme_node, - side, &priv->border_color); - } - if ((int)width != priv->border_width) - { - uniform_border_width = FALSE; - break; - } - } - - /* Pick the first nonzero border radius, but only if we have a uniform border. */ - if (uniform_border_width) - { - for (corner = 0; corner < 4; corner++) - { - double radius = st_theme_node_get_border_radius (theme_node, corner); - if (radius > 0.5) - { - border_radius = (int)(0.5 + radius); - break; - } - } - } - - border_image = st_theme_node_get_border_image (theme_node); - if (border_image) - { - const char *filename; - gint border_left, border_right, border_top, border_bottom; - gint width, height; - - filename = st_border_image_get_filename (border_image); - - /* `border-image' takes precedence over `background-image'. - * Firefox lets the background-image shine thru when border-image has - * alpha an channel, maybe that would be an option for the future. */ - texture = (ClutterTexture*) st_texture_cache_load_file_simple (texture_cache, - filename); - - clutter_texture_get_base_size (CLUTTER_TEXTURE (texture), - &width, &height); - - st_border_image_get_borders (border_image, - &border_left, &border_right, &border_top, &border_bottom); - - priv->border_image = st_texture_frame_new (texture, - border_top, - border_right, - border_bottom, - border_left); - clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self)); - - has_changed = TRUE; - relayout_needed = TRUE; - } - else if (priv->bg_gradient_type != ST_GRADIENT_NONE) - { - priv->draw_border_internal = FALSE; - priv->draw_bg_color = FALSE; - texture = g_object_new (CLUTTER_TYPE_CAIRO_TEXTURE, NULL); - priv->border_image = CLUTTER_ACTOR (texture); - clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self)); - - has_changed = TRUE; - relayout_needed = TRUE; - } - else if (border_radius > 0) - { - priv->draw_border_internal = FALSE; - priv->draw_bg_color = FALSE; - priv->border_image = g_object_new (BIG_TYPE_RECTANGLE, - "color", &priv->bg_color, - "border-width", priv->border_width, - "border-color", &priv->border_color, - "corner-radius", border_radius, - NULL); - - clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self)); - - has_changed = TRUE; - relayout_needed = TRUE; - } - else if (priv->border_width > 0 && priv->border_color.alpha != 0) - { - priv->draw_bg_color = TRUE; - priv->draw_border_internal = TRUE; - has_changed = TRUE; - relayout_needed = TRUE; - } - else if (priv->draw_border_internal) - { - priv->draw_border_internal = FALSE; - has_changed = TRUE; - relayout_needed = TRUE; - } - - bg_file = st_theme_node_get_background_image (theme_node); - if (bg_file != NULL) - { - priv->background_image = st_texture_cache_load_file_simple (texture_cache, bg_file); - clutter_actor_set_parent (priv->background_image, CLUTTER_ACTOR (self)); - - has_changed = TRUE; - relayout_needed = TRUE; - } - - /* 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) - { - if (relayout_needed) - clutter_actor_queue_relayout ((ClutterActor *) self); - else - clutter_actor_queue_redraw ((ClutterActor *) self); - } + clutter_actor_queue_redraw ((ClutterActor *) self); if (CLUTTER_IS_CONTAINER (self)) notify_children_of_style_change ((ClutterContainer *)self);