[clutter/wip/expand-flags: 1/7] actor: Add basic automatic expand flags



commit 9d02f3eeef8a54bc72e9edcf46f1019588b34b0d
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Tue Mar 27 14:53:27 2012 +0100

    actor: Add basic automatic expand flags
    
    The :x-expand and :y-expand flags on ClutterActor are used to signal
    that an actor should expand horizontally and/or vertically - i.e. that
    its parent's layout management policy should try to assign extra space
    to the actor when allocating it.
    
    The expand flags are automatic: when set on a leaf node in the actor
    tree, they will bubble up through the parent and grandparents up to the
    top level actor; during allocation, the actors with children will lazily
    compute whether their children needs to expand.

 clutter/clutter-actor-private.h |    3 +
 clutter/clutter-actor.c         |  434 ++++++++++++++++++++++++++++++++++++++-
 clutter/clutter-actor.h         |   14 ++
 clutter/clutter.symbols         |    6 +
 4 files changed, 449 insertions(+), 8 deletions(-)
---
diff --git a/clutter/clutter-actor-private.h b/clutter/clutter-actor-private.h
index ac207b1..86032d9 100644
--- a/clutter/clutter-actor-private.h
+++ b/clutter/clutter-actor-private.h
@@ -170,6 +170,9 @@ struct _ClutterLayoutInfo
   guint x_align : 4;
   guint y_align : 4;
 
+  guint x_expand : 1;
+  guint y_expand : 1;
+
   float min_width;
   float min_height;
   float natural_width;
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index 2e93854..abc0866 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -770,6 +770,11 @@ struct _ClutterActorPrivate
   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
@@ -860,6 +865,8 @@ enum
 
   PROP_LAYOUT_MANAGER,
 
+  PROP_X_EXPAND,
+  PROP_Y_EXPAND,
   PROP_X_ALIGN,
   PROP_Y_ALIGN,
   PROP_MARGIN_TOP,
@@ -978,6 +985,8 @@ static inline void clutter_actor_set_background_color_internal (ClutterActor *se
 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 { \
@@ -1602,6 +1611,17 @@ clutter_actor_show (ClutterActor *self)
 
   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]);
 
@@ -1697,6 +1717,17 @@ clutter_actor_hide (ClutterActor *self)
 
   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]);
 
@@ -3879,6 +3910,19 @@ clutter_actor_remove_child_internal (ClutterActor                 *self,
 
   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);
@@ -4537,6 +4581,14 @@ clutter_actor_set_property (GObject      *object,
       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;
@@ -4913,6 +4965,24 @@ clutter_actor_get_property (GObject    *object,
       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;
@@ -6252,12 +6322,44 @@ clutter_actor_class_init (ClutterActorClass *klass)
                          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
    */
@@ -7103,6 +7205,15 @@ clutter_actor_init (ClutterActor *self)
   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;
 }
 
 /**
@@ -11042,6 +11153,23 @@ clutter_actor_add_child_internal (ClutterActor              *self,
   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);
@@ -16037,13 +16165,17 @@ clutter_actor_get_layout_manager (ClutterActor *self)
 }
 
 static const ClutterLayoutInfo default_layout_info = {
-  0.f,                          /* fixed-x */
-  0.f,                          /* fixed-y */
-  { 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 */
+  /* .fixed_x = */ 0.f,
+  /* .fixed_y = */ 0.f,
+  /* .margin = */ { 0, 0, 0, 0 },
+  /* .x_align = */ CLUTTER_ACTOR_ALIGN_FILL,
+  /* .y_align = */ CLUTTER_ACTOR_ALIGN_FILL,
+  /* .x_expand = */ FALSE,
+  /* .y_expand = */ FALSE,
+  /* .min_width = */ 0.f,
+  /* .min_height = */ 0.f,
+  /* .natural_width = */ 0.f,
+  /* .natural_height = */ 0.f,
 };
 
 static void
@@ -18175,3 +18307,289 @@ clutter_actor_get_content_scaling_filters (ClutterActor         *self,
   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_x_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_y_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;
+
+  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_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_x_expand:
+ * @self: a #ClutterActor
+ *
+ * Checks whether an actor, or any of its children, is set to expand
+ * horizontally.
+ *
+ * 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_x_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;
+
+  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;
+
+  clutter_actor_compute_expand (self);
+
+  return self->priv->needs_x_expand;
+}
diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h
index b55afd1..36cda22 100644
--- a/clutter/clutter-actor.h
+++ b/clutter/clutter-actor.h
@@ -439,6 +439,20 @@ void                            clutter_actor_set_margin
 CLUTTER_AVAILABLE_IN_1_10
 void                            clutter_actor_get_margin                        (ClutterActor                *self,
                                                                                  ClutterMargin               *margin);
+CLUTTER_AVAILABLE_IN_1_12
+void                            clutter_actor_set_x_expand                      (ClutterActor                *self,
+                                                                                 gboolean                     expand);
+CLUTTER_AVAILABLE_IN_1_12
+gboolean                        clutter_actor_get_x_expand                      (ClutterActor                *self);
+CLUTTER_AVAILABLE_IN_1_12
+void                            clutter_actor_set_y_expand                      (ClutterActor                *self,
+                                                                                 gboolean                     expand);
+CLUTTER_AVAILABLE_IN_1_12
+gboolean                        clutter_actor_get_y_expand                      (ClutterActor                *self);
+CLUTTER_AVAILABLE_IN_1_12
+gboolean                        clutter_actor_needs_x_expand                    (ClutterActor                *self);
+CLUTTER_AVAILABLE_IN_1_12
+gboolean                        clutter_actor_needs_y_expand                    (ClutterActor                *self);
 
 /* Paint */
 void                            clutter_actor_set_clip                          (ClutterActor                *self,
diff --git a/clutter/clutter.symbols b/clutter/clutter.symbols
index 6da9a4b..a4424ac 100644
--- a/clutter/clutter.symbols
+++ b/clutter/clutter.symbols
@@ -151,8 +151,10 @@ clutter_actor_get_transition
 clutter_actor_get_type
 clutter_actor_get_width
 clutter_actor_get_x_align
+clutter_actor_get_x_expand
 clutter_actor_get_x
 clutter_actor_get_y_align
+clutter_actor_get_y_expand
 clutter_actor_get_y
 clutter_actor_get_z_rotation_gravity
 clutter_actor_grab_key_focus
@@ -189,6 +191,8 @@ clutter_actor_meta_set_name
 clutter_actor_move_anchor_point
 clutter_actor_move_anchor_point_from_gravity
 clutter_actor_move_by
+clutter_actor_needs_x_expand
+clutter_actor_needs_y_expand
 clutter_actor_new
 clutter_actor_paint
 clutter_actor_pop_internal
@@ -259,8 +263,10 @@ clutter_actor_set_size
 clutter_actor_set_text_direction
 clutter_actor_set_width
 clutter_actor_set_x_align
+clutter_actor_set_x_expand
 clutter_actor_set_x
 clutter_actor_set_y_align
+clutter_actor_set_y_expand
 clutter_actor_set_y
 clutter_actor_set_z_rotation_from_gravity
 clutter_actor_should_pick_paint



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]