docs: Add example of a border added through ClutterEffect

Add a basic example showing how to implement a ClutterEffect
post_paint() function to overlay a highlight border over a
rectangular actor.
This commit is contained in:
Elliot Smith 2011-03-08 11:19:13 +00:00
parent 1d14e7468b
commit a81dbcf483
4 changed files with 479 additions and 0 deletions

View File

@ -18,6 +18,7 @@ noinst_PROGRAMS = \
animations-rotating \ animations-rotating \
animations-scaling \ animations-scaling \
animations-scaling-zoom \ animations-scaling-zoom \
effects-basic \
effects-built-in \ effects-built-in \
effects-custom-deform \ effects-custom-deform \
text-shadow \ text-shadow \
@ -83,6 +84,7 @@ animations_reuse_SOURCES = animations-reuse.c
animations_rotating_SOURCES = animations-rotating.c animations_rotating_SOURCES = animations-rotating.c
animations_scaling_SOURCES = animations-scaling.c animations_scaling_SOURCES = animations-scaling.c
animations_scaling_zoom_SOURCES = animations-scaling-zoom.c animations_scaling_zoom_SOURCES = animations-scaling-zoom.c
effects_basic_SOURCES = cb-border-effect.c cb-border-effect.h effects-basic.c
effects_built_in_SOURCES = effects-built-in.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 effects_custom_deform_SOURCES = cb-page-fold-effect.c cb-page-fold-effect.h effects-custom-deform.c
text_shadow_SOURCES = text-shadow.c text_shadow_SOURCES = text-shadow.c

View File

@ -0,0 +1,317 @@
#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;
ClutterActorMeta *meta = CLUTTER_ACTOR_META (self);
/* check that the effect is enabled before applying it */
if (!clutter_actor_meta_get_enabled (meta))
return;
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;
}

View File

@ -0,0 +1,54 @@
#ifndef __CB_BORDER_EFFECT_H__
#define __CB_BORDER_EFFECT_H__
#include <clutter/clutter.h>
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__ */

View File

@ -0,0 +1,106 @@
#include <stdlib.h>
#include <clutter/clutter.h>
#include "cb-border-effect.h"
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
static ClutterColor red_color = { 0xff, 0x00, 0x00, 0xff };
static gboolean
add_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 *effect;
gchar *filename;
guint i;
GError *error = NULL;
if (argc < 2)
{
g_print ("Usage: %s <image files>\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);
clutter_actor_add_constraint (box, clutter_bind_constraint_new (stage, CLUTTER_BIND_WIDTH, 0.0));
/* 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 5 pixel red border effect */
effect = cb_border_effect_new (5.0, &red_color);
/* add the effect to the actor, but disabled */
clutter_actor_add_effect (texture, effect);
clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE);
/* on mouse click, toggle the "enabled" property of the border effect */
g_signal_connect (texture,
"button-press-event",
G_CALLBACK (add_highlight),
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;
}