From ba32170d94ba9645e56860049bf5d22a10633a5e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 8 Sep 2006 20:52:38 +0000 Subject: [PATCH] creating a branch for experimenting with the behaviour API --- clutter/Makefile.am | 82 ++--- clutter/clutter-alpha.c | 525 ++++++++++++++++++++++------- clutter/clutter-alpha.h | 103 +++--- clutter/clutter-behaviour.c | 634 +++++++++++++++++++++++++----------- clutter/clutter-behaviour.h | 156 +++++---- clutter/clutter.h | 3 +- examples/behave.c | 12 +- 7 files changed, 1055 insertions(+), 460 deletions(-) diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 38ff474db..468abda33 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -1,3 +1,5 @@ +NULL = + MARSHALFILES = clutter-marshal.c clutter-marshal.h ENUMFILES = clutter-enum-types.c clutter-enum-types.h GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0` @@ -5,26 +7,28 @@ GLIB_MKENUMS=`pkg-config --variable=glib_mkenums glib-2.0` BUILT_SOURCES = $(MARSHALFILES) $(ENUMFILES) -source_h = \ - $(srcdir)/clutter-keysyms.h \ - $(srcdir)/clutter-util.h \ - $(srcdir)/clutter-fixed.h \ - $(srcdir)/clutter-event.h \ - $(srcdir)/clutter-color.h \ - $(srcdir)/clutter-feature.h \ - $(srcdir)/clutter-timeline.h \ - $(srcdir)/clutter-actor.h \ - $(srcdir)/clutter-group.h \ - $(srcdir)/clutter-stage.h \ - $(srcdir)/clutter-rectangle.h \ - $(srcdir)/clutter-texture.h \ - $(srcdir)/clutter-clone-texture.h \ - $(srcdir)/clutter-label.h \ - $(srcdir)/clutter-behaviour.h \ - $(srcdir)/clutter-behaviours.h \ - $(srcdir)/clutter-alpha.h \ - $(srcdir)/clutter-media.h \ - $(srcdir)/clutter-main.h +source_h = \ + $(srcdir)/clutter-keysyms.h \ + $(srcdir)/clutter-util.h \ + $(srcdir)/clutter-fixed.h \ + $(srcdir)/clutter-event.h \ + $(srcdir)/clutter-color.h \ + $(srcdir)/clutter-feature.h \ + $(srcdir)/clutter-timeline.h \ + $(srcdir)/clutter-actor.h \ + $(srcdir)/clutter-group.h \ + $(srcdir)/clutter-stage.h \ + $(srcdir)/clutter-rectangle.h \ + $(srcdir)/clutter-texture.h \ + $(srcdir)/clutter-clone-texture.h \ + $(srcdir)/clutter-label.h \ + $(srcdir)/clutter-behaviour.h \ + $(srcdir)/clutter-opacity-behaviour.h \ + $(srcdir)/clutter-blink-behaviour.h \ + $(srcdir)/clutter-alpha.h \ + $(srcdir)/clutter-media.h \ + $(srcdir)/clutter-main.h \ + $(NULL) clutter-marshal.h: clutter-marshal.list ( $(GLIB_GENMARSHAL) --prefix=clutter_marshal \ @@ -71,24 +75,26 @@ clutter-enum-types.c: clutter-enum-types.h CLEANFILES = $(BUILT_SOURCES) stamp-clutter-enum-types.h -source_c = clutter-main.c \ - clutter-util.c \ - clutter-feature.c \ - clutter-event.c \ - clutter-color.c \ - clutter-timeline.c \ - clutter-group.c \ - clutter-stage.c \ - clutter-rectangle.c \ - clutter-texture.c \ - clutter-clone-texture.c \ - clutter-label.c \ - clutter-actor.c \ - clutter-behaviour.c \ - clutter-behaviours.c \ - clutter-alpha.c \ - clutter-media.c \ - clutter-enum-types.c +source_c = clutter-main.c \ + clutter-util.c \ + clutter-feature.c \ + clutter-event.c \ + clutter-color.c \ + clutter-timeline.c \ + clutter-group.c \ + clutter-stage.c \ + clutter-rectangle.c \ + clutter-texture.c \ + clutter-clone-texture.c \ + clutter-label.c \ + clutter-actor.c \ + clutter-behaviour.c \ + clutter-opacity-behaviour.c \ + clutter-blink-behaviour.c \ + clutter-alpha.c \ + clutter-media.c \ + clutter-enum-types.c \ + $(NULL) source_h_priv = clutter-private.h diff --git a/clutter/clutter-alpha.c b/clutter/clutter-alpha.c index df628d8aa..890c977d6 100644 --- a/clutter/clutter-alpha.c +++ b/clutter/clutter-alpha.c @@ -5,6 +5,7 @@ * * Authored By Matthew Allum * Jorn Baayen + * Emmanuele Bassi * * Copyright (C) 2006 OpenedHand * @@ -31,78 +32,137 @@ * * #ClutterAlpha is a class for calculating an alpha value as a function * of time. + * + * When you create a #ClutterAlpha object you should attach a #ClutterTimeline + * to it, to be used as a time source. You need to supply the alpha + * function to be invoked at each timeline "tick"; some common alpha functions + * are available. */ +#include "config.h" #include "clutter-alpha.h" #include "clutter-main.h" #include "clutter-marshal.h" -G_DEFINE_TYPE (ClutterAlpha, clutter_alpha, G_TYPE_OBJECT); +G_DEFINE_TYPE (ClutterAlpha, + clutter_alpha, + G_TYPE_INITIALLY_UNOWNED); -struct ClutterAlphaPrivate +struct _ClutterAlphaPrivate { ClutterTimeline *timeline; - guint timeline_new_frame_id; - guint32 alpha; + + guint32 alpha; + ClutterAlphaFunc func; + gpointer data; + GDestroyNotify destroy; + + gint delay; + guint is_paused : 1; }; enum { PROP_0, + PROP_TIMELINE, - PROP_FUNC, + PROP_DELAY, + PROP_IS_PAUSED, PROP_ALPHA }; /* Alpha funcs */ +/** + * clutter_alpha_ramp_inc_func: + * @alpha: a #ClutterAlpha + * @data: user data (ignored) + * + * Return value: + * + * Since: 0.2 + */ guint32 -clutter_alpha_ramp_inc_func (ClutterAlpha *alpha) +clutter_alpha_ramp_inc_func (ClutterAlpha *alpha, + gpointer data) { - int current_frame_num, nframes; + ClutterTimeline *timeline; + gint current_frame_num, nframes; - current_frame_num = - clutter_timeline_get_current_frame (alpha->priv->timeline); - nframes = - clutter_timeline_get_n_frames (alpha->priv->timeline); + g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), 0); - return (current_frame_num * CLUTTER_ALPHA_MAX_ALPHA) / nframes; + timeline = clutter_alpha_get_timeline (alpha); + + current_frame_num = clutter_timeline_get_current_frame (timeline); + nframes = clutter_timeline_get_n_frames (timeline); + + return (current_frame_num * CLUTTER_ALPHA_MAX) / nframes; } +/** + * clutter_alpha_ramp_dec_func: + * @alpha: a #ClutterAlpha + * @data: user data (ignored) + * + * Return value: + * + * Since: 0.2 + */ guint32 -clutter_alpha_ramp_dec_func (ClutterAlpha *alpha) +clutter_alpha_ramp_dec_func (ClutterAlpha *alpha, + gpointer data) { - int current_frame_num, nframes; + ClutterTimeline *timeline; + gint current_frame_num, nframes; - current_frame_num = - clutter_timeline_get_current_frame (alpha->priv->timeline); - nframes = - clutter_timeline_get_n_frames (alpha->priv->timeline); + g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), 0); - return ((nframes - current_frame_num) * CLUTTER_ALPHA_MAX_ALPHA) / nframes; + timeline = clutter_alpha_get_timeline (alpha); + + current_frame_num = clutter_timeline_get_current_frame (timeline); + nframes = clutter_timeline_get_n_frames (timeline); + + return ((nframes - current_frame_num) * CLUTTER_ALPHA_MAX) / nframes; } +/** + * clutter_alpha_ramp_func: + * @alpha: a #ClutterAlpha + * @data: user data (ignored) + * + * Return value: + * + * Since: 0.2 + */ guint32 -clutter_alpha_ramp_func (ClutterAlpha *alpha) +clutter_alpha_ramp_func (ClutterAlpha *alpha, + gpointer data) { - int current_frame_num, nframes; + ClutterTimeline *timeline; + gint current_frame_num, nframes; + guint32 retval; - current_frame_num = - clutter_timeline_get_current_frame (alpha->priv->timeline); - nframes = - clutter_timeline_get_n_frames (alpha->priv->timeline); + g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), 0); - if (current_frame_num > (nframes/2)) + timeline = clutter_alpha_get_timeline (alpha); + + current_frame_num = clutter_timeline_get_current_frame (timeline); + nframes = clutter_timeline_get_n_frames (timeline); + + if (current_frame_num > (nframes / 2)) { - return ((nframes - current_frame_num) - * CLUTTER_ALPHA_MAX_ALPHA) / (nframes/2); + retval = (nframes - current_frame_num) * CLUTTER_ALPHA_MAX; + retval = retval / (nframes / 2); } else { - return (current_frame_num * CLUTTER_ALPHA_MAX_ALPHA) / (nframes/2); + retval = current_frame_num * CLUTTER_ALPHA_MAX; + retval = retval / (nframes / 2); } + + return retval; } /* Object */ @@ -112,16 +172,24 @@ timeline_new_frame_cb (ClutterTimeline *timeline, guint current_frame_num, ClutterAlpha *alpha) { - ClutterAlphaPrivate *priv; + ClutterAlphaPrivate *priv = alpha->priv; - priv = alpha->priv; + if (priv->is_paused) + return; + + if ((priv->delay != -1) && (current_frame_num < priv->delay)) + return; /* Update alpha value */ - if (priv->func) { - priv->alpha = priv->func(alpha); - - g_object_notify (G_OBJECT (alpha), "alpha"); - } + if (priv->func) + { + g_object_ref (alpha); + + priv->alpha = priv->func (alpha, priv->data); + g_object_notify (G_OBJECT (alpha), "alpha"); + + g_object_unref (alpha); + } } static void @@ -130,19 +198,18 @@ clutter_alpha_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - ClutterAlpha *alpha; - ClutterAlphaPrivate *priv; - - alpha = CLUTTER_ALPHA(object); - priv = alpha->priv; + ClutterAlpha *alpha = CLUTTER_ALPHA (object); switch (prop_id) { case PROP_TIMELINE: clutter_alpha_set_timeline (alpha, g_value_get_object (value)); break; - case PROP_FUNC: - priv->func = g_value_get_pointer (value); + case PROP_DELAY: + alpha->priv->delay = g_value_get_int (value); + break; + case PROP_IS_PAUSED: + alpha->priv->is_paused = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -156,23 +223,24 @@ clutter_alpha_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - ClutterAlpha *alpha; ClutterAlphaPrivate *priv; - alpha = CLUTTER_ALPHA(object); - priv = alpha->priv; + priv = CLUTTER_ALPHA (object)->priv; switch (prop_id) { case PROP_TIMELINE: g_value_set_object (value, priv->timeline); break; - case PROP_FUNC: - g_value_set_pointer (value, priv->func); - break; case PROP_ALPHA: g_value_set_uint (value, priv->alpha); break; + case PROP_DELAY: + g_value_set_int (value, priv->delay); + break; + case PROP_IS_PAUSED: + g_value_set_boolean (value, priv->is_paused); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -182,13 +250,24 @@ clutter_alpha_get_property (GObject *object, static void clutter_alpha_finalize (GObject *object) { + ClutterAlphaPrivate *priv = CLUTTER_ALPHA (object)->priv; + + if (priv->destroy) + { + priv->destroy (priv->data); + + priv->data = NULL; + priv->destroy = NULL; + priv->func = NULL; + } + G_OBJECT_CLASS (clutter_alpha_parent_class)->finalize (object); } static void clutter_alpha_dispose (GObject *object) { - ClutterAlpha *self = CLUTTER_ALPHA(object); + ClutterAlpha *self = CLUTTER_ALPHA (object); clutter_alpha_set_timeline (self, NULL); @@ -201,7 +280,7 @@ clutter_alpha_class_init (ClutterAlphaClass *klass) { GObjectClass *object_class; - object_class = (GObjectClass*) klass; + object_class = G_OBJECT_CLASS (klass); object_class->set_property = clutter_alpha_set_property; object_class->get_property = clutter_alpha_get_property; @@ -210,52 +289,98 @@ clutter_alpha_class_init (ClutterAlphaClass *klass) g_type_class_add_private (klass, sizeof (ClutterAlphaPrivate)); - g_object_class_install_property - (object_class, PROP_TIMELINE, - g_param_spec_object ("timeline", - "Timeline", - "Timeline", - CLUTTER_TYPE_TIMELINE, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_FUNC, - g_param_spec_pointer ("func", - "Alpha function", - "Alpha function", - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_ALPHA, - g_param_spec_uint ("alpha", - "Alpha value", - "Alpha value", - 0, - CLUTTER_ALPHA_MAX_ALPHA, - 0, - G_PARAM_READABLE)); + /** + * ClutterAlpha:timeline + * + * The #ClutterTimeline object to be used when calculating + * the alpha value. + * + * Since: 0.2 + */ + g_object_class_install_property (object_class, + PROP_TIMELINE, + g_param_spec_object ("timeline", + "Timeline", + "Timeline", + CLUTTER_TYPE_TIMELINE, + G_PARAM_READWRITE)); + /** + * ClutterAlpha:alpha + * + * The last computed value of the alpha function. + * + * Since: 0.2 + */ + g_object_class_install_property (object_class, + PROP_ALPHA, + g_param_spec_uint ("alpha", + "Alpha", + "Alpha value", + CLUTTER_ALPHA_MIN, + CLUTTER_ALPHA_MAX, + CLUTTER_ALPHA_MIN, + G_PARAM_READABLE)); + /** + * ClutterAlpha:delay + * + * The number of frames that should be skipped before + * starting the calculation of the alpha function. + * + * Since: 0.2 + */ + g_object_class_install_property (object_class, + PROP_DELAY, + g_param_spec_int ("delay", + "Delay", + "The number of frames that should be skipped " + "before computing the alpha function", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + /** + * ClutterAlpha:is-paused + * + * Whether the #ClutterAlpha should be paused or not; + * the timeline bound to the #ClutterAlpha object will + * continue to run. + * + * Since: 0.2 + */ + g_object_class_install_property (object_class, + PROP_IS_PAUSED, + g_param_spec_boolean ("is-paused", + "Is Paused", + "Whether the alpha should be paused or not", + FALSE, + G_PARAM_READWRITE)); } static void clutter_alpha_init (ClutterAlpha *self) { - self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, - CLUTTER_TYPE_ALPHA, - ClutterAlphaPrivate); + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + CLUTTER_TYPE_ALPHA, + ClutterAlphaPrivate); self->priv->func = CLUTTER_ALPHA_RAMP_INC; + + self->priv->delay = -1; + self->priv->is_paused = FALSE; } /** - * clutter_alpha_get_alpha: + * clutter_alpha_get_value: * @alpha: A #ClutterAlpha * * Query the current alpha value. * - * Return Value: The current alpha value for the alpha + * Return value: The current alpha value for the alpha + * + * Since: 0.2 */ -gint32 -clutter_alpha_get_alpha (ClutterAlpha *alpha) +guint32 +clutter_alpha_get_value (ClutterAlpha *alpha) { g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), FALSE); @@ -266,74 +391,248 @@ clutter_alpha_get_alpha (ClutterAlpha *alpha) * clutter_alpha_set_func: * @alpha: A #ClutterAlpha * @func: A #ClutterAlphaAlphaFunc + * @data: the data to be passed to func or %NULL + * @destroy: the function to be called when removing the previous + * alpha function or %NULL * + * Since: 0.2 */ void -clutter_alpha_set_func (ClutterAlpha *alpha, - ClutterAlphaFunc func) +clutter_alpha_set_func (ClutterAlpha *alpha, + ClutterAlphaFunc func, + gpointer data, + GDestroyNotify destroy) { - alpha->priv->func = func; + ClutterAlphaPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ALPHA (alpha)); + g_return_if_fail (func != NULL); + + priv = alpha->priv; + + if (priv->destroy) + { + priv->destroy (priv->data); + priv->destroy = NULL; + priv->data = NULL; + } + + priv->func = func; + priv->data = data; + priv->destroy = destroy; } /** * clutter_alpha_set_timeline: * @alpha: A #ClutterAlpha - * @timeline: A #ClutterTimeline + * @timeline: A #ClutterTimeline or %NULL to unset the timeline * - * Binds @alpha to @timeline. + * Binds @timeline to @alpha. Since an alpha is a function of time, + * @timeline will be used to compute the alpha each time the "new-frame" + * signal of @timeline is emitted. + * + * Since: 0.2 */ void -clutter_alpha_set_timeline (ClutterAlpha *alpha, +clutter_alpha_set_timeline (ClutterAlpha *alpha, ClutterTimeline *timeline) { - if (alpha->priv->timeline) - { - g_signal_handler_disconnect (alpha->priv->timeline, - alpha->priv->timeline_new_frame_id); + ClutterAlphaPrivate *priv; - g_object_unref (alpha->priv->timeline); - alpha->priv->timeline = NULL; + g_return_if_fail (CLUTTER_IS_ALPHA (alpha)); + g_return_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline)); + + priv = alpha->priv; + + g_object_ref (alpha); + + if (priv->timeline) + { + g_signal_handlers_disconnect_by_func (priv->timeline, + timeline_new_frame_cb, + alpha); + + g_object_unref (priv->timeline); + priv->timeline = NULL; } if (timeline) { - alpha->priv->timeline = g_object_ref (timeline); + priv->timeline = g_object_ref (timeline); - alpha->priv->timeline_new_frame_id = - g_signal_connect (alpha->priv->timeline, - "new-frame", - G_CALLBACK (timeline_new_frame_cb), - alpha); + g_signal_connect (priv->timeline, "new-frame", + G_CALLBACK (timeline_new_frame_cb), + alpha); } + + g_object_notify (G_OBJECT (alpha), "timeline"); + + g_object_unref (alpha); } /** * clutter_alpha_get_timeline: * @alpha: A #ClutterAlpha * - * Return value: The #ClutterTimeline + * Gets the #ClutterTimeline object bound to @alpha. + * + * Return value: The #ClutterTimeline bount to @alpa or %NULL. + * + * Since: 0.2 */ ClutterTimeline * clutter_alpha_get_timeline (ClutterAlpha *alpha) { + g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), NULL); + return alpha->priv->timeline; } /** - * clutter_alpha_new: - * @timeline: #ClutterTimeline timeline - * @func: #ClutterAlphaFunc alpha function + * clutter_alpha_get_delay: + * @alpha: a #ClutterAlpha * - * Create a new #ClutterAlpha instance. + * Gets the delay used by @alpha. See clutter_alpha_set_delay(). * - * Return Value: a new #ClutterAlpha + * Return value: the number of frames to wait, or -1 + * + * Since: 0.2 */ -ClutterAlpha* -clutter_alpha_new (ClutterTimeline *timeline, - ClutterAlphaFunc func) +gint +clutter_alpha_get_delay (ClutterAlpha *alpha) { - return g_object_new (CLUTTER_TYPE_ALPHA, - "timeline", timeline, - "func", func, - NULL); + g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), -1); + + return alpha->priv->delay; +} + +/** + * clutter_alpha_set_delay: + * @alpha: a #ClutterAlpha + * @delay: the number of frames to wait or -1 for no delay + * + * Sets the number of timeline frames to wait before starting to + * compute the alpha value. + * + * Since: 0.2 + */ +void +clutter_alpha_set_delay (ClutterAlpha *alpha, + gint delay) +{ + g_return_if_fail (CLUTTER_IS_ALPHA (alpha)); + + if (alpha->priv->delay != delay) + { + g_object_ref (alpha); + + alpha->priv->delay = delay; + g_object_notify (G_OBJECT (alpha), "delay"); + + g_object_unref (alpha); + } +} + +/** + * clutter_alpha_get_is_paused: + * @alpha: a #ClutterAlpha + * + * Gets whether @alpha is in paused state or not. + * See clutter_alpha_set_is_paused(). + * + * Return value: %TRUE if @alpha is paused + * + * Since: 0.2 + */ +gboolean +clutter_alpha_get_is_paused (ClutterAlpha *alpha) +{ + g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), FALSE); + + return alpha->priv->is_paused; +} + +/** + * clutter_alpha_set_is_paused: + * @alpha: a #ClutterAlpha + * @is_paused: %TRUE to pause the alpha + * + * Pauses the calculation of the alpha value of @alpha. This is + * independent of the running state of the #ClutterTimeline bount + * to @alpha. + * + * Since: 0.2 + */ +void +clutter_alpha_set_is_paused (ClutterAlpha *alpha, + gboolean is_paused) +{ + g_return_if_fail (CLUTTER_IS_ALPHA (alpha)); + + if (alpha->priv->is_paused != is_paused) + { + g_object_ref (alpha); + + alpha->priv->is_paused = is_paused; + g_object_notify (G_OBJECT (alpha), "is-paused"); + + g_object_unref (alpha); + } +} +/** + * clutter_alpha_new: + * @timeline: a #ClutterTimeline or %NULL + * @func: a #ClutterAlphaFunc alpha function + * @data: data to be passed to func, or %NULL + * + * Creates a new #ClutterAlpha instance, using @timeline as the time + * source and @func as the function to compute the alpha(t) value. + * + * Return value: a new #ClutterAlpha + * + * Since: 0.2 + */ +ClutterAlpha * +clutter_alpha_new (ClutterTimeline *timeline, + ClutterAlphaFunc func, + gpointer data) +{ + return clutter_alpha_new_full (timeline, func, data, NULL); +} + +/** + * clutter_alpha_new_full: + * @timeline: #ClutterTimeline or %NULL + * @func: a #ClutterAlphaFunc + * @data: data to be passed to func + * @destroy: function to be called when removing the alpha function + * + * Creates a new #ClutterAlpha instance, using @timeline as the + * time source and @func as the function to compute the alpha(t) + * value. + * + * You should use this constructor in bindings or if you want to + * let the #ClutterAlpha object control the lifetime of @data. + * + * Return value: the newly created #ClutterAlpha instance + * + * Since: 0.2 + */ +ClutterAlpha * +clutter_alpha_new_full (ClutterTimeline *timeline, + ClutterAlphaFunc func, + gpointer data, + GDestroyNotify destroy) +{ + ClutterAlpha *retval; + + g_return_val_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline), NULL); + g_return_val_if_fail (func != NULL, NULL); + + retval = g_object_new (CLUTTER_TYPE_ALPHA, + "timeline", timeline, + NULL); + clutter_alpha_set_func (retval, func, data, destroy); + + return retval; } diff --git a/clutter/clutter-alpha.h b/clutter/clutter-alpha.h index 8ebc849cb..080564993 100644 --- a/clutter/clutter-alpha.h +++ b/clutter/clutter-alpha.h @@ -5,6 +5,7 @@ * * Authored By Matthew Allum * Jorn Baayen + * Emmanuele Bassi * * Copyright (C) 2006 OpenedHand * @@ -24,54 +25,41 @@ * Boston, MA 02111-1307, USA. */ -#ifndef _HAVE_CLUTTER_ALPHA_H -#define _HAVE_CLUTTER_ALPHA_H +#ifndef __CLUTTER_ALPHA_H__ +#define __CLUTTER_ALPHA_H__ /* clutter-alpha.h */ #include - -#include "clutter-timeline.h" +#include G_BEGIN_DECLS -#define CLUTTER_TYPE_ALPHA clutter_alpha_get_type() +#define CLUTTER_TYPE_ALPHA (clutter_alpha_get_type ()) +#define CLUTTER_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ALPHA, ClutterAlpha)) +#define CLUTTER_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ALPHA, ClutterAlphaClass)) +#define CLUTTER_IS_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ALPHA)) +#define CLUTTER_IS_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ALPHA)) +#define CLUTTER_ALPHA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ALPHA, ClutterAlphaClass)) -#define CLUTTER_ALPHA(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_ALPHA, ClutterAlpha)) +typedef struct _ClutterAlpha ClutterAlpha; +typedef struct _ClutterAlphaClass ClutterAlphaClass; +typedef struct _ClutterAlphaPrivate ClutterAlphaPrivate; -#define CLUTTER_ALPHA_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_ALPHA, ClutterAlphaClass)) - -#define CLUTTER_IS_ALPHA(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_ALPHA)) - -#define CLUTTER_IS_ALPHA_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_ALPHA)) - -#define CLUTTER_ALPHA_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_ALPHA, ClutterAlphaClass)) - -typedef struct _ClutterAlpha ClutterAlpha; -typedef struct _ClutterAlphaClass ClutterAlphaClass; -typedef struct ClutterAlphaPrivate ClutterAlphaPrivate; - -typedef guint32 (*ClutterAlphaFunc) (ClutterAlpha *alpha); +typedef guint32 (*ClutterAlphaFunc) (ClutterAlpha *alpha, + gpointer data); struct _ClutterAlpha { - GObject parent; + GInitiallyUnowned parent; + + /*< private >*/ ClutterAlphaPrivate *priv; }; struct _ClutterAlphaClass { - GObjectClass parent_class; + GInitiallyUnowned parent_class; void (*_clutter_alpha_1) (void); void (*_clutter_alpha_2) (void); @@ -80,34 +68,37 @@ struct _ClutterAlphaClass void (*_clutter_alpha_5) (void); }; -#define CLUTTER_ALPHA_MAX_ALPHA 0xffff +#define CLUTTER_ALPHA_MIN 0x0000 +#define CLUTTER_ALPHA_MAX 0xffff -ClutterAlpha * -clutter_alpha_new (ClutterTimeline *timeline, - ClutterAlphaFunc func); +GType clutter_alpha_get_type (void) G_GNUC_CONST; +ClutterAlpha * clutter_alpha_new (ClutterTimeline *timeline, + ClutterAlphaFunc func, + gpointer data); +ClutterAlpha * clutter_alpha_new_full (ClutterTimeline *timeline, + ClutterAlphaFunc func, + gpointer data, + GDestroyNotify destroy); +guint32 clutter_alpha_get_value (ClutterAlpha *alpha); +void clutter_alpha_set_func (ClutterAlpha *alpha, + ClutterAlphaFunc func, + gpointer data, + GDestroyNotify destroy); +gint clutter_alpha_get_delay (ClutterAlpha *alpha); +void clutter_alpha_set_delay (ClutterAlpha *alpha, + gint delay); +gboolean clutter_alpha_get_is_paused (ClutterAlpha *alpha); +void clutter_alpha_set_is_paused (ClutterAlpha *alpha, + gboolean is_paused); +ClutterTimeline *clutter_alpha_get_timeline (ClutterAlpha *alpha); +void clutter_alpha_set_timeline (ClutterAlpha *alpha, + ClutterTimeline *timeline); -gint32 -clutter_alpha_get_alpha (ClutterAlpha *alpha); -void -clutter_alpha_set_func (ClutterAlpha *alpha, - ClutterAlphaFunc func); - -void -clutter_alpha_set_timeline (ClutterAlpha *alpha, - ClutterTimeline *timeline); - -ClutterTimeline * -clutter_alpha_get_timeline (ClutterAlpha *alpha); - -guint32 -clutter_alpha_ramp_inc_func (ClutterAlpha *alpha); - -guint32 -clutter_alpha_ramp_dec_func (ClutterAlpha *alpha); - -guint32 -clutter_alpha_ramp_func (ClutterAlpha *alpha); +/* predefined alpha functions */ +guint32 clutter_alpha_ramp_inc_func (ClutterAlpha *alpha, gpointer data); +guint32 clutter_alpha_ramp_dec_func (ClutterAlpha *alpha, gpointer data); +guint32 clutter_alpha_ramp_func (ClutterAlpha *alpha, gpointer data); #define CLUTTER_ALPHA_RAMP_INC clutter_alpha_ramp_inc_func #define CLUTTER_ALPHA_RAMP_DEC clutter_alpha_ramp_dec_func diff --git a/clutter/clutter-behaviour.c b/clutter/clutter-behaviour.c index 3e100d2b7..98b4899b7 100644 --- a/clutter/clutter-behaviour.c +++ b/clutter/clutter-behaviour.c @@ -31,156 +31,154 @@ #include "config.h" +#include +#include + #include "clutter-actor.h" #include "clutter-behaviour.h" #include "clutter-marshal.h" -G_DEFINE_TYPE (ClutterBehaviour, clutter_behaviour, G_TYPE_OBJECT); - -struct ClutterBehaviourPrivate +struct _ClutterBehaviourPrivate { - GObject *object; - GParamSpec *param_spec; - guint notify_id; - GSList *actors; + ClutterAlpha *alpha; + + GSList *actors; }; enum { PROP_0, - PROP_OBJECT, - PROP_PROPERTY + + PROP_ALPHA }; -enum { - SIGNAL_PROPERTY_CHANGE, +enum +{ + NOTIFY_BEHAVIOUR, + SIGNAL_LAST }; -static guint signals[SIGNAL_LAST]; +static GObjectClass *clutter_behaviour_parent_class = NULL; +static GParamSpecPool *pspec_pool = NULL; +static GObjectNotifyContext property_notify_context = { 0, }; +static guint behaviour_signals[SIGNAL_LAST] = { 0, }; +static GQuark quark_property_bridge = 0; #define CLUTTER_BEHAVIOUR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ CLUTTER_TYPE_BEHAVIOUR, \ ClutterBehaviourPrivate)) -static void -_clutter_behaviour_dispose (GObject *object) +static void +clutter_behaviour_dispatch_property_changed (ClutterBehaviour *behaviour, + guint n_pspecs, + GParamSpec **pspecs) { - ClutterBehaviour *self = CLUTTER_BEHAVIOUR(object); + guint i; - if (self->priv) + for (i = 0; i < n_pspecs; i++) { - /* FIXME: remove all actors */ + g_signal_emit (behaviour, + behaviour_signals[NOTIFY_BEHAVIOUR], + g_quark_from_string (pspecs[i]->name), + pspecs[i]); + } +} - clutter_behaviour_set_object (self, NULL); +static void +clutter_behaviour_base_finalize (ClutterBehaviourClass *klass) +{ + GList *list, *node; + + list = g_param_spec_pool_list_owned (pspec_pool, + G_OBJECT_CLASS_TYPE (klass)); + for (node = list; node != NULL; node = node->next) + { + GParamSpec *pspec = node->data; + + g_param_spec_pool_remove (pspec_pool, pspec); + g_param_spec_unref (pspec); } - G_OBJECT_CLASS (clutter_behaviour_parent_class)->dispose (object); + g_list_free (list); } static void -_clutter_behaviour_finalize (GObject *object) +clutter_behaviour_finalize (GObject *object) { - ClutterBehaviour *self = CLUTTER_BEHAVIOUR(object); + ClutterBehaviour *self = CLUTTER_BEHAVIOUR (object); - if (self->priv) + if (self->priv->alpha) + g_object_unref (self->priv->alpha); + + if (self->priv->actors) { - g_free(self->priv); - self->priv = NULL; + g_slist_foreach (self->priv->actors, + (GFunc) g_object_unref, + NULL); + g_slist_free (self->priv->actors); } G_OBJECT_CLASS (clutter_behaviour_parent_class)->finalize (object); } static void -_clutter_behaviour_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) +clutter_behaviour_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - ClutterBehaviour *behaviour; - - behaviour = CLUTTER_BEHAVIOUR(object); - switch (prop_id) { - case PROP_OBJECT: - clutter_behaviour_set_object (behaviour, g_value_get_object (value)); - break; - case PROP_PROPERTY: - clutter_behaviour_set_property (behaviour, g_value_get_string (value)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; - } + } } static void -_clutter_behaviour_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) +clutter_behaviour_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - ClutterBehaviour *behaviour; - ClutterBehaviourPrivate *priv; - - behaviour = CLUTTER_BEHAVIOUR(object); - priv = CLUTTER_BEHAVIOUR_GET_PRIVATE(behaviour); - switch (prop_id) { - case PROP_OBJECT: - g_value_set_object (value, priv->object); - break; - case PROP_PROPERTY: - g_value_set_string (value, priv->param_spec->name); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static void +property_notify_dispatcher (GObject *object, + guint n_pspecs, + GParamSpec **pspecs) +{ + CLUTTER_BEHAVIOUR_GET_CLASS (object)->dispatch_property_changed (CLUTTER_BEHAVIOUR (object), n_pspecs, pspecs); +} static void clutter_behaviour_class_init (ClutterBehaviourClass *klass) { GObjectClass *object_class; - object_class = (GObjectClass*) klass; + object_class = G_OBJECT_CLASS (klass); - object_class->finalize = _clutter_behaviour_finalize; - object_class->dispose = _clutter_behaviour_dispose; - object_class->set_property = _clutter_behaviour_set_property; - object_class->get_property = _clutter_behaviour_get_property; + clutter_behaviour_parent_class = g_type_class_peek_parent (klass); + pspec_pool = g_param_spec_pool_new (FALSE); + property_notify_context.quark_notify_queue = + g_quark_from_static_string ("ClutterBehaviour-notify-queue"); + property_notify_context.dispatcher = + property_notify_dispatcher; + quark_property_bridge = g_quark_from_static_string ("clutter-property-bridge"); - g_object_class_install_property - (object_class, PROP_OBJECT, - g_param_spec_object ("object", - "Object", - "Object whose property to monitor", - G_TYPE_OBJECT, - G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + object_class->finalize = clutter_behaviour_finalize; + object_class->set_property = clutter_behaviour_set_property; + object_class->get_property = clutter_behaviour_get_property; - g_object_class_install_property - (object_class, PROP_PROPERTY, - g_param_spec_string ("property", - "Property", - "Property to monitor", - NULL, - G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); - - signals[SIGNAL_PROPERTY_CHANGE] = - g_signal_new ("property-change", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterBehaviourClass, property_change), - NULL, NULL, - clutter_marshal_VOID__OBJECT_POINTER, - G_TYPE_NONE, - 2, G_TYPE_OBJECT, G_TYPE_POINTER); + klass->dispatch_property_changed = clutter_behaviour_dispatch_property_changed; g_type_class_add_private (object_class, sizeof (ClutterBehaviourPrivate)); } @@ -191,157 +189,415 @@ clutter_behaviour_init (ClutterBehaviour *self) ClutterBehaviourPrivate *priv; self->priv = priv = CLUTTER_BEHAVIOUR_GET_PRIVATE (self); - + + priv->actors = NULL; } -ClutterBehaviour* -clutter_behaviour_new (GObject *object, - const char *property) +GType +clutter_behaviour_get_type (void) { - ClutterBehaviour *behave; + static GType behaviour_type = 0; - behave = g_object_new (CLUTTER_TYPE_BEHAVIOUR, - "object", object, - "property", property, - NULL); + if (G_UNLIKELY (behaviour_type == 0)) + { + const GTypeInfo behaviour_info = + { + sizeof (ClutterBehaviourClass), + NULL, /* base init */ + (GBaseFinalizeFunc) clutter_behaviour_base_finalize, + (GClassInitFunc) clutter_behaviour_class_init, + NULL, + NULL, + sizeof (ClutterBehaviour), + 0, + (GInstanceInitFunc) clutter_behaviour_init, + NULL, + }; - return behave; + behaviour_type = + g_type_register_static (G_TYPE_OBJECT, "ClutterBehaviour", + &behaviour_info, + G_TYPE_FLAG_ABSTRACT); + } + + return behaviour_type; } +/** + * clutter_behaviour_apply: + * @behaviour: a #ClutterBehaviour + * @actor: a #ClutterActor + * + * Applies @behaviour to @actor. + * + * This function holds a reference on @actor. + * + * Since: 0.2 + */ void -clutter_behaviour_apply (ClutterBehaviour *behave, ClutterActor *actor) +clutter_behaviour_apply (ClutterBehaviour *behaviour, + ClutterActor *actor) { - g_return_if_fail (actor != NULL); + g_return_if_fail (CLUTTER_IS_BEHAVIOUR (behaviour)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); - if (g_slist_find (behave->priv->actors, (gconstpointer)actor)) - return; + if (g_slist_find (behaviour->priv->actors, actor)) + { + g_warning ("The behaviour of type `%s' has already been " + "applied to the actor of type `%s'", + G_OBJECT_TYPE_NAME (behaviour), + G_OBJECT_TYPE_NAME (actor)); + return; + } + + g_object_ref (behaviour); + g_object_freeze_notify (G_OBJECT (behaviour)); g_object_ref (actor); - behave->priv->actors = g_slist_append (behave->priv->actors, actor); + behaviour->priv->actors = g_slist_append (behaviour->priv->actors, actor); + + g_object_thaw_notify (G_OBJECT (behaviour)); + g_object_unref (behaviour); } +/** + * clutter_behaviour_remove: + * @behaviour: a #ClutterBehaviour + * @actor: a #ClutterActor + * + * Removes @behaviour from the list of behaviours + * applied to @actor. + * + * This function removes a reference on @actor. + * + * Since: 0.2 + */ void -clutter_behaviour_remove (ClutterBehaviour *behave, ClutterActor *actor) +clutter_behaviour_remove (ClutterBehaviour *behaviour, + ClutterActor *actor) { - g_return_if_fail (actor != NULL); + g_return_if_fail (CLUTTER_IS_BEHAVIOUR (behaviour)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); - if (g_slist_find (behave->priv->actors, (gconstpointer)actor)) + g_object_ref (behaviour); + g_object_freeze_notify (G_OBJECT (behaviour)); + + if (g_slist_find (behaviour->priv->actors, (gconstpointer) actor)) { + behaviour->priv->actors = g_slist_remove (behaviour->priv->actors, actor); + g_object_unref (actor); - behave->priv->actors = g_slist_remove (behave->priv->actors, actor); } + else + { + g_warning ("The behaviour of type `%s' was not applied " + "to the actor of type `%s'", + G_OBJECT_TYPE_NAME (behaviour), + G_OBJECT_TYPE_NAME (actor)); + } + + g_object_thaw_notify (G_OBJECT (behaviour)); + g_object_unref (behaviour); } +/** + * clutter_behaviour_actors_foreach: + * @behaviour: a #ClutterBehaviour + * @func: the function you want to apply to all #ClutterActor objects + * @data: data to be passed to @func + * + * Applies @func to all the actors bound to @behaviour. + * + * Since: 0.2 + */ void -clutter_behaviour_remove_all (ClutterBehaviour *behave) -{ - /* tofix */ -} - -void -clutter_behaviour_actors_foreach (ClutterBehaviour *behave, +clutter_behaviour_actors_foreach (ClutterBehaviour *behaviour, GFunc func, - gpointer userdata) + gpointer data) { - g_slist_foreach (behave->priv->actors, func, userdata); + g_return_if_fail (CLUTTER_IS_BEHAVIOUR (behaviour)); + g_return_if_fail (func != NULL); + + g_slist_foreach (behaviour->priv->actors, func, data); } -GObject* -clutter_behaviour_get_object (ClutterBehaviour *behave) +/** + * clutter_behaviour_get_actors: + * @behaviour: a #ClutterBehaviour + * + * Gets the list of all #ClutterActor objects bound to @behaviour. + * + * Return value: a GSList with all the actors. The elements of + * the list are owned by the #ClutterBehaviour and should not + * be freed or modified. You should free the list when done. + * + * Since: 0.2 + */ +GSList * +clutter_behaviour_get_actors (ClutterBehaviour *behaviour) { - return behave->priv->object; + GSList *retval, *l; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (behaviour), NULL); + + retval = NULL; + + for (l = behaviour->priv->actors; l != NULL; l = l->next) + retval = g_slist_prepend (retval, l->data); + + return g_slist_reverse (retval); } -void -clutter_behaviour_set_object (ClutterBehaviour *behave, - GObject *object) +typedef struct { - ClutterBehaviourPrivate *priv; - const char *property; + GType actor_type; + gchar *actor_property; + ClutterAlphaTransform transform_func; +} BridgeData; - priv = CLUTTER_BEHAVIOUR_GET_PRIVATE(behave); - - if (priv->object) - { - property = clutter_behaviour_get_property (behave); - clutter_behaviour_set_property (behave, NULL); - - g_object_unref(priv->object); - priv->object = NULL; - } - else - property = NULL; - - if (object) - { - priv->object = g_object_ref(object); - - if (property) - clutter_behaviour_set_property (behave, property); - } -} - -const char * -clutter_behaviour_get_property (ClutterBehaviour *behave) +static void +bridge_data_free (BridgeData *data) { - if (behave->priv->param_spec) - return behave->priv->param_spec->name; - else - return NULL; -} - -GParamSpec * -clutter_behaviour_get_param_spec (ClutterBehaviour *behave) -{ - return behave->priv->param_spec; + g_free (data->actor_property); + g_slice_free (BridgeData, data); } static void -notify_cb (GObject *object, - GParamSpec *param_spec, - ClutterBehaviour *behave) +clutter_behaviour_apply_value (ClutterBehaviour *behaviour, + GType actor_type, + const gchar *actor_property, + const GValue *value) { - g_signal_emit (behave, - signals[SIGNAL_PROPERTY_CHANGE], - 0, - object, - param_spec); + GSList *l; + + for (l = behaviour->priv->actors; l != NULL; l = l->next) + { + ClutterActor *actor = l->data; + + if (G_OBJECT_TYPE (actor) == actor_type) + { + g_object_set_property (G_OBJECT (actor), + actor_property, + value); + } + } +} + +static void +alpha_notify_func (ClutterAlpha *alpha, + GParamSpec *alpha_pspec, + ClutterBehaviour *behaviour) +{ + ClutterBehaviourClass *klass; + GParamSpec **pspecs; + guint n_pspecs, i; + GValue alpha_value = { 0, }; + + klass = CLUTTER_BEHAVIOUR_GET_CLASS (behaviour); + + g_value_init (&alpha_value, G_TYPE_UINT); + g_value_set_uint (&alpha_value, clutter_alpha_get_value (alpha)); + + pspecs = clutter_behaviour_class_list_properties (klass, &n_pspecs); + for (i = 0; i < n_pspecs; i++) + { + BridgeData *data; + GParamSpec *redirect; + GValue transform_value = { 0, }; + gboolean res; + + redirect = g_param_spec_get_redirect_target (pspecs[i]); + if (!redirect) + redirect = pspecs[i]; + + data = g_param_spec_get_qdata (redirect, quark_property_bridge); + g_assert (data != NULL); + + res = data->transform_func (behaviour, redirect, + &alpha_value, + &transform_value); + if (!res) + continue; + + clutter_behaviour_apply_value (behaviour, + data->actor_type, + data->actor_property, + &transform_value); + } + + g_free (pspecs); } void -clutter_behaviour_set_property (ClutterBehaviour *behave, - const char *property) +clutter_behaviour_set_alpha (ClutterBehaviour *behaviour, + ClutterAlpha *alpha) { - g_return_if_fail (behave->priv->object); + ClutterBehaviourPrivate *priv; - if (behave->priv->notify_id) + g_return_if_fail (CLUTTER_IS_BEHAVIOUR (behaviour)); + g_return_if_fail (alpha == NULL || CLUTTER_IS_ALPHA (alpha)); + + priv = behaviour->priv; + + if (priv->alpha == alpha) + return; + + g_object_ref (behaviour); + + if (priv->alpha) { - g_signal_handler_disconnect (behave->priv->object, - behave->priv->notify_id); - behave->priv->notify_id = 0; + g_signal_handlers_disconnect_by_func (priv->alpha, + alpha_notify_func, + behaviour); + g_object_unref (priv->alpha); + priv->alpha = NULL; } - behave->priv->param_spec = NULL; - - if (property) + if (alpha) { - guint signal_id; - GClosure *closure; + priv->alpha = alpha; + g_object_ref_sink (priv->alpha); - behave->priv->param_spec = - g_object_class_find_property (G_OBJECT_GET_CLASS (behave->priv->object), - property); - g_return_if_fail (behave->priv->param_spec); - - signal_id = g_signal_lookup ("notify", - G_OBJECT_TYPE (behave->priv->object)); - closure = g_cclosure_new ((GCallback) notify_cb, behave, NULL); - - behave->priv->notify_id = - g_signal_connect_closure_by_id (behave->priv->object, - signal_id, - g_quark_from_string (property), - closure, - FALSE); + g_signal_connect (priv->alpha, "notify::alpha", + G_CALLBACK (alpha_notify_func), + behaviour); } + + g_object_notify (G_OBJECT (behaviour), "alpha"); + g_object_unref (behaviour); +} + +ClutterAlpha * +clutter_behaviour_get_alpha (ClutterBehaviour *behaviour) +{ + g_return_val_if_fail (CLUTTER_IS_BEHAVIOUR (behaviour), NULL); + + if (behaviour->priv->alpha) + return g_object_ref (behaviour->priv->alpha); + + return NULL; +} + +/** + * clutter_behaviour_class_bind_property: + * @klass: a #ClutterBehaviourClass + * @actor_type: the type of the actor's class + * @actor_property: the name of the actor's class property + * @pspec: a #GParamSpec + * + * FIXME + * + * Since: 0.2 + */ +void +clutter_behaviour_class_bind_property (ClutterBehaviourClass *klass, + GType actor_type, + const gchar *actor_property, + ClutterAlphaTransform func) +{ + GParamSpec *overridden, *pspec; + GObjectClass *actor_class; + BridgeData *bridge_data; + + g_return_if_fail (CLUTTER_IS_BEHAVIOUR_CLASS (klass)); + g_return_if_fail (actor_type != G_TYPE_INVALID); + g_return_if_fail (actor_property != NULL); + g_return_if_fail (func != NULL); + + actor_class = g_type_class_ref (actor_type); + if (!actor_class) + { + g_warning (G_STRLOC ": unable to find class `%s'", + g_type_name (actor_type)); + return; + } + + pspec = g_object_class_find_property (actor_class, actor_property); + if (!pspec) + { + g_warning (G_STRLOC ": objects of class `%s' do not have a " + "property called `%s'", + g_type_name (actor_type), + actor_property); + } + g_type_class_unref (actor_class); + + overridden = g_param_spec_override (pspec->name, pspec); + + bridge_data = g_slice_new (BridgeData); + bridge_data->actor_type = actor_type; + bridge_data->actor_property = g_strdup (actor_property); + bridge_data->transform_func = func; + + g_param_spec_ref (overridden); + g_param_spec_set_qdata_full (overridden, quark_property_bridge, + bridge_data, + (GDestroyNotify) bridge_data_free); + g_param_spec_pool_insert (pspec_pool, overridden, G_OBJECT_CLASS_TYPE (klass)); +} + +/** + * clutter_behaviour_class_list_properties: + * @klass: a #ClutterBehaviourClass + * @n_pspecs: return location for the number of properties found + * + * FIXME + * + * Return value: an allocated list of #GParamSpec objects. Use + * g_free() on the returned value when done. + * + * Since: 0.2 + */ +GParamSpec ** +clutter_behaviour_class_list_properties (ClutterBehaviourClass *klass, + guint *n_pspecs) +{ + GParamSpec **pspecs; + guint n; + + pspecs = g_param_spec_pool_list (pspec_pool, + G_OBJECT_CLASS_TYPE (klass), + &n); + + if (n_pspecs) + *n_pspecs = n; + + return pspecs; +} + +/** + * clutter_behaviour_class_find_property: + * @klass: a #ClutterBehaviourClass + * @property_name: the name of the property + * + * FIXME + * + * Return value: a #GParamSpec or %NULL + * + * Since: 0.2 + */ +GParamSpec * +clutter_behaviour_class_find_property (ClutterBehaviourClass *klass, + const gchar *property_name) +{ + GParamSpec *pspec; + GParamSpec *redirect; + + g_return_val_if_fail (CLUTTER_IS_BEHAVIOUR_CLASS (klass), NULL); + g_return_val_if_fail (property_name != NULL, NULL); + + pspec = g_param_spec_pool_lookup (pspec_pool, + property_name, + G_OBJECT_CLASS_TYPE (klass), + TRUE); + if (pspec) + { + redirect = g_param_spec_get_redirect_target (pspec); + if (redirect) + return redirect; + else + return pspec; + } + else + return NULL; } diff --git a/clutter/clutter-behaviour.h b/clutter/clutter-behaviour.h index f426d53cc..7ec5d6bdd 100644 --- a/clutter/clutter-behaviour.h +++ b/clutter/clutter-behaviour.h @@ -1,39 +1,59 @@ -#ifndef _HAVE_CLUTTER_BEHAVIOUR_H -#define _HAVE_CLUTTER_BEHAVIOUR_H +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * Emmanuele Bassi + * + * Copyright (C) 2006 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CLUTTER_BEHAVIOUR_H__ +#define __CLUTTER_BEHAVIOUR_H__ #include +#include +#include G_BEGIN_DECLS -#define CLUTTER_TYPE_BEHAVIOUR clutter_behaviour_get_type() +#define CLUTTER_TYPE_BEHAVIOUR (clutter_behaviour_get_type ()) +#define CLUTTER_BEHAVIOUR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BEHAVIOUR, ClutterBehaviour)) +#define CLUTTER_BEHAVIOUR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BEHAVIOUR, ClutterBehaviourClass)) +#define CLUTTER_IS_BEHAVIOUR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BEHAVIOUR)) +#define CLUTTER_IS_BEHAVIOUR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BEHAVIOUR)) +#define CLUTTER_BEHAVIOUR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BEHAVIOUR, ClutterBehaviourClass)) -#define CLUTTER_BEHAVIOUR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_BEHAVIOUR, ClutterBehaviour)) +typedef struct _ClutterBehaviour ClutterBehaviour; +typedef struct _ClutterBehaviourPrivate ClutterBehaviourPrivate; +typedef struct _ClutterBehaviourClass ClutterBehaviourClass; -#define CLUTTER_BEHAVIOUR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_BEHAVIOUR, ClutterBehaviourClass)) +typedef gboolean (*ClutterAlphaTransform) (ClutterBehaviour *behaviour, + GParamSpec *pspec, + const GValue *src, + GValue *dest); -#define CLUTTER_IS_BEHAVIOUR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_BEHAVIOUR)) - -#define CLUTTER_IS_BEHAVIOUR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_BEHAVIOUR)) - -#define CLUTTER_BEHAVIOUR_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_BEHAVIOUR, ClutterBehaviourClass)) - -typedef struct _ClutterBehaviour ClutterBehaviour; -typedef struct ClutterBehaviourPrivate ClutterBehaviourPrivate; -typedef struct _ClutterBehaviourClass ClutterBehaviourClass; - struct _ClutterBehaviour { - GObject parent; + GObject parent_instance; + + /*< private >*/ ClutterBehaviourPrivate *priv; }; @@ -41,48 +61,64 @@ struct _ClutterBehaviourClass { GObjectClass parent_class; - void (* property_change) (ClutterBehaviour *behave, - GObject *object, - GParamSpec *param_spec); + /* override this to change the way behaviour properties + * are dispatched + */ + void (*dispatch_property_changed) (ClutterBehaviour *behaviour, + guint n_pspecs, + GParamSpec **pspecs); + + /* signals */ + void (*notify_behaviour) (ClutterBehaviour *behaviour, + GParamSpec *pspec); + + /* padding, for future expansion */ + void (*_clutter_behaviour_1) (void); + void (*_clutter_behaviour_2) (void); + void (*_clutter_behaviour_3) (void); + void (*_clutter_behaviour_4) (void); }; -GType clutter_behaviour_get_type (void); +GType clutter_behaviour_get_type (void) G_GNUC_CONST; -ClutterBehaviour* -clutter_behaviour_new (GObject *object, - const char *property); +void clutter_behaviour_apply (ClutterBehaviour *behaviour, + ClutterActor *actor); +void clutter_behaviour_remove (ClutterBehaviour *behaviour, + ClutterActor *actor); +void clutter_behaviour_actors_foreach (ClutterBehaviour *behaviour, + GFunc func, + gpointer userdata); +GSList * clutter_behaviour_get_actors (ClutterBehaviour *behaviour); +void clutter_behaviour_set_alpha (ClutterBehaviour *behaviour, + ClutterAlpha *alpha); +ClutterAlpha *clutter_behaviour_get_alpha (ClutterBehaviour *behaviour); -void -clutter_behaviour_apply (ClutterBehaviour *behave, ClutterActor *actor); -void -clutter_behaviour_remove (ClutterBehaviour *behave, ClutterActor *actor); -void -clutter_behaviour_remove_all (ClutterBehaviour *behave); +#define CLUTTER_BEHAVIOUR_WARN_INVALID_TRANSFORM(behaviour,pspec,value) \ +G_STMT_START { \ + ClutterBehaviour *_behaviour = (ClutterBehaviour *) behaviour; \ + GParamSpec *_pspec = (GParamSpec *) pspec; \ + GValue *_value = (GValue *) value; \ + g_warning ("%s: behaviours of type `%s' are unable to transform values " \ + "of type `%s' into values of type `%s' for property `%s'", \ + G_STRLOC, \ + g_type_name (G_OBJECT_TYPE (_behaviour)), \ + g_type_name (G_VALUE_TYPE (_value)), \ + g_type_name (G_PARAM_SPEC_TYPE (_pspec)), \ + _pspec->name); \ +} G_STMT_END -void -clutter_behaviour_actors_foreach (ClutterBehaviour *behave, - GFunc func, - gpointer userdata); +GParamSpec **clutter_behaviour_class_list_properties (ClutterBehaviourClass *klass, + guint *n_pspecs); +GParamSpec * clutter_behaviour_class_find_property (ClutterBehaviourClass *klass, + const gchar *property_name); +void clutter_behaviour_class_bind_property (ClutterBehaviourClass *klass, + GType actor_type, + const gchar *actor_property, + ClutterAlphaTransform func); -void -clutter_behaviour_set_object (ClutterBehaviour *behave, - GObject *object); - -GObject* -clutter_behaviour_get_object (ClutterBehaviour *behave); - -void -clutter_behaviour_set_property (ClutterBehaviour *behave, - const char *property); - -const char* -clutter_behaviour_get_property (ClutterBehaviour *behave); - -GParamSpec* -clutter_behaviour_get_param_spec (ClutterBehaviour *behave); G_END_DECLS -#endif +#endif /* __CLUTTER_BEHAVIOUR_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 8ff061f3c..026ac616b 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -8,7 +8,8 @@ #include "clutter-event.h" #include "clutter-timeline.h" #include "clutter-behaviour.h" -#include "clutter-behaviours.h" +#include "clutter-opacity-behaviour.h" +#include "clutter-blink-behaviour.h" #include "clutter-stage.h" #include "clutter-actor.h" #include "clutter-rectangle.h" diff --git a/examples/behave.c b/examples/behave.c index 666cd8629..c42d278e9 100644 --- a/examples/behave.c +++ b/examples/behave.c @@ -14,6 +14,9 @@ main (int argc, char *argv[]) clutter_init (&argc, &argv); stage = clutter_stage_get_default (); + g_signal_connect (stage, "button-press-event", + G_CALLBACK (clutter_main_quit), + NULL); pixbuf = gdk_pixbuf_new_from_file ("redhand.png", NULL); @@ -30,13 +33,14 @@ main (int argc, char *argv[]) /* Make a timeline */ timeline = clutter_timeline_new (100, 30); /* num frames, fps */ - g_object_set(timeline, "loop", TRUE, 0); + g_object_set (timeline, "loop", TRUE, 0); /* Set an alpha func to power behaviour - ramp is constant rise/fall */ - alpha = clutter_alpha_new (timeline, CLUTTER_ALPHA_RAMP); + alpha = clutter_alpha_new (timeline, CLUTTER_ALPHA_RAMP, NULL); /* Create a behaviour for that time line */ - behave = clutter_behaviour_opacity_new_from_alpha (alpha, 0X33, 0xff); + behave = clutter_opacity_behaviour_new (0X33, 0xff, 0x00); + clutter_behaviour_set_alpha (CLUTTER_BEHAVIOUR (behave), alpha); /* Apply it to our actor */ clutter_behaviour_apply (behave, hand); @@ -48,5 +52,7 @@ main (int argc, char *argv[]) clutter_main(); + g_object_unref (behave); + return 0; }