diff --git a/clutter/Makefile.am b/clutter/Makefile.am index faa9480cc..2f325161b 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -47,6 +47,7 @@ BUILT_SOURCES = $(MARSHALFILES) $(ENUMFILES) source_h = \ $(srcdir)/clutter-actor.h \ $(srcdir)/clutter-alpha.h \ + $(srcdir)/clutter-animatable.h \ $(srcdir)/clutter-animation.h \ $(srcdir)/clutter-backend.h \ $(srcdir)/clutter-behaviour.h \ @@ -137,6 +138,7 @@ CLEANFILES = $(STAMPFILES) source_c = \ clutter-actor.c \ clutter-alpha.c \ + clutter-animatable.c \ clutter-animation.c \ clutter-backend.c \ clutter-behaviour.c \ diff --git a/clutter/clutter-animatable.c b/clutter/clutter-animatable.c new file mode 100644 index 000000000..fb2b88b19 --- /dev/null +++ b/clutter/clutter-animatable.c @@ -0,0 +1,117 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2009 Intel Corporation. + * + * 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, see . + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:clutter-animatable + * @short_description: Interface for animatable classes + * + * #ClutterAnimatable is an interface that allows a #GObject class + * to control how a #ClutterAnimation will animate a property. + * + * Each #ClutterAnimatable should implement the animate_property() + * virtual function of the interface to compute the animation state + * between two values of an interval depending on a progress factor, + * expressed as a floating point value. + * + * If a #ClutterAnimatable is animated by a #ClutterAnimation + * instance, the #ClutterAnimation will call + * clutter_animatable_animate_property() passing the name of the + * currently animated property; the initial and final values of + * the animation interval; the progress factor. The #ClutterAnimatable + * implementation should return the computed value for the animated + * property. + * + * #ClutterAnimatable is available since Clutter 1.0 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-animatable.h" +#include "clutter-debug.h" +#include "clutter-private.h" + +GType +clutter_animatable_get_type (void) +{ + static GType a_type = 0; + + if (G_UNLIKELY (a_type == 0)) + a_type = g_type_register_static_simple (G_TYPE_INTERFACE, + I_("ClutterAnimatable"), + sizeof (ClutterAnimatableIface), + NULL, 0, NULL, 0); + + return a_type; +} + +/** + * clutter_animatable_animate_property: + * @animatable: a #ClutterAnimatable + * @animation: a #ClutterAnimation + * @property_name: the name of the animated property + * @initial_value: the initial value of the animation interval + * @final_value: the final value of the animation interval + * @progress: the progress factor + * @value: return location for the animation value + * + * Calls the animate_property() virtual function for @animatable. + * + * The @initial_value and @final_value #GValues must contain + * the same type; @value must have been initialized to the same + * type of @initial_value and @final_value. + * + * All implementation of the #ClutterAnimatable interface must + * implement this function. + * + * Since: 1.0 + */ +void +clutter_animatable_animate_property (ClutterAnimatable *animatable, + ClutterAnimation *animation, + const gchar *property_name, + const GValue *initial_value, + const GValue *final_value, + gdouble progress, + GValue *value) +{ + g_return_if_fail (CLUTTER_IS_ANIMATABLE (animatable)); + g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); + g_return_if_fail (property_name != NULL); + g_return_if_fail (initial_value != NULL && final_value != NULL); + g_return_if_fail (G_VALUE_TYPE (initial_value) != G_TYPE_INVALID); + g_return_if_fail (G_VALUE_TYPE (final_value) != G_TYPE_INVALID); + g_return_if_fail (value != NULL); + g_return_if_fail (G_VALUE_TYPE (value) == G_VALUE_TYPE (initial_value) && + G_VALUE_TYPE (value) == G_VALUE_TYPE (final_value)); + + CLUTTER_ANIMATABLE_GET_IFACE (animatable)->animate_property (animatable, + animation, + property_name, + initial_value, + final_value, + progress, + value); +} diff --git a/clutter/clutter-animatable.h b/clutter/clutter-animatable.h new file mode 100644 index 000000000..aee717ac4 --- /dev/null +++ b/clutter/clutter-animatable.h @@ -0,0 +1,80 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2009 Intel Corporation. + * + * 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, see . + * + * Author: + * Emmanuele Bassi + */ + +#ifndef __CLUTTER_ANIMATABLE_H__ +#define __CLUTTER_ANIMATABLE_H__ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_ANIMATABLE (clutter_animatable_get_type ()) +#define CLUTTER_ANIMATABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ANIMATABLE, ClutterAnimatable)) +#define CLUTTER_IS_ANIMATABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ANIMATABLE)) +#define CLUTTER_ANIMATABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), CLUTTER_TYPE_ANIMATABLE, ClutterAnimatableIface)) + +typedef struct _ClutterAnimatable ClutterAnimatable; /* dummy typedef */ +typedef struct _ClutterAnimatableIface ClutterAnimatableIface; + +/** + * ClutterAnimatableIface: + * @animate_property: virtual function for animating a property + * + * Base interface for #GObjects that can be animated by a + * a #ClutterAnimation. + * + * Since: 1.0 + */ +struct _ClutterAnimatableIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + /*< public >*/ + void (* animate_property) (ClutterAnimatable *animatable, + ClutterAnimation *animation, + const gchar *property_name, + const GValue *initial_value, + const GValue *final_value, + gdouble progress, + GValue *value); +}; + +GType clutter_animatable_get_type (void) G_GNUC_CONST; + +void clutter_animatable_animate_property (ClutterAnimatable *animatable, + ClutterAnimation *animation, + const gchar *property_name, + const GValue *initial_value, + const GValue *final_value, + gdouble progress, + GValue *value); + +G_END_DECLS + +#endif /* __CLUTTER_ANIMATABLE_H__ */ diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index eae81250d..fb5f3c48d 100644 --- a/clutter/clutter-animation.c +++ b/clutter/clutter-animation.c @@ -27,12 +27,12 @@ * @short_description: Simple implicit animations * * #ClutterAnimation is an object providing simple, implicit animations - * for #ClutterActors. + * for #GObjects. * * #ClutterAnimation instances will bind a #GObject property belonging - * to a #ClutterActor to a #ClutterInterval, and will then use a - * #ClutterTimeline to interpolate the property between the initial - * and final values of the interval. + * to a #GObject to a #ClutterInterval, and will then use a #ClutterTimeline + * to interpolate the property between the initial and final values of the + * interval. * * For convenience, it is possible to use the clutter_actor_animate() * function call which will take care of setting up and tearing down @@ -50,6 +50,7 @@ #include #include "clutter-alpha.h" +#include "clutter-animatable.h" #include "clutter-animation.h" #include "clutter-debug.h" #include "clutter-enum-types.h" @@ -60,7 +61,7 @@ enum { PROP_0, - PROP_ACTOR, + PROP_OBJECT, PROP_MODE, PROP_DURATION, PROP_LOOP, @@ -79,7 +80,7 @@ enum struct _ClutterAnimationPrivate { - ClutterActor *actor; + GObject *object; GHashTable *properties; @@ -96,7 +97,7 @@ struct _ClutterAnimationPrivate static guint animation_signals[LAST_SIGNAL] = { 0, }; -static GQuark quark_actor_animation = 0; +static GQuark quark_object_animation = 0; G_DEFINE_TYPE (ClutterAnimation, clutter_animation, G_TYPE_INITIALLY_UNOWNED); @@ -118,16 +119,14 @@ clutter_animation_dispose (GObject *gobject) { ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv; - if (priv->actor) + if (priv->object) { g_object_weak_unref (G_OBJECT (gobject), on_animation_weak_notify, - priv->actor); - g_object_set_qdata (G_OBJECT (priv->actor), - quark_actor_animation, - NULL); - g_object_unref (priv->actor); - priv->actor = NULL; + priv->object); + g_object_set_qdata (priv->object, quark_object_animation, NULL); + g_object_unref (priv->object); + priv->object = NULL; } if (priv->timeline) @@ -168,8 +167,8 @@ clutter_animation_set_property (GObject *gobject, switch (prop_id) { - case PROP_ACTOR: - clutter_animation_set_actor (animation, g_value_get_object (value)); + case PROP_OBJECT: + clutter_animation_set_object (animation, g_value_get_object (value)); break; case PROP_MODE: @@ -208,8 +207,8 @@ clutter_animation_get_property (GObject *gobject, switch (prop_id) { - case PROP_ACTOR: - g_value_set_object (value, priv->actor); + case PROP_OBJECT: + g_value_set_object (value, priv->object); break; case PROP_MODE: @@ -253,7 +252,7 @@ clutter_animation_class_init (ClutterAnimationClass *klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; - quark_actor_animation = + quark_object_animation = g_quark_from_static_string ("clutter-actor-animation"); g_type_class_add_private (klass, sizeof (ClutterAnimationPrivate)); @@ -266,18 +265,18 @@ clutter_animation_class_init (ClutterAnimationClass *klass) gobject_class->finalize = clutter_animation_finalize; /** - * ClutterAnimation:actor: + * ClutterAnimation:objct: * - * The actor to which the animation applies. + * The #GObject to which the animation applies. * * Since: 1.0 */ - pspec = g_param_spec_object ("actor", - "Actor", - "Actor to which the animation applies", - CLUTTER_TYPE_ACTOR, + pspec = g_param_spec_object ("object", + "Object", + "Object to which the animation applies", + G_TYPE_OBJECT, CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ACTOR, pspec); + g_object_class_install_property (gobject_class, PROP_OBJECT, pspec); /** * ClutterAnimation:mode: @@ -427,7 +426,7 @@ clutter_animation_update_property_internal (ClutterAnimation *animation, * @property_name: the property to control * @interval: a #ClutterInterval * - * Binds @interval to the @property_name of the #ClutterActor + * Binds @interval to the @property_name of the #GObject * attached to @animation. The #ClutterAnimation will take * ownership of the passed #ClutterInterval. * @@ -451,10 +450,10 @@ clutter_animation_bind_property (ClutterAnimation *animation, priv = animation->priv; - if (G_UNLIKELY (!priv->actor)) + if (G_UNLIKELY (!priv->object)) { g_warning ("Cannot bind property `%s': the animation has no " - "actor set. You need to call clutter_animation_set_actor() " + "object set. You need to call clutter_animation_set_object() " "first to be able to bind a property", property_name); return; @@ -468,14 +467,14 @@ clutter_animation_bind_property (ClutterAnimation *animation, return; } - klass = G_OBJECT_GET_CLASS (priv->actor); + klass = G_OBJECT_GET_CLASS (priv->object); pspec = g_object_class_find_property (klass, property_name); if (!pspec) { - g_warning ("Cannot bind property `%s': actors of type `%s' have " + g_warning ("Cannot bind property `%s': objects of type `%s' have " "no such property", property_name, - g_type_name (G_OBJECT_TYPE (priv->actor))); + g_type_name (G_OBJECT_TYPE (priv->object))); return; } @@ -592,14 +591,14 @@ clutter_animation_update_property (ClutterAnimation *animation, return; } - klass = G_OBJECT_GET_CLASS (priv->actor); + klass = G_OBJECT_GET_CLASS (priv->object); pspec = g_object_class_find_property (klass, property_name); if (!pspec) { - g_warning ("Cannot bind property `%s': actors of type `%s' have " + g_warning ("Cannot bind property `%s': objects of type `%s' have " "no such property", property_name, - g_type_name (G_OBJECT_TYPE (priv->actor))); + g_type_name (G_OBJECT_TYPE (priv->object))); return; } @@ -664,10 +663,18 @@ on_alpha_notify (GObject *gobject, ClutterAnimationPrivate *priv = animation->priv; GList *properties, *p; guint32 alpha_value; + gboolean is_animatable = FALSE; + ClutterAnimatable *animatable = NULL; alpha_value = clutter_alpha_get_alpha (CLUTTER_ALPHA (gobject)); - g_object_freeze_notify (G_OBJECT (priv->actor)); + if (CLUTTER_IS_ANIMATABLE (priv->object)) + { + animatable = CLUTTER_ANIMATABLE (priv->object); + is_animatable = TRUE; + } + + g_object_freeze_notify (priv->object); properties = g_hash_table_get_keys (priv->properties); for (p = properties; p != NULL; p = p->next) @@ -683,16 +690,37 @@ on_alpha_notify (GObject *gobject, g_value_init (&value, clutter_interval_get_value_type (interval)); factor = (gdouble) alpha_value / CLUTTER_ALPHA_MAX_ALPHA; - clutter_interval_compute_value (interval, factor, &value); - g_object_set_property (G_OBJECT (priv->actor), p_name, &value); + if (is_animatable) + { + const GValue *initial, *final; + + initial = clutter_interval_peek_initial_value (interval); + final = clutter_interval_peek_final_value (interval); + + CLUTTER_NOTE (ANIMATION, "Animatable property `%s'", p_name); + clutter_animatable_animate_property (animatable, animation, + p_name, + initial, final, + factor, + &value); + + g_object_set_property (priv->object, p_name, &value); + } + else + { + CLUTTER_NOTE (ANIMATION, "Standard property `%s'", p_name); + + if (clutter_interval_compute_value (interval, factor, &value)) + g_object_set_property (priv->object, p_name, &value); + } g_value_unset (&value); } g_list_free (properties); - g_object_thaw_notify (G_OBJECT (priv->actor)); + g_object_thaw_notify (priv->object); } /* @@ -709,7 +737,7 @@ on_animation_weak_notify (gpointer data, clutter_actor_get_gid (CLUTTER_ACTOR (actor)), actor); - g_object_set_qdata (actor, quark_actor_animation, NULL); + g_object_set_qdata (actor, quark_object_animation, NULL); } ClutterAnimation * @@ -719,66 +747,64 @@ clutter_animation_new (void) } /** - * clutter_animation_set_actor: + * clutter_animation_set_object: * @animation: a #ClutterAnimation - * @actor: a #ClutterActor + * @object: a #GObject * - * Attaches @animation to @actor. The #ClutterAnimation will take a - * reference on @actor. + * Attaches @animation to @object. The #ClutterAnimation will take a + * reference on @object. * * Since: 1.0 */ void -clutter_animation_set_actor (ClutterAnimation *animation, - ClutterActor *actor) +clutter_animation_set_object (ClutterAnimation *animation, + GObject *object) { ClutterAnimationPrivate *priv; g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); - g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + g_return_if_fail (G_IS_OBJECT (object)); priv = animation->priv; - g_object_ref (actor); + g_object_ref (object); - if (priv->actor) + if (priv->object) { g_object_weak_unref (G_OBJECT (animation), on_animation_weak_notify, - priv->actor); - g_object_set_qdata (G_OBJECT (priv->actor), - quark_actor_animation, - NULL); - g_object_unref (priv->actor); + priv->object); + g_object_set_qdata (priv->object, quark_object_animation, NULL); + g_object_unref (priv->object); } - priv->actor = actor; + priv->object = object; g_object_weak_ref (G_OBJECT (animation), on_animation_weak_notify, - priv->actor); - g_object_set_qdata (G_OBJECT (priv->actor), - quark_actor_animation, + priv->object); + g_object_set_qdata (G_OBJECT (priv->object), + quark_object_animation, animation); - g_object_notify (G_OBJECT (animation), "actor"); + g_object_notify (G_OBJECT (animation), "object"); } /** - * clutter_animation_get_actor: + * clutter_animation_get_object: * @animation: a #ClutterAnimation * - * Retrieves the #ClutterActor attached to @animation. + * Retrieves the #GObject attached to @animation. * - * Return value: a #ClutterActor + * Return value: a #GObject * * Since: 1.0 */ -ClutterActor * -clutter_animation_get_actor (ClutterAnimation *animation) +GObject * +clutter_animation_get_object (ClutterAnimation *animation) { g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL); - return animation->priv->actor; + return animation->priv->object; } static inline void @@ -1125,7 +1151,7 @@ clutter_animation_setup_valist (ClutterAnimation *animation, GObjectClass *klass; const gchar *property_name; - klass = G_OBJECT_GET_CLASS (priv->actor); + klass = G_OBJECT_GET_CLASS (priv->object); property_name = first_property_name; while (property_name != NULL) @@ -1145,10 +1171,10 @@ clutter_animation_setup_valist (ClutterAnimation *animation, pspec = g_object_class_find_property (klass, property_name); if (!pspec) { - g_warning ("Cannot bind property `%s': actors of type `%s' do " + g_warning ("Cannot bind property `%s': objects of type `%s' do " "not have this property", property_name, - g_type_name (G_OBJECT_TYPE (priv->actor))); + g_type_name (G_OBJECT_TYPE (priv->object))); break; } @@ -1178,9 +1204,7 @@ clutter_animation_setup_valist (ClutterAnimation *animation, GValue initial = { 0, }; g_value_init (&initial, G_PARAM_SPEC_VALUE_TYPE (pspec)); - g_object_get_property (G_OBJECT (priv->actor), - property_name, - &initial); + g_object_get_property (priv->object, property_name, &initial); interval = clutter_interval_new_with_values (G_PARAM_SPEC_VALUE_TYPE (pspec), @@ -1199,7 +1223,7 @@ clutter_animation_setup_valist (ClutterAnimation *animation, g_value_unset (&initial); } else - g_object_set_property (G_OBJECT (priv->actor), property_name, &final); + g_object_set_property (priv->object, property_name, &final); g_value_unset (&final); @@ -1254,7 +1278,7 @@ clutter_actor_animate_with_alpha (ClutterActor *actor, return NULL; } - animation = g_object_get_qdata (G_OBJECT (actor), quark_actor_animation); + animation = g_object_get_qdata (G_OBJECT (actor), quark_object_animation); if (G_LIKELY (!animation)) { animation = clutter_animation_new (); @@ -1265,7 +1289,7 @@ clutter_actor_animate_with_alpha (ClutterActor *actor, clutter_animation_set_timeline (animation, timeline); clutter_animation_set_alpha (animation, alpha); - clutter_animation_set_actor (animation, actor); + clutter_animation_set_object (animation, G_OBJECT (actor)); va_start (args, first_property_name); clutter_animation_setup_valist (animation, first_property_name, args); @@ -1311,7 +1335,7 @@ clutter_actor_animate_with_timeline (ClutterActor *actor, g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL); g_return_val_if_fail (first_property_name != NULL, NULL); - animation = g_object_get_qdata (G_OBJECT (actor), quark_actor_animation); + animation = g_object_get_qdata (G_OBJECT (actor), quark_object_animation); if (G_LIKELY (!animation)) { animation = clutter_animation_new (); @@ -1323,7 +1347,7 @@ clutter_actor_animate_with_timeline (ClutterActor *actor, clutter_animation_set_timeline (animation, timeline); clutter_animation_set_alpha (animation, NULL); clutter_animation_set_mode (animation, mode); - clutter_animation_set_actor (animation, actor); + clutter_animation_set_object (animation, G_OBJECT (actor)); va_start (args, first_property_name); clutter_animation_setup_valist (animation, first_property_name, args); @@ -1406,7 +1430,7 @@ clutter_actor_animate (ClutterActor *actor, g_return_val_if_fail (duration > 0, NULL); g_return_val_if_fail (first_property_name != NULL, NULL); - animation = g_object_get_qdata (G_OBJECT (actor), quark_actor_animation); + animation = g_object_get_qdata (G_OBJECT (actor), quark_object_animation); if (G_LIKELY (!animation)) { /* if there is no animation already attached to the actor, @@ -1416,7 +1440,7 @@ clutter_actor_animate (ClutterActor *actor, animation = clutter_animation_new (); clutter_animation_set_timeline (animation, NULL); clutter_animation_set_alpha (animation, NULL); - clutter_animation_set_actor (animation, actor); + clutter_animation_set_object (animation, G_OBJECT (actor)); CLUTTER_NOTE (ANIMATION, "Created new Animation [%p]", animation); } diff --git a/clutter/clutter-animation.h b/clutter/clutter-animation.h index 115e0a0b5..061d4e439 100644 --- a/clutter/clutter-animation.h +++ b/clutter/clutter-animation.h @@ -97,9 +97,9 @@ GType clutter_animation_get_type (void) G_GNUC_CONST; ClutterAnimation * clutter_animation_new (void); -void clutter_animation_set_actor (ClutterAnimation *animation, - ClutterActor *actor); -ClutterActor * clutter_animation_get_actor (ClutterAnimation *animation); +void clutter_animation_set_object (ClutterAnimation *animation, + GObject *object); +GObject * clutter_animation_get_object (ClutterAnimation *animation); void clutter_animation_set_mode (ClutterAnimation *animation, ClutterAnimationMode mode); ClutterAnimationMode clutter_animation_get_mode (ClutterAnimation *animation); diff --git a/clutter/clutter-interval.c b/clutter/clutter-interval.c index 5f2924d09..efdf58dff 100644 --- a/clutter/clutter-interval.c +++ b/clutter/clutter-interval.c @@ -57,9 +57,18 @@ #include #include +#include "clutter-color.h" +#include "clutter-fixed.h" #include "clutter-interval.h" #include "clutter-units.h" -#include "clutter-fixed.h" + +typedef struct +{ + GType value_type; + ClutterProgressFunc func; +} ProgressData; + +static GHashTable *progress_funcs = NULL; enum { @@ -171,19 +180,39 @@ clutter_interval_real_validate (ClutterInterval *interval, return TRUE; } -static void +static gboolean clutter_interval_real_compute_value (ClutterInterval *interval, gdouble factor, GValue *value) { GValue *initial, *final; GType value_type; + gboolean retval = FALSE; initial = clutter_interval_peek_initial_value (interval); final = clutter_interval_peek_final_value (interval); value_type = clutter_interval_get_value_type (interval); + if (G_UNLIKELY (progress_funcs != NULL)) + { + ProgressData *p_data; + + p_data = + g_hash_table_lookup (progress_funcs, GUINT_TO_POINTER (value_type)); + + /* if we have a progress function, and that function was + * successful in computing the progress, then we bail out + * as fast as we can + */ + if (p_data != NULL) + { + retval = p_data->func (initial, final, factor, value); + if (retval) + return retval; + } + } + switch (G_TYPE_FUNDAMENTAL (value_type)) { case G_TYPE_INT: @@ -196,6 +225,8 @@ clutter_interval_real_compute_value (ClutterInterval *interval, res = (factor * (ib - ia)) + ia; g_value_set_int (value, res); + + retval = TRUE; } break; @@ -209,6 +240,8 @@ clutter_interval_real_compute_value (ClutterInterval *interval, res = (factor * (ib - (gdouble) ia)) + ia; g_value_set_uint (value, res); + + retval = TRUE; } break; @@ -222,6 +255,8 @@ clutter_interval_real_compute_value (ClutterInterval *interval, res = (factor * (ib - (gdouble) ia)) + ia; g_value_set_uchar (value, res); + + retval = TRUE; } break; @@ -239,6 +274,8 @@ clutter_interval_real_compute_value (ClutterInterval *interval, g_value_set_double (value, res); else g_value_set_float (value, res); + + retval = TRUE; } break; @@ -247,11 +284,35 @@ clutter_interval_real_compute_value (ClutterInterval *interval, g_value_set_boolean (value, TRUE); else g_value_set_boolean (value, FALSE); + + retval = TRUE; + break; + + case G_TYPE_BOXED: + if (value_type == CLUTTER_TYPE_COLOR) + { + const ClutterColor *ia, *ib; + ClutterColor res = { 0, }; + + ia = clutter_value_get_color (initial); + ib = clutter_value_get_color (final); + + res.red = (factor * (ib->red - (gdouble) ia->red)) + ia->red; + res.green = (factor * (ib->green - (gdouble) ia->green)) + ia->green; + res.blue = (factor * (ib->blue - (gdouble) ia->blue)) + ia->blue; + res.alpha = (factor * (ib->alpha - (gdouble) ia->alpha)) + ia->alpha; + + clutter_value_set_color (value, &res); + + retval = TRUE; + } break; default: break; } + + return retval; } static void @@ -818,19 +879,99 @@ clutter_interval_validate (ClutterInterval *interval, * Computes the value between the @interval boundaries given the * progress @factor and puts it into @value. * + * Return value: %TRUE if the operation was successful + * * Since: 1.0 */ -void +gboolean clutter_interval_compute_value (ClutterInterval *interval, gdouble factor, GValue *value) { - g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); - g_return_if_fail (value != NULL); + g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), FALSE); + g_return_val_if_fail (value != NULL, FALSE); factor = CLAMP (factor, 0.0, 1.0); - CLUTTER_INTERVAL_GET_CLASS (interval)->compute_value (interval, - factor, - value); + return CLUTTER_INTERVAL_GET_CLASS (interval)->compute_value (interval, + factor, + value); +} + +/** + * clutter_interval_register_progress_func: + * @value_type: a #GType + * @func: a #ClutterProgressFunc, or %NULL to unset a previously + * set progress function + * + * Sets the progress function for a given @value_type, like: + * + * |[ + * clutter_interval_register_progress_func (MY_TYPE_FOO, + * my_foo_progress); + * ]| + * + * Whenever a #ClutterInterval instance using the default + * #ClutterInterval::compute_value implementation is set as an + * interval between two #GValue of type @value_type, it will call + * @func to establish the value depending on the given progress, + * for instance: + * + * |[ + * static gboolean + * my_int_progress (const GValue *a, + * const GValue *b, + * gdouble progress, + * GValue *retval) + * { + * gint ia = g_value_get_int (a); + * gint ib = g_value_get_int (b); + * gint res = factor * (ib - ia) + ia; + * + * g_value_set_int (retval, res); + * + * return TRUE; + * } + * + * clutter_interval_register_progress_func (G_TYPE_INT, my_int_progress); + * ]| + * + * To unset a previously set progress function of a #GType, pass %NULL + * for @func. + * + * Since: 1.0 + */ +void +clutter_interval_register_progress_func (GType value_type, + ClutterProgressFunc func) +{ + ProgressData *progress_func; + + g_return_if_fail (value_type != G_TYPE_INVALID); + + if (G_UNLIKELY (progress_funcs == NULL)) + progress_funcs = g_hash_table_new (NULL, NULL); + + progress_func = + g_hash_table_lookup (progress_funcs, GUINT_TO_POINTER (value_type)); + if (G_UNLIKELY (progress_func)) + { + if (func == NULL) + { + g_hash_table_remove (progress_funcs, GUINT_TO_POINTER (value_type)); + g_slice_free (ProgressData, progress_func); + } + else + progress_func->func = func; + } + else + { + progress_func = g_slice_new (ProgressData); + progress_func->value_type = value_type; + progress_func->func = func; + + g_hash_table_replace (progress_funcs, + GUINT_TO_POINTER (value_type), + progress_func); + } } diff --git a/clutter/clutter-interval.h b/clutter/clutter-interval.h index 56d155f28..72ae5304c 100644 --- a/clutter/clutter-interval.h +++ b/clutter/clutter-interval.h @@ -44,6 +44,34 @@ typedef struct _ClutterInterval ClutterInterval; typedef struct _ClutterIntervalPrivate ClutterIntervalPrivate; typedef struct _ClutterIntervalClass ClutterIntervalClass; +/** + * ClutterProgressFunc: + * @a: the initial value of an interval + * @b: the final value of an interval + * @progress: the progress factor, between 0 and 1 + * @retval: the value used to store the progress + * + * Prototype of the progress function used to compute the value + * between the two ends @a and @b of an interval depending on + * the value of @progress. + * + * The #GValue in @retval is already initialized with the same + * type as @a and @b. + * + * This function will be called by #ClutterInterval if the + * type of the values of the interval was registered using + * clutter_interval_register_progress_func(). + * + * Return value: %TRUE if the function successfully computed + * the value and stored it inside @retval + * + * Since: 1.0 + */ +typedef gboolean (* ClutterProgressFunc) (const GValue *a, + const GValue *b, + gdouble progress, + GValue *retval); + /** * ClutterInterval: * @@ -79,7 +107,7 @@ struct _ClutterIntervalClass /*< public >*/ gboolean (* validate) (ClutterInterval *interval, GParamSpec *pspec); - void (* compute_value) (ClutterInterval *interval, + gboolean (* compute_value) (ClutterInterval *interval, gdouble factor, GValue *value); @@ -122,10 +150,13 @@ void clutter_interval_get_interval (ClutterInterval *interval, gboolean clutter_interval_validate (ClutterInterval *interval, GParamSpec *pspec); -void clutter_interval_compute_value (ClutterInterval *interval, +gboolean clutter_interval_compute_value (ClutterInterval *interval, gdouble factor, GValue *value); +void clutter_interval_register_progress_func (GType value_type, + ClutterProgressFunc func); + G_END_DECLS #endif /* __CLUTTER_INTERVAL_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 817dbef6c..8306ee50a 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -30,6 +30,7 @@ #include "clutter-actor.h" #include "clutter-alpha.h" +#include "clutter-animatable.h" #include "clutter-animation.h" #include "clutter-backend.h" #include "clutter-behaviour-depth.h" diff --git a/doc/reference/clutter/clutter-docs.xml b/doc/reference/clutter/clutter-docs.xml index baeb0b5bf..a9ecc8a0f 100644 --- a/doc/reference/clutter/clutter-docs.xml +++ b/doc/reference/clutter/clutter-docs.xml @@ -105,6 +105,7 @@ + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 3f0a8429f..6f3864908 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -169,6 +169,35 @@ ClutterCloneTexturePrivate clutter_clone_texture_get_type +
+clutter-cairo-texture +ClutterCairoTexture +ClutterCairoTexture +ClutterCairoTextureClass +clutter_cairo_texture_new +clutter_cairo_texture_set_surface_size +clutter_cairo_texture_get_surface_size + + +clutter_cairo_texture_create +clutter_cairo_texture_create_region + + +clutter_cairo_set_source_color + + +CLUTTER_TYPE_CAIRO_TEXTURE +CLUTTER_CAIRO_TEXTURE +CLUTTER_IS_CAIRO_TEXTURE +CLUTTER_CAIRO_TEXTURE_CLASS +CLUTTER_IS_CAIRO_TEXTURE_CLASS +CLUTTER_CAIRO_TEXTURE_GET_CLASS + + +ClutterCairoTexturePrivate +clutter_cairo_texture_get_type +
+
clutter-group ClutterGroup @@ -1507,8 +1536,8 @@ ClutterAnimation ClutterAnimationClass ClutterAnimationMode clutter_animation_new -clutter_animation_set_actor -clutter_animation_get_actor +clutter_animation_set_object +clutter_animation_get_object clutter_animation_set_mode clutter_animation_get_mode clutter_animation_set_duration @@ -1567,6 +1596,10 @@ clutter_interval_get_interval clutter_interval_compute_value clutter_interval_validate + +ClutterProgressFunc +clutter_interval_register_progress_func + CLUTTER_TYPE_INTERVAL CLUTTER_INTERVAL @@ -1688,6 +1721,23 @@ ClutterTextPrivate clutter_text_get_type
+
+clutter-animatable +ClutterAnimatable +ClutterAnimatable +ClutterAnimatableIface +clutter_animatable_animate_property + + +CLUTTER_TYPE_ANIMATABLE +CLUTTER_ANIMATABLE +CLUTTER_IS_ANIMATABLE +CLUTTER_ANIMATABLE_GET_IFACE + + +clutter_animatable_get_type +
+
Key Bindings clutter-binding-pool diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index f59698f02..c4fc972dc 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -28,3 +28,4 @@ clutter_shader_get_type clutter_child_meta_get_type clutter_cairo_texture_get_type clutter_text_get_type +clutter_animatable_get_type diff --git a/tests/interactive/test-animation.c b/tests/interactive/test-animation.c index 000e02bd8..ee0ff7337 100644 --- a/tests/interactive/test-animation.c +++ b/tests/interactive/test-animation.c @@ -23,13 +23,12 @@ on_button_press (ClutterActor *actor, ClutterAnimation *animation; gint old_x, old_y, new_x, new_y; guint old_width, old_height, new_width, new_height; - guint8 old_op, new_op; gdouble new_angle; ClutterVertex vertex = { 0, }; + ClutterColor new_color = { 0, }; clutter_actor_get_position (actor, &old_x, &old_y); clutter_actor_get_size (actor, &old_width, &old_height); - old_op = clutter_actor_get_opacity (actor); /* determine the final state of the animation depending on * the state of the actor @@ -40,8 +39,12 @@ on_button_press (ClutterActor *actor, new_y = old_y - 100; new_width = old_width + 200; new_height = old_height + 200; - new_op = 255; new_angle = 360.0; + + new_color.red = 0xdd; + new_color.green = 0x44; + new_color.blue = 0xdd; + new_color.alpha = 0xff; } else { @@ -49,8 +52,12 @@ on_button_press (ClutterActor *actor, new_y = old_y + 100; new_width = old_width - 200; new_height = old_height - 200; - new_op = 128; new_angle = 0.0; + + new_color.red = 0x44; + new_color.green = 0xdd; + new_color.blue = 0x44; + new_color.alpha = 0x88; } vertex.x = CLUTTER_UNITS_FROM_FLOAT ((float) new_width / 2); @@ -62,7 +69,7 @@ on_button_press (ClutterActor *actor, "y", new_y, "width", new_width, "height", new_height, - "opacity", new_op, + "color", &new_color, "rotation-angle-z", new_angle, "fixed::rotation-center-z", &vertex, "fixed::reactive", FALSE,