Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-actor.c
index 70928b2..5865d57 100644 (file)
  *   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" : [ &lt;top&gt;, &lt;right&gt;, &lt;bottom&gt; &lt;left&gt; ]
+ * // 3 values
+ * "margin" : [ &lt;top&gt;, &lt;left/right&gt;, &lt;bottom&gt; ]
+ * // 2 values
+ * "margin" : [ &lt;top/bottom&gt;, &lt;left/right&gt; ]
+ * // 1 value
+ * "margin" : [ &lt;top/right/bottom/left&gt; ]
+ *     </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
@@ -927,6 +942,14 @@ enum
 
 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);
@@ -3787,6 +3810,25 @@ clutter_actor_continue_paint (ClutterActor *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,
@@ -3836,9 +3878,11 @@ typedef enum {
   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 |
@@ -3846,7 +3890,8 @@ typedef enum {
                                     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
@@ -3870,6 +3915,7 @@ clutter_actor_remove_child_internal (ClutterActor                 *self,
   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;
@@ -3877,9 +3923,13 @@ clutter_actor_remove_child_internal (ClutterActor                 *self,
   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);
 
@@ -6863,7 +6913,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
                   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()
@@ -8258,12 +8308,16 @@ clutter_actor_get_preferred_width (ClutterActor *self,
   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;
@@ -8390,12 +8444,16 @@ clutter_actor_get_preferred_height (ClutterActor *self,
   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;
@@ -8563,9 +8621,9 @@ clutter_actor_adjust_allocation (ClutterActor    *self,
       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
@@ -13009,6 +13067,63 @@ parse_behaviours (ClutterScript *script,
   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,
@@ -13098,6 +13213,17 @@ clutter_actor_parse_custom_node (ClutterScriptable *scriptable,
 
       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;
 }
@@ -13190,6 +13316,11 @@ clutter_actor_set_custom_property (ClutterScriptable *scriptable,
 
       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);
 }
@@ -17118,14 +17249,6 @@ _clutter_actor_get_transition (ClutterActor *actor,
   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)
 {
@@ -17149,39 +17272,29 @@ 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)
@@ -17462,8 +17575,8 @@ clutter_actor_add_transition (ClutterActor      *self,
   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,
@@ -17719,13 +17832,13 @@ clutter_actor_get_easing_delay (ClutterActor *self)
  *   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
@@ -18139,17 +18252,16 @@ clutter_actor_get_content_box (ClutterActor    *self,
     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
               {
@@ -18162,7 +18274,7 @@ clutter_actor_get_content_box (ClutterActor    *self,
           }
         else
           {
-            if (r_a >= 1.0)
+            if ((alloc_w / r_c) > alloc_h)
               {
                 box->y1 = 0.f;
                 box->y2 = alloc_h;
@@ -18175,10 +18287,19 @@ clutter_actor_get_content_box (ClutterActor    *self,
                 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;
     }