Move rendering into st-theme-node-drawing.c

The idea behind this move is that we have a lot more control over
rendering if StWidget isn't a big pile of actors, and things are
more efficient.

https://bugzilla.gnome.org/show_bug.cgi?id=607500
This commit is contained in:
Colin Walters 2010-02-08 13:40:25 -05:00
parent a8fa8a498a
commit 5060081db5
11 changed files with 1231 additions and 1738 deletions

View File

@ -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)

View File

@ -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"

View File

@ -1,273 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include <math.h>
#include <string.h>
#include "st-shadow-texture.h"
/**
* SECTION: st-shadow-texture
* @short_description: a class for creating soft shadow textures
*
* #StShadowTexture is a #ClutterTexture holding a soft shadow texture for
* another #ClutterActor.
* It is used to implement the box-shadow property in StWidget and should
* not be used stand-alone.
*/
struct _StShadowTexture {
ClutterTexture parent;
CoglColor color;
gdouble sigma;
gdouble blur_radius;
};
struct _StShadowTextureClass {
ClutterTextureClass parent_class;
};
G_DEFINE_TYPE (StShadowTexture, st_shadow_texture, CLUTTER_TYPE_TEXTURE);
static gdouble *
calculate_gaussian_kernel (gdouble sigma, guint n_values)
{
gdouble *ret, sum;
gdouble exp_divisor;
gint half, i;
g_return_val_if_fail ((int) sigma > 0, NULL);
half = n_values / 2;
ret = g_malloc (n_values * sizeof (gdouble));
sum = 0.0;
exp_divisor = 2 * sigma * sigma;
/* n_values of 1D Gauss function */
for (i = 0; i < n_values; i++)
{
ret[i] = exp (-(i - half) * (i - half) / exp_divisor);
sum += ret[i];
}
/* normalize */
for (i = 0; i < n_values; i++)
ret[i] /= sum;
return ret;
}
static void
st_shadow_texture_create_shadow (StShadowTexture *st,
ClutterActor *actor)
{
CoglHandle texture, material;
guchar *pixels_in, *pixels_out;
gint width_in, height_in, rowstride_in;
gint width_out, height_out, rowstride_out;
g_return_if_fail (ST_IS_SHADOW_TEXTURE (st));
/* Right now we only deal with actors of type ClutterTexture.
It would be nice to extend this to generic actors with some
clutter_texture_new_from_actor magic in the future */
g_return_if_fail (CLUTTER_IS_TEXTURE (actor));
texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (actor));
if (texture == COGL_INVALID_HANDLE)
return;
width_in = cogl_texture_get_width (texture);
height_in = cogl_texture_get_height (texture);
rowstride_in = (width_in + 3) & ~3;
pixels_in = g_malloc0 (rowstride_in * height_in);
cogl_texture_get_data (texture, COGL_PIXEL_FORMAT_A_8,
rowstride_in, pixels_in);
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)
{
}

View File

@ -1,30 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __ST_SHADOW_TEXTURE__
#define __ST_SHADOW_TEXTURE__
#include <clutter/clutter.h>
G_BEGIN_DECLS
typedef struct _StShadowTexture StShadowTexture;
typedef struct _StShadowTextureClass StShadowTextureClass;
#define ST_TYPE_SHADOW_TEXTURE (st_shadow_texture_get_type ())
#define ST_SHADOW_TEXTURE(object) (G_TYPE_CHECK_INSTANCE_CAST((object),ST_TYPE_SHADOW_TEXTURE, StShadowTexture))
#define ST_IS_SHADOW_TEXTURE(object) (G_TYPE_CHECK_INSTANCE_TYPE((object),ST_TYPE_SHADOW_TEXTURE))
#define ST_SHADOW_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ST_TYPE_SHADOW_TEXTURE, StShadowTextureClass))
#define ST_IS_SHADOW_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ST_TYPE_SHADOW_TEXTURE))
#define ST_SHADOW_TEXTURE_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), ST_TYPE_SHADOW_TEXTURE, StShadowTextureClass))
GType st_shadow_texture_get_type (void) G_GNUC_CONST;
ClutterActor *st_shadow_texture_new (ClutterActor *actor,
ClutterColor *color,
gdouble blur_radius);
void st_shadow_texture_adjust_allocation (StShadowTexture *shadow,
ClutterActorBox *allocation);
G_END_DECLS
#endif /* __ST_SHADOW_TEXTURE__ */

View File

@ -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 <cogl/cogl.h>
#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;
}

View File

@ -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 <st/st.h> can be included directly.h"
#endif
#ifndef __ST_TEXTURE_FRAME_H__
#define __ST_TEXTURE_FRAME_H__
#include <clutter/clutter.h>
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__ */

File diff suppressed because it is too large Load Diff

View File

@ -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 <gdk/gdk.h>
#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__ */

View File

@ -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++)
{

View File

@ -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__ */

View File

@ -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);
}
if (CLUTTER_IS_CONTAINER (self))
notify_children_of_style_change ((ClutterContainer *)self);