* 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>
* 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
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);
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));
}
}
+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);
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_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()
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)
request_min_width = cached_size_request->min_size;
else
- request_min_width = info->minimum.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)
request_min_height = cached_size_request->min_size;
else
- request_min_height = info->minimum.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_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);
-
- CLUTTER_NOTE (LAYOUT, "Calling %s::allocate()",
- _clutter_actor_get_debug_name (self));
-
- 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);
}
/**
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);
}
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;
return g_hash_table_lookup (info->transitions, pspec->name);
}
-typedef struct _TransitionClosure
-{
- ClutterActor *actor;
- ClutterTransition *transition;
- gchar *name;
- gulong completed_id;
-} TransitionClosure;
-
static void
transition_closure_free (gpointer data)
{
}
static void
-on_transition_completed (ClutterTransition *transition,
- TransitionClosure *clos)
+on_transition_stopped (ClutterTransition *transition,
+ gboolean is_finished,
+ TransitionClosure *clos)
{
- ClutterTimeline *timeline = CLUTTER_TIMELINE (transition);
ClutterActor *actor = clos->actor;
ClutterAnimationInfo *info;
- gint n_repeats, cur_repeat;
-
- info = _clutter_actor_get_animation_info (actor);
/* reset the caches used by animations */
clutter_actor_store_content_box (actor, NULL);
- /* ensure that we remove the transition only at the end
- * of its run; we emit ::completed for every repeat
- */
- n_repeats = clutter_timeline_get_repeat_count (timeline);
- cur_repeat = clutter_timeline_get_current_repeat (timeline);
+ if (!is_finished)
+ return;
- if (cur_repeat == n_repeats)
- {
- if (clutter_transition_get_remove_on_complete (transition))
- {
- /* 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);
- }
- }
+ 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);
/* if it's the last transition then we clean up */
if (g_hash_table_size (info->transitions) == 0)
clos->actor = self;
clos->transition = g_object_ref (transition);
clos->name = g_strdup (name);
- clos->completed_id = g_signal_connect (timeline, "completed",
- G_CALLBACK (on_transition_completed),
+ clos->completed_id = g_signal_connect (timeline, "stopped",
+ G_CALLBACK (on_transition_stopped),
clos);
CLUTTER_NOTE (ANIMATION,
* 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
case CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT:
{
double r_c = content_w / content_h;
- double r_a = alloc_w / alloc_h;
if (r_c >= 1.0)
{
- if (r_a >= 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);
+ box->y1 = (alloc_h - (alloc_w / r_c)) / 2.0f;
+ box->y2 = box->y1 + (alloc_w / r_c);
}
else
{
}
else
{
- if (r_a >= 1.0)
+ if ((alloc_w / r_c) > alloc_h)
{
box->y1 = 0.f;
box->y2 = 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);
+ 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;
}
*
* Retrieves the value set with clutter_actor_set_x_expand().
*
- * See also: clutter_actor_needs_x_expand()
+ * See also: clutter_actor_needs_expand()
*
* Return value: %TRUE if the actor has been set to expand
*
*
* Retrieves the value set with clutter_actor_set_y_expand().
*
- * See also: clutter_actor_needs_y_expand()
+ * See also: clutter_actor_needs_expand()
*
* Return value: %TRUE if the actor has been set to 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_x_expand (child);
- y_expand = y_expand || clutter_actor_needs_y_expand (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;
}
/**
- * clutter_actor_needs_x_expand:
+ * 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.
+ * 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_y_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_x_expand (ClutterActor *self)
+clutter_actor_needs_expand (ClutterActor *self,
+ ClutterOrientation orientation)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
clutter_actor_compute_expand (self);
- return self->priv->needs_x_expand;
-}
-
-/**
- * clutter_actor_needs_y_expand:
- * @self: a #ClutterActor
- *
- * Checks whether an actor, or any of its children, is set to expand
- * 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_y_expand().
- *
- * Return value: %TRUE if the actor should expand
- *
- * Since: 1.12
- */
-gboolean
-clutter_actor_needs_y_expand (ClutterActor *self)
-{
- 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;
+ switch (orientation)
+ {
+ case CLUTTER_ORIENTATION_HORIZONTAL:
+ return self->priv->needs_x_expand;
- clutter_actor_compute_expand (self);
+ case CLUTTER_ORIENTATION_VERTICAL:
+ return self->priv->needs_y_expand;
+ }
- return self->priv->needs_x_expand;
+ return FALSE;
}