diff --git a/.gitignore b/.gitignore index 2d149bddf..e07d6bb6e 100644 --- a/.gitignore +++ b/.gitignore @@ -54,60 +54,102 @@ install-sh libtool ltmain.sh missing -stamp-h1 -tests/*.o -tests/test-actors -tests/test-behave -tests/test-boxes -tests/test-depth -tests/test-effects -tests/test-entry -tests/test-events -tests/test-fbo -tests/test-fullscreen -tests/test-grab -tests/test-model -tests/test-multistage -tests/test-offscreen -tests/test-opacity -tests/test-perspective -tests/test-project -tests/test-rotate -tests/test-scale -tests/test-score -tests/test-script -tests/test-shader -tests/test-text -tests/test-textures -tests/test-threads -tests/test-timeline -tests/test-timeline-dup-frames -tests/test-timeline-interpolate -tests/test-timeline-rewind -tests/test-timeline-smoothness -tests/test-unproject -tests/test-viewport -tests/test-cogl-offscreen -tests/test-cogl-primitives -tests/test-cogl-tex-convert -tests/test-cogl-tex-foreign -tests/test-cogl-tex-getset -tests/test-cogl-tex-polygon -tests/test-cogl-tex-tile -tests/test-pixmap -tests/test-stage-read-pixels -tests/test-clip -tests/test-layout -tests/test-random-text -tests/test-texture-quality -tests/test-paint-wrapper -tests/test-devices -tests/test-entry-auto -tests/test-invariants -tests/test-label-cache -tests/test-pick mkinstalldirs +stamp-h1 +/tests/interactive/*.o +/tests/conform/*.o +/tests/micro-bench/*.o +/tests/interactive/test-actors +/tests/interactive/test-behave +/tests/interactive/test-boxes +/tests/interactive/test-depth +/tests/interactive/test-effects +/tests/interactive/test-entry +/tests/interactive/test-events +/tests/interactive/test-fbo +/tests/interactive/test-fullscreen +/tests/interactive/test-grab +/tests/interactive/test-model +/tests/interactive/test-multistage +/tests/interactive/test-offscreen +/tests/interactive/test-opacity +/tests/interactive/test-perspective +/tests/interactive/test-project +/tests/interactive/test-rotate +/tests/interactive/test-scale +/tests/interactive/test-score +/tests/interactive/test-script +/tests/interactive/test-shader +/tests/interactive/test-text +/tests/interactive/test-textures +/tests/interactive/test-threads +/tests/interactive/test-timeline +/tests/interactive/test-timeline-dup-frames +/tests/interactive/test-timeline-interpolate +/tests/interactive/test-timeline-rewind +/tests/interactive/test-timeline-smoothness +/tests/interactive/test-unproject +/tests/interactive/test-viewport +/tests/interactive/test-cogl-offscreen +/tests/interactive/test-cogl-primitives +/tests/interactive/test-cogl-tex-convert +/tests/interactive/test-cogl-tex-foreign +/tests/interactive/test-cogl-tex-getset +/tests/interactive/test-cogl-tex-polygon +/tests/interactive/test-cogl-tex-tile +/tests/interactive/test-pixmap +/tests/interactive/test-stage-read-pixels +/tests/interactive/test-clip +/tests/interactive/test-layout +/tests/interactive/test-random-text +/tests/interactive/test-texture-quality +/tests/interactive/test-paint-wrapper +/tests/interactive/test-devices +/tests/interactive/test-label-cache +/tests/interactive/test-pick +/tests/interactive/test-animation +/tests/interactive/test-easing +/tests/interactive/test-interactive +/tests/interactive/redhand.png +/tests/interactive/test-script.json +/tests/conform/test-conformance +/tests/conform/test-conformance-results.xml +/tests/conform/test-conformance-results.html +/tests/conform/test_entry_append_some +/tests/conform/test_entry_cursor +/tests/conform/test_entry_delete_chars +/tests/conform/test_entry_delete_text +/tests/conform/test_entry_empty +/tests/conform/test_entry_event +/tests/conform/test_entry_insert +/tests/conform/test_entry_prepend_some +/tests/conform/test_entry_set_empty +/tests/conform/test_entry_set_text +/tests/conform/test_entry_utf8_validation +/tests/conform/test_fixed_constants +/tests/conform/test_initial_state +/tests/conform/test_label_cache +/tests/conform/test_mapped +/tests/conform/test_pick +/tests/conform/test_realized +/tests/conform/test_rect_set_color +/tests/conform/test_rect_set_size +/tests/conform/test_show_on_set_parent +/tests/conform/test_timeline +/tests/conform/test_timeline_dup_frames +/tests/conform/test_timeline_interpolate +/tests/conform/test_timeline_rewind +/tests/conform/test_timeline_smoothness +/tests/conform/test_label_opacity +/tests/conform/test_mesh_contiguous +/tests/conform/test_mesh_interleved +/tests/conform/test_mesh_mutability +/tests/conform/test_paint_opacity +/tests/conform/test_rectangle_opacity +/tests/micro-bench/test-text /clutter/x11/clutter-x11-enum-types.[ch] /clutter/x11/stamp-clutter-x11-enum-types.h /po/Makefile.in.in /po/POTFILES +*.swp +*~ diff --git a/ChangeLog b/ChangeLog index 301178dbd..5ee58fe84 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,77 @@ +2008-11-17 Emmanuele Bassi + + Bug 1014 - Clutter Animation API Improvements + + * clutter/Makefile.am: + * clutter/clutter.h: Update the build + + * clutter/clutter-types.h: Add AnimationMode, an enumeration + for easing functions. + + * clutter/clutter-alpha.[ch]: Add the :mode property to + control the function bound to an Alpha instance using an + enumeration value. Also add six new alpha functions: + + - ease-in, ease-out, ease-in-out + - sine-in, sine-out, sine-in-out + + * clutter/clutter-deprecated.h: Deprecate the #defines for + the alpha functions. They will be replaced by entries in the + ClutterAnimationMode. + + * clutter/clutter-interval.[ch]: Add ClutterInterval, an + object for defining, validating and computing an interval + between two values. + + * clutter/clutter-animation.[ch]: Add ClutterAnimation, an + object responsible for animation the properties of a single + actor along an interval of values. ClutterAnimation memory + management is automatic. A simple wrapper method for + ClutterActor is provided: + + clutter_actor_animate() + + which will create, or update, an animation for the passed + actor. + + * clutter/clutter-debug.h: + * clutter/clutter-main.c: Add a new 'animation' debug note. + + * clutter/clutter-script.c: Clean up the alpha functions + whitelist, and add the new functions. + + * doc/reference/clutter/Makefile.am: + * doc/reference/clutter/clutter-sections.txt: Update the + API reference. + + * doc/reference/clutter/clutter-animation.xml: Renamed to + doc/reference/clutter/clutter-animation-tutorial.xml to + avoid clashes with the ClutterAnimation section. + + * doc/reference/clutter/clutter-docs.sgml: Renamed to + doc/reference/clutter/clutter-docs.xml, as it was an XML + file and not a SGML file. + + * tests/Makefile.am: + * tests/interactive/Makefile.am: + * tests/interactive/test-animation.c: + * tests/interactive/test-easing.c: Add two tests for the + new simple animation API and the easing functions. + + * tests/interactive/test-actors.c: + * tests/interactive/test-behave.c: + * tests/interactive/test-depth.c: + * tests/interactive/test-effects.c: + * tests/interactive/test-layout.c: + * tests/interactive/test-multistage.c: + * tests/interactive/test-paint-wrapper.c: + * tests/interactive/test-rotate.c: + * tests/interactive/test-scale.c: + * tests/interactive/test-texture-quality.c: + * tests/interactive/test-threads.c: + * tests/interactive/test-viewport.c: Update interactive tests + to the deprecations and new alpha API. + 2008-11-17 Emmanuele Bassi * clutter/clutter-entry.c: diff --git a/clutter/Makefile.am b/clutter/Makefile.am index f2cca4968..d82ba2ed3 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -47,6 +47,7 @@ BUILT_SOURCES = $(MARSHALFILES) $(ENUMFILES) source_h = \ $(srcdir)/clutter-actor.h \ $(srcdir)/clutter-alpha.h \ + $(srcdir)/clutter-animation.h \ $(srcdir)/clutter-backend.h \ $(srcdir)/clutter-behaviour.h \ $(srcdir)/clutter-behaviour-bspline.h \ @@ -68,6 +69,7 @@ source_h = \ $(srcdir)/clutter-fixed.h \ $(srcdir)/clutter-frame-source.h \ $(srcdir)/clutter-group.h \ + $(srcdir)/clutter-interval.h \ $(srcdir)/clutter-keysyms.h \ $(srcdir)/clutter-label.h \ $(srcdir)/clutter-list-model.h \ @@ -133,6 +135,7 @@ CLEANFILES = $(STAMPFILES) source_c = \ clutter-actor.c \ clutter-alpha.c \ + clutter-animation.c \ clutter-backend.c \ clutter-behaviour.c \ clutter-behaviour-bspline.c \ @@ -155,6 +158,7 @@ source_c = \ clutter-frame-source.c \ clutter-group.c \ clutter-id-pool.c \ + clutter-interval.c \ clutter-label.c \ clutter-list-model.c \ clutter-main.c \ diff --git a/clutter/clutter-alpha.c b/clutter/clutter-alpha.c index d8aa48f46..4c16f06b3 100644 --- a/clutter/clutter-alpha.c +++ b/clutter/clutter-alpha.c @@ -58,10 +58,11 @@ #include #include "clutter-alpha.h" +#include "clutter-debug.h" +#include "clutter-enum-types.h" #include "clutter-main.h" #include "clutter-marshal.h" #include "clutter-private.h" -#include "clutter-debug.h" G_DEFINE_TYPE (ClutterAlpha, clutter_alpha, G_TYPE_INITIALLY_UNOWNED); @@ -74,6 +75,8 @@ struct _ClutterAlphaPrivate guint32 alpha; GClosure *closure; + + ClutterAnimationMode mode; }; enum @@ -81,7 +84,8 @@ enum PROP_0, PROP_TIMELINE, - PROP_ALPHA + PROP_ALPHA, + PROP_MODE }; static void @@ -113,6 +117,9 @@ clutter_alpha_set_property (GObject *object, case PROP_TIMELINE: clutter_alpha_set_timeline (alpha, g_value_get_object (value)); break; + case PROP_MODE: + clutter_alpha_set_mode (alpha, g_value_get_enum (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -139,6 +146,9 @@ clutter_alpha_get_property (GObject *object, case PROP_ALPHA: g_value_set_uint (value, priv->alpha); break; + case PROP_MODE: + g_value_set_enum (value, priv->mode); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -209,6 +219,25 @@ clutter_alpha_class_init (ClutterAlphaClass *klass) CLUTTER_ALPHA_MAX_ALPHA, 0, CLUTTER_PARAM_READABLE)); + /** + * ClutterAlpha:mode: + * + * The progress function as a #ClutterAnimationMode enumeration + * value. If %CLUTTER_CUSTOM_MODE is used then the function set + * using clutter_alpha_set_closure() or clutter_alpha_set_func() + * will be used. + * + * Since: 1.0 + */ + g_object_class_install_property (object_class, + PROP_MODE, + g_param_spec_enum ("mode", + "Mode", + "Progress mode", + CLUTTER_TYPE_ANIMATION_MODE, + CLUTTER_CUSTOM_MODE, + G_PARAM_CONSTRUCT | + CLUTTER_PARAM_READWRITE)); } static void @@ -217,6 +246,8 @@ clutter_alpha_init (ClutterAlpha *self) self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_ALPHA, ClutterAlphaPrivate); + + self->priv->mode = CLUTTER_CUSTOM_MODE; } /** @@ -302,6 +333,9 @@ clutter_alpha_set_closure (ClutterAlpha *alpha, g_closure_set_marshal (closure, marshal); } + + priv->mode = CLUTTER_CUSTOM_MODE; + g_object_notify (G_OBJECT (alpha), "mode"); } /** @@ -370,6 +404,8 @@ clutter_alpha_set_timeline (ClutterAlpha *alpha, G_CALLBACK (timeline_new_frame_cb), alpha); } + + g_object_notify (G_OBJECT (alpha), "timeline"); } /** @@ -444,6 +480,106 @@ clutter_alpha_new_full (ClutterTimeline *timeline, return retval; } +/** + * clutter_alpha_new_for_mode: + * @mode: a #ClutterAnimationMode value + * + * Creates a new #ClutterAlpha using @mode to set the + * progress function using its symbolic name. + * + * Return value: the newly created #ClutterAlpha + * + * Since: 1.0 + */ +ClutterAlpha * +clutter_alpha_new_for_mode (ClutterAnimationMode mode) +{ + return g_object_new (CLUTTER_TYPE_ALPHA, + "mode", mode, + NULL); +} + +/** + * clutter_alpha_get_mode: + * @alpha: a #ClutterAlpha + * + * Retrieves the #ClutterAnimatioMode used by @alpha. + * + * Return value: the animation mode + * + * Since: 1.0 + */ +ClutterAnimationMode +clutter_alpha_get_mode (ClutterAlpha *alpha) +{ + g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), CLUTTER_CUSTOM_MODE); + + return alpha->priv->mode; +} + +/** + * clutter_alpha_set_mode: + * @alpha: a #ClutterAlpha + * @mode: a #ClutterAnimationMode + * + * Sets the progress function of @alpha using the symbolic value + * of @mode, as taken by the #ClutterAnimationMode enumeration + * + * Since: 1.0 + */ +void +clutter_alpha_set_mode (ClutterAlpha *alpha, + ClutterAnimationMode mode) +{ + ClutterAlphaPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ALPHA (alpha)); + + priv = alpha->priv; + + priv->mode = mode; + + switch (priv->mode) + { + case CLUTTER_LINEAR: + clutter_alpha_set_func (alpha, clutter_ramp_inc_func, NULL, NULL); + break; + + case CLUTTER_SINE_IN: + clutter_alpha_set_func (alpha, clutter_sine_in_func, NULL, NULL); + break; + + case CLUTTER_SINE_OUT: + clutter_alpha_set_func (alpha, clutter_sine_out_func, NULL, NULL); + break; + + case CLUTTER_SINE_IN_OUT: + clutter_alpha_set_func (alpha, clutter_sine_inc_func, NULL, NULL); + break; + + case CLUTTER_EASE_IN: + clutter_alpha_set_func (alpha, clutter_ease_in_func, NULL, NULL); + break; + + case CLUTTER_EASE_OUT: + clutter_alpha_set_func (alpha, clutter_ease_out_func, NULL, NULL); + break; + + case CLUTTER_EASE_IN_OUT: + clutter_alpha_set_func (alpha, clutter_ease_in_out_func, NULL, NULL); + break; + + case CLUTTER_CUSTOM_MODE: + break; + + default: + g_assert_not_reached (); + break; + } + + g_object_notify (G_OBJECT (alpha), "mode"); +} + /** * CLUTTER_ALPHA_RAMP_INC: * @@ -789,11 +925,11 @@ guint32 clutter_sine_half_func (ClutterAlpha *alpha, gpointer dummy) { - ClutterTimeline * timeline; - gint frame; - gint n_frames; - ClutterAngle x; - ClutterFixed sine; + ClutterTimeline *timeline; + gint frame; + gint n_frames; + ClutterAngle x; + ClutterFixed sine; timeline = clutter_alpha_get_timeline (alpha); frame = clutter_timeline_get_current_frame (timeline); @@ -803,7 +939,115 @@ clutter_sine_half_func (ClutterAlpha *alpha, sine = cogl_angle_sin (x) * CLUTTER_ALPHA_MAX_ALPHA; - return COGL_FIXED_TO_INT (sine); + return ((guint32) sine) >> COGL_FIXED_Q; +} + +/** + * clutter_sine_in_func: + * @alpha: a #ClutterAlpha + * @dummy: unused argument + * + * Convenience alpha function for (sin(x) + 1) over the + * interval [-pi/2, 0]. + * + * You can use this function as the alpha function for + * clutter_alpha_set_func(). + * + * Return value: an alpha value. + * + * Since: 1.0 + */ +guint32 +clutter_sine_in_func (ClutterAlpha *alpha, + gpointer dummy) +{ + ClutterTimeline *timeline; + gint frame; + gint n_frames; + ClutterAngle x; + ClutterFixed sine; + + timeline = clutter_alpha_get_timeline (alpha); + frame = clutter_timeline_get_current_frame (timeline); + n_frames = clutter_timeline_get_n_frames (timeline); + + /* XXX- if we use 768 we overflow */ + x = 256 * frame / n_frames + 767; + + sine = (cogl_angle_sin (x) + 1) * CLUTTER_ALPHA_MAX_ALPHA; + + return ((guint32) sine) >> COGL_FIXED_Q; +} + +/** + * clutter_sine_in_func: + * @alpha: a #ClutterAlpha + * @dummy: unused argument + * + * Convenience alpha function for sin(x) over the interval [0, pi/2]. + * + * You can use this function as the alpha function for + * clutter_alpha_set_func(). + * + * Return value: an alpha value. + * + * Since: 1.0 + */ +guint32 +clutter_sine_out_func (ClutterAlpha *alpha, + gpointer dummy) +{ + ClutterTimeline *timeline; + gint frame; + gint n_frames; + ClutterAngle x; + ClutterFixed sine; + + timeline = clutter_alpha_get_timeline (alpha); + frame = clutter_timeline_get_current_frame (timeline); + n_frames = clutter_timeline_get_n_frames (timeline); + + x = 256 * frame / n_frames; + + sine = cogl_angle_sin (x) * CLUTTER_ALPHA_MAX_ALPHA; + + return ((guint32) sine) >> COGL_FIXED_Q; +} + +/** + * clutter_sine_in_out_func: + * @alpha: a #ClutterAlpha + * @dummy: unused argument + * + * Convenience alpha function for (sin(x) + 1) / 2 over the + * interval [-pi/2, pi/2]. + * + * You can use this function as the alpha function for + * clutter_alpha_set_func(). + * + * Return value: an alpha value. + * + * Since: 1.0 + */ +guint32 +clutter_sine_in_out_func (ClutterAlpha *alpha, + gpointer dummy) +{ + ClutterTimeline *timeline; + gint frame; + gint n_frames; + ClutterAngle x; + ClutterFixed sine; + + timeline = clutter_alpha_get_timeline (alpha); + frame = clutter_timeline_get_current_frame (timeline); + n_frames = clutter_timeline_get_n_frames (timeline); + + x = -256 * frame / n_frames + 256; + + sine = (cogl_angle_sin (x) + 1) / 2 * CLUTTER_ALPHA_MAX_ALPHA; + + return ((guint32) sine) >> COGL_FIXED_Q; } /** @@ -812,6 +1056,8 @@ clutter_sine_half_func (ClutterAlpha *alpha, * Convenience symbol for clutter_square_func(). * * Since: 0.4 + * + * Deprecated: 1.0: Use clutter_square_func() instead */ /** @@ -1025,3 +1271,131 @@ clutter_exp_dec_func (ClutterAlpha *alpha, return result; } + +static inline gdouble +clutter_cubic_bezier (ClutterAlpha *alpha, + gdouble x_1, + gdouble y_1, + gdouble x_2, + gdouble y_2) +{ + ClutterTimeline *timeline; + gdouble t, b_t, res; + + /* the cubic bezier has a parametric form of: + * + * B(t) = (1 - t)^3 * P_0 + * + 3t * (1 - t)^2 * P_1 + * + 3t^2 * (1 - t) * P_2 + * + 3t^3 * P_3 (with t included in [0, 1]) + * + * the P_0 and P_3 points are set to (0, 0) and (1, 1) respectively, + * and the curve never passes through P_1 and P_2 - with these two + * points merely acting as control points for the curve starting + * from P_0 and ending at P_3. + * + * since the starting point is (0, 0) we can simplify the previous + * parametric form to: + * + * B(t) = 3t * (1 - t)^2 * P_1 + * + 3t^2 * (1 - t) * P_2 + * + 3t^3 * P_3 (with t included in [0, 1]) + * + * and, similarly, since the final point is (1, 1) we can simplify + * it further to: + * + * B(t) = 3t * (1 - t)^2 * P_1 + * + 3t^2 * (1 - t) * P_2 + * + 3t^3 (with t included in [0, 1]) + * + * since an alpha function has only a time parameter and we have two + * coordinates for each point, we pass the time as the first + * coordinate for the point and then we solve the cubic beziér curve + * for the second coordinate at the same point. + */ + + timeline = clutter_alpha_get_timeline (alpha); + t = clutter_timeline_get_progress (timeline); + + b_t = 3 * t * pow (1 - t, 2) * x_1 + + 3 * pow (t, 2) * (1 - t) * x_2 + + 3 * pow (t, 3); + + res = 3 * b_t * pow (1 - b_t, 2) * y_1 + + 3 * pow (b_t, 2) * (1 - b_t) * y_2 + + 3 * pow (b_t, 3); + + return res; +} + +/** + * clutter_ease_in_func: + * @alpha: a #ClutterAlpha + * @dummy: unused argument + * + * Convenience alpha function for a cubic Beziér curve with control + * points at (0.42, 0) and (1, 0). You can use this function as the + * alpha function for clutter_alpha_set_func(). + * + * Return value: an alpha value. + * + * Since: 1.0 + */ +guint32 +clutter_ease_in_func (ClutterAlpha *alpha, + gpointer dummy) +{ + gdouble res; + + res = clutter_cubic_bezier (alpha, 0.42, 0, 1, 0); + + return CLAMP (res * CLUTTER_ALPHA_MAX_ALPHA, 0, CLUTTER_ALPHA_MAX_ALPHA); +} + +/** + * clutter_ease_out_func: + * @alpha: a #ClutterAlpha + * @dummy: unused argument + * + * Convenience alpha function for a cubic Beziér curve with control + * points at (0, 0) and (0.58, 1). You can use this function as the + * alpha function for clutter_alpha_set_func(). + * + * Return value: an alpha value. + * + * Since: 1.0 + */ +guint32 +clutter_ease_out_func (ClutterAlpha *alpha, + gpointer dummy) +{ + gdouble res; + + res = clutter_cubic_bezier (alpha, 0, 0, 0.58, 1); + + return CLAMP (res * CLUTTER_ALPHA_MAX_ALPHA, 0, CLUTTER_ALPHA_MAX_ALPHA); +} + +/** + * clutter_ease_in_out_func: + * @alpha: a #ClutterAlpha + * @dummy: unused argument + * + * Convenience alpha function for a cubic Beziér curve with control + * points at (0.42, 0) and (0.58, 1). You can use this function as + * the alpha function for clutter_alpha_set_func(). + * + * Return value: an alpha value. + * + * Since: 1.0 + */ +guint32 +clutter_ease_in_out_func (ClutterAlpha *alpha, + gpointer dummy) +{ + gdouble res; + + res = clutter_cubic_bezier (alpha, 0.42, 0, 0.58, 1); + + return CLAMP (res * CLUTTER_ALPHA_MAX_ALPHA, 0, CLUTTER_ALPHA_MAX_ALPHA); +} diff --git a/clutter/clutter-alpha.h b/clutter/clutter-alpha.h index 56991a65a..4cd05f999 100644 --- a/clutter/clutter-alpha.h +++ b/clutter/clutter-alpha.h @@ -31,9 +31,9 @@ #ifndef __CLUTTER_ALPHA_H__ #define __CLUTTER_ALPHA_H__ -#include -#include #include +#include +#include G_BEGIN_DECLS @@ -125,42 +125,36 @@ struct _ClutterAlphaClass GType clutter_alpha_get_type (void) G_GNUC_CONST; -ClutterAlpha * clutter_alpha_new (void); -ClutterAlpha * clutter_alpha_new_full (ClutterTimeline *timeline, - ClutterAlphaFunc func, - gpointer data, - GDestroyNotify destroy); -guint32 clutter_alpha_get_alpha (ClutterAlpha *alpha); -void clutter_alpha_set_func (ClutterAlpha *alpha, - ClutterAlphaFunc func, - gpointer data, - GDestroyNotify destroy); -void clutter_alpha_set_closure (ClutterAlpha *alpha, - GClosure *closure); -void clutter_alpha_set_timeline (ClutterAlpha *alpha, - ClutterTimeline *timeline); -ClutterTimeline *clutter_alpha_get_timeline (ClutterAlpha *alpha); +ClutterAlpha * clutter_alpha_new (void); +ClutterAlpha * clutter_alpha_new_full (ClutterTimeline *timeline, + ClutterAlphaFunc func, + gpointer data, + GDestroyNotify destroy); + +ClutterAlpha * clutter_alpha_new_for_mode (ClutterAnimationMode mode); + +guint32 clutter_alpha_get_alpha (ClutterAlpha *alpha); +void clutter_alpha_set_func (ClutterAlpha *alpha, + ClutterAlphaFunc func, + gpointer data, + GDestroyNotify destroy); +void clutter_alpha_set_closure (ClutterAlpha *alpha, + GClosure *closure); +void clutter_alpha_set_timeline (ClutterAlpha *alpha, + ClutterTimeline *timeline); +ClutterTimeline * clutter_alpha_get_timeline (ClutterAlpha *alpha); +void clutter_alpha_set_mode (ClutterAlpha *alpha, + ClutterAnimationMode mode); +ClutterAnimationMode clutter_alpha_get_mode (ClutterAlpha *alpha); /* convenience functions */ -#define CLUTTER_ALPHA_RAMP_INC clutter_ramp_inc_func -#define CLUTTER_ALPHA_RAMP_DEC clutter_ramp_dec_func -#define CLUTTER_ALPHA_RAMP clutter_ramp_func -#define CLUTTER_ALPHA_SINE clutter_sine_func -#define CLUTTER_ALPHA_SINE_INC clutter_sine_inc_func -#define CLUTTER_ALPHA_SINE_DEC clutter_sine_dec_func -#define CLUTTER_ALPHA_SINE_HALF clutter_sine_half_func -#define CLUTTER_ALPHA_SQUARE clutter_square_func -#define CLUTTER_ALPHA_SMOOTHSTEP_INC clutter_smoothstep_inc_func -#define CLUTTER_ALPHA_SMOOTHSTEP_DEC clutter_smoothstep_dec_func -#define CLUTTER_ALPHA_EXP_INC clutter_exp_inc_func -#define CLUTTER_ALPHA_EXP_DEC clutter_exp_dec_func - guint32 clutter_ramp_inc_func (ClutterAlpha *alpha, gpointer dummy); guint32 clutter_ramp_dec_func (ClutterAlpha *alpha, gpointer dummy); guint32 clutter_ramp_func (ClutterAlpha *alpha, gpointer dummy); + guint32 clutter_sine_func (ClutterAlpha *alpha, gpointer dummy); guint32 clutter_sine_inc_func (ClutterAlpha *alpha, @@ -169,17 +163,33 @@ guint32 clutter_sine_dec_func (ClutterAlpha *alpha, gpointer dummy); guint32 clutter_sine_half_func (ClutterAlpha *alpha, gpointer dummy); +guint32 clutter_sine_in_func (ClutterAlpha *alpha, + gpointer dummy); +guint32 clutter_sine_out_func (ClutterAlpha *alpha, + gpointer dummy); +guint32 clutter_sine_in_out_func (ClutterAlpha *alpha, + gpointer dummy); + guint32 clutter_square_func (ClutterAlpha *alpha, gpointer dummy); + guint32 clutter_smoothstep_inc_func (ClutterAlpha *alpha, gpointer dummy); guint32 clutter_smoothstep_dec_func (ClutterAlpha *alpha, gpointer dummy); + guint32 clutter_exp_inc_func (ClutterAlpha *alpha, gpointer dummy); guint32 clutter_exp_dec_func (ClutterAlpha *alpha, gpointer dummy); +guint32 clutter_ease_in_func (ClutterAlpha *alpha, + gpointer dummy); +guint32 clutter_ease_out_func (ClutterAlpha *alpha, + gpointer dummy); +guint32 clutter_ease_in_out_func (ClutterAlpha *alpha, + gpointer dummy); + G_END_DECLS #endif /* __CLUTTER_ALPHA_H__ */ diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c new file mode 100644 index 000000000..83845057b --- /dev/null +++ b/clutter/clutter-animation.c @@ -0,0 +1,1431 @@ +/* + * 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 . + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:clutter-animation + * @short_description: Simple implicit animations + * + * #ClutterAnimation is an object providing simple, implicit animations + * for #ClutterActors. + * + * #ClutterAnimation instances will bind a #GObject property belonging + * to a #ClutterActor to a #ClutterInterval, and will then use a + * #ClutterTimeline to interpolate the property between the initial + * and final values of the interval. + * + * 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 +#include + +#include "clutter-alpha.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_ACTOR, + 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 +{ + ClutterActor *actor; + + GHashTable *properties; + + ClutterAnimationMode 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_actor_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->actor) + { + g_object_weak_unref (G_OBJECT (gobject), + on_animation_weak_notify, + priv->actor); + g_object_set_qdata (G_OBJECT (priv->actor), + quark_actor_animation, + NULL); + g_object_unref (priv->actor); + priv->actor = NULL; + } + + 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_ACTOR: + clutter_animation_set_actor (animation, g_value_get_object (value)); + break; + + case PROP_MODE: + clutter_animation_set_mode (animation, g_value_get_enum (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_ACTOR: + g_value_set_object (value, priv->actor); + break; + + case PROP_MODE: + g_value_set_enum (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_actor_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:actor: + * + * The actor to which the animation applies. + * + * Since: 1.0 + */ + pspec = g_param_spec_object ("actor", + "Actor", + "Actor to which the animation applies", + CLUTTER_TYPE_ACTOR, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ACTOR, pspec); + + /** + * ClutterAnimation:mode: + * + * The animation mode. + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("mode", + "Mode", + "The mode of the animation", + CLUTTER_TYPE_ANIMATION_MODE, + 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 #ClutterActor + * 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->actor)) + { + g_warning ("Cannot bind property `%s': the animation has no " + "actor set. You need to call clutter_animation_set_actor() " + "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->actor); + pspec = g_object_class_find_property (klass, property_name); + if (!pspec) + { + g_warning ("Cannot bind property `%s': actors of type `%s' have " + "no such property", + property_name, + g_type_name (G_OBJECT_TYPE (priv->actor))); + 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->actor); + pspec = g_object_class_find_property (klass, property_name); + if (!pspec) + { + g_warning ("Cannot bind property `%s': actors of type `%s' have " + "no such property", + property_name, + g_type_name (G_OBJECT_TYPE (priv->actor))); + 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; + guint32 alpha_value; + + alpha_value = clutter_alpha_get_alpha (CLUTTER_ALPHA (gobject)); + + g_object_freeze_notify (G_OBJECT (priv->actor)); + + properties = g_hash_table_get_keys (priv->properties); + for (p = properties; p != NULL; p = p->next) + { + const gchar *p_name = p->data; + ClutterInterval *interval; + gdouble factor; + 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)); + + factor = (gdouble) alpha_value / CLUTTER_ALPHA_MAX_ALPHA; + clutter_interval_compute_value (interval, factor, &value); + + g_object_set_property (G_OBJECT (priv->actor), p_name, &value); + + g_value_unset (&value); + } + + g_list_free (properties); + + g_object_thaw_notify (G_OBJECT (priv->actor)); +} + +/* + * 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_actor_animation, NULL); +} + +ClutterAnimation * +clutter_animation_new (void) +{ + return g_object_new (CLUTTER_TYPE_ANIMATION, NULL); +} + +/** + * clutter_animation_set_actor: + * @animation: a #ClutterAnimation + * @actor: a #ClutterActor + * + * Attaches @animation to @actor. The #ClutterAnimation will take a + * reference on @actor. + * + * Since: 1.0 + */ +void +clutter_animation_set_actor (ClutterAnimation *animation, + ClutterActor *actor) +{ + ClutterAnimationPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + priv = animation->priv; + + if (priv->actor) + { + g_object_weak_unref (G_OBJECT (animation), + on_animation_weak_notify, + priv->actor); + g_object_set_qdata (G_OBJECT (priv->actor), + quark_actor_animation, + NULL); + g_object_unref (priv->actor); + } + + priv->actor = g_object_ref (actor); + g_object_weak_ref (G_OBJECT (animation), + on_animation_weak_notify, + priv->actor); + g_object_set_qdata (G_OBJECT (priv->actor), + quark_actor_animation, + animation); + + g_object_notify (G_OBJECT (animation), "actor"); +} + +/** + * clutter_animation_get_actor: + * @animation: a #ClutterAnimation + * + * Retrieves the #ClutterActor attached to @animation. + * + * Return value: a #ClutterActor + * + * Since: 1.0 + */ +ClutterActor * +clutter_animation_get_actor (ClutterAnimation *animation) +{ + g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL); + + return animation->priv->actor; +} + +static inline void +clutter_animation_set_mode_internal (ClutterAnimation *animation) +{ + ClutterAnimationPrivate *priv = animation->priv; + ClutterAlpha *alpha; + + alpha = clutter_animation_get_alpha (animation); + if (alpha) + clutter_alpha_set_mode (alpha, priv->mode); +} + +/** + * clutter_animation_set_mode: + * @animation: a #ClutterAnimation + * @mode: a #ClutterAnimationMode + * + * Sets the animation @mode of @animation. + * + * Since: 1.0 + */ +void +clutter_animation_set_mode (ClutterAnimation *animation, + ClutterAnimationMode mode) +{ + ClutterAnimationPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); + + priv = animation->priv; + + priv->mode = mode; + clutter_animation_set_mode_internal (animation); + + g_object_notify (G_OBJECT (animation), "mode"); +} + +/** + * clutter_animation_get_mode: + * @animation: a #ClutterAnimation + * + * Retrieves the animation mode of @animation. + * + * Return value: the #ClutterAnimationMode for the animation + * + * Since: 1.0 + */ +ClutterAnimationMode +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; + + 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 (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; + } + + 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); + } + + priv->alpha = g_object_ref_sink (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->actor); + + 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': actors of type `%s' do " + "not have this property", + property_name, + g_type_name (G_OBJECT_TYPE (priv->actor))); + 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 (G_OBJECT (priv->actor), + 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 (G_OBJECT (priv->actor), 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_actor_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_actor (animation, 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: a #ClutterAnimationMode value + * @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, + ClutterAnimationMode 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_actor_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_actor (animation, 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: a #ClutterAnimationMode value + * @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. + * + * 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. + * + * 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(). + * + * 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, + ClutterAnimationMode 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_actor_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_actor (animation, 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; +} diff --git a/clutter/clutter-animation.h b/clutter/clutter-animation.h new file mode 100644 index 000000000..115e0a0b5 --- /dev/null +++ b/clutter/clutter-animation.h @@ -0,0 +1,149 @@ +/* + * 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 . + * + * Author: + * Emmanuele Bassi + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_ANIMATION_H__ +#define __CLUTTER_ANIMATION_H__ + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_ANIMATION (clutter_animation_get_type ()) +#define CLUTTER_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ANIMATION, ClutterAnimation)) +#define CLUTTER_IS_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ANIMATION)) +#define CLUTTER_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ANIMATION, ClutterAnimationClass)) +#define CLUTTER_IS_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ANIMATION)) +#define CLUTTER_ANIMATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ANIMATION, ClutterAnimationClass)) + +typedef struct _ClutterAnimation ClutterAnimation; +typedef struct _ClutterAnimationPrivate ClutterAnimationPrivate; +typedef struct _ClutterAnimationClass ClutterAnimationClass; + +/** + * ClutterAnimation: + * + * The #ClutterAnimation structure contains only private data and should + * be accessed using the provided functions. + * + * Since: 1.0 + */ +struct _ClutterAnimation +{ + /*< private >*/ + GInitiallyUnowned parent_instance; + + ClutterAnimationPrivate *priv; +}; + +/** + * ClutterAnimationClass: + * @completed: class handler for the #ClutterAnimation::completed signal + * + * The #ClutterAnimationClass structure contains only private data and + * should be accessed using the provided functions. + * + * Since: 1.0 + */ +struct _ClutterAnimationClass +{ + /*< private >*/ + GInitiallyUnownedClass parent_class; + + /*< public >*/ + void (* completed) (ClutterAnimation *animation); + + /*< private >*/ + /* padding for future expansion */ + void (*_clutter_reserved1) (void); + void (*_clutter_reserved2) (void); + void (*_clutter_reserved3) (void); + void (*_clutter_reserved4) (void); + void (*_clutter_reserved5) (void); + void (*_clutter_reserved6) (void); + void (*_clutter_reserved7) (void); + void (*_clutter_reserved8) (void); +}; + +GType clutter_animation_get_type (void) G_GNUC_CONST; + +ClutterAnimation * clutter_animation_new (void); + +void clutter_animation_set_actor (ClutterAnimation *animation, + ClutterActor *actor); +ClutterActor * clutter_animation_get_actor (ClutterAnimation *animation); +void clutter_animation_set_mode (ClutterAnimation *animation, + ClutterAnimationMode mode); +ClutterAnimationMode clutter_animation_get_mode (ClutterAnimation *animation); +void clutter_animation_set_duration (ClutterAnimation *animation, + gint msecs); +guint clutter_animation_get_duration (ClutterAnimation *animation); +void clutter_animation_set_loop (ClutterAnimation *animation, + gboolean loop); +gboolean clutter_animation_get_loop (ClutterAnimation *animation); +void clutter_animation_set_timeline (ClutterAnimation *animation, + ClutterTimeline *timeline); +ClutterTimeline * clutter_animation_get_timeline (ClutterAnimation *animation); +void clutter_animation_set_alpha (ClutterAnimation *animation, + ClutterAlpha *alpha); +ClutterAlpha * clutter_animation_get_alpha (ClutterAnimation *animation); + +void clutter_animation_bind_property (ClutterAnimation *animation, + const gchar *property_name, + ClutterInterval *interval); +gboolean clutter_animation_has_property (ClutterAnimation *animation, + const gchar *property_name); +void clutter_animation_update_property (ClutterAnimation *animation, + const gchar *property_name, + ClutterInterval *interval); +void clutter_animation_unbind_property (ClutterAnimation *animation, + const gchar *property_name); +ClutterInterval *clutter_animation_get_interval (ClutterAnimation *animation, + const gchar *property_name); + +ClutterAnimation * clutter_actor_animate (ClutterActor *actor, + ClutterAnimationMode mode, + guint duration, + const gchar *first_property_name, + ...) G_GNUC_NULL_TERMINATED; +ClutterAnimation * clutter_actor_animate_with_timeline (ClutterActor *actor, + ClutterAnimationMode mode, + ClutterTimeline *timeline, + const gchar *first_property_name, + ...) G_GNUC_NULL_TERMINATED; +ClutterAnimation * clutter_actor_animate_with_alpha (ClutterActor *actor, + ClutterAlpha *alpha, + const gchar *first_property_name, + ...) G_GNUC_NULL_TERMINATED; + +G_END_DECLS + +#endif /* __CLUTTER_ANIMATION_H__ */ diff --git a/clutter/clutter-debug.h b/clutter/clutter-debug.h index 9233f45f0..697e61288 100644 --- a/clutter/clutter-debug.h +++ b/clutter/clutter-debug.h @@ -20,7 +20,8 @@ typedef enum { CLUTTER_DEBUG_SCHEDULER = 1 << 10, CLUTTER_DEBUG_SCRIPT = 1 << 11, CLUTTER_DEBUG_SHADER = 1 << 12, - CLUTTER_DEBUG_MULTISTAGE = 1 << 13 + CLUTTER_DEBUG_MULTISTAGE = 1 << 13, + CLUTTER_DEBUG_ANIMATION = 1 << 14 } ClutterDebugFlag; #ifdef CLUTTER_ENABLE_DEBUG diff --git a/clutter/clutter-deprecated.h b/clutter/clutter-deprecated.h index bf375bf12..5dfc221b2 100644 --- a/clutter/clutter-deprecated.h +++ b/clutter/clutter-deprecated.h @@ -52,4 +52,17 @@ #define clutter_actor_get_abs_size clutter_actor_get_abs_size_REPLACED_BY_clutter_actor_get_transformed_size #define clutter_actor_get_abs_opacity clutter_actor_get_abs_opacity_REPLACED_BY_clutter_actor_get_paint_opacity +#define CLUTTER_ALPHA_RAMP_INC CLUTTER_ALPHA_RAMP_INC_DEPRECATED_BY_clutter_ramp_inc_func +#define CLUTTER_ALPHA_RAMP_DEC CLUTTER_ALPHA_RAMP_DEC_DEPRECATED_BY_clutter_ramp_dec_func +#define CLUTTER_ALPHA_RAMP CLUTTER_ALPHA_RAMP_DEPRECATED_BY_clutter_ramp_func +#define CLUTTER_ALPHA_SINE_INC CLUTTER_ALPHA_SINE_INC_DEPRECATED_BY_clutter_sine_inc_func +#define CLUTTER_ALPHA_SINE_DEC CLUTTER_ALPHA_SINE_DEC_DEPRECATED_BY_clutter_sine_dec_func +#define CLUTTER_ALPHA_SINE_HALF CLUTTER_ALPHA_SINE_HALF_DEPRECATED_BY_clutter_sine_half_func +#define CLUTTER_ALPHA_SINE CLUTTER_ALPHA_SINE_DEPRECATED_BY_clutter_sine_func +#define CLUTTER_ALPHA_SQUARE CLUTTER_ALPHA_SQUARE_DEPRECATED_BY_clutter_quare_func +#define CLUTTER_ALPHA_SMOOTHSTEP_INC CLUTTER_ALPHA_SMOOTHSTEP_INC_DEPRECATED_BY_clutter_smoothstep_inc_func +#define CLUTTER_ALPHA_SMOOTHSTEP_DEC CLUTTER_ALPHA_SMOOTHSTEP_DEC_DEPRECATED_BY_clutter_smoothstep_dec_func +#define CLUTTER_ALPHA_EXP_INC CLUTTER_ALPHA_EXP_INC_DEPRECATED_BY_clutter_exp_inc_func +#define CLUTTER_ALPHA_EXP_DEC CLUTTER_ALPHA_EXP_DEC_DEPRECATED_BY_clutter_exp_dec_func + #endif /* CLUTTER_DEPRECATED_H */ diff --git a/clutter/clutter-interval.c b/clutter/clutter-interval.c new file mode 100644 index 000000000..f8d6c4aa6 --- /dev/null +++ b/clutter/clutter-interval.c @@ -0,0 +1,836 @@ +/* + * 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 . + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:clutter-interval + * @short_description: An object holding an interval of two values + * + * #ClutterInterval is a simple object that can hold two values + * defining an interval. #ClutterInterval can hold any value that + * can be enclosed inside a #GValue. + * + * Once a #ClutterInterval for a specific #GType has been instantiated + * the #ClutterInterval:value-type property cannot be changed anymore. + * + * #ClutterInterval starts with a floating reference; this means that + * any object taking a reference on a #ClutterInterval instance should + * also take ownership of the interval by using g_object_ref_sink(). + * + * #ClutterInterval is used by #ClutterAnimation to define the + * interval of values that an implicit animation should tween over. + * + * #ClutterInterval can be subclassed to override the validation + * and value computation. + * + * #ClutterInterval is available since Clutter 1.0 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include + +#include "clutter-interval.h" +#include "clutter-units.h" +#include "clutter-fixed.h" + +enum +{ + PROP_0, + + PROP_VALUE_TYPE +}; + +#define CLUTTER_INTERVAL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_INTERVAL, ClutterIntervalPrivate)) + +struct _ClutterIntervalPrivate +{ + GType value_type; + + GValue *values; +}; + +G_DEFINE_TYPE (ClutterInterval, clutter_interval, G_TYPE_INITIALLY_UNOWNED); + +static gboolean +clutter_interval_real_validate (ClutterInterval *interval, + GParamSpec *pspec) +{ + GType pspec_gtype = G_PARAM_SPEC_VALUE_TYPE (pspec); + + /* check the GTypes we provide first */ + if (pspec_gtype == CLUTTER_TYPE_UNIT) + { + ClutterParamSpecUnit *pspec_unit = CLUTTER_PARAM_SPEC_UNIT (pspec); + ClutterFixed a, b; + + a = b = 0; + clutter_interval_get_interval (interval, &a, &b); + if ((a >= pspec_unit->minimum && a <= pspec_unit->maximum) && + (b >= pspec_unit->minimum && b <= pspec_unit->maximum)) + return TRUE; + else + return FALSE; + } + else if (pspec_gtype == CLUTTER_TYPE_FIXED) + { + ClutterParamSpecFixed *pspec_fixed = CLUTTER_PARAM_SPEC_FIXED (pspec); + ClutterFixed a, b; + + a = b = 0; + clutter_interval_get_interval (interval, &a, &b); + if ((a >= pspec_fixed->minimum && a <= pspec_fixed->maximum) && + (b >= pspec_fixed->minimum && b <= pspec_fixed->maximum)) + return TRUE; + else + return FALSE; + } + + /* then check the fundamental types */ + switch (G_TYPE_FUNDAMENTAL (pspec_gtype)) + { + case G_TYPE_INT: + { + GParamSpecInt *pspec_int = G_PARAM_SPEC_INT (pspec); + gint a, b; + + a = b = 0; + clutter_interval_get_interval (interval, &a, &b); + if ((a >= pspec_int->minimum && a <= pspec_int->maximum) && + (b >= pspec_int->minimum && b <= pspec_int->maximum)) + return TRUE; + else + return FALSE; + } + break; + + case G_TYPE_UINT: + { + GParamSpecUInt *pspec_uint = G_PARAM_SPEC_UINT (pspec); + guint a, b; + + a = b = 0; + clutter_interval_get_interval (interval, &a, &b); + if ((a >= pspec_uint->minimum && a <= pspec_uint->maximum) && + (b >= pspec_uint->minimum && b <= pspec_uint->maximum)) + return TRUE; + else + return FALSE; + } + break; + + case G_TYPE_UCHAR: + { + GParamSpecUChar *pspec_uchar = G_PARAM_SPEC_UCHAR (pspec); + guchar a, b; + + a = b = 0; + clutter_interval_get_interval (interval, &a, &b); + if ((a >= pspec_uchar->minimum && a <= pspec_uchar->maximum) && + (b >= pspec_uchar->minimum && b <= pspec_uchar->maximum)) + return TRUE; + else + return FALSE; + } + break; + + case G_TYPE_BOOLEAN: + return TRUE; + + default: + break; + } + + return TRUE; +} + +static void +clutter_interval_real_compute_value (ClutterInterval *interval, + gdouble factor, + GValue *value) +{ + GValue *initial, *final; + GType value_type; + + initial = clutter_interval_peek_initial_value (interval); + final = clutter_interval_peek_final_value (interval); + + value_type = clutter_interval_get_value_type (interval); + + switch (G_TYPE_FUNDAMENTAL (value_type)) + { + case G_TYPE_INT: + { + gint ia, ib, res; + + ia = g_value_get_int (initial); + ib = g_value_get_int (final); + + res = (factor * (ib - ia)) + ia; + + g_value_set_int (value, res); + } + break; + + case G_TYPE_UINT: + { + guint ia, ib, res; + + ia = g_value_get_uint (initial); + ib = g_value_get_uint (final); + + res = (factor * (ib - ia)) + ia; + + g_value_set_uint (value, res); + } + break; + + case G_TYPE_UCHAR: + { + guchar ia, ib, res; + + ia = g_value_get_uchar (initial); + ib = g_value_get_uchar (final); + + res = (factor * (ib - ia)) + ia; + + g_value_set_uchar (value, res); + } + break; + + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + { + gdouble ia, ib, res; + + ia = g_value_get_double (initial); + ib = g_value_get_double (final); + + res = (factor * (ib - ia)) + ia; + + if (value_type == G_TYPE_DOUBLE) + g_value_set_double (value, res); + else + g_value_set_float (value, res); + } + break; + + case G_TYPE_BOOLEAN: + if (COGL_FIXED_FROM_FLOAT (factor) > COGL_FIXED_0_5) + g_value_set_boolean (value, TRUE); + else + g_value_set_boolean (value, FALSE); + break; + + default: + break; + } +} + +static void +clutter_interval_finalize (GObject *gobject) +{ + ClutterIntervalPrivate *priv = CLUTTER_INTERVAL (gobject)->priv; + + g_value_unset (&priv->values[0]); + g_value_unset (&priv->values[1]); + + g_free (priv->values); +} + +static void +clutter_interval_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterIntervalPrivate *priv = CLUTTER_INTERVAL_GET_PRIVATE (gobject); + + switch (prop_id) + { + case PROP_VALUE_TYPE: + priv->value_type = g_value_get_gtype (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_interval_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterIntervalPrivate *priv = CLUTTER_INTERVAL_GET_PRIVATE (gobject); + + switch (prop_id) + { + case PROP_VALUE_TYPE: + g_value_set_gtype (value, priv->value_type); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_interval_class_init (ClutterIntervalClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (ClutterIntervalPrivate)); + + klass->validate = clutter_interval_real_validate; + klass->compute_value = clutter_interval_real_compute_value; + + gobject_class->set_property = clutter_interval_set_property, + gobject_class->get_property = clutter_interval_get_property; + gobject_class->finalize = clutter_interval_finalize; + + /** + * ClutterInterval:value-type: + * + * The type of the values in the interval. + * + * Since: 1.0 + */ + pspec = g_param_spec_gtype ("value-type", + "Value Type", + "The type of the values in the interval", + G_TYPE_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, PROP_VALUE_TYPE, pspec); +} + +static void +clutter_interval_init (ClutterInterval *self) +{ + ClutterIntervalPrivate *priv; + + self->priv = priv = CLUTTER_INTERVAL_GET_PRIVATE (self); + + priv->value_type = G_TYPE_INVALID; + priv->values = g_malloc0 (sizeof (GValue) * 2); +} + +static void +clutter_interval_set_interval_valist (ClutterInterval *interval, + va_list var_args) +{ + GType gtype = interval->priv->value_type; + GValue value = { 0, }; + gchar *error; + + /* initial value */ + g_value_init (&value, gtype); + G_VALUE_COLLECT (&value, var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + + /* we leak the value here as it might not be in a valid state + * given the error and calling g_value_unset() might lead to + * undefined behaviour + */ + g_free (error); + return; + } + + clutter_interval_set_initial_value (interval, &value); + g_value_unset (&value); + + /* final value */ + g_value_init (&value, gtype); + G_VALUE_COLLECT (&value, var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + + /* see above */ + g_free (error); + return; + } + + clutter_interval_set_final_value (interval, &value); + g_value_unset (&value); +} + +static void +clutter_interval_get_interval_valist (ClutterInterval *interval, + va_list var_args) +{ + GType gtype = interval->priv->value_type; + GValue value = { 0, }; + gchar *error; + + /* initial value */ + g_value_init (&value, gtype); + clutter_interval_get_initial_value (interval, &value); + G_VALUE_LCOPY (&value, var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + g_value_unset (&value); + return; + } + + g_value_unset (&value); + + /* final value */ + g_value_init (&value, gtype); + clutter_interval_get_final_value (interval, &value); + G_VALUE_LCOPY (&value, var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + g_value_unset (&value); + return; + } + + g_value_unset (&value); +} + +/** + * clutter_interval_new: + * @gtype: the type of the values in the interval + * @Varargs: the initial value and the final value of the interval + * + * Creates a new #ClutterInterval holding values of type @gtype. + * + * This function avoids using a #GValue for the initial and final values + * of the interval: + * + * |[ + * interval = clutter_interval_new (G_TYPE_FLOAT, 0.0, 1.0); + * interval = clutter_interval_new (G_TYPE_BOOLEAN, FALSE, TRUE); + * interval = clutter_interval_new (G_TYPE_INT, 0, 360); + * ]| + * + * Return value: the newly created #ClutterInterval + * + * Since: 1.0 + */ +ClutterInterval * +clutter_interval_new (GType gtype, + ...) +{ + ClutterInterval *retval; + va_list args; + + g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL); + + retval = g_object_new (CLUTTER_TYPE_INTERVAL, "value-type", gtype, NULL); + + va_start (args, gtype); + clutter_interval_set_interval_valist (retval, args); + va_end (args); + + return retval; +} + +/** + * clutter_interval_new_with_values: + * @gtype: the type of the values in the interval + * @initial: a #GValue holding the initial value of the interval + * @final: a #GValue holding the final value of the interval + * + * Creates a new #ClutterInterval of type @gtype, between @initial + * and @final. + * + * This function is useful for language bindings. + * + * Return value: the newly created #ClutterInterval + * + * Since: 1.0 + */ +ClutterInterval * +clutter_interval_new_with_values (GType gtype, + const GValue *initial, + const GValue *final) +{ + ClutterInterval *retval; + + g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL); + g_return_val_if_fail (initial != NULL, NULL); + g_return_val_if_fail (final != NULL, NULL); + g_return_val_if_fail (G_VALUE_TYPE (initial) == gtype, NULL); + g_return_val_if_fail (G_VALUE_TYPE (final) == gtype, NULL); + + retval = g_object_new (CLUTTER_TYPE_INTERVAL, "value-type", gtype, NULL); + + clutter_interval_set_initial_value (retval, initial); + clutter_interval_set_final_value (retval, final); + + return retval; +} + +/** + * clutter_interval_clone: + * @interval: a #ClutterInterval + * + * Creates a copy of @interval. + * + * Return value: the newly created #ClutterInterval + * + * Since: 1.0 + */ +ClutterInterval * +clutter_interval_clone (ClutterInterval *interval) +{ + ClutterInterval *retval; + GType gtype; + GValue *tmp; + + g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), NULL); + g_return_val_if_fail (interval->priv->value_type != G_TYPE_INVALID, NULL); + + gtype = interval->priv->value_type; + retval = g_object_new (CLUTTER_TYPE_INTERVAL, "value-type", gtype, NULL); + + tmp = clutter_interval_peek_initial_value (interval); + clutter_interval_set_initial_value (retval, tmp); + + tmp = clutter_interval_peek_final_value (interval); + clutter_interval_set_final_value (retval, tmp); + + return retval; +} + +/** + * clutter_interval_get_value_type: + * @interval: a #ClutterInterval + * + * Retrieves the #GType of the values inside @interval. + * + * Return value: the type of the value, or G_TYPE_INVALID + * + * Since: 1.0 + */ +GType +clutter_interval_get_value_type (ClutterInterval *interval) +{ + g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), G_TYPE_INVALID); + + return interval->priv->value_type; +} + +static inline void +clutter_interval_set_value_internal (ClutterInterval *interval, + gint index_, + const GValue *value) +{ + ClutterIntervalPrivate *priv = interval->priv; + + if (G_IS_VALUE (&priv->values[index_])) + g_value_unset (&priv->values[index_]); + + g_value_init (&priv->values[index_], priv->value_type); + g_value_copy (value, &priv->values[index_]); +} + +static inline void +clutter_interval_get_value_internal (ClutterInterval *interval, + gint index_, + GValue *value) +{ + ClutterIntervalPrivate *priv = interval->priv; + + g_value_copy (&priv->values[index_], value); +} + +/** + * clutter_interval_set_initial_value: + * @interval: a #ClutterInterval + * @value: a #GValue + * + * Sets the initial value of @interval to @value. The value is copied + * inside the #ClutterInterval. + * + * Since: 1.0 + */ +void +clutter_interval_set_initial_value (ClutterInterval *interval, + const GValue *value) +{ + ClutterIntervalPrivate *priv; + + g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); + g_return_if_fail (value != NULL); + + priv = interval->priv; + + g_return_if_fail (G_VALUE_TYPE (value) == priv->value_type); + + clutter_interval_set_value_internal (interval, 0, value); +} + +/** + * clutter_interval_get_initial_value: + * @interval: a #ClutterInterval + * @value: a #GValue + * + * Retrieves the initial value of @interval and copies + * it into @value. + * + * The passed #GValue must be initialized to the value held by + * the #ClutterInterval. + * + * Since: 1.0 + */ +void +clutter_interval_get_initial_value (ClutterInterval *interval, + GValue *value) +{ + ClutterIntervalPrivate *priv; + + g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); + g_return_if_fail (value != NULL); + + priv = interval->priv; + + clutter_interval_get_value_internal (interval, 0, value); +} + +/** + * clutter_interval_peek_initial_value: + * @interval: a #ClutterInterval + * + * Gets the pointer to the initial value of @interval + * + * Return value: the initial value of the interval. The value + * is owned by the #ClutterInterval and it should not be + * modified or freed + * + * Since: 1.0 + */ +GValue * +clutter_interval_peek_initial_value (ClutterInterval *interval) +{ + g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), NULL); + + return interval->priv->values; +} + +/** + * clutter_interval_set_final_value: + * @interval: a #ClutterInterval + * @value: a #GValue + * + * Sets the final value of @interval to @value. The value is + * copied inside the #ClutterInterval. + * + * Since: 1.0 + */ +void +clutter_interval_set_final_value (ClutterInterval *interval, + const GValue *value) +{ + ClutterIntervalPrivate *priv; + + g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); + g_return_if_fail (value != NULL); + + priv = interval->priv; + + g_return_if_fail (G_VALUE_TYPE (value) == priv->value_type); + + clutter_interval_set_value_internal (interval, 1, value); +} + +/** + * clutter_interval_get_final_value: + * @interval: a #ClutterInterval + * @value: a #GValue + * + * Retrieves the final value of @interval and copies + * it into @value. + * + * The passed #GValue must be initialized to the value held by + * the #ClutterInterval. + * + * Since: 1.0 + */ +void +clutter_interval_get_final_value (ClutterInterval *interval, + GValue *value) +{ + ClutterIntervalPrivate *priv; + + g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); + g_return_if_fail (value != NULL); + + priv = interval->priv; + + clutter_interval_get_value_internal (interval, 1, value); +} + +/** + * clutter_interval_peek_final_value: + * @interval: a #ClutterInterval + * + * Gets the pointer to the final value of @interval + * + * Return value: the final value of the interval. The value + * is owned by the #ClutterInterval and it should not be + * modified or freed + * + * Since: 1.0 + */ +GValue * +clutter_interval_peek_final_value (ClutterInterval *interval) +{ + g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), NULL); + + return interval->priv->values + 1; +} + +/** + * clutter_interval_set_interval: + * @interval: a #ClutterInterval + * @Varargs: the initial and final values of the interval + * + * Variable arguments wrapper for clutter_interval_set_initial_value() + * and clutter_interval_set_final_value() that avoids using the + * #GValue arguments: + * + * |[ + * clutter_interval_set_interval (interval, 0, 50); + * clutter_interval_set_interval (interval, 1.0, 0.0); + * clutter_interval_set_interval (interval, FALSE, TRUE); + * ]| + * + * This function is meant for the convenience of the C API; bindings + * should reimplement this function using the #GValue-based API. + * + * Since: 1.0 + */ +void +clutter_interval_set_interval (ClutterInterval *interval, + ...) +{ + va_list args; + + g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); + g_return_if_fail (interval->priv->value_type != G_TYPE_INVALID); + + va_start (args, interval); + clutter_interval_set_interval_valist (interval, args); + va_end (args); +} + +/** + * clutter_interval_get_interval: + * @interval: a #ClutterInterval + * @Varargs: return locations for the initial and final values of + * the interval + * + * Variable arguments wrapper for clutter_interval_get_initial_value() + * and clutter_interval_get_final_value() that avoids using the + * #GValue arguments: + * + * |[ + * gint a = 0, b = 0; + * clutter_interval_get_interval (interval, &a, &b); + * ]| + * + * This function is meant for the convenience of the C API; bindings + * should reimplement this function using the #GValue-based API. + * + * Since: 1.0 + */ +void +clutter_interval_get_interval (ClutterInterval *interval, + ...) +{ + va_list args; + + g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); + g_return_if_fail (interval->priv->value_type != G_TYPE_INVALID); + + va_start (args, interval); + clutter_interval_get_interval_valist (interval, args); + va_end (args); +} + +/** + * clutter_interval_validate: + * @interval: a #ClutterInterval + * @pspec: a #GParamSpec + * + * Validates the initial and final values of @interval against + * a #GParamSpec. + * + * Return value: %TRUE if the #ClutterInterval is valid, %FALSE otherwise + * + * Since: 1.0 + */ +gboolean +clutter_interval_validate (ClutterInterval *interval, + GParamSpec *pspec) +{ + g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), FALSE); + g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE); + + return CLUTTER_INTERVAL_GET_CLASS (interval)->validate (interval, pspec); +} + +/** + * clutter_interval_compute_value: + * @interval: a #ClutterInterval + * @factor: the progress factor, between 0 and 1 + * @value: return location for an initialized #GValue + * + * Computes the value between the @interval boundaries given the + * progress @factor and puts it into @value. + * + * Since: 1.0 + */ +void +clutter_interval_compute_value (ClutterInterval *interval, + gdouble factor, + GValue *value) +{ + g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); + g_return_if_fail (value != NULL); + + factor = CLAMP (factor, 0.0, 1.0); + + CLUTTER_INTERVAL_GET_CLASS (interval)->compute_value (interval, + factor, + value); +} diff --git a/clutter/clutter-interval.h b/clutter/clutter-interval.h new file mode 100644 index 000000000..56d155f28 --- /dev/null +++ b/clutter/clutter-interval.h @@ -0,0 +1,131 @@ +/* + * 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 . + * + * Author: + * Emmanuele Bassi + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_INTERVAL_H__ +#define __CLUTTER_INTERVAL_H__ + +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_INTERVAL (clutter_interval_get_type ()) +#define CLUTTER_INTERVAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INTERVAL, ClutterInterval)) +#define CLUTTER_IS_INTERVAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INTERVAL)) +#define CLUTTER_INTERVAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_INTERVAL, ClutterIntervalClass)) +#define CLUTTER_IS_INTERVAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_INTERVAL)) +#define CLUTTER_INTERVAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_INTERVAL, ClutterIntervalClass)) + +typedef struct _ClutterInterval ClutterInterval; +typedef struct _ClutterIntervalPrivate ClutterIntervalPrivate; +typedef struct _ClutterIntervalClass ClutterIntervalClass; + +/** + * ClutterInterval: + * + * The #ClutterInterval structure contains only private data and should + * be accessed using the provided functions. + * + * Since: 1.0 + */ +struct _ClutterInterval +{ + /*< private >*/ + GInitiallyUnowned parent_instance; + + ClutterIntervalPrivate *priv; +}; + +/** + * ClutterIntervalClass: + * @validate: virtual function for validating an interval + * using a #GParamSpec + * @compute_value: virtual function for computing the value + * inside an interval using an adimensional factor between 0 and 1 + * + * The #ClutterIntervalClass contains only private data. + * + * Since: 1.0 + */ +struct _ClutterIntervalClass +{ + /*< private >*/ + GInitiallyUnownedClass parent_class; + + /*< public >*/ + gboolean (* validate) (ClutterInterval *interval, + GParamSpec *pspec); + void (* compute_value) (ClutterInterval *interval, + gdouble factor, + GValue *value); + + /*< private >*/ + /* padding for future expansion */ + void (*_clutter_reserved1) (void); + void (*_clutter_reserved2) (void); + void (*_clutter_reserved3) (void); + void (*_clutter_reserved4) (void); + void (*_clutter_reserved5) (void); + void (*_clutter_reserved6) (void); +}; + +GType clutter_interval_get_type (void) G_GNUC_CONST; + +ClutterInterval *clutter_interval_new (GType gtype, + ...); +ClutterInterval *clutter_interval_new_with_values (GType gtype, + const GValue *initial, + const GValue *final); + +ClutterInterval *clutter_interval_clone (ClutterInterval *interval); + +GType clutter_interval_get_value_type (ClutterInterval *interval); +void clutter_interval_set_initial_value (ClutterInterval *interval, + const GValue *value); +void clutter_interval_get_initial_value (ClutterInterval *interval, + GValue *value); +GValue * clutter_interval_peek_initial_value (ClutterInterval *interval); +void clutter_interval_set_final_value (ClutterInterval *interval, + const GValue *value); +void clutter_interval_get_final_value (ClutterInterval *interval, + GValue *value); +GValue * clutter_interval_peek_final_value (ClutterInterval *interval); + +void clutter_interval_set_interval (ClutterInterval *interval, + ...); +void clutter_interval_get_interval (ClutterInterval *interval, + ...); + +gboolean clutter_interval_validate (ClutterInterval *interval, + GParamSpec *pspec); +void clutter_interval_compute_value (ClutterInterval *interval, + gdouble factor, + GValue *value); + +G_END_DECLS + +#endif /* __CLUTTER_INTERVAL_H__ */ diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index f68e89020..f77f3313e 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -87,6 +87,7 @@ static const GDebugKey clutter_debug_keys[] = { { "script", CLUTTER_DEBUG_SCRIPT }, { "shader", CLUTTER_DEBUG_SHADER }, { "multistage", CLUTTER_DEBUG_MULTISTAGE }, + { "animation", CLUTTER_DEBUG_ANIMATION } }; #endif /* CLUTTER_ENABLE_DEBUG */ diff --git a/clutter/clutter-script.c b/clutter/clutter-script.c index 35915c6f1..7cc00ed15 100644 --- a/clutter/clutter-script.c +++ b/clutter/clutter-script.c @@ -526,66 +526,26 @@ static const struct const gchar *short_name; ClutterAlphaFunc symbol; } clutter_alphas[] = { - { - "clutter_ramp_inc_func", - "ramp-inc", - CLUTTER_ALPHA_RAMP_INC - }, - { - "clutter_ramp_dec_func", - "ramp-dec", - CLUTTER_ALPHA_RAMP_DEC - }, - { - "clutter_ramp_func", - "ramp", - CLUTTER_ALPHA_RAMP - }, - { - "clutter_sine_inc_func", - "sine-inc", - CLUTTER_ALPHA_SINE_INC - }, - { - "clutter_sine_dec_func", - "sine-dec", - CLUTTER_ALPHA_SINE_DEC - }, - { - "clutter_sine_half_func", - "sine-half", - CLUTTER_ALPHA_SINE_HALF - }, - { - "clutter_sine_func", - "sine", - CLUTTER_ALPHA_SINE - }, - { - "clutter_square_func", - "square", - CLUTTER_ALPHA_SQUARE - }, - { - "clutter_smoothstep_inc_func", - "smoothstep-inc", - CLUTTER_ALPHA_SMOOTHSTEP_INC - }, - { - "clutter_smoothstep_dec_func", - "smoothstep-dec", - CLUTTER_ALPHA_SMOOTHSTEP_DEC - }, - { - "clutter_exp_inc_func", - "exp-inc", - CLUTTER_ALPHA_EXP_INC - }, - { - "clutter_exp_dec_func", - "exp-dec", - CLUTTER_ALPHA_EXP_DEC - } +#define ALPHA_FUNC(func,nick) { #func, nick, func } + ALPHA_FUNC (clutter_ramp_inc_func, "ramp-inc"), + ALPHA_FUNC (clutter_ramp_dec_func, "ramp-dec"), + ALPHA_FUNC (clutter_ramp_func, "ramp"), + ALPHA_FUNC (clutter_sine_inc_func, "sine-inc"), + ALPHA_FUNC (clutter_sine_dec_func, "sine-dec"), + ALPHA_FUNC (clutter_sine_half_func, "sine-half"), + ALPHA_FUNC (clutter_sine_in_func, "sine-in"), + ALPHA_FUNC (clutter_sine_out_func, "sine-out"), + ALPHA_FUNC (clutter_sine_in_out_func, "sine-in-out"), + ALPHA_FUNC (clutter_sine_func, "sine"), + ALPHA_FUNC (clutter_square_func, "square"), + ALPHA_FUNC (clutter_smoothstep_inc_func, "smoothstep-inc"), + ALPHA_FUNC (clutter_smoothstep_dec_func, "smoothstep-dec"), + ALPHA_FUNC (clutter_exp_inc_func, "exp-inc"), + ALPHA_FUNC (clutter_exp_dec_func, "exp-dec"), + ALPHA_FUNC (clutter_ease_in_func, "ease-in"), + ALPHA_FUNC (clutter_ease_out_func, "ease-out"), + ALPHA_FUNC (clutter_ease_in_out_func, "ease-in-out") +#undef ALPHA_FUNC }; static const gint n_clutter_alphas = G_N_ELEMENTS (clutter_alphas); diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index 34dd12137..82b905012 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -186,6 +186,33 @@ typedef enum { CLUTTER_REQUEST_WIDTH_FOR_HEIGHT } ClutterRequestMode; +/** + * ClutterAnimationMode: + * @CLUTTER_CUSTOM_MODE: custom progress function + * @CLUTTER_LINEAR: linear progress + * @CLUTTER_SINE_IN: sine-in progress + * @CLUTTER_SINE_OUT: sine-out progress + * @CLUTTER_SINE_IN_OUT: sine-in-out progress + * @CLUTTER_EASE_IN: ease-in progress + * @CLUTTER_EASE_OUT: ease-out progress + * @CLUTTER_EASE_IN_OUT: ease-in-out progress + * + * The animation modes used by #ClutterAlpha and #ClutterAnimation. This + * enumeration can be expanded in later versions of Clutter. + * + * Since: 1.0 + */ +typedef enum { + CLUTTER_CUSTOM_MODE, + CLUTTER_LINEAR, + CLUTTER_SINE_IN, + CLUTTER_SINE_OUT, + CLUTTER_SINE_IN_OUT, + CLUTTER_EASE_IN, + CLUTTER_EASE_OUT, + CLUTTER_EASE_IN_OUT +} ClutterAnimationMode; + G_END_DECLS #endif /* __CLUTTER_TYPES_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index cb29599e3..03b56b25d 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -30,6 +30,7 @@ #include "clutter-actor.h" #include "clutter-alpha.h" +#include "clutter-animation.h" #include "clutter-backend.h" #include "clutter-color.h" #include "clutter-container.h" @@ -50,6 +51,7 @@ #include "clutter-feature.h" #include "clutter-rectangle.h" #include "clutter-group.h" +#include "clutter-interval.h" #include "clutter-keysyms.h" #include "clutter-label.h" #include "clutter-list-model.h" diff --git a/doc/reference/clutter/Makefile.am b/doc/reference/clutter/Makefile.am index 176160b48..ca941565f 100644 --- a/doc/reference/clutter/Makefile.am +++ b/doc/reference/clutter/Makefile.am @@ -12,7 +12,7 @@ AUTOMAKE_OPTIONS = 1.6 DOC_MODULE=clutter # The top-level SGML file. You can change this if you want to. -DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml # The directory containing the source code. Relative to $(srcdir). # gtk-doc will search all .c & .h files beneath here for inline comments @@ -99,7 +99,7 @@ HTML_IMAGES=\ content_files= \ version.xml \ subclassing-ClutterActor.xml \ - clutter-animation.xml \ + clutter-animation-tutorial.xml \ creating-behaviours.xml \ clutter-overview.xml \ building-clutter.xml @@ -109,7 +109,7 @@ content_files= \ # e.g. expand_content_files=running.sgml expand_content_files= \ subclassing-ClutterActor.xml \ - clutter-animation.xml \ + clutter-animation-tutorial.xml \ creating-behaviours.xml \ clutter-overview.xml \ building-clutter.xml diff --git a/doc/reference/clutter/clutter-animation.xml b/doc/reference/clutter/clutter-animation-tutorial.xml similarity index 79% rename from doc/reference/clutter/clutter-animation.xml rename to doc/reference/clutter/clutter-animation-tutorial.xml index 1e8515d64..cb3797d2a 100644 --- a/doc/reference/clutter/clutter-animation.xml +++ b/doc/reference/clutter/clutter-animation-tutorial.xml @@ -52,7 +52,8 @@ rotate_actor (gpointer data) clutter_actor_set_rotationx (clos->actor, clos->current_angle, 0, 0, 0); - clos->current_angle += CFX_ONE; + /* add one degree */ + clos->current_angle += COGL_FIXED_ONE; if (clos->current_angle == clos->final_angle) return FALSE; @@ -60,16 +61,27 @@ rotate_actor (gpointer data) return TRUE; } +static void +rotate_actor_cleanup (gpointer data) +{ + RotationClosure *clos = data; + + g_object_unref (clos->actor); + g_free (clos); +} + ... - RotationClosure clos = { NULL, } + RotationClosure *clos = NULL; - clos.actor = an_actor; - clos.final_angle = CLUTTER_FLOAT_TO_FIXED (360.0); - clos.current_angle = 0; + clos = g_new (RotationClosure, 1); + clos->actor = g_object_ref (an_actor); + clos->final_angle = CLUTTER_FLOAT_TO_FIXED (360.0); + clos->current_angle = 0; - g_timeout_add (1000 / 360, /* fps to interval in milliseconds */ - rotate_actor, - &clos); + g_timeout_add_full (1000 / 360, /* fps to interval in milliseconds */ + rotate_actor, + clos, + rotate_actor_cleanup); @@ -140,7 +152,7 @@ clutter_timeline_new_for_duration (msecs); The speed, duration and number of frames of the timeline then be modifed via the objects properties and API calls. The timeline can - be made to loop by settings it "loop" property to %TRUE. + be made to loop by setting its "loop" property to %TRUE. @@ -153,7 +165,7 @@ clutter_timeline_new_for_duration (msecs); - By attaching a handler to the timeline's ClutterTimeline::new-frame + By attaching a handler to the timeline's #ClutterTimeline::new-frame signal a timeline can then be used to drive an animation by altering an actor's visual properties in this callback. The callback looks like: @@ -164,13 +176,12 @@ on_new_frame (ClutterTimeline *timeline, gint frame_num, gpointer user_data) { - } The frame_num parameter is set to the timeline's - current frame number (which is between 0 and the "num-frames" property). + current frame number (which is between 1 and the "num-frames" property). This value can be used to compute the state of a particular animation that is dependant on the frame numer. The clutter_timeline_get_progress() function can also be used to get a normalised value of the timeline's @@ -235,7 +246,7 @@ main (int argc, char *argv[]) clutter_actor_set_position (actor, 100, 100); - timeline = clutter_timeline_new_for (360, 60); /* a degree per frame */ + timeline = clutter_timeline_new_for (360, 60); /* one degree per frame */ clutter_timeline_set_loop (timeline, TRUE); g_signal_connect (timeline, "new-frame", G_CALLBACK (on_new_frame), actor); @@ -263,7 +274,7 @@ main (int argc, char *argv[]) With a large application containing many animations, the use of just - timelines can become unweldy and difficult to manage with much code + timelines can become unwieldy and difficult to manage with much code duplication in the new-frame handlers that can require over complex code changes for minor animation modifications. To ease these problems the #ClutterAlpha and #ClutterBehaviour classes were created. @@ -279,7 +290,7 @@ main (int argc, char *argv[]) - A ClutterAlpha is simply a 'function of time' (not pixel alpha!). It + A ClutterAlpha is simply a 'function of time' (not a pixel alpha!). It is created by referencing a source timeline and a function which produces a value between 0 and %CLUTTER_ALPHA_MAX dependant on the timeline position. Various prebuilt alpha functions are included @@ -344,7 +355,7 @@ main (int argc, char *argv[]) A Behaviour is created with a #ClutterAlpha and a set of limits for - whatever the behaviour modifys actor wise. The current #ClutterAlpha + whatever the behaviour modifies in an actor. The current #ClutterAlpha value is then mapped to a value between these limits and this value set on any applied actors. With the #ClutterAlpha's underlying timeline playing the produced value will change and the behaviour @@ -353,8 +364,8 @@ main (int argc, char *argv[]) - A #ClutterBehaviour is effectively 'driven' by a supplied #ClutterAlpha and - when then applied to an actor it will modify a visual property or + A #ClutterBehaviour is effectively 'driven' by a supplied #ClutterAlpha + and when then applied to an actor it will modify a visual property or feature of the actor dependant on the Alpha's value. For example a path based behaviour applied to an actor will alter its position along the path dependant on the current alpha value over time. The @@ -431,7 +442,7 @@ main (int argc, char *argv[]) - + The following example demonstrates an ellipse behaviour in action. @@ -503,72 +514,81 @@ main (int argc, char *argv[]) Combining behaviours that effect the same actor properties (i.e two separate paths) will cause unexpected results. The values - will not be merged in any way with essentially a the last applied - behaviour taking precedence. + will not be merged in any way with only the last applied behaviour taking + precedence. - - Tips for implementing a new behaviour can be found here. - + + Tips for implementing a new behaviour can be found here. + -
- Effects +
+ Implicit Animations + Using behaviours for simple animations of a single actor may + be too complicated, in terms of memory management and bookkeeping + of the object instances. For this reason, Clutter also provides a + simple animation API for implicit animations using properties of + an actor: clutter_actor_animate(). + + The clutter_actor_animate() family of functions will create + and use an implicit #ClutterAnimation instance, which will then + handle the animation of one or more #ClutterActor properties between + a range of values. + + - - Clutter effects API provide a simplified abstraction for firing simple - transitions from code. Clutter effects are created from a - #ClutterEffectTemplate which is an abstraction of a timeline and - an alpha function. An effect template can be created with: - + The following example demonstrates how to use the + clutter_actor_animate() method to tween an actor + between the current position and a new set of coordinates. + The animation takes 200 milliseconds to complete and + uses a linear speed. -ClutterEffectTemplate *etemplate = - clutter_effect_template_new_for_duration (2000, CLUTTER_ALPHA_RAMP_INC); + clutter_actor_animate (actor, CLUTTER_LINEAR, 200 + "x", 200, + "y", 200, + NULL); + + + The clutter_actor_animate() method returns a #ClutterAnimation + instance that can be used to start, stop and modify the animation + while it's running. The #ClutterAnimation::completed signal will + be emitted when the animation has been completed. + + When the animation is complete it will be automatically + unreferenced, and disposed if nothing else is holding a reference + on it. + + Calling clutter_actor_animate() multiple times on an + actor which is being animated will cause the animation to be updated + with the new values. + + - - This will create an effect template lasting 2000 milliseconds (2 - seconds) and use an alpha function of %CLUTTER_ALPHA_RAMP_INC, there - are other more advanced forms for creating effect templates from - existing timelines, as well as attaching a callback to be called - with user_data when the effect template is destroyed. - - - - - When we have an effect template we can create a temporary behaviour - animating an actor simply by issuing: - + The following example demonstrates how to animate an actor + inside the signal handler for a button press event. If the + user presses the button on a new position while the animation + is running, the animation will be restarted with the new + final values updated. -clutter_effect_move (etemplate, actor, 23, 42, NULL, NULL); + static gboolean + on_button_press (ClutterActor *actor, + ClutterButtonEvent *event, + gpointer user_data) + { + clutter_actor_animate (actor, CLUTTER_SINE_IN_OUT, 500, + "x", event->x, + "y", event->y, + NULL); + return TRUE; + } - -and the actor will move from its current position to the coordinates -(23, 42) in 2 seconds. Effects can also be stacked, so calling: - - -clutter_effect_move (etemplate, actor, 23, 42, NULL, NULL); -clutter_effect_fade (etemplate, actor, 0, NULL, NULL); - - -The actor will move and fade out at the same time. - - + - Since effects return a #ClutterTimeline, you can stop an effect from - immediatly happening by calling clutter_timeline_stop () on the - returned timeline. - - - - - The timeline and all the effect infrastructure is unreferenced as soon - as the timeline emits the ClutterTimeline::completed signal. - -
@@ -582,9 +602,9 @@ The actor will move and fade out at the same time. - Of course animations can be created outside of Clutter Utilities, - they are not expected to cover every kind of possible animation - scenario. + Of course animations can becreated outside of the Clutter animation + framework, as the framework is not expected to cover every kind of + possible animation scenario. diff --git a/doc/reference/clutter/clutter-docs.sgml b/doc/reference/clutter/clutter-docs.xml similarity index 97% rename from doc/reference/clutter/clutter-docs.sgml rename to doc/reference/clutter/clutter-docs.xml index bd7531838..ff98eb8ef 100644 --- a/doc/reference/clutter/clutter-docs.sgml +++ b/doc/reference/clutter/clutter-docs.xml @@ -101,9 +101,10 @@ - Simple effects API + High Level API - + + @@ -164,7 +165,7 @@ - + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 3adde1e9b..1acd4bbf0 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -143,6 +143,7 @@ ClutterAlpha ClutterAlphaClass clutter_alpha_new clutter_alpha_new_full +clutter_alpha_new_for_mode clutter_alpha_get_alpha CLUTTER_ALPHA_MAX_ALPHA ClutterAlphaFunc @@ -150,30 +151,29 @@ clutter_alpha_set_func clutter_alpha_set_closure clutter_alpha_set_timeline clutter_alpha_get_timeline -CLUTTER_ALPHA_RAMP_INC +clutter_alpha_set_mode +clutter_alpha_get_mode + + clutter_ramp_inc_func -CLUTTER_ALPHA_RAMP_DEC clutter_ramp_dec_func -CLUTTER_ALPHA_RAMP clutter_ramp_func -CLUTTER_ALPHA_SINE clutter_sine_func -CLUTTER_ALPHA_SINE_INC clutter_sine_inc_func -CLUTTER_ALPHA_SINE_DEC clutter_sine_dec_func -CLUTTER_ALPHA_SINE_HALF clutter_sine_half_func -CLUTTER_ALPHA_SQUARE +clutter_sine_in_func +clutter_sine_out_func +clutter_sine_in_out_func clutter_square_func -CLUTTER_ALPHA_SMOOTHSTEP_INC clutter_smoothstep_inc_func -CLUTTER_ALPHA_SMOOTHSTEP_DEC clutter_smoothstep_dec_func -CLUTTER_ALPHA_EXP_INC clutter_exp_inc_func -CLUTTER_ALPHA_EXP_DEC clutter_exp_dec_func +clutter_ease_in_func +clutter_ease_out_func +clutter_ease_in_out_func + CLUTTER_ALPHA CLUTTER_IS_ALPHA @@ -181,6 +181,7 @@ CLUTTER_TYPE_ALPHA CLUTTER_ALPHA_CLASS CLUTTER_IS_ALPHA_CLASS CLUTTER_ALPHA_GET_CLASS + ClutterAlphaPrivate clutter_alpha_get_type @@ -1516,3 +1517,79 @@ ClutterShaderPrivate clutter_shader_get_type clutter_shader_error_quark
+ +
+Implicit Animations +clutter-animation +ClutterAnimation +ClutterAnimationClass +ClutterAnimationMode +clutter_animation_new +clutter_animation_set_actor +clutter_animation_get_actor +clutter_animation_set_mode +clutter_animation_get_mode +clutter_animation_set_duration +clutter_animation_get_duration +clutter_animation_set_loop +clutter_animation_get_loop +clutter_animation_set_timeline +clutter_animation_get_timeline +clutter_animation_set_alpha +clutter_animation_get_alpha + + +clutter_animation_bind_property +clutter_animation_update_property +clutter_animation_has_property +clutter_animation_unbind_property +clutter_animation_get_interval + + +clutter_actor_animate +clutter_actor_animate_with_timeline +clutter_actor_animate_with_alpha + + +CLUTTER_TYPE_ANIMATION +CLUTTER_ANIMATION +CLUTTER_ANIMATION_CLASS +CLUTTER_IS_ANIMATION +CLUTTER_IS_ANIMATION_CLASS +CLUTTER_ANIMATION_GET_CLASS + + +ClutterAnimationPrivate +clutter_animation_get_type +
+ +
+Value intervals +clutter-interval +ClutterInterval +ClutterIntervalClass +clutter_interval_new +clutter_interval_new_with_values +clutter_interval_clone +clutter_interval_get_value_type +clutter_interval_set_initial_value +clutter_interval_get_initial_value +clutter_interval_peek_initial_value +clutter_interval_set_final_value +clutter_interval_get_final_value +clutter_interval_peek_final_value +clutter_interval_set_interval +clutter_interval_get_interval + + +CLUTTER_TYPE_INTERVAL +CLUTTER_INTERVAL +CLUTTER_INTERVAL_CLASS +CLUTTER_IS_INTERVAL +CLUTTER_IS_INTERVAL_CLASS +CLUTTER_INTERVAL_GET_CLASS + + +ClutterIntervalPrivate +clutter_interval_get_type +
diff --git a/tests/Makefile.am b/tests/Makefile.am index cdebda58f..e31dba9da 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,3 @@ - SUBDIRS = data conform interactive micro-bench tools EXTRA_DIST = README @@ -12,4 +11,3 @@ test-report full-report: # run make test as part of make check check-local: test - diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 0c17a33a1..2b7346611 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -35,7 +35,9 @@ UNIT_TESTS = \ test-clip.c \ test-paint-wrapper.c \ test-texture-quality.c \ - test-layout.c + test-layout.c \ + test-animation.c \ + test-easing.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-actors.c b/tests/interactive/test-actors.c index ffe4f37b3..d5116cdf7 100644 --- a/tests/interactive/test-actors.c +++ b/tests/interactive/test-actors.c @@ -168,7 +168,7 @@ test_actors_main (int argc, char *argv[]) g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), oh); /* Set up some behaviours to handle scaling */ - alpha = clutter_alpha_new_full (timeline, CLUTTER_ALPHA_SINE, NULL, NULL); + alpha = clutter_alpha_new_full (timeline, clutter_sine_func, NULL, NULL); scaler_1 = clutter_behaviour_scale_new (alpha, 0.5, 0.5, diff --git a/tests/interactive/test-animation.c b/tests/interactive/test-animation.c new file mode 100644 index 000000000..b90e912d5 --- /dev/null +++ b/tests/interactive/test-animation.c @@ -0,0 +1,95 @@ +#include +#include +#include + +static gboolean is_expanded = FALSE; + +static void +on_animation_complete (ClutterAnimation *animation, + ClutterActor *actor) +{ + is_expanded = !is_expanded; + + g_print ("Animation complete\n"); + + clutter_actor_set_reactive (actor, TRUE); +} + +static gboolean +on_button_press (ClutterActor *actor, + ClutterButtonEvent *event, + gpointer dummy) +{ + ClutterAnimation *animation; + gint old_x, old_y, new_x, new_y; + gint old_width, old_height, new_width, new_height; + guint8 old_op, new_op; + + clutter_actor_get_position (actor, &old_x, &old_y); + clutter_actor_get_size (actor, &old_width, &old_height); + old_op = clutter_actor_get_opacity (actor); + + if (!is_expanded) + { + new_x = old_x - 100; + new_y = old_y - 100; + new_width = old_width + 200; + new_height = old_height + 200; + new_op = 255; + } + else + { + new_x = old_x + 100; + new_y = old_y + 100; + new_width = old_width - 200; + new_height = old_height - 200; + new_op = 128; + } + + animation = + clutter_actor_animate (actor, CLUTTER_EASE_IN, 2000, + "x", new_x, + "y", new_y, + "width", new_width, + "height", new_height, + "opacity", new_op, + "fixed::reactive", FALSE, + NULL); + g_signal_connect (animation, + "completed", G_CALLBACK (on_animation_complete), + actor); + + return TRUE; +} + +G_MODULE_EXPORT int +test_animation_main (int argc, char *argv[]) +{ + ClutterActor *stage, *rect; + ClutterColor stage_color = { 0x66, 0x66, 0xdd, 0xff }; + ClutterColor rect_color = { 0x44, 0xdd, 0x44, 0xff }; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + + rect = clutter_rectangle_new_with_color (&rect_color); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); + clutter_actor_set_size (rect, 50, 50); + clutter_actor_set_anchor_point (rect, 25, 25); + clutter_actor_set_position (rect, + clutter_actor_get_width (stage) / 2, + clutter_actor_get_height (stage) / 2); + clutter_actor_set_opacity (rect, 0x88); + clutter_actor_set_reactive (rect, TRUE); + g_signal_connect (rect, + "button-press-event", G_CALLBACK (on_button_press), + NULL); + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +} diff --git a/tests/interactive/test-behave.c b/tests/interactive/test-behave.c index c3e5d09b3..2f1e46a50 100644 --- a/tests/interactive/test-behave.c +++ b/tests/interactive/test-behave.c @@ -173,10 +173,9 @@ test_behave_main (int argc, char *argv[]) "completed", G_CALLBACK (timeline_completed), NULL); - /* Set an alpha func to power behaviour - ramp is constant rise/fall */ - alpha = clutter_alpha_new_full (timeline, - CLUTTER_ALPHA_RAMP_INC, - NULL, NULL); + /* Set an alpha func to power behaviour - ramp is constant rise */ + alpha = clutter_alpha_new_for_mode (CLUTTER_LINEAR); + clutter_alpha_set_timeline (alpha, timeline); /* Create a behaviour for that alpha */ o_behave = clutter_behaviour_opacity_new (alpha, 0X33, 0xff); diff --git a/tests/interactive/test-depth.c b/tests/interactive/test-depth.c index ab13975c0..a07eff234 100644 --- a/tests/interactive/test-depth.c +++ b/tests/interactive/test-depth.c @@ -160,7 +160,7 @@ test_depth_main (int argc, char *argv[]) NULL); d_behave = clutter_behaviour_depth_new (clutter_alpha_new_full (timeline, - CLUTTER_ALPHA_RAMP_INC, + clutter_ramp_inc_func, NULL, NULL), -100, 100); clutter_behaviour_apply (d_behave, label); @@ -171,7 +171,7 @@ test_depth_main (int argc, char *argv[]) clutter_actor_set_position (janus, 300, 350); r_behave = clutter_behaviour_rotate_new (clutter_alpha_new_full (timeline, - CLUTTER_ALPHA_RAMP_INC, + clutter_ramp_inc_func, NULL, NULL), CLUTTER_Y_AXIS, CLUTTER_ROTATE_CW, @@ -188,7 +188,7 @@ test_depth_main (int argc, char *argv[]) clutter_actor_set_opacity (box, 0x44); r_behave = clutter_behaviour_rotate_new (clutter_alpha_new_full (timeline, - CLUTTER_ALPHA_RAMP_INC, + clutter_ramp_inc_func, NULL, NULL), CLUTTER_Y_AXIS, CLUTTER_ROTATE_CW, diff --git a/tests/interactive/test-easing.c b/tests/interactive/test-easing.c new file mode 100644 index 000000000..d968239bf --- /dev/null +++ b/tests/interactive/test-easing.c @@ -0,0 +1,113 @@ +#include +#include +#include + +const struct { + const gchar *name; + ClutterAnimationMode mode; +} easing_modes[] = { + { "linear", CLUTTER_LINEAR }, + { "sine-in", CLUTTER_SINE_IN }, + { "sine-out", CLUTTER_SINE_OUT }, + { "sine-in-out", CLUTTER_SINE_IN_OUT }, + { "ease-in", CLUTTER_EASE_IN }, + { "ease-out", CLUTTER_EASE_OUT }, + { "ease-in-out", CLUTTER_EASE_IN_OUT } +}; + +static const gint n_easing_modes = G_N_ELEMENTS (easing_modes); +static gint current_mode = 0; + +static ClutterActor *main_stage = NULL; +static ClutterActor *easing_mode_label = NULL; + +static gboolean +on_button_press (ClutterActor *actor, + ClutterButtonEvent *event, + ClutterActor *rectangle) +{ + ClutterAnimation *animation; + ClutterAnimationMode cur_mode; + gchar *text; + gint stage_width, stage_height; + gint label_width, label_height; + + text = g_strdup_printf ("Easing mode: %s (%d of %d)\n", + easing_modes[current_mode].name, + current_mode + 1, + n_easing_modes); + + clutter_label_set_text (CLUTTER_LABEL (easing_mode_label), text); + g_free (text); + + clutter_actor_get_size (main_stage, &stage_width, &stage_height); + clutter_actor_get_size (easing_mode_label, &label_width, &label_height); + + clutter_actor_set_position (easing_mode_label, + stage_width - label_width - 10, + stage_height - label_height - 10); + + cur_mode = easing_modes[current_mode].mode; + + animation = + clutter_actor_animate (rectangle, cur_mode, 2000, + "x", event->x, + "y", event->y, + NULL); + + current_mode = (current_mode + 1 < n_easing_modes) ? current_mode + 1 : 0; + + return TRUE; +} + +G_MODULE_EXPORT int +test_easing_main (int argc, char *argv[]) +{ + ClutterActor *stage, *rect, *label; + ClutterColor stage_color = { 0x66, 0x66, 0xdd, 0xff }; + ClutterColor rect_color = { 0x44, 0xdd, 0x44, 0xff }; + gchar *text; + gint stage_width, stage_height; + gint label_width, label_height; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + main_stage = stage; + + clutter_actor_get_size (stage, &stage_width, &stage_height); + + rect = clutter_rectangle_new_with_color (&rect_color); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); + clutter_actor_set_size (rect, 50, 50); + clutter_actor_set_anchor_point (rect, 25, 25); + clutter_actor_set_position (rect, stage_width / 2, stage_height / 2); + clutter_actor_set_opacity (rect, 0x88); + g_signal_connect (stage, + "button-press-event", G_CALLBACK (on_button_press), + rect); + + text = g_strdup_printf ("Easing mode: %s (%d of %d)\n", + easing_modes[current_mode].name, + current_mode + 1, + n_easing_modes); + + label = clutter_label_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), label); + clutter_label_set_font_name (CLUTTER_LABEL (label), "Sans 18px"); + clutter_label_set_text (CLUTTER_LABEL (label), text); + clutter_actor_get_size (label, &label_width, &label_height); + clutter_actor_set_position (label, + stage_width - label_width - 10, + stage_height - label_height - 10); + easing_mode_label = label; + + g_free (text); + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +} diff --git a/tests/interactive/test-effects.c b/tests/interactive/test-effects.c index 08db46694..10dcbec7b 100644 --- a/tests/interactive/test-effects.c +++ b/tests/interactive/test-effects.c @@ -18,7 +18,7 @@ test_effects_main (int argc, char *argv[]) timeline = clutter_timeline_new_for_duration (5000); clutter_timeline_set_loop (timeline, TRUE); tmpl = - clutter_effect_template_new (timeline, CLUTTER_ALPHA_RAMP_INC); + clutter_effect_template_new (timeline, clutter_ramp_inc_func); stage = clutter_stage_get_default (); container = CLUTTER_CONTAINER (stage); diff --git a/tests/interactive/test-layout.c b/tests/interactive/test-layout.c index 704bae706..9fe17222e 100644 --- a/tests/interactive/test-layout.c +++ b/tests/interactive/test-layout.c @@ -765,7 +765,7 @@ test_layout_main (int argc, char *argv[]) NULL); behaviour = clutter_behaviour_scale_new (clutter_alpha_new_full (main_timeline, - CLUTTER_ALPHA_SINE, + clutter_sine_func, NULL, NULL), 1.0, 1.0, 2.0, 2.0); diff --git a/tests/interactive/test-multistage.c b/tests/interactive/test-multistage.c index 3395c3445..19b9c750f 100644 --- a/tests/interactive/test-multistage.c +++ b/tests/interactive/test-multistage.c @@ -75,10 +75,8 @@ on_button_press (ClutterActor *actor, timeline = clutter_timeline_new_for_duration (2000); g_object_set (timeline, "loop", TRUE, NULL); - alpha = clutter_alpha_new_full (timeline, - CLUTTER_ALPHA_RAMP_INC, - NULL, NULL); - + alpha = clutter_alpha_new_for_mode (CLUTTER_LINEAR); + clutter_alpha_set_timeline (alpha, timeline); r_behave = clutter_behaviour_rotate_new (alpha, CLUTTER_Y_AXIS, CLUTTER_ROTATE_CW, diff --git a/tests/interactive/test-paint-wrapper.c b/tests/interactive/test-paint-wrapper.c index fda1a809c..ab8b8a38f 100644 --- a/tests/interactive/test-paint-wrapper.c +++ b/tests/interactive/test-paint-wrapper.c @@ -197,7 +197,7 @@ test_paint_wrapper_main (int argc, char *argv[]) g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), oh); /* Set up some behaviours to handle scaling */ - alpha = clutter_alpha_new_full (timeline, CLUTTER_ALPHA_SINE, NULL, NULL); + alpha = clutter_alpha_new_full (timeline, clutter_sine_func, NULL, NULL); scaler_1 = clutter_behaviour_scale_new (alpha, 0.5, 0.5, diff --git a/tests/interactive/test-rotate.c b/tests/interactive/test-rotate.c index 371cf54fc..377a309db 100644 --- a/tests/interactive/test-rotate.c +++ b/tests/interactive/test-rotate.c @@ -44,10 +44,9 @@ test_rotate_main (int argc, char *argv[]) timeline = clutter_timeline_new (200, 26); /* num frames, fps */ g_object_set (timeline, "loop", TRUE, NULL); - /* Set an alpha func to power behaviour - ramp is constant rise/fall */ - alpha = clutter_alpha_new_full (timeline, - CLUTTER_ALPHA_RAMP_INC, - NULL, NULL); + /* Set an alpha func to power behaviour */ + alpha = clutter_alpha_new_for_mode (CLUTTER_LINEAR); + clutter_alpha_set_timeline (alpha, timeline); /* Create a behaviour for that alpha */ r_behave = clutter_behaviour_rotate_new (alpha, diff --git a/tests/interactive/test-scale.c b/tests/interactive/test-scale.c index 5b806deab..27054745e 100644 --- a/tests/interactive/test-scale.c +++ b/tests/interactive/test-scale.c @@ -62,7 +62,7 @@ test_scale_main (int argc, char *argv[]) timeline = clutter_timeline_new_for_duration (750); alpha = clutter_alpha_new_full (timeline, - CLUTTER_ALPHA_RAMP, + clutter_ramp_func, NULL, NULL); behave = clutter_behaviour_scale_new (alpha, diff --git a/tests/interactive/test-texture-quality.c b/tests/interactive/test-texture-quality.c index 11f189d78..8c5721f85 100644 --- a/tests/interactive/test-texture-quality.c +++ b/tests/interactive/test-texture-quality.c @@ -43,6 +43,7 @@ G_MODULE_EXPORT gint test_texture_quality_main (int argc, char *argv[]) { ClutterTimeline *timeline; + ClutterAlpha *alpha; ClutterBehaviour *depth_behavior; ClutterActor *stage; ClutterActor *image; @@ -80,10 +81,9 @@ test_texture_quality_main (int argc, char *argv[]) "completed", G_CALLBACK (timeline_completed), NULL); - depth_behavior = clutter_behaviour_depth_new ( - clutter_alpha_new_full (timeline, CLUTTER_ALPHA_RAMP_INC, NULL, NULL), - -2500, 400); - + alpha = clutter_alpha_new_for_mode (CLUTTER_LINEAR); + clutter_alpha_set_timeline (alpha, timeline); + depth_behavior = clutter_behaviour_depth_new (alpha, -2500, 400); clutter_behaviour_apply (depth_behavior, image); clutter_actor_show (stage); diff --git a/tests/interactive/test-threads.c b/tests/interactive/test-threads.c index b8647e946..b21acbe59 100644 --- a/tests/interactive/test-threads.c +++ b/tests/interactive/test-threads.c @@ -213,7 +213,7 @@ test_threads_main (int argc, char *argv[]) timeline = clutter_timeline_new (150, 50); clutter_timeline_set_loop (timeline, TRUE); r_behaviour = clutter_behaviour_rotate_new (clutter_alpha_new_full (timeline, - CLUTTER_ALPHA_RAMP_INC, + clutter_ramp_inc_func, NULL, NULL), CLUTTER_Z_AXIS, CLUTTER_ROTATE_CW, @@ -221,7 +221,7 @@ test_threads_main (int argc, char *argv[]) clutter_behaviour_apply (r_behaviour, rect); p_behaviour = clutter_behaviour_path_new (clutter_alpha_new_full (timeline, - CLUTTER_ALPHA_SINE, + clutter_ramp_inc_func, NULL, NULL), knots, G_N_ELEMENTS (knots)); diff --git a/tests/interactive/test-viewport.c b/tests/interactive/test-viewport.c index 6d2a6305f..fcee1cf89 100644 --- a/tests/interactive/test-viewport.c +++ b/tests/interactive/test-viewport.c @@ -37,12 +37,11 @@ test_viewport_main (int argc, char *argv[]) /* Make a timeline */ timeline = clutter_timeline_new (200, 26); /* num frames, fps */ - g_object_set (timeline, "loop", TRUE, NULL); + clutter_timeline_set_loop (timeline, TRUE); - /* Set an alpha func to power behaviour - ramp is constant rise/fall */ - alpha = clutter_alpha_new_full (timeline, - CLUTTER_ALPHA_RAMP_INC, - NULL, NULL); + /* Set an alpha func to power behaviour */ + alpha = clutter_alpha_new_for_mode (CLUTTER_LINEAR); + clutter_alpha_set_timeline (alpha, timeline); /* Create a behaviour for that alpha */ r_behave = clutter_behaviour_rotate_new (alpha,