[clutter/wip/apocalypses/apocalypse-3: 3/35] Add ClutterContent



commit f588177ce491a0f5693c0a05c78beba4cfd2baed
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Wed Feb 1 18:33:55 2012 +0000

    Add ClutterContent
    
    ClutterContent is an interface for creating delegate objects that handle
    what an actor is going to paint.
    
    Since they are a newly added type, they only hook into the new PaintNode
    based API.
    
    The position and size of the content is controlled in part by the
    content's own preferred size, and by the ClutterContentGravity
    enumeration.

 clutter/Makefile.am               |    3 +
 clutter/clutter-actor.c           |  417 ++++++++++++++++++++++++++++++++++++-
 clutter/clutter-actor.h           |   15 ++
 clutter/clutter-content-private.h |   47 ++++
 clutter/clutter-content.c         |  248 ++++++++++++++++++++++
 clutter/clutter-content.h         |  100 +++++++++
 clutter/clutter-enums.h           |   36 ++++
 clutter/clutter-types.h           |    1 +
 clutter/clutter.h                 |    1 +
 tests/interactive/Makefile.am     |    3 +-
 tests/interactive/test-content.c  |  223 ++++++++++++++++++++
 11 files changed, 1091 insertions(+), 3 deletions(-)
---
diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index 6c22c42..9df651b 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -74,6 +74,7 @@ source_h =					\
 	$(srcdir)/clutter-colorize-effect.h	\
 	$(srcdir)/clutter-constraint.h		\
 	$(srcdir)/clutter-container.h		\
+	$(srcdir)/clutter-content.h		\
 	$(srcdir)/clutter-deform-effect.h	\
 	$(srcdir)/clutter-deprecated.h		\
 	$(srcdir)/clutter-desaturate-effect.h	\
@@ -151,6 +152,7 @@ source_c = \
 	$(srcdir)/clutter-colorize-effect.c	\
 	$(srcdir)/clutter-constraint.c		\
 	$(srcdir)/clutter-container.c		\
+	$(srcdir)/clutter-content.c		\
 	$(srcdir)/clutter-deform-effect.c	\
 	$(srcdir)/clutter-desaturate-effect.c	\
 	$(srcdir)/clutter-device-manager.c	\
@@ -209,6 +211,7 @@ source_h_priv = \
 	$(srcdir)/clutter-actor-private.h		\
 	$(srcdir)/clutter-backend-private.h		\
 	$(srcdir)/clutter-bezier.h			\
+	$(srcdir)/clutter-content-private.h		\
 	$(srcdir)/clutter-debug.h 			\
 	$(srcdir)/clutter-device-manager-private.h	\
 	$(srcdir)/clutter-easing.h			\
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index 6336dc4..8f9da94 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -380,6 +380,7 @@
 #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"
@@ -506,6 +507,11 @@ struct _ClutterActorPrivate
   /* 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;
+
+  ClutterContentGravity content_gravity;
+
   /* used when painting, to update the paint volume */
   ClutterEffect *current_effect;
 
@@ -668,6 +674,10 @@ enum
   PROP_FIRST_CHILD,
   PROP_LAST_CHILD,
 
+  PROP_CONTENT,
+  PROP_CONTENT_GRAVITY,
+  PROP_CONTENT_BOX,
+
   PROP_LAST
 };
 
@@ -2045,6 +2055,10 @@ clutter_actor_set_allocation_internal (ClutterActor           *self,
 
       g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]);
 
+      /* if the allocation changes, so does the content box */
+      if (priv->content != NULL)
+        g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]);
+
       retval = TRUE;
     }
   else
@@ -3109,6 +3123,9 @@ clutter_actor_paint_node (ClutterActor     *actor,
       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);
 
