diff --git a/doc/cookbook/Makefile.am b/doc/cookbook/Makefile.am
index c567cbdeb..5c70e4dfe 100644
--- a/doc/cookbook/Makefile.am
+++ b/doc/cookbook/Makefile.am
@@ -37,6 +37,8 @@ HTML_FILES = $(top_builddir)/doc/cookbook/html/*.html
CSS_FILES = $(top_builddir)/doc/cookbook/html/*.css
IMAGE_FILES = \
$(srcdir)/images/clutter-logo.png \
+ $(srcdir)/images/effects-basic.png \
+ $(srcdir)/images/effects-basic-background.png \
$(srcdir)/images/effects-built-in.png \
$(srcdir)/images/effects-custom-deform.png \
$(srcdir)/images/effects-custom-deform-back-material.png \
diff --git a/doc/cookbook/effects.xml b/doc/cookbook/effects.xml
index 2688bc941..79b5566e3 100644
--- a/doc/cookbook/effects.xml
+++ b/doc/cookbook/effects.xml
@@ -245,6 +245,536 @@ clutter_actor_add_effect (texture, effect);
+
+ Changing an actor's paint sequence using
+ ClutterEffect
+
+
+ Problem
+
+ You want to paint on top of or under an actor in a generic
+ way, without editing the actor's paint()
+ implementation. Example use cases are:
+
+
+
+ Adding a border on top of an actor.
+
+
+ Drawing a background for an actor.
+
+
+
+ A quick way to achieve the same thing (though not
+ readily portable between actors) is to connect a callback
+ before or after an actor's paint signal.
+ See this recipe for
+ more details. However, using a ClutterEffect
+ implementation, as explained in this recipe, is the preferred
+ approach.
+
+
+
+ Solution
+
+ Create a subclass of the ClutterEffect abstract
+ class; then implement the pre_paint() and/or
+ post_paint() virtual functions. When the
+ effect is applied to an actor, these functions will paint
+ before and after the actor's own paint()
+ implementation.
+
+
+ For this solution, we implement a simple
+ CbBackgroundEffect which draws a gray rectangle
+ under an actor. The full source is in
+ this
+ section. To keep it simple, the effect has no properties
+ and isn't configurable (the background is always gray); see the
+ border
+ effect for a more detailed implementation with GObject
+ trimmings.
+
+
+ First, create a ClutterEffect subclass. This
+ requires the trappings of a GObject class; in particular,
+ it needs a private struct to hold the effect's state. This
+ should include any CoglMaterials,
+ CoglColors or other private member variables
+ you intend to use to draw the effect.
+
+ In the case of the background effect, we have a background
+ CoglMaterial and a CoglColor for that
+ material:
+
+
+
+struct _CbBackgroundEffectPrivate
+{
+ CoglMaterial *background;
+ CoglColor *color;
+};
+
+
+
+ In the init() function for objects of
+ your class, create any Cogl resources which you need to draw the
+ effect. In the case of the background effect,
+ we need to create the CoglMaterial and
+ CoglColor for the private struct:
+
+
+
+priv = CB_BACKGROUND_EFFECT_GET_PRIVATE (self);
+
+ /* create the background material */
+ priv->background = cogl_material_new ();
+
+ /* gray color for filling the background material */
+ priv->color = cogl_color_new ();
+ cogl_color_init_from_4ub (priv->color, 122, 122, 122, 255);
+
+ /* set the color on the material; NB this isn't configurable
+ * for this effect, and is always gray
+ */
+ cogl_material_set_color (priv->background, priv->color);
+}
+]]>
+
+
+
+ Optionally, you can create GObject properties for
+ the class, if you want a configurable effect: see
+ this
+ section for details.
+
+ The dispose() function for your effect
+ should clean up any Cogl resources:
+
+
+
+priv;
+
+ if (priv->background != COGL_INVALID_HANDLE)
+ {
+ cogl_handle_unref (priv->background);
+ priv->background = COGL_INVALID_HANDLE;
+ }
+
+ if (priv->color != NULL)
+ {
+ cogl_color_free (priv->color);
+ priv->color = NULL;
+ }
+
+ G_OBJECT_CLASS (cb_background_effect_parent_class)->dispose (gobject);
+}
+]]>
+
+
+
+ Now, the important part: implement pre_paint()
+ and/or post_paint(), using Cogl to draw on the
+ material(s) set up for the effect.
+
+ For the background effect, we implement pre_paint(),
+ to draw a gray rectangle under the actor:
+
+
+
+priv;
+
+ /* get the associated actor's dimensions */
+ actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self));
+ clutter_actor_get_size (actor, &width, &height);
+
+ /* draw a Cogl rectangle in the background using the default color */
+ cogl_set_source (priv->background);
+
+ /* the rectangle has the same dimensions as the actor */
+ cogl_rectangle (0, 0, width, height);
+
+ return TRUE;
+}
+]]>
+
+
+
+ Now, in the init() function for the
+ effect class, assign your implementations to the
+ virtual methods of the ClutterEffect abstract class:
+
+
+
+pre_paint = cb_background_effect_pre_paint;
+ gobject_class->dispose = cb_background_effect_dispose;
+
+ g_type_class_add_private (klass, sizeof (CbBackgroundEffectPrivate));
+}
+]]>
+
+
+
+ If you intend to make your effect reusable, provide
+ a public constructor (as is done for the example effects in this
+ recipe):
+
+
+
+ClutterEffect *
+cb_background_effect_new ()
+{
+ return g_object_new (CB_TYPE_BACKGROUND_EFFECT,
+ NULL);
+}
+
+
+
+ The effect is now ready to be used. The application code
+ for applying your effect to an actor is the same as for any
+ other effect:
+
+
+
+ClutterActor *texture;
+ClutterEffect *background_effect;
+
+/* ...initialize texture, load image file etc... */
+
+/* create a gray background effect */
+background_effect = cb_background_effect_new ();
+
+/* apply the effect to the actor */
+clutter_actor_add_effect (texture, background_effect);
+
+
+
+ Below is an example of applying this effect to a texture loaded
+ with an image; the image has a transparent background, so the
+ background is visible through it. The screenshot is from
+ the example
+ application:
+
+
+
+
+
+
+
+ Applying CbBackgroundEffect
+ to a texture loaded with an image that has a transparent
+ background
+
+
+
+
+
+
+
+ Discussion
+
+ A basic ClutterEffect is particularly useful for
+ amending the appearance of an actor on the fly: for example,
+ to highlight an actor in response to a button presses. This
+ could be done by creating a custom widget
+ whose appearance could be toggled. But what if you wanted to make
+ an arbitrary actor's appearance "togglable"? A generic effect
+ in the style of the border effect in this recipe can be applied
+ to any actor, and easily toggled by enabling/disabling the
+ effect.
+
+ ClutterEffect works best where
+ you want to overlay or underlay the actor with Cogl paths or
+ primitives, without changing the actor's geometry. If you want
+ to do complicated geometry transformations, or other subtle
+ manipulations of an actor's appearance, it is better to use
+ a ClutterEffect subclass like
+ ClutterOffscreenEffect, ClutterDeformEffect,
+ or ClutterShaderEffect.
+
+ In a similar vein, when a ClutterEffect is
+ applied to an actor, the effect shouldn't paint outside the actor's
+ allocation. However, if the effect provides a
+ get_paint_volume() implementation which
+ returns a volume larger than the actor's allocation, the effect
+ can paint anywhere within that volume. Though
+ in most cases, creating a custom paint volume is only going to be
+ useful for offscreen effects, where you are changing the
+ actor's geometry.
+
+
+ Effect properties
+
+ If your effect has GObject properties, you should
+ ensure that an actor associated with the effect is queued
+ for a redraw when those properties change. (You only need to
+ do this for properties which change the effect's appearance;
+ but this is likely to include most of an effect's properties.)
+
+ In most cases, you're likely define standard GObject
+ properties for the class; for example,
+ CbBorderEffect
+ defines a width property like this:
+
+
+
+
+
+
+
+ It also defines a standard GObject
+ set_property() function for
+ width:
+
+
+
+
+
+
+
+ Note that this calls
+ cb_border_effect_set_width(), which is
+ also exposed in the public API. This is where the
+ width member variable is actually set in
+ the private struct; and also where the redraw for the actor
+ associated with the effect should be queued:
+
+
+
+width = width;
+
+ /* the property has been updated, so queue a redraw of the actor (if set) */
+ cb_border_effect_update (self);
+}
+]]>
+
+
+
+ Any other property setters which affect the associated
+ actor's appearance (i.e. color in the case of
+ CbBorderEffect) should also call the update
+ function after setting the property.
+
+
+ If your effect exposes GObject properties in this way,
+ it can also be animated with the Clutter animation API as usual.
+ For example, you could animate the border effect in this recipe
+ so that the border gradually becomes thinner or thicker.
+
+
+
+
+
+
+
+ Full example
+
+ The example application applies two effects to a
+ group of ClutterTextures:
+
+
+
+ A CbBackgroundEffect which draws a gray
+ background under each actor. The effect is implemented in
+ a header
+ file and a C
+ code file.
+
+
+ A CbBorderEffect which draws a
+ red border on top of an actor; this is toggled by clicking
+ on the actor. The effect is implemented in
+ a header
+ file and a C
+ code file.
+
+
+
+ The application
+ creates textures from the file paths specified
+ on the command line then applies both of these effects to
+ each texture. In the case of the CbBorderEffect,
+ a 5 pixel red border is applied; this is also disabled by default,
+ and enabled when a texture is clicked.
+
+ Here is an example of the output when the application is loaded
+ with four images:
+
+
+
+
+
+
+
+ Applying CbBackgroundEffect
+ and a togglable CbBorderEffect
+ to a several textures
+
+
+
+
+
+ CbBackgroundEffect
+
+
+ cb-background-effect.h (header file)
+
+
+ a code sample should be here... but isn't
+
+
+
+
+
+ cb-background-effect.c (code file)
+
+
+ a code sample should be here... but isn't
+
+
+
+
+
+
+ CbBorderEffect
+
+ This is a more sophisticated effect with configurable
+ border color and width.
+
+
+ cb-border-effect.h (header file)
+
+
+ a code sample should be here... but isn't
+
+
+
+
+
+ cb-border-effect.c (code file)
+
+
+ a code sample should be here... but isn't
+
+
+
+
+
+
+ Application
+
+
+ Application which applies CbBorderEffect
+ and CbBackgroundEffect to a group of
+ ClutterTextures.
+
+
+ a code sample should be here... but isn't
+
+
+
+
+
+
+
+
+
Creating and animating a custom ClutterDeformEffect
diff --git a/doc/cookbook/examples/Makefile.am b/doc/cookbook/examples/Makefile.am
index fa7a8781a..52fec52f7 100644
--- a/doc/cookbook/examples/Makefile.am
+++ b/doc/cookbook/examples/Makefile.am
@@ -18,6 +18,7 @@ noinst_PROGRAMS = \
animations-rotating \
animations-scaling \
animations-scaling-zoom \
+ effects-basic \
effects-built-in \
effects-custom-deform \
text-shadow \
@@ -83,6 +84,11 @@ animations_reuse_SOURCES = animations-reuse.c
animations_rotating_SOURCES = animations-rotating.c
animations_scaling_SOURCES = animations-scaling.c
animations_scaling_zoom_SOURCES = animations-scaling-zoom.c
+effects_basic_SOURCES = cb-border-effect.c \
+ cb-border-effect.h \
+ cb-background-effect.c \
+ cb-background-effect.h \
+ effects-basic.c
effects_built_in_SOURCES = effects-built-in.c
effects_custom_deform_SOURCES = cb-page-fold-effect.c cb-page-fold-effect.h effects-custom-deform.c
text_shadow_SOURCES = text-shadow.c
diff --git a/doc/cookbook/examples/cb-background-effect.c b/doc/cookbook/examples/cb-background-effect.c
new file mode 100644
index 000000000..44a42b8f7
--- /dev/null
+++ b/doc/cookbook/examples/cb-background-effect.c
@@ -0,0 +1,104 @@
+#include "cb-background-effect.h"
+
+G_DEFINE_TYPE (CbBackgroundEffect, cb_background_effect, CLUTTER_TYPE_EFFECT);
+
+#define CB_BACKGROUND_EFFECT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+ CB_TYPE_BACKGROUND_EFFECT, \
+ CbBackgroundEffectPrivate))
+
+struct _CbBackgroundEffectPrivate
+{
+ CoglMaterial *background;
+ CoglColor *color;
+};
+
+/* ClutterEffect implementation */
+
+/* note that if pre_paint() returns FALSE
+ * any post_paint() defined for the effect will not be called
+ */
+static gboolean
+cb_background_effect_pre_paint (ClutterEffect *self)
+{
+ ClutterActor *actor;
+ gfloat width;
+ gfloat height;
+ CbBackgroundEffectPrivate *priv;
+
+ priv = CB_BACKGROUND_EFFECT (self)->priv;
+
+ /* get the associated actor's dimensions */
+ actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self));
+ clutter_actor_get_size (actor, &width, &height);
+
+ /* draw a grey Cogl rectangle in the background */
+ cogl_set_source (priv->background);
+
+ cogl_rectangle (0, 0, width, height);
+
+ return TRUE;
+}
+
+/* GObject implementation */
+static void
+cb_background_effect_dispose (GObject *gobject)
+{
+ CbBackgroundEffectPrivate *priv = CB_BACKGROUND_EFFECT (gobject)->priv;
+
+ if (priv->background != COGL_INVALID_HANDLE)
+ {
+ cogl_handle_unref (priv->background);
+ priv->background = COGL_INVALID_HANDLE;
+ }
+
+ if (priv->color != NULL)
+ {
+ cogl_color_free (priv->color);
+ priv->color = NULL;
+ }
+
+ G_OBJECT_CLASS (cb_background_effect_parent_class)->dispose (gobject);
+}
+
+static void
+cb_background_effect_class_init (CbBackgroundEffectClass *klass)
+{
+ ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ effect_class->pre_paint = cb_background_effect_pre_paint;
+ gobject_class->dispose = cb_background_effect_dispose;
+
+ g_type_class_add_private (klass, sizeof (CbBackgroundEffectPrivate));
+}
+
+static void
+cb_background_effect_init (CbBackgroundEffect *self)
+{
+ CbBackgroundEffectPrivate *priv;
+
+ priv = self->priv = CB_BACKGROUND_EFFECT_GET_PRIVATE (self);
+
+ priv->background = cogl_material_new ();
+
+ /* grey color for filling the background material */
+ priv->color = cogl_color_new ();
+ cogl_color_init_from_4ub (priv->color, 122, 122, 122, 255);
+
+ cogl_material_set_color (priv->background, priv->color);
+}
+
+/* public API */
+
+/**
+ * cb_background_effect_new:
+ *
+ * Creates a new #ClutterEffect which adds a grey background
+ * when applied to a rectangular actor.
+ */
+ClutterEffect *
+cb_background_effect_new ()
+{
+ return g_object_new (CB_TYPE_BACKGROUND_EFFECT,
+ NULL);
+}
diff --git a/doc/cookbook/examples/cb-background-effect.h b/doc/cookbook/examples/cb-background-effect.h
new file mode 100644
index 000000000..b2ce987e3
--- /dev/null
+++ b/doc/cookbook/examples/cb-background-effect.h
@@ -0,0 +1,42 @@
+#ifndef __CB_BACKGROUND_EFFECT_H__
+#define __CB_BACKGROUND_EFFECT_H__
+
+#include
+
+GType cb_background_effect_get_type (void);
+
+#define CB_TYPE_BACKGROUND_EFFECT (cb_background_effect_get_type ())
+#define CB_BACKGROUND_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ CB_TYPE_BACKGROUND_EFFECT, \
+ CbBackgroundEffect))
+#define CB_IS_BACKGROUND_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ CB_TYPE_BACKGROUND_EFFECT))
+#define CB_BACKGROUND_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ CB_TYPE_BACKGROUND_EFFECT, \
+ CbBackgroundEffectClass))
+#define CB_IS_BACKGROUND_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ CB_TYPE_BACKGROUND_EFFECT))
+#define CB_BACKGROUND_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ CB_TYPE_BACKGROUND_EFFECT, \
+ CbBackgroundEffectClass))
+
+typedef struct _CbBackgroundEffectPrivate CbBackgroundEffectPrivate;
+typedef struct _CbBackgroundEffect CbBackgroundEffect;
+typedef struct _CbBackgroundEffectClass CbBackgroundEffectClass;
+
+/* object */
+struct _CbBackgroundEffect
+{
+ ClutterEffect parent_instance;
+ CbBackgroundEffectPrivate *priv;
+};
+
+/* class */
+struct _CbBackgroundEffectClass
+{
+ ClutterEffectClass parent_class;
+};
+
+ClutterEffect *cb_background_effect_new ();
+
+#endif /* __CB_BACKGROUND_EFFECT_H__ */
diff --git a/doc/cookbook/examples/cb-border-effect.c b/doc/cookbook/examples/cb-border-effect.c
new file mode 100644
index 000000000..3de316d44
--- /dev/null
+++ b/doc/cookbook/examples/cb-border-effect.c
@@ -0,0 +1,311 @@
+#include "cb-border-effect.h"
+
+G_DEFINE_TYPE (CbBorderEffect, cb_border_effect, CLUTTER_TYPE_EFFECT);
+
+#define CB_BORDER_EFFECT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+ CB_TYPE_BORDER_EFFECT, \
+ CbBorderEffectPrivate))
+
+static const ClutterColor grey = { 0xaa, 0xaa, 0xaa, 0xff };
+
+struct _CbBorderEffectPrivate
+{
+ CoglMaterial *border;
+ ClutterColor color;
+ gfloat width;
+};
+
+enum {
+ PROP_0,
+
+ PROP_COLOR,
+ PROP_WIDTH,
+
+ PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST];
+
+/* ClutterEffect implementation */
+static void
+cb_border_effect_post_paint (ClutterEffect *self)
+{
+ ClutterActor *actor;
+ gfloat width;
+ gfloat height;
+ CbBorderEffectPrivate *priv;
+
+ priv = CB_BORDER_EFFECT (self)->priv;
+
+ /* get the associated actor's dimensions */
+ actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self));
+ clutter_actor_get_size (actor, &width, &height);
+
+ /* draw Cogl rectangles on top */
+ cogl_set_source (priv->border);
+ cogl_path_new ();
+
+ /* left rectangle */
+ cogl_path_rectangle (0, 0, priv->width, height);
+
+ /* top rectangle */
+ cogl_path_rectangle (priv->width, 0, width, priv->width);
+
+ /* right rectangle */
+ cogl_path_rectangle (width - priv->width, priv->width, width, height);
+
+ /* bottom rectangle */
+ cogl_path_rectangle (priv->width,
+ height - priv->width,
+ width - priv->width,
+ height);
+
+ cogl_path_fill ();
+}
+
+/* GObject implementation */
+static void
+cb_border_effect_dispose (GObject *gobject)
+{
+ CbBorderEffectPrivate *priv = CB_BORDER_EFFECT (gobject)->priv;
+
+ if (priv->border != COGL_INVALID_HANDLE)
+ {
+ cogl_handle_unref (priv->border);
+ priv->border = COGL_INVALID_HANDLE;
+ }
+
+ G_OBJECT_CLASS (cb_border_effect_parent_class)->dispose (gobject);
+}
+
+static void
+cb_border_effect_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CbBorderEffect *effect = CB_BORDER_EFFECT (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_COLOR:
+ cb_border_effect_set_color (effect, clutter_value_get_color (value));
+ break;
+
+ case PROP_WIDTH:
+ cb_border_effect_set_width (effect, g_value_get_float (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cb_border_effect_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CbBorderEffectPrivate *priv = CB_BORDER_EFFECT (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_COLOR:
+ g_value_set_object (value, &(priv->color));
+ break;
+
+ case PROP_WIDTH:
+ g_value_set_float (value, priv->width);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+/* GObject class and instance init */
+static void
+cb_border_effect_class_init (CbBorderEffectClass *klass)
+{
+ ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ effect_class->post_paint = cb_border_effect_post_paint;
+
+ gobject_class->set_property = cb_border_effect_set_property;
+ gobject_class->get_property = cb_border_effect_get_property;
+ gobject_class->dispose = cb_border_effect_dispose;
+
+ g_type_class_add_private (klass, sizeof (CbBorderEffectPrivate));
+
+ /**
+ * CbBorderEffect:width:
+ *
+ * The width of the border
+ */
+ pspec = g_param_spec_float ("width",
+ "Width",
+ "The width of the border (in pixels)",
+ 1.0, 100.0,
+ 10.0,
+ G_PARAM_READWRITE);
+ obj_props[PROP_WIDTH] = pspec;
+ g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
+
+ /**
+ * CbBorderEffect:color:
+ *
+ * The color of the border
+ */
+ pspec = clutter_param_spec_color ("color",
+ "Color",
+ "The border color",
+ &grey,
+ G_PARAM_READWRITE);
+ obj_props[PROP_COLOR] = pspec;
+ g_object_class_install_property (gobject_class, PROP_COLOR, pspec);
+}
+
+static void
+cb_border_effect_init (CbBorderEffect *self)
+{
+ CbBorderEffectPrivate *priv;
+
+ priv = self->priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
+
+ priv->border = cogl_material_new ();
+
+ priv->color = grey;
+}
+
+/* called each time a property is set on the effect */
+static void
+cb_border_effect_update (CbBorderEffect *self)
+{
+ ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self));
+
+ if (actor != NULL)
+ clutter_actor_queue_redraw (actor);
+}
+
+/* public API */
+
+/**
+ * cb_border_effect_new:
+ * @width: width of the border applied by the effect
+ * @color: a #ClutterColor
+ *
+ * Creates a new #ClutterEffect with the given @width
+ * and of the given @color.
+ */
+ClutterEffect *
+cb_border_effect_new (gfloat width,
+ const ClutterColor *color)
+{
+ return g_object_new (CB_TYPE_BORDER_EFFECT,
+ "width", width,
+ "color", color,
+ NULL);
+}
+
+/**
+ * cb_border_effect_set_color:
+ * @self: a #CbBorderEffect
+ * @color: a #ClutterColor
+ *
+ * Sets the color of the border provided by the effect @self.
+ */
+void
+cb_border_effect_set_color (CbBorderEffect *self,
+ const ClutterColor *color)
+{
+ CbBorderEffectPrivate *priv;
+
+ g_return_if_fail (CB_IS_BORDER_EFFECT (self));
+ g_return_if_fail (color != NULL);
+
+ priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
+
+ priv->color.red = color->red;
+ priv->color.green = color->green;
+ priv->color.blue = color->blue;
+ priv->color.alpha = color->alpha;
+
+ cogl_material_set_color4ub (priv->border,
+ color->red,
+ color->green,
+ color->blue,
+ color->alpha);
+
+ cb_border_effect_update (self);
+}
+
+/**
+ * cb_border_effect_get_color:
+ * @self: a #CbBorderEffect
+ * @color: return location for a #ClutterColor
+ *
+ * Retrieves the color of the border applied by the effect @self.
+ */
+void
+cb_border_effect_get_color (CbBorderEffect *self,
+ ClutterColor *color)
+{
+ CbBorderEffectPrivate *priv;
+
+ g_return_if_fail (CB_IS_BORDER_EFFECT (self));
+
+ priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
+
+ color->red = priv->color.red;
+ color->green = priv->color.green;
+ color->blue = priv->color.blue;
+ color->alpha = priv->color.alpha;
+}
+
+/**
+ * cb_border_effect_set_width:
+ * @self: a #CbBorderEffect
+ * @width: the width of the border
+ *
+ * Sets the width (in pixels) of the border applied by the effect @self.
+ */
+void
+cb_border_effect_set_width (CbBorderEffect *self,
+ gfloat width)
+{
+ CbBorderEffectPrivate *priv;
+
+ g_return_if_fail (CB_IS_BORDER_EFFECT (self));
+
+ priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
+
+ priv->width = width;
+
+ cb_border_effect_update (self);
+}
+
+/**
+ * cb_border_effect_get_width:
+ * @self: a #CbBorderEffect
+ *
+ * Gets the width (in pixels) of the border applied by the effect @self.
+ *
+ * Return value: the border's width, or 0.0 if @self is not
+ * a #CbBorderEffect
+ */
+gfloat
+cb_border_effect_get_width (CbBorderEffect *self)
+{
+ CbBorderEffectPrivate *priv;
+
+ g_return_val_if_fail (CB_IS_BORDER_EFFECT (self), 0.0);
+
+ priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
+
+ return priv->width;
+}
diff --git a/doc/cookbook/examples/cb-border-effect.h b/doc/cookbook/examples/cb-border-effect.h
new file mode 100644
index 000000000..c4c6a3675
--- /dev/null
+++ b/doc/cookbook/examples/cb-border-effect.h
@@ -0,0 +1,54 @@
+#ifndef __CB_BORDER_EFFECT_H__
+#define __CB_BORDER_EFFECT_H__
+
+#include
+
+GType cb_border_effect_get_type (void);
+
+#define CB_TYPE_BORDER_EFFECT (cb_border_effect_get_type ())
+#define CB_BORDER_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ CB_TYPE_BORDER_EFFECT, \
+ CbBorderEffect))
+#define CB_IS_BORDER_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ CB_TYPE_BORDER_EFFECT))
+#define CB_BORDER_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ CB_TYPE_BORDER_EFFECT, \
+ CbBorderEffectClass))
+#define CB_IS_BORDER_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ CB_TYPE_BORDER_EFFECT))
+#define CB_BORDER_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ CB_TYPE_BORDER_EFFECT, \
+ CbBorderEffectClass))
+
+typedef struct _CbBorderEffectPrivate CbBorderEffectPrivate;
+typedef struct _CbBorderEffect CbBorderEffect;
+typedef struct _CbBorderEffectClass CbBorderEffectClass;
+
+/* object */
+struct _CbBorderEffect
+{
+ ClutterEffect parent_instance;
+ CbBorderEffectPrivate *priv;
+};
+
+/* class */
+struct _CbBorderEffectClass
+{
+ ClutterEffectClass parent_class;
+};
+
+ClutterEffect *cb_border_effect_new (gfloat width,
+ const ClutterColor *color);
+
+void cb_border_effect_set_color (CbBorderEffect *self,
+ const ClutterColor *color);
+
+void cb_border_effect_get_color (CbBorderEffect *self,
+ ClutterColor *color);
+
+void cb_border_effect_set_width (CbBorderEffect *self,
+ gfloat width);
+
+gfloat cb_border_effect_get_width (CbBorderEffect *self);
+
+#endif /* __CB_BORDER_EFFECT_H__ */
diff --git a/doc/cookbook/examples/effects-basic.c b/doc/cookbook/examples/effects-basic.c
new file mode 100644
index 000000000..875e4c8e5
--- /dev/null
+++ b/doc/cookbook/examples/effects-basic.c
@@ -0,0 +1,119 @@
+#include
+#include
+
+#include "cb-border-effect.h"
+#include "cb-background-effect.h"
+
+static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
+static ClutterColor red_color = { 0xff, 0x00, 0x00, 0xff };
+
+static gboolean
+toggle_highlight (ClutterActor *actor,
+ ClutterEvent *event,
+ gpointer user_data)
+{
+ ClutterActorMeta *meta = CLUTTER_ACTOR_META (user_data);
+
+ gboolean effect_enabled = clutter_actor_meta_get_enabled (meta);
+
+ clutter_actor_meta_set_enabled (meta, !effect_enabled);
+
+ clutter_actor_queue_redraw (actor);
+
+ return TRUE;
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ ClutterActor *stage;
+ ClutterActor *box;
+ ClutterLayoutManager *layout_manager;
+ ClutterActor *texture;
+ ClutterEffect *background_effect;
+ ClutterEffect *border_effect;
+ ClutterConstraint *width_constraint;
+ gchar *filename;
+ guint i;
+ GError *error = NULL;
+
+ if (argc < 2)
+ {
+ g_print ("Usage: %s \n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
+ return EXIT_FAILURE;
+
+ stage = clutter_stage_new ();
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+ clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
+ clutter_actor_set_size (stage, 600, 400);
+ g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
+
+ layout_manager = clutter_flow_layout_new (CLUTTER_FLOW_HORIZONTAL);
+ clutter_flow_layout_set_column_spacing (CLUTTER_FLOW_LAYOUT (layout_manager),
+ 10);
+ clutter_flow_layout_set_row_spacing (CLUTTER_FLOW_LAYOUT (layout_manager),
+ 10);
+
+ box = clutter_box_new (layout_manager);
+ width_constraint = clutter_bind_constraint_new (stage,
+ CLUTTER_BIND_WIDTH,
+ 0.0);
+ clutter_actor_add_constraint (box, width_constraint);
+
+ /* loop through the files specified on the command line, adding
+ * each one into the box
+ */
+ for (i = 1; i < argc; i++)
+ {
+ filename = argv[i];
+
+ texture = clutter_texture_new ();
+ clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture), TRUE);
+ clutter_actor_set_width (texture, 150);
+ clutter_actor_set_reactive (texture, TRUE);
+
+ clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
+ filename,
+ &error);
+
+ if (error != NULL)
+ g_warning ("Error loading file %s:\n%s",
+ filename,
+ error->message);
+
+ /* create a grey background effect */
+ background_effect = cb_background_effect_new ();
+
+ /* apply the effect to the actor */
+ clutter_actor_add_effect (texture, background_effect);
+
+ /* create a 5 pixel red border effect */
+ border_effect = cb_border_effect_new (5.0, &red_color);
+
+ /* apply the effect to the actor, but disabled */
+ clutter_actor_add_effect (texture, border_effect);
+ clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (border_effect),
+ FALSE);
+
+ /* on mouse click, toggle the "enabled" property of the border effect */
+ g_signal_connect (texture,
+ "button-press-event",
+ G_CALLBACK (toggle_highlight),
+ border_effect);
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (box), texture);
+ }
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
+
+ clutter_actor_show (stage);
+
+ clutter_main ();
+
+ return EXIT_SUCCESS;
+}
diff --git a/doc/cookbook/images/effects-basic-background.png b/doc/cookbook/images/effects-basic-background.png
new file mode 100644
index 000000000..737affe91
Binary files /dev/null and b/doc/cookbook/images/effects-basic-background.png differ
diff --git a/doc/cookbook/images/effects-basic.png b/doc/cookbook/images/effects-basic.png
new file mode 100644
index 000000000..38c36a258
Binary files /dev/null and b/doc/cookbook/images/effects-basic.png differ