mirror of
https://github.com/brl/mutter.git
synced 2024-12-24 12:02:04 +00:00
7d7372af43
The current Alpha value is an unsigned integer that can be used implicitly as a fixed point value. This makes writing an alpha function overshooting below and above the current range basically impossible without complicating an already complex code, and creating weird corner cases. For this reason, the Alpha value should be defined as a floating point normalized value, spanning a range between 0.0 and 1.0; in order to allow overshooting, the valid range is extended one unit below and one unit above, thus making it -1.0 .. 2.0. This commit updates the various users of the ClutterAlpha API and the tests cases. This commit also removes all the current alpha functions exposed in the public API.
1467 lines
41 KiB
C
1467 lines
41 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Copyright (C) 2008 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author:
|
|
* Emmanuele Bassi <ebassi@linux.intel.com>
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-animation
|
|
* @short_description: Simple implicit animations
|
|
*
|
|
* #ClutterAnimation is an object providing simple, implicit animations
|
|
* for #GObject<!-- -->s.
|
|
*
|
|
* #ClutterAnimation instances will bind a #GObject property belonging
|
|
* 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
|
|
* a #ClutterAnimation instance and animate an actor between its current
|
|
* state and the specified final state.
|
|
*
|
|
* #ClutterAnimation is available since Clutter 1.0
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <glib-object.h>
|
|
#include <gobject/gvaluecollector.h>
|
|
|
|
#include "clutter-alpha.h"
|
|
#include "clutter-animatable.h"
|
|
#include "clutter-animation.h"
|
|
#include "clutter-debug.h"
|
|
#include "clutter-enum-types.h"
|
|
#include "clutter-interval.h"
|
|
#include "clutter-private.h"
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_OBJECT,
|
|
PROP_MODE,
|
|
PROP_DURATION,
|
|
PROP_LOOP,
|
|
PROP_TIMELINE,
|
|
PROP_ALPHA
|
|
};
|
|
|
|
enum
|
|
{
|
|
COMPLETED,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
#define CLUTTER_ANIMATION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ANIMATION, ClutterAnimationPrivate))
|
|
|
|
struct _ClutterAnimationPrivate
|
|
{
|
|
GObject *object;
|
|
|
|
GHashTable *properties;
|
|
|
|
gulong mode;
|
|
|
|
guint loop : 1;
|
|
guint duration;
|
|
ClutterTimeline *timeline;
|
|
guint timeline_completed_id;
|
|
|
|
ClutterAlpha *alpha;
|
|
guint alpha_notify_id;
|
|
};
|
|
|
|
static guint animation_signals[LAST_SIGNAL] = { 0, };
|
|
|
|
static GQuark quark_object_animation = 0;
|
|
|
|
G_DEFINE_TYPE (ClutterAnimation, clutter_animation, G_TYPE_INITIALLY_UNOWNED);
|
|
|
|
static void on_animation_weak_notify (gpointer data,
|
|
GObject *animation_pointer);
|
|
|
|
static void
|
|
clutter_animation_finalize (GObject *gobject)
|
|
{
|
|
ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv;
|
|
|
|
g_hash_table_destroy (priv->properties);
|
|
|
|
G_OBJECT_CLASS (clutter_animation_parent_class)->finalize (gobject);
|
|
}
|
|
|
|
static void
|
|
clutter_animation_dispose (GObject *gobject)
|
|
{
|
|
ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv;
|
|
|
|
if (priv->object)
|
|
{
|
|
g_object_weak_unref (G_OBJECT (gobject),
|
|
on_animation_weak_notify,
|
|
priv->object);
|
|
g_object_set_qdata (priv->object, quark_object_animation, NULL);
|
|
g_object_unref (priv->object);
|
|
priv->object = NULL;
|
|
}
|
|
|
|
if (priv->timeline)
|
|
{
|
|
if (priv->timeline_completed_id)
|
|
{
|
|
g_signal_handler_disconnect (priv->timeline,
|
|
priv->timeline_completed_id);
|
|
priv->timeline_completed_id = 0;
|
|
}
|
|
|
|
g_object_unref (priv->timeline);
|
|
priv->timeline = NULL;
|
|
}
|
|
|
|
if (priv->alpha)
|
|
{
|
|
if (priv->alpha_notify_id)
|
|
{
|
|
g_signal_handler_disconnect (priv->alpha, priv->alpha_notify_id);
|
|
priv->alpha_notify_id = 0;
|
|
}
|
|
|
|
g_object_unref (priv->alpha);
|
|
priv->alpha = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (clutter_animation_parent_class)->dispose (gobject);
|
|
}
|
|
|
|
static void
|
|
clutter_animation_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterAnimation *animation = CLUTTER_ANIMATION (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_OBJECT:
|
|
clutter_animation_set_object (animation, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_MODE:
|
|
clutter_animation_set_mode (animation, g_value_get_ulong (value));
|
|
break;
|
|
|
|
case PROP_DURATION:
|
|
clutter_animation_set_duration (animation, g_value_get_uint (value));
|
|
break;
|
|
|
|
case PROP_LOOP:
|
|
clutter_animation_set_loop (animation, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_TIMELINE:
|
|
clutter_animation_set_timeline (animation, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_ALPHA:
|
|
clutter_animation_set_alpha (animation, g_value_get_object (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_animation_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_OBJECT:
|
|
g_value_set_object (value, priv->object);
|
|
break;
|
|
|
|
case PROP_MODE:
|
|
g_value_set_ulong (value, priv->mode);
|
|
break;
|
|
|
|
case PROP_DURATION:
|
|
g_value_set_uint (value, priv->duration);
|
|
break;
|
|
|
|
case PROP_LOOP:
|
|
g_value_set_boolean (value, priv->loop);
|
|
break;
|
|
|
|
case PROP_TIMELINE:
|
|
g_value_set_object (value, priv->timeline);
|
|
break;
|
|
|
|
case PROP_ALPHA:
|
|
g_value_set_object (value, priv->alpha);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_animation_real_completed (ClutterAnimation *animation)
|
|
{
|
|
CLUTTER_NOTE (ANIMATION, "Animation [%p] complete: unreffing",
|
|
animation);
|
|
|
|
g_object_unref (animation);
|
|
}
|
|
|
|
static void
|
|
clutter_animation_class_init (ClutterAnimationClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
quark_object_animation =
|
|
g_quark_from_static_string ("clutter-actor-animation");
|
|
|
|
g_type_class_add_private (klass, sizeof (ClutterAnimationPrivate));
|
|
|
|
klass->completed = clutter_animation_real_completed;
|
|
|
|
gobject_class->set_property = clutter_animation_set_property;
|
|
gobject_class->get_property = clutter_animation_get_property;
|
|
gobject_class->dispose = clutter_animation_dispose;
|
|
gobject_class->finalize = clutter_animation_finalize;
|
|
|
|
/**
|
|
* ClutterAnimation:objct:
|
|
*
|
|
* The #GObject to which the animation applies.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
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_OBJECT, pspec);
|
|
|
|
/**
|
|
* ClutterAnimation:mode:
|
|
*
|
|
* The animation mode, either a value from #ClutterAnimationMode
|
|
* or a value returned by clutter_alpha_register_func(). The
|
|
* default value is %CLUTTER_LINEAR.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_ulong ("mode",
|
|
"Mode",
|
|
"The mode of the animation",
|
|
0, G_MAXULONG,
|
|
CLUTTER_LINEAR,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_MODE, pspec);
|
|
|
|
/**
|
|
* ClutterAnimation:duration:
|
|
*
|
|
* The duration of the animation, expressed in milliseconds.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_uint ("duration",
|
|
"Duration",
|
|
"Duration of the animation, in milliseconds",
|
|
0, G_MAXUINT, 0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_DURATION, pspec);
|
|
|
|
/**
|
|
* ClutterAnimation:loop:
|
|
*
|
|
* Whether the animation should loop.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_boolean ("loop",
|
|
"Loop",
|
|
"Whether the animation should loop",
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_LOOP, pspec);
|
|
|
|
/**
|
|
* ClutterAnimation:timeline:
|
|
*
|
|
* The #ClutterTimeline used by the animation.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_object ("timeline",
|
|
"Timeline",
|
|
"The timeline used by the animation",
|
|
CLUTTER_TYPE_TIMELINE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_TIMELINE, pspec);
|
|
|
|
/**
|
|
* ClutterAnimation:alpha:
|
|
*
|
|
* The #ClutterAlpha used by the animation.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_object ("alpha",
|
|
"Alpha",
|
|
"The alpha used by the animation",
|
|
CLUTTER_TYPE_ALPHA,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_ALPHA, pspec);
|
|
|
|
/**
|
|
* ClutterAniamtion::completed:
|
|
* @animation: the animation that emitted the signal
|
|
*
|
|
* The ::completed signal is emitted once the animation has
|
|
* been completed.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
animation_signals[COMPLETED] =
|
|
g_signal_new (I_("completed"),
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterAnimationClass, completed),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
}
|
|
|
|
static void
|
|
clutter_animation_init (ClutterAnimation *self)
|
|
{
|
|
self->priv = CLUTTER_ANIMATION_GET_PRIVATE (self);
|
|
|
|
self->priv->mode = CLUTTER_LINEAR;
|
|
self->priv->properties =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
(GDestroyNotify) g_free,
|
|
(GDestroyNotify) g_object_unref);
|
|
}
|
|
|
|
static inline void
|
|
clutter_animation_bind_property_internal (ClutterAnimation *animation,
|
|
GParamSpec *pspec,
|
|
ClutterInterval *interval)
|
|
{
|
|
ClutterAnimationPrivate *priv = animation->priv;
|
|
|
|
if (!clutter_interval_validate (interval, pspec))
|
|
{
|
|
g_warning ("Cannot bind property `%s': the interval is out "
|
|
"of bounds",
|
|
pspec->name);
|
|
return;
|
|
}
|
|
|
|
g_hash_table_insert (priv->properties,
|
|
g_strdup (pspec->name),
|
|
g_object_ref_sink (interval));
|
|
}
|
|
|
|
static inline void
|
|
clutter_animation_update_property_internal (ClutterAnimation *animation,
|
|
GParamSpec *pspec,
|
|
ClutterInterval *interval)
|
|
{
|
|
ClutterAnimationPrivate *priv = animation->priv;
|
|
|
|
if (!clutter_interval_validate (interval, pspec))
|
|
{
|
|
g_warning ("Cannot bind property `%s': the interval is out "
|
|
"of bounds",
|
|
pspec->name);
|
|
return;
|
|
}
|
|
|
|
g_hash_table_replace (priv->properties,
|
|
g_strdup (pspec->name),
|
|
g_object_ref_sink (interval));
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_bind_property:
|
|
* @animation: a #ClutterAnimation
|
|
* @property_name: the property to control
|
|
* @interval: a #ClutterInterval
|
|
*
|
|
* Binds @interval to the @property_name of the #GObject
|
|
* attached to @animation. The #ClutterAnimation will take
|
|
* ownership of the passed #ClutterInterval.
|
|
*
|
|
* If you need to update the interval instance use
|
|
* clutter_animation_update_property() instead.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_animation_bind_property (ClutterAnimation *animation,
|
|
const gchar *property_name,
|
|
ClutterInterval *interval)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
GObjectClass *klass;
|
|
GParamSpec *pspec;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
|
|
g_return_if_fail (property_name != NULL);
|
|
g_return_if_fail (CLUTTER_IS_INTERVAL (interval));
|
|
|
|
priv = animation->priv;
|
|
|
|
if (G_UNLIKELY (!priv->object))
|
|
{
|
|
g_warning ("Cannot bind property `%s': the animation has no "
|
|
"object set. You need to call clutter_animation_set_object() "
|
|
"first to be able to bind a property",
|
|
property_name);
|
|
return;
|
|
}
|
|
|
|
if (G_UNLIKELY (clutter_animation_has_property (animation, property_name)))
|
|
{
|
|
g_warning ("Cannot bind property `%s': the animation already has "
|
|
"a bound property with the same name",
|
|
property_name);
|
|
return;
|
|
}
|
|
|
|
klass = G_OBJECT_GET_CLASS (priv->object);
|
|
pspec = g_object_class_find_property (klass, property_name);
|
|
if (!pspec)
|
|
{
|
|
g_warning ("Cannot bind property `%s': objects of type `%s' have "
|
|
"no such property",
|
|
property_name,
|
|
g_type_name (G_OBJECT_TYPE (priv->object)));
|
|
return;
|
|
}
|
|
|
|
if (!(pspec->flags & G_PARAM_WRITABLE))
|
|
{
|
|
g_warning ("Cannot bind property `%s': the property is not writable",
|
|
property_name);
|
|
return;
|
|
}
|
|
|
|
if (!g_value_type_compatible (G_PARAM_SPEC_VALUE_TYPE (pspec),
|
|
clutter_interval_get_value_type (interval)))
|
|
{
|
|
g_warning ("Cannot bind property `%s': the interval value of "
|
|
"type `%s' is not compatible with the property value "
|
|
"of type `%s'",
|
|
property_name,
|
|
g_type_name (clutter_interval_get_value_type (interval)),
|
|
g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
|
|
return;
|
|
}
|
|
|
|
clutter_animation_bind_property_internal (animation, pspec, interval);
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_unbind_property:
|
|
* @animation: a #ClutterAnimation
|
|
* @property_name: name of the property
|
|
*
|
|
* Removes @property_name from the list of animated properties.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_animation_unbind_property (ClutterAnimation *animation,
|
|
const gchar *property_name)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
|
|
g_return_if_fail (property_name != NULL);
|
|
|
|
priv = animation->priv;
|
|
|
|
if (!clutter_animation_has_property (animation, property_name))
|
|
{
|
|
g_warning ("Cannot unbind property `%s': the animation has "
|
|
"no bound property with that name",
|
|
property_name);
|
|
return;
|
|
}
|
|
|
|
g_hash_table_remove (priv->properties, property_name);
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_has_property:
|
|
* @animation: a #ClutterAnimation
|
|
* @property_name: name of the property
|
|
*
|
|
* Checks whether @animation is controlling @property_name.
|
|
*
|
|
* Return value: %TRUE if the property is animated by the
|
|
* #ClutterAnimation, %FALSE otherwise
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
gboolean
|
|
clutter_animation_has_property (ClutterAnimation *animation,
|
|
const gchar *property_name)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), FALSE);
|
|
g_return_val_if_fail (property_name != NULL, FALSE);
|
|
|
|
priv = animation->priv;
|
|
|
|
return g_hash_table_lookup (priv->properties, property_name) != NULL;
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_update_property:
|
|
* @animation: a #ClutterAnimation
|
|
* @property_name: name of the property
|
|
* @interval: a #ClutterInterval
|
|
*
|
|
* Changes the @interval for @property_name. The #ClutterAnimation
|
|
* will take ownership of the passed #ClutterInterval.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_animation_update_property (ClutterAnimation *animation,
|
|
const gchar *property_name,
|
|
ClutterInterval *interval)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
GObjectClass *klass;
|
|
GParamSpec *pspec;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
|
|
g_return_if_fail (property_name != NULL);
|
|
g_return_if_fail (CLUTTER_IS_INTERVAL (interval));
|
|
|
|
priv = animation->priv;
|
|
|
|
if (!clutter_animation_has_property (animation, property_name))
|
|
{
|
|
g_warning ("Cannot unbind property `%s': the animation has "
|
|
"no bound property with that name",
|
|
property_name);
|
|
return;
|
|
}
|
|
|
|
klass = G_OBJECT_GET_CLASS (priv->object);
|
|
pspec = g_object_class_find_property (klass, property_name);
|
|
if (!pspec)
|
|
{
|
|
g_warning ("Cannot bind property `%s': objects of type `%s' have "
|
|
"no such property",
|
|
property_name,
|
|
g_type_name (G_OBJECT_TYPE (priv->object)));
|
|
return;
|
|
}
|
|
|
|
if (!g_value_type_compatible (G_PARAM_SPEC_VALUE_TYPE (pspec),
|
|
clutter_interval_get_value_type (interval)))
|
|
{
|
|
g_warning ("Cannot bind property `%s': the interval value of "
|
|
"type `%s' is not compatible with the property value "
|
|
"of type `%s'",
|
|
property_name,
|
|
g_type_name (clutter_interval_get_value_type (interval)),
|
|
g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
|
|
return;
|
|
}
|
|
|
|
clutter_animation_update_property_internal (animation, pspec, interval);
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_get_interval:
|
|
* @animation: a #ClutterAnimation
|
|
* @property_name: name of the property
|
|
*
|
|
* Retrieves the #ClutterInterval associated to @property_name
|
|
* inside @animation.
|
|
*
|
|
* Return value: a #ClutterInterval or %NULL if no property with
|
|
* the same name was found. The returned interval is owned by
|
|
* the #ClutterAnimation and should not be unreferenced
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterInterval *
|
|
clutter_animation_get_interval (ClutterAnimation *animation,
|
|
const gchar *property_name)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
|
|
g_return_val_if_fail (property_name != NULL, NULL);
|
|
|
|
priv = animation->priv;
|
|
|
|
return g_hash_table_lookup (priv->properties, property_name);
|
|
}
|
|
|
|
static void
|
|
on_timeline_completed (ClutterTimeline *timeline,
|
|
ClutterAnimation *animation)
|
|
{
|
|
CLUTTER_NOTE (ANIMATION, "Timeline [%p] complete", timeline);
|
|
|
|
if (!animation->priv->loop)
|
|
g_signal_emit (animation, animation_signals[COMPLETED], 0);
|
|
}
|
|
|
|
static void
|
|
on_alpha_notify (GObject *gobject,
|
|
GParamSpec *pspec,
|
|
ClutterAnimation *animation)
|
|
{
|
|
ClutterAnimationPrivate *priv = animation->priv;
|
|
GList *properties, *p;
|
|
gdouble alpha_value;
|
|
gboolean is_animatable = FALSE;
|
|
ClutterAnimatable *animatable = NULL;
|
|
|
|
alpha_value = clutter_alpha_get_alpha (CLUTTER_ALPHA (gobject));
|
|
|
|
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)
|
|
{
|
|
const gchar *p_name = p->data;
|
|
ClutterInterval *interval;
|
|
GValue value = { 0, };
|
|
|
|
interval = g_hash_table_lookup (priv->properties, p_name);
|
|
g_assert (CLUTTER_IS_INTERVAL (interval));
|
|
|
|
g_value_init (&value, clutter_interval_get_value_type (interval));
|
|
|
|
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,
|
|
alpha_value,
|
|
&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, alpha_value, &value))
|
|
g_object_set_property (priv->object, p_name, &value);
|
|
}
|
|
|
|
g_value_unset (&value);
|
|
}
|
|
|
|
g_list_free (properties);
|
|
|
|
g_object_thaw_notify (priv->object);
|
|
}
|
|
|
|
/*
|
|
* Removes the animation pointer from the qdata section of the
|
|
* actor attached to the animation
|
|
*/
|
|
static void
|
|
on_animation_weak_notify (gpointer data,
|
|
GObject *animation_pointer)
|
|
{
|
|
GObject *actor = data;
|
|
|
|
CLUTTER_NOTE (ANIMATION, "Removing Animation from actor %d[%p]",
|
|
clutter_actor_get_gid (CLUTTER_ACTOR (actor)),
|
|
actor);
|
|
|
|
g_object_set_qdata (actor, quark_object_animation, NULL);
|
|
}
|
|
|
|
ClutterAnimation *
|
|
clutter_animation_new (void)
|
|
{
|
|
return g_object_new (CLUTTER_TYPE_ANIMATION, NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_set_object:
|
|
* @animation: a #ClutterAnimation
|
|
* @object: a #GObject
|
|
*
|
|
* Attaches @animation to @object. The #ClutterAnimation will take a
|
|
* reference on @object.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_animation_set_object (ClutterAnimation *animation,
|
|
GObject *object)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
|
|
priv = animation->priv;
|
|
|
|
g_object_ref (object);
|
|
|
|
if (priv->object)
|
|
{
|
|
g_object_weak_unref (G_OBJECT (animation),
|
|
on_animation_weak_notify,
|
|
priv->object);
|
|
g_object_set_qdata (priv->object, quark_object_animation, NULL);
|
|
g_object_unref (priv->object);
|
|
}
|
|
|
|
priv->object = object;
|
|
g_object_weak_ref (G_OBJECT (animation),
|
|
on_animation_weak_notify,
|
|
priv->object);
|
|
g_object_set_qdata (G_OBJECT (priv->object),
|
|
quark_object_animation,
|
|
animation);
|
|
|
|
g_object_notify (G_OBJECT (animation), "object");
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_get_object:
|
|
* @animation: a #ClutterAnimation
|
|
*
|
|
* Retrieves the #GObject attached to @animation.
|
|
*
|
|
* Return value: a #GObject
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
GObject *
|
|
clutter_animation_get_object (ClutterAnimation *animation)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
|
|
|
|
return animation->priv->object;
|
|
}
|
|
|
|
static inline void
|
|
clutter_animation_set_mode_internal (ClutterAnimation *animation,
|
|
ClutterAlpha *alpha)
|
|
{
|
|
ClutterAnimationPrivate *priv = animation->priv;
|
|
|
|
if (alpha)
|
|
clutter_alpha_set_mode (alpha, priv->mode);
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_set_mode:
|
|
* @animation: a #ClutterAnimation
|
|
* @mode: an animation mode logical id
|
|
*
|
|
* Sets the animation @mode of @animation. The animation @mode is
|
|
* a logical id, either coming from the #ClutterAnimationMode enumeration
|
|
* or the return value of clutter_alpha_register_func().
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_animation_set_mode (ClutterAnimation *animation,
|
|
gulong mode)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
|
|
|
|
priv = animation->priv;
|
|
|
|
priv->mode = mode;
|
|
clutter_animation_set_mode_internal (animation, priv->alpha);
|
|
|
|
g_object_notify (G_OBJECT (animation), "mode");
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_get_mode:
|
|
* @animation: a #ClutterAnimation
|
|
*
|
|
* Retrieves the animation mode of @animation, as set by
|
|
* clutter_animation_set_mode().
|
|
*
|
|
* Return value: the mode for the animation
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
gulong
|
|
clutter_animation_get_mode (ClutterAnimation *animation)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), CLUTTER_LINEAR);
|
|
|
|
return animation->priv->mode;
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_set_duration:
|
|
* @animation: a #ClutterAnimation
|
|
* @msecs: the duration in milliseconds
|
|
*
|
|
* Sets the duration of @animation in milliseconds.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_animation_set_duration (ClutterAnimation *animation,
|
|
gint msecs)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
|
|
|
|
priv = animation->priv;
|
|
|
|
priv->duration = msecs;
|
|
|
|
if (priv->timeline)
|
|
{
|
|
gboolean was_playing;
|
|
|
|
was_playing = clutter_timeline_is_playing (priv->timeline);
|
|
if (was_playing)
|
|
clutter_timeline_stop (priv->timeline);
|
|
|
|
clutter_timeline_set_duration (priv->timeline, msecs);
|
|
|
|
if (was_playing)
|
|
clutter_timeline_start (priv->timeline);
|
|
}
|
|
|
|
g_object_notify (G_OBJECT (animation), "duration");
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_set_loop:
|
|
* @animation: a #ClutterAnimation
|
|
* @loop: %TRUE if the animation should loop
|
|
*
|
|
* Sets whether @animation should loop over itself once finished.
|
|
*
|
|
* A looping #ClutterAnimation will not emit the #ClutterAnimation::completed
|
|
* signal when finished.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_animation_set_loop (ClutterAnimation *animation,
|
|
gboolean loop)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
|
|
|
|
priv = animation->priv;
|
|
|
|
if (priv->loop != loop)
|
|
{
|
|
priv->loop = loop;
|
|
|
|
if (priv->timeline)
|
|
clutter_timeline_set_loop (priv->timeline, priv->loop);
|
|
|
|
g_object_notify (G_OBJECT (animation), "loop");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_get_loop:
|
|
* @animation: a #ClutterAnimation
|
|
*
|
|
* Retrieves whether @animation is looping.
|
|
*
|
|
* Return value: %TRUE if the animation is looping
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
gboolean
|
|
clutter_animation_get_loop (ClutterAnimation *animation)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), FALSE);
|
|
|
|
return animation->priv->loop;
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_get_duration:
|
|
* @animation: a #ClutterAnimation
|
|
*
|
|
* Retrieves the duration of @animation, in milliseconds.
|
|
*
|
|
* Return value: the duration of the animation
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
guint
|
|
clutter_animation_get_duration (ClutterAnimation *animation)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), 0);
|
|
|
|
return animation->priv->duration;
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_set_timeline:
|
|
* @animation: a #ClutterAnimation
|
|
* @timeline: a #ClutterTimeline or %NULL
|
|
*
|
|
* Sets the #ClutterTimeline used by @animation.
|
|
*
|
|
* The #ClutterAnimation:duration and #ClutterAnimation:loop properties
|
|
* will be set using the corresponding #ClutterTimeline properties as a
|
|
* side effect.
|
|
*
|
|
* If @timeline is %NULL a new #ClutterTimeline will be constructed
|
|
* using the current values of the #ClutterAnimation:duration and
|
|
* #ClutterAnimation:loop properties.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_animation_set_timeline (ClutterAnimation *animation,
|
|
ClutterTimeline *timeline)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
|
|
g_return_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
priv = animation->priv;
|
|
|
|
if (timeline && priv->timeline == timeline)
|
|
return;
|
|
|
|
g_object_freeze_notify (G_OBJECT (animation));
|
|
|
|
if (priv->timeline)
|
|
{
|
|
if (priv->timeline_completed_id)
|
|
g_signal_handler_disconnect (priv->timeline,
|
|
priv->timeline_completed_id);
|
|
|
|
g_object_unref (priv->timeline);
|
|
priv->timeline_completed_id = 0;
|
|
priv->timeline = 0;
|
|
}
|
|
|
|
if (!timeline)
|
|
timeline = g_object_new (CLUTTER_TYPE_TIMELINE,
|
|
"duration", priv->duration,
|
|
"loop", priv->loop,
|
|
NULL);
|
|
else
|
|
{
|
|
priv->duration = clutter_timeline_get_duration (timeline);
|
|
g_object_notify (G_OBJECT (animation), "duration");
|
|
|
|
priv->loop = clutter_timeline_get_loop (timeline);
|
|
g_object_notify (G_OBJECT (animation), "loop");
|
|
}
|
|
|
|
priv->timeline = g_object_ref (timeline);
|
|
g_object_notify (G_OBJECT (animation), "timeline");
|
|
|
|
priv->timeline_completed_id =
|
|
g_signal_connect (timeline, "completed",
|
|
G_CALLBACK (on_timeline_completed),
|
|
animation);
|
|
|
|
g_object_thaw_notify (G_OBJECT (animation));
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_get_timeline:
|
|
* @animation: a #ClutterAnimation
|
|
*
|
|
* Retrieves the #ClutterTimeline used by @animation
|
|
*
|
|
* Return value: the timeline used by the animation
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterTimeline *
|
|
clutter_animation_get_timeline (ClutterAnimation *animation)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
|
|
|
|
return animation->priv->timeline;
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_set_alpha:
|
|
* @animation: a #ClutterAnimation
|
|
* @alpha: a #ClutterAlpha, or %NULL
|
|
*
|
|
* Sets @alpha as the #ClutterAlpha used by @animation.
|
|
*
|
|
* If @alpha is %NULL, a new #ClutterAlpha will be constructed from
|
|
* the current values of the #ClutterAnimation:mode and
|
|
* #ClutterAnimation:timeline properties.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_animation_set_alpha (ClutterAnimation *animation,
|
|
ClutterAlpha *alpha)
|
|
{
|
|
ClutterAnimationPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
|
|
g_return_if_fail (alpha == NULL || CLUTTER_IS_ALPHA (alpha));
|
|
|
|
priv = animation->priv;
|
|
|
|
if (!alpha)
|
|
{
|
|
ClutterTimeline *timeline;
|
|
|
|
timeline = clutter_animation_get_timeline (animation);
|
|
|
|
alpha = clutter_alpha_new ();
|
|
clutter_alpha_set_timeline (alpha, timeline);
|
|
clutter_animation_set_mode_internal (animation, alpha);
|
|
}
|
|
|
|
g_object_ref_sink (alpha);
|
|
|
|
if (priv->alpha)
|
|
{
|
|
if (priv->alpha_notify_id)
|
|
g_signal_handler_disconnect (priv->alpha, priv->alpha_notify_id);
|
|
|
|
g_object_unref (priv->alpha);
|
|
priv->alpha_notify_id = 0;
|
|
priv->alpha = NULL;
|
|
}
|
|
|
|
priv->alpha = alpha;
|
|
|
|
priv->alpha_notify_id =
|
|
g_signal_connect (alpha, "notify::alpha",
|
|
G_CALLBACK (on_alpha_notify),
|
|
animation);
|
|
}
|
|
|
|
/**
|
|
* clutter_animation_get_alpha:
|
|
* @animation: a #ClutterAnimation
|
|
*
|
|
* Retrieves the #ClutterAlpha used by @animation.
|
|
*
|
|
* Return value: the alpha object used by the animation
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterAlpha *
|
|
clutter_animation_get_alpha (ClutterAnimation *animation)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
|
|
|
|
return animation->priv->alpha;
|
|
}
|
|
|
|
/*
|
|
* starts the timeline
|
|
*/
|
|
static void
|
|
clutter_animation_start (ClutterAnimation *animation)
|
|
{
|
|
if (animation->priv->timeline)
|
|
clutter_timeline_start (animation->priv->timeline);
|
|
else
|
|
{
|
|
/* sanity check */
|
|
g_warning (G_STRLOC ": no timeline found, unable to start the animation");
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
clutter_animation_setup_valist (ClutterAnimation *animation,
|
|
const gchar *first_property_name,
|
|
va_list var_args)
|
|
{
|
|
ClutterAnimationPrivate *priv = animation->priv;
|
|
GObjectClass *klass;
|
|
const gchar *property_name;
|
|
|
|
klass = G_OBJECT_GET_CLASS (priv->object);
|
|
|
|
property_name = first_property_name;
|
|
while (property_name != NULL)
|
|
{
|
|
GValue final = { 0, };
|
|
GParamSpec *pspec = NULL;
|
|
gboolean is_fixed = FALSE;
|
|
gchar *error = NULL;
|
|
|
|
/* fixed properties will not be animated */
|
|
if (g_str_has_prefix (property_name, "fixed::"))
|
|
{
|
|
is_fixed = TRUE;
|
|
property_name += 7;
|
|
}
|
|
|
|
pspec = g_object_class_find_property (klass, property_name);
|
|
if (!pspec)
|
|
{
|
|
g_warning ("Cannot bind property `%s': objects of type `%s' do "
|
|
"not have this property",
|
|
property_name,
|
|
g_type_name (G_OBJECT_TYPE (priv->object)));
|
|
break;
|
|
}
|
|
|
|
if (!(pspec->flags & G_PARAM_WRITABLE))
|
|
{
|
|
g_warning ("Cannot bind property `%s': the property is "
|
|
"not writable",
|
|
property_name);
|
|
break;
|
|
}
|
|
|
|
g_value_init (&final, G_PARAM_SPEC_VALUE_TYPE (pspec));
|
|
G_VALUE_COLLECT (&final, var_args, 0, &error);
|
|
if (error)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error);
|
|
g_free (error);
|
|
break;
|
|
}
|
|
|
|
/* create an interval and bind it to the property, in case
|
|
* it's not a fixed property, otherwise just set it
|
|
*/
|
|
if (G_LIKELY (!is_fixed))
|
|
{
|
|
ClutterInterval *interval;
|
|
GValue initial = { 0, };
|
|
|
|
g_value_init (&initial, G_PARAM_SPEC_VALUE_TYPE (pspec));
|
|
g_object_get_property (priv->object, property_name, &initial);
|
|
|
|
interval =
|
|
clutter_interval_new_with_values (G_PARAM_SPEC_VALUE_TYPE (pspec),
|
|
&initial,
|
|
&final);
|
|
|
|
if (!clutter_animation_has_property (animation, pspec->name))
|
|
clutter_animation_bind_property_internal (animation,
|
|
pspec,
|
|
interval);
|
|
else
|
|
clutter_animation_update_property_internal (animation,
|
|
pspec,
|
|
interval);
|
|
|
|
g_value_unset (&initial);
|
|
}
|
|
else
|
|
g_object_set_property (priv->object, property_name, &final);
|
|
|
|
g_value_unset (&final);
|
|
|
|
property_name = va_arg (var_args, gchar*);
|
|
}
|
|
|
|
/* start the animation by default */
|
|
clutter_animation_start (animation);
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_animate_with_alpha:
|
|
* @actor: a #ClutterActor
|
|
* @alpha: a #ClutterAlpha
|
|
* @first_property_name: the name of a property
|
|
* @VarArgs: a %NULL terminated list of property names and
|
|
* property values
|
|
*
|
|
* Animates the given list of properties of @actor between the current
|
|
* value for each property and a new final value. The animation has a
|
|
* definite behaviour given by the passed @alpha.
|
|
*
|
|
* See clutter_actor_animate() for further details.
|
|
*
|
|
* This function is useful if you want to use an existing #ClutterAlpha
|
|
* to animate @actor.
|
|
*
|
|
* Return value: a #ClutterAnimation object. The object is owned by the
|
|
* #ClutterActor and should not be unreferenced with g_object_unref()
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterAnimation *
|
|
clutter_actor_animate_with_alpha (ClutterActor *actor,
|
|
ClutterAlpha *alpha,
|
|
const gchar *first_property_name,
|
|
...)
|
|
{
|
|
ClutterAnimation *animation;
|
|
ClutterTimeline *timeline;
|
|
va_list args;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
|
|
g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), NULL);
|
|
g_return_val_if_fail (first_property_name != NULL, NULL);
|
|
|
|
timeline = clutter_alpha_get_timeline (alpha);
|
|
if (G_UNLIKELY (!timeline))
|
|
{
|
|
g_warning ("The passed ClutterAlpha does not have an "
|
|
"associated ClutterTimeline.");
|
|
return NULL;
|
|
}
|
|
|
|
animation = g_object_get_qdata (G_OBJECT (actor), quark_object_animation);
|
|
if (G_LIKELY (!animation))
|
|
{
|
|
animation = clutter_animation_new ();
|
|
CLUTTER_NOTE (ANIMATION, "Created new Animation [%p]", animation);
|
|
}
|
|
else
|
|
CLUTTER_NOTE (ANIMATION, "Reusing Animation [%p]", animation);
|
|
|
|
clutter_animation_set_timeline (animation, timeline);
|
|
clutter_animation_set_alpha (animation, alpha);
|
|
clutter_animation_set_object (animation, G_OBJECT (actor));
|
|
|
|
va_start (args, first_property_name);
|
|
clutter_animation_setup_valist (animation, first_property_name, args);
|
|
va_end (args);
|
|
|
|
return animation;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_animate_with_timeline:
|
|
* @actor: a #ClutterActor
|
|
* @mode: an animation mode logical id
|
|
* @timeline: a #ClutterTimeline
|
|
* @first_property_name: the name of a property
|
|
* @VarArgs: a %NULL terminated list of property names and
|
|
* property values
|
|
*
|
|
* Animates the given list of properties of @actor between the current
|
|
* value for each property and a new final value. The animation has a
|
|
* definite duration given by @timeline and a speed given by the @mode.
|
|
*
|
|
* See clutter_actor_animate() for further details.
|
|
*
|
|
* This function is useful if you want to use an existing timeline
|
|
* to animate @actor.
|
|
*
|
|
* Return value: a #ClutterAnimation object. The object is owned by the
|
|
* #ClutterActor and should not be unreferenced with g_object_unref()
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterAnimation *
|
|
clutter_actor_animate_with_timeline (ClutterActor *actor,
|
|
gulong mode,
|
|
ClutterTimeline *timeline,
|
|
const gchar *first_property_name,
|
|
...)
|
|
{
|
|
ClutterAnimation *animation;
|
|
va_list args;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
|
|
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_object_animation);
|
|
if (G_LIKELY (!animation))
|
|
{
|
|
animation = clutter_animation_new ();
|
|
CLUTTER_NOTE (ANIMATION, "Created new Animation [%p]", animation);
|
|
}
|
|
else
|
|
CLUTTER_NOTE (ANIMATION, "Reusing Animation [%p]", animation);
|
|
|
|
clutter_animation_set_timeline (animation, timeline);
|
|
clutter_animation_set_alpha (animation, NULL);
|
|
clutter_animation_set_mode (animation, mode);
|
|
clutter_animation_set_object (animation, G_OBJECT (actor));
|
|
|
|
va_start (args, first_property_name);
|
|
clutter_animation_setup_valist (animation, first_property_name, args);
|
|
va_end (args);
|
|
|
|
return animation;
|
|
}
|
|
|
|
/**
|
|
* clutter_actor_animate:
|
|
* @actor: a #ClutterActor
|
|
* @mode: an animation mode logical id
|
|
* @duration: duration of the animation, in milliseconds
|
|
* @first_property_name: the name of a property
|
|
* @VarArgs: a %NULL terminated list of property names and
|
|
* property values
|
|
*
|
|
* Animates the given list of properties of @actor between the current
|
|
* value for each property and a new final value. The animation has a
|
|
* definite duration and a speed given by the @mode.
|
|
*
|
|
* For example, this:
|
|
*
|
|
* |[
|
|
* clutter_actor_animate (rectangle, CLUTTER_LINEAR, 250,
|
|
* "width", 100,
|
|
* "height", 100,
|
|
* NULL);
|
|
* ]|
|
|
*
|
|
* will make width and height properties of the #ClutterActor "rectangle"
|
|
* grow linearly between the current value and 100 pixels, in 250 milliseconds.
|
|
*
|
|
* The animation @mode is a logical id, either from the #ClutterAnimationMode
|
|
* enumeration of from clutter_alpha_register_func().
|
|
*
|
|
* All the properties specified will be animated between the current value
|
|
* and the final value. If a property should be set at the beginning of
|
|
* the animation but not updated during the animation, it should be prefixed
|
|
* by the "fixed::" string, for instance:
|
|
*
|
|
* |[
|
|
* clutter_actor_animate (actor, CLUTTER_EASE_IN, 100,
|
|
* "rotation-angle-z", 360,
|
|
* "fixed::rotation-center-x", 100,
|
|
* "fixed::rotation-center-y", 100,
|
|
* NULL);
|
|
* ]|
|
|
*
|
|
* Will animate the "rotation-angle-z" property between the current value
|
|
* and 360 degrees, and set the "rotation-center-x" and "rotation-center-y"
|
|
* to the fixed value of 100 pixels.
|
|
*
|
|
* This function will implicitly create a #ClutterAnimation object which
|
|
* will be assigned to the @actor and will be returned to the developer
|
|
* to control the animation or to know when the animation has been
|
|
* completed.
|
|
*
|
|
* Calling this function on an actor that is already being animated
|
|
* will cause the current animation to change with the new final value.
|
|
*
|
|
* <note>Unless the animation is looping, it will become invalid as soon
|
|
* as it is complete. To avoid this, you should keep a reference on the
|
|
* returned value using g_object_ref().</note>
|
|
*
|
|
* Return value: a #ClutterAnimation object. The object is owned by the
|
|
* #ClutterActor and should not be unreferenced with g_object_unref()
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterAnimation *
|
|
clutter_actor_animate (ClutterActor *actor,
|
|
gulong mode,
|
|
guint duration,
|
|
const gchar *first_property_name,
|
|
...)
|
|
{
|
|
ClutterAnimation *animation;
|
|
va_list args;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
|
|
g_return_val_if_fail (mode != CLUTTER_CUSTOM_MODE, NULL);
|
|
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_object_animation);
|
|
if (G_LIKELY (!animation))
|
|
{
|
|
/* if there is no animation already attached to the actor,
|
|
* create one and set up the timeline and alpha using the
|
|
* current values for duration, mode and loop
|
|
*/
|
|
animation = clutter_animation_new ();
|
|
clutter_animation_set_timeline (animation, NULL);
|
|
clutter_animation_set_alpha (animation, NULL);
|
|
clutter_animation_set_object (animation, G_OBJECT (actor));
|
|
|
|
CLUTTER_NOTE (ANIMATION, "Created new Animation [%p]", animation);
|
|
}
|
|
else
|
|
CLUTTER_NOTE (ANIMATION, "Reusing Animation [%p]", animation);
|
|
|
|
/* force the update of duration and mode using the new
|
|
* values coming from the parameters of this function
|
|
*/
|
|
clutter_animation_set_duration (animation, duration);
|
|
clutter_animation_set_mode (animation, mode);
|
|
|
|
va_start (args, first_property_name);
|
|
clutter_animation_setup_valist (animation, first_property_name, args);
|
|
va_end (args);
|
|
|
|
return animation;
|
|
}
|