@@ -4353,6 +4370,14 @@ clutter_actor_set_property (GObject      *object,
       clutter_actor_set_background_color (actor, g_value_get_boxed (value));
       break;
 
+    case PROP_CONTENT:
+      clutter_actor_set_content (actor, g_value_get_object (value));
+      break;
+
+    case PROP_CONTENT_GRAVITY:
+      clutter_actor_set_content_gravity (actor, g_value_get_enum (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -4751,6 +4776,23 @@ clutter_actor_get_property (GObject    *object,
       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;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -4807,8 +4849,13 @@ clutter_actor_dispose (GObject *object)
   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);
@@ -6152,6 +6199,66 @@ clutter_actor_class_init (ClutterActorClass *klass)
                          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.
+   *
+   * 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,
+                        CLUTTER_PARAM_READABLE);
+
   g_object_class_install_properties (object_class, PROP_LAST, obj_props);
 
   /**
@@ -6731,6 +6838,12 @@ clutter_actor_init (ClutterActor *self)
   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;
 }
 
 /**
@@ -17236,3 +17349,303 @@ clutter_actor_restore_easing_state (ClutterActor *self)
   g_array_remove_index (info->states, info->states->len - 1);
   info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
 }
+
+/**
+ * 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_object_unref (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.
+ *
+ * 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_gravity = gravity;
+
+  clutter_actor_queue_redraw (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_GRAVITY]);
+  g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
+}
+
+/**
+ * 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.
+ *
+ * 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;
+
+  if (!clutter_actor_has_allocation (self))
+    return;
+
+  if (priv->content == NULL)
+    return;
+
+  *box = priv->allocation;
+
+  /* no need to do any more work */
+  if (priv->content_gravity == CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
+    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;
+
+  clutter_actor_box_get_size (&priv->allocation, &alloc_w, &alloc_h);
+
+  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:
+      if (content_w >= content_h && content_h > 0)
+        {
+          double ratio = content_w / content_h;
+
+          box->x2 = box->x1 + alloc_w;
+
+          box->y1 += ceilf ((alloc_h - (alloc_h / ratio)) / 2.0);
+          box->y2 = box->y1 + (alloc_h / ratio);
+        }
+      else if (content_h > content_w && content_w > 0)
+        {
+          double ratio = content_h / content_w;
+
+          box->x1 += ceilf ((alloc_w - (alloc_w / ratio)) / 2.0);
+          box->x2 = box->x2 + (alloc_w / ratio);
+
+          box->y2 = box->x1 + alloc_h;
+        }
+      break;
+    }
+}
diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h
index 81fa794..42f0fce 100644
--- a/clutter/clutter-actor.h
+++ b/clutter/clutter-actor.h
@@ -467,6 +467,21 @@ gboolean                        clutter_actor_is_in_clone_paint
 gboolean                        clutter_actor_get_paint_box                     (ClutterActor               *self,
                                                                                  ClutterActorBox            *box);
 gboolean                        clutter_actor_has_overlaps                      (ClutterActor               *self);
+
+/* Content */
+CLUTTER_AVAILABLE_IN_1_10
+void                            clutter_actor_set_content                       (ClutterActor               *self,
+                                                                                 ClutterContent             *content);
+CLUTTER_AVAILABLE_IN_1_10
+ClutterContent *                clutter_actor_get_content                       (ClutterActor               *self);
+CLUTTER_AVAILABLE_IN_1_10
+void                            clutter_actor_set_content_gravity               (ClutterActor               *self,
+                                                                                 ClutterContentGravity       gravity);
+CLUTTER_AVAILABLE_IN_1_10
+ClutterContentGravity           clutter_actor_get_content_gravity               (ClutterActor               *self);
+CLUTTER_AVAILABLE_IN_1_10
+void                            clutter_actor_get_content_box                   (ClutterActor               *self,
+                                                                                 ClutterActorBox            *box);
 CLUTTER_AVAILABLE_IN_1_10
 void                            clutter_actor_set_background_color              (ClutterActor               *self,
                                                                                  const ClutterColor         *color);
diff --git a/clutter/clutter-content-private.h b/clutter/clutter-content-private.h
new file mode 100644
index 0000000..56c3f78
--- /dev/null
+++ b/clutter/clutter-content-private.h
@@ -0,0 +1,47 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011  Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+#ifndef __CLUTTER_CONTENT_PRIVATE_H__
+#define __CLUTTER_CONTENT_PRIVATE_H__
+
+#include <clutter/clutter-content.h>
+
+G_BEGIN_DECLS
+
+gboolean        _clutter_content_get_preferred_size     (ClutterContent   *content,
+                                                         gfloat           *width,
+                                                         gfloat           *height);
+
+void            _clutter_content_attached               (ClutterContent   *content,
+                                                         ClutterActor     *actor);
+void            _clutter_content_detached               (ClutterContent   *content,
+                                                         ClutterActor     *actor);
+
+void            _clutter_content_paint_content          (ClutterContent   *content,
+                                                         ClutterActor     *actor,
+                                                         ClutterPaintNode *node);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_CONTENT_PRIVATE_H__ */
diff --git a/clutter/clutter-content.c b/clutter/clutter-content.c
new file mode 100644
index 0000000..d14a5ce
--- /dev/null
+++ b/clutter/clutter-content.c
@@ -0,0 +1,248 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011  Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+/**
+ * SECTION:clutter-content
+ * @Title: ClutterContent
+ * @short_desc: Delegate for painting the content of an actor
+ *
+ * #ClutterContent is an interface to implement types responsible for
+ * painting the content of a #ClutterActor.
+ *
+ * Multiple actors can use the same #ClutterContent instance, in order
+ * to share the resources associated with painting the same content.
+ *
+ * #ClutterContent is available since Clutter 1.10.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "clutter-content-private.h"
+
+#include "clutter-debug.h"
+#include "clutter-marshal.h"
+#include "clutter-private.h"
+
+typedef struct _ClutterContentIface     ClutterContentInterface;
+
+static GQuark quark_content_actors = 0;
+
+G_DEFINE_INTERFACE (ClutterContent, clutter_content, G_TYPE_OBJECT)
+
+static gboolean
+clutter_content_real_get_preferred_size (ClutterContent *content,
+                                         gfloat         *width,
+                                         gfloat         *height)
+{
+  if (width != NULL)
+    *width = 0.f;
+
+  if (height != NULL)
+    *height = 0.f;
+
+  return FALSE;
+}
+
+static void
+clutter_content_real_attached (ClutterContent *content,
+                               ClutterActor   *actor)
+{
+  GObject *obj = G_OBJECT (content);
+  GHashTable *actors;
+
+  actors = g_object_get_qdata (obj, quark_content_actors);
+  if (actors == NULL)
+    {
+      actors = g_hash_table_new (NULL, NULL);
+      g_object_set_qdata_full (obj, quark_content_actors,
+                               actors,
+                               (GDestroyNotify) g_hash_table_unref);
+    }
+
+  g_hash_table_insert (actors, actor, actor);
+}
+
+static void
+clutter_content_real_detached (ClutterContent *content,
+                               ClutterActor   *actor)
+{
+  GObject *obj = G_OBJECT (content);
+  GHashTable *actors;
+
+  actors = g_object_get_qdata (obj, quark_content_actors);
+  g_assert (actors != NULL);
+
+  g_hash_table_remove (actors, actor);
+
+  if (g_hash_table_size (actors) == 0)
+    g_object_set_qdata (obj, quark_content_actors, NULL);
+}
+
+static void
+clutter_content_real_invalidate (ClutterContent *content)
+{
+  GHashTable *actors;
+  GHashTableIter iter;
+  gpointer key_p, value_p;
+
+  actors = g_object_get_qdata (G_OBJECT (content), quark_content_actors);
+  if (actors == NULL)
+    return;
+
+  g_hash_table_iter_init (&iter, actors);
+  while (g_hash_table_iter_next (&iter, &key_p, &value_p))
+    {
+      ClutterActor *actor = key_p;
+
+      g_assert (actor != NULL);
+
+      clutter_actor_queue_redraw (actor);
+    }
+}
+
+static void
+clutter_content_real_paint_content (ClutterContent   *content,
+                                    ClutterActor     *actor,
+                                    ClutterPaintNode *context)
+{
+}
+
+static void
+clutter_content_default_init (ClutterContentInterface *iface)
+{
+  quark_content_actors = g_quark_from_static_string ("-clutter-content-actors");
+
+  iface->get_preferred_size = clutter_content_real_get_preferred_size;
+  iface->paint_content = clutter_content_real_paint_content;
+  iface->attached = clutter_content_real_attached;
+  iface->detached = clutter_content_real_detached;
+  iface->invalidate = clutter_content_real_invalidate;
+}
+
+/**
+ * clutter_content_invalidate:
+ * @content: a #ClutterContent
+ *
+ * Invalidates a #ClutterContent.
+ *
+ * This function should be called by #ClutterContent implementations when
+ * they change the way a the content should be painted regardless of the
+ * actor state.
+ *
+ * Since: 1.10
+ */
+void
+clutter_content_invalidate (ClutterContent *content)
+{
+  g_return_if_fail (CLUTTER_IS_CONTENT (content));
+
+  CLUTTER_CONTENT_GET_IFACE (content)->invalidate (content);
+}
+
+/*< private >
+ * _clutter_content_attached:
+ * @content: a #ClutterContent
+ * @actor: a #ClutterActor
+ *
+ * Attaches @actor to the @content.
+ *
+ * This function should be used internally every time a #ClutterActor
+ * is associated to a #ClutterContent, to set up a backpointer from
+ * the @content to the @actor.
+ *
+ * This function will invoke the #ClutterContentIface.attached() virtual
+ * function.
+ */
+void
+_clutter_content_attached (ClutterContent *content,
+                           ClutterActor   *actor)
+{
+  CLUTTER_CONTENT_GET_IFACE (content)->attached (content, actor);
+}
+
+/*< private >
+ * _clutter_content_detached:
+ * @content: a #ClutterContent
+ * @actor: a #ClutterActor
+ *
+ * Detaches @actor from @content.
+ *
+ * This function should be used internally every time a #ClutterActor
+ * removes the association with a #ClutterContent.
+ *
+ * This function will invoke the #ClutterContentIface.detached() virtual
+ * function.
+ */
+void
+_clutter_content_detached (ClutterContent *content,
+                           ClutterActor   *actor)
+{
+  CLUTTER_CONTENT_GET_IFACE (content)->detached (content, actor);
+}
+
+/*< private >
+ * _clutter_content_paint_content:
+ * @content: a #ClutterContent
+ * @actor: a #ClutterActor
+ * @context: a #ClutterPaintNode
+ *
+ * Creates the render tree for the @content and @actor.
+ *
+ * This function will invoke the #ClutterContentIface.paint_content()
+ * virtual function.
+ */
+void
+_clutter_content_paint_content (ClutterContent   *content,
+                                ClutterActor     *actor,
+                                ClutterPaintNode *node)
+{
+  CLUTTER_CONTENT_GET_IFACE (content)->paint_content (content, actor, node);
+}
+
+/*< private >
+ * _clutter_content_get_preferred_size:
+ * @content: a #ClutterContent
+ * @width: (out): return location for the natural width of the content
+ * @height: (out): return location for the natural height of the content
+ *
+ * Retrieves the natural size of the @content, if any.
+ *
+ * The natural size of a #ClutterContent is defined as the size the content
+ * would have regardless of the allocation of the actor that is painting it,
+ * for instance the size of an image data.
+ *
+ * Return value: %TRUE if the content has a preferred size, and %FALSE
+ *   otherwise
+ */
+gboolean
+_clutter_content_get_preferred_size (ClutterContent *content,
+                                     gfloat         *width,
+                                     gfloat         *height)
+{
+  return CLUTTER_CONTENT_GET_IFACE (content)->get_preferred_size (content,
+                                                                  width,
+                                                                  height);
+}
diff --git a/clutter/clutter-content.h b/clutter/clutter-content.h
new file mode 100644
index 0000000..0f91fa2
--- /dev/null
+++ b/clutter/clutter-content.h
@@ -0,0 +1,100 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011  Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <clutter/clutter.h> can be included directly."
+#endif
+
+#ifndef __CLUTTER_CONTENT_H__
+#define __CLUTTER_CONTENT_H__
+
+#include <clutter/clutter-types.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_CONTENT            (clutter_content_get_type ())
+#define CLUTTER_CONTENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CONTENT, ClutterContent))
+#define CLUTTER_IS_CONTENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CONTENT))
+#define CLUTTER_CONTENT_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), CLUTTER_TYPE_CONTENT, ClutterContentIface))
+
+typedef struct _ClutterContentIface     ClutterContentIface;
+
+/**
+ * ClutterContent:
+ *
+ * The <structname>ClutterContent</structname> structure is an opaque type
+ * whose members cannot be acccessed directly.
+ *
+ * Since: 1.10
+ */
+
+/**
+ * ClutterContentIface:
+ * @get_preferred_size: virtual function; should be overridden by subclasses
+ *   of #ClutterContent that have a natural size
+ * @paint_content: virtual function; called each time the content needs to
+ *   paint itself
+ * @attached: virtual function; called each time a #ClutterContent is attached
+ *   to a #ClutterActor. If overridden, the subclass must chain up to the
+ *   parent class implementation.
+ * @detached: virtual function; called each time a #ClutterContent is detached
+ *   from a #ClutterActor. If overridden, the subclass must chain up to the
+ *   parent class implementation.
+ * @invalidate: virtual function; called each time a #ClutterContent state
+ *   is changed. If overridden, the subclass must chain up to the parent
+ *   class implementation.
+ *
+ * The <structname>ClutterContentIface</structname> structure contains only
+ * private data.
+ *
+ * Since: 1.10
+ */
+struct _ClutterContentIface
+{
+  /*< private >*/
+  GTypeInterface g_iface;
+
+  /*< public >*/
+  gboolean      (* get_preferred_size)  (ClutterContent   *content,
+                                         gfloat           *width,
+                                         gfloat           *height);
+  void          (* paint_content)       (ClutterContent   *content,
+                                         ClutterActor     *actor,
+                                         ClutterPaintNode *node);
+
+  void          (* attached)            (ClutterContent   *content,
+                                         ClutterActor     *actor);
+  void          (* detached)            (ClutterContent   *content,
+                                         ClutterActor     *actor);
+
+  void          (* invalidate)          (ClutterContent   *content);
+};
+
+GType clutter_content_get_type (void) G_GNUC_CONST;
+
+void            clutter_content_invalidate              (ClutterContent *content);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_CONTENT_H__ */
diff --git a/clutter/clutter-enums.h b/clutter/clutter-enums.h
index 0588780..8e68015 100644
--- a/clutter/clutter-enums.h
+++ b/clutter/clutter-enums.h
@@ -1113,6 +1113,42 @@ typedef enum {
   CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD = 1 << 2
 } ClutterRepaintFlags;
 
+/**
+ * ClutterContentGravity:
+ * @CLUTTER_CONTENT_GRAVITY_TOP_LEFT: Align the content to the top left corner
+ * @CLUTTER_CONTENT_GRAVITY_TOP: Align the content to the top edge
+ * @CLUTTER_CONTENT_GRAVITY_TOP_RIGHT: Align the content to the top right corner
+ * @CLUTTER_CONTENT_GRAVITY_LEFT: Align the content to the left edge
+ * @CLUTTER_CONTENT_GRAVITY_CENTER: Align the content to the center
+ * @CLUTTER_CONTENT_GRAVITY_RIGHT: Align the content to the right edge
+ * @CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT: Align the content to the bottom left corner
+ * @CLUTTER_CONTENT_GRAVITY_BOTTOM: Align the content to the bottom edge
+ * @CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT: Align the content to the bottom right corner
+ * @CLUTTER_CONTENT_GRAVITY_RESIZE_FILL: Resize the content to fill the allocation
+ * @CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT: Resize the content to remain within the
+ *   allocation, while maintaining the aspect ratio
+ *
+ * Controls the alignment of the #ClutterContent inside a #ClutterActor.
+ *
+ * Since: 1.10
+ */
+typedef enum {
+  CLUTTER_CONTENT_GRAVITY_TOP_LEFT,
+  CLUTTER_CONTENT_GRAVITY_TOP,
+  CLUTTER_CONTENT_GRAVITY_TOP_RIGHT,
+
+  CLUTTER_CONTENT_GRAVITY_LEFT,
+  CLUTTER_CONTENT_GRAVITY_CENTER,
+  CLUTTER_CONTENT_GRAVITY_RIGHT,
+
+  CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT,
+  CLUTTER_CONTENT_GRAVITY_BOTTOM,
+  CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT,
+
+  CLUTTER_CONTENT_GRAVITY_RESIZE_FILL,
+  CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT
+} ClutterContentGravity;
+
 G_END_DECLS
 
 #endif /* __CLUTTER_ENUMS_H__ */
diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h
index 3fd9335..8b41f63 100644
--- a/clutter/clutter-types.h
+++ b/clutter/clutter-types.h
@@ -54,6 +54,7 @@ typedef struct _ClutterActorMeta        ClutterActorMeta;
 typedef struct _ClutterLayoutManager    ClutterLayoutManager;
 typedef struct _ClutterActorIter        ClutterActorIter;
 typedef struct _ClutterPaintNode        ClutterPaintNode;
+typedef struct _ClutterContent          ClutterContent; /* dummy */
 
 typedef struct _ClutterAlpha            ClutterAlpha;
 typedef struct _ClutterAnimatable       ClutterAnimatable; /* dummy */
diff --git a/clutter/clutter.h b/clutter/clutter.h
index 52d443f..070691b 100644
--- a/clutter/clutter.h
+++ b/clutter/clutter.h
@@ -55,6 +55,7 @@
 #include "clutter-colorize-effect.h"
 #include "clutter-constraint.h"
 #include "clutter-container.h"
+#include "clutter-content.h"
 #include "clutter-deform-effect.h"
 #include "clutter-desaturate-effect.h"
 #include "clutter-device-manager.h"
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index a31e87c..b0523e5 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -56,7 +56,8 @@ UNIT_TESTS = \
 	test-drop.c \
 	test-devices.c \
 	test-actor.c \
-	test-transitions.c
+	test-transitions.c \
+	test-content.c
 
 if X11_TESTS
 UNIT_TESTS += test-pixmap.c
diff --git a/tests/interactive/test-content.c b/tests/interactive/test-content.c
new file mode 100644
index 0000000..f8d007a
--- /dev/null
+++ b/tests/interactive/test-content.c
@@ -0,0 +1,223 @@
+#include <stdlib.h>
+#include <gmodule.h>
+#include <clutter/clutter.h>
+
+typedef struct _ColorContent {
+  GObject parent_instance;
+
+  double red;
+  double green;
+  double blue;
+  double alpha;
+
+  float padding;
+} ColorContent;
+
+typedef struct _ColorContentClass {
+  GObjectClass parent_class;
+} ColorContentClass;
+
+static void clutter_content_iface_init (ClutterContentIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ColorContent, color_content, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
+                                                clutter_content_iface_init))
+
+static void
+color_content_paint_content (ClutterContent   *content,
+                             ClutterActor     *actor,
+                             ClutterPaintNode *root)
+{
+  ColorContent *self = (ColorContent *) content;
+  ClutterActorBox box, content_box;
+  ClutterColor color;
+  PangoLayout *layout;
+  PangoRectangle logical;
+  ClutterPaintNode *node;
+
+#if 0
+  g_debug ("Painting content [%p] "
+           "{ r:%.2f, g:%.2f, b:%.2f, a:%.2f } "
+           "for actor [%p] (context: [%p])",
+           content,
+           self->red,
+           self->green,
+           self->blue,
+           self->alpha,
+           actor, context);
+#endif
+
+  clutter_actor_get_content_box (actor, &content_box);
+
+  box = content_box;
+  box.x1 += self->padding;
+  box.y1 += self->padding;
+  box.x2 -= self->padding;
+  box.y2 -= self->padding;
+
+  color.alpha = self->alpha * 255;
+
+  color.red = self->red * 255;
+  color.green = self->green * 255;
+  color.blue = self->blue * 255;
+
+  node = clutter_color_node_new (&color);
+  clutter_paint_node_add_rectangle (node, &box);
+  clutter_paint_node_add_child (root, node);
+  clutter_paint_node_unref (node);
+
+  color.red = (1.0 - self->red) * 255;
+  color.green = (1.0 - self->green) * 255;
+  color.blue = (1.0 - self->blue) * 255;
+
+  layout = clutter_actor_create_pango_layout (actor, "A");
+  pango_layout_get_pixel_extents (layout, NULL, &logical);
+
+  node = clutter_text_node_new (layout, &color);
+
+  /* top-left */
+  box.x1 = clutter_actor_box_get_x (&content_box);
+  box.y1 = clutter_actor_box_get_y (&content_box);
+  clutter_paint_node_add_rectangle (node, &box);
+
+  /* top-right */
+  box.x1 = clutter_actor_box_get_x (&content_box)
+         + clutter_actor_box_get_width (&content_box)
+         - logical.width;
+  box.y1 = clutter_actor_box_get_y (&content_box);
+  clutter_paint_node_add_rectangle (node, &box);
+
+  /* bottom-right */
+  box.x1 = clutter_actor_box_get_x (&content_box)
+         + clutter_actor_box_get_width (&content_box)
+         - logical.width;
+  box.y1 = clutter_actor_box_get_y (&content_box)
+         + clutter_actor_box_get_height (&content_box)
+         - logical.height;
+  clutter_paint_node_add_rectangle (node, &box);
+
+  /* bottom-left */
+  box.x1 = clutter_actor_box_get_x (&content_box);
+  box.y1 = clutter_actor_box_get_y (&content_box)
+         + clutter_actor_box_get_height (&content_box)
+         - logical.height;
+  clutter_paint_node_add_rectangle (node, &box);
+
+  /* center */
+  box.x1 = clutter_actor_box_get_x (&content_box)
+         + (clutter_actor_box_get_width (&content_box) - logical.width) / 2.0;
+  box.y1 = clutter_actor_box_get_y (&content_box)
+         + (clutter_actor_box_get_height (&content_box) - logical.height) / 2.0;
+  clutter_paint_node_add_rectangle (node, &box);
+
+  clutter_paint_node_add_child (root, node);
+  clutter_paint_node_unref (node);
+
+  g_object_unref (layout);
+}
+
+static void
+clutter_content_iface_init (ClutterContentIface *iface)
+{
+  iface->paint_content = color_content_paint_content;
+}
+
+static void
+color_content_class_init (ColorContentClass *klass)
+{
+}
+
+static void
+color_content_init (ColorContent *self)
+{
+}
+
+static ClutterContent *
+color_content_new (double red,
+                   double green,
+                   double blue,
+                   double alpha,
+                   float  padding)
+{
+  ColorContent *self = g_object_new (color_content_get_type (), NULL);
+
+  self->red = red;
+  self->green = green;
+  self->blue = blue;
+  self->alpha = alpha;
+  self->padding = padding;
+
+  return (ClutterContent *) self;
+}
+
+G_MODULE_EXPORT int
+test_content_main (int argc, char *argv[])
+{
+  ClutterActor *stage, *grid;
+  ClutterContent *content;
+  int i, n_rects;
+
+  if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
+    return EXIT_FAILURE;
+
+  stage = clutter_stage_new ();
+  clutter_actor_set_name (stage, "Stage");
+  clutter_stage_set_title (CLUTTER_STAGE (stage), "Content");
+  clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
+  g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
+  clutter_actor_show (stage);
+
+  grid = clutter_actor_new ();
+  clutter_actor_set_name (grid, "Grid");
+  clutter_actor_set_margin_top (grid, 12);
+  clutter_actor_set_margin_right (grid, 12);
+  clutter_actor_set_margin_bottom (grid, 12);
+  clutter_actor_set_margin_left (grid, 12);
+  clutter_actor_set_layout_manager (grid, clutter_flow_layout_new (CLUTTER_FLOW_HORIZONTAL));
+  clutter_actor_add_constraint (grid, clutter_bind_constraint_new (stage, CLUTTER_BIND_SIZE, 0.0));
+  clutter_actor_add_child (stage, grid);
+
+  content = color_content_new (g_random_double_range (0.0, 1.0),
+                               g_random_double_range (0.0, 1.0),
+                               g_random_double_range (0.0, 1.0),
+                               1.0,
+                               2.0);
+
+  n_rects = g_random_int_range (12, 24);
+  for (i = 0; i < n_rects; i++)
+    {
+      ClutterActor *box = clutter_actor_new ();
+      ClutterColor bg_color = {
+        g_random_int_range (0, 255),
+        g_random_int_range (0, 255),
+        g_random_int_range (0, 255),
+        255
+      };
+      char *name, *color;
+
+      color = clutter_color_to_string (&bg_color);
+      name = g_strconcat ("Box <", color, ">", NULL);
+      clutter_actor_set_name (box, name);
+
+      g_free (name);
+      g_free (color);
+
+      clutter_actor_set_background_color (box, &bg_color);
+      clutter_actor_set_content (box, content);
+      clutter_actor_set_size (box, 64, 64);
+
+      clutter_actor_add_child (grid, box);
+    }
+
+  clutter_main ();
+
+  g_object_unref (content);
+
+  return EXIT_SUCCESS;
+}
+
+G_MODULE_EXPORT const char *
+test_content_describe (void)
+{
+  return "A simple test for ClutterContent";
+}



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