4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2012 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/>.
21 * Author: Emmanuele Bassi <ebassi@linux.intel.com>
25 * SECTION:clutter-transition
26 * @Title: ClutterTransition
27 * @Short_Description: Transition between two values
29 * #ClutterTransition is a subclass of #ClutterTimeline that computes
30 * the interpolation between two values, stored by a #ClutterInterval.
37 #include "clutter-transition.h"
39 #include "clutter-animatable.h"
40 #include "clutter-debug.h"
41 #include "clutter-interval.h"
42 #include "clutter-private.h"
43 #include "clutter-timeline.h"
45 #include <gobject/gvaluecollector.h>
47 struct _ClutterTransitionPrivate
49 ClutterInterval *interval;
50 ClutterAnimatable *animatable;
52 guint remove_on_complete : 1;
61 PROP_REMOVE_ON_COMPLETE,
66 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
68 static GQuark quark_animatable_set = 0;
70 G_DEFINE_ABSTRACT_TYPE (ClutterTransition, clutter_transition, CLUTTER_TYPE_TIMELINE)
73 clutter_transition_attach (ClutterTransition *transition,
74 ClutterAnimatable *animatable)
76 CLUTTER_TRANSITION_GET_CLASS (transition)->attached (transition, animatable);
80 clutter_transition_detach (ClutterTransition *transition,
81 ClutterAnimatable *animatable)
83 CLUTTER_TRANSITION_GET_CLASS (transition)->detached (transition, animatable);
87 clutter_transition_real_compute_value (ClutterTransition *transition,
88 ClutterAnimatable *animatable,
89 ClutterInterval *interval,
95 clutter_transition_real_attached (ClutterTransition *transition,
96 ClutterAnimatable *animatable)
101 clutter_transition_real_detached (ClutterTransition *transition,
102 ClutterAnimatable *animatable)
107 clutter_transition_new_frame (ClutterTimeline *timeline,
108 gint elapsed G_GNUC_UNUSED)
110 ClutterTransition *transition = CLUTTER_TRANSITION (timeline);
111 ClutterTransitionPrivate *priv = transition->priv;
114 if (priv->interval == NULL ||
115 priv->animatable == NULL)
118 progress = clutter_timeline_get_progress (timeline);
120 CLUTTER_TRANSITION_GET_CLASS (timeline)->compute_value (transition,
127 clutter_transition_stopped (ClutterTimeline *timeline,
128 gboolean is_finished)
130 ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (timeline)->priv;
133 priv->animatable != NULL &&
134 priv->remove_on_complete)
136 clutter_transition_detach (CLUTTER_TRANSITION (timeline),
138 g_clear_object (&priv->animatable);
139 g_object_unref (timeline);
144 clutter_transition_set_property (GObject *gobject,
149 ClutterTransition *transition = CLUTTER_TRANSITION (gobject);
154 clutter_transition_set_interval (transition, g_value_get_object (value));
157 case PROP_ANIMATABLE:
158 clutter_transition_set_animatable (transition, g_value_get_object (value));
161 case PROP_REMOVE_ON_COMPLETE:
162 clutter_transition_set_remove_on_complete (transition, g_value_get_boolean (value));
166 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
172 clutter_transition_get_property (GObject *gobject,
177 ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (gobject)->priv;
182 g_value_set_object (value, priv->interval);
185 case PROP_ANIMATABLE:
186 g_value_set_object (value, priv->animatable);
189 case PROP_REMOVE_ON_COMPLETE:
190 g_value_set_boolean (value, priv->remove_on_complete);
194 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
200 clutter_transition_dispose (GObject *gobject)
202 ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (gobject)->priv;
204 if (priv->animatable != NULL)
205 clutter_transition_detach (CLUTTER_TRANSITION (gobject),
208 g_clear_object (&priv->interval);
209 g_clear_object (&priv->animatable);
211 G_OBJECT_CLASS (clutter_transition_parent_class)->dispose (gobject);
215 clutter_transition_class_init (ClutterTransitionClass *klass)
217 ClutterTimelineClass *timeline_class = CLUTTER_TIMELINE_CLASS (klass);
218 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
220 quark_animatable_set =
221 g_quark_from_static_string ("-clutter-transition-animatable-set");
223 g_type_class_add_private (klass, sizeof (ClutterTransitionPrivate));
225 klass->compute_value = clutter_transition_real_compute_value;
226 klass->attached = clutter_transition_real_attached;
227 klass->detached = clutter_transition_real_detached;
229 timeline_class->new_frame = clutter_transition_new_frame;
230 timeline_class->stopped = clutter_transition_stopped;
232 gobject_class->set_property = clutter_transition_set_property;
233 gobject_class->get_property = clutter_transition_get_property;
234 gobject_class->dispose = clutter_transition_dispose;
237 * ClutterTransition:interval:
239 * The #ClutterInterval used to describe the initial and final states
244 obj_props[PROP_INTERVAL] =
245 g_param_spec_object ("interval",
247 P_("The interval of values to transition"),
248 CLUTTER_TYPE_INTERVAL,
250 G_PARAM_STATIC_STRINGS);
253 * ClutterTransition:animatable:
255 * The #ClutterAnimatable instance currently being animated.
259 obj_props[PROP_ANIMATABLE] =
260 g_param_spec_object ("animatable",
262 P_("The animatable object"),
263 CLUTTER_TYPE_ANIMATABLE,
265 G_PARAM_STATIC_STRINGS);
268 * ClutterTransition:remove-on-complete:
270 * Whether the #ClutterTransition should be automatically detached
271 * from the #ClutterTransition:animatable instance whenever the
272 * #ClutterTimeline::completed signal is emitted.
274 * The #ClutterTransition:remove-on-complete property takes into
275 * account the value of the #ClutterTimeline:repeat-count property,
276 * and it only detaches the transition if the transition is not
281 obj_props[PROP_REMOVE_ON_COMPLETE] =
282 g_param_spec_boolean ("remove-on-complete",
283 P_("Remove on Complete"),
284 P_("Detach the transition when completed"),
286 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
288 g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
292 clutter_transition_init (ClutterTransition *self)
294 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_TRANSITION,
295 ClutterTransitionPrivate);
299 * clutter_transition_set_interval:
300 * @transition: a #ClutterTransition
301 * @interval: (allow-none): a #ClutterInterval, or %NULL
303 * Sets the #ClutterTransition:interval property using @interval.
305 * The @transition will acquire a reference on the @interval, sinking
306 * the floating flag on it if necessary.
311 clutter_transition_set_interval (ClutterTransition *transition,
312 ClutterInterval *interval)
314 ClutterTransitionPrivate *priv;
316 g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
317 g_return_if_fail (interval == NULL || CLUTTER_IS_INTERVAL (interval));
319 priv = transition->priv;
321 g_clear_object (&priv->interval);
323 if (interval != NULL)
324 priv->interval = g_object_ref_sink (interval);
326 g_object_notify_by_pspec (G_OBJECT (transition), obj_props[PROP_INTERVAL]);
330 * clutter_transition_get_interval:
331 * @transition: a #ClutterTransition
333 * Retrieves the interval set using clutter_transition_set_interval()
335 * Return value: (transfer none): a #ClutterInterval, or %NULL; the returned
336 * interval is owned by the #ClutterTransition and it should not be freed
342 clutter_transition_get_interval (ClutterTransition *transition)
344 g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), NULL);
346 return transition->priv->interval;
350 * clutter_transition_set_animatable:
351 * @transition: a #ClutterTransition
352 * @animatable: (allow-none): a #ClutterAnimatable, or %NULL
354 * Sets the #ClutterTransition:animatable property.
356 * The @transition will acquire a reference to the @animatable instance,
357 * and will call the #ClutterTransitionClass.attached() virtual function.
359 * If an existing #ClutterAnimatable is attached to @transition, the
360 * reference will be released, and the #ClutterTransitionClass.detached()
361 * virtual function will be called.
366 clutter_transition_set_animatable (ClutterTransition *transition,
367 ClutterAnimatable *animatable)
369 ClutterTransitionPrivate *priv;
371 g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
372 g_return_if_fail (animatable == NULL || CLUTTER_IS_ANIMATABLE (animatable));
374 priv = transition->priv;
376 if (priv->animatable == animatable)
379 if (priv->animatable != NULL)
380 clutter_transition_detach (transition, priv->animatable);
382 g_clear_object (&priv->animatable);
384 if (animatable != NULL)
386 priv->animatable = g_object_ref (animatable);
387 clutter_transition_attach (transition, priv->animatable);
392 * clutter_transition_get_animatable:
393 * @transition: a #ClutterTransition
395 * Retrieves the #ClutterAnimatable set using clutter_transition_set_animatable().
397 * Return value: (transfer none): a #ClutterAnimatable, or %NULL; the returned
398 * animatable is owned by the #ClutterTransition, and it should not be freed
404 clutter_transition_get_animatable (ClutterTransition *transition)
406 g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), NULL);
408 return transition->priv->animatable;
412 * clutter_transition_set_remove_on_complete:
413 * @transition: a #ClutterTransition
414 * @remove_complete: whether to detach @transition when complete
416 * Sets whether @transition should be detached from the #ClutterAnimatable
417 * set using clutter_transition_set_animatable() when the
418 * #ClutterTimeline::completed signal is emitted.
423 clutter_transition_set_remove_on_complete (ClutterTransition *transition,
424 gboolean remove_complete)
426 g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
428 remove_complete = !!remove_complete;
430 if (transition->priv->remove_on_complete == remove_complete)
433 transition->priv->remove_on_complete = remove_complete;
435 g_object_notify_by_pspec (G_OBJECT (transition),
436 obj_props[PROP_REMOVE_ON_COMPLETE]);
440 * clutter_transition_get_remove_on_complete:
441 * @transition: a #ClutterTransition
443 * Retrieves the value of the #ClutterTransition:remove-on-complete property.
445 * Return value: %TRUE if the @transition should be detached when complete,
446 * and %FALSE otherwise
451 clutter_transition_get_remove_on_complete (ClutterTransition *transition)
453 g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), FALSE);
455 return transition->priv->remove_on_complete;
458 typedef void (* IntervalSetFunc) (ClutterInterval *interval,
459 const GValue *value);
462 clutter_transition_set_value (ClutterTransition *transition,
463 IntervalSetFunc interval_set_func,
466 ClutterTransitionPrivate *priv = transition->priv;
469 if (priv->interval == NULL)
471 priv->interval = clutter_interval_new_with_values (G_VALUE_TYPE (value),
474 g_object_ref_sink (priv->interval);
478 interval_type = clutter_interval_get_value_type (priv->interval);
480 if (!g_type_is_a (G_VALUE_TYPE (value), interval_type))
482 if (g_value_type_compatible (G_VALUE_TYPE (value), interval_type))
484 interval_set_func (priv->interval, value);
488 if (g_value_type_transformable (G_VALUE_TYPE (value), interval_type))
490 GValue transform = G_VALUE_INIT;
492 g_value_init (&transform, interval_type);
493 if (g_value_transform (value, &transform))
494 interval_set_func (priv->interval, &transform);
497 g_warning ("%s: Unable to convert a value of type '%s' into "
498 "the value type '%s' of the interval used by the "
501 g_type_name (G_VALUE_TYPE (value)),
502 g_type_name (interval_type));
505 g_value_unset (&transform);
509 interval_set_func (priv->interval, value);
513 * clutter_transition_set_from_value:
514 * @transition: a #ClutterTransition
515 * @value: a #GValue with the initial value of the transition
517 * Sets the initial value of the transition.
519 * This is a convenience function that will either create the
520 * #ClutterInterval used by @transition, or will update it if
521 * the #ClutterTransition:interval is already set.
523 * This function will copy the contents of @value, so it is
524 * safe to call g_value_unset() after it returns.
526 * If @transition already has a #ClutterTransition:interval set,
527 * then @value must hold the same type, or a transformable type,
528 * as the interval's #ClutterInterval:value-type property.
530 * This function is meant to be used by language bindings.
532 * Rename to: clutter_transition_set_from
537 clutter_transition_set_from_value (ClutterTransition *transition,
540 g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
541 g_return_if_fail (G_IS_VALUE (value));
543 clutter_transition_set_value (transition,
544 clutter_interval_set_initial_value,
549 * clutter_transition_set_to_value:
550 * @transition: a #ClutterTransition
551 * @value: a #GValue with the final value of the transition
553 * Sets the final value of the transition.
555 * This is a convenience function that will either create the
556 * #ClutterInterval used by @transition, or will update it if
557 * the #ClutterTransition:interval is already set.
559 * This function will copy the contents of @value, so it is
560 * safe to call g_value_unset() after it returns.
562 * If @transition already has a #ClutterTransition:interval set,
563 * then @value must hold the same type, or a transformable type,
564 * as the interval's #ClutterInterval:value-type property.
566 * This function is meant to be used by language bindings.
568 * Rename to: clutter_transition_set_to
573 clutter_transition_set_to_value (ClutterTransition *transition,
576 g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
577 g_return_if_fail (G_IS_VALUE (value));
579 clutter_transition_set_value (transition,
580 clutter_interval_set_final_value,
585 * clutter_transition_set_from: (skip)
586 * @transition: a #ClutterTransition
587 * @value_type: the type of the value to set
588 * @...: the initial value
590 * Sets the initial value of the transition.
592 * This is a convenience function that will either create the
593 * #ClutterInterval used by @transition, or will update it if
594 * the #ClutterTransition:interval is already set.
596 * If @transition already has a #ClutterTransition:interval set,
597 * then @value must hold the same type, or a transformable type,
598 * as the interval's #ClutterInterval:value-type property.
600 * This is a convenience function for the C API; language bindings
601 * should use clutter_transition_set_from_value() instead.
606 clutter_transition_set_from (ClutterTransition *transition,
610 GValue value = G_VALUE_INIT;
614 g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
615 g_return_if_fail (value_type != G_TYPE_INVALID);
617 va_start (args, value_type);
619 G_VALUE_COLLECT_INIT (&value, value_type, args, 0, &error);
625 g_warning ("%s: %s", G_STRLOC, error);
630 clutter_transition_set_value (transition,
631 clutter_interval_set_initial_value,
634 g_value_unset (&value);
638 * clutter_transition_set_to: (skip)
639 * @transition: a #ClutterTransition
640 * @value_type: the type of the value to set
641 * @...: the final value
643 * Sets the final value of the transition.
645 * This is a convenience function that will either create the
646 * #ClutterInterval used by @transition, or will update it if
647 * the #ClutterTransition:interval is already set.
649 * If @transition already has a #ClutterTransition:interval set,
650 * then @value must hold the same type, or a transformable type,
651 * as the interval's #ClutterInterval:value-type property.
653 * This is a convenience function for the C API; language bindings
654 * should use clutter_transition_set_to_value() instead.
659 clutter_transition_set_to (ClutterTransition *transition,
663 GValue value = G_VALUE_INIT;
667 g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
668 g_return_if_fail (value_type != G_TYPE_INVALID);
670 va_start (args, value_type);
672 G_VALUE_COLLECT_INIT (&value, value_type, args, 0, &error);
678 g_warning ("%s: %s", G_STRLOC, error);
683 clutter_transition_set_value (transition,
684 clutter_interval_set_final_value,
687 g_value_unset (&value);