From 270894342e6847fbec2397b6e5709bd2cc2027a2 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 11 Apr 2012 15:53:52 +0100 Subject: [PATCH] Add ClutterKeyframeTransition A simple transition class that interpolates a property between key frames. --- clutter/Makefile.am | 2 + clutter/clutter-keyframe-transition.c | 628 +++++++++++++++++++ clutter/clutter-keyframe-transition.h | 105 ++++ clutter/clutter-types.h | 1 + clutter/clutter.h | 1 + clutter/clutter.symbols | 11 + doc/reference/clutter/clutter-docs.xml.in | 5 +- doc/reference/clutter/clutter-sections.txt | 22 + doc/reference/clutter/clutter.types | 1 + tests/interactive/Makefile.am | 3 +- tests/interactive/test-keyframe-transition.c | 77 +++ 11 files changed, 853 insertions(+), 3 deletions(-) create mode 100644 clutter/clutter-keyframe-transition.c create mode 100644 clutter/clutter-keyframe-transition.h create mode 100644 tests/interactive/test-keyframe-transition.c diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 9130af229..fff949bec 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -93,6 +93,7 @@ source_h = \ $(srcdir)/clutter-image.h \ $(srcdir)/clutter-input-device.h \ $(srcdir)/clutter-interval.h \ + $(srcdir)/clutter-keyframe-transition.h \ $(srcdir)/clutter-keysyms.h \ $(srcdir)/clutter-layout-manager.h \ $(srcdir)/clutter-layout-meta.h \ @@ -171,6 +172,7 @@ source_c = \ $(srcdir)/clutter-image.c \ $(srcdir)/clutter-input-device.c \ $(srcdir)/clutter-interval.c \ + $(srcdir)/clutter-keyframe-transition.c \ $(srcdir)/clutter-keysyms-table.c \ $(srcdir)/clutter-layout-manager.c \ $(srcdir)/clutter-layout-meta.c \ diff --git a/clutter/clutter-keyframe-transition.c b/clutter/clutter-keyframe-transition.c new file mode 100644 index 000000000..0703d7e2e --- /dev/null +++ b/clutter/clutter-keyframe-transition.c @@ -0,0 +1,628 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2012 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Emmanuele Bassi + */ + +/** + * SECTION:clutter-keyframe-transition + * @Title: ClutterKeyframeTransition + * @Short_Description: Keyframe property transition + * + * #ClutterKeyframeTransition allows animating a property by defining + * "key frames": values at a normalized position on the transition + * duration. + * + * The #ClutterKeyframeTransition interpolates the value of the property + * to which it's bound across these key values. + * + * Setting up a #ClutterKeyframeTransition means providing the times, + * values, and easing modes between these key frames, for instance: + * + * |[ + * ClutterTransition *keyframe; + * + * keyframe = clutter_keyframe_transition_new ("opacity"); + * clutter_transition_set_from (keyframe, G_TYPE_UINT, 255); + * clutter_transition_set_to (keyframe, G_TYPE_UINT, 0); + * clutter_keyframe_transition_set_keys (CLUTTER_KEYFRAME_TRANSITION (keyframe), + * G_TYPE_UINT, + * 1, /* number of key frames */ + * 0.5, 128, CLUTTER_EASE_IN_OUT_CUBIC); + * ]| + * + * The example above sets up a keyframe transition for the #ClutterActor:opacity + * property of a #ClutterActor; the transition starts and sets the value of the + * property to fully transparent; between the start of the transition and its mid + * point, it will animate the property to half opacity, using an easy in/easy out + * progress. Once the transition reaches the mid point, it will linearly fade the + * actor out until it reaches the end of the transition. + * + * The #ClutterKeyframeTransition will add an implicit key frame between the last + * and the 1.0 value, to interpolate to the final value of the transition's + * interval. + * + * #ClutterKeyframeTransition is available since Clutter 1.12. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-keyframe-transition.h" + +#include "clutter-debug.h" +#include "clutter-easing.h" +#include "clutter-interval.h" +#include "clutter-private.h" +#include "clutter-timeline.h" + +#include +#include + +typedef struct _KeyFrame +{ + double key; + + double start; + double end; + + ClutterAnimationMode mode; + + ClutterInterval *interval; +} KeyFrame; + +struct _ClutterKeyframeTransitionPrivate +{ + GArray *frames; + + gint current_frame; +}; + +G_DEFINE_TYPE (ClutterKeyframeTransition, + clutter_keyframe_transition, + CLUTTER_TYPE_PROPERTY_TRANSITION) + +static void +key_frame_free (gpointer data) +{ + if (data != NULL) + { + KeyFrame *key = data; + + g_object_unref (key->interval); + } +} + +static int +sort_by_key (gconstpointer a, + gconstpointer b) +{ + const KeyFrame *k_a = a; + const KeyFrame *k_b = b; + + if (fabs (k_a->key - k_b->key) < 0.0001) + return 0; + + if (k_a->key > k_a->key) + return 1; + + return -1; +} + +static inline void +clutter_keyframe_transition_sort_frames (ClutterKeyframeTransition *transition) +{ + if (transition->priv->frames != NULL) + g_array_sort (transition->priv->frames, sort_by_key); +} + +static inline void +clutter_keyframe_transition_init_frames (ClutterKeyframeTransition *transition, + gssize n_key_frames) +{ + ClutterKeyframeTransitionPrivate *priv = transition->priv; + guint i; + + priv->frames = g_array_sized_new (FALSE, FALSE, + sizeof (KeyFrame), + n_key_frames); + g_array_set_clear_func (priv->frames, key_frame_free); + + /* we add an implicit key frame that goes to 1.0, so that the + * user doesn't have to do that an can simply add key frames + * in between 0.0 and 1.0 + */ + for (i = 0; i < n_key_frames + 1; i++) + { + KeyFrame frame; + + if (i == n_key_frames) + frame.key = 1.0; + else + frame.key = 0.0; + + frame.mode = CLUTTER_LINEAR; + frame.interval = NULL; + + g_array_insert_val (priv->frames, i, frame); + } +} + +static inline void +clutter_keyframe_transition_update_frames (ClutterKeyframeTransition *transition) +{ + ClutterKeyframeTransitionPrivate *priv = transition->priv; + guint i; + + if (priv->frames == NULL) + return; + + for (i = 0; i < priv->frames->len; i++) + { + KeyFrame *cur_frame = &g_array_index (priv->frames, KeyFrame, i); + KeyFrame *prev_frame; + + if (i > 0) + prev_frame = &g_array_index (priv->frames, KeyFrame, i - 1); + else + prev_frame = NULL; + + if (prev_frame != NULL) + { + cur_frame->start = prev_frame->key; + + if (prev_frame->interval != NULL) + { + const GValue *value; + + value = clutter_interval_peek_final_value (prev_frame->interval); + + if (cur_frame->interval != NULL) + clutter_interval_set_initial_value (cur_frame->interval, value); + else + { + cur_frame->interval = + clutter_interval_new_with_values (G_VALUE_TYPE (value), value, NULL); + } + } + } + else + cur_frame->start = 0.0; + + cur_frame->end = cur_frame->key; + } +} + +static void +clutter_keyframe_transition_compute_value (ClutterTransition *transition, + ClutterAnimatable *animatable, + ClutterInterval *interval, + gdouble progress) +{ + ClutterKeyframeTransition *self = CLUTTER_KEYFRAME_TRANSITION (transition); + ClutterTimeline *timeline = CLUTTER_TIMELINE (transition); + ClutterKeyframeTransitionPrivate *priv = self->priv; + ClutterTransitionClass *parent_class; + ClutterTimelineDirection direction; + ClutterInterval *real_interval; + gdouble real_progress; + double t, d, p; + KeyFrame *cur_frame = NULL; + + real_interval = interval; + real_progress = progress; + + /* if we don't have any keyframe, we behave like our parent class */ + if (priv->frames == NULL) + goto out; + + direction = clutter_timeline_get_direction (timeline); + + /* we need a normalized linear value */ + t = clutter_timeline_get_elapsed_time (timeline); + d = clutter_timeline_get_duration (timeline); + p = t / d; + + if (priv->current_frame < 0) + { + if (direction == CLUTTER_TIMELINE_FORWARD) + priv->current_frame = 0; + else + priv->current_frame = priv->frames->len - 1; + } + + cur_frame = &g_array_index (priv->frames, KeyFrame, priv->current_frame); + + /* skip to the next key frame, depending on the direction of the timeline */ + if (direction == CLUTTER_TIMELINE_FORWARD) + { + if (p > cur_frame->end) + { + priv->current_frame = MIN (priv->current_frame + 1, + priv->frames->len - 1); + + cur_frame = &g_array_index (priv->frames, KeyFrame, priv->current_frame); + } + } + else + { + if (p < cur_frame->start) + { + priv->current_frame = MAX (priv->current_frame - 1, 0); + + cur_frame = &g_array_index (priv->frames, KeyFrame, priv->current_frame); + } + } + + /* if we are at the boundaries of the transition, use the from and to + * value from the transition + */ + if (priv->current_frame == 0) + { + const GValue *value; + + value = clutter_interval_peek_initial_value (interval); + clutter_interval_set_initial_value (cur_frame->interval, value); + } + else if (priv->current_frame == priv->frames->len - 1) + { + const GValue *value; + + cur_frame->mode = clutter_timeline_get_progress_mode (timeline); + + value = clutter_interval_peek_final_value (interval); + clutter_interval_set_final_value (cur_frame->interval, value); + } + + /* update the interval to be used to interpolate the property */ + real_interval = cur_frame->interval; + + /* normalize the progress */ + real_progress = (p - cur_frame->start) / (cur_frame->end - cur_frame->start); + +#ifdef CLUTTER_ENABLE_DEBUG + if (CLUTTER_HAS_DEBUG (ANIMATION)) + { + char *from, *to; + const GValue *value; + + value = clutter_interval_peek_initial_value (cur_frame->interval); + from = g_strdup_value_contents (value); + + value = clutter_interval_peek_final_value (cur_frame->interval); + to = g_strdup_value_contents (value); + + CLUTTER_NOTE (ANIMATION, + "cur_frame [%d] => { %g, %s, %s %s %s } - " + "progress: %g, sub-progress: %g\n", + priv->current_frame, + cur_frame->key, + clutter_get_easing_name_for_mode (cur_frame->mode), + from, + direction == CLUTTER_TIMELINE_FORWARD ? "->" : "<-", + to, + p, real_progress); + + g_free (from); + g_free (to); + } +#endif /* CLUTTER_ENABLE_DEBUG */ + +out: + parent_class = + CLUTTER_TRANSITION_CLASS (clutter_keyframe_transition_parent_class); + parent_class->compute_value (transition, animatable, real_interval, real_progress); +} + +static void +clutter_keyframe_transition_started (ClutterTimeline *timeline) +{ + ClutterKeyframeTransition *transition; + + transition = CLUTTER_KEYFRAME_TRANSITION (timeline); + + transition->priv->current_frame = -1; + + clutter_keyframe_transition_sort_frames (transition); + clutter_keyframe_transition_update_frames (transition); +} + +static void +clutter_keyframe_transition_completed (ClutterTimeline *timeline) +{ + ClutterKeyframeTransitionPrivate *priv; + + priv = CLUTTER_KEYFRAME_TRANSITION (timeline)->priv; + + priv->current_frame = -1; +} + +static void +clutter_keyframe_transition_finalize (GObject *gobject) +{ + ClutterKeyframeTransitionPrivate *priv; + + priv = CLUTTER_KEYFRAME_TRANSITION (gobject)->priv; + + if (priv->frames != NULL) + g_array_unref (priv->frames); + + G_OBJECT_CLASS (clutter_keyframe_transition_parent_class)->finalize (gobject); +} + +static void +clutter_keyframe_transition_class_init (ClutterKeyframeTransitionClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterTimelineClass *timeline_class = CLUTTER_TIMELINE_CLASS (klass); + ClutterTransitionClass *transition_class = CLUTTER_TRANSITION_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ClutterKeyframeTransitionPrivate)); + + gobject_class->finalize = clutter_keyframe_transition_finalize; + + timeline_class->started = clutter_keyframe_transition_started; + timeline_class->completed = clutter_keyframe_transition_completed; + + transition_class->compute_value = clutter_keyframe_transition_compute_value; +} + +static void +clutter_keyframe_transition_init (ClutterKeyframeTransition *self) +{ + self->priv = + G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_KEYFRAME_TRANSITION, + ClutterKeyframeTransitionPrivate); +} + +/** + * clutter_keyframe_transition_new: + * @property_name: the property to animate + * + * Creates a new #ClutterKeyframeTransition for @property_name. + * + * Return value: (transfer full): the newly allocated + * #ClutterKeyframeTransition instance. Use g_object_unref() when + * done to free its resources. + * + * Since: 1.12 + */ +ClutterTransition * +clutter_keyframe_transition_new (const char *property_name) +{ + return g_object_new (CLUTTER_TYPE_KEYFRAME_TRANSITION, + "property-name", property_name, + NULL); +} + +/** + * clutter_keyframe_transition_set_key_frames: + * @transition: a #ClutterKeyframeTransition + * @n_key_frames: the number of values + * @key_frames: (array length=n_key_frames): an array of keys between 0.0 + * and 1.0, one for each key frame + * + * Sets the keys for each key frame inside @transition. + * + * If @transition does not hold any key frame, @n_key_frames key frames + * will be created; if @transition already has key frames, @key_frames must + * have at least as many elements as the number of key frames. + * + * Since: 1.12 + */ +void +clutter_keyframe_transition_set_key_frames (ClutterKeyframeTransition *transition, + guint n_key_frames, + const double *key_frames) +{ + ClutterKeyframeTransitionPrivate *priv; + guint i; + + g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); + g_return_if_fail (n_key_frames > 0); + g_return_if_fail (key_frames != NULL); + + priv = transition->priv; + + if (priv->frames == NULL) + clutter_keyframe_transition_init_frames (transition, n_key_frames); + else + g_return_if_fail (n_key_frames == priv->frames->len - 1); + + for (i = 0; i < n_key_frames; i++) + { + KeyFrame *frame = &g_array_index (priv->frames, KeyFrame, i); + + frame->key = key_frames[i]; + } +} + +/** + * clutter_keyframe_transition_set_values: + * @transition: a #ClutterKeyframeTransition + * @n_values: the number of values + * @values: (array length=n_values): an array of values, one for each + * key frame + * + * Sets the values for each key frame inside @transition. + * + * If @transition does not hold any key frame, @n_values key frames will + * be created; if @transition already has key frames, @values must have + * at least as many elements as the number of key frames. + * + * Since: 1.12 + */ +void +clutter_keyframe_transition_set_values (ClutterKeyframeTransition *transition, + guint n_values, + const GValue *values) +{ + ClutterKeyframeTransitionPrivate *priv; + guint i; + + g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); + g_return_if_fail (n_values > 0); + g_return_if_fail (values != NULL); + + priv = transition->priv; + + if (priv->frames == NULL) + clutter_keyframe_transition_init_frames (transition, n_values); + else + g_return_if_fail (n_values == priv->frames->len - 1); + + for (i = 0; i < n_values; i++) + { + KeyFrame *frame = &g_array_index (priv->frames, KeyFrame, i); + + clutter_interval_set_final_value (frame->interval, &values[i]); + } +} + +/** + * clutter_keyframe_transition_set_modes: + * @transition: a #ClutterKeyframeTransition + * @n_modes: the number of easing modes + * @modes: (array length=n_modes): an array of easing modes, one for + * each key frame + * + * Sets the easing modes for each key frame inside @transition. + * + * If @transition does not hold any key frame, @n_modes key frames will + * be created; if @transition already has key frames, @modes must have + * at least as many elements as the number of key frames. + * + * Since: 1.12 + */ +void +clutter_keyframe_transition_set_modes (ClutterKeyframeTransition *transition, + guint n_modes, + const ClutterAnimationMode *modes) +{ + ClutterKeyframeTransitionPrivate *priv; + guint i; + + g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); + g_return_if_fail (n_modes > 0); + g_return_if_fail (modes != NULL); + + priv = transition->priv; + + if (priv->frames == NULL) + clutter_keyframe_transition_init_frames (transition, n_modes); + else + g_return_if_fail (n_modes == priv->frames->len - 1); + + for (i = 0; i < n_modes; i++) + { + KeyFrame *frame = &g_array_index (priv->frames, KeyFrame, i); + + frame->mode = modes[i]; + } +} + +/** + * clutter_keyframe_transition_set: (skip) + * @transition: a #ClutterKeyframeTransition + * @gtype: the type of the values to use for the key frames + * @n_key_frames: the number of key frames between the initial + * and final values + * @...: a list of tuples, containing the key frame index, the value + * at the key frame, and the animation mode + * + * Sets the key frames of the @transition. + * + * This variadic arguments function is a convenience for C developers; + * language bindings should use clutter_keyframe_transition_set_keys(), + * clutter_keyframe_transition_set_modes(), and + * clutter_keyframe_transition_set_values() instead. + * + * Since: 1.12 + */ +void +clutter_keyframe_transition_set (ClutterKeyframeTransition *transition, + GType gtype, + guint n_key_frames, + ...) +{ + ClutterKeyframeTransitionPrivate *priv; + va_list args; + guint i; + + g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); + g_return_if_fail (gtype != G_TYPE_INVALID); + g_return_if_fail (n_key_frames > 0); + + priv = transition->priv; + + if (priv->frames == NULL) + clutter_keyframe_transition_init_frames (transition, n_key_frames); + else + g_return_if_fail (n_key_frames == priv->frames->len - 1); + + va_start (args, n_key_frames); + + for (i = 0; i < n_key_frames; i++) + { + KeyFrame *frame = &g_array_index (priv->frames, KeyFrame, i); + GValue value = G_VALUE_INIT; + char *error = NULL; + + frame->key = va_arg (args, double); + + G_VALUE_COLLECT_INIT (&value, gtype, args, 0, &error); + if (error != NULL) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + break; + } + + frame->mode = va_arg (args, ClutterAnimationMode); + + g_clear_object (&frame->interval); + frame->interval = clutter_interval_new_with_values (gtype, NULL, &value); + + g_value_unset (&value); + } + + va_end (args); +} + +/** + * clutter_keyframe_transition_clear: + * @transition: a #ClutterKeyframeTransition + * + * Removes all key frames from @transition. + * + * Since: 1.12 + */ +void +clutter_keyframe_transition_clear (ClutterKeyframeTransition *transition) +{ + g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); + + if (transition->priv->frames != NULL) + { + g_array_unref (transition->priv->frames); + transition->priv->frames = NULL; + } +} diff --git a/clutter/clutter-keyframe-transition.h b/clutter/clutter-keyframe-transition.h new file mode 100644 index 000000000..ce1016dce --- /dev/null +++ b/clutter/clutter-keyframe-transition.h @@ -0,0 +1,105 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2012 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Emmanuele Bassi + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_KEYFRAME_TRANSITION_H__ +#define __CLUTTER_KEYFRAME_TRANSITION_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_KEYFRAME_TRANSITION (clutter_keyframe_transition_get_type ()) +#define CLUTTER_KEYFRAME_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_KEYFRAME_TRANSITION, ClutterKeyframeTransition)) +#define CLUTTER_IS_KEYFRAME_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_KEYFRAME_TRANSITION)) +#define CLUTTER_KEYFRAME_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_KEYFRAME_TRANSITION, ClutterKeyframeTransitionClass)) +#define CLUTTER_IS_KEYFRAME_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_KEYFRAME_TRANSITION)) +#define CLUTTER_KEYFRAME_TRANSITION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_KEYFRAME_TRANSITION, ClutterKeyframeTransitionClass)) + +typedef struct _ClutterKeyframeTransitionPrivate ClutterKeyframeTransitionPrivate; +typedef struct _ClutterKeyframeTransitionClass ClutterKeyframeTransitionClass; + +/** + * ClutterKeyframeTransition: + * + * FIXME + * + * Since: 1.12 + */ +struct _ClutterKeyframeTransition +{ + /*< private >*/ + ClutterPropertyTransition parent_instance; + + ClutterKeyframeTransitionPrivate *priv; +}; + +/** + * ClutterKeyframeTransitionClass: + * + * FIXME + * + * Since: 1.12 + */ +struct _ClutterKeyframeTransitionClass +{ + /*< private >*/ + ClutterPropertyTransitionClass parent_class; + + gpointer _padding[8]; +}; + +CLUTTER_AVAILABLE_IN_1_12 +GType clutter_keyframe_transition_get_type (void) G_GNUC_CONST; + +CLUTTER_AVAILABLE_IN_1_12 +ClutterTransition * clutter_keyframe_transition_new (const char *property_name); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_keyframe_transition_set_key_frames (ClutterKeyframeTransition *transition, + guint n_key_frames, + const double *key_frames); +CLUTTER_AVAILABLE_IN_1_12 +void clutter_keyframe_transition_set_values (ClutterKeyframeTransition *transition, + guint n_values, + const GValue *values); +CLUTTER_AVAILABLE_IN_1_12 +void clutter_keyframe_transition_set_modes (ClutterKeyframeTransition *transition, + guint n_modes, + const ClutterAnimationMode *modes); +CLUTTER_AVAILABLE_IN_1_12 +void clutter_keyframe_transition_set (ClutterKeyframeTransition *transition, + GType gtype, + guint n_key_frames, + ...); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_keyframe_transition_clear (ClutterKeyframeTransition *transition); + +G_END_DECLS + +#endif /* __CLUTTER_KEYFRAME_TRANSITION_H__ */ diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index 21982a152..fdfaafdaf 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -67,6 +67,7 @@ typedef struct _ClutterState ClutterState; typedef struct _ClutterTimeline ClutterTimeline; typedef struct _ClutterTransition ClutterTransition; typedef struct _ClutterPropertyTransition ClutterPropertyTransition; +typedef struct _ClutterKeyframeTransition ClutterKeyframeTransition; typedef struct _ClutterAction ClutterAction; typedef struct _ClutterConstraint ClutterConstraint; diff --git a/clutter/clutter.h b/clutter/clutter.h index 32a3739e7..a42d656fc 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -73,6 +73,7 @@ #include "clutter-image.h" #include "clutter-input-device.h" #include "clutter-interval.h" +#include "clutter-keyframe-transition.h" #include "clutter-keysyms.h" #include "clutter-layout-manager.h" #include "clutter-layout-meta.h" diff --git a/clutter/clutter.symbols b/clutter/clutter.symbols index 75a538850..1c671ea5f 100644 --- a/clutter/clutter.symbols +++ b/clutter/clutter.symbols @@ -799,6 +799,13 @@ clutter_input_device_type_get_type clutter_input_device_ungrab clutter_input_device_update_from_event clutter_input_mode_get_type +clutter_keyframe_transition_clear +clutter_keyframe_transition_get_type +clutter_keyframe_transition_new +clutter_keyframe_transition_set_key_frames +clutter_keyframe_transition_set_modes +clutter_keyframe_transition_set_values +clutter_keyframe_transition_set clutter_keysym_to_unicode clutter_knot_copy clutter_knot_equal @@ -1413,8 +1420,12 @@ clutter_transition_get_interval clutter_transition_get_type clutter_transition_get_remove_on_complete clutter_transition_set_animatable +clutter_transition_set_from_value +clutter_transition_set_from clutter_transition_set_interval clutter_transition_set_remove_on_complete +clutter_transition_set_to_value +clutter_transition_set_to clutter_ungrab_keyboard clutter_ungrab_pointer clutter_ungrab_pointer_for_device diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in index 9327ddfac..556edce81 100644 --- a/doc/reference/clutter/clutter-docs.xml.in +++ b/doc/reference/clutter/clutter-docs.xml.in @@ -150,11 +150,11 @@ Base classes - - + + @@ -260,6 +260,7 @@ + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 44d9fdd4b..226844b92 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -3221,3 +3221,25 @@ clutter_rect_get_type clutter_size_get_type clutter_vertex_get_type + +
+clutter-keyframe-transition +ClutterKeyframeTransition +ClutterKeyframeTransitionClass +clutter_keyframe_transition_new +clutter_keyframe_transition_set +clutter_keyframe_transition_set_key_frames +clutter_keyframe_transition_set_modes +clutter_keyframe_transition_set_values +clutter_keyframe_transition_clear +
+CLUTTER_TYPE_KEYFRAME_TRANSITION +CLUTTER_KEYFRAME_TRANSITION +CLUTTER_KEYFRAME_TRANSITION_CLASS +CLUTTER_IS_KEYFRAME_TRANSITION +CLUTTER_IS_KEYFRAME_TRANSITION_CLASS +CLUTTER_KEYFRAME_TRANSITION_GET_CLASS + +ClutterKeyframeTransitionPrivate +clutter_keyframe_transition_get_type +
diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index d81be1214..f94fc394c 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -43,6 +43,7 @@ clutter_gesture_action_get_type clutter_group_get_type clutter_input_device_get_type clutter_interval_get_type +clutter_keyframe_transition_get_type clutter_layout_manager_get_type clutter_layout_meta_get_type clutter_list_model_get_type diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 2c38e30a9..1dfef93b2 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -58,7 +58,8 @@ UNIT_TESTS = \ test-actor.c \ test-transitions.c \ test-content.c \ - test-canvas.c + test-canvas.c \ + test-keyframe-transition.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-keyframe-transition.c b/tests/interactive/test-keyframe-transition.c new file mode 100644 index 000000000..aeef54885 --- /dev/null +++ b/tests/interactive/test-keyframe-transition.c @@ -0,0 +1,77 @@ +#include +#include +#include + +static const ClutterColor colors[] = { + { 255, 0, 0, 255 }, + { 0, 255, 0, 255 }, + { 0, 0, 255, 255 }, +}; + +#define PADDING (64.0f) +#define SIZE (64.0f) + +G_MODULE_EXPORT const char * +test_keyframe_transition_describe (void) +{ + return "Demonstrate the keyframe transition."; +} + +G_MODULE_EXPORT int +test_keyframe_transition_main (int argc, char *argv[]) +{ + ClutterActor *stage; + int i; + + if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) + return EXIT_FAILURE; + + stage = clutter_stage_new (); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Keyframe Transitions"); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); + + for (i = 0; i < 3; i++) + { + ClutterTransition *transition; + ClutterActor *rect; + float cur_x, cur_y; + float new_x; + + cur_x = PADDING; + cur_y = PADDING + ((SIZE + PADDING) * i); + + new_x = clutter_actor_get_width (stage) - PADDING - SIZE; + + rect = clutter_actor_new (); + + clutter_actor_set_background_color (rect, &colors[i]); + clutter_actor_set_size (rect, SIZE, SIZE); + clutter_actor_set_position (rect, PADDING, cur_y); + clutter_actor_add_child (stage, rect); + + clutter_actor_save_easing_state (rect); + clutter_actor_set_easing_duration (rect, 2000); + clutter_actor_set_easing_mode (rect, CLUTTER_LINEAR); + + transition = clutter_keyframe_transition_new ("x"); + clutter_transition_set_from (transition, G_TYPE_FLOAT, cur_x); + clutter_transition_set_to (transition, G_TYPE_FLOAT, new_x); + + clutter_keyframe_transition_set (CLUTTER_KEYFRAME_TRANSITION (transition), + G_TYPE_FLOAT, 1, + 0.5, new_x / 2.0f, CLUTTER_EASE_OUT_EXPO); + + clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 1); + clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE); + + clutter_actor_add_transition (rect, "horizAnimation", transition); + + clutter_actor_restore_easing_state (rect); + } + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +}