4
* An OpenGL based 'interactive canvas' library.
6
* Copyright (C) 2010 Intel Corporation
8
* This library is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public
10
* License as published by the Free Software Foundation; either
11
* version 2 of the License, or (at your option) any later version.
13
* This library is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* Lesser General Public License for more details.
18
* You should have received a copy of the GNU Lesser General Public
19
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
22
* Ćyvind KolĆ„s <pippin@linux.intel.com>
26
* SECTION:clutter-animator
27
* @short_description: Multi-actor tweener
28
* @See_Also: #ClutterAnimatable, #ClutterInterval, #ClutterAlpha,
31
* #ClutterAnimator is an object providing declarative animations for
32
* #GObject properties belonging to one or more #GObject<!-- -->s to
35
* #ClutterAnimator is used to build and describe complex animations
36
* in terms of "key frames". #ClutterAnimator is meant to be used
37
* through the #ClutterScript definition format, but it comes with a
40
* <refsect2 id="ClutterAnimator-key-frames">
41
* <title>Key Frames</title>
42
* <para>Every animation handled by a #ClutterAnimator can be
43
* described in terms of "key frames". For each #GObject property
44
* there can be multiple key frames, each one defined by the end
45
* value for the property to be computed starting from the current
46
* value to a specific point in time, using a given easing
48
* <para>The point in time is defined using a value representing
49
* the progress in the normalized interval of [ 0, 1 ]. This maps
50
* the value returned by clutter_timeline_get_duration().</para>
51
* <figure id="easing-modes">
52
* <title>Key Frames</title>
53
* <graphic fileref="animator-key-frames.png" format="PNG"/>
55
* <para>In the image above the duration of the animation is
56
* represented by the blue line. Each key frame is the white dot,
57
* along with its progress. The red line represents the computed
58
* function of time given the easing mode.</para>
61
* <refsect2 id="ClutterAnimator-script">
62
* <title>ClutterAnimator description for #ClutterScript</title>
63
* <para>#ClutterAnimator defines a custom "properties" property
64
* which allows describing the key frames for objects.</para>
65
* <para>The "properties" property has the following syntax:</para>
71
* "object" : <id of an object>,
72
* "name" : <name of the property>,
73
* "ease-in" : <boolean>,
74
* "interpolation" : <#ClutterInterpolation value>,
76
* [ <progress>, <easing mode>, <final value> ]
82
* <example id="ClutterAnimator-script-example">
83
* <title>ClutterAnimator definition</title>
84
* <para>The following JSON fragment defines a #ClutterAnimator
85
* with the duration of 1 second and operating on the x and y
86
* properties of a #ClutterActor named "rect-01", with two frames
87
* for each property. The first frame will linearly move the actor
88
* from its current position to the 100, 100 position in 20 percent
89
* of the duration of the animation; the second will using a cubic
90
* easing to move the actor to the 200, 200 coordinates.</para>
93
* "type" : "ClutterAnimator",
97
* "object" : "rect-01",
101
* [ 0.2, "linear", 100.0 ],
102
* [ 1.0, "easeOutCubic", 200.0 ]
106
* "object" : "rect-01",
110
* [ 0.2, "linear", 100.0 ],
111
* [ 1.0, "easeOutCubic", 200.0 ]
120
* #ClutterAnimator is available since Clutter 1.2
128
#include <gobject/gvaluecollector.h>
130
#include "clutter-animator.h"
132
#include "clutter-alpha.h"
133
#include "clutter-debug.h"
134
#include "clutter-enum-types.h"
135
#include "clutter-interval.h"
136
#include "clutter-private.h"
137
#include "clutter-script-private.h"
138
#include "clutter-scriptable.h"
140
#define CLUTTER_ANIMATOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ANIMATOR, ClutterAnimatorPrivate))
142
/* progress values varying by less than this are considered equal */
143
#define PROGRESS_EPSILON 0.00001
145
struct _ClutterAnimatorPrivate
147
ClutterTimeline *timeline;
148
ClutterTimeline *slave_timeline;
152
GHashTable *properties;
155
struct _ClutterAnimatorKey
158
const gchar *property_name;
163
/* normalized progress, between 0.0 and 1.0 */
166
/* back-pointer to the animator which owns the key */
167
ClutterAnimator *animator;
169
/* interpolation mode */
170
ClutterInterpolation interpolation;
172
/* ease from the current object state into the animation when it starts */
175
/* This key is already being destroyed and shouldn't
176
* trigger additional weak unrefs
191
static void clutter_scriptable_init (ClutterScriptableIface *iface);
193
G_DEFINE_TYPE_WITH_CODE (ClutterAnimator,
196
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
197
clutter_scriptable_init));
199
* clutter_animator_new:
201
* Creates a new #ClutterAnimator instance
203
* Return value: a new #ClutterAnimator.
208
clutter_animator_new (void)
210
return g_object_new (CLUTTER_TYPE_ANIMATOR, NULL);
215
typedef struct _PropObjectKey {
217
const gchar *property_name;
222
typedef struct _KeyAnimator {
224
ClutterInterval *interval;
229
gdouble start; /* the progress of current */
230
gdouble end; /* until which progress it is valid */
231
ClutterInterpolation interpolation;
236
static PropObjectKey *
237
prop_actor_key_new (GObject *object,
238
const gchar *property_name)
240
PropObjectKey *key = g_slice_new0 (PropObjectKey);
242
key->object = object;
243
key->property_name = g_intern_string (property_name);
249
prop_actor_key_free (gpointer key)
252
g_slice_free (PropObjectKey, key);
256
key_animator_free (gpointer key)
260
KeyAnimator *key_animator = key;
262
g_object_unref (key_animator->interval);
263
g_object_unref (key_animator->alpha);
265
g_slice_free (KeyAnimator, key_animator);
270
key_animator_new (ClutterAnimator *animator,
274
KeyAnimator *key_animator = g_slice_new (KeyAnimator);
275
ClutterInterval *interval = g_object_new (CLUTTER_TYPE_INTERVAL,
279
/* we own this interval */
280
g_object_ref_sink (interval);
282
key_animator->interval = interval;
283
key_animator->key = key;
284
key_animator->alpha = clutter_alpha_new ();
285
clutter_alpha_set_timeline (key_animator->alpha,
286
animator->priv->slave_timeline);
288
/* as well as the alpha */
289
g_object_ref_sink (key_animator->alpha);
295
prop_actor_hash (gconstpointer value)
297
const PropObjectKey *info = value;
299
return GPOINTER_TO_INT (info->property_name)
300
^ GPOINTER_TO_INT (info->object);
304
prop_actor_equal (gconstpointer a, gconstpointer b)
306
const PropObjectKey *infoa = a;
307
const PropObjectKey *infob = b;
309
/* property name strings are interned so we can just compare pointers */
310
if (infoa->object == infob->object &&
311
(infoa->property_name == infob->property_name))
318
sort_actor_prop_progress_func (gconstpointer a,
321
const ClutterAnimatorKey *pa = a;
322
const ClutterAnimatorKey *pb = b;
324
if (pa->object == pb->object)
326
gint pdiff = pb->property_name - pa->property_name;
331
if (fabs (pa->progress - pb->progress) < PROGRESS_EPSILON)
334
if (pa->progress > pb->progress)
340
return pa->object - pb->object;
344
sort_actor_prop_func (gconstpointer a,
347
const ClutterAnimatorKey *pa = a;
348
const ClutterAnimatorKey *pb = b;
350
if (pa->object == pb->object)
351
return pa->property_name - pb->property_name;
353
return pa->object - pb->object;
358
object_disappeared (gpointer data,
359
GObject *where_the_object_was)
361
clutter_animator_remove_key (data, where_the_object_was, NULL, -1.0);
364
static ClutterAnimatorKey *
365
clutter_animator_key_new (ClutterAnimator *animator,
367
const gchar *property_name,
371
ClutterAnimatorKey *animator_key;
373
animator_key = g_slice_new (ClutterAnimatorKey);
375
animator_key->ref_count = 1;
376
animator_key->animator = animator;
377
animator_key->object = object;
378
animator_key->mode = mode;
379
memset (&(animator_key->value), 0, sizeof (GValue));
380
animator_key->progress = progress;
381
animator_key->property_name = g_intern_string (property_name);
382
animator_key->interpolation = CLUTTER_INTERPOLATION_LINEAR;
383
animator_key->ease_in = FALSE;
384
animator_key->is_inert = FALSE;
386
/* keep a weak reference on the animator, so that we can release the
387
* back-pointer when needed
389
g_object_weak_ref (object, object_disappeared,
390
animator_key->animator);
396
clutter_animator_key_copy (gpointer boxed)
398
ClutterAnimatorKey *key = boxed;
407
clutter_animator_key_free (gpointer boxed)
409
ClutterAnimatorKey *key = boxed;
416
if (key->ref_count > 0)
420
g_object_weak_unref (key->object, object_disappeared, key->animator);
422
g_slice_free (ClutterAnimatorKey, key);
426
clutter_animator_finalize (GObject *object)
428
ClutterAnimator *animator = CLUTTER_ANIMATOR (object);
429
ClutterAnimatorPrivate *priv = animator->priv;
431
g_list_foreach (priv->score, (GFunc) clutter_animator_key_free, NULL);
432
g_list_free (priv->score);
437
priv->score = g_list_remove (priv->score, priv->score->data))
439
clutter_animator_key_free (priv->score->data);
443
g_object_unref (priv->timeline);
444
g_object_unref (priv->slave_timeline);
446
G_OBJECT_CLASS (clutter_animator_parent_class)->finalize (object);
449
/* XXX: this is copied and slightly modified from glib,
450
* there is only one way to do this. */
452
list_find_custom_reverse (GList *list,
458
if (! func (list->data, data))
467
/* Ensures that the interval provided by the animator is correct
468
* for the requested progress value.
471
animation_animator_ensure_animator (ClutterAnimator *animator,
472
KeyAnimator *key_animator,
477
if (progress > key_animator->end)
479
while (progress > key_animator->end)
481
ClutterAnimatorKey *initial_key, *next_key;
482
GList *initial, *next;
484
initial = g_list_find_custom (key_animator->current->next,
486
sort_actor_prop_func);
490
initial_key = initial->data;
492
clutter_interval_set_initial_value (key_animator->interval,
493
&initial_key->value);
494
key_animator->current = initial;
495
key_animator->start = initial_key->progress;
497
next = g_list_find_custom (initial->next,
499
sort_actor_prop_func);
502
next_key = next->data;
504
key_animator->end = next_key->progress;
508
next_key = initial_key;
510
key_animator->end = 1.0;
513
clutter_interval_set_final_value (key_animator->interval,
516
if ((clutter_alpha_get_mode (key_animator->alpha) != next_key->mode))
517
clutter_alpha_set_mode (key_animator->alpha, next_key->mode);
519
else /* no relevant interval */
521
ClutterAnimatorKey *current_key = key_animator->current->data;
522
clutter_interval_set_initial_value (key_animator->interval,
523
¤t_key->value);
524
clutter_interval_set_final_value (key_animator->interval,
525
¤t_key->value);
530
else if (progress < key_animator->start)
532
while (progress < key_animator->start)
534
ClutterAnimatorKey *initial_key, *next_key;
536
GList *old = key_animator->current;
538
initial = list_find_custom_reverse (key_animator->current->prev,
540
sort_actor_prop_func);
544
initial_key = initial->data;
546
clutter_interval_set_initial_value (key_animator->interval,
547
&initial_key->value);
548
key_animator->current = initial;
549
key_animator->end = key_animator->start;
550
key_animator->start = initial_key->progress;
554
next_key = old->data;
556
key_animator->end = next_key->progress;
560
next_key = initial_key;
562
key_animator->end = 1.0;
565
clutter_interval_set_final_value (key_animator->interval,
567
if ((clutter_alpha_get_mode (key_animator->alpha) != next_key->mode))
568
clutter_alpha_set_mode (key_animator->alpha, next_key->mode);
576
/* XXX - this might be useful as an internal function exposed somewhere */
578
cubic_interpolation (const gdouble dx,
582
const gdouble nextnext)
584
return (((( - prev + 3 * j - 3 * next + nextnext ) * dx +
585
( 2 * prev - 5 * j + 4 * next - nextnext ) ) * dx +
586
( - prev + next ) ) * dx + (j + j) ) / 2.0;
589
/* try to get a floating point key value from a key for a property,
590
* failing use the closest key in that direction or the starting point.
593
list_try_get_rel (GList *list,
596
ClutterAnimatorKey *key;
602
while (count -- && iter != NULL)
604
iter = g_list_find_custom (iter->next, list->data,
605
sort_actor_prop_func);
612
while (count ++ < 0 && iter != NULL)
614
iter = list_find_custom_reverse (iter->prev, list->data,
615
sort_actor_prop_func);
621
if (best != NULL && best->data != NULL)
625
return g_value_get_float (&(key->value));
632
animation_animator_new_frame (ClutterTimeline *timeline,
634
ClutterAnimator *animator)
640
progress = 1.0 * msecs / clutter_timeline_get_duration (timeline);
642
/* for each property that is managed figure out the GValue to set,
643
* avoid creating new ClutterInterval's for each interval crossed
645
g_hash_table_iter_init (&iter, animator->priv->properties);
648
while (g_hash_table_iter_next (&iter, &key, &value))
650
PropObjectKey *prop_actor_key = key;
651
KeyAnimator *key_animator = value;
652
ClutterAnimatorKey *start_key;
653
gdouble sub_progress;
655
animation_animator_ensure_animator (animator, key_animator,
658
start_key = key_animator->current->data;
660
sub_progress = (progress - key_animator->start)
661
/ (key_animator->end - key_animator->start);
663
/* do not change values if we're not active yet (delay) */
664
if (sub_progress >= 0.0 && sub_progress <= 1.0)
666
GValue tmp_value = { 0, };
669
g_value_init (&tmp_value, G_VALUE_TYPE (&start_key->value));
671
clutter_timeline_advance (animator->priv->slave_timeline,
672
sub_progress * 10000);
674
sub_progress = clutter_alpha_get_alpha (key_animator->alpha);
675
int_type = clutter_interval_get_value_type (key_animator->interval);
677
if (key_animator->interpolation == CLUTTER_INTERPOLATION_CUBIC &&
678
int_type == G_TYPE_FLOAT)
680
gdouble prev, current, next, nextnext;
683
if ((key_animator->ease_in == FALSE ||
684
(key_animator->ease_in &&
685
list_find_custom_reverse (key_animator->current->prev,
686
key_animator->current->data,
687
sort_actor_prop_func))))
689
current = g_value_get_float (&start_key->value);
690
prev = list_try_get_rel (key_animator->current, -1);
694
/* interpolated and easing in */
695
clutter_interval_get_initial_value (key_animator->interval,
697
prev = current = g_value_get_float (&tmp_value);
700
next = list_try_get_rel (key_animator->current, 1);
701
nextnext = list_try_get_rel (key_animator->current, 2);
702
res = cubic_interpolation (sub_progress, prev, current, next,
705
g_value_set_float (&tmp_value, res);
708
clutter_interval_compute_value (key_animator->interval,
712
g_object_set_property (prop_actor_key->object,
713
prop_actor_key->property_name,
716
g_value_unset (&tmp_value);
722
animation_animator_started (ClutterTimeline *timeline,
723
ClutterAnimator *animator)
727
/* Ensure that animators exist for all involved properties */
728
for (k = animator->priv->score; k != NULL; k = k->next)
730
ClutterAnimatorKey *key = k->data;
731
KeyAnimator *key_animator;
732
PropObjectKey *prop_actor_key;
734
prop_actor_key = prop_actor_key_new (key->object, key->property_name);
735
key_animator = g_hash_table_lookup (animator->priv->properties,
739
prop_actor_key_free (prop_actor_key);
743
GObjectClass *klass = G_OBJECT_GET_CLASS (key->object);
746
pspec = g_object_class_find_property (klass, key->property_name);
748
key_animator = key_animator_new (animator, prop_actor_key,
749
G_PARAM_SPEC_VALUE_TYPE (pspec));
750
g_hash_table_insert (animator->priv->properties,
756
/* initialize animator with initial list pointers */
761
g_hash_table_iter_init (&iter, animator->priv->properties);
762
while (g_hash_table_iter_next (&iter, &key, &value))
764
KeyAnimator *key_animator = value;
765
ClutterAnimatorKey *initial_key, *next_key;
769
initial = g_list_find_custom (animator->priv->score,
771
sort_actor_prop_func);
772
g_assert (initial != NULL);
773
initial_key = initial->data;
774
clutter_interval_set_initial_value (key_animator->interval,
775
&initial_key->value);
777
key_animator->current = initial;
778
key_animator->start = initial_key->progress;
779
key_animator->ease_in = initial_key->ease_in;
780
key_animator->interpolation = initial_key->interpolation;
782
if (key_animator->ease_in)
784
GValue tmp_value = { 0, };
787
int_type = clutter_interval_get_value_type (key_animator->interval);
788
g_value_init (&tmp_value, int_type);
790
g_object_get_property (initial_key->object,
791
initial_key->property_name,
794
clutter_interval_set_initial_value (key_animator->interval,
797
g_value_unset (&tmp_value);
800
next = g_list_find_custom (initial->next, key, sort_actor_prop_func);
803
next_key = next->data;
804
key_animator->end = next_key->progress;
808
next_key = initial_key;
809
key_animator->end = 1.0;
812
clutter_interval_set_final_value (key_animator->interval,
814
if ((clutter_alpha_get_mode (key_animator->alpha) != next_key->mode))
815
clutter_alpha_set_mode (key_animator->alpha, next_key->mode);
821
* clutter_animator_compute_value:
822
* @animator: a #ClutterAnimator
823
* @object: a #GObject
824
* @property_name: the name of the property on object to check
825
* @progress: a value between 0.0 and 1.0
826
* @value: an initialized value to store the computed result
828
* Compute the value for a managed property at a given progress.
830
* If the property is an ease-in property, the current value of the property
831
* on the object will be used as the starting point for computation.
833
* Return value: %TRUE if the computation yields has a value, otherwise (when
834
* an error occurs or the progress is before any of the keys) %FALSE is
835
* returned and the #GValue is left untouched
840
clutter_animator_compute_value (ClutterAnimator *animator,
842
const gchar *property_name,
846
ClutterAnimatorKey key;
847
ClutterAnimatorKey *previous;
848
ClutterAnimatorKey *next = NULL;
854
ClutterInterpolation interpolation;
856
g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), FALSE);
857
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
858
g_return_val_if_fail (property_name, FALSE);
859
g_return_val_if_fail (value, FALSE);
861
ease_in = clutter_animator_property_get_ease_in (animator, object,
863
interpolation = clutter_animator_property_get_interpolation (animator,
864
object, property_name);
866
property_name = g_intern_string (property_name);
868
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
872
key.property_name = property_name;
874
initial_l = g_list_find_custom (animator->priv->score, &key,
875
sort_actor_prop_func);
879
/* first find the interval we belong in, that is the first interval
880
* existing between keys
883
for (previous_l = initial_l, next_l = previous_l->next ;
885
previous_l = previous_l->next, next_l = previous_l->next)
887
previous = previous_l->data;
891
if (next->object != object ||
892
next->property_name != property_name)
903
if (progress < previous->progress)
905
/* we are before the defined values */
907
/* value has not been set */
911
if (!next && previous->progress <= progress)
913
/* we only had one key for this object/property */
914
/* and we are past it, that is our value */
915
g_value_copy (&previous->value, value);
918
if (next->progress >= progress)
920
ClutterInterval *interval;
923
gdouble sub_progress = (progress - previous->progress)
924
/ (next->progress - previous->progress);
925
/* this should be our interval */
926
interval = g_object_new (CLUTTER_TYPE_INTERVAL,
927
"value-type", pspec->value_type,
930
if (ease_in && previous_l == initial_l)
932
GValue tmp_value = {0, };
933
g_value_init (&tmp_value, pspec->value_type);
934
g_object_get_property (object, property_name, &tmp_value);
935
clutter_interval_set_initial_value (interval, &tmp_value);
936
g_value_unset (&tmp_value);
940
clutter_interval_set_initial_value (interval, &previous->value);
942
clutter_interval_set_final_value (interval, &next->value);
944
alpha = clutter_alpha_new ();
945
clutter_alpha_set_timeline (alpha,
946
animator->priv->slave_timeline);
947
clutter_alpha_set_mode (alpha, next->mode);
949
clutter_timeline_advance (animator->priv->slave_timeline,
950
sub_progress * 10000);
951
sub_progress = clutter_alpha_get_alpha (alpha);
953
if (interpolation == CLUTTER_INTERPOLATION_CUBIC &&
954
pspec->value_type == G_TYPE_FLOAT)
956
gdouble prev, current, nextv, nextnext;
959
if ((ease_in == FALSE ||
961
list_find_custom_reverse (previous_l->prev,
963
sort_actor_prop_func))))
965
current = g_value_get_float (&previous->value);
966
prev = list_try_get_rel (previous_l, -1);
970
/* interpolated and easing in */
971
GValue tmp_value = {0, };
972
g_value_init (&tmp_value, pspec->value_type);
973
clutter_interval_get_initial_value (interval,
975
prev = current = g_value_get_float (&tmp_value);
976
g_value_unset (&tmp_value);
979
nextv = list_try_get_rel (previous_l, 1);
980
nextnext = list_try_get_rel (previous_l, 2);
981
res = cubic_interpolation (sub_progress, prev, current, nextv,
983
g_value_set_float (value, res);
986
clutter_interval_compute_value (interval,
990
g_object_ref_sink (interval);
991
g_object_unref (interval);
992
g_object_ref_sink (alpha);
993
g_object_unref (alpha);
1003
/* We're at, or past the end, use the last value */
1004
g_value_copy (&next->value, value);
1011
* clutter_animator_set_timeline:
1012
* @animator: a #ClutterAnimator
1013
* @timeline: a #ClutterTimeline
1015
* Sets an external timeline that will be used for driving the animation
1020
clutter_animator_set_timeline (ClutterAnimator *animator,
1021
ClutterTimeline *timeline)
1023
ClutterAnimatorPrivate *priv;
1025
g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
1027
priv = animator->priv;
1029
if (priv->timeline != NULL)
1031
g_signal_handlers_disconnect_by_func (priv->timeline,
1032
animation_animator_new_frame,
1034
g_signal_handlers_disconnect_by_func (priv->timeline,
1035
animation_animator_started,
1037
g_object_unref (priv->timeline);
1040
priv->timeline = timeline;
1041
if (timeline != NULL)
1043
g_object_ref_sink (priv->timeline);
1045
g_signal_connect (priv->timeline, "new-frame",
1046
G_CALLBACK (animation_animator_new_frame),
1048
g_signal_connect (priv->timeline, "started",
1049
G_CALLBACK (animation_animator_started),
1055
* clutter_animator_get_timeline:
1056
* @animator: a #ClutterAnimator
1058
* Get the timeline hooked up for driving the #ClutterAnimator
1060
* Return value: (transfer none): the #ClutterTimeline that drives the animator
1065
clutter_animator_get_timeline (ClutterAnimator *animator)
1067
g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), NULL);
1068
return animator->priv->timeline;
1072
* clutter_animator_start:
1073
* @animator: a #ClutterAnimator
1075
* Start the ClutterAnimator, this is a thin wrapper that rewinds
1076
* and starts the animators current timeline.
1078
* Return value: the #ClutterTimeline that drives the animator.
1083
clutter_animator_start (ClutterAnimator *animator)
1085
ClutterAnimatorPrivate *priv;
1087
g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), NULL);
1089
priv = animator->priv;
1091
clutter_timeline_rewind (priv->timeline);
1092
clutter_timeline_start (priv->timeline);
1094
return priv->timeline;
1098
* clutter_animator_set_duration:
1099
* @animator: a #ClutterAnimator
1100
* @duration: milliseconds a run of the animator should last.
1102
* Runs the timeline of the #ClutterAnimator with a duration in msecs
1108
clutter_animator_set_duration (ClutterAnimator *animator,
1111
g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
1113
clutter_timeline_set_duration (animator->priv->timeline, duration);
1117
* clutter_animator_get_duration:
1118
* @animator: a #ClutterAnimator
1120
* Retrieves the current duration of an animator
1122
* Return value: the duration of the animation, in milliseconds
1127
clutter_animator_get_duration (ClutterAnimator *animator)
1129
g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), 0);
1131
return clutter_timeline_get_duration (animator->priv->timeline);
1135
* clutter_animator_set:
1136
* @animator: a #ClutterAnimator
1137
* @first_object: a #GObject
1138
* @first_property_name: the property to specify a key for
1139
* @first_mode: the id of the alpha function to use
1140
* @first_progress: at which stage of the animation this value applies; the
1141
* range is a normalized floating point value between 0 and 1
1142
* @VarArgs: the value first_property_name should have for first_object
1143
* at first_progress, followed by more (object, property_name, mode,
1144
* progress, value) tuples, followed by %NULL
1146
* Adds multiple keys to a #ClutterAnimator, specifying the value a given
1147
* property should have at a given progress of the animation. The mode
1148
* specified is the mode used when going to this key from the previous key of
1149
* the @property_name
1151
* If a given (object, property, progress) tuple already exist the mode and
1152
* value will be replaced with the new values.
1157
clutter_animator_set (ClutterAnimator *animator,
1158
gpointer first_object,
1159
const gchar *first_property_name,
1161
gdouble first_progress,
1165
const gchar *property_name;
1170
g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
1172
object = first_object;
1173
property_name = first_property_name;
1175
progress = first_progress;
1177
va_start (args, first_progress);
1179
while (object != NULL)
1182
GObjectClass *klass;
1183
GValue value = { 0, };
1184
gchar *error = NULL;
1186
g_return_if_fail (object);
1187
g_return_if_fail (property_name);
1189
klass = G_OBJECT_GET_CLASS (object);
1190
pspec = g_object_class_find_property (klass, property_name);
1194
g_warning ("Cannot bind property '%s': object of type '%s' "
1195
"do not have this property",
1196
property_name, G_OBJECT_TYPE_NAME (object));
1200
#if GLIB_CHECK_VERSION (2, 23, 2)
1201
G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec),
1205
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1206
G_VALUE_COLLECT (&value, args, 0, &error);
1207
#endif /* GLIB_CHECK_VERSION (2, 23, 2) */
1211
g_warning ("%s: %s", G_STRLOC, error);
1216
clutter_animator_set_key (animator,
1223
object= va_arg (args, GObject *);
1226
property_name = va_arg (args, gchar*);
1229
g_warning ("%s: expected a property name", G_STRLOC);
1232
mode = va_arg (args, guint);
1233
progress = va_arg (args, gdouble);
1241
clutter_animator_set_key_internal (ClutterAnimator *animator,
1242
ClutterAnimatorKey *key)
1244
ClutterAnimatorPrivate *priv = animator->priv;
1247
old_item = g_list_find_custom (priv->score, key,
1248
sort_actor_prop_progress_func);
1250
/* replace the key if we already have a similar one */
1251
if (old_item != NULL)
1253
ClutterAnimatorKey *old_key = old_item->data;
1255
clutter_animator_key_free (old_key);
1257
priv->score = g_list_remove (priv->score, old_key);
1260
priv->score = g_list_insert_sorted (priv->score, key,
1261
sort_actor_prop_progress_func);
1265
* clutter_animator_set_key:
1266
* @animator: a #ClutterAnimator
1267
* @object: a #GObject
1268
* @property_name: the property to specify a key for
1269
* @mode: the id of the alpha function to use
1270
* @progress: the normalized range at which stage of the animation this
1272
* @value: the value property_name should have at progress.
1274
* Sets a single key in the #ClutterAnimator for the @property_name of
1275
* @object at @progress.
1277
* See also: clutter_animator_set()
1279
* Return value: (transfer none): The animator instance
1284
clutter_animator_set_key (ClutterAnimator *animator,
1286
const gchar *property_name,
1289
const GValue *value)
1291
ClutterAnimatorKey *animator_key;
1293
g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), NULL);
1294
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
1295
g_return_val_if_fail (property_name, NULL);
1296
g_return_val_if_fail (value, NULL);
1298
property_name = g_intern_string (property_name);
1300
animator_key = clutter_animator_key_new (animator,
1301
object, property_name,
1305
g_value_init (&animator_key->value, G_VALUE_TYPE (value));
1306
g_value_copy (value, &animator_key->value);
1308
clutter_animator_set_key_internal (animator, animator_key);
1314
* clutter_animator_get_keys:
1315
* @animator: a #ClutterAnimator instance
1316
* @object: (allow-none): a #GObject to search for, or %NULL for all objects
1317
* @property_name: (allow-none): a specific property name to query for,
1318
* or %NULL for all properties
1319
* @progress: a specific progress to search for, or a negative value for all
1322
* Returns a list of pointers to opaque structures with accessor functions
1323
* that describe the keys added to an animator.
1325
* Return value: (transfer container) (element-type ClutterAnimatorKey): a
1326
* list of #ClutterAnimatorKey<!-- -->s; the contents of the list are owned
1327
* by the #ClutterAnimator, but you should free the returned list when done,
1328
* using g_list_free()
1333
clutter_animator_get_keys (ClutterAnimator *animator,
1335
const gchar *property_name,
1341
g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), NULL);
1342
g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
1344
property_name = g_intern_string (property_name);
1346
for (k = animator->priv->score; k; k = k->next)
1348
ClutterAnimatorKey *key = k->data;
1350
if ((object == NULL || (object == key->object)) &&
1351
(property_name == NULL || ((property_name == key->property_name))) &&
1352
(progress < 0 || fabs (progress - key->progress) < PROGRESS_EPSILON))
1354
keys = g_list_prepend (keys, key);
1358
return g_list_reverse (keys);
1362
* clutter_animator_remove_key:
1363
* @animator: a #ClutterAnimator
1364
* @object: (allow-none): a #GObject to search for, or %NULL for all
1365
* @property_name: (allow-none): a specific property name to query for,
1367
* @progress: a specific progress to search for or a negative value
1370
* Removes all keys matching the conditions specificed in the arguments.
1375
clutter_animator_remove_key (ClutterAnimator *animator,
1377
const gchar *property_name,
1380
ClutterAnimatorPrivate *priv;
1383
g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
1384
g_return_if_fail (object == NULL || G_IS_OBJECT (object));
1386
property_name = g_intern_string (property_name);
1388
priv = animator->priv;
1390
for (k = priv->score; k != NULL; k = k->next)
1392
ClutterAnimatorKey *key = k->data;
1394
if ((object == NULL || (object == key->object)) &&
1395
(property_name == NULL || ((property_name == key->property_name))) &&
1396
(progress < 0 || fabs (progress - key->progress) < PROGRESS_EPSILON)
1399
key->is_inert = TRUE;
1401
clutter_animator_key_free (key);
1403
/* FIXME: non performant since we reiterate the list many times */
1404
k = priv->score = g_list_remove (priv->score, key);
1410
GHashTableIter iter;
1411
gpointer key, value;
1414
g_hash_table_iter_init (&iter, priv->properties);
1415
while (g_hash_table_iter_next (&iter, &key, &value))
1417
PropObjectKey *prop_actor_key = key;
1418
if (prop_actor_key->object == object)
1420
g_hash_table_remove (priv->properties, key);
1430
typedef struct _ParseClosure {
1431
ClutterAnimator *animator;
1432
ClutterScript *script;
1439
static ClutterInterpolation
1440
resolve_interpolation (JsonNode *node)
1442
if ((JSON_NODE_TYPE (node) != JSON_NODE_VALUE))
1443
return CLUTTER_INTERPOLATION_LINEAR;
1445
if (json_node_get_value_type (node) == G_TYPE_INT64)
1447
return json_node_get_int (node);
1449
else if (json_node_get_value_type (node) == G_TYPE_STRING)
1451
const gchar *str = json_node_get_string (node);
1455
res = clutter_script_enum_from_string (CLUTTER_TYPE_INTERPOLATION,
1462
return CLUTTER_INTERPOLATION_LINEAR;
1466
parse_animator_property (JsonArray *array,
1471
ParseClosure *clos = data;
1475
const gchar *id, *pname;
1476
GObjectClass *klass;
1478
GSList *valid_keys = NULL;
1480
ClutterInterpolation interpolation = CLUTTER_INTERPOLATION_LINEAR;
1481
gboolean ease_in = FALSE;
1483
if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT)
1485
g_warning ("The 'properties' member of a ClutterAnimator description "
1486
"should be an array of objects, but the element %d of the "
1487
"array is of type '%s'. The element will be ignored.",
1489
json_node_type_name (element));
1493
object = json_node_get_object (element);
1495
if (!json_object_has_member (object, "object") ||
1496
!json_object_has_member (object, "name") ||
1497
!json_object_has_member (object, "keys"))
1499
g_warning ("The property description at index %d is missing one of "
1500
"the mandatory fields: object, name and keys",
1505
id = json_object_get_string_member (object, "object");
1506
gobject = clutter_script_get_object (clos->script, id);
1507
if (gobject == NULL)
1509
g_warning ("No object with id '%s' has been defined.", id);
1513
pname = json_object_get_string_member (object, "name");
1514
klass = G_OBJECT_GET_CLASS (gobject);
1515
pspec = g_object_class_find_property (klass, pname);
1518
g_warning ("The object of type '%s' and name '%s' has no "
1519
"property named '%s'",
1520
G_OBJECT_TYPE_NAME (gobject),
1526
if (json_object_has_member (object, "ease-in"))
1527
ease_in = json_object_get_boolean_member (object, "ease-in");
1529
if (json_object_has_member (object, "interpolation"))
1531
JsonNode *node = json_object_get_member (object, "interpolation");
1533
interpolation = resolve_interpolation (node);
1536
keys = json_object_get_array_member (object, "keys");
1539
g_warning ("The property description at index %d has an invalid "
1540
"key field of type '%s' when an array was expected.",
1542
json_node_type_name (json_object_get_member (object, "keys")));
1546
if (G_IS_VALUE (clos->value))
1547
valid_keys = g_slist_reverse (g_value_get_pointer (clos->value));
1549
g_value_init (clos->value, G_TYPE_POINTER);
1551
for (k = json_array_get_elements (keys);
1555
JsonNode *node = k->data;
1556
JsonArray *key = json_node_get_array (node);
1557
ClutterAnimatorKey *animator_key;
1562
progress = json_array_get_double_element (key, 0);
1563
mode = clutter_script_resolve_animation_mode (json_array_get_element (key, 1));
1565
animator_key = clutter_animator_key_new (clos->animator,
1571
res = clutter_script_parse_node (clos->script,
1572
&(animator_key->value),
1574
json_array_get_element (key, 2),
1578
g_warning ("Unable to parse the key value for the "
1579
"property '%s' (progress: %.2f) at index %d",
1586
animator_key->ease_in = ease_in;
1587
animator_key->interpolation = interpolation;
1589
valid_keys = g_slist_prepend (valid_keys, animator_key);
1592
g_value_set_pointer (clos->value, g_slist_reverse (valid_keys));
1594
clos->result = TRUE;
1598
clutter_animator_parse_custom_node (ClutterScriptable *scriptable,
1599
ClutterScript *script,
1604
ParseClosure parse_closure;
1606
if (strcmp (name, "properties") != 0)
1609
if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
1612
parse_closure.animator = CLUTTER_ANIMATOR (scriptable);
1613
parse_closure.script = script;
1614
parse_closure.value = value;
1615
parse_closure.result = FALSE;
1617
json_array_foreach_element (json_node_get_array (node),
1618
parse_animator_property,
1621
/* we return TRUE if we had at least one key parsed */
1623
return parse_closure.result;
1627
clutter_animator_set_custom_property (ClutterScriptable *scriptable,
1628
ClutterScript *script,
1630
const GValue *value)
1632
if (strcmp (name, "properties") == 0)
1634
ClutterAnimator *animator = CLUTTER_ANIMATOR (scriptable);
1635
GSList *keys = g_value_get_pointer (value);
1638
for (k = keys; k != NULL; k = k->next)
1639
clutter_animator_set_key_internal (animator, k->data);
1641
g_slist_free (keys);
1644
g_object_set_property (G_OBJECT (scriptable), name, value);
1648
clutter_scriptable_init (ClutterScriptableIface *iface)
1650
iface->parse_custom_node = clutter_animator_parse_custom_node;
1651
iface->set_custom_property = clutter_animator_set_custom_property;
1655
clutter_animator_set_property (GObject *gobject,
1657
const GValue *value,
1660
ClutterAnimator *self = CLUTTER_ANIMATOR (gobject);
1665
clutter_animator_set_duration (self, g_value_get_uint (value));
1669
clutter_animator_set_timeline (self, g_value_get_object (value));
1673
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1679
clutter_animator_get_property (GObject *gobject,
1684
ClutterAnimatorPrivate *priv = CLUTTER_ANIMATOR (gobject)->priv;
1689
g_value_set_uint (value, clutter_timeline_get_duration (priv->timeline));
1693
g_value_set_object (value, priv->timeline);
1697
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1703
clutter_animator_class_init (ClutterAnimatorClass *klass)
1705
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1708
g_type_class_add_private (klass, sizeof (ClutterAnimatorPrivate));
1710
gobject_class->set_property = clutter_animator_set_property;
1711
gobject_class->get_property = clutter_animator_get_property;
1712
gobject_class->finalize = clutter_animator_finalize;
1715
* ClutterAnimator:duration:
1717
* The duration of the #ClutterTimeline used by the #ClutterAnimator
1718
* to drive the animation
1722
pspec = g_param_spec_uint ("duration",
1724
"The duration of the animation",
1727
CLUTTER_PARAM_READWRITE);
1728
g_object_class_install_property (gobject_class, PROP_DURATION, pspec);
1731
* ClutterAnimator:timeline:
1733
* The #ClutterTimeline used by the #ClutterAnimator to drive the
1738
pspec = g_param_spec_object ("timeline",
1740
"The timeline of the animation",
1741
CLUTTER_TYPE_TIMELINE,
1742
CLUTTER_PARAM_READWRITE);
1743
g_object_class_install_property (gobject_class, PROP_TIMELINE, pspec);
1747
clutter_animator_init (ClutterAnimator *animator)
1749
ClutterAnimatorPrivate *priv;
1751
animator->priv = priv = CLUTTER_ANIMATOR_GET_PRIVATE (animator);
1753
priv->properties = g_hash_table_new_full (prop_actor_hash,
1755
prop_actor_key_free,
1758
clutter_animator_set_timeline (animator, clutter_timeline_new (2000));
1760
priv->slave_timeline = clutter_timeline_new (10000);
1761
g_object_ref_sink (priv->slave_timeline);
1766
* clutter_animator_property_get_ease_in:
1767
* @animator: a #ClutterAnimatorKey
1768
* @object: a #GObject
1769
* @property_name: the name of a property on object
1771
* Checks if a property value is to be eased into the animation.
1773
* Return value: %TRUE if the property is eased in
1778
clutter_animator_property_get_ease_in (ClutterAnimator *animator,
1780
const gchar *property_name)
1782
ClutterAnimatorKey key, *initial_key;
1785
g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), FALSE);
1786
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
1787
g_return_val_if_fail (property_name, FALSE);
1789
key.object = object;
1790
key.property_name = g_intern_string (property_name);
1791
initial = g_list_find_custom (animator->priv->score, &key,
1792
sort_actor_prop_func);
1793
if (initial != NULL)
1795
initial_key = initial->data;
1797
return initial_key->ease_in;
1804
* clutter_animator_property_set_ease_in:
1805
* @animator: a #ClutterAnimatorKey
1806
* @object: a #GObject
1807
* @property_name: the name of a property on object
1808
* @ease_in: we are going to be easing in this property
1810
* Sets whether a property value is to be eased into the animation.
1815
clutter_animator_property_set_ease_in (ClutterAnimator *animator,
1817
const gchar *property_name,
1820
ClutterAnimatorKey key, *initial_key;
1823
g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
1824
g_return_if_fail (G_IS_OBJECT (object));
1825
g_return_if_fail (property_name);
1827
key.object = object;
1828
key.property_name = g_intern_string (property_name);
1829
initial = g_list_find_custom (animator->priv->score, &key,
1830
sort_actor_prop_func);
1833
initial_key = initial->data;
1834
initial_key->ease_in = ease_in;
1837
g_warning ("The animator has no object of type '%s' with a "
1838
"property named '%s'",
1839
G_OBJECT_TYPE_NAME (object),
1845
* clutter_animator_property_get_interpolation:
1846
* @animator: a #ClutterAnimatorKey
1847
* @object: a #GObject
1848
* @property_name: the name of a property on object
1850
* Get the interpolation used by animator for a property on a particular
1853
* Returns: a ClutterInterpolation value.
1856
ClutterInterpolation
1857
clutter_animator_property_get_interpolation (ClutterAnimator *animator,
1859
const gchar *property_name)
1862
ClutterAnimatorKey key, *initial_key;
1864
g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator),
1865
CLUTTER_INTERPOLATION_LINEAR);
1866
g_return_val_if_fail (G_IS_OBJECT (object),
1867
CLUTTER_INTERPOLATION_LINEAR);
1868
g_return_val_if_fail (property_name,
1869
CLUTTER_INTERPOLATION_LINEAR);
1871
key.object = object;
1872
key.property_name = g_intern_string (property_name);
1873
initial = g_list_find_custom (animator->priv->score, &key,
1874
sort_actor_prop_func);
1877
initial_key = initial->data;
1879
return initial_key->interpolation;
1882
return CLUTTER_INTERPOLATION_LINEAR;
1886
* clutter_animator_property_set_interpolation:
1887
* @animator: a #ClutterAnimatorKey
1888
* @object: a #GObject
1889
* @property_name: the name of a property on object
1890
* @interpolation: the #ClutterInterpolation to use
1892
* Set the interpolation method to use, %CLUTTER_INTERPOLATION_LINEAR causes
1893
* the values to linearly change between the values, and
1894
* %CLUTTER_INTERPOLATION_CUBIC causes the values to smoothly change between
1900
clutter_animator_property_set_interpolation (ClutterAnimator *animator,
1902
const gchar *property_name,
1903
ClutterInterpolation interpolation)
1906
ClutterAnimatorKey key, *initial_key;
1908
g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
1909
g_return_if_fail (G_IS_OBJECT (object));
1910
g_return_if_fail (property_name);
1912
key.object = object;
1913
key.property_name = g_intern_string (property_name);
1914
initial = g_list_find_custom (animator->priv->score, &key,
1915
sort_actor_prop_func);
1918
initial_key = initial->data;
1919
initial_key->interpolation = interpolation;
1924
clutter_animator_key_get_type (void)
1926
static GType our_type = 0;
1929
our_type = g_boxed_type_register_static (I_("ClutterAnimatorKey"),
1930
clutter_animator_key_copy,
1931
clutter_animator_key_free);
1938
* clutter_animator_key_get_object:
1939
* @key: a #ClutterAnimatorKey
1941
* Retrieves the object a key applies to.
1943
* Return value: (transfer none): the object an animator_key exist for.
1948
clutter_animator_key_get_object (const ClutterAnimatorKey *key)
1950
g_return_val_if_fail (key != NULL, NULL);
1956
* clutter_animator_key_get_property_name:
1957
* @key: a #ClutterAnimatorKey
1959
* Retrieves the name of the property a key applies to.
1961
* Return value: the name of the property an animator_key exist for.
1965
G_CONST_RETURN gchar *
1966
clutter_animator_key_get_property_name (const ClutterAnimatorKey *key)
1968
g_return_val_if_fail (key != NULL, NULL);
1970
return key->property_name;
1974
* clutter_animator_key_get_property_type:
1975
* @key: a #ClutterAnimatorKey
1977
* Retrieves the #GType of the property a key applies to
1979
* You can use this type to initialize the #GValue to pass to
1980
* clutter_animator_key_get_value()
1982
* Return value: the #GType of the property
1987
clutter_animator_key_get_property_type (const ClutterAnimatorKey *key)
1989
g_return_val_if_fail (key != NULL, G_TYPE_INVALID);
1991
return G_VALUE_TYPE (&key->value);
1995
* clutter_animator_key_get_mode:
1996
* @key: a #ClutterAnimatorKey
1998
* Retrieves the mode of a #ClutterAnimator key, for the first key of a
1999
* property for an object this represents the whether the animation is
2000
* open ended and or curved for the remainding keys for the property it
2001
* represents the easing mode.
2003
* Return value: the mode of a #ClutterAnimatorKey
2008
clutter_animator_key_get_mode (const ClutterAnimatorKey *key)
2010
g_return_val_if_fail (key != NULL, 0);
2016
* clutter_animator_key_get_progress:
2017
* @key: a #ClutterAnimatorKey
2019
* Retrieves the progress of an clutter_animator_key
2021
* Return value: the progress defined for a #ClutterAnimator key.
2026
clutter_animator_key_get_progress (const ClutterAnimatorKey *key)
2028
g_return_val_if_fail (key != NULL, 0.0);
2030
return key->progress;
2034
* clutter_animator_key_get_value:
2035
* @key: a #ClutterAnimatorKey
2036
* @value: a #GValue initialized with the correct type for the animator key
2038
* Retrieves a copy of the value for a #ClutterAnimatorKey.
2040
* The passed in #GValue needs to be already initialized for the value
2041
* type of the key or to a type that allow transformation from the value
2044
* Use g_value_unset() when done.
2046
* Return value: %TRUE if the passed #GValue was successfully set, and
2052
clutter_animator_key_get_value (const ClutterAnimatorKey *key,
2057
g_return_val_if_fail (key != NULL, FALSE);
2059
gtype = G_VALUE_TYPE (&key->value);
2061
if (g_value_type_compatible (gtype, G_VALUE_TYPE (value)))
2063
g_value_copy (&key->value, value);
2067
if (g_value_type_transformable (gtype, G_VALUE_TYPE (value)))
2069
if (g_value_transform (&key->value, value))