From 999359d64ccfbdbcc952662c14fa47441d6a5225 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 11 Dec 2009 23:48:58 +0000 Subject: [PATCH 1/9] Add ActorBox animation methods ClutterActorBox should have an interpolate() method that allows to compute the intermediate values between two states, given a progress value, e.g.: clutter_actor_box_interpolate (start, end, alpha, &result); Another utility method, useful for layout managers, is a modifier that clamps the members of the actor box to the nearest integer value. --- clutter/clutter-actor.c | 47 ++++++++++++++++++++++ clutter/clutter-types.h | 5 +++ doc/reference/clutter/clutter-sections.txt | 2 + 3 files changed, 54 insertions(+) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index f676d5e3b..c439437d0 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -8317,6 +8317,53 @@ clutter_actor_box_from_vertices (ClutterActorBox *box, box->y2 = y_2; } +/** + * clutter_actor_box_interpolate: + * @initial: the initial #ClutterActorBox + * @final: the final #ClutterActorBox + * @progress: the interpolation progress + * @result: (out): return location for the interpolation + * + * Interpolates between @initial and @final #ClutterActorBoxes + * using @progress + * + * Since: 1.2 + */ +void +clutter_actor_box_interpolate (const ClutterActorBox *initial, + const ClutterActorBox *final, + gdouble progress, + ClutterActorBox *result) +{ + g_return_if_fail (initial != NULL); + g_return_if_fail (final != NULL); + g_return_if_fail (result != NULL); + + result->x1 = initial->x1 + (final->x1 - initial->x1) * progress; + result->y1 = initial->y1 + (final->y1 - initial->y1) * progress; + result->x2 = initial->x2 + (final->x2 - initial->x2) * progress; + result->y2 = initial->y2 + (final->y2 - initial->y2) * progress; +} + +/** + * clutter_actor_box_clamp_to_pixel: + * @box: (inout): the #ClutterActorBox to clamp + * + * Clamps the components of @box to the nearest integer + * + * Since: 1.2 + */ +void +clutter_actor_box_clamp_to_pixel (ClutterActorBox *box) +{ + g_return_if_fail (box != NULL); + + box->x1 = floorf (box->x1 + 0.5); + box->y1 = floorf (box->y1 + 0.5); + box->x2 = floorf (box->x2 + 0.5); + box->y2 = floorf (box->y2 + 0.5); +} + /******************************************************************************/ struct _ShaderData diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index ec7502310..b13a4dfed 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -153,6 +153,11 @@ gboolean clutter_actor_box_contains (const ClutterActorBox *box, gfloat y); void clutter_actor_box_from_vertices (ClutterActorBox *box, const ClutterVertex verts[]); +void clutter_actor_box_interpolate (const ClutterActorBox *initial, + const ClutterActorBox *final, + gdouble progress, + ClutterActorBox *result); +void clutter_actor_box_clamp_to_pixel (ClutterActorBox *box); /** * ClutterGeometry: diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index e7dafd772..d90358739 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -414,6 +414,8 @@ clutter_actor_box_get_size clutter_actor_box_get_area clutter_actor_box_contains clutter_actor_box_from_vertices +clutter_actor_box_clamp_to_pixel +clutter_actor_box_interpolate ClutterVertex From f94a903d9ebdbc055010bb4a4c6fe7e76f418bd8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sat, 12 Dec 2009 00:02:43 +0000 Subject: [PATCH 2/9] layout: Add animation support to LayoutManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to animate a fluid layout we cannot use the common animation code paths as they will override the size request and allocation paths that are handled by the layout manager itself. One way to introduce animations in the allocation sequence is to use a Timeline and an Alpha to compute a progress value and then use that value to interpolate an ActorBox between the initial and final states of the animation - with the initial state being the last allocation of the child prior to the animation start, and the final state the allocation of the child at the end; for every frame of the Timeline we then queue a relayout on the layout manager's container, which will result in an animation. ClutterLayoutManager is the most likely place to add a generic API for beginning and ending an animation, as well as the place to provide a default code path to create the ancillary Timeline and Alpha instances needed to drive the animation. A LayoutManager sub-class will need to: • call clutter_layout_manager_begin_animation() whenever it should animate between two states, for instance: whenever a layout property changes value; • eventually override begin_animation() and end_animation() in case further state needs to be set up, and then chain up to the default implementation provided by LayoutManager; • if a completely different implementation is required, the layout manager sub-class should override begin_animation(), end_animation() and get_animation_progress(). Inside the allocate() implementation the sub-class should also interpolate between the last known allocation of a child and the newly computed allocation. --- clutter/clutter-layout-manager.c | 159 ++++++++++++++++++++- clutter/clutter-layout-manager.h | 62 +++++--- doc/reference/clutter/clutter-sections.txt | 5 + 3 files changed, 204 insertions(+), 22 deletions(-) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 119539b6f..251f3e647 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -107,6 +107,11 @@ * child of the #ClutterContainer. * * + * + * Animating a ClutterLayoutManager + * ... + * + * * #ClutterLayoutManager is available since Clutter 1.2 */ @@ -117,11 +122,13 @@ #include #include +#include "clutter-alpha.h" #include "clutter-debug.h" #include "clutter-layout-manager.h" #include "clutter-layout-meta.h" #include "clutter-marshal.h" #include "clutter-private.h" +#include "clutter-timeline.h" #define LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED(m,method) G_STMT_START { \ GObject *_obj = G_OBJECT (m); \ @@ -141,7 +148,9 @@ G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager, clutter_layout_manager, G_TYPE_INITIALLY_UNOWNED); -static GQuark quark_layout_meta = 0; +static GQuark quark_layout_meta = 0; +static GQuark quark_layout_alpha = 0; + static guint manager_signals[LAST_SIGNAL] = { 0, }; static void @@ -217,17 +226,92 @@ layout_manager_real_get_child_meta_type (ClutterLayoutManager *manager) return G_TYPE_INVALID; } +static void +layout_manager_real_begin_animation (ClutterLayoutManager *manager, + guint duration, + gulong mode) +{ + ClutterTimeline *timeline; + ClutterAlpha *alpha; + + alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); + if (alpha != NULL) + return; + + timeline = clutter_timeline_new (duration); + alpha = clutter_alpha_new_full (timeline, mode); + g_object_unref (timeline); + + g_signal_connect_swapped (timeline, "completed", + G_CALLBACK (clutter_layout_manager_end_animation), + manager); + g_signal_connect_swapped (timeline, "new-frame", + G_CALLBACK (clutter_layout_manager_layout_changed), + manager); + + g_object_set_qdata_full (G_OBJECT (manager), + quark_layout_alpha, alpha, + (GDestroyNotify) g_object_unref); + + clutter_timeline_start (timeline); +} + +static gdouble +layout_manager_real_get_animation_progress (ClutterLayoutManager *manager) +{ + ClutterAlpha *alpha; + + alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); + if (alpha == NULL) + return 1.0; + + return clutter_alpha_get_alpha (alpha); +} + +static void +layout_manager_real_end_animation (ClutterLayoutManager *manager) +{ + ClutterTimeline *timeline; + ClutterAlpha *alpha; + + alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); + if (alpha == NULL) + return; + + timeline = clutter_alpha_get_timeline (alpha); + g_assert (timeline != NULL); + + if (clutter_timeline_is_playing (timeline)) + clutter_timeline_stop (timeline); + + g_signal_handlers_disconnect_by_func (timeline, + G_CALLBACK (clutter_layout_manager_end_animation), + manager); + g_signal_handlers_disconnect_by_func (timeline, + G_CALLBACK (clutter_layout_manager_layout_changed), + manager); + + g_object_set_qdata (G_OBJECT (manager), quark_layout_alpha, NULL); + + clutter_layout_manager_layout_changed (manager); +} + static void clutter_layout_manager_class_init (ClutterLayoutManagerClass *klass) { quark_layout_meta = g_quark_from_static_string ("clutter-layout-manager-child-meta"); + quark_layout_alpha = + g_quark_from_static_string ("clutter-layout-manager-alpha"); klass->get_preferred_width = layout_manager_real_get_preferred_width; klass->get_preferred_height = layout_manager_real_get_preferred_height; klass->allocate = layout_manager_real_allocate; klass->create_child_meta = layout_manager_real_create_child_meta; klass->get_child_meta_type = layout_manager_real_get_child_meta_type; + klass->begin_animation = layout_manager_real_begin_animation; + klass->get_animation_progress = layout_manager_real_get_animation_progress; + klass->end_animation = layout_manager_real_end_animation; /** * ClutterLayoutManager::layout-changed: @@ -905,3 +989,76 @@ clutter_layout_manager_list_child_properties (ClutterLayoutManager *manager, return pspecs; } + +/** + * clutter_layout_manager_begin_animation: + * @manager: a #ClutterLayoutManager + * @duration: the duration of the animation, in milliseconds + * @mode: the easing mode of the animation + * + * Begins an animation of @duration milliseconds, using the provided + * easing @mode + * + * The easing mode can be specified either as a #ClutterAnimationMode + * or as a logical id returned by clutter_alpha_register_func() + * + * The result of this function depends on the @manager implementation + * + * Since: 1.2 + */ +void +clutter_layout_manager_begin_animation (ClutterLayoutManager *manager, + guint duration, + gulong mode) +{ + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (duration > 0 && duration < 1000); + + CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager)->begin_animation (manager, + duration, + mode); +} + +/** + * clutter_layout_manager_end_animation: + * @manager: a #ClutterLayoutManager + * + * Ends an animation started by clutter_layout_manager_begin_animation() + * + * The result of this call depends on the @manager implementation + * + * Since: 1.2 + */ +void +clutter_layout_manager_end_animation (ClutterLayoutManager *manager) +{ + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + + CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager)->end_animation (manager); +} + +/** + * clutter_layout_manager_get_animation_progress: + * @manager: a #ClutterLayoutManager + * + * Retrieves the progress of the animation, if one has been started by + * clutter_layout_manager_begin_animation() + * + * The returned value has the same semantics of the #ClutterAlpha:alpha + * value + * + * Return value: the progress of the animation + * + * Since: 1.2 + */ +gdouble +clutter_layout_manager_get_animation_progress (ClutterLayoutManager *manager) +{ + ClutterLayoutManagerClass *klass; + + g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), 1.0); + + klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + + return klass->get_animation_progress (manager); +} diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h index 676837dea..1271a3db4 100644 --- a/clutter/clutter-layout-manager.h +++ b/clutter/clutter-layout-manager.h @@ -82,6 +82,12 @@ struct _ClutterLayoutManager * @create_child_meta: virtual function; override to create a * #ClutterLayoutMeta instance associated to a #ClutterContainer and a * child #ClutterActor, used to maintain layout manager specific properties + * @begin_animation: virtual function; override to control the animation + * of a #ClutterLayoutManager with the given duration and easing mode + * @end_animation: virtual function; override to end an animation started + * by clutter_layout_manager_begin_animation() + * @get_animation_progress: virtual function; override to control the + * progress of the animation of a #ClutterLayoutManager * @layout_changed: class handler for the #ClutterLayoutManager::layout-changed * signal * @@ -96,30 +102,38 @@ struct _ClutterLayoutManagerClass GInitiallyUnownedClass parent_class; /*< public >*/ - void (* get_preferred_width) (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_height, - gfloat *minimum_width_p, - gfloat *natural_width_p); - void (* get_preferred_height) (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_width, - gfloat *minimum_height_p, - gfloat *natural_height_p); - void (* allocate) (ClutterLayoutManager *manager, - ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags); + /* vfuncs, not signals */ + void (* get_preferred_width) (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *minimum_width_p, + gfloat *natural_width_p); + void (* get_preferred_height) (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *minimum_height_p, + gfloat *natural_height_p); + void (* allocate) (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags); - void (* set_container) (ClutterLayoutManager *manager, - ClutterContainer *container); + void (* set_container) (ClutterLayoutManager *manager, + ClutterContainer *container); - GType (* get_child_meta_type) (ClutterLayoutManager *manager); - ClutterLayoutMeta *(* create_child_meta) (ClutterLayoutManager *manager, - ClutterContainer *container, - ClutterActor *actor); + GType (* get_child_meta_type) (ClutterLayoutManager *manager); + ClutterLayoutMeta *(* create_child_meta) (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor); - void (* layout_changed) (ClutterLayoutManager *manager); + void (* begin_animation) (ClutterLayoutManager *manager, + guint duration, + gulong mode); + gdouble (* get_animation_progress) (ClutterLayoutManager *manager); + void (* end_animation) (ClutterLayoutManager *manager); + + /* signals */ + void (* layout_changed) (ClutterLayoutManager *manager); /*< private >*/ /* padding for future expansion */ @@ -184,6 +198,12 @@ void clutter_layout_manager_child_get_property (ClutterLayoutMa const gchar *property_name, GValue *value); +void clutter_layout_manager_begin_animation (ClutterLayoutManager *manager, + guint duration, + gulong mode); +void clutter_layout_manager_end_animation (ClutterLayoutManager *manager); +gdouble clutter_layout_manager_get_animation_progress (ClutterLayoutManager *manager); + G_END_DECLS #endif /* __CLUTTER_LAYOUT_MANAGER_H__ */ diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index d90358739..2bd5f55eb 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1768,6 +1768,11 @@ clutter_layout_manager_child_get_property clutter_layout_manager_find_child_property clutter_layout_manager_list_child_properties + +clutter_layout_manager_begin_animation +clutter_layout_manager_end_animation +clutter_layout_manager_get_animation_progress + CLUTTER_TYPE_LAYOUT_MANAGER CLUTTER_LAYOUT_MANAGER From 3c2e91aef586de5521aa9674b80d449cf96da0e1 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sat, 12 Dec 2009 00:13:05 +0000 Subject: [PATCH 3/9] box-layout: Animate layout properties Use the newly added animation support inside LayoutManager to animate between state changes of the BoxLayout properties. The implementation is based on equivalent code from Mx, written by: Thomas Wood --- clutter/clutter-box-layout.c | 134 ++++++++++++++++++++++++++++++----- 1 file changed, 115 insertions(+), 19 deletions(-) diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c index 6d30bfb93..49b31a686 100644 --- a/clutter/clutter-box-layout.c +++ b/clutter/clutter-box-layout.c @@ -94,8 +94,11 @@ struct _ClutterBoxLayoutPrivate guint spacing; + GHashTable *allocations; + guint is_vertical : 1; guint is_pack_start : 1; + guint is_animating : 1; }; struct _ClutterBoxChild @@ -169,6 +172,7 @@ box_child_set_align (ClutterBoxChild *self, ClutterLayoutManager *layout; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); + clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); clutter_layout_manager_layout_changed (layout); if (x_changed) @@ -205,6 +209,7 @@ box_child_set_fill (ClutterBoxChild *self, ClutterLayoutManager *layout; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); + clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); clutter_layout_manager_layout_changed (layout); if (x_changed) @@ -226,6 +231,7 @@ box_child_set_expand (ClutterBoxChild *self, self->expand = expand; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); + clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); clutter_layout_manager_layout_changed (layout); g_object_notify (G_OBJECT (self), "expand"); @@ -698,14 +704,6 @@ allocate_box_child (ClutterBoxLayout *self, child_box.x1 = 0; child_box.x2 = floorf (avail_width + 0.5); - - allocate_fill (child, &child_box, box_child); - clutter_actor_allocate (child, &child_box, flags); - - if (box_child->expand) - *position += (child_nat + priv->spacing + extra_space); - else - *position += (child_nat + priv->spacing); } else { @@ -721,15 +719,64 @@ allocate_box_child (ClutterBoxLayout *self, child_box.y1 = 0; child_box.y2 = floorf (avail_height + 0.5); - - allocate_fill (child, &child_box, box_child); - clutter_actor_allocate (child, &child_box, flags); - - if (box_child->expand) - *position += (child_nat + priv->spacing + extra_space); - else - *position += (child_nat + priv->spacing); } + + allocate_fill (child, &child_box, box_child); + + if (priv->is_animating) + { + ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self); + ClutterActorBox *start = NULL; + ClutterActorBox end = { 0, }; + gdouble p; + + p = clutter_layout_manager_get_animation_progress (manager); + + start = g_hash_table_lookup (priv->allocations, child); + if (start == NULL) + { + /* if there is no allocation available then the child has just + * been added to the container; we put it in the final state + * and store its allocation for later + */ + start = clutter_actor_box_copy (&child_box); + g_hash_table_insert (priv->allocations, child, start); + + goto do_allocate; + } + + end = child_box; + + /* interpolate between the initial and final values */ + clutter_actor_box_interpolate (start, &end, p, &child_box); + + CLUTTER_NOTE (ANIMATION, + "Animate { %.1f, %.1f, %.1f, %.1f }\t" + "%.3f * { %.1f, %.1f, %.1f, %.1f }\t" + "-> { %.1f, %.1f, %.1f, %.1f }", + start->x1, start->y1, + start->x2, start->y2, + p, + child_box.x1, child_box.y1, + child_box.x2, child_box.y2, + end.x1, end.y1, + end.x2, end.y2); + } + else + { + ClutterActorBox *start = clutter_actor_box_copy (&child_box); + + /* store the allocation for later animations */ + g_hash_table_replace (priv->allocations, child, start); + } + +do_allocate: + clutter_actor_allocate (child, &child_box, flags); + + if (box_child->expand) + *position += (child_nat + priv->spacing + extra_space); + else + *position += (child_nat + priv->spacing); } static void @@ -887,6 +934,37 @@ clutter_box_layout_allocate (ClutterLayoutManager *layout, g_list_free (children); } +static void +clutter_box_layout_begin_animation (ClutterLayoutManager *manager, + guint duration, + gulong easing) +{ + ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (manager)->priv; + ClutterLayoutManagerClass *parent_class; + + if (priv->is_animating) + return; + + priv->is_animating = TRUE; + + /* we want the default implementation */ + parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_box_layout_parent_class); + parent_class->begin_animation (manager, duration, easing); +} + +static void +clutter_box_layout_end_animation (ClutterLayoutManager *manager) +{ + ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (manager)->priv; + ClutterLayoutManagerClass *parent_class; + + priv->is_animating = FALSE; + + /* we want the default implementation */ + parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_box_layout_parent_class); + parent_class->end_animation (manager); +} + static void clutter_box_layout_set_property (GObject *gobject, guint prop_id, @@ -943,6 +1021,16 @@ clutter_box_layout_get_property (GObject *gobject, } } +static void +clutter_box_layout_finalize (GObject *gobject) +{ + ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (gobject)->priv; + + g_hash_table_destroy (priv->allocations); + + G_OBJECT_CLASS (clutter_box_layout_parent_class)->finalize (gobject); +} + static void clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) { @@ -954,6 +1042,7 @@ clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) gobject_class->set_property = clutter_box_layout_set_property; gobject_class->get_property = clutter_box_layout_get_property; + gobject_class->finalize = clutter_box_layout_finalize; layout_class->get_preferred_width = clutter_box_layout_get_preferred_width; @@ -963,6 +1052,8 @@ clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) layout_class->set_container = clutter_box_layout_set_container; layout_class->get_child_meta_type = clutter_box_layout_get_child_meta_type; + layout_class->begin_animation = clutter_box_layout_begin_animation; + layout_class->end_animation = clutter_box_layout_end_animation; g_type_class_add_private (klass, sizeof (ClutterBoxLayoutPrivate)); @@ -1022,6 +1113,11 @@ clutter_box_layout_init (ClutterBoxLayout *layout) priv->is_vertical = FALSE; priv->is_pack_start = FALSE; priv->spacing = 0; + + priv->allocations = + g_hash_table_new_full (NULL, NULL, + NULL, + (GDestroyNotify) clutter_actor_box_free); } /** @@ -1065,7 +1161,7 @@ clutter_box_layout_set_spacing (ClutterBoxLayout *layout, priv->spacing = spacing; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_layout_changed (manager); + clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); g_object_notify (G_OBJECT (layout), "spacing"); } @@ -1116,7 +1212,7 @@ clutter_box_layout_set_vertical (ClutterBoxLayout *layout, priv->is_vertical = vertical ? TRUE : FALSE; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_layout_changed (manager); + clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); g_object_notify (G_OBJECT (layout), "vertical"); } @@ -1170,7 +1266,7 @@ clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, priv->is_pack_start = pack_start ? TRUE : FALSE; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_layout_changed (manager); + clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); g_object_notify (G_OBJECT (layout), "pack-start"); } From 2e6397c39132007e79f5f87743fe9378fd8d5387 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 13 Dec 2009 01:07:44 +0000 Subject: [PATCH 4/9] box-layout: Add knobs for controlling animations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClutterLayoutManager does not have any state associated with it, and defers all the state to its sub-classes. The BoxLayout is thus in charge of controlling: • whether or not animations should be used • the duration of the animation • the easing mode of the animation By adding three new properties: • ClutterBoxLayout:use-animations • ClutterBoxLayout:easing-duration • ClutterBoxLayout:easing-mode And their relative accessors pairs we can make BoxLayout decide whether or not, and with which parameters, call the begin_animation() method of ClutterLayoutManager. The test-box-layout has been modified to reflect this new functionality, by checking the key-press event for the 'a' key symbol to toggle the use of animations. --- clutter/clutter-box-layout.c | 320 ++++++++++++++++++++- clutter/clutter-box-layout.h | 86 +++--- doc/reference/clutter/clutter-sections.txt | 8 + tests/interactive/test-box-layout.c | 6 + 4 files changed, 368 insertions(+), 52 deletions(-) diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c index 49b31a686..00b16256a 100644 --- a/clutter/clutter-box-layout.c +++ b/clutter/clutter-box-layout.c @@ -94,11 +94,14 @@ struct _ClutterBoxLayoutPrivate guint spacing; + gulong easing_mode; + guint easing_duration; GHashTable *allocations; - guint is_vertical : 1; - guint is_pack_start : 1; - guint is_animating : 1; + guint is_vertical : 1; + guint is_pack_start : 1; + guint is_animating : 1; + guint use_animations : 1; }; struct _ClutterBoxChild @@ -131,7 +134,10 @@ enum PROP_SPACING, PROP_VERTICAL, - PROP_PACK_START + PROP_PACK_START, + PROP_USE_ANIMATIONS, + PROP_EASING_MODE, + PROP_EASING_DURATION }; G_DEFINE_TYPE (ClutterBoxChild, @@ -170,10 +176,19 @@ box_child_set_align (ClutterBoxChild *self, if (x_changed || y_changed) { ClutterLayoutManager *layout; + ClutterBoxLayout *box; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); - clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); - clutter_layout_manager_layout_changed (layout); + box = CLUTTER_BOX_LAYOUT (layout); + + if (box->priv->use_animations) + { + clutter_layout_manager_begin_animation (layout, + box->priv->easing_duration, + box->priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (layout); if (x_changed) g_object_notify (G_OBJECT (self), "x-align"); @@ -207,10 +222,19 @@ box_child_set_fill (ClutterBoxChild *self, if (x_changed || y_changed) { ClutterLayoutManager *layout; + ClutterBoxLayout *box; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); - clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); - clutter_layout_manager_layout_changed (layout); + box = CLUTTER_BOX_LAYOUT (layout); + + if (box->priv->use_animations) + { + clutter_layout_manager_begin_animation (layout, + box->priv->easing_duration, + box->priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (layout); if (x_changed) g_object_notify (G_OBJECT (self), "x-fill"); @@ -227,12 +251,21 @@ box_child_set_expand (ClutterBoxChild *self, if (self->expand != expand) { ClutterLayoutManager *layout; + ClutterBoxLayout *box; self->expand = expand; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); - clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); - clutter_layout_manager_layout_changed (layout); + box = CLUTTER_BOX_LAYOUT (layout); + + if (box->priv->use_animations) + { + clutter_layout_manager_begin_animation (layout, + box->priv->easing_duration, + box->priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (layout); g_object_notify (G_OBJECT (self), "expand"); } @@ -723,7 +756,7 @@ allocate_box_child (ClutterBoxLayout *self, allocate_fill (child, &child_box, box_child); - if (priv->is_animating) + if (priv->use_animations && priv->is_animating) { ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self); ClutterActorBox *start = NULL; @@ -987,6 +1020,18 @@ clutter_box_layout_set_property (GObject *gobject, clutter_box_layout_set_pack_start (self, g_value_get_boolean (value)); break; + case PROP_USE_ANIMATIONS: + clutter_box_layout_set_use_animations (self, g_value_get_boolean (value)); + break; + + case PROP_EASING_MODE: + clutter_box_layout_set_easing_mode (self, g_value_get_ulong (value)); + break; + + case PROP_EASING_DURATION: + clutter_box_layout_set_easing_duration (self, g_value_get_uint (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -1015,6 +1060,18 @@ clutter_box_layout_get_property (GObject *gobject, g_value_set_boolean (value, priv->is_pack_start); break; + case PROP_USE_ANIMATIONS: + g_value_set_boolean (value, priv->use_animations); + break; + + case PROP_EASING_MODE: + g_value_set_ulong (value, priv->easing_mode); + break; + + case PROP_EASING_DURATION: + g_value_set_uint (value, priv->easing_duration); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -1101,6 +1158,62 @@ clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) 0, G_MAXUINT, 0, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_SPACING, pspec); + + /** + * ClutterBoxLayout:use-animations: + * + * Whether the #ClutterBoxLayout should animate changes in the + * layout properties + * + * Since: 1.2 + */ + pspec = g_param_spec_boolean ("use-animations", + "Use Animations", + "Whether layout changes should be animated", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_USE_ANIMATIONS, pspec); + + /** + * ClutterBoxLayout:easing-mode: + * + * The easing mode for the animations, in case + * #ClutterBoxLayout:use-animations is set to %TRUE + * + * The easing mode has the same semantics of #ClutterAnimation:mode: it can + * either be a value from the #ClutterAnimationMode enumeration, like + * %CLUTTER_EASE_OUT_CUBIC, or a logical id as returned by + * clutter_alpha_register_func() + * + * The default value is %CLUTTER_EASE_OUT_CUBIC + * + * Since: 1.2 + */ + pspec = g_param_spec_ulong ("easing-mode", + "Easing Mode", + "The easing mode of the animations", + 0, G_MAXULONG, + CLUTTER_EASE_OUT_CUBIC, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_EASING_MODE, pspec); + + /** + * ClutterBoxLayout:easing-duration: + * + * The duration of the animations, in case #ClutterBoxLayout:use-animations + * is set to %TRUE + * + * The duration is expressed in milliseconds + * + * Since: 1.2 + */ + pspec = g_param_spec_uint ("easing-duration", + "Easing Duration", + "The duration of the animations", + 0, G_MAXUINT, + 500, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_EASING_DURATION, pspec); } static void @@ -1114,6 +1227,10 @@ clutter_box_layout_init (ClutterBoxLayout *layout) priv->is_pack_start = FALSE; priv->spacing = 0; + priv->use_animations = FALSE; + priv->easing_mode = CLUTTER_EASE_OUT_CUBIC; + priv->easing_duration = 500; + priv->allocations = g_hash_table_new_full (NULL, NULL, NULL, @@ -1161,7 +1278,15 @@ clutter_box_layout_set_spacing (ClutterBoxLayout *layout, priv->spacing = spacing; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); + + if (priv->use_animations) + { + clutter_layout_manager_begin_animation (manager, + priv->easing_duration, + priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (layout), "spacing"); } @@ -1212,7 +1337,15 @@ clutter_box_layout_set_vertical (ClutterBoxLayout *layout, priv->is_vertical = vertical ? TRUE : FALSE; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); + + if (priv->use_animations) + { + clutter_layout_manager_begin_animation (manager, + priv->easing_duration, + priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (layout), "vertical"); } @@ -1266,7 +1399,15 @@ clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, priv->is_pack_start = pack_start ? TRUE : FALSE; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); + + if (priv->use_animations) + { + clutter_layout_manager_begin_animation (manager, + priv->easing_duration, + priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (layout), "pack-start"); } @@ -1670,3 +1811,154 @@ clutter_box_layout_get_expand (ClutterBoxLayout *layout, return CLUTTER_BOX_CHILD (meta)->expand; } + +/** + * clutter_box_layout_set_use_animations: + * @layout: a #ClutterBoxLayout + * @animate: %TRUE if the @layout should use animations + * + * Sets whether @layout should animate changes in the layout properties + * + * The duration of the animations is controlled by + * clutter_box_layout_set_easing_duration(); the easing mode to be used + * by the animations is controlled by clutter_box_layout_set_easing_mode() + * + * Since: 1.2 + */ +void +clutter_box_layout_set_use_animations (ClutterBoxLayout *layout, + gboolean animate) +{ + ClutterBoxLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->use_animations != animate) + { + priv->use_animations = animate; + + g_object_notify (G_OBJECT (layout), "use-animations"); + } +} + +/** + * clutter_box_layout_get_use_animations: + * @layout: a #ClutterBoxLayout + * + * Retrieves whether @layout should animate changes in the layout properties + * + * Since clutter_box_layout_set_use_animations() + * + * Return value: %TRUE if the animations should be used, %FALSE otherwise + * + * Since: 1.2 + */ +gboolean +clutter_box_layout_get_use_animations (ClutterBoxLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE); + + return layout->priv->use_animations; +} + +/** + * clutter_box_layout_set_easing_mode: + * @layout: a #ClutterBoxLayout + * @mode: an easing mode, either from #ClutterAnimationMode or a logical id + * from clutter_alpha_register_func() + * + * Sets the easing mode to be used by @layout when animating changes in layout + * properties + * + * Use clutter_box_layout_set_use_animations() to enable and disable the + * animations + * + * Since: 1.2 + */ +void +clutter_box_layout_set_easing_mode (ClutterBoxLayout *layout, + gulong mode) +{ + ClutterBoxLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->easing_mode != mode) + { + priv->easing_mode = mode; + + g_object_notify (G_OBJECT (layout), "easing-mode"); + } +} + +/** + * clutter_box_layout_get_easing_mode: + * @layout: a #ClutterBoxLayout + * + * Retrieves the easing mode set using clutter_box_layout_set_easing_mode() + * + * Return value: an easing mode + * + * Since: 1.2 + */ +gulong +clutter_box_layout_get_easing_mode (ClutterBoxLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), + CLUTTER_EASE_OUT_CUBIC); + + return layout->priv->easing_mode; +} + +/** + * clutter_box_layout_set_easing_duration: + * @layout: a #ClutterBoxLayout + * @msecs: the duration of the animations, in milliseconds + * + * Sets the duration of the animations used by @layout when animating changes + * in the layout properties + * + * Use clutter_box_layout_set_use_animations() to enable and disable the + * animations + * + * Since: 1.2 + */ +void +clutter_box_layout_set_easing_duration (ClutterBoxLayout *layout, + guint msecs) +{ + ClutterBoxLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->easing_duration != msecs) + { + priv->easing_duration = msecs; + + g_object_notify (G_OBJECT (layout), "easing-duration"); + } +} + +/** + * clutter_box_layout_get_easing_duration: + * @layout: a #ClutterBoxLayout + * + * Retrieves the duration set using clutter_box_layout_set_easing_duration() + * + * Return value: the duration of the animations, in milliseconds + * + * Since: 1.2 + */ +guint +clutter_box_layout_get_easing_duration (ClutterBoxLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), 500); + + return layout->priv->easing_duration; +} diff --git a/clutter/clutter-box-layout.h b/clutter/clutter-box-layout.h index be2be9130..035368ac2 100644 --- a/clutter/clutter-box-layout.h +++ b/clutter/clutter-box-layout.h @@ -97,46 +97,56 @@ struct _ClutterBoxLayoutClass GType clutter_box_layout_get_type (void) G_GNUC_CONST; -ClutterLayoutManager *clutter_box_layout_new (void); +ClutterLayoutManager *clutter_box_layout_new (void); -void clutter_box_layout_set_spacing (ClutterBoxLayout *layout, - guint spacing); -guint clutter_box_layout_get_spacing (ClutterBoxLayout *layout); -void clutter_box_layout_set_vertical (ClutterBoxLayout *layout, - gboolean vertical); -gboolean clutter_box_layout_get_vertical (ClutterBoxLayout *layout); -void clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, - gboolean pack_start); -gboolean clutter_box_layout_get_pack_start (ClutterBoxLayout *layout); +void clutter_box_layout_set_spacing (ClutterBoxLayout *layout, + guint spacing); +guint clutter_box_layout_get_spacing (ClutterBoxLayout *layout); +void clutter_box_layout_set_vertical (ClutterBoxLayout *layout, + gboolean vertical); +gboolean clutter_box_layout_get_vertical (ClutterBoxLayout *layout); +void clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, + gboolean pack_start); +gboolean clutter_box_layout_get_pack_start (ClutterBoxLayout *layout); -void clutter_box_layout_pack (ClutterBoxLayout *layout, - ClutterActor *actor, - gboolean expand, - gboolean x_fill, - gboolean y_fill, - ClutterBoxAlignment x_align, - ClutterBoxAlignment y_align); -void clutter_box_layout_set_alignment (ClutterBoxLayout *layout, - ClutterActor *actor, - ClutterBoxAlignment x_align, - ClutterBoxAlignment y_align); -void clutter_box_layout_get_alignment (ClutterBoxLayout *layout, - ClutterActor *actor, - ClutterBoxAlignment *x_align, - ClutterBoxAlignment *y_align); -void clutter_box_layout_set_fill (ClutterBoxLayout *layout, - ClutterActor *actor, - gboolean x_fill, - gboolean y_fill); -void clutter_box_layout_get_fill (ClutterBoxLayout *layout, - ClutterActor *actor, - gboolean *x_fill, - gboolean *y_fill); -void clutter_box_layout_set_expand (ClutterBoxLayout *layout, - ClutterActor *actor, - gboolean expand); -gboolean clutter_box_layout_get_expand (ClutterBoxLayout *layout, - ClutterActor *actor); +void clutter_box_layout_pack (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean expand, + gboolean x_fill, + gboolean y_fill, + ClutterBoxAlignment x_align, + ClutterBoxAlignment y_align); +void clutter_box_layout_set_alignment (ClutterBoxLayout *layout, + ClutterActor *actor, + ClutterBoxAlignment x_align, + ClutterBoxAlignment y_align); +void clutter_box_layout_get_alignment (ClutterBoxLayout *layout, + ClutterActor *actor, + ClutterBoxAlignment *x_align, + ClutterBoxAlignment *y_align); +void clutter_box_layout_set_fill (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean x_fill, + gboolean y_fill); +void clutter_box_layout_get_fill (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean *x_fill, + gboolean *y_fill); +void clutter_box_layout_set_expand (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean expand); +gboolean clutter_box_layout_get_expand (ClutterBoxLayout *layout, + ClutterActor *actor); + +void clutter_box_layout_set_use_animations (ClutterBoxLayout *layout, + gboolean animate); +gboolean clutter_box_layout_get_use_animations (ClutterBoxLayout *layout); +void clutter_box_layout_set_easing_mode (ClutterBoxLayout *layout, + gulong mode); +gulong clutter_box_layout_get_easing_mode (ClutterBoxLayout *layout); +void clutter_box_layout_set_easing_duration (ClutterBoxLayout *layout, + guint msecs); +guint clutter_box_layout_get_easing_duration (ClutterBoxLayout *layout); G_END_DECLS diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 2bd5f55eb..8ad28f785 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1937,6 +1937,14 @@ clutter_box_layout_get_expand clutter_box_layout_set_fill clutter_box_layout_get_fill + +clutter_box_layout_set_use_animations +clutter_box_layout_get_use_animations +clutter_box_layout_set_easing_duration +clutter_box_layout_get_easing_duration +clutter_box_layout_set_easing_mode +clutter_box_layout_get_easing_mode + CLUTTER_TYPE_BOX_LAYOUT CLUTTER_BOX_LAYOUT diff --git a/tests/interactive/test-box-layout.c b/tests/interactive/test-box-layout.c index e45631880..a8b72453a 100644 --- a/tests/interactive/test-box-layout.c +++ b/tests/interactive/test-box-layout.c @@ -29,6 +29,7 @@ "Press v\t\342\236\236\tSwitch horizontal/vertical\n" \ "Press p\t\342\236\236\tSwitch pack start/end\n" \ "Press s\t\342\236\236\tIncrement spacing (up to 12px)\n" \ + "Press a\t\342\236\236\tSwitch animations on/off\n" \ "Press q\t\342\236\236\tQuit" static ClutterActor *hover_actor = NULL; @@ -167,6 +168,11 @@ key_release_cb (ClutterActor *actor, switch (clutter_event_get_key_symbol (event)) { + case CLUTTER_a: + toggle = clutter_box_layout_get_use_animations (layout); + clutter_box_layout_set_use_animations (layout, !toggle); + break; + case CLUTTER_v: toggle = clutter_box_layout_get_vertical (layout); clutter_box_layout_set_vertical (layout, !toggle); From 4a21425f4837798d01f4435b68582faf9029c61c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 13 Dec 2009 01:23:54 +0000 Subject: [PATCH 5/9] layout: Let begin_animation() return the Alpha When beginning a new animation for a LayoutManager, the implementation should return the ClutterAlpha used. This allows controlling the timeline and/or modifying the animation parameters on the fly. --- clutter/clutter-box-layout.c | 5 +++-- clutter/clutter-layout-manager.c | 30 ++++++++++++++++++++++-------- clutter/clutter-layout-manager.h | 5 +++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c index 00b16256a..f9a7dbb0d 100644 --- a/clutter/clutter-box-layout.c +++ b/clutter/clutter-box-layout.c @@ -967,7 +967,7 @@ clutter_box_layout_allocate (ClutterLayoutManager *layout, g_list_free (children); } -static void +static ClutterAlpha * clutter_box_layout_begin_animation (ClutterLayoutManager *manager, guint duration, gulong easing) @@ -982,7 +982,8 @@ clutter_box_layout_begin_animation (ClutterLayoutManager *manager, /* we want the default implementation */ parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_box_layout_parent_class); - parent_class->begin_animation (manager, duration, easing); + + return parent_class->begin_animation (manager, duration, easing); } static void diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 251f3e647..35b624f1d 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -226,7 +226,7 @@ layout_manager_real_get_child_meta_type (ClutterLayoutManager *manager) return G_TYPE_INVALID; } -static void +static ClutterAlpha * layout_manager_real_begin_animation (ClutterLayoutManager *manager, guint duration, gulong mode) @@ -236,7 +236,14 @@ layout_manager_real_begin_animation (ClutterLayoutManager *manager, alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); if (alpha != NULL) - return; + { + timeline = clutter_alpha_get_timeline (alpha); + clutter_timeline_set_duration (timeline, duration); + + clutter_alpha_set_mode (alpha, mode); + + return alpha; + }; timeline = clutter_timeline_new (duration); alpha = clutter_alpha_new_full (timeline, mode); @@ -254,6 +261,8 @@ layout_manager_real_begin_animation (ClutterLayoutManager *manager, (GDestroyNotify) g_object_unref); clutter_timeline_start (timeline); + + return alpha; } static gdouble @@ -1004,19 +1013,24 @@ clutter_layout_manager_list_child_properties (ClutterLayoutManager *manager, * * The result of this function depends on the @manager implementation * + * Return value: (transfer none): The #ClutterAlpha created by the + * layout manager; the returned instance is owned by the layout + * manager and should not be unreferenced + * * Since: 1.2 */ -void +ClutterAlpha * clutter_layout_manager_begin_animation (ClutterLayoutManager *manager, guint duration, gulong mode) { - g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); - g_return_if_fail (duration > 0 && duration < 1000); + ClutterLayoutManagerClass *klass; - CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager)->begin_animation (manager, - duration, - mode); + g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL); + + klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + + return klass->begin_animation (manager, duration, mode); } /** diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h index 1271a3db4..e249b6b1a 100644 --- a/clutter/clutter-layout-manager.h +++ b/clutter/clutter-layout-manager.h @@ -30,6 +30,7 @@ #define __CLUTTER_LAYOUT_MANAGER_H__ #include +#include #include #include @@ -126,7 +127,7 @@ struct _ClutterLayoutManagerClass ClutterContainer *container, ClutterActor *actor); - void (* begin_animation) (ClutterLayoutManager *manager, + ClutterAlpha * (* begin_animation) (ClutterLayoutManager *manager, guint duration, gulong mode); gdouble (* get_animation_progress) (ClutterLayoutManager *manager); @@ -198,7 +199,7 @@ void clutter_layout_manager_child_get_property (ClutterLayoutMa const gchar *property_name, GValue *value); -void clutter_layout_manager_begin_animation (ClutterLayoutManager *manager, +ClutterAlpha * clutter_layout_manager_begin_animation (ClutterLayoutManager *manager, guint duration, gulong mode); void clutter_layout_manager_end_animation (ClutterLayoutManager *manager); From c8c5e0ee39626d434c2d3e5486c6520eff17f519 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 22 Dec 2009 18:10:39 +0000 Subject: [PATCH 6/9] docs: Clean up the README file --- README | 137 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 54 deletions(-) diff --git a/README b/README index bab7a3941..7f38af7f7 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -Clutter 1.2 README -================== +Clutter - README +=============================================================================== Clutter is an open source software library for creating fast, visually rich and animated graphical user interfaces. @@ -22,7 +22,6 @@ If you are building the Introspection data you will also need: • GObject-Introspection >= 0.6.4 - The official website is: http://www.clutter-project.org @@ -42,85 +41,115 @@ Clutter is licensed under the terms of the GNU Lesser General Public License, version 2.1 or (at your option) later. INSTALLATION -============ +=============================================================================== See the INSTALL file. Info on specific Clutter options; --enable-debug=[no/minimum/yes] - Controls Clutter debugging level (default=yes): - yes: All glib asserts, checks and runtime clutter verbose messages. - minimum: Just glib cast checks and runtime clutter verbose messagaes. - no: No glib asserts or checks and no runtime clutter verbose messages - (Only really of use in extreme performance cases) + Controls Clutter debugging level: + + yes: + All GLib asserts, checks and support for runtime Clutter + debugging notes through CLUTTER_DEBUG. This is the default + value for snapshots. + + minimum: + Just GType cast checks and support for runtime Clutter + debugging notes through CLUTTER_DEBUG. This is the default + for stable releases. + + no: + No GLib asserts or checks and no support for runtime Clutter + debugging notes. Only use in extreme performance and/or size + optimization cases. --enable-cogl-debug=[no/minimum/yes] Controls COGL debugging level (default=minimum): - yes: All runtime verbose messages and error checking for each GL - primitive - minimum: All runtime verbose messages - no: No error checking and no messages + + yes: + Support for COGL debugging notes through COGL_DEBUG and + error checking for each GL primitive. + + minimum: + Support for COGL debugging notes through COGL_DEBUG. This is + the default for snapshots. + + no: + Disable support for COGL runtime debugging notes. This is + the default for stable releases. --enable-maintainer-flags=[no/yes] - Use strict compiler flags (default=no) + Use strict compiler flags. This defaults to 'yes' for snapshots and + to 'no' for stable releases. --enable-gtk-doc use gtk-doc to build API documentation (default=no). Requires gtk-doc - present on system + present on the target system. - --enable-manual=[no/yes] - Build application developers manual. Requires jw and xmlto binaries. - Presently incomplete. + --enable-docs=[no/yes] + Build additional documentation. Requires xsltproc for DocBook + conversion, and optionally jw for PDF generation. - --with-flavour=[glx/eglx/eglnative/sdl/osx/win32/fruity] + --with-flavour=[glx/eglx/eglnative/sdl/osx/win32/fruity] Select the Clutter backend: (default=glx) - glx: Fully featured GLX backend. Using Open GL. + glx: + Fully featured GLX backend. Using Open GL. - eglx: EGL/Open GL ES backend for EGL on X windows implementations + eglx: + EGL/Open GL ES backend for EGL on X windows implementations - eglnative: + eglnative: EGL/Open GL ES backend on 'native windowing system' - i.e raw framebuffer. Expects the EGL implementation to provide a createNativeWindow() call. Also it optionally supports tslib for touchscreen events. - sdl: Basic SDL backend, using Open GL. Should provide portability + sdl: + Basic SDL backend, using Open GL. Should provide portability to Windows and possibly other OS's. (DEPRECATED) - osx: OS X backend. (EXPERIMENTAL) + osx: + OS X backend. (EXPERIMENTAL) - win32: + win32: Microsoft Windows(tm) WGL backend - fruity: + fruity: Apple iPod Touch(tm)/iPhone(tm) backend (EXPERIMENTAL) - --with-imagebackend=[gdk-pixbuf/quartz/internal] + --with-imagebackend=[gdk-pixbuf/quartz/internal] Select the image loading backend used by COGL - gdk-pixbuf: Depend on gdk-pixbuf-2.0 (default for the glx, eglx, - eglnative, sdl, win32 flavours and recommended) + gdk-pixbuf: + Depend on gdk-pixbuf-2.0 (default for the glx, eglx, + eglnative, sdl, win32 flavours and recommended) - quartz: Depend on CoreGraphics (default for the osx flavour) + quartz: + Depend on CoreGraphics (default for the osx flavour) - internal: Internal JPEG and PNG loader. Should only be used - for testing on new platforms + internal: + Internal JPEG and PNG loader. Should only be used + for testing on new platforms - --with-gles=[1.1/2.0] + --with-gles=[1.1/2.0] Select the GLES version (for EGL backends) (default=1.1) - --with-json=[internal/check/system] + --with-json=[internal/check/system] Select the JSON-GLib copy to use (default=check) - internal: Use the internal copy of JSON-GLib for ClutterScript + internal: + Use the internal copy of JSON-GLib for ClutterScript - check: Check for the existence of a system copy of JSON-GLib - and if it is available, make Clutter depend on it + check: + Check for the existence of a system copy of JSON-GLib + and if it is available, make Clutter depend on it - system: Only use the system copy of JSON-GLib + system: + Only use the system copy of JSON-GLib VERSIONING -========== +=============================================================================== Clutter uses the common "Linux kernel" versioning system, where even-numbered minor versions are stable and odd-numbered minor @@ -137,7 +166,7 @@ numbers are only used for released archives; odd micro numbers are only used on the SVN repository. HACKING -======= +=============================================================================== If you want to hack on and improve Clutter, check the contained TODO file for pending tasks, the HACKING file for general implementation guidelines, @@ -147,7 +176,7 @@ used throughout Clutter. Remember: the coding style is mandatory; patches not conforming to it will be rejected. BUGS -==== +=============================================================================== Bugs should be reported to the OpenedHand Bugzilla at: @@ -171,8 +200,8 @@ behaviour. If the bug exposes a crash, the exact text printed out and a stack trace obtained using gdb are greatly appreciated. -PATCHES -======= +CONTRIBUTING +=============================================================================== Patches should be submitted using Bugzilla. Patches fixing a bug should be attached to the bug report; patches for new features or for fixing bugs not @@ -199,13 +228,13 @@ If you do not intend to waive your copyright you should contact the Clutter development team to arrange a suitable solution. RELEASE NOTES -============= +=============================================================================== Relevant information for developers with existing Clutter applications wanting to port to newer releases (See NEWS for general new feature info). Release Notes for Clutter 1.2 -------------------------------- +------------------------------------------------------------------------------- * ClutterStageManager is now publicly available and documented API. @@ -213,7 +242,7 @@ Release Notes for Clutter 1.2 back to the internal copy only if JSON-GLib is not installed. Cogl API changes for Clutter 1.2 --------------------------------- +------------------------------------------------------------------------------- * cogl_viewport is now deprecated in favour of cogl_set_viewport which accepts a viewport offset. @@ -250,7 +279,7 @@ Cogl API changes for Clutter 1.2 Release Notes for Clutter 1.0 -------------------------------- +------------------------------------------------------------------------------- * The clutter_actor_set_shader_param() function now takes a GValue, which can be set using the clutter_value_set_shader() @@ -365,8 +394,8 @@ Release Notes for Clutter 1.0 takes the duration of the timeline in milliseconds, and thus it replaces the clutter_timeline_new_for_duration() variant. - Cogl API changes for Clutter 1.0 - -------------------------------- +Cogl API changes for Clutter 1.0 +------------------------------------------------------------------------------- * All drawing functions now use a source material to determine how geometry is filled. The source material is set via cogl_set_source. Or the convenience @@ -473,7 +502,7 @@ Release Notes for Clutter 1.0 a corresponding cogl_get_depth_test_enabled function has been added. Release Notes for Clutter 0.8 -------------------------------- +------------------------------------------------------------------------------- * The COGL GL wrapper API has been completely overhauled and now contains many new features including new greatly improved texture @@ -639,7 +668,7 @@ Release Notes for Clutter 0.8 * ClutterContainer can have per child custom properties via ClutterChildMeta. Release Notes for Clutter 0.6 -------------------------------- +------------------------------------------------------------------------------- * Now that every actor has events, the class signal handlers have been removed from ClutterStageClass and moved into ClutterActorClass. @@ -728,7 +757,7 @@ Release Notes for Clutter 0.6 respectively. Release Notes for Clutter 0.4.0 -------------------------------- +------------------------------------------------------------------------------- * clutter_actor_show_all does not recurse for groups at least (this is to match the original group_show_all behaviour). This is like 0.3 but was @@ -747,7 +776,7 @@ Release Notes for Clutter 0.4.0 overhauled. Release Notes for Clutter 0.3.1 -------------------------------- +------------------------------------------------------------------------------- * clutter_actor_apply_transform_to_point() parameters changed to use ClutterVertices. @@ -758,7 +787,7 @@ Release Notes for Clutter 0.3.1 * Exisiting X11 based egl backend public API calls now prefixed eglx. Release Notes for Clutter 0.3 ------------------------------ +------------------------------------------------------------------------------- * ClutterTexture changes: + clutter_texture_set_pixbuf() now takes a GError paremeter. From b9c1de0ec7001ce6a6b62dbd8d1fd9ee15539093 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 23 Dec 2009 08:34:08 +0000 Subject: [PATCH 7/9] box-layout: Remove the allocations hash table The BoxLayout uses a HashTable to map the latest stable allocation of each child, in order to use that as the initial value during an animation; this in spite of already having a perfectly valid per-child storage as part of the layout manager: ClutterBoxChild. The last stable allocation should be stored inside the ClutterBoxChild instead of having it in the private data for ClutterBoxLayout. The access remains O(1), since there is a 1:1 mapping between child and BoxChild instances, but we save a little bit of memory and we avoid keeping aroud allocations for old children. --- clutter/clutter-box-layout.c | 47 ++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c index f9a7dbb0d..90aad9837 100644 --- a/clutter/clutter-box-layout.c +++ b/clutter/clutter-box-layout.c @@ -96,7 +96,6 @@ struct _ClutterBoxLayoutPrivate gulong easing_mode; guint easing_duration; - GHashTable *allocations; guint is_vertical : 1; guint is_pack_start : 1; @@ -115,6 +114,11 @@ struct _ClutterBoxChild guint y_fill : 1; guint expand : 1; + + /* the last stable allocation before an animation; it is + * used as the initial ActorBox when interpolating + */ + ClutterActorBox *last_allocation; }; enum @@ -351,6 +355,16 @@ clutter_box_child_get_property (GObject *gobject, } } +static void +clutter_box_child_finalize (GObject *gobject) +{ + ClutterBoxChild *self = CLUTTER_BOX_CHILD (gobject); + + clutter_actor_box_free (self->last_allocation); + + G_OBJECT_CLASS (clutter_box_child_parent_class)->finalize (gobject); +} + static void clutter_box_child_class_init (ClutterBoxChildClass *klass) { @@ -359,6 +373,7 @@ clutter_box_child_class_init (ClutterBoxChildClass *klass) gobject_class->set_property = clutter_box_child_set_property; gobject_class->get_property = clutter_box_child_get_property; + gobject_class->finalize = clutter_box_child_finalize; pspec = g_param_spec_boolean ("expand", "Expand", @@ -413,6 +428,8 @@ clutter_box_child_init (ClutterBoxChild *self) self->x_fill = self->y_fill = FALSE; self->expand = FALSE; + + self->last_allocation = NULL; } static inline void @@ -765,15 +782,14 @@ allocate_box_child (ClutterBoxLayout *self, p = clutter_layout_manager_get_animation_progress (manager); - start = g_hash_table_lookup (priv->allocations, child); + start = box_child->last_allocation; if (start == NULL) { /* if there is no allocation available then the child has just * been added to the container; we put it in the final state * and store its allocation for later */ - start = clutter_actor_box_copy (&child_box); - g_hash_table_insert (priv->allocations, child, start); + box_child->last_allocation = clutter_actor_box_copy (&child_box); goto do_allocate; } @@ -797,10 +813,8 @@ allocate_box_child (ClutterBoxLayout *self, } else { - ClutterActorBox *start = clutter_actor_box_copy (&child_box); - /* store the allocation for later animations */ - g_hash_table_replace (priv->allocations, child, start); + box_child->last_allocation = clutter_actor_box_copy (&child_box); } do_allocate: @@ -975,9 +989,6 @@ clutter_box_layout_begin_animation (ClutterLayoutManager *manager, ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (manager)->priv; ClutterLayoutManagerClass *parent_class; - if (priv->is_animating) - return; - priv->is_animating = TRUE; /* we want the default implementation */ @@ -1079,16 +1090,6 @@ clutter_box_layout_get_property (GObject *gobject, } } -static void -clutter_box_layout_finalize (GObject *gobject) -{ - ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (gobject)->priv; - - g_hash_table_destroy (priv->allocations); - - G_OBJECT_CLASS (clutter_box_layout_parent_class)->finalize (gobject); -} - static void clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) { @@ -1100,7 +1101,6 @@ clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) gobject_class->set_property = clutter_box_layout_set_property; gobject_class->get_property = clutter_box_layout_get_property; - gobject_class->finalize = clutter_box_layout_finalize; layout_class->get_preferred_width = clutter_box_layout_get_preferred_width; @@ -1231,11 +1231,6 @@ clutter_box_layout_init (ClutterBoxLayout *layout) priv->use_animations = FALSE; priv->easing_mode = CLUTTER_EASE_OUT_CUBIC; priv->easing_duration = 500; - - priv->allocations = - g_hash_table_new_full (NULL, NULL, - NULL, - (GDestroyNotify) clutter_actor_box_free); } /** From 713c2952410e99e681825dac51da3e0b575ef2cf Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 23 Dec 2009 08:39:16 +0000 Subject: [PATCH 8/9] layout-manager: Rewind the timeline in begin_animation() If the default implementation begin_animation() is called twice then we should rewind the timeline, as well as updating its duration and the easing mode of the alpha. --- clutter/clutter-layout-manager.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 35b624f1d..77efdd96c 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -237,16 +237,20 @@ layout_manager_real_begin_animation (ClutterLayoutManager *manager, alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); if (alpha != NULL) { + clutter_alpha_set_mode (alpha, mode); + timeline = clutter_alpha_get_timeline (alpha); clutter_timeline_set_duration (timeline, duration); - - clutter_alpha_set_mode (alpha, mode); + clutter_timeline_rewind (timeline); return alpha; }; timeline = clutter_timeline_new (duration); + alpha = clutter_alpha_new_full (timeline, mode); + + /* let the alpha take ownership of the timeline */ g_object_unref (timeline); g_signal_connect_swapped (timeline, "completed", From 934eee17ae34098773ff5876747061b5a1d1f1a5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 23 Dec 2009 10:35:16 +0000 Subject: [PATCH 9/9] layout-manager: Document the animation support Add a section inside the LayoutManager class API reference documenting, with examples, how to implement animation support inside a layout manager sub-class. --- clutter/clutter-layout-manager.c | 213 +++++++++++++++++++++++++++---- 1 file changed, 190 insertions(+), 23 deletions(-) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 77efdd96c..850c3916a 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -36,10 +36,10 @@ * a generic container using #ClutterLayoutManager called #ClutterBox. * * Clutter provides some simple #ClutterLayoutManager sub-classes, like - * #ClutterFixedLayout and #ClutterBinLayout. + * #ClutterFlowLayout and #ClutterBinLayout. * * - * Using ClutterLayoutManager inside an Actor + * Using a Layout Manager inside an Actor * In order to use a #ClutterLayoutManager inside a #ClutterActor * sub-class you should invoke clutter_layout_manager_get_preferred_width() * inside the ClutterActor::get_preferred_width() @@ -56,10 +56,10 @@ * does not need to perform specific operations whenever a layout * manager changes: * - * g_signal_connect_swapped (layout_manager, - * "layout-changed", - * G_CALLBACK (clutter_actor_queue_relayout), - * actor); + * g_signal_connect_swapped (layout_manager, + * "layout-changed", + * G_CALLBACK (clutter_actor_queue_relayout), + * actor); * * * @@ -70,18 +70,20 @@ * #ClutterActor, so you should read the relative documentation * for subclassing * ClutterActor. - * The layout manager implementation can hold a back reference - * to the #ClutterContainer by implementing the set_container() - * virtual function. The layout manager should not hold a reference - * on the container actor, to avoid reference cycles. + * The layout manager implementation can hold a back pointer + * to the #ClutterContainer by implementing the + * set_container() virtual function. The layout manager + * should not hold a real reference (i.e. call g_object_ref()) on the + * container actor, to avoid reference cycles. * If the layout manager has properties affecting the layout * policies then it should emit the #ClutterLayoutManager::layout-changed * signal on itself by using the clutter_layout_manager_layout_changed() - * function. + * function whenever one of these properties changes. * If the layout manager has layout properties, that is properties that * should exist only as the result of the presence of a specific (layout - * manager, container actor, child actor) combination, then it should - * override the ClutterLayoutManager::get_child_meta_type() + * manager, container actor, child actor) combination, and it wishes to store + * those properties inside a #ClutterLayoutMeta then it should override the + * ClutterLayoutManager::get_child_meta_type() * virtual function to return the #GType of the #ClutterLayoutMeta sub-class * used to store the layout properties; optionally, the #ClutterLayoutManager * sub-class might also override the @@ -89,17 +91,17 @@ * function to control how the #ClutterLayoutMeta instance is created, * otherwise the default implementation will be equivalent to: * - * ClutterLayoutManagerClass *klass; - * GType meta_type; + * ClutterLayoutManagerClass *klass; + * GType meta_type; * - * klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); - * meta_type = klass->get_child_meta_type (manager); + * klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + * meta_type = klass->get_child_meta_type (manager); * - * return g_object_new (meta_type, - * "manager", manager, - * "container", container, - * "actor", actor, - * NULL); + * return g_object_new (meta_type, + * "manager", manager, + * "container", container, + * "actor", actor, + * NULL); * * Where manager is the #ClutterLayoutManager, * container is the #ClutterContainer using the @@ -109,7 +111,172 @@ * * * Animating a ClutterLayoutManager - * ... + * A layout manager is used to let a #ClutterContainer take complete + * ownership over the layout (that is: the position and sizing) of its + * children; this means that using the Clutter animation API, like + * clutter_actor_animate(), to animate the position and sizing of a child of + * a layout manager it is not going to work properly, as the animation will + * automatically override any setting done by the layout manager + * itself. + * It is possible for a #ClutterLayoutManager sub-class to animate its + * children layout by using the base class animation support. The + * #ClutterLayoutManager animation support consists of three virtual + * functions: begin_animation(), + * get_animation_progress() and + * end_animation(). + * + * + * begin_animation (duration, easing) + * This virtual function is invoked when the layout + * manager should begin an animation. The implementation should set up + * the state for the animation and create the ancillary objects for + * animating the layout. The default implementation creates a + * #ClutterTimeline for the given duration and a #ClutterAlpha binding + * the timeline to the given easing mode. This function returns a + * #ClutterAlpha which should be used to control the animation from + * the caller perspective. + * + * + * get_animation_progress() + * This virtual function should be invoked when animating + * a layout manager. It returns the progress of the animation, using the + * same semantics as the #ClutterAlpha:alpha value. + * + * + * end_animation() + * This virtual function is invoked when the animation of + * a layout manager ends, and it is meant to be used for bookkeeping the + * objects created in the begin_animation() + * function. The default implementation will call it implicitly when the + * timeline is complete. + * + * + * The simplest way to animate a layout is to create a #ClutterTimeline + * inside the begin_animation() virtual function, along + * with a #ClutterAlpha, and for each #ClutterTimeline::new-frame signal + * emission call clutter_layout_manager_layout_changed(), which will cause a + * relayout. The #ClutterTimeline::completed signal emission should cause + * clutter_layout_manager_end_animation() to be called. The default + * implementation provided internally by #ClutterLayoutManager does exactly + * this, so most sub-classes should either not override any animation-related + * virtual function or simply override begin_animation() + * and end_animation() to set up ad hoc state, and then + * chain up to the parent's implementation. + * + * Animation of a Layout Manager + * The code below shows how a #ClutterLayoutManager sub-class should + * provide animating the allocation of its children from within the + * allocate() virtual function implementation. The + * animation is computed between the last stable allocation performed + * before the animation started and the desired final allocation. + * The is_animating variable is stored inside the + * #ClutterLayoutManager sub-class and it is updated by overriding the + * begin_animation() and + * end_animation() virtual functions and chaining up + * to the base class implementation. + * The last stable allocation is stored within a #ClutterLayoutMeta + * sub-class used by the implementation. + * + * static void + * my_layout_manager_allocate (ClutterLayoutManager *manager, + * ClutterContainer *container, + * const ClutterActorBox *allocation, + * ClutterAllocationFlags flags) + * { + * MyLayoutManager *self = MY_LAYOUT_MANAGER (manager); + * GList *children, *l; + * + * children = clutter_container_get_children (container); + * + * for (l = children; l != NULL; l = l->next) + * { + * ClutterActor *child = l->data; + * ClutterLayoutMeta *meta; + * MyLayoutMeta *my_meta; + * + * /* retrieve the layout meta-object */ + * meta = clutter_layout_manager_get_child_meta (manager, + * container, + * child); + * my_meta = MY_LAYOUT_META (meta); + * + * /* compute the desired allocation for the child */ + * compute_allocation (self, my_meta, child, + * allocation, flags, + * &child_box); + * + * /* this is the additional code that deals with the animation + * * of the layout manager + * */ + * if (!self->is_animating) + * { + * /* store the last stable allocation for later use */ + * my_meta->last_alloc = clutter_actor_box_copy (&child_box); + * } + * else + * { + * ClutterActorBox end = { 0, }; + * gdouble p; + * + * /* get the progress of the animation */ + * p = clutter_layout_manager_get_animation_progress (manager); + * + * if (my_meta->last_alloc != NULL) + * { + * /* copy the desired allocation as the final state */ + * end = child_box; + * + * /* then interpolate the initial and final state + * * depending on the progress of the animation, + * * and put the result inside the box we will use + * * to allocate the child + * */ + * clutter_actor_box_interpolate (my_meta->last_alloc, + * &end, + * p, + * &child_box); + * } + * else + * { + * /* if there is no stable allocation then the child was + * * added while animating; one possible course of action + * * is to just bail out and fall through to the allocation + * * to position the child directly at its final state + * */ + * my_meta->last_alloc = + * clutter_actor_box_copy (&child_box); + * } + * } + * + * /* allocate the child */ + * clutter_actor_allocate (child, &child_box, flags); + * } + * + * g_list_free (children); + * } + * + * + * Sub-classes of #ClutterLayoutManager that support animations of the + * layout changes should call clutter_layout_manager_begin_animation() + * whenever a layout property changes value, e.g.: + * + * + * if (self->orientation != new_orientation) + * { + * ClutterLayoutManager *manager; + * + * self->orientation = new_orientation; + * + * manager = CLUTTER_LAYOUT_MANAGER (self); + * clutter_layout_manager_layout_changed (manager); + * clutter_layout_manager_begin_animation (manager, 500, CLUTTER_LINEAR); + * + * g_object_notify (G_OBJECT (self), "orientation"); + * } + * + * + * The code above will animate a change in the + * orientation layout property of a layout manager. * * * #ClutterLayoutManager is available since Clutter 1.2