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 + <type>ClutterEffect</type> + +
+ 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 + + + + +
+ <type>CbBackgroundEffect</type> + + + <filename>cb-background-effect.h</filename> (header file) + + + a code sample should be here... but isn't + + + + + + <filename>cb-background-effect.c</filename> (code file) + + + a code sample should be here... but isn't + + + +
+ +
+ <type>CbBorderEffect</type> + + This is a more sophisticated effect with configurable + border color and width. + + + <filename>cb-border-effect.h</filename> (header file) + + + a code sample should be here... but isn't + + + + + + <filename>cb-border-effect.c</filename> (code file) + + + a code sample should be here... but isn't + + + +
+ +
+ Application + + + Application which applies <type>CbBorderEffect</type> + and <type>CbBackgroundEffect</type> to a group of + <type>ClutterTextures</type>. + + + a code sample should be here... but isn't + + + +
+ +
+ +
+
Creating and animating a custom <type>ClutterDeformEffect</type> 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