/**
* SECTION:clutter-actor
- * @short_description: Base abstract class for all visual stage actors.
+ * @short_description: The basic element of the scene graph
*
* The ClutterActor class is the basic element of the scene graph in Clutter,
* and it encapsulates the position, size, and transformations of a node in
* of children from a ClutterActor, use the #ClutterContainer::actor-removed
* signal.</para>
* <informalexample><programlisting>
- * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../tests/interactive/test-actor.c">
+ * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../examples/basic-actor.c">
* <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
* </xi:include>
* </programlisting></informalexample>
* </figure>
* </refsect2>
*
+ * <refsect2 id="ClutterActor-painting">
+ * <title>Painting an actor</title>
+ * <para>There are three ways to paint an actor:</para>
+ * <itemizedlist>
+ * <listitem><para>set a delegate #ClutterContent as the value for the
+ * #ClutterActor:content property of the actor;</para></listitem>
+ * <listitem><para>subclass #ClutterActor and override the
+ * #ClutterActorClass.paint_node() virtual function;</para></listitem>
+ * <listitem><para>subclass #ClutterActor and override the
+ * #ClutterActorClass.paint() virtual function.</para></listitem>
+ * </itemizedlist>
+ * <formalpara>
+ * <title>Setting the Content property</title>
+ * <para>A #ClutterContent is a delegate object that takes over the
+ * painting operation of one, or more actors. The #ClutterContent
+ * painting will be performed on top of the #ClutterActor:background-color
+ * of the actor, and before calling the #ClutterActorClass.paint_node()
+ * virtual function.</para>
+ * <informalexample><programlisting>
+ * ClutterActor *actor = clutter_actor_new ();
+ *
+ * /* set the bounding box */
+ * clutter_actor_set_position (actor, 50, 50);
+ * clutter_actor_set_size (actor, 100, 100);
+ *
+ * /* set the content; the image_content variable is set elsewhere */
+ * clutter_actor_set_content (actor, image_content);
+ * </programlisting></informalexample>
+ * </formalpara>
+ * <formalpara>
+ * <title>Overriding the paint_node virtual function</title>
+ * <para>The #ClutterActorClass.paint_node() virtual function is invoked
+ * whenever an actor needs to be painted. The implementation of the
+ * virtual function must only paint the contents of the actor itself,
+ * and not the contents of its children, if the actor has any.</para>
+ * <para>The #ClutterPaintNode passed to the virtual function is the
+ * local root of the render tree; any node added to it will be
+ * rendered at the correct position, as defined by the actor's
+ * #ClutterActor:allocation.</para>
+ * <informalexample><programlisting>
+ * static void
+ * my_actor_paint_node (ClutterActor *actor,
+ * ClutterPaintNode *root)
+ * {
+ * ClutterPaintNode *node;
+ * ClutterActorBox box;
+ *
+ * /* where the content of the actor should be painted */
+ * clutter_actor_get_allocation_box (actor, &box);
+ *
+ * /* the cogl_texture variable is set elsewhere */
+ * node = clutter_texture_node_new (cogl_texture, CLUTTER_COLOR_White,
+ * CLUTTER_SCALING_FILTER_TRILINEAR,
+ * CLUTTER_SCALING_FILTER_LINEAR);
+ *
+ * /* paint the content of the node using the allocation */
+ * clutter_paint_node_add_rectangle (node, &box);
+ *
+ * /* add the node, and transfer ownership */
+ * clutter_paint_node_add_child (root, node);
+ * clutter_paint_node_unref (node);
+ * }
+ * </programlisting></informalexample>
+ * </formalpara>
+ * <formalpara>
+ * <title>Overriding the paint virtual function</title>
+ * <para>The #ClutterActorClass.paint() virtual function is invoked
+ * when the #ClutterActor::paint signal is emitted, and after the other
+ * signal handlers have been invoked. Overriding the paint virtual
+ * function gives total control to the paint sequence of the actor
+ * itself, including the children of the actor, if any.</para>
+ * <warning><para>It is strongly discouraged to override the
+ * #ClutterActorClass.paint() virtual function, as well as connecting
+ * to the #ClutterActor::paint signal. These hooks into the paint
+ * sequence are considered legacy, and will be removed when the Clutter
+ * API changes.</para></warning>
+ * </formalpara>
+ * </refsect2>
+ *
* <refsect2 id="ClutterActor-events">
* <title>Handling events on an actor</title>
* <para>A #ClutterActor can receive and handle input device events, for
* returning %CLUTTER_EVENT_PROPAGATE.</para>
* </refsect2>
*
+ * <refsect2 id="ClutterActor-animation">
+ * <title>Animation</title>
+ * <para>Animation is a core concept of modern user interfaces; Clutter
+ * provides a complete and powerful animation framework that automatically
+ * tweens the actor's state without requiring direct, frame by frame
+ * manipulation from your application code.</para>
+ * <formalpara>
+ * <title>Implicit animations</title>
+ * <para>The implicit animation model of Clutter assumes that all the
+ * changes in an actor state should be gradual and asynchronous; Clutter
+ * will automatically transition an actor's property change between the
+ * current state and the desired one without manual intervention.</para>
+ * <para>By default, in the 1.0 API series, the transition happens with
+ * a duration of zero milliseconds, and the implicit animation is an
+ * opt in feature to retain backwards compatibility. In order to enable
+ * implicit animations, it is necessary to change the easing state of
+ * an actor by using clutter_actor_save_easing_state():</para>
+ * <informalexample><programlisting>
+ * /* assume that the actor is currently positioned at (100, 100) */
+ * clutter_actor_save_easing_state (actor);
+ * clutter_actor_set_position (actor, 500, 500);
+ * clutter_actor_restore_easing_state (actor);
+ * </programlisting></informalexample>
+ * <para>The example above will trigger an implicit animation of the
+ * actor between its current position to a new position.</para>
+ * <para>It is possible to animate multiple properties of an actor
+ * at the same time, and you can animate multiple actors at the same
+ * time as well, for instance:</para>
+ * <informalexample><programlisting>
+ * /* animate the actor's opacity and depth */
+ * clutter_actor_save_easing_state (actor);
+ * clutter_actor_set_opacity (actor, 0);
+ * clutter_actor_set_depth (actor, -100);
+ * clutter_actor_restore_easing_state (actor);
+ *
+ * /* animate another actor's opacity */
+ * clutter_actor_save_easing_state (another_actor);
+ * clutter_actor_set_opacity (another_actor, 255);
+ * clutter_actor_set_depth (another_actor, 100);
+ * clutter_actor_restore_easing_state (another_actor);
+ * </programlisting></informalexample>
+ * <para>Implicit animations use a default duration of 250 milliseconds,
+ * and a default easing mode of %CLUTTER_EASE_OUT_CUBIC, unless you call
+ * clutter_actor_set_easing_mode() and clutter_actor_set_easing_duration()
+ * after changing the easing state of the actor.</para>
+ * <para>It is important to note that if you modify the state on an
+ * animatable property while a transition is in flight, the transition's
+ * final value will be updated, as well as its duration and progress
+ * mode by using the current easing state; for instance, in the following
+ * example:</para>
+ * <informalexample><programlisting>
+ * clutter_actor_save_easing_state (actor);
+ * clutter_actor_set_x (actor, 200);
+ * clutter_actor_restore_easing_state (actor);
+ *
+ * clutter_actor_save_easing_state (actor);
+ * clutter_actor_set_x (actor, 100);
+ * clutter_actor_restore_easing_state (actor);
+ * </programlisting></informalexample>
+ * <para>the first call to clutter_actor_set_x() will begin a transition
+ * of the #ClutterActor:x property to the value of 200; the second call
+ * to clutter_actor_set_x() will change the transition's final value to
+ * 100.</para>
+ * <para>It is possible to retrieve the #ClutterTransition used by the
+ * animatable properties by using clutter_actor_get_transition() and using
+ * the property name as the transition name.</para>
+ * </formalpara>
+ * <formalpara>
+ * <title>Explicit animations</title>
+ * <para>The explicit animation model supported by Clutter requires that
+ * you create a #ClutterTransition object, and set the initial and
+ * final values. The transition will not start unless you add it to the
+ * #ClutterActor.</para>
+ * <informalexample><programlisting>
+ * ClutterTransition *transition;
+ *
+ * transition = clutter_property_transition_new ("opacity");
+ * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 3000);
+ * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 2);
+ * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
+ * clutter_transition_set_from (transition, G_TYPE_UINT, 255);
+ * clutter_transition_set_to (transition, G_TYPE_UINT, 0);
+ *
+ * clutter_actor_add_transition (actor, "animate-opacity", transition);
+ * </programlisting></informalexample>
+ * <para>The example above will animate the #ClutterActor:opacity property
+ * of an actor between fully opaque and fully transparent, and back, over
+ * a span of 3 seconds. The animation does not begin until it is added to
+ * the actor.</para>
+ * <para>The explicit animation API should also be used when using custom
+ * animatable properties for #ClutterAction, #ClutterConstraint, and
+ * #ClutterEffect instances associated to an actor; see the section on
+ * <ulink linkend="ClutterActor-custom-animatable-properties">custom
+ * animatable properties below</ulink> for an example.</para>
+ * <para>Finally, explicit animations are useful for creating animations
+ * that run continuously, for instance:</para>
+ * <informalexample><programlisting>
+ * /* this animation will pulse the actor's opacity continuously */
+ * ClutterTransition *transition;
+ * ClutterInterval *interval;
+ *
+ * transition = clutter_property_transition_new ("opacity");
+ *
+ * /* we want to animate the opacity between 0 and 255 */
+ * clutter_transition_set_from (transition, G_TYPE_UINT, 0);
+ * clutter_transition_set_to (transition, G_TYPE_UINT, 255);
+ *
+ * /* over a one second duration, running an infinite amount of times */
+ * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 1000);
+ * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), -1);
+ *
+ * /* we want to fade in and out, so we need to auto-reverse the transition */
+ * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
+ *
+ * /* and we want to use an easing function that eases both in and out */
+ * clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
+ * CLUTTER_EASE_IN_OUT_CUBIC);
+ *
+ * /* add the transition to the desired actor; this will
+ * * start the animation.
+ * */
+ * clutter_actor_add_transition (actor, "opacityAnimation", transition);
+ * </programlisting></informalexample>
+ * </formalpara>
+ * </refsect2>
+ *
* <refsect2 id="ClutterActor-subclassing">
* <title>Implementing an actor</title>
* <para>Careful consideration should be given when deciding to implement
* it must contain the center of rotation as described by two coordinates:
* Y and Z for "x-axis"; X and Z for "y-axis"; and X and Y for
* "z-axis".</para>
+ * <para>#ClutterActor also defines a scriptable "margin" property which
+ * follows the CSS "margin" shorthand.
+ * <informalexample>
+ * <programlisting>
+ * // 4 values
+ * "margin" : [ <top>, <right>, <bottom> <left> ]
+ * // 3 values
+ * "margin" : [ <top>, <left/right>, <bottom> ]
+ * // 2 values
+ * "margin" : [ <top/bottom>, <left/right> ]
+ * // 1 value
+ * "margin" : [ <top/right/bottom/left> ]
+ * </programlisting>
+ * </informalexample>
+ * </para>
* <para>#ClutterActor will also parse every positional and dimensional
* property defined as a string through clutter_units_from_string(); you
* should read the documentation for the #ClutterUnits parser format for
* the valid units and syntax.</para>
* </refsect2>
*
- * <refsect2 id="ClutterActor-animating">
+ * <refsect2 id="ClutterActor-custom-animatable-properties">
* <title>Custom animatable properties</title>
- * <para>#ClutterActor allows accessing properties of #ClutterAction
- * and #ClutterConstraint instances associated to an actor instance
- * for animation purposes.</para>
+ * <para>#ClutterActor allows accessing properties of #ClutterAction,
+ * #ClutterEffect, and #ClutterConstraint instances associated to an actor
+ * instance for animation purposes.</para>
* <para>In order to access a specific #ClutterAction or a #ClutterConstraint
* property it is necessary to set the #ClutterActorMeta:name property on the
* given action or constraint.</para>
* <para>The example below animates a #ClutterBindConstraint applied to an
* actor using clutter_actor_animate(). The <emphasis>rect</emphasis> has
* a binding constraint for the <emphasis>origin</emphasis> actor, and in
- * its initial state is fully transparent and overlapping the actor to
- * which is bound to. </para>
+ * its initial state is overlapping the actor to which is bound to.</para>
* <informalexample><programlisting>
* constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_X, 0.0);
* clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-x");
* clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-y");
* clutter_actor_add_constraint (rect, constraint);
*
- * clutter_actor_set_reactive (rect, TRUE);
- * clutter_actor_set_opacity (rect, 0);
+ * clutter_actor_set_reactive (origin, TRUE);
*
- * g_signal_connect (rect, "button-press-event",
+ * g_signal_connect (origin, "button-press-event",
* G_CALLBACK (on_button_press),
- * NULL);
+ * rect);
* </programlisting></informalexample>
* <para>On button press, the rectangle "slides" from behind the actor to
- * which is bound to, using the #ClutterBindConstraint:offset property and
- * the #ClutterActor:opacity property.</para>
- * <informalexample><programlisting>
- * float new_offset = clutter_actor_get_width (origin) + h_padding;
- *
- * clutter_actor_animate (rect, CLUTTER_EASE_OUT_CUBIC, 500,
- * "opacity", 255,
- * "@constraints.bind-x.offset", new_offset,
- * NULL);
- * </programlisting></informalexample>
- * </refsect2>
- *
- * <refsect2 id="ClutterActor-animatable-properties">
- * <title>Animatable properties</title>
- * <para>Certain properties on #ClutterActor are marked as "animatable";
- * these properties will be automatically tweened between the current
- * value and the new value when one is set.</para>
- * <para>For backward compatibility, animatable properties will only be
- * tweened if the easing duration is greater than 0, or if a new easing
- * state is set, for instance the following example:</para>
- * <informalexample><programlisting>
- * clutter_actor_save_easing_state (actor);
- * clutter_actor_set_position (actor, 200, 200);
- * clutter_actor_restore_easing_state (actor);
- * </programlisting></informalexample>
- * will tween the actor to the (200, 200) coordinates using the default
- * easing mode and duration of a new easing state. The example above is
- * equivalent to the following code:</para>
- * <informalexample><programlisting>
- * clutter_actor_set_easing_mode (actor, CLUTTER_EASE_OUT_CUBIC);
- * clutter_actor_set_easing_duration (actor, 250);
- * clutter_actor_set_position (actor, 200, 200);
- * clutter_actor_restore_easing_state (actor);
- * </programlisting></informalexample>
- * <para>It is possible to nest easing states to tween animatable
- * properties using different modes and durations, for instance:</para>
+ * which is bound to, using the #ClutterBindConstraint:offset property to
+ * achieve the effect:</para>
* <informalexample><programlisting>
- * clutter_actor_save_easing_state (actor); /* outer state */
- *
- * /* set the duration of the animation to 2 seconds and change position */
- * clutter_actor_set_easing_duration (actor, 2000);
- * clutter_actor_set_position (actor, 0, 0);
- *
- * clutter_actor_save_easing_state (actor); /* inner state */
- *
- * /* set the duration of the animation to 5 seconds and change depth and opacity */
- * clutter_actor_set_easing_duration (actor, 5000);
- * clutter_actor_set_depth (actor, 200);
- * clutter_actor_set_opacity (actor, 0);
- *
- * clutter_actor_restore_easing_state (actor);
- *
- * clutter_actor_restore_easing_state (actor);
+ * gboolean
+ * on_button_press (ClutterActor *origin,
+ * ClutterEvent *event,
+ * ClutterActor *rect)
+ * {
+ * ClutterTransition *transition;
+ * ClutterInterval *interval;
+ *
+ * /* the offset that we want to apply; this will make the actor
+ * * slide in from behind the origin and rest at the right of
+ * * the origin, plus a padding value.
+ * */
+ * float new_offset = clutter_actor_get_width (origin) + h_padding;
+ *
+ * /* the property we wish to animate; the "@constraints" section
+ * * tells Clutter to check inside the constraints associated
+ * * with the actor; the "bind-x" section is the name of the
+ * * constraint; and the "offset" is the name of the property
+ * * on the constraint.
+ * */
+ * const char *prop = "@constraints.bind-x.offset";
+ *
+ * /* create a new transition for the given property */
+ * transition = clutter_property_transition_new (prop);
+ *
+ * /* set the easing mode and duration */
+ * clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
+ * CLUTTER_EASE_OUT_CUBIC);
+ * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 500);
+ *
+ * /* create the interval with the initial and final values */
+ * interval = clutter_interval_new (G_TYPE_FLOAT, 0, new_offset);
+ * clutter_transition_set_interval (transition, interval);
+ *
+ * /* add the transition to the actor; this causes the animation
+ * * to start. the name "offsetAnimation" can be used to retrieve
+ * * the transition later.
+ * */
+ * clutter_actor_add_transition (rect, "offsetAnimation", transition);
+ *
+ * /* we handled the event */
+ * return CLUTTER_EVENT_STOP;
+ * }
* </programlisting></informalexample>
* </refsect2>
*/
#include "clutter-color.h"
#include "clutter-constraint.h"
#include "clutter-container.h"
+#include "clutter-content-private.h"
#include "clutter-debug.h"
#include "clutter-effect-private.h"
#include "clutter-enum-types.h"
#include "clutter-interval.h"
#include "clutter-main.h"
#include "clutter-marshal.h"
+#include "clutter-paint-nodes.h"
+#include "clutter-paint-node-private.h"
#include "clutter-paint-volume-private.h"
#include "clutter-private.h"
#include "clutter-profile.h"
/* delegate object used to allocate the children of this actor */
ClutterLayoutManager *layout_manager;
+ /* delegate object used to paint the contents of this actor */
+ ClutterContent *content;
+
+ ClutterActorBox content_box;
+ ClutterContentGravity content_gravity;
+ ClutterScalingFilter min_filter;
+ ClutterScalingFilter mag_filter;
+
/* used when painting, to update the paint volume */
ClutterEffect *current_effect;
queued without an effect. */
guint is_dirty : 1;
guint bg_color_set : 1;
+ guint content_box_valid : 1;
+ guint x_expand_set : 1;
+ guint y_expand_set : 1;
+ guint needs_compute_expand : 1;
+ guint needs_x_expand : 1;
+ guint needs_y_expand : 1;
};
enum
PROP_WIDTH,
PROP_HEIGHT,
+ PROP_POSITION,
+ PROP_SIZE,
+
/* Then the rest of these size-related properties are the "actual"
* underlying properties set or gotten by X, Y, WIDTH, HEIGHT
*/
PROP_LAYOUT_MANAGER,
+ PROP_X_EXPAND,
+ PROP_Y_EXPAND,
PROP_X_ALIGN,
PROP_Y_ALIGN,
PROP_MARGIN_TOP,
PROP_FIRST_CHILD,
PROP_LAST_CHILD,
+ PROP_CONTENT,
+ PROP_CONTENT_GRAVITY,
+ PROP_CONTENT_BOX,
+ PROP_MINIFICATION_FILTER,
+ PROP_MAGNIFICATION_FILTER,
+
PROP_LAST
};
ENTER_EVENT,
LEAVE_EVENT,
ALLOCATION_CHANGED,
+ TRANSITIONS_COMPLETED,
LAST_SIGNAL
};
static guint actor_signals[LAST_SIGNAL] = { 0, };
+typedef struct _TransitionClosure
+{
+ ClutterActor *actor;
+ ClutterTransition *transition;
+ gchar *name;
+ gulong completed_id;
+} TransitionClosure;
+
static void clutter_container_iface_init (ClutterContainerIface *iface);
static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
static void clutter_animatable_iface_init (ClutterAnimatableIface *iface);
static void on_layout_manager_changed (ClutterLayoutManager *manager,
ClutterActor *self);
+static inline void clutter_actor_queue_compute_expand (ClutterActor *self);
+
/* Helper macro which translates by the anchor coord, applies the
given transformation and then translates back */
#define TRANSFORM_ABOUT_ANCHOR_COORD(a,m,c,_transform) G_STMT_START { \
set_show_on_set_parent (self, TRUE);
+ /* if we're showing a child that needs to expand, or may
+ * expand, then we need to recompute the expand flags for
+ * its parent as well
+ */
+ if (priv->needs_compute_expand ||
+ priv->needs_x_expand ||
+ priv->needs_y_expand)
+ {
+ clutter_actor_queue_compute_expand (self);
+ }
+
g_signal_emit (self, actor_signals[SHOW], 0);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
set_show_on_set_parent (self, FALSE);
+ /* if we're hiding a child that needs to expand, or may
+ * expand, then we need to recompute the expand flags for
+ * its parent as well
+ */
+ if (priv->needs_compute_expand ||
+ priv->needs_x_expand ||
+ priv->needs_y_expand)
+ {
+ clutter_actor_queue_compute_expand (self);
+ }
+
g_signal_emit (self, actor_signals[HIDE], 0);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
{
g_object_notify_by_pspec (obj, obj_props[PROP_X]);
g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
+ g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
+ g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
}
else if (priv->needs_width_request || priv->needs_height_request)
{
g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
+ g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
}
else
{
- gfloat xu, yu;
- gfloat widthu, heightu;
+ gfloat x, y;
+ gfloat width, height;
- xu = priv->allocation.x1;
- yu = priv->allocation.y1;
- widthu = priv->allocation.x2 - priv->allocation.x1;
- heightu = priv->allocation.y2 - priv->allocation.y1;
+ x = priv->allocation.x1;
+ y = priv->allocation.y1;
+ width = priv->allocation.x2 - priv->allocation.x1;
+ height = priv->allocation.y2 - priv->allocation.y1;
- if (xu != old->x1)
- g_object_notify_by_pspec (obj, obj_props[PROP_X]);
+ if (x != old->x1)
+ {
+ g_object_notify_by_pspec (obj, obj_props[PROP_X]);
+ g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
+ }
- if (yu != old->y1)
- g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
+ if (y != old->y1)
+ {
+ g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
+ g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
+ }
- if (widthu != (old->x2 - old->x1))
- g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
+ if (width != (old->x2 - old->x1))
+ {
+ g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
+ g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
+ }
- if (heightu != (old->y2 - old->y1))
- g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
+ if (height != (old->y2 - old->y1))
+ {
+ g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
+ g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
+ }
}
g_object_thaw_notify (obj);
ClutterActorPrivate *priv = self->priv;
GObject *obj;
gboolean x1_changed, y1_changed, x2_changed, y2_changed;
- gboolean flags_changed;
gboolean retval;
ClutterActorBox old_alloc = { 0, };
x2_changed = priv->allocation.x2 != box->x2;
y2_changed = priv->allocation.y2 != box->y2;
- flags_changed = priv->allocation_flags != flags;
-
priv->allocation = *box;
priv->allocation_flags = flags;
priv->needs_height_request = FALSE;
priv->needs_allocation = FALSE;
- if (x1_changed || y1_changed || x2_changed || y2_changed || flags_changed)
+ if (x1_changed ||
+ y1_changed ||
+ x2_changed ||
+ y2_changed)
{
CLUTTER_NOTE (LAYOUT, "Allocation for '%s' changed",
_clutter_actor_get_debug_name (self));
g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]);
+ /* if the allocation changes, so does the content box */
+ if (priv->content != NULL)
+ {
+ priv->content_box_valid = FALSE;
+ g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]);
+ }
+
retval = TRUE;
}
else
return;
_clutter_meta_group_remove_meta (priv->effects, CLUTTER_ACTOR_META (effect));
+
+ if (_clutter_meta_group_peek_metas (priv->effects) == NULL)
+ g_clear_object (&priv->effects);
}
static gboolean
/* Destroy the effect so that it will lose its fbo cache of
the actor */
_clutter_actor_remove_effect_internal (self, priv->flatten_effect);
- g_object_unref (priv->flatten_effect);
- priv->flatten_effect = NULL;
+ g_clear_object (&priv->flatten_effect);
}
}
}
ClutterActorPrivate *priv = actor->priv;
ClutterActor *iter;
- /* paint the background color, if set */
- if (priv->bg_color_set)
- {
- float width, height;
- guint8 real_alpha;
-
- clutter_actor_box_get_size (&priv->allocation, &width, &height);
-
- real_alpha = clutter_actor_get_paint_opacity_internal (actor)
- * priv->bg_color.alpha
- / 255;
-
- cogl_set_source_color4ub (priv->bg_color.red,
- priv->bg_color.green,
- priv->bg_color.blue,
- real_alpha);
-
- cogl_rectangle (0, 0, width, height);
- }
-
for (iter = priv->first_child;
iter != NULL;
iter = iter->priv->next_sibling)
}
}
+static gboolean
+clutter_actor_paint_node (ClutterActor *actor,
+ ClutterPaintNode *root)
+{
+ ClutterActorPrivate *priv = actor->priv;
+
+ if (root == NULL)
+ return FALSE;
+
+ if (priv->bg_color_set &&
+ !clutter_color_equal (&priv->bg_color, CLUTTER_COLOR_Transparent))
+ {
+ ClutterPaintNode *node;
+ ClutterColor bg_color;
+ ClutterActorBox box;
+
+ box.x1 = 0.f;
+ box.y1 = 0.f;
+ box.x2 = clutter_actor_box_get_width (&priv->allocation);
+ box.y2 = clutter_actor_box_get_height (&priv->allocation);
+
+ bg_color = priv->bg_color;
+ bg_color.alpha = clutter_actor_get_paint_opacity_internal (actor)
+ * priv->bg_color.alpha
+ / 255;
+
+ node = clutter_color_node_new (&bg_color);
+ clutter_paint_node_set_name (node, "backgroundColor");
+ clutter_paint_node_add_rectangle (node, &box);
+ clutter_paint_node_add_child (root, node);
+ clutter_paint_node_unref (node);
+ }
+
+ if (priv->content != NULL)
+ _clutter_content_paint_content (priv->content, actor, root);
+
+ if (CLUTTER_ACTOR_GET_CLASS (actor)->paint_node != NULL)
+ CLUTTER_ACTOR_GET_CLASS (actor)->paint_node (actor, root);
+
+ if (clutter_paint_node_get_n_children (root) == 0)
+ return FALSE;
+
+#ifdef CLUTTER_ENABLE_DEBUG
+ if (CLUTTER_HAS_DEBUG (PAINT))
+ {
+ /* dump the tree only if we have one */
+ _clutter_paint_node_dump_tree (root);
+ }
+#endif /* CLUTTER_ENABLE_DEBUG */
+
+ _clutter_paint_node_paint (root);
+
+#if 0
+ /* XXX: Uncomment this when we disable emitting the paint signal */
+ CLUTTER_ACTOR_GET_CLASS (actor)->paint (actor);
+#endif
+
+ return TRUE;
+}
+
/**
* clutter_actor_paint:
* @self: A #ClutterActor
{
if (_clutter_context_get_pick_mode () == CLUTTER_PICK_NONE)
{
+ ClutterPaintNode *dummy;
+
+ /* XXX - this will go away in 2.0, when we can get rid of this
+ * stuff and switch to a pure retained render tree of PaintNodes
+ * for the entire frame, starting from the Stage; the paint()
+ * virtual function can then be called directly.
+ */
+ dummy = _clutter_dummy_node_new (self);
+ clutter_paint_node_set_name (dummy, "Root");
+
+ /* XXX - for 1.12, we use the return value of paint_node() to
+ * decide whether we should emit the ::paint signal.
+ */
+ clutter_actor_paint_node (self, dummy);
+ clutter_paint_node_unref (dummy);
+
g_signal_emit (self, actor_signals[PAINT], 0);
}
else
}
}
+static void
+_clutter_actor_stop_transitions (ClutterActor *self)
+{
+ const ClutterAnimationInfo *info;
+ GHashTableIter iter;
+ gpointer value;
+
+ info = _clutter_actor_get_animation_info_or_defaults (self);
+ if (info->transitions == NULL)
+ return;
+
+ g_hash_table_iter_init (&iter, info->transitions);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ TransitionClosure *closure = value;
+ clutter_timeline_stop (CLUTTER_TIMELINE (closure->transition));
+ }
+}
+
static ClutterActorTraverseVisitFlags
invalidate_queue_redraw_entry (ClutterActor *self,
int depth,
REMOVE_CHILD_CHECK_STATE = 1 << 3,
REMOVE_CHILD_FLUSH_QUEUE = 1 << 4,
REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 5,
+ REMOVE_CHILD_STOP_TRANSITIONS = 1 << 6,
/* default flags for public API */
- REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_DESTROY_META |
+ REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
+ REMOVE_CHILD_DESTROY_META |
REMOVE_CHILD_EMIT_PARENT_SET |
REMOVE_CHILD_EMIT_ACTOR_REMOVED |
REMOVE_CHILD_CHECK_STATE |
REMOVE_CHILD_NOTIFY_FIRST_LAST,
/* flags for legacy/deprecated API */
- REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_CHECK_STATE |
+ REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
+ REMOVE_CHILD_CHECK_STATE |
REMOVE_CHILD_FLUSH_QUEUE |
REMOVE_CHILD_EMIT_PARENT_SET |
REMOVE_CHILD_NOTIFY_FIRST_LAST
gboolean flush_queue;
gboolean notify_first_last;
gboolean was_mapped;
+ gboolean stop_transitions;
destroy_meta = (flags & REMOVE_CHILD_DESTROY_META) != 0;
emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0;
check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0;
flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
+ stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0;
g_object_freeze_notify (G_OBJECT (self));
+ if (stop_transitions)
+ _clutter_actor_stop_transitions (child);
+
if (destroy_meta)
clutter_container_destroy_child_meta (CLUTTER_CONTAINER (self), child);
self->priv->age += 1;
+ /* if the child that got removed was visible and set to
+ * expand then we want to reset the parent's state in
+ * case the child was the only thing that was making it
+ * expand.
+ */
+ if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
+ (child->priv->needs_compute_expand ||
+ child->priv->needs_x_expand ||
+ child->priv->needs_y_expand))
+ {
+ clutter_actor_queue_compute_expand (self);
+ }
+
/* clutter_actor_reparent() will emit ::parent-set for us */
if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
ClutterRotateAxis axis,
gdouble angle)
{
- ClutterTransformInfo *info;
+ const ClutterTransformInfo *info;
+ const double *cur_angle_p = NULL;
+ GParamSpec *pspec = NULL;
- info = _clutter_actor_get_transform_info (self);
+ info = _clutter_actor_get_transform_info_or_defaults (self);
- if (clutter_actor_get_easing_duration (self) != 0)
+ switch (axis)
{
- ClutterTransition *transition;
- GParamSpec *pspec = NULL;
- double *cur_angle_p = NULL;
-
- switch (axis)
- {
- case CLUTTER_X_AXIS:
- cur_angle_p = &info->rx_angle;
- pspec = obj_props[PROP_ROTATION_ANGLE_X];
- break;
-
- case CLUTTER_Y_AXIS:
- cur_angle_p = &info->ry_angle;
- pspec = obj_props[PROP_ROTATION_ANGLE_Y];
- break;
+ case CLUTTER_X_AXIS:
+ cur_angle_p = &info->rx_angle;
+ pspec = obj_props[PROP_ROTATION_ANGLE_X];
+ break;
- case CLUTTER_Z_AXIS:
- cur_angle_p = &info->rz_angle;
- pspec = obj_props[PROP_ROTATION_ANGLE_Z];
- break;
- }
+ case CLUTTER_Y_AXIS:
+ cur_angle_p = &info->ry_angle;
+ pspec = obj_props[PROP_ROTATION_ANGLE_Y];
+ break;
- g_assert (pspec != NULL);
- g_assert (cur_angle_p != NULL);
+ case CLUTTER_Z_AXIS:
+ cur_angle_p = &info->rz_angle;
+ pspec = obj_props[PROP_ROTATION_ANGLE_Z];
+ break;
+ }
- transition = _clutter_actor_get_transition (self, pspec);
- if (transition == NULL)
- {
- transition = _clutter_actor_create_transition (self, pspec,
- *cur_angle_p,
- angle);
- clutter_timeline_start (CLUTTER_TIMELINE (transition));
- }
- else
- _clutter_actor_update_transition (self, pspec, angle);
+ g_assert (pspec != NULL);
+ g_assert (cur_angle_p != NULL);
- self->priv->transform_valid = FALSE;
- clutter_actor_queue_redraw (self);
- }
+ if (_clutter_actor_get_transition (self, pspec) == NULL)
+ _clutter_actor_create_transition (self, pspec, *cur_angle_p, angle);
else
- clutter_actor_set_rotation_angle_internal (self, axis, angle);
+ _clutter_actor_update_transition (self, pspec, angle);
+
+ clutter_actor_queue_redraw (self);
}
/*< private >
}
static void
-clutter_actor_animate_scale_factor (ClutterActor *self,
- double old_factor,
- double new_factor,
- GParamSpec *pspec)
-{
- ClutterTransition *transition;
-
- transition = _clutter_actor_get_transition (self, pspec);
- if (transition == NULL)
- {
- transition = _clutter_actor_create_transition (self, pspec,
- old_factor,
- new_factor);
- clutter_timeline_start (CLUTTER_TIMELINE (transition));
- }
- else
- _clutter_actor_update_transition (self, pspec, new_factor);
-
-
- self->priv->transform_valid = FALSE;
- clutter_actor_queue_redraw (self);
-}
-
-static void
clutter_actor_set_scale_factor_internal (ClutterActor *self,
double factor,
GParamSpec *pspec)
ClutterRotateAxis axis,
gdouble factor)
{
- GObject *obj = G_OBJECT (self);
- ClutterTransformInfo *info;
- GParamSpec *pspec;
-
- info = _clutter_actor_get_transform_info (self);
+ const ClutterTransformInfo *info;
+ const double *scale_p = NULL;
+ GParamSpec *pspec = NULL;
- g_object_freeze_notify (obj);
+ info = _clutter_actor_get_transform_info_or_defaults (self);
switch (axis)
{
case CLUTTER_X_AXIS:
pspec = obj_props[PROP_SCALE_X];
-
- if (clutter_actor_get_easing_duration (self) != 0)
- clutter_actor_animate_scale_factor (self, info->scale_x, factor, pspec);
- else
- clutter_actor_set_scale_factor_internal (self, factor, pspec);
+ scale_p = &info->scale_x;
break;
case CLUTTER_Y_AXIS:
pspec = obj_props[PROP_SCALE_Y];
-
- if (clutter_actor_get_easing_duration (self) != 0)
- clutter_actor_animate_scale_factor (self, info->scale_y, factor, pspec);
- else
- clutter_actor_set_scale_factor_internal (self, factor, pspec);
+ scale_p = &info->scale_y;
break;
- default:
- g_assert_not_reached ();
+ case CLUTTER_Z_AXIS:
+ break;
}
- g_object_thaw_notify (obj);
+ g_assert (pspec != NULL);
+ g_assert (scale_p != NULL);
+
+ if (_clutter_actor_get_transition (self, pspec) == NULL)
+ _clutter_actor_create_transition (self, pspec, *scale_p, factor);
+ else
+ _clutter_actor_update_transition (self, pspec, factor);
+
+ clutter_actor_queue_redraw (self);
}
static inline void
}
static inline void
+clutter_actor_set_scale_gravity (ClutterActor *self,
+ ClutterGravity gravity)
+{
+ ClutterTransformInfo *info;
+ GObject *obj;
+
+ info = _clutter_actor_get_transform_info (self);
+ obj = G_OBJECT (self);
+
+ if (gravity == CLUTTER_GRAVITY_NONE)
+ clutter_anchor_coord_set_units (&info->scale_center, 0, 0, 0);
+ else
+ clutter_anchor_coord_set_gravity (&info->scale_center, gravity);
+
+ self->priv->transform_valid = FALSE;
+
+ g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_X]);
+ g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_Y]);
+ g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_GRAVITY]);
+
+ clutter_actor_queue_redraw (self);
+}
+
+static inline void
clutter_actor_set_anchor_coord (ClutterActor *self,
ClutterRotateAxis axis,
gfloat coord)
clutter_actor_set_y (actor, g_value_get_float (value));
break;
+ case PROP_POSITION:
+ {
+ const ClutterPoint *pos = g_value_get_boxed (value);
+
+ if (pos != NULL)
+ clutter_actor_set_position (actor, pos->x, pos->y);
+ else
+ clutter_actor_set_fixed_position_set (actor, FALSE);
+ }
+ break;
+
case PROP_WIDTH:
clutter_actor_set_width (actor, g_value_get_float (value));
break;
clutter_actor_set_height (actor, g_value_get_float (value));
break;
+ case PROP_SIZE:
+ {
+ const ClutterSize *size = g_value_get_boxed (value);
+
+ if (size != NULL)
+ clutter_actor_set_size (actor, size->width, size->height);
+ else
+ clutter_actor_set_size (actor, -1, -1);
+ }
+ break;
+
case PROP_FIXED_X:
clutter_actor_set_x (actor, g_value_get_float (value));
break;
break;
case PROP_SCALE_GRAVITY:
- {
- const ClutterTransformInfo *info;
- ClutterGravity gravity;
-
- info = _clutter_actor_get_transform_info_or_defaults (actor);
- gravity = g_value_get_enum (value);
-
- clutter_actor_set_scale_with_gravity (actor,
- info->scale_x,
- info->scale_y,
- gravity);
- }
+ clutter_actor_set_scale_gravity (actor, g_value_get_enum (value));
break;
case PROP_CLIP:
clutter_actor_set_layout_manager (actor, g_value_get_object (value));
break;
+ case PROP_X_EXPAND:
+ clutter_actor_set_x_expand (actor, g_value_get_boolean (value));
+ break;
+
+ case PROP_Y_EXPAND:
+ clutter_actor_set_y_expand (actor, g_value_get_boolean (value));
+ break;
+
case PROP_X_ALIGN:
clutter_actor_set_x_align (actor, g_value_get_enum (value));
break;
clutter_actor_set_background_color (actor, g_value_get_boxed (value));
break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ case PROP_CONTENT:
+ clutter_actor_set_content (actor, g_value_get_object (value));
break;
- }
-}
-static void
-clutter_actor_get_property (GObject *object,
- guint prop_id,
+ case PROP_CONTENT_GRAVITY:
+ clutter_actor_set_content_gravity (actor, g_value_get_enum (value));
+ break;
+
+ case PROP_MINIFICATION_FILTER:
+ clutter_actor_set_content_scaling_filters (actor,
+ g_value_get_enum (value),
+ actor->priv->mag_filter);
+ break;
+
+ case PROP_MAGNIFICATION_FILTER:
+ clutter_actor_set_content_scaling_filters (actor,
+ actor->priv->min_filter,
+ g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_actor_get_property (GObject *object,
+ guint prop_id,
GValue *value,
GParamSpec *pspec)
{
g_value_set_float (value, clutter_actor_get_y (actor));
break;
+ case PROP_POSITION:
+ {
+ ClutterPoint position;
+
+ clutter_point_init (&position,
+ clutter_actor_get_x (actor),
+ clutter_actor_get_y (actor));
+ g_value_set_boxed (value, &position);
+ }
+ break;
+
case PROP_WIDTH:
g_value_set_float (value, clutter_actor_get_width (actor));
break;
g_value_set_float (value, clutter_actor_get_height (actor));
break;
+ case PROP_SIZE:
+ {
+ ClutterSize size;
+
+ clutter_size_init (&size,
+ clutter_actor_get_width (actor),
+ clutter_actor_get_height (actor));
+ g_value_set_boxed (value, &size);
+ }
+ break;
+
case PROP_FIXED_X:
{
const ClutterLayoutInfo *info;
info = _clutter_actor_get_layout_info_or_defaults (actor);
- g_value_set_float (value, info->fixed_x);
+ g_value_set_float (value, info->fixed_pos.x);
}
break;
const ClutterLayoutInfo *info;
info = _clutter_actor_get_layout_info_or_defaults (actor);
- g_value_set_float (value, info->fixed_y);
+ g_value_set_float (value, info->fixed_pos.y);
}
break;
const ClutterLayoutInfo *info;
info = _clutter_actor_get_layout_info_or_defaults (actor);
- g_value_set_float (value, info->min_width);
+ g_value_set_float (value, info->minimum.width);
}
break;
const ClutterLayoutInfo *info;
info = _clutter_actor_get_layout_info_or_defaults (actor);
- g_value_set_float (value, info->min_height);
+ g_value_set_float (value, info->minimum.height);
}
break;
const ClutterLayoutInfo *info;
info = _clutter_actor_get_layout_info_or_defaults (actor);
- g_value_set_float (value, info->natural_width);
+ g_value_set_float (value, info->natural.width);
}
break;
const ClutterLayoutInfo *info;
info = _clutter_actor_get_layout_info_or_defaults (actor);
- g_value_set_float (value, info->natural_height);
+ g_value_set_float (value, info->natural.height);
}
break;
g_value_set_object (value, priv->layout_manager);
break;
+ case PROP_X_EXPAND:
+ {
+ const ClutterLayoutInfo *info;
+
+ info = _clutter_actor_get_layout_info_or_defaults (actor);
+ g_value_set_boolean (value, info->x_expand);
+ }
+ break;
+
+ case PROP_Y_EXPAND:
+ {
+ const ClutterLayoutInfo *info;
+
+ info = _clutter_actor_get_layout_info_or_defaults (actor);
+ g_value_set_boolean (value, info->y_expand);
+ }
+ break;
+
case PROP_X_ALIGN:
{
const ClutterLayoutInfo *info;
g_value_set_object (value, priv->last_child);
break;
+ case PROP_CONTENT:
+ g_value_set_object (value, priv->content);
+ break;
+
+ case PROP_CONTENT_GRAVITY:
+ g_value_set_enum (value, priv->content_gravity);
+ break;
+
+ case PROP_CONTENT_BOX:
+ {
+ ClutterActorBox box = { 0, };
+
+ clutter_actor_get_content_box (actor, &box);
+ g_value_set_boxed (value, &box);
+ }
+ break;
+
+ case PROP_MINIFICATION_FILTER:
+ g_value_set_enum (value, priv->min_filter);
+ break;
+
+ case PROP_MAGNIFICATION_FILTER:
+ g_value_set_enum (value, priv->mag_filter);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
if (priv->layout_manager != NULL)
{
clutter_layout_manager_set_container (priv->layout_manager, NULL);
- g_object_unref (priv->layout_manager);
- priv->layout_manager = NULL;
+ g_clear_object (&priv->layout_manager);
+ }
+
+ if (priv->content != NULL)
+ {
+ _clutter_content_detached (priv->content, self);
+ g_clear_object (&priv->content);
}
G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
ClutterPaintVolume *volume)
{
ClutterActorPrivate *priv = self->priv;
- gboolean res = FALSE;
+ gboolean res = TRUE;
/* we start from the allocation */
clutter_paint_volume_set_width (volume,
{
const ClutterPaintVolume *child_volume;
+ if (!CLUTTER_ACTOR_IS_MAPPED (child))
+ continue;
+
child_volume = clutter_actor_get_transformed_paint_volume (child, self);
if (child_volume == NULL)
{
res = FALSE;
}
- if (clutter_actor_update_default_paint_volume (self, volume))
- return res;
+ /* update_default_paint_volume() should only fail if one of the children
+ * reported an invalid, or no, paint volume
+ */
+ if (!clutter_actor_update_default_paint_volume (self, volume))
+ return FALSE;
- return FALSE;
+ return res;
}
/**
{
ClutterActorIter iter;
+ g_object_freeze_notify (G_OBJECT (actor));
+
clutter_actor_iter_init (&iter, actor);
while (clutter_actor_iter_next (&iter, NULL))
clutter_actor_iter_destroy (&iter);
+
+ g_object_thaw_notify (G_OBJECT (actor));
}
static GObject *
CLUTTER_PARAM_ANIMATABLE);
/**
+ * ClutterActor:position:
+ *
+ * The position of the origin of the actor.
+ *
+ * This property is a shorthand for setting and getting the
+ * #ClutterActor:x and #ClutterActor:y properties at the same
+ * time.
+ *
+ * The #ClutterActor:position property is animatable.
+ *
+ * Since: 1.12
+ */
+ obj_props[PROP_POSITION] =
+ g_param_spec_boxed ("position",
+ P_("Position"),
+ P_("The position of the origin of the actor"),
+ CLUTTER_TYPE_POINT,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ CLUTTER_PARAM_ANIMATABLE);
+
+ /**
* ClutterActor:width:
*
* Width of the actor (in pixels). If written, forces the minimum and
CLUTTER_PARAM_ANIMATABLE);
/**
+ * ClutterActor:size:
+ *
+ * The size of the actor.
+ *
+ * This property is a shorthand for setting and getting the
+ * #ClutterActor:width and #ClutterActor:height at the same time.
+ *
+ * The #ClutterActor:size property is animatable.
+ *
+ * Since: 1.12
+ */
+ obj_props[PROP_SIZE] =
+ g_param_spec_boxed ("size",
+ P_("Size"),
+ P_("The size of the actor"),
+ CLUTTER_TYPE_SIZE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ CLUTTER_PARAM_ANIMATABLE);
+
+ /**
* ClutterActor:fixed-x:
*
* The fixed X position of the actor in pixels.
P_("Allocation"),
P_("The actor's allocation"),
CLUTTER_TYPE_ACTOR_BOX,
- CLUTTER_PARAM_READABLE);
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS |
+ CLUTTER_PARAM_ANIMATABLE);
/**
* ClutterActor:request-mode:
CLUTTER_TYPE_LAYOUT_MANAGER,
CLUTTER_PARAM_READWRITE);
+ /**
+ * ClutterActor:x-expand:
+ *
+ * Whether a layout manager should assign more space to the actor on
+ * the X axis.
+ *
+ * Since: 1.12
+ */
+ obj_props[PROP_X_EXPAND] =
+ g_param_spec_boolean ("x-expand",
+ P_("X Expand"),
+ P_("Whether extra horizontal space should be assigned to the actor"),
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ /**
+ * ClutterActor:y-expand:
+ *
+ * Whether a layout manager should assign more space to the actor on
+ * the Y axis.
+ *
+ * Since: 1.12
+ */
+ obj_props[PROP_Y_EXPAND] =
+ g_param_spec_boolean ("y-expand",
+ P_("Y Expand"),
+ P_("Whether extra vertical space should be assigned to the actor"),
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
/**
* ClutterActor:x-align:
*
* The alignment of an actor on the X axis, if the actor has been given
- * extra space for its allocation.
+ * extra space for its allocation. See also the #ClutterActor:x-expand
+ * property.
*
* Since: 1.10
*/
CLUTTER_TYPE_ACTOR,
CLUTTER_PARAM_READABLE);
+ /**
+ * ClutterActor:content:
+ *
+ * The #ClutterContent implementation that controls the content
+ * of the actor.
+ *
+ * Since: 1.10
+ */
+ obj_props[PROP_CONTENT] =
+ g_param_spec_object ("content",
+ P_("Content"),
+ P_("Delegate object for painting the actor's content"),
+ CLUTTER_TYPE_CONTENT,
+ CLUTTER_PARAM_READWRITE);
+
+ /**
+ * ClutterActor:content-gravity:
+ *
+ * The alignment that should be honoured by the #ClutterContent
+ * set with the #ClutterActor:content property.
+ *
+ * Changing the value of this property will change the bounding box of
+ * the content; you can use the #ClutterActor:content-box property to
+ * get the position and size of the content within the actor's
+ * allocation.
+ *
+ * This property is meaningful only for #ClutterContent implementations
+ * that have a preferred size, and if the preferred size is smaller than
+ * the actor's allocation.
+ *
+ * The #ClutterActor:content-gravity property is animatable.
+ *
+ * Since: 1.10
+ */
+ obj_props[PROP_CONTENT_GRAVITY] =
+ g_param_spec_enum ("content-gravity",
+ P_("Content Gravity"),
+ P_("Alignment of the actor's content"),
+ CLUTTER_TYPE_CONTENT_GRAVITY,
+ CLUTTER_CONTENT_GRAVITY_RESIZE_FILL,
+ CLUTTER_PARAM_READWRITE);
+
+ /**
+ * ClutterActor:content-box:
+ *
+ * The bounding box for the #ClutterContent used by the actor.
+ *
+ * The value of this property is controlled by the #ClutterActor:allocation
+ * and #ClutterActor:content-gravity properties of #ClutterActor.
+ *
+ * The bounding box for the content is guaranteed to never exceed the
+ * allocation's of the actor.
+ *
+ * Since: 1.10
+ */
+ obj_props[PROP_CONTENT_BOX] =
+ g_param_spec_boxed ("content-box",
+ P_("Content Box"),
+ P_("The bounding box of the actor's content"),
+ CLUTTER_TYPE_ACTOR_BOX,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS |
+ CLUTTER_PARAM_ANIMATABLE);
+
+ obj_props[PROP_MINIFICATION_FILTER] =
+ g_param_spec_enum ("minification-filter",
+ P_("Minification Filter"),
+ P_("The filter used when reducing the size of the content"),
+ CLUTTER_TYPE_SCALING_FILTER,
+ CLUTTER_SCALING_FILTER_LINEAR,
+ CLUTTER_PARAM_READWRITE);
+
+ obj_props[PROP_MAGNIFICATION_FILTER] =
+ g_param_spec_enum ("magnification-filter",
+ P_("Magnification Filter"),
+ P_("The filter used when increasing the size of the content"),
+ CLUTTER_TYPE_SCALING_FILTER,
+ CLUTTER_SCALING_FILTER_LINEAR,
+ CLUTTER_PARAM_READWRITE);
+
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
/**
g_signal_new (I_("queue-redraw"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST |
- G_SIGNAL_NO_RECURSE |
G_SIGNAL_NO_HOOKS,
G_STRUCT_OFFSET (ClutterActorClass, queue_redraw),
NULL, NULL,
CLUTTER_TYPE_ACTOR);
/**
- * ClutterActor::queue-relayout
+ * ClutterActor::queue-relayout:
* @actor: the actor being queued for relayout
*
* The ::queue_layout signal is emitted when clutter_actor_queue_relayout()
g_signal_new (I_("queue-relayout"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST |
- G_SIGNAL_NO_RECURSE |
G_SIGNAL_NO_HOOKS,
G_STRUCT_OFFSET (ClutterActorClass, queue_relayout),
NULL, NULL,
g_signal_new (I_("paint"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST |
- G_SIGNAL_NO_RECURSE |
G_SIGNAL_NO_HOOKS,
G_STRUCT_OFFSET (ClutterActorClass, paint),
NULL, NULL,
G_TYPE_NONE, 2,
CLUTTER_TYPE_ACTOR_BOX | G_SIGNAL_TYPE_STATIC_SCOPE,
CLUTTER_TYPE_ALLOCATION_FLAGS);
+
+ /**
+ * ClutterActor::transitions-completed:
+ * @actor: a #ClutterActor
+ *
+ * The ::transitions-completed signal is emitted once all transitions
+ * involving @actor are complete.
+ *
+ * Since: 1.10
+ */
+ actor_signals[TRANSITIONS_COMPLETED] =
+ g_signal_new (I_("transitions-completed"),
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ _clutter_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
}
static void
priv->last_paint_volume_valid = TRUE;
priv->transform_valid = FALSE;
+
+ /* the default is to stretch the content, to match the
+ * current behaviour of basically all actors. also, it's
+ * the easiest thing to compute.
+ */
+ priv->content_gravity = CLUTTER_CONTENT_GRAVITY_RESIZE_FILL;
+ priv->min_filter = CLUTTER_SCALING_FILTER_LINEAR;
+ priv->mag_filter = CLUTTER_SCALING_FILTER_LINEAR;
+
+ /* this flag will be set to TRUE if the actor gets a child
+ * or if the [xy]-expand flags are explicitly set; until
+ * then, the actor does not need to expand.
+ *
+ * this also allows us to avoid computing the expand flag
+ * when building up a scene.
+ */
+ priv->needs_compute_expand = FALSE;
}
/**
return res;
}
+/*< private >
+ * _clutter_actor_get_effective_x_align:
+ * @self: a #ClutterActor
+ *
+ * Retrieves the effective horizontal alignment, taking into
+ * consideration the text direction of @self.
+ *
+ * Return value: the effective horizontal alignment
+ */
+ClutterActorAlign
+_clutter_actor_get_effective_x_align (ClutterActor *self)
+{
+ return effective_align (clutter_actor_get_x_align (self),
+ clutter_actor_get_text_direction (self));
+}
+
static inline void
adjust_for_margin (float margin_start,
float margin_end,
if (priv->min_width_set && priv->natural_width_set)
{
if (min_width_p != NULL)
- *min_width_p = info->min_width + (info->margin.left + info->margin.right);
+ *min_width_p = info->minimum.width + (info->margin.left + info->margin.right);
if (natural_width_p != NULL)
- *natural_width_p = info->natural_width + (info->margin.left + info->margin.right);
+ *natural_width_p = info->natural.width + (info->margin.left + info->margin.right);
return;
}
if (!priv->min_width_set)
request_min_width = cached_size_request->min_size;
else
- request_min_width = info->min_width;
+ request_min_width = info->margin.left
+ + info->minimum.width
+ + info->margin.right;
if (!priv->natural_width_set)
request_natural_width = cached_size_request->natural_size;
else
- request_natural_width = info->natural_width;
+ request_natural_width = info->margin.left
+ + info->natural.width
+ + info->margin.right;
if (min_width_p)
*min_width_p = request_min_width;
if (priv->min_height_set && priv->natural_height_set)
{
if (min_height_p != NULL)
- *min_height_p = info->min_height + (info->margin.top + info->margin.bottom);
+ *min_height_p = info->minimum.height + (info->margin.top + info->margin.bottom);
if (natural_height_p != NULL)
- *natural_height_p = info->natural_height + (info->margin.top + info->margin.bottom);
+ *natural_height_p = info->natural.height + (info->margin.top + info->margin.bottom);
return;
}
if (!priv->min_height_set)
request_min_height = cached_size_request->min_size;
else
- request_min_height = info->min_height;
+ request_min_height = info->margin.top
+ + info->minimum.height
+ + info->margin.bottom;
if (!priv->natural_height_set)
request_natural_height = cached_size_request->natural_size;
else
- request_natural_height = info->natural_height;
+ request_natural_height = info->margin.top
+ + info->natural.height
+ + info->margin.bottom;
if (min_height_p)
*min_height_p = request_min_height;
_clutter_constraint_update_allocation (constraint,
self,
allocation);
+
+ CLUTTER_NOTE (LAYOUT,
+ "Allocation of '%s' after constraint '%s': "
+ "{ %.2f, %.2f, %.2f, %.2f }",
+ _clutter_actor_get_debug_name (self),
+ _clutter_actor_meta_get_debug_name (meta),
+ allocation->x1,
+ allocation->y1,
+ allocation->x2,
+ allocation->y2);
}
}
}
clutter_actor_get_preferred_height (self, -1,
&min_height,
&nat_height);
- clutter_actor_get_preferred_height (self, alloc_height,
- &min_width,
- &nat_width);
+ clutter_actor_get_preferred_width (self, alloc_height,
+ &min_width,
+ &nat_width);
}
#ifdef CLUTTER_ENABLE_DEBUG
*allocation = adj_allocation;
}
+static void
+clutter_actor_allocate_internal (ClutterActor *self,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags)
+{
+ ClutterActorClass *klass;
+
+ CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
+
+ CLUTTER_NOTE (LAYOUT, "Calling %s::allocate()",
+ _clutter_actor_get_debug_name (self));
+
+ klass = CLUTTER_ACTOR_GET_CLASS (self);
+ klass->allocate (self, allocation, flags);
+
+ CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
+
+ clutter_actor_queue_redraw (self);
+}
+
/**
* clutter_actor_allocate:
* @self: A #ClutterActor
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
- ClutterActorPrivate *priv;
- ClutterActorClass *klass;
ClutterActorBox old_allocation, real_allocation;
gboolean origin_changed, child_moved, size_changed;
gboolean stage_allocation_changed;
+ ClutterActorPrivate *priv;
g_return_if_fail (CLUTTER_IS_ACTOR (self));
if (G_UNLIKELY (_clutter_actor_get_stage_internal (self) == NULL))
if (child_moved)
flags |= CLUTTER_ABSOLUTE_ORIGIN_CHANGED;
- CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
-
- klass = CLUTTER_ACTOR_GET_CLASS (self);
- klass->allocate (self, &real_allocation, flags);
-
- CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
+ /* store the flags here, so that they can be propagated by the
+ * transition code
+ */
+ self->priv->allocation_flags = flags;
- if (stage_allocation_changed)
- clutter_actor_queue_redraw (self);
+ if (_clutter_actor_get_transition (self, obj_props[PROP_ALLOCATION]) == NULL)
+ {
+ _clutter_actor_create_transition (self, obj_props[PROP_ALLOCATION],
+ &priv->allocation,
+ &real_allocation);
+ }
+ else
+ _clutter_actor_update_transition (self, obj_props[PROP_ALLOCATION],
+ &real_allocation);
}
/**
gfloat x,
gfloat y)
{
+ ClutterPoint new_position;
+
g_return_if_fail (CLUTTER_IS_ACTOR (self));
- g_object_freeze_notify (G_OBJECT (self));
+ clutter_point_init (&new_position, x, y);
+
+ if (_clutter_actor_get_transition (self, obj_props[PROP_POSITION]) == NULL)
+ {
+ ClutterPoint cur_position;
- clutter_actor_set_x (self, x);
- clutter_actor_set_y (self, y);
+ cur_position.x = clutter_actor_get_x (self);
+ cur_position.y = clutter_actor_get_y (self);
- g_object_thaw_notify (G_OBJECT (self));
+ _clutter_actor_create_transition (self, obj_props[PROP_POSITION],
+ &cur_position,
+ &new_position);
+ }
+ else
+ _clutter_actor_update_transition (self,
+ obj_props[PROP_POSITION],
+ &new_position);
+
+ clutter_actor_queue_relayout (self);
}
/**
g_return_if_fail (CLUTTER_IS_ACTOR (self));
info = _clutter_actor_get_layout_info_or_defaults (self);
- x = info->fixed_x;
- y = info->fixed_y;
+ x = info->fixed_pos.x;
+ y = info->fixed_pos.y;
clutter_actor_set_position (self, x + dx, y + dy);
}
info = _clutter_actor_get_layout_info (self);
- if (priv->min_width_set && min_width == info->min_width)
+ if (priv->min_width_set && min_width == info->minimum.width)
return;
g_object_freeze_notify (G_OBJECT (self));
clutter_actor_store_old_geometry (self, &old);
- info->min_width = min_width;
+ info->minimum.width = min_width;
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH]);
clutter_actor_set_min_width_set (self, TRUE);
info = _clutter_actor_get_layout_info (self);
- if (priv->min_height_set && min_height == info->min_height)
+ if (priv->min_height_set && min_height == info->minimum.height)
return;
g_object_freeze_notify (G_OBJECT (self));
clutter_actor_store_old_geometry (self, &old);
- info->min_height = min_height;
+ info->minimum.height = min_height;
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT]);
clutter_actor_set_min_height_set (self, TRUE);
info = _clutter_actor_get_layout_info (self);
- if (priv->natural_width_set && natural_width == info->natural_width)
+ if (priv->natural_width_set && natural_width == info->natural.width)
return;
g_object_freeze_notify (G_OBJECT (self));
clutter_actor_store_old_geometry (self, &old);
- info->natural_width = natural_width;
+ info->natural.width = natural_width;
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH]);
clutter_actor_set_natural_width_set (self, TRUE);
info = _clutter_actor_get_layout_info (self);
- if (priv->natural_height_set && natural_height == info->natural_height)
+ if (priv->natural_height_set && natural_height == info->natural.height)
return;
g_object_freeze_notify (G_OBJECT (self));
clutter_actor_store_old_geometry (self, &old);
- info->natural_height = natural_height;
+ info->natural.height = natural_height;
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT]);
clutter_actor_set_natural_height_set (self, TRUE);
}
}
+static void
+clutter_actor_set_size_internal (ClutterActor *self,
+ const ClutterSize *size)
+{
+ if (size != NULL)
+ {
+ clutter_actor_set_width_internal (self, size->width);
+ clutter_actor_set_height_internal (self, size->height);
+ }
+ else
+ {
+ clutter_actor_set_width_internal (self, -1);
+ clutter_actor_set_height_internal (self, -1);
+ }
+}
+
/**
* clutter_actor_set_size:
* @self: A #ClutterActor
gfloat width,
gfloat height)
{
+ ClutterSize new_size;
+
g_return_if_fail (CLUTTER_IS_ACTOR (self));
- g_object_freeze_notify (G_OBJECT (self));
+ clutter_size_init (&new_size, width, height);
+
+ if (_clutter_actor_get_transition (self, obj_props[PROP_SIZE]) == NULL)
+ {
+ /* minor optimization: if we don't have a duration then we can
+ * skip the get_size() below, to avoid the chance of going through
+ * get_preferred_width() and get_preferred_height() just to jump to
+ * a new desired size
+ */
+ if (clutter_actor_get_easing_duration (self) == 0)
+ {
+ g_object_freeze_notify (G_OBJECT (self));
- clutter_actor_set_width (self, width);
- clutter_actor_set_height (self, height);
+ clutter_actor_set_size_internal (self, &new_size);
- g_object_thaw_notify (G_OBJECT (self));
+ g_object_thaw_notify (G_OBJECT (self));
+
+ return;
+ }
+ else
+ {
+ ClutterSize cur_size;
+
+ clutter_size_init (&cur_size,
+ clutter_actor_get_width (self),
+ clutter_actor_get_height (self));
+
+ _clutter_actor_create_transition (self,
+ obj_props[PROP_SIZE],
+ &cur_size,
+ &new_size);
+ }
+ }
+ else
+ _clutter_actor_update_transition (self, obj_props[PROP_SIZE], &new_size);
+
+ clutter_actor_queue_relayout (self);
}
/**
{
g_return_if_fail (CLUTTER_IS_ACTOR (self));
- if (clutter_actor_get_easing_duration (self) != 0)
+ if (_clutter_actor_get_transition (self, obj_props[PROP_WIDTH]) == NULL)
{
- ClutterTransition *transition;
+ float cur_size;
- transition = _clutter_actor_get_transition (self, obj_props[PROP_WIDTH]);
- if (transition == NULL)
+ /* minor optimization: if we don't have a duration
+ * then we can skip the get_width() below, to avoid
+ * the chance of going through get_preferred_width()
+ * just to jump to a new desired width.
+ */
+ if (clutter_actor_get_easing_duration (self) == 0)
{
- float old_width = clutter_actor_get_width (self);
+ g_object_freeze_notify (G_OBJECT (self));
+
+ clutter_actor_set_width_internal (self, width);
- transition = _clutter_actor_create_transition (self,
- obj_props[PROP_WIDTH],
- old_width,
- width);
- clutter_timeline_start (CLUTTER_TIMELINE (transition));
+ g_object_thaw_notify (G_OBJECT (self));
+
+ return;
}
else
- _clutter_actor_update_transition (self, obj_props[PROP_WIDTH], width);
+ cur_size = clutter_actor_get_width (self);
- clutter_actor_queue_relayout (self);
+ _clutter_actor_create_transition (self,
+ obj_props[PROP_WIDTH],
+ cur_size,
+ width);
}
else
- {
- g_object_freeze_notify (G_OBJECT (self));
-
- clutter_actor_set_width_internal (self, width);
-
- g_object_thaw_notify (G_OBJECT (self));
- }
+ _clutter_actor_update_transition (self, obj_props[PROP_WIDTH], width);
}
/**
{
g_return_if_fail (CLUTTER_IS_ACTOR (self));
- if (clutter_actor_get_easing_duration (self) != 0)
+ if (_clutter_actor_get_transition (self, obj_props[PROP_HEIGHT]) == NULL)
{
- ClutterTransition *transition;
+ float cur_size;
- transition = _clutter_actor_get_transition (self, obj_props[PROP_HEIGHT]);
- if (transition == NULL)
+ /* see the comment in clutter_actor_set_width() above */
+ if (clutter_actor_get_easing_duration (self) == 0)
{
- float old_height = clutter_actor_get_height (self);
+ g_object_freeze_notify (G_OBJECT (self));
+
+ clutter_actor_set_height_internal (self, height);
- transition = _clutter_actor_create_transition (self,
- obj_props[PROP_HEIGHT],
- old_height,
- height);
- clutter_timeline_start (CLUTTER_TIMELINE (transition));
+ g_object_thaw_notify (G_OBJECT (self));
+
+ return;
}
else
- _clutter_actor_update_transition (self, obj_props[PROP_HEIGHT], height);
+ cur_size = clutter_actor_get_height (self);
- clutter_actor_queue_relayout (self);
+ _clutter_actor_create_transition (self,
+ obj_props[PROP_HEIGHT],
+ cur_size,
+ height);
}
else
- {
- g_object_freeze_notify (G_OBJECT (self));
-
- clutter_actor_set_height_internal (self, height);
-
- g_object_thaw_notify (G_OBJECT (self));
- }
+ _clutter_actor_update_transition (self, obj_props[PROP_HEIGHT], height);
}
static inline void
linfo = _clutter_actor_get_layout_info (self);
- if (priv->position_set && linfo->fixed_x == x)
+ if (priv->position_set && linfo->fixed_pos.x == x)
return;
clutter_actor_store_old_geometry (self, &old);
- linfo->fixed_x = x;
+ linfo->fixed_pos.x = x;
clutter_actor_set_fixed_position_set (self, TRUE);
clutter_actor_notify_if_geometry_changed (self, &old);
linfo = _clutter_actor_get_layout_info (self);
- if (priv->position_set && linfo->fixed_y == y)
+ if (priv->position_set && linfo->fixed_pos.y == y)
return;
clutter_actor_store_old_geometry (self, &old);
- linfo->fixed_y = y;
+ linfo->fixed_pos.y = y;
clutter_actor_set_fixed_position_set (self, TRUE);
clutter_actor_notify_if_geometry_changed (self, &old);
+
+ clutter_actor_queue_relayout (self);
}
-/**
- * clutter_actor_set_x:
- * @self: a #ClutterActor
- * @x: the actor's position on the X axis
- *
- * Sets the actor's X coordinate, relative to its parent, in pixels.
- *
- * Overrides any layout manager and forces a fixed position for
- * the actor.
- *
- * The #ClutterActor:x property is animatable.
- *
- * Since: 0.6
+static void
+clutter_actor_set_position_internal (ClutterActor *self,
+ const ClutterPoint *position)
+{
+ ClutterActorPrivate *priv = self->priv;
+ ClutterLayoutInfo *linfo;
+ ClutterActorBox old = { 0, };
+
+ linfo = _clutter_actor_get_layout_info (self);
+
+ if (priv->position_set &&
+ clutter_point_equals (position, &linfo->fixed_pos))
+ return;
+
+ clutter_actor_store_old_geometry (self, &old);
+
+ if (position != NULL)
+ {
+ linfo->fixed_pos = *position;
+ clutter_actor_set_fixed_position_set (self, TRUE);
+ }
+ else
+ clutter_actor_set_fixed_position_set (self, FALSE);
+
+ clutter_actor_notify_if_geometry_changed (self, &old);
+
+ clutter_actor_queue_relayout (self);
+}
+
+/**
+ * clutter_actor_set_x:
+ * @self: a #ClutterActor
+ * @x: the actor's position on the X axis
+ *
+ * Sets the actor's X coordinate, relative to its parent, in pixels.
+ *
+ * Overrides any layout manager and forces a fixed position for
+ * the actor.
+ *
+ * The #ClutterActor:x property is animatable.
+ *
+ * Since: 0.6
*/
void
clutter_actor_set_x (ClutterActor *self,
gfloat x)
{
- const ClutterLayoutInfo *linfo;
-
g_return_if_fail (CLUTTER_IS_ACTOR (self));
- linfo = _clutter_actor_get_layout_info_or_defaults (self);
-
- if (clutter_actor_get_easing_duration (self) != 0)
+ if (_clutter_actor_get_transition (self, obj_props[PROP_X]) == NULL)
{
- ClutterTransition *transition;
-
- transition = _clutter_actor_get_transition (self, obj_props[PROP_X]);
- if (transition == NULL)
- {
- transition = _clutter_actor_create_transition (self,
- obj_props[PROP_X],
- linfo->fixed_x,
- x);
-
- clutter_timeline_start (CLUTTER_TIMELINE (transition));
- }
- else
- _clutter_actor_update_transition (self, obj_props[PROP_X], x);
+ float cur_position = clutter_actor_get_x (self);
- clutter_actor_queue_relayout (self);
+ _clutter_actor_create_transition (self, obj_props[PROP_X],
+ cur_position,
+ x);
}
else
- clutter_actor_set_x_internal (self, x);
+ _clutter_actor_update_transition (self, obj_props[PROP_X], x);
}
/**
clutter_actor_set_y (ClutterActor *self,
gfloat y)
{
- const ClutterLayoutInfo *linfo;
-
g_return_if_fail (CLUTTER_IS_ACTOR (self));
- linfo = _clutter_actor_get_layout_info_or_defaults (self);
-
- if (clutter_actor_get_easing_duration (self) != 0)
+ if (_clutter_actor_get_transition (self, obj_props[PROP_Y]) == NULL)
{
- ClutterTransition *transition;
-
- transition = _clutter_actor_get_transition (self, obj_props[PROP_Y]);
- if (transition == NULL)
- {
- transition = _clutter_actor_create_transition (self,
- obj_props[PROP_Y],
- linfo->fixed_y,
- y);
-
- clutter_timeline_start (CLUTTER_TIMELINE (transition));
- }
- else
- _clutter_actor_update_transition (self, obj_props[PROP_Y], y);
+ float cur_position = clutter_actor_get_y (self);
- clutter_actor_queue_relayout (self);
+ _clutter_actor_create_transition (self, obj_props[PROP_Y],
+ cur_position,
+ y);
}
else
- clutter_actor_set_y_internal (self, y);
-
- clutter_actor_queue_relayout (self);
+ _clutter_actor_update_transition (self, obj_props[PROP_Y], y);
}
/**
info = _clutter_actor_get_layout_info_or_defaults (self);
- return info->fixed_x;
+ return info->fixed_pos.x;
}
else
return 0;
info = _clutter_actor_get_layout_info_or_defaults (self);
- return info->fixed_y;
+ return info->fixed_pos.y;
}
else
return 0;
gdouble scale_y,
ClutterGravity gravity)
{
- ClutterTransformInfo *info;
- GObject *obj;
-
g_return_if_fail (CLUTTER_IS_ACTOR (self));
- obj = G_OBJECT (self);
-
- g_object_freeze_notify (obj);
-
- info = _clutter_actor_get_transform_info (self);
- info->scale_x = scale_x;
- info->scale_y = scale_y;
-
- if (gravity == CLUTTER_GRAVITY_NONE)
- clutter_anchor_coord_set_units (&info->scale_center, 0, 0, 0);
- else
- clutter_anchor_coord_set_gravity (&info->scale_center, gravity);
-
- self->priv->transform_valid = FALSE;
-
- g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_X]);
- g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_Y]);
- g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_X]);
- g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_Y]);
- g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_GRAVITY]);
+ g_object_freeze_notify (G_OBJECT (self));
- clutter_actor_queue_redraw (self);
+ clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x);
+ clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y);
+ clutter_actor_set_scale_gravity (self, gravity);
- g_object_thaw_notify (obj);
+ g_object_thaw_notify (G_OBJECT (self));
}
/**
clutter_actor_set_opacity (ClutterActor *self,
guint8 opacity)
{
- ClutterActorPrivate *priv;
-
g_return_if_fail (CLUTTER_IS_ACTOR (self));
- priv = self->priv;
-
- if (clutter_actor_get_easing_duration (self) != 0)
+ if (_clutter_actor_get_transition (self, obj_props[PROP_OPACITY]) == NULL)
{
- ClutterTransition *transition;
-
- transition = _clutter_actor_get_transition (self, obj_props[PROP_OPACITY]);
- if (transition == NULL)
- {
- transition = _clutter_actor_create_transition (self,
- obj_props[PROP_OPACITY],
- priv->opacity,
- opacity);
- clutter_timeline_start (CLUTTER_TIMELINE (transition));
- }
- else
- _clutter_actor_update_transition (self, obj_props[PROP_OPACITY], opacity);
-
- clutter_actor_queue_redraw (self);
+ _clutter_actor_create_transition (self, obj_props[PROP_OPACITY],
+ self->priv->opacity,
+ opacity);
}
else
- clutter_actor_set_opacity_internal (self, opacity);
+ _clutter_actor_update_transition (self, obj_props[PROP_OPACITY], opacity);
}
/*
clutter_actor_set_depth (ClutterActor *self,
gfloat depth)
{
- const ClutterTransformInfo *tinfo;
-
g_return_if_fail (CLUTTER_IS_ACTOR (self));
- tinfo = _clutter_actor_get_transform_info_or_defaults (self);
-
- if (clutter_actor_get_easing_duration (self) != 0)
+ if (_clutter_actor_get_transition (self, obj_props[PROP_DEPTH]) == NULL)
{
- ClutterTransition *transition;
+ const ClutterTransformInfo *info;
- transition = _clutter_actor_get_transition (self, obj_props[PROP_DEPTH]);
- if (transition == NULL)
- {
- transition = _clutter_actor_create_transition (self, obj_props[PROP_DEPTH],
- tinfo->depth,
- depth);
- clutter_timeline_start (CLUTTER_TIMELINE (transition));
- }
- else
- _clutter_actor_update_transition (self, obj_props[PROP_DEPTH], depth);
+ info = _clutter_actor_get_transform_info_or_defaults (self);
- clutter_actor_queue_redraw (self);
+ _clutter_actor_create_transition (self, obj_props[PROP_DEPTH],
+ info->depth,
+ depth);
}
else
- clutter_actor_set_depth_internal (self, depth);
+ _clutter_actor_update_transition (self, obj_props[PROP_DEPTH], depth);
+
+ clutter_actor_queue_redraw (self);
}
/**
gpointer data);
typedef enum {
- ADD_CHILD_CREATE_META = 1 << 0,
- ADD_CHILD_EMIT_PARENT_SET = 1 << 1,
- ADD_CHILD_EMIT_ACTOR_ADDED = 1 << 2,
- ADD_CHILD_CHECK_STATE = 1 << 3,
- ADD_CHILD_NOTIFY_FIRST_LAST = 1 << 4,
+ ADD_CHILD_CREATE_META = 1 << 0,
+ ADD_CHILD_EMIT_PARENT_SET = 1 << 1,
+ ADD_CHILD_EMIT_ACTOR_ADDED = 1 << 2,
+ ADD_CHILD_CHECK_STATE = 1 << 3,
+ ADD_CHILD_NOTIFY_FIRST_LAST = 1 << 4,
+ ADD_CHILD_SHOW_ON_SET_PARENT = 1 << 5,
/* default flags for public API */
ADD_CHILD_DEFAULT_FLAGS = ADD_CHILD_CREATE_META |
ADD_CHILD_EMIT_PARENT_SET |
ADD_CHILD_EMIT_ACTOR_ADDED |
ADD_CHILD_CHECK_STATE |
- ADD_CHILD_NOTIFY_FIRST_LAST,
+ ADD_CHILD_NOTIFY_FIRST_LAST |
+ ADD_CHILD_SHOW_ON_SET_PARENT,
/* flags for legacy/deprecated API */
ADD_CHILD_LEGACY_FLAGS = ADD_CHILD_EMIT_PARENT_SET |
ADD_CHILD_CHECK_STATE |
- ADD_CHILD_NOTIFY_FIRST_LAST
+ ADD_CHILD_NOTIFY_FIRST_LAST |
+ ADD_CHILD_SHOW_ON_SET_PARENT
} ClutterActorAddChildFlags;
/*< private >
gboolean emit_parent_set, emit_actor_added;
gboolean check_state;
gboolean notify_first_last;
+ gboolean show_on_set_parent;
ClutterActor *old_first_child, *old_last_child;
if (child->priv->parent != NULL)
emit_actor_added = (flags & ADD_CHILD_EMIT_ACTOR_ADDED) != 0;
check_state = (flags & ADD_CHILD_CHECK_STATE) != 0;
notify_first_last = (flags & ADD_CHILD_NOTIFY_FIRST_LAST) != 0;
+ show_on_set_parent = (flags & ADD_CHILD_SHOW_ON_SET_PARENT) != 0;
old_first_child = self->priv->first_child;
old_last_child = self->priv->last_child;
if (self->priv->internal_child)
CLUTTER_SET_PRIVATE_FLAGS (child, CLUTTER_INTERNAL_CHILD);
+ /* children may cause their parent to expand, if they are set
+ * to expand; if a child is not expanded then it cannot change
+ * its parent's state. any further change later on will queue
+ * an expand state check.
+ *
+ * this check, with the initial state of the needs_compute_expand
+ * flag set to FALSE, should avoid recomputing the expand flags
+ * state while building the actor tree.
+ */
+ if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
+ (child->priv->needs_compute_expand ||
+ child->priv->needs_x_expand ||
+ child->priv->needs_y_expand))
+ {
+ clutter_actor_queue_compute_expand (self);
+ }
+
/* clutter_actor_reparent() will emit ::parent-set for us */
if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL);
clutter_actor_set_text_direction (child, text_dir);
}
- if (child->priv->show_on_set_parent)
+ if (show_on_set_parent && child->priv->show_on_set_parent)
clutter_actor_show (child);
if (CLUTTER_ACTOR_IS_MAPPED (child))
if (sibling != NULL)
g_return_if_fail (sibling->priv->parent == self);
+ if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
+ CLUTTER_ACTOR_IN_DESTRUCTION (child) ||
+ (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling)))
+ return;
+
/* we don't want to change the state of child, or emit signals, or
* regenerate ChildMeta instances here, but we still want to follow
* the correct sequence of steps encoded in remove_child() and
if (sibling != NULL)
g_return_if_fail (sibling->priv->parent == self);
+ if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
+ CLUTTER_ACTOR_IN_DESTRUCTION (child) ||
+ (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling)))
+ return;
+
/* see the comment in set_child_above_sibling() */
g_object_ref (child);
clutter_actor_remove_child_internal (self, child, 0);
g_return_if_fail (child->priv->parent == self);
g_return_if_fail (index_ <= self->priv->n_children);
+ if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
+ CLUTTER_ACTOR_IN_DESTRUCTION (child))
+ return;
+
g_object_ref (child);
clutter_actor_remove_child_internal (self, child, 0);
clutter_actor_add_child_internal (self, child,
}
static void
+clutter_actor_store_content_box (ClutterActor *self,
+ const ClutterActorBox *box)
+{
+ if (box != NULL)
+ {
+ self->priv->content_box = *box;
+ self->priv->content_box_valid = TRUE;
+ }
+ else
+ self->priv->content_box_valid = FALSE;
+
+ clutter_actor_queue_redraw (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
+}
+
+static void
clutter_container_iface_init (ClutterContainerIface *iface)
{
/* we don't override anything, as ClutterContainer already has a default
ParseDimension dimension,
JsonNode *node)
{
- GValue value = { 0, };
+ GValue value = G_VALUE_INIT;
gfloat retval = 0;
if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
return g_slist_reverse (retval);
}
+static ClutterMargin *
+parse_margin (ClutterActor *self,
+ JsonNode *node)
+{
+ ClutterMargin *margin;
+ JsonArray *array;
+
+ if (!JSON_NODE_HOLDS_ARRAY (node))
+ {
+ g_warning ("The margin property must be an array of 1 to 4 elements");
+ return NULL;
+ }
+
+ margin = clutter_margin_new ();
+ array = json_node_get_array (node);
+ switch (json_array_get_length (array))
+ {
+ case 1:
+ margin->top = margin->right = margin->bottom = margin->left =
+ parse_units (self, 0, json_array_get_element (array, 0));
+ break;
+
+ case 2:
+ margin->top = margin->bottom =
+ parse_units (self, 0, json_array_get_element (array, 0));
+ margin->right = margin->left =
+ parse_units (self, 0, json_array_get_element (array, 1));
+ break;
+
+ case 3:
+ margin->top =
+ parse_units (self, 0, json_array_get_element (array, 0));
+ margin->right = margin->left =
+ parse_units (self, 0, json_array_get_element (array, 1));
+ margin->bottom =
+ parse_units (self, 0, json_array_get_element (array, 2));
+ break;
+
+ case 4:
+ margin->top =
+ parse_units (self, 0, json_array_get_element (array, 0));
+ margin->right =
+ parse_units (self, 0, json_array_get_element (array, 1));
+ margin->bottom =
+ parse_units (self, 0, json_array_get_element (array, 2));
+ margin->left =
+ parse_units (self, 0, json_array_get_element (array, 3));
+ break;
+
+ default:
+ g_warning ("The margin property must be an array of 1 to 4 elements");
+ clutter_margin_free (margin);
+ return NULL;
+ }
+ return margin;
+}
+
static gboolean
clutter_actor_parse_custom_node (ClutterScriptable *scriptable,
ClutterScript *script,
retval = TRUE;
}
+ else if (strcmp (name, "margin") == 0)
+ {
+ ClutterMargin *margin = parse_margin (actor, node);
+
+ if (margin)
+ {
+ g_value_init (value, CLUTTER_TYPE_MARGIN);
+ g_value_set_boxed (value, margin);
+ retval = TRUE;
+ }
+ }
return retval;
}
return;
}
+ if (strcmp (name, "margin") == 0)
+ {
+ clutter_actor_set_margin (actor, g_value_get_boxed (value));
+ return;
+ }
g_object_set_property (G_OBJECT (scriptable), name, value);
}
const GValue *value,
GParamSpec *pspec)
{
+ GObject *obj = G_OBJECT (actor);
+
+ g_object_freeze_notify (obj);
+
switch (prop_id)
{
case PROP_X:
clutter_actor_set_y_internal (actor, g_value_get_float (value));
break;
+ case PROP_POSITION:
+ clutter_actor_set_position_internal (actor, g_value_get_boxed (value));
+ break;
+
case PROP_WIDTH:
clutter_actor_set_width_internal (actor, g_value_get_float (value));
break;
clutter_actor_set_height_internal (actor, g_value_get_float (value));
break;
+ case PROP_SIZE:
+ clutter_actor_set_size_internal (actor, g_value_get_boxed (value));
+ break;
+
+ case PROP_ALLOCATION:
+ clutter_actor_allocate_internal (actor,
+ g_value_get_boxed (value),
+ actor->priv->allocation_flags);
+ break;
+
case PROP_DEPTH:
clutter_actor_set_depth_internal (actor, g_value_get_float (value));
break;
g_value_get_double (value));
break;
+ case PROP_CONTENT_BOX:
+ clutter_actor_store_content_box (actor, g_value_get_boxed (value));
+ break;
+
default:
- g_object_set_property (G_OBJECT (actor), pspec->name, value);
+ g_object_set_property (obj, pspec->name, value);
break;
}
+
+ g_object_thaw_notify (obj);
}
static void
clutter_actor_set_animatable_property (actor, pspec->param_id, final, pspec);
}
else
- g_object_set_property (G_OBJECT (animatable), property_name, final);
+ g_object_set_property (G_OBJECT (animatable), pspec->name, final);
}
g_free (p_name);
return TRUE;
}
-/*
- * ClutterGeometry
- */
-
-static ClutterGeometry*
-clutter_geometry_copy (const ClutterGeometry *geometry)
-{
- return g_slice_dup (ClutterGeometry, geometry);
-}
-
-static void
-clutter_geometry_free (ClutterGeometry *geometry)
-{
- if (G_LIKELY (geometry != NULL))
- g_slice_free (ClutterGeometry, geometry);
-}
-
-/**
- * clutter_geometry_union:
- * @geometry_a: a #ClutterGeometry
- * @geometry_b: another #ClutterGeometry
- * @result: (out): location to store the result
- *
- * Find the union of two rectangles represented as #ClutterGeometry.
- *
- * Since: 1.4
- */
-void
-clutter_geometry_union (const ClutterGeometry *geometry_a,
- const ClutterGeometry *geometry_b,
- ClutterGeometry *result)
-{
- /* We don't try to handle rectangles that can't be represented
- * as a signed integer box */
- gint x_1 = MIN (geometry_a->x, geometry_b->x);
- gint y_1 = MIN (geometry_a->y, geometry_b->y);
- gint x_2 = MAX (geometry_a->x + (gint)geometry_a->width,
- geometry_b->x + (gint)geometry_b->width);
- gint y_2 = MAX (geometry_a->y + (gint)geometry_a->height,
- geometry_b->y + (gint)geometry_b->height);
- result->x = x_1;
- result->y = y_1;
- result->width = x_2 - x_1;
- result->height = y_2 - y_1;
-}
-
/**
- * clutter_geometry_intersects:
- * @geometry0: The first geometry to test
- * @geometry1: The second geometry to test
+ * clutter_actor_is_rotated:
+ * @self: a #ClutterActor
*
- * Determines if @geometry0 and geometry1 intersect returning %TRUE if
- * they do else %FALSE.
+ * Checks whether any rotation is applied to the actor.
*
- * Return value: %TRUE of @geometry0 and geometry1 intersect else
- * %FALSE.
+ * Return value: %TRUE if the actor is rotated.
*
- * Since: 1.4
+ * Since: 0.6
*/
gboolean
-clutter_geometry_intersects (const ClutterGeometry *geometry0,
- const ClutterGeometry *geometry1)
-{
- if (geometry1->x >= (geometry0->x + (gint)geometry0->width) ||
- geometry1->y >= (geometry0->y + (gint)geometry0->height) ||
- (geometry1->x + (gint)geometry1->width) <= geometry0->x ||
- (geometry1->y + (gint)geometry1->height) <= geometry0->y)
- return FALSE;
- else
- return TRUE;
-}
-
-static gboolean
-clutter_geometry_progress (const GValue *a,
- const GValue *b,
- gdouble progress,
- GValue *retval)
+clutter_actor_is_rotated (ClutterActor *self)
{
- const ClutterGeometry *a_geom = g_value_get_boxed (a);
- const ClutterGeometry *b_geom = g_value_get_boxed (b);
- ClutterGeometry res = { 0, };
- gint a_width = a_geom->width;
- gint b_width = b_geom->width;
- gint a_height = a_geom->height;
- gint b_height = b_geom->height;
+ const ClutterTransformInfo *info;
- res.x = a_geom->x + (b_geom->x - a_geom->x) * progress;
- res.y = a_geom->y + (b_geom->y - a_geom->y) * progress;
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
- res.width = a_width + (b_width - a_width) * progress;
- res.height = a_height + (b_height - a_height) * progress;
+ info = _clutter_actor_get_transform_info_or_defaults (self);
- g_value_set_boxed (retval, &res);
+ if (info->rx_angle || info->ry_angle || info->rz_angle)
+ return TRUE;
- return TRUE;
+ return FALSE;
}
-G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterGeometry, clutter_geometry,
- clutter_geometry_copy,
- clutter_geometry_free,
- CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_geometry_progress));
-
-/*
- * ClutterVertices
- */
-
/**
- * clutter_vertex_new:
- * @x: X coordinate
- * @y: Y coordinate
- * @z: Z coordinate
+ * clutter_actor_is_scaled:
+ * @self: a #ClutterActor
*
- * Creates a new #ClutterVertex for the point in 3D space
- * identified by the 3 coordinates @x, @y, @z
+ * Checks whether the actor is scaled in either dimension.
*
- * Return value: the newly allocate #ClutterVertex. Use
- * clutter_vertex_free() to free the resources
+ * Return value: %TRUE if the actor is scaled.
*
- * Since: 1.0
+ * Since: 0.6
*/
-ClutterVertex *
-clutter_vertex_new (gfloat x,
- gfloat y,
- gfloat z)
+gboolean
+clutter_actor_is_scaled (ClutterActor *self)
{
- ClutterVertex *vertex;
+ const ClutterTransformInfo *info;
- vertex = g_slice_new (ClutterVertex);
- vertex->x = x;
- vertex->y = y;
- vertex->z = z;
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
- return vertex;
-}
+ info = _clutter_actor_get_transform_info_or_defaults (self);
-/**
- * clutter_vertex_copy:
- * @vertex: a #ClutterVertex
- *
- * Copies @vertex
- *
- * Return value: a newly allocated copy of #ClutterVertex. Use
- * clutter_vertex_free() to free the allocated resources
- *
- * Since: 1.0
- */
-ClutterVertex *
-clutter_vertex_copy (const ClutterVertex *vertex)
-{
- if (G_LIKELY (vertex != NULL))
- return g_slice_dup (ClutterVertex, vertex);
+ if (info->scale_x != 1.0 || info->scale_y != 1.0)
+ return TRUE;
- return NULL;
+ return FALSE;
}
-/**
- * clutter_vertex_free:
- * @vertex: a #ClutterVertex
- *
- * Frees a #ClutterVertex allocated using clutter_vertex_copy()
- *
- * Since: 1.0
- */
-void
-clutter_vertex_free (ClutterVertex *vertex)
+ClutterActor *
+_clutter_actor_get_stage_internal (ClutterActor *actor)
{
- if (G_UNLIKELY (vertex != NULL))
- g_slice_free (ClutterVertex, vertex);
+ while (actor && !CLUTTER_ACTOR_IS_TOPLEVEL (actor))
+ actor = actor->priv->parent;
+
+ return actor;
}
/**
- * clutter_vertex_equal:
- * @vertex_a: a #ClutterVertex
- * @vertex_b: a #ClutterVertex
+ * clutter_actor_get_stage:
+ * @actor: a #ClutterActor
*
- * Compares @vertex_a and @vertex_b for equality
+ * Retrieves the #ClutterStage where @actor is contained.
*
- * Return value: %TRUE if the passed #ClutterVertex are equal
+ * Return value: (transfer none) (type Clutter.Stage): the stage
+ * containing the actor, or %NULL
*
- * Since: 1.0
+ * Since: 0.8
*/
-gboolean
-clutter_vertex_equal (const ClutterVertex *vertex_a,
- const ClutterVertex *vertex_b)
+ClutterActor *
+clutter_actor_get_stage (ClutterActor *actor)
{
- g_return_val_if_fail (vertex_a != NULL && vertex_b != NULL, FALSE);
-
- if (vertex_a == vertex_b)
- return TRUE;
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
- return vertex_a->x == vertex_b->x &&
- vertex_a->y == vertex_b->y &&
- vertex_a->z == vertex_b->z;
-}
-
-static gboolean
-clutter_vertex_progress (const GValue *a,
- const GValue *b,
- gdouble progress,
- GValue *retval)
-{
- const ClutterVertex *av = g_value_get_boxed (a);
- const ClutterVertex *bv = g_value_get_boxed (b);
- ClutterVertex res = { 0, };
-
- res.x = av->x + (bv->x - av->x) * progress;
- res.y = av->y + (bv->y - av->y) * progress;
- res.z = av->z + (bv->z - av->z) * progress;
-
- g_value_set_boxed (retval, &res);
-
- return TRUE;
-}
-
-G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterVertex, clutter_vertex,
- clutter_vertex_copy,
- clutter_vertex_free,
- CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_vertex_progress));
-
-/**
- * clutter_actor_is_rotated:
- * @self: a #ClutterActor
- *
- * Checks whether any rotation is applied to the actor.
- *
- * Return value: %TRUE if the actor is rotated.
- *
- * Since: 0.6
- */
-gboolean
-clutter_actor_is_rotated (ClutterActor *self)
-{
- const ClutterTransformInfo *info;
-
- g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
-
- info = _clutter_actor_get_transform_info_or_defaults (self);
-
- if (info->rx_angle || info->ry_angle || info->rz_angle)
- return TRUE;
-
- return FALSE;
-}
-
-/**
- * clutter_actor_is_scaled:
- * @self: a #ClutterActor
- *
- * Checks whether the actor is scaled in either dimension.
- *
- * Return value: %TRUE if the actor is scaled.
- *
- * Since: 0.6
- */
-gboolean
-clutter_actor_is_scaled (ClutterActor *self)
-{
- const ClutterTransformInfo *info;
-
- g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
-
- info = _clutter_actor_get_transform_info_or_defaults (self);
-
- if (info->scale_x != 1.0 || info->scale_y != 1.0)
- return TRUE;
-
- return FALSE;
-}
-
-ClutterActor *
-_clutter_actor_get_stage_internal (ClutterActor *actor)
-{
- while (actor && !CLUTTER_ACTOR_IS_TOPLEVEL (actor))
- actor = actor->priv->parent;
-
- return actor;
-}
-
-/**
- * clutter_actor_get_stage:
- * @actor: a #ClutterActor
- *
- * Retrieves the #ClutterStage where @actor is contained.
- *
- * Return value: (transfer none) (type Clutter.Stage): the stage
- * containing the actor, or %NULL
- *
- * Since: 0.8
- */
-ClutterActor *
-clutter_actor_get_stage (ClutterActor *actor)
-{
- g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
-
- return _clutter_actor_get_stage_internal (actor);
+ return _clutter_actor_get_stage_internal (actor);
}
/**
_clutter_meta_group_remove_meta (priv->actions, CLUTTER_ACTOR_META (action));
+ if (_clutter_meta_group_peek_metas (priv->actions) == NULL)
+ g_clear_object (&priv->actions);
+
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
}
_clutter_meta_group_remove_meta (priv->constraints,
CLUTTER_ACTOR_META (constraint));
+
+ if (_clutter_meta_group_peek_metas (priv->constraints) == NULL)
+ g_clear_object (&priv->constraints);
+
clutter_actor_queue_relayout (self);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
gboolean
clutter_actor_has_effects (ClutterActor *self)
{
- g_return_val_if_fail (CLUTTER_IS_ACTOR (self), TRUE);
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
if (self->priv->effects == NULL)
return FALSE;
gboolean
clutter_actor_has_constraints (ClutterActor *self)
{
- g_return_val_if_fail (CLUTTER_IS_ACTOR (self), TRUE);
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
return self->priv->constraints != NULL;
}
gboolean
clutter_actor_has_actions (ClutterActor *self)
{
- g_return_val_if_fail (CLUTTER_IS_ACTOR (self), TRUE);
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
return self->priv->actions != NULL;
}
ClutterForeachCallback callback,
gpointer user_data)
{
- ClutterActorPrivate *priv = self->priv;
ClutterActor *iter;
gboolean cont;
- for (cont = TRUE, iter = priv->first_child;
- cont && iter != NULL;
- iter = iter->priv->next_sibling)
+ if (self->priv->first_child == NULL)
+ return TRUE;
+
+ cont = TRUE;
+ iter = self->priv->first_child;
+
+ /* we use this form so that it's safe to change the children
+ * list while iterating it
+ */
+ while (cont && iter != NULL)
{
+ ClutterActor *next = iter->priv->next_sibling;
+
cont = callback (iter, user_data);
+
+ iter = next;
}
return cont;
G_CALLBACK (on_layout_manager_changed),
self);
clutter_layout_manager_set_container (priv->layout_manager, NULL);
- g_object_unref (priv->layout_manager);
+ g_clear_object (&priv->layout_manager);
}
priv->layout_manager = manager;
}
static const ClutterLayoutInfo default_layout_info = {
- 0.f, /* fixed-x */
- 0.f, /* fixed-y */
+ CLUTTER_POINT_INIT_ZERO, /* fixed-pos */
{ 0, 0, 0, 0 }, /* margin */
CLUTTER_ACTOR_ALIGN_FILL, /* x-align */
CLUTTER_ACTOR_ALIGN_FILL, /* y-align */
- 0.f, 0.f, /* min_width, natural_width */
- 0.f, 0.f, /* natual_width, natural_height */
+ FALSE, FALSE, /* expand */
+ CLUTTER_SIZE_INIT_ZERO, /* minimum */
+ CLUTTER_SIZE_INIT_ZERO, /* natural */
};
static void
return _clutter_actor_get_layout_info_or_defaults (self)->y_align;
}
-
-/**
- * clutter_margin_new:
- *
- * Creates a new #ClutterMargin.
- *
- * Return value: (transfer full): a newly allocated #ClutterMargin. Use
- * clutter_margin_free() to free the resources associated with it when
- * done.
- *
- * Since: 1.10
- */
-ClutterMargin *
-clutter_margin_new (void)
-{
- return g_slice_new0 (ClutterMargin);
-}
-
-/**
- * clutter_margin_copy:
- * @margin_: a #ClutterMargin
- *
- * Creates a new #ClutterMargin and copies the contents of @margin_ into
- * the newly created structure.
- *
- * Return value: (transfer full): a copy of the #ClutterMargin.
- *
- * Since: 1.10
- */
-ClutterMargin *
-clutter_margin_copy (const ClutterMargin *margin_)
-{
- if (G_LIKELY (margin_ != NULL))
- return g_slice_dup (ClutterMargin, margin_);
-
- return NULL;
-}
-
-/**
- * clutter_margin_free:
- * @margin_: a #ClutterMargin
- *
- * Frees the resources allocated by clutter_margin_new() and
- * clutter_margin_copy().
- *
- * Since: 1.10
- */
-void
-clutter_margin_free (ClutterMargin *margin_)
-{
- if (G_LIKELY (margin_ != NULL))
- g_slice_free (ClutterMargin, margin_);
-}
-
-G_DEFINE_BOXED_TYPE (ClutterMargin, clutter_margin,
- clutter_margin_copy,
- clutter_margin_free)
-
/**
* clutter_actor_set_margin:
* @self: a #ClutterActor
}
bg_color_pspec = obj_props[PROP_BACKGROUND_COLOR];
- if (clutter_actor_get_easing_duration (self) != 0)
+ if (_clutter_actor_get_transition (self, bg_color_pspec) == NULL)
{
- ClutterTransition *transition;
-
- transition = _clutter_actor_get_transition (self, bg_color_pspec);
- if (transition == NULL)
- {
- transition = _clutter_actor_create_transition (self, bg_color_pspec,
- &priv->bg_color,
- color);
- clutter_timeline_start (CLUTTER_TIMELINE (transition));
- }
- else
- _clutter_actor_update_transition (self, bg_color_pspec, color);
-
- clutter_actor_queue_redraw (self);
+ _clutter_actor_create_transition (self, bg_color_pspec,
+ &priv->bg_color,
+ color);
}
else
- clutter_actor_set_background_color_internal (self, color);
+ _clutter_actor_update_transition (self, bg_color_pspec, color);
+
+ clutter_actor_queue_redraw (self);
}
/**
return g_hash_table_lookup (info->transitions, pspec->name);
}
-typedef struct _TransitionClosure
+static void
+transition_closure_free (gpointer data)
{
- ClutterActor *actor;
- ClutterTransition *transition;
- GParamSpec *property;
- gulong completed_id;
-} TransitionClosure;
+ if (G_LIKELY (data != NULL))
+ {
+ TransitionClosure *clos = data;
+ ClutterTimeline *timeline;
+
+ timeline = CLUTTER_TIMELINE (clos->transition);
+
+ if (clutter_timeline_is_playing (timeline))
+ clutter_timeline_stop (timeline);
+
+ g_signal_handler_disconnect (clos->transition, clos->completed_id);
+
+ g_object_unref (clos->transition);
+ g_free (clos->name);
+
+ g_slice_free (TransitionClosure, clos);
+ }
+}
static void
-on_transition_completed (ClutterTransition *transition,
- TransitionClosure *clos)
+on_transition_stopped (ClutterTransition *transition,
+ gboolean is_finished,
+ TransitionClosure *clos)
{
+ ClutterActor *actor = clos->actor;
ClutterAnimationInfo *info;
- info = _clutter_actor_get_animation_info (clos->actor);
+ /* reset the caches used by animations */
+ clutter_actor_store_content_box (actor, NULL);
+
+ if (!is_finished)
+ return;
+
+ info = _clutter_actor_get_animation_info (actor);
+
+ /* we take a reference here because removing the closure
+ * will release the reference on the transition, and we
+ * want the transition to survive the signal emission;
+ * the master clock will release the last reference at
+ * the end of the frame processing.
+ */
+ g_object_ref (transition);
+ g_hash_table_remove (info->transitions, clos->name);
- g_hash_table_remove (info->transitions, clos->property->name);
+ /* if it's the last transition then we clean up */
+ if (g_hash_table_size (info->transitions) == 0)
+ {
+ g_hash_table_unref (info->transitions);
+ info->transitions = NULL;
- g_signal_handler_disconnect (transition, clos->completed_id);
+ CLUTTER_NOTE (ANIMATION, "Transitions for '%s' completed",
+ _clutter_actor_get_debug_name (actor));
- g_slice_free (TransitionClosure, clos);
+ g_signal_emit (actor, actor_signals[TRANSITIONS_COMPLETED], 0);
+ }
}
void
GParamSpec *pspec,
...)
{
- ClutterTransition *transition;
+ TransitionClosure *clos;
+ ClutterTimeline *timeline;
ClutterInterval *interval;
const ClutterAnimationInfo *info;
va_list var_args;
if (info->transitions == NULL)
return;
- transition = g_hash_table_lookup (info->transitions, pspec->name);
- if (transition == NULL)
+ clos = g_hash_table_lookup (info->transitions, pspec->name);
+ if (clos == NULL)
return;
+ timeline = CLUTTER_TIMELINE (clos->transition);
+
va_start (var_args, pspec);
ptype = G_PARAM_SPEC_VALUE_TYPE (pspec);
goto out;
}
- interval = clutter_transition_get_interval (transition);
+ interval = clutter_transition_get_interval (clos->transition);
clutter_interval_set_initial_value (interval, &initial);
clutter_interval_set_final_value (interval, &final);
- clutter_timeline_rewind (CLUTTER_TIMELINE (transition));
+ /* if we're updating with an easing duration of zero milliseconds,
+ * we just jump the timeline to the end and let it run its course
+ */
+ if (info->cur_state != NULL &&
+ info->cur_state->easing_duration != 0)
+ {
+ guint cur_duration = clutter_timeline_get_duration (timeline);
+ ClutterAnimationMode cur_mode =
+ clutter_timeline_get_progress_mode (timeline);
+
+ if (cur_duration != info->cur_state->easing_duration)
+ clutter_timeline_set_duration (timeline, info->cur_state->easing_duration);
+
+ if (cur_mode != info->cur_state->easing_mode)
+ clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode);
+
+ clutter_timeline_rewind (timeline);
+ }
+ else
+ {
+ guint duration = clutter_timeline_get_duration (timeline);
+
+ clutter_timeline_advance (timeline, duration);
+ }
out:
g_value_unset (&initial);
ClutterAnimationInfo *info;
ClutterTransition *res = NULL;
gboolean call_restore = FALSE;
+ TransitionClosure *clos;
va_list var_args;
info = _clutter_actor_get_animation_info (actor);
+ /* XXX - this will go away in 2.0
+ *
+ * if no state has been pushed, we assume that the easing state is
+ * in "compatibility mode": all transitions have a duration of 0
+ * msecs, which means that they happen immediately. in Clutter 2.0
+ * this will turn into a g_assert(info->states != NULL), as every
+ * actor will start with a predefined easing state
+ */
if (info->states == NULL)
{
clutter_actor_save_easing_state (actor);
+ clutter_actor_set_easing_duration (actor, 0);
call_restore = TRUE;
}
if (info->transitions == NULL)
- info->transitions = g_hash_table_new (g_str_hash, g_str_equal);
+ info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL,
+ transition_closure_free);
va_start (var_args, pspec);
- res = g_hash_table_lookup (info->transitions, pspec->name);
- if (res == NULL)
+ clos = g_hash_table_lookup (info->transitions, pspec->name);
+ if (clos == NULL)
{
+ ClutterTimeline *timeline;
ClutterInterval *interval;
GValue initial = G_VALUE_INIT;
GValue final = G_VALUE_INIT;
GType ptype;
char *error;
- TransitionClosure *clos;
ptype = G_PARAM_SPEC_VALUE_TYPE (pspec);
goto out;
}
+ /* if the current easing state has a duration of 0, then we don't
+ * bother to create the transition, and we just set the final value
+ * directly on the actor; we don't go through the Animatable
+ * interface because we know we got here through an animatable
+ * property.
+ */
+ if (info->cur_state->easing_duration == 0)
+ {
+ clutter_actor_set_animatable_property (actor,
+ pspec->param_id,
+ &final,
+ pspec);
+ g_value_unset (&initial);
+ g_value_unset (&final);
+
+ goto out;
+ }
+
interval = clutter_interval_new_with_values (ptype, &initial, &final);
g_value_unset (&initial);
g_value_unset (&final);
- res = clutter_property_transition_new (CLUTTER_ANIMATABLE (actor),
- pspec->name);
+ res = clutter_property_transition_new (pspec->name);
clutter_transition_set_interval (res, interval);
clutter_transition_set_remove_on_complete (res, TRUE);
- clutter_timeline_set_duration (CLUTTER_TIMELINE (res),
- info->cur_state->easing_duration);
- clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (res),
- info->cur_state->easing_mode);
+ timeline = CLUTTER_TIMELINE (res);
+ clutter_timeline_set_delay (timeline, info->cur_state->easing_delay);
+ clutter_timeline_set_duration (timeline, info->cur_state->easing_duration);
+ clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode);
- clos = g_slice_new (TransitionClosure);
- clos->actor = actor;
- clos->transition = res;
- clos->property = pspec;
- clos->completed_id =
- g_signal_connect (res, "completed",
- G_CALLBACK (on_transition_completed),
- clos);
+ /* this will start the transition as well */
+ clutter_actor_add_transition (actor, pspec->name, res);
- g_hash_table_insert (info->transitions, (gpointer) pspec->name, res);
+ /* the actor now owns the transition */
+ g_object_unref (res);
}
+ else
+ res = clos->transition;
out:
if (call_restore)
}
/**
- * clutter_actor_set_easing_duration:
+ * clutter_actor_add_transition:
* @self: a #ClutterActor
- * @msecs: the duration of the easing, or %NULL
+ * @name: the name of the transition to add
+ * @transition: the #ClutterTransition to add
*
- * Sets the duration of the tweening for animatable properties
- * of @self for the current easing state.
+ * Adds a @transition to the #ClutterActor's list of animations.
*
- * Calling this function will implicitly call
- * clutter_actor_save_easing_state() if no previous call to
- * that function was made.
+ * The @name string is a per-actor unique identifier of the @transition: only
+ * one #ClutterTransition can be associated to the specified @name.
+ *
+ * The @transition will be started once added.
+ *
+ * This function will take a reference on the @transition.
+ *
+ * This function is usually called implicitly when modifying an animatable
+ * property.
*
* Since: 1.10
*/
void
-clutter_actor_set_easing_duration (ClutterActor *self,
- guint msecs)
+clutter_actor_add_transition (ClutterActor *self,
+ const char *name,
+ ClutterTransition *transition)
{
+ ClutterTimeline *timeline;
+ TransitionClosure *clos;
ClutterAnimationInfo *info;
g_return_if_fail (CLUTTER_IS_ACTOR (self));
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
info = _clutter_actor_get_animation_info (self);
- if (info->states == NULL)
- clutter_actor_save_easing_state (self);
+ if (info->transitions == NULL)
+ info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL,
+ transition_closure_free);
- if (info->cur_state->easing_duration != msecs)
- info->cur_state->easing_duration = msecs;
+ if (g_hash_table_lookup (info->transitions, name) != NULL)
+ {
+ g_warning ("A transition with name '%s' already exists for "
+ "the actor '%s'",
+ name,
+ _clutter_actor_get_debug_name (self));
+ return;
+ }
+
+ clutter_transition_set_animatable (transition, CLUTTER_ANIMATABLE (self));
+
+ timeline = CLUTTER_TIMELINE (transition);
+
+ clos = g_slice_new (TransitionClosure);
+ clos->actor = self;
+ clos->transition = g_object_ref (transition);
+ clos->name = g_strdup (name);
+ clos->completed_id = g_signal_connect (timeline, "stopped",
+ G_CALLBACK (on_transition_stopped),
+ clos);
+
+ CLUTTER_NOTE (ANIMATION,
+ "Adding transition '%s' [%p] to actor '%s'",
+ clos->name,
+ clos->transition,
+ _clutter_actor_get_debug_name (self));
+
+ g_hash_table_insert (info->transitions, clos->name, clos);
+ clutter_timeline_start (timeline);
}
/**
- * clutter_actor_get_easing_duration:
+ * clutter_actor_remove_transition:
* @self: a #ClutterActor
+ * @name: the name of the transition to remove
*
- * Retrieves the duration of the tweening for animatable
- * properties of @self for the current easing state.
+ * Removes the transition stored inside a #ClutterActor using @name
+ * identifier.
*
- * Return value: the duration of the tweening, in milliseconds
+ * If the transition is currently in progress, it will be stopped.
+ *
+ * This function releases the reference acquired when the transition
+ * was added to the #ClutterActor.
*
* Since: 1.10
*/
-guint
-clutter_actor_get_easing_duration (ClutterActor *self)
+void
+clutter_actor_remove_transition (ClutterActor *self,
+ const char *name)
{
const ClutterAnimationInfo *info;
- g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+ g_return_if_fail (name != NULL);
info = _clutter_actor_get_animation_info_or_defaults (self);
- if (info->cur_state != NULL)
- return info->cur_state->easing_duration;
+ if (info->transitions == NULL)
+ return;
- return 0;
+ g_hash_table_remove (info->transitions, name);
}
/**
- * clutter_actor_set_easing_mode:
+ * clutter_actor_remove_all_transitions:
* @self: a #ClutterActor
- * @mode: an easing mode, excluding %CLUTTER_CUSTOM_MODE
- *
- * Sets the easing mode for the tweening of animatable properties
- * of @self.
*
- * Calling this function will implicitly call
- * clutter_actor_save_easing_state() if no previous calls to
- * that function were made.
+ * Removes all transitions associated to @self.
*
* Since: 1.10
*/
void
-clutter_actor_set_easing_mode (ClutterActor *self,
- ClutterAnimationMode mode)
+clutter_actor_remove_all_transitions (ClutterActor *self)
{
- ClutterAnimationInfo *info;
+ const ClutterAnimationInfo *info;
g_return_if_fail (CLUTTER_IS_ACTOR (self));
- g_return_if_fail (mode != CLUTTER_CUSTOM_MODE);
- g_return_if_fail (mode < CLUTTER_ANIMATION_LAST);
- info = _clutter_actor_get_animation_info (self);
-
- if (info->states == NULL)
- clutter_actor_save_easing_state (self);
+ info = _clutter_actor_get_animation_info_or_defaults (self);
+ if (info->transitions == NULL)
+ return;
- if (info->cur_state->easing_mode != mode)
- info->cur_state->easing_mode = mode;
+ g_hash_table_remove_all (info->transitions);
}
/**
- * clutter_actor_get_easing_mode:
+ * clutter_actor_set_easing_duration:
* @self: a #ClutterActor
+ * @msecs: the duration of the easing, or %NULL
*
- * Retrieves the easing mode for the tweening of animatable properties
+ * Sets the duration of the tweening for animatable properties
* of @self for the current easing state.
*
- * Return value: an easing mode
- *
* Since: 1.10
*/
-ClutterAnimationMode
+void
+clutter_actor_set_easing_duration (ClutterActor *self,
+ guint msecs)
+{
+ ClutterAnimationInfo *info;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ info = _clutter_actor_get_animation_info (self);
+
+ if (info->cur_state == NULL)
+ {
+ g_warning ("You must call clutter_actor_save_easing_state() prior "
+ "to calling clutter_actor_set_easing_duration().");
+ return;
+ }
+
+ if (info->cur_state->easing_duration != msecs)
+ info->cur_state->easing_duration = msecs;
+}
+
+/**
+ * clutter_actor_get_easing_duration:
+ * @self: a #ClutterActor
+ *
+ * Retrieves the duration of the tweening for animatable
+ * properties of @self for the current easing state.
+ *
+ * Return value: the duration of the tweening, in milliseconds
+ *
+ * Since: 1.10
+ */
+guint
+clutter_actor_get_easing_duration (ClutterActor *self)
+{
+ const ClutterAnimationInfo *info;
+
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
+
+ info = _clutter_actor_get_animation_info_or_defaults (self);
+
+ if (info->cur_state != NULL)
+ return info->cur_state->easing_duration;
+
+ return 0;
+}
+
+/**
+ * clutter_actor_set_easing_mode:
+ * @self: a #ClutterActor
+ * @mode: an easing mode, excluding %CLUTTER_CUSTOM_MODE
+ *
+ * Sets the easing mode for the tweening of animatable properties
+ * of @self.
+ *
+ * Since: 1.10
+ */
+void
+clutter_actor_set_easing_mode (ClutterActor *self,
+ ClutterAnimationMode mode)
+{
+ ClutterAnimationInfo *info;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+ g_return_if_fail (mode != CLUTTER_CUSTOM_MODE);
+ g_return_if_fail (mode < CLUTTER_ANIMATION_LAST);
+
+ info = _clutter_actor_get_animation_info (self);
+
+ if (info->cur_state == NULL)
+ {
+ g_warning ("You must call clutter_actor_save_easing_state() prior "
+ "to calling clutter_actor_set_easing_mode().");
+ return;
+ }
+
+ if (info->cur_state->easing_mode != mode)
+ info->cur_state->easing_mode = mode;
+}
+
+/**
+ * clutter_actor_get_easing_mode:
+ * @self: a #ClutterActor
+ *
+ * Retrieves the easing mode for the tweening of animatable properties
+ * of @self for the current easing state.
+ *
+ * Return value: an easing mode
+ *
+ * Since: 1.10
+ */
+ClutterAnimationMode
clutter_actor_get_easing_mode (ClutterActor *self)
{
const ClutterAnimationInfo *info;
}
/**
+ * clutter_actor_set_easing_delay:
+ * @self: a #ClutterActor
+ * @msecs: the delay before the start of the tweening, in milliseconds
+ *
+ * Sets the delay that should be applied before tweening animatable
+ * properties.
+ *
+ * Since: 1.10
+ */
+void
+clutter_actor_set_easing_delay (ClutterActor *self,
+ guint msecs)
+{
+ ClutterAnimationInfo *info;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ info = _clutter_actor_get_animation_info (self);
+
+ if (info->cur_state == NULL)
+ {
+ g_warning ("You must call clutter_actor_save_easing_state() prior "
+ "to calling clutter_actor_set_easing_delay().");
+ return;
+ }
+
+ if (info->cur_state->easing_delay != msecs)
+ info->cur_state->easing_delay = msecs;
+}
+
+/**
+ * clutter_actor_get_easing_delay:
+ * @self: a #ClutterActor
+ *
+ * Retrieves the delay that should be applied when tweening animatable
+ * properties.
+ *
+ * Return value: a delay, in milliseconds
+ *
+ * Since: 1.10
+ */
+guint
+clutter_actor_get_easing_delay (ClutterActor *self)
+{
+ const ClutterAnimationInfo *info;
+
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
+
+ info = _clutter_actor_get_animation_info_or_defaults (self);
+
+ if (info->cur_state != NULL)
+ return info->cur_state->easing_delay;
+
+ return 0;
+}
+
+/**
* clutter_actor_get_transition:
* @self: a #ClutterActor
* @name: the name of the transition
* clutter_actor_set_rotation (actor, CLUTTER_Y_AXIS, 360.0, x, y, z);
*
* transition = clutter_actor_get_transition (actor, "rotation-angle-y");
- * g_signal_connect (transition, "completed",
- * G_CALLBACK (on_transition_complete),
+ * g_signal_connect (transition, "stopped",
+ * G_CALLBACK (on_transition_stopped),
* actor);
* ]|
*
- * will call the <function>on_transition_complete</function> callback when
- * the transition is complete.
+ * will call the <function>on_transition_stopped</function> callback when
+ * the transition is finished.
*
* Return value: (transfer none): a #ClutterTransition, or %NULL is none
* was found to match the passed name; the returned instance is owned
clutter_actor_get_transition (ClutterActor *self,
const char *name)
{
+ TransitionClosure *clos;
const ClutterAnimationInfo *info;
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
g_return_val_if_fail (name != NULL, NULL);
info = _clutter_actor_get_animation_info_or_defaults (self);
-
if (info->transitions == NULL)
return NULL;
- return g_hash_table_lookup (info->transitions, name);
+ clos = g_hash_table_lookup (info->transitions, name);
+ if (clos == NULL)
+ return NULL;
+
+ return clos->transition;
}
/**
new_state.easing_mode = CLUTTER_EASE_OUT_CUBIC;
new_state.easing_duration = 250;
+ new_state.easing_delay = 0;
g_array_append_val (info->states, new_state);
}
g_array_remove_index (info->states, info->states->len - 1);
- info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
+
+ if (info->states->len > 0)
+ info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
+ else
+ {
+ g_array_unref (info->states);
+ info->states = NULL;
+ info->cur_state = NULL;
+ }
+}
+
+/**
+ * clutter_actor_set_content:
+ * @self: a #ClutterActor
+ * @content: (allow-none): a #ClutterContent, or %NULL
+ *
+ * Sets the contents of a #ClutterActor.
+ *
+ * Since: 1.10
+ */
+void
+clutter_actor_set_content (ClutterActor *self,
+ ClutterContent *content)
+{
+ ClutterActorPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+ g_return_if_fail (content == NULL || CLUTTER_IS_CONTENT (content));
+
+ priv = self->priv;
+
+ if (priv->content != NULL)
+ {
+ _clutter_content_detached (priv->content, self);
+ g_clear_object (&priv->content);
+ }
+
+ priv->content = content;
+
+ if (priv->content != NULL)
+ {
+ g_object_ref (priv->content);
+ _clutter_content_attached (priv->content, self);
+ }
+
+ /* given that the content is always painted within the allocation,
+ * we only need to queue a redraw here
+ */
+ clutter_actor_queue_redraw (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT]);
+
+ /* if the content gravity is not resize-fill, and the new content has a
+ * different preferred size than the previous one, then the content box
+ * may have been changed. since we compute that lazily, we just notify
+ * here, and let whomever watches :content-box do whatever they need to
+ * do.
+ */
+ if (priv->content_gravity != CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
+}
+
+/**
+ * clutter_actor_get_content:
+ * @self: a #ClutterActor
+ *
+ * Retrieves the contents of @self.
+ *
+ * Return value: (transfer none): a pointer to the #ClutterContent instance,
+ * or %NULL if none was set
+ *
+ * Since: 1.10
+ */
+ClutterContent *
+clutter_actor_get_content (ClutterActor *self)
+{
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
+
+ return self->priv->content;
+}
+
+/**
+ * clutter_actor_set_content_gravity:
+ * @self: a #ClutterActor
+ * @gravity: the #ClutterContentGravity
+ *
+ * Sets the gravity of the #ClutterContent used by @self.
+ *
+ * See the description of the #ClutterActor:content-gravity property for
+ * more information.
+ *
+ * The #ClutterActor:content-gravity property is animatable.
+ *
+ * Since: 1.10
+ */
+void
+clutter_actor_set_content_gravity (ClutterActor *self,
+ ClutterContentGravity gravity)
+{
+ ClutterActorPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ priv = self->priv;
+
+ if (priv->content_gravity == gravity)
+ return;
+
+ priv->content_box_valid = FALSE;
+
+ if (_clutter_actor_get_transition (self, obj_props[PROP_CONTENT_BOX]) == NULL)
+ {
+ ClutterActorBox from_box, to_box;
+
+ clutter_actor_get_content_box (self, &from_box);
+
+ priv->content_gravity = gravity;
+
+ clutter_actor_get_content_box (self, &to_box);
+
+ _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX],
+ &from_box,
+ &to_box);
+ }
+ else
+ {
+ ClutterActorBox to_box;
+
+ priv->content_gravity = gravity;
+
+ clutter_actor_get_content_box (self, &to_box);
+
+ _clutter_actor_update_transition (self, obj_props[PROP_CONTENT_BOX],
+ &to_box);
+ }
+
+ clutter_actor_queue_redraw (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_GRAVITY]);
+}
+
+/**
+ * clutter_actor_get_content_gravity:
+ * @self: a #ClutterActor
+ *
+ * Retrieves the content gravity as set using
+ * clutter_actor_get_content_gravity().
+ *
+ * Return value: the content gravity
+ *
+ * Since: 1.10
+ */
+ClutterContentGravity
+clutter_actor_get_content_gravity (ClutterActor *self)
+{
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
+ CLUTTER_CONTENT_GRAVITY_RESIZE_FILL);
+
+ return self->priv->content_gravity;
+}
+
+/**
+ * clutter_actor_get_content_box:
+ * @self: a #ClutterActor
+ * @box: (out caller-allocates): the return location for the bounding
+ * box for the #ClutterContent
+ *
+ * Retrieves the bounding box for the #ClutterContent of @self.
+ *
+ * The bounding box is relative to the actor's allocation.
+ *
+ * If no #ClutterContent is set for @self, or if @self has not been
+ * allocated yet, then the result is undefined.
+ *
+ * The content box is guaranteed to be, at most, as big as the allocation
+ * of the #ClutterActor.
+ *
+ * If the #ClutterContent used by the actor has a preferred size, then
+ * it is possible to modify the content box by using the
+ * #ClutterActor:content-gravity property.
+ *
+ * Since: 1.10
+ */
+void
+clutter_actor_get_content_box (ClutterActor *self,
+ ClutterActorBox *box)
+{
+ ClutterActorPrivate *priv;
+ gfloat content_w, content_h;
+ gfloat alloc_w, alloc_h;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+ g_return_if_fail (box != NULL);
+
+ priv = self->priv;
+
+ box->x1 = 0.f;
+ box->y1 = 0.f;
+ box->x2 = priv->allocation.x2 - priv->allocation.x1;
+ box->y2 = priv->allocation.y2 - priv->allocation.y1;
+
+ if (priv->content_box_valid)
+ {
+ *box = priv->content_box;
+ return;
+ }
+
+ /* no need to do any more work */
+ if (priv->content_gravity == CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
+ return;
+
+ if (priv->content == NULL)
+ return;
+
+ /* if the content does not have a preferred size then there is
+ * no point in computing the content box
+ */
+ if (!clutter_content_get_preferred_size (priv->content,
+ &content_w,
+ &content_h))
+ return;
+
+ alloc_w = box->x2;
+ alloc_h = box->y2;
+
+ switch (priv->content_gravity)
+ {
+ case CLUTTER_CONTENT_GRAVITY_TOP_LEFT:
+ box->x2 = box->x1 + MIN (content_w, alloc_w);
+ box->y2 = box->y1 + MIN (content_h, alloc_h);
+ break;
+
+ case CLUTTER_CONTENT_GRAVITY_TOP:
+ if (alloc_w > content_w)
+ {
+ box->x1 += ceilf ((alloc_w - content_w) / 2.0);
+ box->x2 = box->x1 + content_w;
+ }
+ box->y2 = box->y1 + MIN (content_h, alloc_h);
+ break;
+
+ case CLUTTER_CONTENT_GRAVITY_TOP_RIGHT:
+ if (alloc_w > content_w)
+ {
+ box->x1 += (alloc_w - content_w);
+ box->x2 = box->x1 + content_w;
+ }
+ box->y2 = box->y1 + MIN (content_h, alloc_h);
+ break;
+
+ case CLUTTER_CONTENT_GRAVITY_LEFT:
+ box->x2 = box->x1 + MIN (content_w, alloc_w);
+ if (alloc_h > content_h)
+ {
+ box->y1 += ceilf ((alloc_h - content_h) / 2.0);
+ box->y2 = box->y1 + content_h;
+ }
+ break;
+
+ case CLUTTER_CONTENT_GRAVITY_CENTER:
+ if (alloc_w > content_w)
+ {
+ box->x1 += ceilf ((alloc_w - content_w) / 2.0);
+ box->x2 = box->x1 + content_w;
+ }
+ if (alloc_h > content_h)
+ {
+ box->y1 += ceilf ((alloc_h - content_h) / 2.0);
+ box->y2 = box->y1 + content_h;
+ }
+ break;
+
+ case CLUTTER_CONTENT_GRAVITY_RIGHT:
+ if (alloc_w > content_w)
+ {
+ box->x1 += (alloc_w - content_w);
+ box->x2 = box->x1 + content_w;
+ }
+ if (alloc_h > content_h)
+ {
+ box->y1 += ceilf ((alloc_h - content_h) / 2.0);
+ box->y2 = box->y1 + content_h;
+ }
+ break;
+
+ case CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT:
+ box->x2 = box->x1 + MIN (content_w, alloc_w);
+ if (alloc_h > content_h)
+ {
+ box->y1 += (alloc_h - content_h);
+ box->y2 = box->y1 + content_h;
+ }
+ break;
+
+ case CLUTTER_CONTENT_GRAVITY_BOTTOM:
+ if (alloc_w > content_w)
+ {
+ box->x1 += ceilf ((alloc_w - content_w) / 2.0);
+ box->x2 = box->x1 + content_w;
+ }
+ if (alloc_h > content_h)
+ {
+ box->y1 += (alloc_h - content_h);
+ box->y2 = box->y1 + content_h;
+ }
+ break;
+
+ case CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT:
+ if (alloc_w > content_w)
+ {
+ box->x1 += (alloc_w - content_w);
+ box->x2 = box->x1 + content_w;
+ }
+ if (alloc_h > content_h)
+ {
+ box->y1 += (alloc_h - content_h);
+ box->y2 = box->y1 + content_h;
+ }
+ break;
+
+ case CLUTTER_CONTENT_GRAVITY_RESIZE_FILL:
+ g_assert_not_reached ();
+ break;
+
+ case CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT:
+ {
+ double r_c = content_w / content_h;
+
+ if (r_c >= 1.0)
+ {
+ if ((alloc_w / r_c) > alloc_h)
+ {
+ box->x1 = 0.f;
+ box->x2 = alloc_w;
+
+ box->y1 = (alloc_h - (alloc_w / r_c)) / 2.0f;
+ box->y2 = box->y1 + (alloc_w / r_c);
+ }
+ else
+ {
+ box->y1 = 0.f;
+ box->y2 = alloc_h;
+
+ box->x1 = (alloc_w - (alloc_h * r_c)) / 2.0f;
+ box->x2 = box->x1 + (alloc_h * r_c);
+ }
+ }
+ else
+ {
+ if ((alloc_w / r_c) > alloc_h)
+ {
+ box->y1 = 0.f;
+ box->y2 = alloc_h;
+
+ box->x1 = (alloc_w - (alloc_h * r_c)) / 2.0f;
+ box->x2 = box->x1 + (alloc_h * r_c);
+ }
+ else
+ {
+ box->x1 = 0.f;
+ box->x2 = alloc_w;
+
+ box->y1 = (alloc_h - (alloc_w / r_c)) / 2.0f;
+ box->y2 = box->y1 + (alloc_w / r_c);
+ }
+ }
+
+ CLUTTER_NOTE (LAYOUT,
+ "r_c: %.3f, r_a: %.3f\t"
+ "a: [%.2fx%.2f], c: [%.2fx%.2f]\t"
+ "b: [%.2f, %.2f, %.2f, %.2f]",
+ r_c, alloc_w / alloc_h,
+ alloc_w, alloc_h,
+ content_w, content_h,
+ box->x1, box->y1, box->x2, box->y2);
+ }
+ break;
+ }
+}
+
+/**
+ * clutter_actor_set_content_scaling_filters:
+ * @self: a #ClutterActor
+ * @min_filter: the minification filter for the content
+ * @mag_filter: the magnification filter for the content
+ *
+ * Sets the minification and magnification filter to be applied when
+ * scaling the #ClutterActor:content of a #ClutterActor.
+ *
+ * The #ClutterActor:minification-filter will be used when reducing
+ * the size of the content; the #ClutterActor:magnification-filter
+ * will be used when increasing the size of the content.
+ *
+ * Since: 1.10
+ */
+void
+clutter_actor_set_content_scaling_filters (ClutterActor *self,
+ ClutterScalingFilter min_filter,
+ ClutterScalingFilter mag_filter)
+{
+ ClutterActorPrivate *priv;
+ gboolean changed;
+ GObject *obj;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ priv = self->priv;
+ obj = G_OBJECT (self);
+
+ g_object_freeze_notify (obj);
+
+ changed = FALSE;
+
+ if (priv->min_filter != min_filter)
+ {
+ priv->min_filter = min_filter;
+ changed = TRUE;
+
+ g_object_notify_by_pspec (obj, obj_props[PROP_MINIFICATION_FILTER]);
+ }
+
+ if (priv->mag_filter != mag_filter)
+ {
+ priv->mag_filter = mag_filter;
+ changed = TRUE;
+
+ g_object_notify_by_pspec (obj, obj_props[PROP_MAGNIFICATION_FILTER]);
+ }
+
+ if (changed)
+ clutter_actor_queue_redraw (self);
+
+ g_object_thaw_notify (obj);
+}
+
+/**
+ * clutter_actor_get_content_scaling_filters:
+ * @self: a #ClutterActor
+ * @min_filter: (out) (allow-none): return location for the minification
+ * filter, or %NULL
+ * @mag_filter: (out) (allow-none): return location for the magnification
+ * filter, or %NULL
+ *
+ * Retrieves the values set using clutter_actor_set_content_scaling_filters().
+ *
+ * Since: 1.10
+ */
+void
+clutter_actor_get_content_scaling_filters (ClutterActor *self,
+ ClutterScalingFilter *min_filter,
+ ClutterScalingFilter *mag_filter)
+{
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ if (min_filter != NULL)
+ *min_filter = self->priv->min_filter;
+
+ if (mag_filter != NULL)
+ *mag_filter = self->priv->mag_filter;
+}
+
+/*
+ * clutter_actor_queue_compute_expand:
+ * @self: a #ClutterActor
+ *
+ * Invalidates the needs_x_expand and needs_y_expand flags on @self
+ * and its parents up to the top-level actor.
+ *
+ * This function also queues a relayout if anything changed.
+ */
+static inline void
+clutter_actor_queue_compute_expand (ClutterActor *self)
+{
+ ClutterActor *parent;
+ gboolean changed;
+
+ if (self->priv->needs_compute_expand)
+ return;
+
+ changed = FALSE;
+ parent = self;
+ while (parent != NULL)
+ {
+ if (!parent->priv->needs_compute_expand)
+ {
+ parent->priv->needs_compute_expand = TRUE;
+ changed = TRUE;
+ }
+
+ parent = parent->priv->parent;
+ }
+
+ if (changed)
+ clutter_actor_queue_relayout (self);
+}
+
+/**
+ * clutter_actor_set_x_expand:
+ * @self: a #ClutterActor
+ * @expand: whether the actor should expand horizontally
+ *
+ * Sets whether a #ClutterActor should expand horizontally; this means
+ * that layout manager should allocate extra space for the actor, if
+ * possible.
+ *
+ * Setting an actor to expand will also make all its parent expand, so
+ * that it's possible to build an actor tree and only set this flag on
+ * its leaves and not on every single actor.
+ *
+ * Since: 1.12
+ */
+void
+clutter_actor_set_x_expand (ClutterActor *self,
+ gboolean expand)
+{
+ ClutterLayoutInfo *info;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ expand = !!expand;
+
+ info = _clutter_actor_get_layout_info (self);
+ if (info->x_expand != expand)
+ {
+ info->x_expand = expand;
+
+ self->priv->x_expand_set = TRUE;
+
+ clutter_actor_queue_compute_expand (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self),
+ obj_props[PROP_X_EXPAND]);
+ }
+}
+
+/**
+ * clutter_actor_get_x_expand:
+ * @self: a #ClutterActor
+ *
+ * Retrieves the value set with clutter_actor_set_x_expand().
+ *
+ * See also: clutter_actor_needs_expand()
+ *
+ * Return value: %TRUE if the actor has been set to expand
+ *
+ * Since: 1.12
+ */
+gboolean
+clutter_actor_get_x_expand (ClutterActor *self)
+{
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
+
+ return _clutter_actor_get_layout_info_or_defaults (self)->x_expand;
+}
+
+/**
+ * clutter_actor_set_y_expand:
+ * @self: a #ClutterActor
+ * @expand: whether the actor should expand vertically
+ *
+ * Sets whether a #ClutterActor should expand horizontally; this means
+ * that layout manager should allocate extra space for the actor, if
+ * possible.
+ *
+ * Setting an actor to expand will also make all its parent expand, so
+ * that it's possible to build an actor tree and only set this flag on
+ * its leaves and not on every single actor.
+ *
+ * Since: 1.12
+ */
+void
+clutter_actor_set_y_expand (ClutterActor *self,
+ gboolean expand)
+{
+ ClutterLayoutInfo *info;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ expand = !!expand;
+
+ info = _clutter_actor_get_layout_info (self);
+ if (info->y_expand != expand)
+ {
+ info->y_expand = expand;
+
+ self->priv->y_expand_set = TRUE;
+
+ clutter_actor_queue_compute_expand (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self),
+ obj_props[PROP_Y_EXPAND]);
+ }
+}
+
+/**
+ * clutter_actor_get_y_expand:
+ * @self: a #ClutterActor
+ *
+ * Retrieves the value set with clutter_actor_set_y_expand().
+ *
+ * See also: clutter_actor_needs_expand()
+ *
+ * Return value: %TRUE if the actor has been set to expand
+ *
+ * Since: 1.12
+ */
+gboolean
+clutter_actor_get_y_expand (ClutterActor *self)
+{
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
+
+ return _clutter_actor_get_layout_info_or_defaults (self)->y_expand;
+}
+
+static void
+clutter_actor_compute_expand_recursive (ClutterActor *self,
+ gboolean *x_expand_p,
+ gboolean *y_expand_p)
+{
+ ClutterActorIter iter;
+ ClutterActor *child;
+ gboolean x_expand, y_expand;
+
+ x_expand = y_expand = FALSE;
+
+ /* note that we don't recurse into children if we're already set to expand;
+ * this avoids traversing the whole actor tree, even if it may lead to some
+ * child left with the needs_compute_expand flag set.
+ */
+ clutter_actor_iter_init (&iter, self);
+ while (clutter_actor_iter_next (&iter, &child))
+ {
+ x_expand = x_expand ||
+ clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL);
+
+ y_expand = y_expand ||
+ clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL);
+ }
+
+ *x_expand_p = x_expand;
+ *y_expand_p = y_expand;
+}
+
+static void
+clutter_actor_compute_expand (ClutterActor *self)
+{
+ if (self->priv->needs_compute_expand)
+ {
+ const ClutterLayoutInfo *info;
+ gboolean x_expand, y_expand;
+
+ info = _clutter_actor_get_layout_info_or_defaults (self);
+
+ if (self->priv->x_expand_set)
+ x_expand = info->x_expand;
+ else
+ x_expand = FALSE;
+
+ if (self->priv->y_expand_set)
+ y_expand = info->y_expand;
+ else
+ y_expand = FALSE;
+
+ /* we don't need to recurse down to the children if the
+ * actor has been forcibly set to expand
+ */
+ if (!(self->priv->x_expand_set && self->priv->y_expand_set))
+ {
+ if (self->priv->n_children != 0)
+ {
+ gboolean *x_expand_p, *y_expand_p;
+ gboolean ignored = FALSE;
+
+ x_expand_p = self->priv->x_expand_set ? &ignored : &x_expand;
+ y_expand_p = self->priv->y_expand_set ? &ignored : &y_expand;
+
+ clutter_actor_compute_expand_recursive (self,
+ x_expand_p,
+ y_expand_p);
+ }
+ }
+
+ self->priv->needs_compute_expand = FALSE;
+ self->priv->needs_x_expand = (x_expand != FALSE);
+ self->priv->needs_y_expand = (y_expand != FALSE);
+ }
+}
+
+/**
+ * clutter_actor_needs_expand:
+ * @self: a #ClutterActor
+ * @orientation: the direction of expansion
+ *
+ * Checks whether an actor, or any of its children, is set to expand
+ * horizontally or vertically.
+ *
+ * This function should only be called by layout managers that can
+ * assign extra space to their children.
+ *
+ * If you want to know whether the actor was explicitly set to expand,
+ * use clutter_actor_get_x_expand() or clutter_actor_get_y_expand().
+ *
+ * Return value: %TRUE if the actor should expand
+ *
+ * Since: 1.12
+ */
+gboolean
+clutter_actor_needs_expand (ClutterActor *self,
+ ClutterOrientation orientation)
+{
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (self))
+ return FALSE;
+
+ if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
+ return FALSE;
+
+ clutter_actor_compute_expand (self);
+
+ switch (orientation)
+ {
+ case CLUTTER_ORIENTATION_HORIZONTAL:
+ return self->priv->needs_x_expand;
+
+ case CLUTTER_ORIENTATION_VERTICAL:
+ return self->priv->needs_y_expand;
+ }
+
+ return FALSE;
}