diff --git a/.gitignore b/.gitignore
index cfecd86ca..71c402306 100644
--- a/.gitignore
+++ b/.gitignore
@@ -132,6 +132,9 @@ TAGS
/tests/interactive/redhand_alpha.png
/tests/interactive/test-script.json
/tests/interactive/test-clutter-cairo-flowers
+/tests/interactive/test-bin-layout
+/tests/interactive/test-flow-layout
+/tests/interactive/test-box-layout
/tests/conform/stamp-test-conformance
/tests/conform/test-anchors
/tests/conform/test-conformance
diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index 8348b1040..6b162dc99 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -64,6 +64,9 @@ source_h = \
$(srcdir)/clutter-behaviour-rotate.h \
$(srcdir)/clutter-behaviour-scale.h \
$(srcdir)/clutter-binding-pool.h \
+ $(srcdir)/clutter-bin-layout.h \
+ $(srcdir)/clutter-box.h \
+ $(srcdir)/clutter-box-layout.h \
$(srcdir)/clutter-cairo-texture.h \
$(srcdir)/clutter-child-meta.h \
$(srcdir)/clutter-clone.h \
@@ -73,10 +76,14 @@ source_h = \
$(srcdir)/clutter-event.h \
$(srcdir)/clutter-feature.h \
$(srcdir)/clutter-fixed.h \
+ $(srcdir)/clutter-fixed-layout.h \
+ $(srcdir)/clutter-flow-layout.h \
$(srcdir)/clutter-frame-source.h \
$(srcdir)/clutter-group.h \
$(srcdir)/clutter-interval.h \
$(srcdir)/clutter-keysyms.h \
+ $(srcdir)/clutter-layout-manager.h \
+ $(srcdir)/clutter-layout-meta.h \
$(srcdir)/clutter-list-model.h \
$(srcdir)/clutter-main.h \
$(srcdir)/clutter-media.h \
@@ -128,6 +135,9 @@ source_c = \
$(srcdir)/clutter-behaviour-scale.c \
$(srcdir)/clutter-bezier.c \
$(srcdir)/clutter-binding-pool.c \
+ $(srcdir)/clutter-bin-layout.c \
+ $(srcdir)/clutter-box.c \
+ $(srcdir)/clutter-box-layout.c \
$(srcdir)/clutter-cairo-texture.c \
$(srcdir)/clutter-child-meta.c \
$(srcdir)/clutter-clone.c \
@@ -137,10 +147,14 @@ source_c = \
$(srcdir)/clutter-event.c \
$(srcdir)/clutter-feature.c \
$(srcdir)/clutter-fixed.c \
+ $(srcdir)/clutter-fixed-layout.c \
+ $(srcdir)/clutter-flow-layout.c \
$(srcdir)/clutter-frame-source.c \
$(srcdir)/clutter-group.c \
$(srcdir)/clutter-id-pool.c \
$(srcdir)/clutter-interval.c \
+ $(srcdir)/clutter-layout-manager.c \
+ $(srcdir)/clutter-layout-meta.c \
$(srcdir)/clutter-list-model.c \
$(srcdir)/clutter-main.c \
clutter-marshal.c \
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index 652365a80..6d75c3c5e 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -473,8 +473,6 @@ static void clutter_actor_set_natural_width_set (ClutterActor *self,
gboolean use_natural_width);
static void clutter_actor_set_natural_height_set (ClutterActor *self,
gboolean use_natural_height);
-static void clutter_actor_set_request_mode (ClutterActor *self,
- ClutterRequestMode mode);
static void clutter_actor_update_map_state (ClutterActor *self,
MapStateChange change);
static void clutter_actor_unrealize_not_hiding (ClutterActor *self);
@@ -3297,7 +3295,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
* gfloat natural_width, min_width;
* gfloat natural_height, min_height;
*
- * g_object_get (G_OBJECT (child), "request-mode", &mode, NULL);
+ * mode = clutter_actor_get_request_mode (child);
* if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
* {
* clutter_actor_get_preferred_width (child, -1,
@@ -5184,11 +5182,28 @@ clutter_actor_set_natural_height_set (ClutterActor *self,
clutter_actor_queue_relayout (self);
}
-static void
-clutter_actor_set_request_mode (ClutterActor *self,
- ClutterRequestMode mode)
+/**
+ * clutter_actor_set_request_mode:
+ * @self: a #ClutterActor
+ * @mode: the request mode
+ *
+ * Sets the geometry request mode of @self.
+ *
+ * The @mode determines the order for invoking
+ * clutter_actor_get_preferred_width() and
+ * clutter_actor_get_preferred_height()
+ *
+ * Since: 1.2
+ */
+void
+clutter_actor_set_request_mode (ClutterActor *self,
+ ClutterRequestMode mode)
{
- ClutterActorPrivate *priv = self->priv;
+ ClutterActorPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ priv = self->priv;
if (priv->request_mode == mode)
return;
@@ -5203,6 +5218,25 @@ clutter_actor_set_request_mode (ClutterActor *self,
clutter_actor_queue_relayout (self);
}
+/**
+ * clutter_actor_get_request_mode:
+ * @self: a #ClutterActor
+ *
+ * Retrieves the geometry request mode of @self
+ *
+ * Return value: the request mode for the actor
+ *
+ * Since: 1.2
+ */
+ClutterRequestMode
+clutter_actor_get_request_mode (ClutterActor *self)
+{
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
+ CLUTTER_REQUEST_HEIGHT_FOR_WIDTH);
+
+ return self->priv->request_mode;
+}
+
/* variant of set_width() without checks and without notification
* freeze+thaw, for internal usage only
*/
diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h
index d3e28a91b..4a5a38319 100644
--- a/clutter/clutter-actor.h
+++ b/clutter/clutter-actor.h
@@ -301,6 +301,9 @@ void clutter_actor_queue_relayout (ClutterActor
void clutter_actor_destroy (ClutterActor *self);
/* size negotiation */
+void clutter_actor_set_request_mode (ClutterActor *self,
+ ClutterRequestMode mode);
+ClutterRequestMode clutter_actor_get_request_mode (ClutterActor *self);
void clutter_actor_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c
new file mode 100644
index 000000000..80100feda
--- /dev/null
+++ b/clutter/clutter-bin-layout.c
@@ -0,0 +1,865 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+/**
+ * SECTION:clutter-bin-layout
+ * @short_description: A simple layout manager
+ *
+ * #ClutterBinLayout is a layout manager which implements the following
+ * policy:
+ *
+ *
+ * the preferred size is the maximum preferred size
+ * between all the children of the container using the
+ * layout;
+ * each child is allocated in "layers", on on top
+ * of the other;
+ * for each layer there are horizontal and vertical
+ * alignment policies.
+ *
+ *
+ *
+ * How to pack actors inside a BinLayout
+ * The following code shows how to build a composite actor with
+ * a texture and a background, and add controls overlayed on top. The
+ * background is set to fill the whole allocation, whilst the texture
+ * is centered; there is a control in the top right corner and a label
+ * in the bottom, filling out the whole allocated width.
+ *
+ * ClutterLayoutManager *manager;
+ * ClutterActor *box;
+ *
+ * /* create the layout first */
+ * layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
+ * CLUTTER_BIN_ALIGNMENT_CENTER);
+ * box = clutter_box_new (layout); /* then the container */
+ *
+ * /* we can use the layout object to add actors */
+ * clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), background,
+ * CLUTTER_BIN_ALIGNMENT_FILL,
+ * CLUTTER_BIN_ALIGNMENT_FILL);
+ * clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), icon,
+ * CLUTTER_BIN_ALIGNMENT_CENTER,
+ * CLUTTER_BIN_ALIGNMENT_CENTER);
+ *
+ * /* align to the bottom left */
+ * clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), label,
+ * CLUTTER_BIN_ALIGNMENT_START,
+ * CLUTTER_BIN_ALIGNMENT_END);
+ * /* align to the top right */
+ * clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), button,
+ * CLUTTER_BIN_ALIGNMENT_END,
+ * CLUTTER_BIN_ALIGNMENT_START);
+ *
+ *
+ *
+ * #ClutterBinLayout is available since Clutter 1.2
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+
+#include "clutter-actor.h"
+#include "clutter-animatable.h"
+#include "clutter-bin-layout.h"
+#include "clutter-child-meta.h"
+#include "clutter-debug.h"
+#include "clutter-enum-types.h"
+#include "clutter-layout-meta.h"
+#include "clutter-private.h"
+
+#define CLUTTER_TYPE_BIN_LAYER (clutter_bin_layer_get_type ())
+#define CLUTTER_BIN_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BIN_LAYER, ClutterBinLayer))
+#define CLUTTER_IS_BIN_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BIN_LAYER))
+
+#define CLUTTER_BIN_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutPrivate))
+
+typedef struct _ClutterBinLayer ClutterBinLayer;
+typedef struct _ClutterLayoutMetaClass ClutterBinLayerClass;
+
+struct _ClutterBinLayoutPrivate
+{
+ ClutterBinAlignment x_align;
+ ClutterBinAlignment y_align;
+
+ ClutterContainer *container;
+};
+
+struct _ClutterBinLayer
+{
+ ClutterLayoutMeta parent_instance;
+
+ ClutterBinAlignment x_align;
+ ClutterBinAlignment y_align;
+};
+
+enum
+{
+ PROP_LAYER_0,
+
+ PROP_LAYER_X_ALIGN,
+ PROP_LAYER_Y_ALIGN
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_X_ALIGN,
+ PROP_Y_ALIGN
+};
+
+G_DEFINE_TYPE (ClutterBinLayer,
+ clutter_bin_layer,
+ CLUTTER_TYPE_LAYOUT_META);
+
+G_DEFINE_TYPE (ClutterBinLayout,
+ clutter_bin_layout,
+ CLUTTER_TYPE_LAYOUT_MANAGER);
+
+/*
+ * ClutterBinLayer
+ */
+
+static void
+set_layer_x_align (ClutterBinLayer *self,
+ ClutterBinAlignment alignment)
+{
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ if (self->x_align == alignment)
+ return;
+
+ self->x_align = alignment;
+
+ meta = CLUTTER_LAYOUT_META (self);
+ manager = clutter_layout_meta_get_manager (meta);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (self), "x-align");
+}
+
+static void
+set_layer_y_align (ClutterBinLayer *self,
+ ClutterBinAlignment alignment)
+{
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ if (self->y_align == alignment)
+ return;
+
+ self->y_align = alignment;
+
+ meta = CLUTTER_LAYOUT_META (self);
+ manager = clutter_layout_meta_get_manager (meta);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (self), "y-align");
+}
+
+static void
+clutter_bin_layer_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterBinLayer *layer = CLUTTER_BIN_LAYER (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_LAYER_X_ALIGN:
+ set_layer_x_align (layer, g_value_get_enum (value));
+ break;
+
+ case PROP_LAYER_Y_ALIGN:
+ set_layer_y_align (layer, g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_bin_layer_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterBinLayer *layer = CLUTTER_BIN_LAYER (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_LAYER_X_ALIGN:
+ g_value_set_enum (value, layer->x_align);
+ break;
+
+ case PROP_LAYER_Y_ALIGN:
+ g_value_set_enum (value, layer->y_align);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_bin_layer_class_init (ClutterBinLayerClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ gobject_class->set_property = clutter_bin_layer_set_property;
+ gobject_class->get_property = clutter_bin_layer_get_property;
+
+ pspec = g_param_spec_enum ("x-align",
+ "Horizontal Alignment",
+ "Horizontal alignment for the actor "
+ "inside the layer",
+ CLUTTER_TYPE_BIN_ALIGNMENT,
+ CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_LAYER_X_ALIGN,
+ pspec);
+
+ pspec = g_param_spec_enum ("y-align",
+ "Vertical Alignment",
+ "Vertical alignment for the actor "
+ "inside the layer manager",
+ CLUTTER_TYPE_BIN_ALIGNMENT,
+ CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_LAYER_Y_ALIGN,
+ pspec);
+}
+
+static void
+clutter_bin_layer_init (ClutterBinLayer *layer)
+{
+ layer->x_align = CLUTTER_BIN_ALIGNMENT_CENTER;
+ layer->y_align = CLUTTER_BIN_ALIGNMENT_CENTER;
+}
+
+/*
+ * ClutterBinLayout
+ */
+
+static void
+set_x_align (ClutterBinLayout *self,
+ ClutterBinAlignment alignment)
+{
+ ClutterBinLayoutPrivate *priv = self->priv;
+
+ if (priv->x_align != alignment)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->x_align = alignment;
+
+ manager = CLUTTER_LAYOUT_MANAGER (self);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (self), "x-align");
+ }
+}
+
+static void
+set_y_align (ClutterBinLayout *self,
+ ClutterBinAlignment alignment)
+{
+ ClutterBinLayoutPrivate *priv = self->priv;
+
+ if (priv->y_align != alignment)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->y_align = alignment;
+
+ manager = CLUTTER_LAYOUT_MANAGER (self);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (self), "y-align");
+ }
+}
+
+static void
+clutter_bin_layout_get_preferred_width (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *nat_width_p)
+{
+ GList *children = clutter_container_get_children (container);
+ GList *l;
+ gfloat min_width, nat_width;
+
+ min_width = nat_width = 0.0;
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+ gfloat minimum, natural;
+
+ clutter_actor_get_preferred_width (child, for_height,
+ &minimum,
+ &natural);
+
+ min_width = MAX (min_width, minimum);
+ nat_width = MAX (nat_width, natural);
+ }
+
+ if (min_width_p)
+ *min_width_p = min_width;
+
+ if (nat_width_p)
+ *nat_width_p = nat_width;
+}
+
+static void
+clutter_bin_layout_get_preferred_height (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *nat_height_p)
+{
+ GList *children = clutter_container_get_children (container);
+ GList *l;
+ gfloat min_height, nat_height;
+
+ min_height = nat_height = 0.0;
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+ gfloat minimum, natural;
+
+ clutter_actor_get_preferred_height (child, for_width,
+ &minimum,
+ &natural);
+
+ min_height = MAX (min_height, minimum);
+ nat_height = MAX (nat_height, natural);
+ }
+
+ if (min_height_p)
+ *min_height_p = min_height;
+
+ if (nat_height_p)
+ *nat_height_p = nat_height;
+}
+
+static gdouble
+get_bin_alignment_factor (ClutterBinAlignment alignment)
+{
+ switch (alignment)
+ {
+ case CLUTTER_BIN_ALIGNMENT_CENTER:
+ return 0.5;
+
+ case CLUTTER_BIN_ALIGNMENT_START:
+ return 0.0;
+
+ case CLUTTER_BIN_ALIGNMENT_END:
+ return 1.0;
+
+ case CLUTTER_BIN_ALIGNMENT_FIXED:
+ case CLUTTER_BIN_ALIGNMENT_FILL:
+ return 0.0;
+ }
+
+ return 0.0;
+}
+
+static void
+clutter_bin_layout_allocate (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags)
+{
+ GList *children = clutter_container_get_children (container);
+ GList *l;
+ gfloat available_w, available_h;
+
+ available_w = clutter_actor_box_get_width (allocation);
+ available_h = clutter_actor_box_get_height (allocation);
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+ ClutterLayoutMeta *meta;
+ ClutterBinLayer *layer;
+ ClutterActorBox child_alloc = { 0, };
+ gfloat child_width, child_height;
+ ClutterRequestMode request;
+
+ meta = clutter_layout_manager_get_child_meta (manager,
+ container,
+ child);
+ layer = CLUTTER_BIN_LAYER (meta);
+
+ if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL)
+ {
+ child_alloc.x1 = 0;
+ child_alloc.x2 = ceilf (available_w);
+ }
+
+ if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL)
+ {
+ child_alloc.y1 = 0;
+ child_alloc.y2 = ceilf (available_h);
+ }
+
+ /* if we are filling horizontally and vertically then we
+ * can break here because we already have a full allocation
+ */
+ if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL &&
+ layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL)
+ {
+ clutter_actor_allocate (child, &child_alloc, flags);
+ continue;
+ }
+
+ request = clutter_actor_get_request_mode (child);
+ if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
+ {
+ gfloat min_width, nat_width;
+ gfloat min_height, nat_height;
+
+ clutter_actor_get_preferred_width (child, available_h,
+ &min_width,
+ &nat_width);
+ child_width = CLAMP (nat_width, min_width, available_w);
+
+ clutter_actor_get_preferred_height (child, child_width,
+ &min_height,
+ &nat_height);
+ child_height = CLAMP (nat_height, min_height, available_h);
+ }
+ else if (request == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
+ {
+ gfloat min_width, nat_width;
+ gfloat min_height, nat_height;
+
+ clutter_actor_get_preferred_height (child, available_w,
+ &min_height,
+ &nat_height);
+ child_height = CLAMP (nat_height, min_height, available_h);
+
+ clutter_actor_get_preferred_width (child, child_height,
+ &min_width,
+ &nat_width);
+ child_width = CLAMP (nat_width, min_width, available_w);
+ }
+
+ if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FIXED)
+ {
+ child_alloc.x1 = ceilf (clutter_actor_get_x (child));
+ child_alloc.x2 = ceilf (child_alloc.x1 + child_width);
+ }
+ else
+ {
+ gdouble x_align = get_bin_alignment_factor (layer->x_align);
+
+ if (layer->x_align != CLUTTER_BIN_ALIGNMENT_FILL)
+ {
+ child_alloc.x1 = ceilf ((available_w - child_width) * x_align);
+ child_alloc.x2 = ceilf (child_alloc.x1 + child_width);
+ }
+ }
+
+ if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FIXED)
+ {
+ child_alloc.y1 = ceilf (clutter_actor_get_y (child));
+ child_alloc.y2 = ceilf (child_alloc.y1 + child_height);
+ }
+ else
+ {
+ gdouble y_align = get_bin_alignment_factor (layer->y_align);
+
+ if (layer->y_align != CLUTTER_BIN_ALIGNMENT_FILL)
+ {
+ child_alloc.y1 = ceilf ((available_h - child_height) * y_align);
+ child_alloc.y2 = ceilf (child_alloc.y1 + child_height);
+ }
+ }
+
+ clutter_actor_allocate (child, &child_alloc, flags);
+ }
+
+ g_list_free (children);
+}
+
+static ClutterLayoutMeta *
+clutter_bin_layout_create_child_meta (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor)
+{
+ ClutterBinLayoutPrivate *priv;
+
+ priv = CLUTTER_BIN_LAYOUT (manager)->priv;
+
+ return g_object_new (CLUTTER_TYPE_BIN_LAYER,
+ "container", container,
+ "actor", actor,
+ "manager", manager,
+ "x-align", priv->x_align,
+ "y_align", priv->y_align,
+ NULL);
+}
+
+static void
+clutter_bin_layout_set_container (ClutterLayoutManager *manager,
+ ClutterContainer *container)
+{
+ ClutterBinLayoutPrivate *priv;
+
+ priv = CLUTTER_BIN_LAYOUT (manager)->priv;
+ priv->container = container;
+}
+
+static void
+clutter_bin_layout_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterBinLayout *layout = CLUTTER_BIN_LAYOUT (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_X_ALIGN:
+ set_x_align (layout, g_value_get_enum (value));
+ break;
+
+ case PROP_Y_ALIGN:
+ set_y_align (layout, g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_bin_layout_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterBinLayoutPrivate *priv;
+
+ priv = CLUTTER_BIN_LAYOUT (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_X_ALIGN:
+ g_value_set_enum (value, priv->x_align);
+ break;
+
+ case PROP_Y_ALIGN:
+ g_value_set_enum (value, priv->y_align);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_bin_layout_class_init (ClutterBinLayoutClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterLayoutManagerClass *layout_class =
+ CLUTTER_LAYOUT_MANAGER_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (ClutterBinLayoutPrivate));
+
+ gobject_class->set_property = clutter_bin_layout_set_property;
+ gobject_class->get_property = clutter_bin_layout_get_property;
+
+ /**
+ * ClutterBinLayout:x-align:
+ *
+ * The default horizontal alignment policy for actors managed
+ * by the #ClutterBinLayout
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_enum ("x-align",
+ "Horizontal Alignment",
+ "Default horizontal alignment for the actors "
+ "inside the layout manager",
+ CLUTTER_TYPE_BIN_ALIGNMENT,
+ CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec);
+
+ /**
+ * ClutterBinLayout:y-align:
+ *
+ * The default vertical alignment policy for actors managed
+ * by the #ClutterBinLayout
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_enum ("y-align",
+ "Vertical Alignment",
+ "Default vertical alignment for the actors "
+ "inside the layout manager",
+ CLUTTER_TYPE_BIN_ALIGNMENT,
+ CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec);
+
+ layout_class->get_preferred_width =
+ clutter_bin_layout_get_preferred_width;
+ layout_class->get_preferred_height =
+ clutter_bin_layout_get_preferred_height;
+ layout_class->allocate =
+ clutter_bin_layout_allocate;
+ layout_class->create_child_meta =
+ clutter_bin_layout_create_child_meta;
+ layout_class->set_container =
+ clutter_bin_layout_set_container;
+}
+
+static void
+clutter_bin_layout_init (ClutterBinLayout *self)
+{
+ self->priv = CLUTTER_BIN_LAYOUT_GET_PRIVATE (self);
+
+ self->priv->x_align = CLUTTER_BIN_ALIGNMENT_CENTER;
+ self->priv->y_align = CLUTTER_BIN_ALIGNMENT_CENTER;
+}
+
+/**
+ * clutter_bin_layout_new:
+ * @x_align: the default alignment policy to be used on the
+ * horizontal axis
+ * @y_align: the default alignment policy to be used on the
+ * vertical axis
+ *
+ * Creates a new #ClutterBinLayout layout manager
+ *
+ * Return value: the newly created layout manager
+ *
+ * Since: 1.2
+ */
+ClutterLayoutManager *
+clutter_bin_layout_new (ClutterBinAlignment x_align,
+ ClutterBinAlignment y_align)
+{
+ return g_object_new (CLUTTER_TYPE_BIN_LAYOUT,
+ "x-align", x_align,
+ "y-align", y_align,
+ NULL);
+}
+
+/**
+ * clutter_bin_layout_set_alignment:
+ * @self: a #ClutterBinLayout
+ * @child: (allow-none): a child of @container
+ * @x_align: the horizontal alignment policy to be used for the @child
+ * inside @container
+ * @y_align: the vertical aligment policy to be used on the @child
+ * inside @container
+ *
+ * Sets the horizontal and vertical alignment policies to be applied
+ * to a @child of @self
+ *
+ * If @child is %NULL then the @x_align and @y_align values will
+ * be set as the default alignment policies
+ *
+ * Since: 1.2
+ */
+void
+clutter_bin_layout_set_alignment (ClutterBinLayout *self,
+ ClutterActor *child,
+ ClutterBinAlignment x_align,
+ ClutterBinAlignment y_align)
+{
+ ClutterBinLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self));
+ g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child));
+
+ priv = self->priv;
+
+ if (priv->container == NULL)
+ {
+ if (child == NULL)
+ {
+ set_x_align (self, x_align);
+ set_y_align (self, y_align);
+ }
+ else
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before setting the alignment "
+ "on its children",
+ G_OBJECT_TYPE_NAME (self));
+
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (self);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ child);
+ g_assert (CLUTTER_IS_BIN_LAYER (meta));
+
+ set_layer_x_align (CLUTTER_BIN_LAYER (meta), x_align);
+ set_layer_y_align (CLUTTER_BIN_LAYER (meta), y_align);
+}
+
+/**
+ * clutter_bin_layout_get_alignment:
+ * @self: a #ClutterBinLayout
+ * @child: (allow-none): a child of @container
+ * @x_align: (out) (allow-none): return location for the horizontal
+ * alignment policy
+ * @y_align: (out) (allow-none): return location for the vertical
+ * alignment policy
+ *
+ * Retrieves the horizontal and vertical alignment policies for
+ * a child of @self
+ *
+ * If @child is %NULL the default alignment policies will be returned
+ * instead
+ *
+ * Since: 1.2
+ */
+void
+clutter_bin_layout_get_alignment (ClutterBinLayout *self,
+ ClutterActor *child,
+ ClutterBinAlignment *x_align,
+ ClutterBinAlignment *y_align)
+{
+ ClutterBinLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+ ClutterBinLayer *layer;
+
+ g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self));
+
+ priv = self->priv;
+
+ if (priv->container == NULL)
+ {
+ if (child == NULL)
+ {
+ if (x_align)
+ *x_align = priv->x_align;
+
+ if (y_align)
+ *y_align = priv->y_align;
+ }
+ else
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before getting the alignment "
+ "of its children",
+ G_OBJECT_TYPE_NAME (self));
+
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (self);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ child);
+ g_assert (CLUTTER_IS_BIN_LAYER (meta));
+
+ layer = CLUTTER_BIN_LAYER (meta);
+
+ if (x_align)
+ *x_align = layer->x_align;
+
+ if (y_align)
+ *y_align = layer->y_align;
+}
+
+/**
+ * clutter_bin_layout_add:
+ * @self: a #ClutterBinLayout
+ * @child: a #ClutterActor
+ * @x_align: horizontal alignment policy for @child
+ * @y_align: vertical alignment policy for @child
+ *
+ * Adds a #ClutterActor to the container using @self and
+ * sets the alignment policies for it
+ *
+ * This function is equivalent to clutter_container_add_actor()
+ * and clutter_layout_manager_child_set_property() but it does not
+ * require a pointer to the #ClutterContainer associated to the
+ * #ClutterBinLayout
+ *
+ * Since: 1.2
+ */
+void
+clutter_bin_layout_add (ClutterBinLayout *self,
+ ClutterActor *child,
+ ClutterBinAlignment x_align,
+ ClutterBinAlignment y_align)
+{
+ ClutterBinLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self));
+ g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+ priv = self->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before adding children",
+ G_OBJECT_TYPE_NAME (self));
+ return;
+ }
+
+ clutter_container_add_actor (priv->container, child);
+
+ manager = CLUTTER_LAYOUT_MANAGER (self);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ child);
+ g_assert (CLUTTER_IS_BIN_LAYER (meta));
+
+ set_layer_x_align (CLUTTER_BIN_LAYER (meta), x_align);
+ set_layer_y_align (CLUTTER_BIN_LAYER (meta), y_align);
+}
diff --git a/clutter/clutter-bin-layout.h b/clutter/clutter-bin-layout.h
new file mode 100644
index 000000000..169af0774
--- /dev/null
+++ b/clutter/clutter-bin-layout.h
@@ -0,0 +1,123 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_BIN_LAYOUT_H__
+#define __CLUTTER_BIN_LAYOUT_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_BIN_LAYOUT (clutter_bin_layout_get_type ())
+#define CLUTTER_BIN_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayout))
+#define CLUTTER_IS_BIN_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BIN_LAYOUT))
+#define CLUTTER_BIN_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutClass))
+#define CLUTTER_IS_BIN_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BIN_LAYOUT))
+#define CLUTTER_BIN_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutClass))
+
+typedef struct _ClutterBinLayout ClutterBinLayout;
+typedef struct _ClutterBinLayoutPrivate ClutterBinLayoutPrivate;
+typedef struct _ClutterBinLayoutClass ClutterBinLayoutClass;
+
+/**
+ * ClutterBinAlignment:
+ * @CLUTTER_BIN_ALIGNMENT_FIXED: Fixed position alignment; the
+ * #ClutterBinLayout will honour the fixed position provided
+ * by the actors themselves when allocating them
+ * @CLUTTER_BIN_ALIGNMENT_FILL: Fill the allocation size
+ * @CLUTTER_BIN_ALIGNMENT_START: Position the actors at the top
+ * or left side of the container, depending on the axis
+ * @CLUTTER_BIN_ALIGNMENT_END: Position the actors at the bottom
+ * or right side of the container, depending on the axis
+ * @CLUTTER_BIN_ALIGNMENT_CENTER: Position the actors at the
+ * center of the container, depending on the axis
+ *
+ * The alignment policies available on each axis for #ClutterBinLayout
+ *
+ * Since: 1.2
+ */
+typedef enum {
+ CLUTTER_BIN_ALIGNMENT_FIXED,
+ CLUTTER_BIN_ALIGNMENT_FILL,
+ CLUTTER_BIN_ALIGNMENT_START,
+ CLUTTER_BIN_ALIGNMENT_END,
+ CLUTTER_BIN_ALIGNMENT_CENTER
+} ClutterBinAlignment;
+
+/**
+ * ClutterBinLayout:
+ *
+ * The #ClutterBinLayout structure contains only private data
+ * and should be accessed using the provided API
+ *
+ * Since: 1.2
+ */
+struct _ClutterBinLayout
+{
+ /*< private >*/
+ ClutterLayoutManager parent_instance;
+
+ ClutterBinLayoutPrivate *priv;
+};
+
+/**
+ * ClutterBinLayoutClass:
+ *
+ * The #ClutterBinLayoutClass structure contains only private
+ * data and should be accessed using the provided API
+ *
+ * Since: 1.2
+ */
+struct _ClutterBinLayoutClass
+{
+ /*< private >*/
+ ClutterLayoutManagerClass parent_class;
+};
+
+GType clutter_bin_layout_get_type (void) G_GNUC_CONST;
+
+ClutterLayoutManager *clutter_bin_layout_new (ClutterBinAlignment x_align,
+ ClutterBinAlignment y_align);
+
+void clutter_bin_layout_set_alignment (ClutterBinLayout *self,
+ ClutterActor *child,
+ ClutterBinAlignment x_align,
+ ClutterBinAlignment y_align);
+void clutter_bin_layout_get_alignment (ClutterBinLayout *self,
+ ClutterActor *child,
+ ClutterBinAlignment *x_align,
+ ClutterBinAlignment *y_align);
+
+void clutter_bin_layout_add (ClutterBinLayout *self,
+ ClutterActor *child,
+ ClutterBinAlignment x_align,
+ ClutterBinAlignment y_align);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_BIN_LAYOUT_H__ */
diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c
new file mode 100644
index 000000000..c9371f950
--- /dev/null
+++ b/clutter/clutter-box-layout.c
@@ -0,0 +1,1496 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ *
+ * Based on the NBTK NbtkBoxLayout actor by:
+ * Thomas Wood
+ */
+
+/**
+ * SECTION:clutter-box-layout
+ * @short_description: A layout manager arranging children on a single line
+ *
+ * The #ClutterBoxLayout is a #ClutterLayoutManager implementing the
+ * following layout policy:
+ *
+ * all children are arranged on a single
+ * line;
+ * the axis used is controlled by the
+ * #ClutterBoxLayout:vertical boolean property;
+ * the order of the packing is determined by the
+ * #ClutterBoxLayout:pack-start boolean property;
+ * each child will be allocated to its natural
+ * size or, if set to expand, the available size;
+ * if a child is set to fill on either (or both)
+ * axis, its allocation will match all the available size; the
+ * fill layout property only makes sense if the expand property is
+ * also set;
+ * if a child is set to expand but not to fill then
+ * it is possible to control the alignment using the X and Y alignment
+ * layout properties.
+ *
+ *
+ * It is possible to control the spacing between children of a
+ * #ClutterBoxLayout by using clutter_box_layout_set_spacing().
+ *
+ * In order to set the layout properties when packing an actor inside a
+ * #ClutterBoxLayout you should use the clutter_box_layout_pack()
+ * function.
+ *
+ * #ClutterBoxLayout is available since Clutter 1.2
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+
+#include "clutter-box-layout.h"
+#include "clutter-debug.h"
+#include "clutter-enum-types.h"
+#include "clutter-layout-meta.h"
+#include "clutter-private.h"
+#include "clutter-types.h"
+
+#define CLUTTER_TYPE_BOX_CHILD (clutter_box_child_get_type ())
+#define CLUTTER_BOX_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BOX_CHILD, ClutterBoxChild))
+#define CLUTTER_IS_BOX_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BOX_CHILD))
+
+#define CLUTTER_BOX_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayoutPrivate))
+
+typedef struct _ClutterBoxChild ClutterBoxChild;
+typedef struct _ClutterLayoutMetaClass ClutterBoxChildClass;
+
+struct _ClutterBoxLayoutPrivate
+{
+ ClutterContainer *container;
+
+ guint spacing;
+
+ guint is_vertical : 1;
+ guint is_pack_start : 1;
+};
+
+struct _ClutterBoxChild
+{
+ ClutterLayoutMeta parent_instance;
+
+ ClutterBoxAlignment x_align;
+ ClutterBoxAlignment y_align;
+
+ guint x_fill : 1;
+ guint y_fill : 1;
+
+ guint expand : 1;
+};
+
+enum
+{
+ PROP_CHILD_0,
+
+ PROP_CHILD_X_ALIGN,
+ PROP_CHILD_Y_ALIGN,
+ PROP_CHILD_X_FILL,
+ PROP_CHILD_Y_FILL,
+ PROP_CHILD_EXPAND
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_SPACING,
+ PROP_VERTICAL,
+ PROP_PACK_START
+};
+
+G_DEFINE_TYPE (ClutterBoxChild,
+ clutter_box_child,
+ CLUTTER_TYPE_LAYOUT_META);
+
+G_DEFINE_TYPE (ClutterBoxLayout,
+ clutter_box_layout,
+ CLUTTER_TYPE_LAYOUT_MANAGER);
+
+/*
+ * ClutterBoxChild
+ */
+
+static void
+box_child_set_align (ClutterBoxChild *self,
+ ClutterBoxAlignment x_align,
+ ClutterBoxAlignment y_align)
+{
+ gboolean x_changed = FALSE, y_changed = FALSE;
+
+ if (self->x_align != x_align)
+ {
+ self->x_align = x_align;
+
+ x_changed = TRUE;
+ }
+
+ if (self->y_align != y_align)
+ {
+ self->y_align = y_align;
+
+ y_changed = TRUE;
+ }
+
+ if (x_changed || y_changed)
+ {
+ ClutterLayoutManager *layout;
+
+ layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
+ clutter_layout_manager_layout_changed (layout);
+
+ if (x_changed)
+ g_object_notify (G_OBJECT (self), "x-align");
+
+ if (y_changed)
+ g_object_notify (G_OBJECT (self), "y-align");
+ }
+}
+
+static void
+box_child_set_fill (ClutterBoxChild *self,
+ gboolean x_fill,
+ gboolean y_fill)
+{
+ gboolean x_changed = FALSE, y_changed = FALSE;
+
+ if (self->x_fill != x_fill)
+ {
+ self->x_fill = x_fill;
+
+ x_changed = TRUE;
+ }
+
+ if (self->y_fill != y_fill)
+ {
+ self->y_fill = y_fill;
+
+ y_changed = TRUE;
+ }
+
+ if (x_changed || y_changed)
+ {
+ ClutterLayoutManager *layout;
+
+ layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
+ clutter_layout_manager_layout_changed (layout);
+
+ if (x_changed)
+ g_object_notify (G_OBJECT (self), "x-fill");
+
+ if (y_changed)
+ g_object_notify (G_OBJECT (self), "y-fill");
+ }
+}
+
+static void
+box_child_set_expand (ClutterBoxChild *self,
+ gboolean expand)
+{
+ if (self->expand != expand)
+ {
+ ClutterLayoutManager *layout;
+
+ self->expand = expand;
+
+ layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
+ clutter_layout_manager_layout_changed (layout);
+
+ g_object_notify (G_OBJECT (self), "expand");
+ }
+}
+
+static void
+clutter_box_child_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterBoxChild *self = CLUTTER_BOX_CHILD (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD_X_ALIGN:
+ box_child_set_align (self,
+ g_value_get_enum (value),
+ self->y_align);
+ break;
+
+ case PROP_CHILD_Y_ALIGN:
+ box_child_set_align (self,
+ self->x_align,
+ g_value_get_enum (value));
+ break;
+
+ case PROP_CHILD_X_FILL:
+ box_child_set_fill (self,
+ g_value_get_boolean (value),
+ self->y_fill);
+ break;
+
+ case PROP_CHILD_Y_FILL:
+ box_child_set_fill (self,
+ self->x_fill,
+ g_value_get_boolean (value));
+ break;
+
+ case PROP_CHILD_EXPAND:
+ box_child_set_expand (self, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_box_child_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterBoxChild *self = CLUTTER_BOX_CHILD (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD_X_ALIGN:
+ g_value_set_enum (value, self->x_align);
+ break;
+
+ case PROP_CHILD_Y_ALIGN:
+ g_value_set_enum (value, self->y_align);
+ break;
+
+ case PROP_CHILD_X_FILL:
+ g_value_set_boolean (value, self->x_fill);
+ break;
+
+ case PROP_CHILD_Y_FILL:
+ g_value_set_boolean (value, self->y_fill);
+ break;
+
+ case PROP_CHILD_EXPAND:
+ g_value_set_boolean (value, self->expand);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_box_child_class_init (ClutterBoxChildClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ gobject_class->set_property = clutter_box_child_set_property;
+ gobject_class->get_property = clutter_box_child_get_property;
+
+ pspec = g_param_spec_boolean ("expand",
+ "Expand",
+ "Allocate extra space for the child",
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_EXPAND, pspec);
+
+ pspec = g_param_spec_boolean ("x-fill",
+ "Horizontal Fill",
+ "Whether the child should receive priority "
+ "when the container is allocating spare space "
+ "on the horizontal axis",
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_X_FILL, pspec);
+
+ pspec = g_param_spec_boolean ("y-fill",
+ "Vertical Fill",
+ "Whether the child should receive priority "
+ "when the container is allocating spare space "
+ "on the vertical axis",
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_Y_FILL, pspec);
+
+ pspec = g_param_spec_enum ("x-align",
+ "Horizontal Alignment",
+ "Horizontal alignment of the actor within "
+ "the cell",
+ CLUTTER_TYPE_BOX_ALIGNMENT,
+ CLUTTER_BOX_ALIGNMENT_CENTER,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_X_ALIGN, pspec);
+
+ pspec = g_param_spec_enum ("y-align",
+ "Vertical Alignment",
+ "Vertical alignment of the actor within "
+ "the cell",
+ CLUTTER_TYPE_BOX_ALIGNMENT,
+ CLUTTER_BOX_ALIGNMENT_CENTER,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_Y_ALIGN, pspec);
+}
+
+static void
+clutter_box_child_init (ClutterBoxChild *self)
+{
+ self->x_align = CLUTTER_BOX_ALIGNMENT_CENTER;
+ self->y_align = CLUTTER_BOX_ALIGNMENT_CENTER;
+
+ self->x_fill = self->y_fill = FALSE;
+
+ self->expand = FALSE;
+}
+
+static inline void
+allocate_fill (ClutterActor *child,
+ ClutterActorBox *childbox,
+ ClutterBoxChild *box_child)
+{
+ gfloat natural_width, natural_height;
+ gfloat min_width, min_height;
+ gfloat child_width, child_height;
+ gfloat available_width, available_height;
+ ClutterRequestMode request;
+ ClutterActorBox allocation = { 0, };
+ gdouble x_align, y_align;
+
+ if (box_child->x_align == CLUTTER_BOX_ALIGNMENT_START)
+ x_align = 0.0;
+ else if (box_child->x_align == CLUTTER_BOX_ALIGNMENT_CENTER)
+ x_align = 0.5;
+ else
+ x_align = 1.0;
+
+ if (box_child->y_align == CLUTTER_BOX_ALIGNMENT_START)
+ y_align = 0.0;
+ else if (box_child->y_align == CLUTTER_BOX_ALIGNMENT_CENTER)
+ y_align = 0.5;
+ else
+ y_align = 1.0;
+
+ available_width = childbox->x2 - childbox->x1;
+ available_height = childbox->y2 - childbox->y1;
+
+ if (available_width < 0)
+ available_width = 0;
+
+ if (available_height < 0)
+ available_height = 0;
+
+ if (box_child->x_fill)
+ {
+ allocation.x1 = childbox->x1;
+ allocation.x2 = ceilf (allocation.x1 + available_width);
+ }
+
+ if (box_child->y_fill)
+ {
+ allocation.y1 = childbox->y1;
+ allocation.y2 = ceilf (allocation.y1 + available_height);
+ }
+
+ /* if we are filling horizontally and vertically then we're done */
+ if (box_child->x_fill && box_child->y_fill)
+ {
+ *childbox = allocation;
+ return;
+ }
+
+ request = clutter_actor_get_request_mode (child);
+ if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
+ {
+ clutter_actor_get_preferred_width (child, available_height,
+ &min_width,
+ &natural_width);
+
+ child_width = CLAMP (natural_width, min_width, available_width);
+
+ clutter_actor_get_preferred_height (child, child_width,
+ &min_height,
+ &natural_height);
+
+ child_height = CLAMP (natural_height, min_height, available_height);
+ }
+ else
+ {
+ clutter_actor_get_preferred_height (child, available_width,
+ &min_height,
+ &natural_height);
+
+ child_height = CLAMP (natural_height, min_height, available_height);
+
+ clutter_actor_get_preferred_width (child, child_height,
+ &min_width,
+ &natural_width);
+
+ child_width = CLAMP (natural_width, min_width, available_width);
+ }
+
+ if (!box_child->x_fill)
+ {
+ allocation.x1 = ceilf (childbox->x1
+ + ((available_width - child_width) * x_align));
+ allocation.x2 = ceilf (allocation.x1 + child_width);
+ }
+
+ if (!box_child->y_fill)
+ {
+ allocation.y1 = ceilf (childbox->y1
+ + ((available_height - child_height) * y_align));
+ allocation.y2 = ceilf (allocation.y1 + child_height);
+ }
+
+ *childbox = allocation;
+}
+
+static ClutterLayoutMeta *
+clutter_box_layout_create_child_meta (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ ClutterActor *actor)
+{
+ return g_object_new (CLUTTER_TYPE_BOX_CHILD,
+ "manager", layout,
+ "container", container,
+ "actor", actor,
+ NULL);
+}
+
+static void
+clutter_box_layout_set_container (ClutterLayoutManager *layout,
+ ClutterContainer *container)
+{
+ ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (layout)->priv;
+
+ priv->container = container;
+
+ if (priv->container != NULL)
+ {
+ ClutterRequestMode request_mode;
+
+ /* we need to change the :request-mode of the container
+ * to match the orientation
+ */
+ request_mode = (priv->is_vertical)
+ ? CLUTTER_REQUEST_WIDTH_FOR_HEIGHT
+ : CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
+ clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container),
+ request_mode);
+ }
+}
+
+static void
+get_preferred_width (ClutterBoxLayout *self,
+ const GList *children,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ ClutterBoxLayoutPrivate *priv = self->priv;
+ gint n_children = 0;
+ const GList *l;
+
+ if (min_width_p)
+ *min_width_p = 0;
+
+ if (natural_width_p)
+ *natural_width_p = 0;
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+ gfloat child_min = 0, child_nat = 0;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ n_children++;
+
+ clutter_actor_get_preferred_width (child,
+ (!priv->is_vertical)
+ ? for_height
+ : -1,
+ &child_min,
+ &child_nat);
+
+ if (priv->is_vertical)
+ {
+ if (min_width_p)
+ *min_width_p = MAX (child_min, *min_width_p);
+
+ if (natural_width_p)
+ *natural_width_p = MAX (child_nat, *natural_width_p);
+ }
+ else
+ {
+ if (min_width_p)
+ *min_width_p += child_min;
+
+ if (natural_width_p)
+ *natural_width_p += child_nat;
+ }
+ }
+
+
+ if (!priv->is_vertical && n_children > 1)
+ {
+ if (min_width_p)
+ *min_width_p += priv->spacing * (n_children - 1);
+
+ if (natural_width_p)
+ *natural_width_p += priv->spacing * (n_children - 1);
+ }
+}
+
+static void
+get_preferred_height (ClutterBoxLayout *self,
+ const GList *children,
+ gfloat for_height,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ ClutterBoxLayoutPrivate *priv = self->priv;
+ gint n_children = 0;
+ const GList *l;
+
+ if (min_height_p)
+ *min_height_p = 0;
+
+ if (natural_height_p)
+ *natural_height_p = 0;
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+ gfloat child_min = 0, child_nat = 0;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ n_children++;
+
+ clutter_actor_get_preferred_height (child,
+ (priv->is_vertical)
+ ? for_height
+ : -1,
+ &child_min,
+ &child_nat);
+
+ if (!priv->is_vertical)
+ {
+ if (min_height_p)
+ *min_height_p = MAX (child_min, *min_height_p);
+
+ if (natural_height_p)
+ *natural_height_p = MAX (child_nat, *natural_height_p);
+ }
+ else
+ {
+ if (min_height_p)
+ *min_height_p += child_min;
+
+ if (natural_height_p)
+ *natural_height_p += child_nat;
+ }
+ }
+
+ if (priv->is_vertical && n_children > 1)
+ {
+ if (min_height_p)
+ *min_height_p += priv->spacing * (n_children - 1);
+
+ if (natural_height_p)
+ *natural_height_p += priv->spacing * (n_children - 1);
+ }
+}
+
+static void
+clutter_box_layout_get_preferred_width (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (layout);
+ GList *children;
+
+ children = clutter_container_get_children (container);
+
+ get_preferred_width (self, children, for_height,
+ min_width_p,
+ natural_width_p);
+
+ g_list_free (children);
+}
+
+static void
+clutter_box_layout_get_preferred_height (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (layout);
+ GList *children;
+
+ children = clutter_container_get_children (container);
+
+ get_preferred_height (self, children, for_width,
+ min_height_p,
+ natural_height_p);
+
+ g_list_free (children);
+}
+
+static void
+clutter_box_layout_allocate (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (layout)->priv;
+ gfloat avail_width, avail_height, pref_width, pref_height;
+ GList *children, *l;
+ gint n_expand_children, extra_space;
+ gfloat position;
+
+ children = clutter_container_get_children (container);
+ if (children == NULL)
+ return;
+
+ clutter_actor_box_get_size (box, &avail_width, &avail_height);
+
+ if (priv->is_vertical)
+ {
+ get_preferred_height (CLUTTER_BOX_LAYOUT (layout),
+ children, avail_width,
+ NULL,
+ &pref_height);
+
+ pref_width = avail_width;
+ }
+ else
+ {
+ get_preferred_width (CLUTTER_BOX_LAYOUT (layout),
+ children, avail_height,
+ NULL,
+ &pref_width);
+
+ pref_height = avail_height;
+ }
+
+ /* count the number of children with expand set to TRUE */
+ n_expand_children = 0;
+ for (l = children; l; l = l->next)
+ {
+ ClutterLayoutMeta *meta;
+
+ meta = clutter_layout_manager_get_child_meta (layout,
+ container,
+ l->data);
+
+ if (CLUTTER_BOX_CHILD (meta)->expand)
+ n_expand_children++;
+ }
+
+ if (n_expand_children == 0)
+ {
+ extra_space = 0;
+ n_expand_children = 1;
+ }
+ else
+ {
+ if (priv->is_vertical)
+ extra_space = (avail_height - pref_height) / n_expand_children;
+ else
+ extra_space = (avail_width - pref_width) / n_expand_children;
+
+ /* don't shrink anything */
+ if (extra_space < 0)
+ extra_space = 0;
+ }
+
+ position = 0;
+
+ for (l = (priv->is_pack_start) ? g_list_last (children) : children;
+ l != NULL;
+ l = (priv->is_pack_start) ? l->prev : l->next)
+ {
+ ClutterActor *child = l->data;
+ ClutterActorBox child_box;
+ ClutterBoxChild *box_child;
+ ClutterLayoutMeta *meta;
+ gfloat child_nat;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ meta = clutter_layout_manager_get_child_meta (layout,
+ container,
+ child);
+ box_child = CLUTTER_BOX_CHILD (meta);
+
+ if (priv->is_vertical)
+ {
+ clutter_actor_get_preferred_height (child, avail_width,
+ NULL, &child_nat);
+
+ child_box.y1 = ceilf (position);
+
+ if (box_child->expand)
+ child_box.y2 = ceilf (position + child_nat + extra_space);
+ else
+ child_box.y2 = ceilf (position + child_nat);
+
+ child_box.x1 = 0;
+ child_box.x2 = ceilf (avail_width);
+
+ allocate_fill (child, &child_box, box_child);
+ clutter_actor_allocate (child, &child_box, flags);
+
+ if (box_child->expand)
+ position += (child_nat + priv->spacing + extra_space);
+ else
+ position += (child_nat + priv->spacing);
+ }
+ else
+ {
+ clutter_actor_get_preferred_width (child, avail_height,
+ NULL, &child_nat);
+
+ child_box.x1 = ceilf (position);
+
+ if (box_child->expand)
+ child_box.x2 = ceilf (position + child_nat + extra_space);
+ else
+ child_box.x2 = ceilf (position + child_nat);
+
+ child_box.y1 = 0;
+ child_box.y2 = ceilf (avail_height);
+
+ allocate_fill (child, &child_box, box_child);
+ clutter_actor_allocate (child, &child_box, flags);
+
+ if (box_child->expand)
+ position += (child_nat + priv->spacing + extra_space);
+ else
+ position += (child_nat + priv->spacing);
+ }
+ }
+
+ g_list_free (children);
+}
+
+static void
+clutter_box_layout_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_VERTICAL:
+ clutter_box_layout_set_vertical (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_SPACING:
+ clutter_box_layout_set_spacing (self, g_value_get_uint (value));
+ break;
+
+ case PROP_PACK_START:
+ clutter_box_layout_set_pack_start (self, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_box_layout_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_VERTICAL:
+ g_value_set_boolean (value, priv->is_vertical);
+ break;
+
+ case PROP_SPACING:
+ g_value_set_uint (value, priv->spacing);
+ break;
+
+ case PROP_PACK_START:
+ g_value_set_boolean (value, priv->is_pack_start);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_box_layout_class_init (ClutterBoxLayoutClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterLayoutManagerClass *layout_class;
+ GParamSpec *pspec;
+
+ layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
+
+ gobject_class->set_property = clutter_box_layout_set_property;
+ gobject_class->get_property = clutter_box_layout_get_property;
+
+ layout_class->get_preferred_width =
+ clutter_box_layout_get_preferred_width;
+ layout_class->get_preferred_height =
+ clutter_box_layout_get_preferred_height;
+ layout_class->allocate = clutter_box_layout_allocate;
+ layout_class->set_container = clutter_box_layout_set_container;
+ layout_class->create_child_meta = clutter_box_layout_create_child_meta;
+
+ g_type_class_add_private (klass, sizeof (ClutterBoxLayoutPrivate));
+
+ /**
+ * ClutterBoxLayout:vertical:
+ *
+ * Whether the #ClutterBoxLayout should arrange its children
+ * alongside the Y axis, instead of alongside the X axis
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_boolean ("vertical",
+ "Vertical",
+ "Whether the layout should be vertical, rather"
+ "than horizontal",
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_VERTICAL, pspec);
+
+ /**
+ * ClutterBoxLayout:pack-start:
+ *
+ * Whether the #ClutterBoxLayout should pack items at the start
+ * or append them at the end
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_boolean ("pack-start",
+ "Pack Start",
+ "Whether to pack items at the start of the box",
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_PACK_START, pspec);
+
+ /**
+ * ClutterBoxLayout:spacing:
+ *
+ * The spacing between children of the #ClutterBoxLayout, in pixels
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_uint ("spacing",
+ "Spacing",
+ "Spacing between children",
+ 0, G_MAXUINT, 0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_SPACING, pspec);
+}
+
+static void
+clutter_box_layout_init (ClutterBoxLayout *layout)
+{
+ ClutterBoxLayoutPrivate *priv;
+
+ layout->priv = priv = CLUTTER_BOX_LAYOUT_GET_PRIVATE (layout);
+
+ priv->is_vertical = FALSE;
+ priv->is_pack_start = FALSE;
+ priv->spacing = 0;
+}
+
+/**
+ * clutter_box_layout_new:
+ *
+ * Creates a new #ClutterBoxLayout layout manager
+ *
+ * Return value: the newly created #ClutterBoxLayout
+ *
+ * Since: 1.2
+ */
+ClutterLayoutManager *
+clutter_box_layout_new (void)
+{
+ return g_object_new (CLUTTER_TYPE_BOX_LAYOUT, NULL);
+}
+
+/**
+ * clutter_box_layout_set_spacing:
+ * @layout: a #ClutterBoxLayout
+ * @spacing: the spacing between children of the layout, in pixels
+ *
+ * Sets the spacing between children of @layout
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_layout_set_spacing (ClutterBoxLayout *layout,
+ guint spacing)
+{
+ ClutterBoxLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->spacing != spacing)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->spacing = spacing;
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (layout), "spacing");
+ }
+}
+
+/**
+ * clutter_box_layout_get_spacing:
+ * @layout: a #ClutterBoxLayout
+ *
+ * Retrieves the spacing set using clutter_box_layout_set_spacing()
+ *
+ * Return value: the spacing between children of the #ClutterBoxLayout
+ *
+ * Since: 1.2
+ */
+guint
+clutter_box_layout_get_spacing (ClutterBoxLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), 0);
+
+ return layout->priv->spacing;
+}
+
+/**
+ * clutter_box_layout_set_vertical:
+ * @layout: a #ClutterBoxLayout
+ * @vertical: %TRUE if the layout should be vertical
+ *
+ * Sets whether @layout should arrange its children vertically alongside
+ * the Y axis, instead of horizontally alongside the X axis
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_layout_set_vertical (ClutterBoxLayout *layout,
+ gboolean vertical)
+{
+ ClutterBoxLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->is_vertical != vertical)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->is_vertical = vertical ? TRUE : FALSE;
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (layout), "vertical");
+ }
+}
+
+/**
+ * clutter_box_layout_get_vertical:
+ * @layout: a #ClutterBoxLayout
+ *
+ * Retrieves the orientation of the @layout as set using the
+ * clutter_box_layout_set_vertical() function
+ *
+ * Return value: %TRUE if the #ClutterBoxLayout is arranging its children
+ * vertically, and %FALSE otherwise
+ *
+ * Since: 1.2
+ */
+gboolean
+clutter_box_layout_get_vertical (ClutterBoxLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE);
+
+ return layout->priv->is_vertical;
+}
+
+/**
+ * clutter_box_layout_set_pack_start:
+ * @layout: a #ClutterBoxLayout
+ * @pack_start: %TRUE if the @layout should pack children at the
+ * beginning of the layout
+ *
+ * Sets whether children of @layout should be layed out by appending
+ * them or by prepending them
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_layout_set_pack_start (ClutterBoxLayout *layout,
+ gboolean pack_start)
+{
+ ClutterBoxLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->is_pack_start != pack_start)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->is_pack_start = pack_start ? TRUE : FALSE;
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (layout), "pack-start");
+ }
+}
+
+/**
+ * clutter_box_layout_get_pack_start:
+ * @layout: a #ClutterBoxLayout
+ *
+ * Retrieves the value set using clutter_box_layout_set_pack_start()
+ *
+ * Return value: %TRUE if the #ClutterBoxLayout should pack children
+ * at the beginning of the layout, and %FALSE otherwise
+ *
+ * Since: 1.2
+ */
+gboolean
+clutter_box_layout_get_pack_start (ClutterBoxLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE);
+
+ return layout->priv->is_pack_start;
+}
+
+/**
+ * clutter_box_layout_pack:
+ * @layout: a #ClutterBoxLayout
+ * @actor: a #ClutterActor
+ * @expand: whether the @actor should expand
+ * @x_fill: whether the @actor should fill horizontally
+ * @y_fill: whether the @actor should fill vertically
+ * @x_align: the horizontal alignment policy for @actor
+ * @y_align: the vertical alignment policy for @actor
+ *
+ * Packs @actor inside the #ClutterContainer associated to @layout
+ * and sets the layout properties
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_layout_pack (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ gboolean expand,
+ gboolean x_fill,
+ gboolean y_fill,
+ ClutterBoxAlignment x_align,
+ ClutterBoxAlignment y_align)
+{
+ ClutterBoxLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before adding children",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ clutter_container_add_actor (priv->container, actor);
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ g_assert (CLUTTER_IS_BOX_CHILD (meta));
+
+ box_child_set_align (CLUTTER_BOX_CHILD (meta), x_align, y_align);
+ box_child_set_fill (CLUTTER_BOX_CHILD (meta), x_fill, y_fill);
+ box_child_set_expand (CLUTTER_BOX_CHILD (meta), expand);
+}
+
+/**
+ * clutter_box_layout_set_alignment:
+ * @layout: a #ClutterBoxLayout
+ * @actor: a #ClutterActor child of @layout
+ * @x_align: Horizontal alignment policy for @actor
+ * @y_align: Vertical alignment policy for @actor
+ *
+ * Sets the horizontal and vertical alignment policies for @actor
+ * inside @layout
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_layout_set_alignment (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ ClutterBoxAlignment x_align,
+ ClutterBoxAlignment y_align)
+{
+ ClutterBoxLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_BOX_CHILD (meta));
+
+ box_child_set_align (CLUTTER_BOX_CHILD (meta), x_align, y_align);
+}
+
+/**
+ * clutter_box_layout_get_alignment:
+ * @layout: a #ClutterBoxLayout
+ * @actor: a #ClutterActor child of @layout
+ * @x_align: (out): return location for the horizontal alignment policy
+ * @y_align: (out): return location for the vertical alignment policy
+ *
+ * Retrieves the horizontal and vertical alignment policies for @actor
+ * as set using clutter_box_layout_pack() or clutter_box_layout_set_alignment()
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_layout_get_alignment (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ ClutterBoxAlignment *x_align,
+ ClutterBoxAlignment *y_align)
+{
+ ClutterBoxLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_BOX_CHILD (meta));
+
+ if (x_align)
+ *x_align = CLUTTER_BOX_CHILD (meta)->x_align;
+
+ if (y_align)
+ *y_align = CLUTTER_BOX_CHILD (meta)->y_align;
+}
+
+/**
+ * clutter_box_layout_set_fill:
+ * @layout: a #ClutterBoxLayout
+ * @actor: a #ClutterActor child of @layout
+ * @x_fill: whether @actor should fill horizontally the allocated space
+ * @y_fill: whether @actor should fill vertically the allocated space
+ *
+ * Sets the horizontal and vertical fill policies for @actor
+ * inside @layout
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_layout_set_fill (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ gboolean x_fill,
+ gboolean y_fill)
+{
+ ClutterBoxLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_BOX_CHILD (meta));
+
+ box_child_set_fill (CLUTTER_BOX_CHILD (meta), x_fill, y_fill);
+}
+
+/**
+ * clutter_box_layout_get_fill:
+ * @layout: a #ClutterBoxLayout
+ * @actor: a #ClutterActor child of @layout
+ * @x_fill: (out): return location for the horizontal fill policy
+ * @y_fill: (out): return location for the vertical fill policy
+ *
+ * Retrieves the horizontal and vertical fill policies for @actor
+ * as set using clutter_box_layout_pack() or clutter_box_layout_set_fill()
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_layout_get_fill (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ gboolean *x_fill,
+ gboolean *y_fill)
+{
+ ClutterBoxLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_BOX_CHILD (meta));
+
+ if (x_fill)
+ *x_fill = CLUTTER_BOX_CHILD (meta)->x_fill;
+
+ if (y_fill)
+ *y_fill = CLUTTER_BOX_CHILD (meta)->y_fill;
+}
+
+/**
+ * clutter_box_layout_set_expand:
+ * @layout: a #ClutterBoxLayout
+ * @actor: a #ClutterActor child of @layout
+ * @expand: whether @actor should expand
+ *
+ * Sets whether @actor should expand inside @layout
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_layout_set_expand (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ gboolean expand)
+{
+ ClutterBoxLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_BOX_CHILD (meta));
+
+ box_child_set_expand (CLUTTER_BOX_CHILD (meta), expand);
+}
+
+/**
+ * clutter_box_layout_get_expand:
+ * @layout: a #ClutterBoxLayout
+ * @actor: a #ClutterActor child of @layout
+ *
+ * Retrieves whether @actor should expand inside @layout
+ *
+ * Return value: %TRUE if the #ClutterActor should expand, %FALSE otherwise
+ *
+ * Since: 1.2
+ */
+gboolean
+clutter_box_layout_get_expand (ClutterBoxLayout *layout,
+ ClutterActor *actor)
+{
+ ClutterBoxLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE);
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return FALSE;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return FALSE;
+ }
+
+ g_assert (CLUTTER_IS_BOX_CHILD (meta));
+
+ return CLUTTER_BOX_CHILD (meta)->expand;
+}
diff --git a/clutter/clutter-box-layout.h b/clutter/clutter-box-layout.h
new file mode 100644
index 000000000..be2be9130
--- /dev/null
+++ b/clutter/clutter-box-layout.h
@@ -0,0 +1,143 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ *
+ * Based on the NBTK NbtkBoxLayout actor by:
+ * Thomas Wood
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_BOX_LAYOUT_H__
+#define __CLUTTER_BOX_LAYOUT_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_BOX_LAYOUT (clutter_box_layout_get_type ())
+#define CLUTTER_BOX_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayout))
+#define CLUTTER_IS_BOX_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BOX_LAYOUT))
+#define CLUTTER_BOX_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayoutClass))
+#define CLUTTER_IS_BOX_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BOX_LAYOUT))
+#define CLUTTER_BOX_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayoutClass))
+
+typedef struct _ClutterBoxLayout ClutterBoxLayout;
+typedef struct _ClutterBoxLayoutPrivate ClutterBoxLayoutPrivate;
+typedef struct _ClutterBoxLayoutClass ClutterBoxLayoutClass;
+
+/**
+ * ClutterBoxAlignment:
+ * @CLUTTER_BOX_ALIGNMENT_START: Align the child to the top or to
+ * to the left, depending on the used axis
+ * @CLUTTER_BOX_ALIGNMENT_CENTER: Align the child to the center
+ * @CLUTTER_BOX_ALIGNMENT_END: Align the child to the bottom or to
+ * the right, depending on the used axis
+ *
+ * The alignment policies available on each axis of the #ClutterBoxLayout
+ *
+ * Since: 1.2
+ */
+typedef enum {
+ CLUTTER_BOX_ALIGNMENT_START,
+ CLUTTER_BOX_ALIGNMENT_END,
+ CLUTTER_BOX_ALIGNMENT_CENTER
+} ClutterBoxAlignment;
+
+/**
+ * ClutterBoxLayout:
+ *
+ * The #ClutterBoxLayout structure contains only private data
+ * and should be accessed using the provided API
+ *
+ * Since: 1.2
+ */
+struct _ClutterBoxLayout
+{
+ /*< private >*/
+ ClutterLayoutManager parent_instance;
+
+ ClutterBoxLayoutPrivate *priv;
+};
+
+/**
+ * ClutterBoxLayoutClass:
+ *
+ * The #ClutterBoxLayoutClass structure contains only private
+ * data and should be accessed using the provided API
+ *
+ * Since: 1.2
+ */
+struct _ClutterBoxLayoutClass
+{
+ /*< private >*/
+ ClutterLayoutManagerClass parent_class;
+};
+
+GType clutter_box_layout_get_type (void) G_GNUC_CONST;
+
+ClutterLayoutManager *clutter_box_layout_new (void);
+
+void clutter_box_layout_set_spacing (ClutterBoxLayout *layout,
+ guint spacing);
+guint clutter_box_layout_get_spacing (ClutterBoxLayout *layout);
+void clutter_box_layout_set_vertical (ClutterBoxLayout *layout,
+ gboolean vertical);
+gboolean clutter_box_layout_get_vertical (ClutterBoxLayout *layout);
+void clutter_box_layout_set_pack_start (ClutterBoxLayout *layout,
+ gboolean pack_start);
+gboolean clutter_box_layout_get_pack_start (ClutterBoxLayout *layout);
+
+void clutter_box_layout_pack (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ gboolean expand,
+ gboolean x_fill,
+ gboolean y_fill,
+ ClutterBoxAlignment x_align,
+ ClutterBoxAlignment y_align);
+void clutter_box_layout_set_alignment (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ ClutterBoxAlignment x_align,
+ ClutterBoxAlignment y_align);
+void clutter_box_layout_get_alignment (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ ClutterBoxAlignment *x_align,
+ ClutterBoxAlignment *y_align);
+void clutter_box_layout_set_fill (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ gboolean x_fill,
+ gboolean y_fill);
+void clutter_box_layout_get_fill (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ gboolean *x_fill,
+ gboolean *y_fill);
+void clutter_box_layout_set_expand (ClutterBoxLayout *layout,
+ ClutterActor *actor,
+ gboolean expand);
+gboolean clutter_box_layout_get_expand (ClutterBoxLayout *layout,
+ ClutterActor *actor);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_BOX_LAYOUT_H__ */
diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c
new file mode 100644
index 000000000..131327551
--- /dev/null
+++ b/clutter/clutter-box.c
@@ -0,0 +1,828 @@
+/**
+ * SECTION:clutter-box
+ * @short_description: A Generic layout container
+ *
+ * #ClutterBox is a #ClutterActor sub-class implementing the #ClutterContainer
+ * interface. A Box delegates the whole size requisition and size allocation to
+ * a #ClutterLayoutManager instance.
+ *
+ *
+ * Using ClutterBox
+ * The following code shows how to create a #ClutterBox with
+ * a #ClutterLayoutManager sub-class, and how to add children to
+ * it via clutter_box_pack().
+ *
+ * ClutterActor *box;
+ * ClutterLayoutManager *layout;
+ *
+ * /* Create the layout manager first */
+ * layout = clutter_box_layout_new ();
+ * clutter_box_layout_set_homogeneous (CLUTTER_BOX_LAYOUT (layout), TRUE);
+ * clutter_box_layout_set_spacing (CLUTTER_BOX_LAYOUT (layout), 12);
+ *
+ * /* Then create the ClutterBox actor. The Box will take
+ * * ownership of the ClutterLayoutManager instance by sinking
+ * * its floating reference
+ * */
+ * box = clutter_box_new (layout);
+ *
+ * /* Now add children to the Box using the variadic arguments
+ * * function clutter_box_pack() to set layout properties
+ * */
+ * clutter_box_pack (CLUTTER_BOX (box), actor,
+ * "x-align", CLUTTER_BOX_ALIGNMENT_CENTER,
+ * "y-align", CLUTTER_BOX_ALIGNMENT_END,
+ * "expand", TRUE,
+ * NULL);
+ *
+ *
+ *
+ * #ClutterBox's clutter_box_pack() wraps the generic
+ * clutter_container_add_actor() function, but it also allows setting
+ * layout properties while adding the new child to the box.
+ *
+ * #ClutterBox is available since Clutter 1.2
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+#include
+
+#include "clutter-box.h"
+#include "clutter-debug.h"
+#include "clutter-enum-types.h"
+#include "clutter-marshal.h"
+#include "clutter-private.h"
+
+#define CLUTTER_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BOX, ClutterBoxPrivate))
+
+struct _ClutterBoxPrivate
+{
+ ClutterLayoutManager *manager;
+
+ GList *children;
+
+ guint changed_id;
+
+ ClutterColor color;
+ guint color_set : 1;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_LAYOUT_MANAGER,
+ PROP_COLOR,
+ PROP_COLOR_SET
+};
+
+static const ClutterColor default_box_color = { 255, 255, 255, 255 };
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ClutterBox, clutter_box, CLUTTER_TYPE_ACTOR,
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+ clutter_container_iface_init));
+
+static gint
+sort_by_depth (gconstpointer a,
+ gconstpointer b)
+{
+ gfloat depth_a = clutter_actor_get_depth ((ClutterActor *) a);
+ gfloat depth_b = clutter_actor_get_depth ((ClutterActor *) b);
+
+ if (depth_a < depth_b)
+ return -1;
+
+ if (depth_a > depth_b)
+ return 1;
+
+ return 0;
+}
+
+static void
+clutter_box_real_add (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv;
+
+ g_object_ref (actor);
+
+ priv->children = g_list_insert_sorted (priv->children,
+ actor,
+ sort_by_depth);
+
+ clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
+
+ clutter_layout_manager_add_child_meta (priv->manager,
+ container,
+ actor);
+
+ clutter_actor_queue_relayout (actor);
+
+ g_signal_emit_by_name (container, "actor-added", actor);
+
+ g_object_unref (actor);
+}
+
+static void
+clutter_box_real_remove (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv;
+
+ g_object_ref (actor);
+
+ priv->children = g_list_remove (priv->children, actor);
+ clutter_actor_unparent (actor);
+
+ clutter_layout_manager_remove_child_meta (priv->manager,
+ container,
+ actor);
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
+
+ g_signal_emit_by_name (container, "actor-removed", actor);
+
+ g_object_unref (actor);
+}
+
+static void
+clutter_box_real_foreach (ClutterContainer *container,
+ ClutterCallback callback,
+ gpointer user_data)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv;
+ GList *l;
+
+ for (l = priv->children; l != NULL; l = l->next)
+ (* callback) (l->data, user_data);
+}
+
+static void
+clutter_box_real_raise (ClutterContainer *container,
+ ClutterActor *actor,
+ ClutterActor *sibling)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv;
+
+ priv->children = g_list_remove (priv->children, actor);
+
+ if (sibling == NULL)
+ priv->children = g_list_append (priv->children, actor);
+ else
+ {
+ gint index_ = g_list_index (priv->children, sibling) + 1;
+
+ priv->children = g_list_insert (priv->children, actor, index_);
+ }
+
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (container));
+}
+
+static void
+clutter_box_real_lower (ClutterContainer *container,
+ ClutterActor *actor,
+ ClutterActor *sibling)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv;
+
+ priv->children = g_list_remove (priv->children, actor);
+
+ if (sibling == NULL)
+ priv->children = g_list_prepend (priv->children, actor);
+ else
+ {
+ gint index_ = g_list_index (priv->children, sibling);
+
+ priv->children = g_list_insert (priv->children, actor, index_);
+ }
+
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (container));
+}
+
+static void
+clutter_box_real_sort_depth_order (ClutterContainer *container)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (container)->priv;
+
+ priv->children = g_list_sort (priv->children, sort_by_depth);
+
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (container));
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+ iface->add = clutter_box_real_add;
+ iface->remove = clutter_box_real_remove;
+ iface->foreach = clutter_box_real_foreach;
+ iface->raise = clutter_box_real_raise;
+ iface->lower = clutter_box_real_lower;
+ iface->sort_depth_order = clutter_box_real_sort_depth_order;
+}
+
+static void
+clutter_box_real_paint (ClutterActor *actor)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv;
+
+ if (priv->color_set)
+ {
+ ClutterActorBox box = { 0, };
+ gfloat width, height;
+ guint8 tmp_alpha;
+
+ clutter_actor_get_allocation_box (actor, &box);
+ clutter_actor_box_get_size (&box, &width, &height);
+
+ tmp_alpha = clutter_actor_get_paint_opacity (actor)
+ * priv->color.alpha
+ / 255;
+
+ cogl_set_source_color4ub (priv->color.red,
+ priv->color.green,
+ priv->color.blue,
+ tmp_alpha);
+
+ cogl_rectangle (0, 0, width, height);
+ }
+
+ g_list_foreach (priv->children, (GFunc) clutter_actor_paint, NULL);
+}
+
+static void
+clutter_box_real_pick (ClutterActor *actor,
+ const ClutterColor *pick)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv;
+
+ CLUTTER_ACTOR_CLASS (clutter_box_parent_class)->pick (actor, pick);
+
+ g_list_foreach (priv->children, (GFunc) clutter_actor_paint, NULL);
+}
+
+static void
+clutter_box_real_get_preferred_width (ClutterActor *actor,
+ gfloat for_height,
+ gfloat *min_width,
+ gfloat *natural_width)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv;
+
+ /* if we don't have any children don't bother proxying the
+ * call to the layout manager instance
+ */
+ if (priv->children == NULL)
+ {
+ if (min_width)
+ *min_width = 0.0;
+
+ if (natural_width)
+ *natural_width = 0.0;
+
+ return;
+ }
+
+ clutter_layout_manager_get_preferred_width (priv->manager,
+ CLUTTER_CONTAINER (actor),
+ for_height,
+ min_width, natural_width);
+}
+
+static void
+clutter_box_real_get_preferred_height (ClutterActor *actor,
+ gfloat for_width,
+ gfloat *min_height,
+ gfloat *natural_height)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv;
+
+ /* if we don't have any children don't bother proxying the
+ * call to the layout manager instance
+ */
+ if (priv->children == NULL)
+ {
+ if (min_height)
+ *min_height = 0.0;
+
+ if (natural_height)
+ *natural_height = 0.0;
+
+ return;
+ }
+
+ clutter_layout_manager_get_preferred_height (priv->manager,
+ CLUTTER_CONTAINER (actor),
+ for_width,
+ min_height, natural_height);
+}
+
+static void
+clutter_box_real_allocate (ClutterActor *actor,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv;
+ ClutterActorClass *klass;
+
+ klass = CLUTTER_ACTOR_CLASS (clutter_box_parent_class);
+ klass->allocate (actor, allocation, flags);
+
+ if (priv->children == NULL)
+ return;
+
+ clutter_layout_manager_allocate (priv->manager,
+ CLUTTER_CONTAINER (actor),
+ allocation, flags);
+}
+
+static void
+clutter_box_destroy (ClutterActor *actor)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (actor)->priv;
+
+ /* destroy all our children */
+ g_list_foreach (priv->children, (GFunc) clutter_actor_destroy, NULL);
+
+ CLUTTER_ACTOR_CLASS (clutter_box_parent_class)->destroy (actor);
+}
+
+static inline void
+set_layout_manager (ClutterBox *self,
+ ClutterLayoutManager *manager)
+{
+ ClutterBoxPrivate *priv = self->priv;
+
+ if (priv->manager == manager)
+ return;
+
+ if (priv->manager != NULL)
+ {
+ if (priv->changed_id != 0)
+ g_signal_handler_disconnect (priv->manager, priv->changed_id);
+
+ clutter_layout_manager_set_container (priv->manager, NULL);
+ g_object_unref (priv->manager);
+
+ priv->manager = NULL;
+ priv->changed_id = 0;
+ }
+
+ if (manager != NULL)
+ {
+ priv->manager = g_object_ref_sink (manager);
+ clutter_layout_manager_set_container (manager,
+ CLUTTER_CONTAINER (self));
+
+ priv->changed_id =
+ g_signal_connect_swapped (priv->manager, "layout-changed",
+ G_CALLBACK (clutter_actor_queue_relayout),
+ self);
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+
+ g_object_notify (G_OBJECT (self), "layout-manager");
+}
+
+static void
+clutter_box_dispose (GObject *gobject)
+{
+ ClutterBox *self = CLUTTER_BOX (gobject);
+
+ set_layout_manager (self, NULL);
+
+ G_OBJECT_CLASS (clutter_box_parent_class)->dispose (gobject);
+}
+
+static void
+clutter_box_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterBox *self = CLUTTER_BOX (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_LAYOUT_MANAGER:
+ set_layout_manager (self, g_value_get_object (value));
+ break;
+
+ case PROP_COLOR:
+ clutter_box_set_color (self, clutter_value_get_color (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_box_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterBoxPrivate *priv = CLUTTER_BOX (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_LAYOUT_MANAGER:
+ g_value_set_object (value, priv->manager);
+ break;
+
+ case PROP_COLOR:
+ clutter_value_set_color (value, &priv->color);
+ break;
+
+ case PROP_COLOR_SET:
+ g_value_set_boolean (value, priv->color_set);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_box_class_init (ClutterBoxClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (ClutterBoxPrivate));
+
+ actor_class->get_preferred_width = clutter_box_real_get_preferred_width;
+ actor_class->get_preferred_height = clutter_box_real_get_preferred_height;
+ actor_class->allocate = clutter_box_real_allocate;
+ actor_class->paint = clutter_box_real_paint;
+ actor_class->pick = clutter_box_real_pick;
+ actor_class->destroy = clutter_box_destroy;
+
+ gobject_class->set_property = clutter_box_set_property;
+ gobject_class->get_property = clutter_box_get_property;
+ gobject_class->dispose = clutter_box_dispose;
+
+ /**
+ * ClutterBox:layout-manager:
+ *
+ * The #ClutterLayoutManager used by the #ClutterBox
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_object ("layout-manager",
+ "Layout Manager",
+ "The layout manager used by the box",
+ CLUTTER_TYPE_LAYOUT_MANAGER,
+ CLUTTER_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT);
+ g_object_class_install_property (gobject_class,
+ PROP_LAYOUT_MANAGER,
+ pspec);
+
+ /**
+ * ClutterBox:color:
+ *
+ * The color to be used to paint the background of the
+ * #ClutterBox. Setting this property will set the
+ * #ClutterBox:color-set property as a side effect
+ *
+ * Since: 1.2
+ */
+ pspec = clutter_param_spec_color ("color",
+ "Color",
+ "The background color of the box",
+ &default_box_color,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_COLOR, pspec);
+
+ /**
+ * ClutterBox:color-set:
+ *
+ * Whether the #ClutterBox:color property has been set
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_boolean ("color-set",
+ "Color Set",
+ "Whether the background color is set",
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_COLOR_SET, pspec);
+}
+
+static void
+clutter_box_init (ClutterBox *self)
+{
+ self->priv = CLUTTER_BOX_GET_PRIVATE (self);
+
+ self->priv->color = default_box_color;
+}
+
+/**
+ * clutter_box_new:
+ * @manager: a #ClutterLayoutManager
+ *
+ * Creates a new #ClutterBox. The children of the box will be layed
+ * out by the passed @manager
+ *
+ * Return value: the newly created #ClutterBox actor
+ *
+ * Since: 1.0
+ */
+ClutterActor *
+clutter_box_new (ClutterLayoutManager *manager)
+{
+ g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL);
+
+ return g_object_new (CLUTTER_TYPE_BOX,
+ "layout-manager", manager,
+ NULL);
+}
+
+/**
+ * clutter_box_set_layout_manager:
+ * @box: a #ClutterBox
+ * @manager: a #ClutterLayoutManager
+ *
+ * Sets the #ClutterLayoutManager for @box
+ *
+ * A #ClutterLayoutManager is a delegate object that controls the
+ * layout of the children of @box
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_set_layout_manager (ClutterBox *box,
+ ClutterLayoutManager *manager)
+{
+ g_return_if_fail (CLUTTER_IS_BOX (box));
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+
+ set_layout_manager (box, manager);
+}
+
+/**
+ * clutter_box_get_layout_manager:
+ * @box: a #ClutterBox
+ *
+ * Retrieves the #ClutterLayoutManager instance used by @box
+ *
+ * Return value: a #ClutterLayoutManager
+ *
+ * Since: 1.2
+ */
+ClutterLayoutManager *
+clutter_box_get_layout_manager (ClutterBox *box)
+{
+ g_return_val_if_fail (CLUTTER_IS_BOX (box), NULL);
+
+ return box->priv->manager;
+}
+
+/**
+ * clutter_box_packv:
+ * @box: a #ClutterBox
+ * @actor: a #ClutterActor
+ * @n_properties: the number of properties to set
+ * @properties: (array length=n_properties) (element-type utf8): a vector
+ * containing the property names to set
+ * @values: (array length=n_properties): a vector containing the property
+ * values to set
+ *
+ * Vector-based variant of clutter_box_pack(), intended for language
+ * bindings to use
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_packv (ClutterBox *box,
+ ClutterActor *actor,
+ guint n_properties,
+ const gchar * const properties[],
+ const GValue *values)
+{
+ ClutterContainer *container;
+ ClutterBoxPrivate *priv;
+ ClutterLayoutMeta *meta;
+ GObjectClass *klass;
+ gint i;
+
+ g_return_if_fail (CLUTTER_IS_BOX (box));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ container = CLUTTER_CONTAINER (box);
+ clutter_container_add_actor (container, actor);
+
+ priv = box->priv;
+
+ meta = clutter_layout_manager_get_child_meta (priv->manager,
+ container,
+ actor);
+
+ if (meta == NULL)
+ return;
+
+ klass = G_OBJECT_GET_CLASS (meta);
+
+ for (i = 0; i < n_properties; i++)
+ {
+ const gchar *pname = properties[i];
+ GParamSpec *pspec;
+
+ pspec = g_object_class_find_property (klass, pname);
+ if (pspec == NULL)
+ {
+ g_warning ("%s: the layout property '%s' for managers "
+ "of type '%s' (meta type '%s') does not exist",
+ G_STRLOC,
+ pname,
+ G_OBJECT_TYPE_NAME (priv->manager),
+ G_OBJECT_TYPE_NAME (meta));
+ break;
+ }
+
+ if (!(pspec->flags & G_PARAM_WRITABLE))
+ {
+ g_warning ("%s: the layout property '%s' for managers "
+ "of type '%s' (meta type '%s') is not writable",
+ G_STRLOC,
+ pspec->name,
+ G_OBJECT_TYPE_NAME (priv->manager),
+ G_OBJECT_TYPE_NAME (meta));
+ break;
+ }
+
+ clutter_layout_manager_child_set_property (priv->manager,
+ container, actor,
+ pname, &values[i]);
+ }
+}
+
+/**
+ * clutter_box_pack:
+ * @box: a #ClutterBox
+ * @actor: a #ClutterActor
+ * @first_property: the name of the first property to set, or %NULL
+ * @Varargs: a list of property name and value pairs, terminated by %NULL
+ *
+ * Adds @actor to @box and sets layout properties at the same time,
+ * if the #ClutterLayoutManager used by @box has them
+ *
+ * This function is a wrapper around clutter_container_add_actor()
+ * and clutter_layout_manager_child_set()
+ *
+ * Language bindings should use the vector-based clutter_box_addv()
+ * variant instead
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_pack (ClutterBox *box,
+ ClutterActor *actor,
+ const gchar *first_property,
+ ...)
+{
+ ClutterBoxPrivate *priv;
+ ClutterContainer *container;
+ ClutterLayoutMeta *meta;
+ GObjectClass *klass;
+ const gchar *pname;
+ va_list var_args;
+
+ g_return_if_fail (CLUTTER_IS_BOX (box));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ container = CLUTTER_CONTAINER (box);
+ clutter_container_add_actor (container, actor);
+
+ if (first_property == NULL || *first_property == '\0')
+ return;
+
+ priv = box->priv;
+
+ meta = clutter_layout_manager_get_child_meta (priv->manager,
+ container,
+ actor);
+
+ if (meta == NULL)
+ return;
+
+ klass = G_OBJECT_GET_CLASS (meta);
+
+ va_start (var_args, first_property);
+
+ pname = first_property;
+ while (pname)
+ {
+ GValue value = { 0, };
+ GParamSpec *pspec;
+ gchar *error;
+
+ pspec = g_object_class_find_property (klass, pname);
+ if (pspec == NULL)
+ {
+ g_warning ("%s: the layout property '%s' for managers "
+ "of type '%s' (meta type '%s') does not exist",
+ G_STRLOC,
+ pname,
+ G_OBJECT_TYPE_NAME (priv->manager),
+ G_OBJECT_TYPE_NAME (meta));
+ break;
+ }
+
+ if (!(pspec->flags & G_PARAM_WRITABLE))
+ {
+ g_warning ("%s: the layout property '%s' for managers "
+ "of type '%s' (meta type '%s') is not writable",
+ G_STRLOC,
+ pspec->name,
+ G_OBJECT_TYPE_NAME (priv->manager),
+ G_OBJECT_TYPE_NAME (meta));
+ break;
+ }
+
+ g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ G_VALUE_COLLECT (&value, var_args, 0, &error);
+ if (error)
+ {
+ g_warning ("%s: %s", G_STRLOC, error);
+ g_free (error);
+ break;
+ }
+
+ clutter_layout_manager_child_set_property (priv->manager,
+ container, actor,
+ pspec->name, &value);
+
+ g_value_unset (&value);
+
+ pname = va_arg (var_args, gchar*);
+ }
+
+ va_end (var_args);
+}
+
+/**
+ * clutter_box_set_color:
+ * @box: a #ClutterBox
+ * @color: (allow-none): the background color, or %NULL to unset
+ *
+ * Sets (or unsets) the background color for @box
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_set_color (ClutterBox *box,
+ const ClutterColor *color)
+{
+ ClutterBoxPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_BOX (box));
+
+ priv = box->priv;
+
+ if (color)
+ {
+ priv->color = *color;
+ priv->color_set = TRUE;
+ }
+ else
+ priv->color_set = FALSE;
+
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (box));
+
+ g_object_notify (G_OBJECT (box), "color-set");
+ g_object_notify (G_OBJECT (box), "color");
+}
+
+/**
+ * clutter_box_get_color:
+ * @box: a #ClutterBox
+ * @color: (out): return location for a #ClutterColor
+ *
+ * Retrieves the background color of @box
+ *
+ * If the #ClutterBox:color-set property is set to %FALSE the
+ * returned #ClutterColor is undefined
+ *
+ * Since: 1.2
+ */
+void
+clutter_box_get_color (ClutterBox *box,
+ ClutterColor *color)
+{
+ g_return_if_fail (CLUTTER_IS_BOX (box));
+ g_return_if_fail (color != NULL);
+
+ *color = box->priv->color;
+}
diff --git a/clutter/clutter-box.h b/clutter/clutter-box.h
new file mode 100644
index 000000000..a44c274c1
--- /dev/null
+++ b/clutter/clutter-box.h
@@ -0,0 +1,62 @@
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_BOX_H__
+#define __CLUTTER_BOX_H__
+
+#include
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_BOX (clutter_box_get_type ())
+#define CLUTTER_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BOX, ClutterBox))
+#define CLUTTER_IS_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BOX))
+#define CLUTTER_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BOX, ClutterBoxClass))
+#define CLUTTER_IS_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BOX))
+#define CLUTTER_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BOX, ClutterBoxClass))
+
+typedef struct _ClutterBox ClutterBox;
+typedef struct _ClutterBoxPrivate ClutterBoxPrivate;
+typedef struct _ClutterBoxClass ClutterBoxClass;
+
+struct _ClutterBox
+{
+ ClutterActor parent_instance;
+
+ ClutterBoxPrivate *priv;
+};
+
+struct _ClutterBoxClass
+{
+ ClutterActorClass parent_class;
+};
+
+GType clutter_box_get_type (void) G_GNUC_CONST;
+
+ClutterActor * clutter_box_new (ClutterLayoutManager *manager);
+
+void clutter_box_set_layout_manager (ClutterBox *box,
+ ClutterLayoutManager *manager);
+ClutterLayoutManager *clutter_box_get_layout_manager (ClutterBox *box);
+
+void clutter_box_set_color (ClutterBox *box,
+ const ClutterColor *color);
+void clutter_box_get_color (ClutterBox *box,
+ ClutterColor *color);
+
+void clutter_box_pack (ClutterBox *box,
+ ClutterActor *actor,
+ const gchar *first_property,
+ ...) G_GNUC_NULL_TERMINATED;
+void clutter_box_packv (ClutterBox *box,
+ ClutterActor *actor,
+ guint n_properties,
+ const gchar * const properties[],
+ const GValue *values);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_BOX_H__ */
diff --git a/clutter/clutter-fixed-layout.c b/clutter/clutter-fixed-layout.c
new file mode 100644
index 000000000..5ec56fa3d
--- /dev/null
+++ b/clutter/clutter-fixed-layout.c
@@ -0,0 +1,186 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ *
+ * Based on the fixed layout code inside clutter-group.c
+ */
+
+/**
+ * SECTION:clutter-fixed-layout
+ * @short_description: A fixed layout manager
+ *
+ * #ClutterFixedLayout is a layout manager implementing the same
+ * layout policies as #ClutterGroup.
+ *
+ * #ClutterFixedLayout is available since Clutter 1.2
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "clutter-debug.h"
+#include "clutter-fixed-layout.h"
+#include "clutter-private.h"
+
+G_DEFINE_TYPE (ClutterFixedLayout,
+ clutter_fixed_layout,
+ CLUTTER_TYPE_LAYOUT_MANAGER);
+
+static void
+clutter_fixed_layout_get_preferred_width (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *nat_width_p)
+{
+ GList *children, *l;
+ gdouble min_right;
+ gdouble natural_right;
+
+ min_right = 0;
+ natural_right = 0;
+
+ children = clutter_container_get_children (container);
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+ gfloat child_x, child_min, child_natural;
+
+ child_x = clutter_actor_get_x (child);
+
+ clutter_actor_get_preferred_size (child,
+ &child_min, NULL,
+ &child_natural, NULL);
+
+ if (child_x + child_min > min_right)
+ min_right = child_x + child_min;
+
+ if (child_x + child_natural > natural_right)
+ natural_right = child_x + child_natural;
+ }
+
+ g_list_free (children);
+
+ if (min_width_p)
+ *min_width_p = min_right;
+
+ if (nat_width_p)
+ *nat_width_p = natural_right;
+}
+
+static void
+clutter_fixed_layout_get_preferred_height (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *nat_height_p)
+{
+ GList *children, *l;
+ gdouble min_bottom;
+ gdouble natural_bottom;
+
+ min_bottom = 0;
+ natural_bottom = 0;
+
+ children = clutter_container_get_children (container);
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+ gfloat child_y, child_min, child_natural;
+
+ child_y = clutter_actor_get_y (child);
+
+ clutter_actor_get_preferred_size (child,
+ NULL, &child_min,
+ NULL, &child_natural);
+
+ if (child_y + child_min > min_bottom)
+ min_bottom = child_y + child_min;
+
+ if (child_y + child_natural > natural_bottom)
+ natural_bottom = child_y + child_natural;
+ }
+
+ g_list_free (children);
+
+ if (min_height_p)
+ *min_height_p = min_bottom;
+
+ if (nat_height_p)
+ *nat_height_p = natural_bottom;
+}
+
+static void
+clutter_fixed_layout_allocate (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags)
+{
+ GList *children, *l;
+
+ children = clutter_container_get_children (container);
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+
+ clutter_actor_allocate_preferred_size (child, flags);
+ }
+
+ g_list_free (children);
+}
+
+static void
+clutter_fixed_layout_class_init (ClutterFixedLayoutClass *klass)
+{
+ ClutterLayoutManagerClass *manager_class =
+ CLUTTER_LAYOUT_MANAGER_CLASS (klass);
+
+ manager_class->get_preferred_width =
+ clutter_fixed_layout_get_preferred_width;
+ manager_class->get_preferred_height =
+ clutter_fixed_layout_get_preferred_height;
+ manager_class->allocate = clutter_fixed_layout_allocate;
+}
+
+static void
+clutter_fixed_layout_init (ClutterFixedLayout *self)
+{
+}
+
+/**
+ * clutter_fixed_layout_new:
+ *
+ * Creates a new #ClutterFixedLayout
+ *
+ * Return value: the newly created #ClutterFixedLayout
+ *
+ * Since: 1.2
+ */
+ClutterLayoutManager *
+clutter_fixed_layout_new (void)
+{
+ return g_object_new (CLUTTER_TYPE_FIXED_LAYOUT, NULL);
+}
diff --git a/clutter/clutter-fixed-layout.h b/clutter/clutter-fixed-layout.h
new file mode 100644
index 000000000..32273bc00
--- /dev/null
+++ b/clutter/clutter-fixed-layout.h
@@ -0,0 +1,80 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_FIXED_LAYOUT_H__
+#define __CLUTTER_FIXED_LAYOUT_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_FIXED_LAYOUT (clutter_fixed_layout_get_type ())
+#define CLUTTER_FIXED_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_FIXED_LAYOUT, ClutterFixedLayout))
+#define CLUTTER_IS_FIXED_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_FIXED_LAYOUT))
+#define CLUTTER_FIXED_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_FIXED_LAYOUT, ClutterFixedLayoutClass))
+#define CLUTTER_IS_FIXED_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_FIXED_LAYOUT))
+#define CLUTTER_FIXED_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_FIXED_LAYOUT, ClutterFixedLayoutClass))
+
+typedef struct _ClutterFixedLayout ClutterFixedLayout;
+typedef struct _ClutterFixedLayoutClass ClutterFixedLayoutClass;
+
+/**
+ * ClutterFixedLayout:
+ *
+ * The #ClutterFixedLayout structure contains only private data and
+ * it should be accessed using the provided API
+ *
+ * Since: 1.2
+ */
+struct _ClutterFixedLayout
+{
+ /*< private >*/
+ ClutterLayoutManager parent_instance;
+};
+
+/**
+ * ClutterFixedLayoutClass:
+ *
+ * The #ClutterFixedLayoutClass structure contains only private data
+ * and it should be accessed using the provided API
+ *
+ * Since: 1.2
+ */
+struct _ClutterFixedLayoutClass
+{
+ /*< private >*/
+ ClutterLayoutManagerClass parent_class;
+};
+
+GType clutter_fixed_layout_get_type (void) G_GNUC_CONST;
+
+ClutterLayoutManager *clutter_fixed_layout_new (void);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_FIXED_LAYOUT_H__ */
diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c
new file mode 100644
index 000000000..bc81a679d
--- /dev/null
+++ b/clutter/clutter-flow-layout.c
@@ -0,0 +1,1321 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+/**
+ * SECTION:clutter-flow-layout
+ * @short_description: A reflowing layout manager
+ *
+ * #ClutterFlowLayout is a layout manager which implements the following
+ * policy:
+ *
+ *
+ * the preferred natural size depends on the value
+ * of the #ClutterFlowLayout:orientation property; the layout will try
+ * to maintain all its children on a single row or
+ * column;
+ * if either the width or the height allocated are
+ * smaller than the preferred ones, the layout will wrap; in this case,
+ * the preferred height or width, respectively, will take into account
+ * the amount of columns and rows;
+ * each line (either column or row) in reflowing will
+ * have the size of the biggest cell on that line; if the
+ * #ClutterFlowLayout:homogeneous property is set to %FALSE the actor
+ * will be allocated within that area, and if set to %TRUE instead the
+ * actor will be given exactly that area;
+ * the size of the columns or rows can be controlled
+ * for both minimum and maximum; the spacing can also be controlled
+ * in both columns and rows.
+ *
+ *
+ * #ClutterFlowLayout is available since Clutter 1.2
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+
+#include "clutter-actor.h"
+#include "clutter-animatable.h"
+#include "clutter-child-meta.h"
+#include "clutter-debug.h"
+#include "clutter-enum-types.h"
+#include "clutter-flow-layout.h"
+#include "clutter-layout-meta.h"
+#include "clutter-private.h"
+
+#define CLUTTER_FLOW_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayoutPrivate))
+
+struct _ClutterFlowLayoutPrivate
+{
+ ClutterContainer *container;
+
+ ClutterFlowOrientation orientation;
+
+ gfloat col_spacing;
+ gfloat row_spacing;
+
+ gfloat min_col_width;
+ gfloat max_col_width;
+ gfloat col_width;
+
+ gfloat min_row_height;
+ gfloat max_row_height;
+ gfloat row_height;
+
+ /* per-line size */
+ GArray *line_min;
+ GArray *line_natural;
+
+ guint line_count;
+
+ guint is_homogeneous : 1;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_ORIENTATION,
+
+ PROP_HOMOGENEOUS,
+
+ PROP_COLUMN_SPACING,
+ PROP_ROW_SPACING,
+
+ PROP_MIN_COLUMN_WIDTH,
+ PROP_MAX_COLUMN_WIDTH,
+ PROP_MIN_ROW_HEGHT,
+ PROP_MAX_ROW_HEIGHT
+};
+
+G_DEFINE_TYPE (ClutterFlowLayout,
+ clutter_flow_layout,
+ CLUTTER_TYPE_LAYOUT_MANAGER);
+
+static gint
+get_columns (ClutterFlowLayout *self,
+ gfloat for_width)
+{
+ ClutterFlowLayoutPrivate *priv = self->priv;
+ gint n_columns;
+
+ if (for_width < 0)
+ return 1;
+
+ if (priv->col_width == 0)
+ return 1;
+
+ n_columns = (gint) (for_width + priv->col_spacing)
+ / (priv->col_width + priv->col_spacing);
+
+ if (n_columns == 0)
+ return 1;
+
+ return n_columns;
+}
+
+static gint
+get_rows (ClutterFlowLayout *self,
+ gfloat for_height)
+{
+ ClutterFlowLayoutPrivate *priv = self->priv;
+ gint n_rows;
+
+ if (for_height < 0)
+ return 1;
+
+ if (priv->row_height == 0)
+ return 1;
+
+ n_rows = (gint) (for_height + priv->row_spacing)
+ / (priv->row_height + priv->row_spacing);
+
+ if (n_rows == 0)
+ return 1;
+
+ return n_rows;
+}
+
+static gint
+compute_lines (ClutterFlowLayout *self,
+ gfloat avail_width,
+ gfloat avail_height)
+{
+ ClutterFlowLayoutPrivate *priv = self->priv;
+
+ if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
+ return get_columns (self, avail_width);
+ else
+ return get_rows (self, avail_height);
+}
+
+static void
+clutter_flow_layout_get_preferred_width (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *nat_width_p)
+{
+ ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv;
+ GList *l, *children = clutter_container_get_children (container);
+ gint n_rows, line_item_count, line_count;
+ gfloat total_min_width, total_natural_width;
+ gfloat line_min_width, line_natural_width;
+ gfloat max_min_width, max_natural_width;
+ gfloat item_y;
+
+ n_rows = get_rows (CLUTTER_FLOW_LAYOUT (manager), for_height);
+
+ total_min_width = 0;
+ total_natural_width = 0;
+
+ line_min_width = 0;
+ line_natural_width = 0;
+
+ line_item_count = 0;
+ line_count = 0;
+
+ item_y = 0;
+
+ /* clear the line width arrays */
+ if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0)
+ {
+ if (priv->line_min != NULL)
+ g_array_free (priv->line_min, TRUE);
+
+ if (priv->line_natural != NULL)
+ g_array_free (priv->line_natural, TRUE);
+
+ priv->line_min = g_array_sized_new (FALSE, FALSE,
+ sizeof (gfloat),
+ 16);
+ priv->line_natural = g_array_sized_new (FALSE, FALSE,
+ sizeof (gfloat),
+ 16);
+ }
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+ gfloat child_min, child_natural;
+ gfloat new_y, item_height;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0)
+ {
+ if (line_item_count == n_rows)
+ {
+ total_min_width += line_min_width;
+ total_natural_width += line_natural_width;
+
+ g_array_append_val (priv->line_min,
+ line_min_width);
+ g_array_append_val (priv->line_natural,
+ line_natural_width);
+
+ line_min_width = line_natural_width = 0;
+
+ line_item_count = 0;
+ line_count += 1;
+ item_y = 0;
+ }
+
+ new_y = ((line_item_count + 1) * (for_height + priv->row_spacing))
+ / n_rows;
+ item_height = new_y - item_y - priv->row_spacing;
+
+ clutter_actor_get_preferred_width (child, item_height,
+ &child_min,
+ &child_natural);
+
+ line_min_width = MAX (line_min_width, child_min);
+ line_natural_width = MAX (line_natural_width, child_natural);
+
+ item_y = new_y;
+ line_item_count += 1;
+
+ max_min_width = MAX (max_min_width, line_min_width);
+ max_natural_width = MAX (max_natural_width, line_natural_width);
+ }
+ else
+ {
+ clutter_actor_get_preferred_width (child, for_height,
+ &child_min,
+ &child_natural);
+
+ max_min_width = MAX (max_min_width, child_min);
+ max_natural_width = MAX (max_natural_width, child_natural);
+
+ line_count += 1;
+ }
+ }
+
+ g_list_free (children);
+
+ priv->col_width = max_natural_width;
+
+ if (priv->max_col_width > 0 && priv->col_width > priv->max_col_width)
+ priv->col_width = MAX (priv->max_col_width, max_min_width);
+
+ if (priv->col_width < priv->min_col_width)
+ priv->col_width = priv->min_col_width;
+
+ if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0)
+ {
+ /* if we have a non-full row we need to add it */
+ if (line_item_count > 0)
+ {
+ total_min_width += line_min_width;
+ total_natural_width += line_natural_width;
+
+ g_array_append_val (priv->line_min,
+ line_min_width);
+ g_array_append_val (priv->line_natural,
+ line_natural_width);
+ }
+
+ priv->line_count = line_count;
+ if (priv->line_count > 0)
+ {
+ gfloat total_spacing;
+
+ total_spacing = priv->col_spacing * (priv->line_count - 1);
+
+ total_min_width += total_spacing;
+ total_natural_width += total_spacing;
+ }
+ }
+
+ if (min_width_p)
+ *min_width_p = total_min_width;
+
+ if (nat_width_p)
+ *nat_width_p = total_natural_width;
+}
+
+static void
+clutter_flow_layout_get_preferred_height (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *nat_height_p)
+{
+ ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv;
+ GList *l, *children = clutter_container_get_children (container);
+ gint n_columns, line_item_count, line_count;
+ gfloat total_min_height, total_natural_height;
+ gfloat line_min_height, line_natural_height;
+ gfloat max_min_height, max_natural_height;
+ gfloat item_x;
+
+ n_columns = get_columns (CLUTTER_FLOW_LAYOUT (manager), for_width);
+
+ total_min_height = 0;
+ total_natural_height = 0;
+
+ line_min_height = 0;
+ line_natural_height = 0;
+
+ line_item_count = 0;
+ line_count = 0;
+
+ item_x = 0;
+
+ /* clear the line height arrays */
+ if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0)
+ {
+ if (priv->line_min != NULL)
+ g_array_free (priv->line_min, TRUE);
+
+ if (priv->line_natural != NULL)
+ g_array_free (priv->line_natural, TRUE);
+
+ priv->line_min = g_array_sized_new (FALSE, FALSE,
+ sizeof (gfloat),
+ 16);
+ priv->line_natural = g_array_sized_new (FALSE, FALSE,
+ sizeof (gfloat),
+ 16);
+ }
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+ gfloat child_min, child_natural;
+ gfloat new_x, item_width;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0)
+ {
+ if (line_item_count == n_columns)
+ {
+ total_min_height += line_min_height;
+ total_natural_height += line_natural_height;
+
+ g_array_append_val (priv->line_min,
+ line_min_height);
+ g_array_append_val (priv->line_natural,
+ line_natural_height);
+
+ line_min_height = line_natural_height = 0;
+
+ line_item_count = 0;
+ line_count += 1;
+ item_x = 0;
+ }
+
+ new_x = ((line_item_count + 1) * (for_width + priv->col_spacing))
+ / n_columns;
+ item_width = new_x - item_x - priv->col_spacing;
+
+ clutter_actor_get_preferred_height (child, item_width,
+ &child_min,
+ &child_natural);
+
+ line_min_height = MAX (line_min_height, child_min);
+ line_natural_height = MAX (line_natural_height, child_natural);
+
+ item_x = new_x;
+ line_item_count += 1;
+
+ max_min_height = MAX (max_min_height, line_min_height);
+ max_natural_height = MAX (max_natural_height, line_natural_height);
+ }
+ else
+ {
+ clutter_actor_get_preferred_height (child, for_width,
+ &child_min,
+ &child_natural);
+
+ max_min_height = MAX (max_min_height, child_min);
+ max_natural_height = MAX (max_natural_height, child_natural);
+
+ line_count += 1;
+ }
+ }
+
+ g_list_free (children);
+
+ priv->row_height = max_natural_height;
+
+ if (priv->max_row_height > 0 && priv->row_height > priv->max_row_height)
+ priv->row_height = MAX (priv->max_row_height, max_min_height);
+
+ if (priv->row_height < priv->min_row_height)
+ priv->row_height = priv->min_row_height;
+
+ if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0)
+ {
+ /* if we have a non-full row we need to add it */
+ if (line_item_count > 0)
+ {
+ total_min_height += line_min_height;
+ total_natural_height += line_natural_height;
+
+ g_array_append_val (priv->line_min,
+ line_min_height);
+ g_array_append_val (priv->line_natural,
+ line_natural_height);
+ }
+
+ priv->line_count = line_count;
+ if (priv->line_count > 0)
+ {
+ gfloat total_spacing;
+
+ total_spacing = priv->row_spacing * (priv->line_count - 1);
+
+ total_min_height += total_spacing;
+ total_natural_height += total_spacing;
+ }
+ }
+
+ if (min_height_p)
+ *min_height_p = total_min_height;
+
+ if (nat_height_p)
+ *nat_height_p = total_natural_height;
+}
+
+static void
+clutter_flow_layout_allocate (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags)
+{
+ ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv;
+ GList *l, *children = clutter_container_get_children (container);
+ gfloat avail_width, avail_height;
+ gfloat item_x, item_y;
+ gint line_item_count;
+ gint items_per_line;
+ gint line_index;
+
+ if (children == NULL)
+ return;
+
+ clutter_actor_box_get_size (allocation, &avail_width, &avail_height);
+
+ items_per_line = compute_lines (CLUTTER_FLOW_LAYOUT (manager),
+ avail_width, avail_height);
+
+ item_x = item_y = 0;
+
+ line_item_count = 0;
+ line_index = 0;
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ ClutterActor *child = l->data;
+ ClutterActorBox child_alloc;
+ gfloat item_width, item_height;
+ gfloat new_x, new_y;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
+ {
+ if (line_item_count == items_per_line && line_item_count > 0)
+ {
+ item_y += g_array_index (priv->line_natural,
+ gfloat,
+ line_index);
+
+ line_item_count = 0;
+ line_index += 1;
+
+ item_x = 0;
+ }
+
+ new_x = ((line_item_count + 1) * (avail_width + priv->col_spacing))
+ / items_per_line;
+ item_width = new_x - item_x - priv->col_spacing;
+ item_height = g_array_index (priv->line_natural,
+ gfloat,
+ line_index);
+
+ if (!priv->is_homogeneous)
+ {
+ gfloat child_min, child_natural;
+
+ clutter_actor_get_preferred_width (child, item_height,
+ &child_min,
+ &child_natural);
+ item_width = MIN (item_width, child_min);
+
+ clutter_actor_get_preferred_height (child, item_width,
+ &child_min,
+ &child_natural);
+ item_height = MIN (item_height, child_natural);
+ }
+ }
+ else
+ {
+ if (line_item_count == items_per_line && line_item_count > 0)
+ {
+ item_x += g_array_index (priv->line_natural,
+ gfloat,
+ line_index);
+
+ line_item_count = 0;
+ line_index += 1;
+
+ item_y = 0;
+ }
+
+ new_y = ((line_item_count + 1) * (avail_height + priv->row_spacing))
+ / items_per_line;
+ item_height = new_y - item_y - priv->row_spacing;
+ item_width = g_array_index (priv->line_natural,
+ gfloat,
+ line_index);
+
+ if (!priv->is_homogeneous)
+ {
+ gfloat child_min, child_natural;
+
+ clutter_actor_get_preferred_width (child, item_height,
+ &child_min,
+ &child_natural);
+ item_width = MIN (item_width, child_min);
+
+ clutter_actor_get_preferred_height (child, item_width,
+ &child_min,
+ &child_natural);
+ item_height = MIN (item_height, child_natural);
+ }
+ }
+
+ CLUTTER_NOTE (LAYOUT,
+ "flow[line:%d, item:%d/%d] ="
+ "{ %.2f, %.2f, %.2f, %.2f }",
+ line_index, line_item_count + 1, items_per_line,
+ item_x, item_y, item_width, item_height);
+
+ child_alloc.x1 = ceil (item_x);
+ child_alloc.y1 = ceil (item_y);
+ child_alloc.x2 = ceil (child_alloc.x1 + item_width);
+ child_alloc.y2 = ceil (child_alloc.y1 + item_height);
+ clutter_actor_allocate (child, &child_alloc, flags);
+
+ if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
+ item_x = new_x;
+ else
+ item_y = new_y;
+
+ line_item_count += 1;
+ }
+
+ g_list_free (children);
+}
+
+static void
+clutter_flow_layout_set_container (ClutterLayoutManager *manager,
+ ClutterContainer *container)
+{
+ ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv;
+
+ priv->container = container;
+
+ if (priv->container != NULL)
+ {
+ ClutterRequestMode request_mode;
+
+ /* we need to change the :request-mode of the container
+ * to match the orientation
+ */
+ request_mode = (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
+ ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH
+ : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT;
+ clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container),
+ request_mode);
+ }
+}
+
+static void
+clutter_flow_layout_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterFlowLayout *self = CLUTTER_FLOW_LAYOUT (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ clutter_flow_layout_set_orientation (self, g_value_get_enum (value));
+ break;
+
+ case PROP_HOMOGENEOUS:
+ clutter_flow_layout_set_homogeneous (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_COLUMN_SPACING:
+ clutter_flow_layout_set_column_spacing (self, g_value_get_float (value));
+ break;
+
+ case PROP_ROW_SPACING:
+ clutter_flow_layout_set_row_spacing (self, g_value_get_float (value));
+ break;
+
+ case PROP_MIN_COLUMN_WIDTH:
+ clutter_flow_layout_set_column_width (self,
+ g_value_get_float (value),
+ self->priv->max_col_width);
+ break;
+
+ case PROP_MAX_COLUMN_WIDTH:
+ clutter_flow_layout_set_column_width (self,
+ self->priv->min_col_width,
+ g_value_get_float (value));
+ break;
+
+ case PROP_MIN_ROW_HEGHT:
+ clutter_flow_layout_set_row_height (self,
+ g_value_get_float (value),
+ self->priv->max_row_height);
+ break;
+
+ case PROP_MAX_ROW_HEIGHT:
+ clutter_flow_layout_set_row_height (self,
+ self->priv->min_row_height,
+ g_value_get_float (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_flow_layout_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, priv->orientation);
+ break;
+
+ case PROP_HOMOGENEOUS:
+ g_value_set_boolean (value, priv->is_homogeneous);
+ break;
+
+ case PROP_COLUMN_SPACING:
+ g_value_set_float (value, priv->col_spacing);
+ break;
+
+ case PROP_ROW_SPACING:
+ g_value_set_float (value, priv->row_spacing);
+ break;
+
+ case PROP_MIN_COLUMN_WIDTH:
+ g_value_set_float (value, priv->min_col_width);
+ break;
+
+ case PROP_MAX_COLUMN_WIDTH:
+ g_value_set_float (value, priv->max_col_width);
+ break;
+
+ case PROP_MIN_ROW_HEGHT:
+ g_value_set_float (value, priv->min_row_height);
+ break;
+
+ case PROP_MAX_ROW_HEIGHT:
+ g_value_set_float (value, priv->max_row_height);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_flow_layout_finalize (GObject *gobject)
+{
+ ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (gobject)->priv;
+
+ if (priv->line_min != NULL)
+ g_array_free (priv->line_min, TRUE);
+
+ if (priv->line_natural != NULL)
+ g_array_free (priv->line_natural, TRUE);
+
+ G_OBJECT_CLASS (clutter_flow_layout_parent_class)->finalize (gobject);
+}
+
+static void
+clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass)
+{
+ GObjectClass *gobject_class;
+ ClutterLayoutManagerClass *layout_class;
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (ClutterFlowLayoutPrivate));
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
+
+ gobject_class->set_property = clutter_flow_layout_set_property;
+ gobject_class->get_property = clutter_flow_layout_get_property;
+ gobject_class->finalize = clutter_flow_layout_finalize;
+
+ layout_class->get_preferred_width =
+ clutter_flow_layout_get_preferred_width;
+ layout_class->get_preferred_height =
+ clutter_flow_layout_get_preferred_height;
+ layout_class->allocate = clutter_flow_layout_allocate;
+ layout_class->set_container = clutter_flow_layout_set_container;
+
+ /**
+ * ClutterFlowLayout:orientation:
+ *
+ * The orientation of the #ClutterFlowLayout. The children
+ * of the layout will be layed out following the orientation.
+ *
+ * This property also controls the overflowing directions
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_enum ("orientation",
+ "Orientation",
+ "The orientation of the layout",
+ CLUTTER_TYPE_FLOW_ORIENTATION,
+ CLUTTER_FLOW_HORIZONTAL,
+ CLUTTER_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT);
+ g_object_class_install_property (gobject_class, PROP_ORIENTATION, pspec);
+
+ /**
+ * ClutterFlowLayout:homogeneous:
+ *
+ * Whether each child inside the #ClutterFlowLayout should receive
+ * the same allocation
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_boolean ("homogeneous",
+ "Homogeneous",
+ "Whether each item should receive the "
+ "same allocation",
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_HOMOGENEOUS, pspec);
+
+ /**
+ * ClutterFlowLayout:column-spacing:
+ *
+ * The spacing between columns, in pixels; the value of this
+ * property is honoured by horizontal non-overflowing layouts
+ * and by vertical overflowing layouts
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_float ("column-spacing",
+ "Column Spacing",
+ "The spacing between columns",
+ 0.0, G_MAXFLOAT,
+ 0.0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_COLUMN_SPACING,
+ pspec);
+
+ /**
+ * ClutterFlowLayout:row-spacing:
+ *
+ * The spacing between rows, in pixels; the value of this
+ * property is honoured by vertical non-overflowing layouts and
+ * by horizontal overflowing layouts
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_float ("row-spacing",
+ "Row Spacing",
+ "The spacing between rows",
+ 0.0, G_MAXFLOAT,
+ 0.0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_ROW_SPACING,
+ pspec);
+
+ /**
+ * ClutterFlowLayout:min-column-width:
+ *
+ * Minimum width for each column in the layout, in pixels
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_float ("min-column-width",
+ "Minimum Column Width",
+ "Minimum width for each column",
+ 0.0, G_MAXFLOAT,
+ 0.0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_MIN_COLUMN_WIDTH,
+ pspec);
+
+ /**
+ * ClutterFlowLayout:max-column-width:
+ *
+ * Maximum width for each column in the layout, in pixels. If
+ * set to -1 the width will be the maximum child width
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_float ("max-column-width",
+ "Maximum Column Width",
+ "Maximum width for each column",
+ -1.0, G_MAXFLOAT,
+ -1.0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_MAX_COLUMN_WIDTH,
+ pspec);
+
+ /**
+ * ClutterFlowLayout:min-row-height:
+ *
+ * Minimum height for each row in the layout, in pixels
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_float ("min-row-height",
+ "Minimum Row Height",
+ "Minimum height for each row",
+ 0.0, G_MAXFLOAT,
+ 0.0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_MIN_ROW_HEGHT,
+ pspec);
+
+ /**
+ * ClutterFlowLayout:max-row-height:
+ *
+ * Maximum height for each row in the layout, in pixels. If
+ * set to -1 the width will be the maximum child height
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_float ("max-row-height",
+ "Maximum Row Height",
+ "Maximum height for each row",
+ -1.0, G_MAXFLOAT,
+ -1.0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_MAX_ROW_HEIGHT,
+ pspec);
+}
+
+static void
+clutter_flow_layout_init (ClutterFlowLayout *self)
+{
+ ClutterFlowLayoutPrivate *priv;
+
+ self->priv = priv = CLUTTER_FLOW_LAYOUT_GET_PRIVATE (self);
+
+ priv->orientation = CLUTTER_FLOW_HORIZONTAL;
+
+ priv->col_spacing = 0;
+ priv->row_spacing = 0;
+
+ priv->min_col_width = priv->min_row_height = 0;
+ priv->max_col_width = priv->max_row_height = -1;
+
+ priv->line_min = NULL;
+ priv->line_natural = NULL;
+}
+
+/**
+ * clutter_flow_layout_new:
+ * @orientation: the orientation of the flow layout
+ *
+ * Creates a new #ClutterFlowLayout with the given @orientation
+ *
+ * Return value: the newly created #ClutterFlowLayout
+ *
+ * Since: 1.2
+ */
+ClutterLayoutManager *
+clutter_flow_layout_new (ClutterFlowOrientation orientation)
+{
+ return g_object_new (CLUTTER_TYPE_FLOW_LAYOUT,
+ "orientation", orientation,
+ NULL);
+}
+
+/**
+ * clutter_flow_layout_set_orientation:
+ * @layout: a #ClutterFlowLayout
+ * @orientation: the orientation of the layout
+ *
+ * Sets the orientation of the flow layout
+ *
+ * The orientation controls the direction used to allocate
+ * the children: either horizontally or vertically. The
+ * orientation also controls the direction of the overflowing
+ *
+ * Since: 1.2
+ */
+void
+clutter_flow_layout_set_orientation (ClutterFlowLayout *layout,
+ ClutterFlowOrientation orientation)
+{
+ ClutterFlowLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->orientation != orientation)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->orientation = orientation;
+
+ if (priv->container != NULL)
+ {
+ ClutterRequestMode request_mode;
+
+ /* we need to change the :request-mode of the container
+ * to match the orientation
+ */
+ request_mode = (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
+ ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH
+ : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT;
+ clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container),
+ request_mode);
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (layout), "orientation");
+ }
+}
+
+/**
+ * clutter_flow_layout_get_orientation:
+ * @layout: a #ClutterFlowLayout
+ *
+ * Retrieves the orientation of the @layout
+ *
+ * Return value: the orientation of the #ClutterFlowLayout
+ *
+ * Since: 1.2
+ */
+ClutterFlowOrientation
+clutter_flow_layout_get_orientation (ClutterFlowLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout),
+ CLUTTER_FLOW_HORIZONTAL);
+
+ return layout->priv->orientation;
+}
+
+/**
+ * clutter_flow_layout_set_homogeneous:
+ * @layout: a #ClutterFlowLayout
+ * @homogeneous: whether the layout should be homogeneous or not
+ *
+ * Sets whether the @layout should allocate the same space for
+ * each child
+ *
+ * Since: 1.2
+ */
+void
+clutter_flow_layout_set_homogeneous (ClutterFlowLayout *layout,
+ gboolean homogeneous)
+{
+ ClutterFlowLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->is_homogeneous != homogeneous)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->is_homogeneous = homogeneous;
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (layout), "homogeneous");
+ }
+}
+
+/**
+ * clutter_flow_layout_get_homogeneous:
+ * @layout: a #ClutterFlowLayout
+ *
+ * Retrieves whether the @layout is homogeneous
+ *
+ * Return value: %TRUE if the #ClutterFlowLayout is homogeneous
+ *
+ * Since: 1.2
+ */
+gboolean
+clutter_flow_layout_get_homogeneous (ClutterFlowLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), FALSE);
+
+ return layout->priv->is_homogeneous;
+}
+
+/**
+ * clutter_flow_layout_set_column_spacing:
+ * @layout: a #ClutterFlowLayout
+ * @spacing: the space between columns
+ *
+ * Sets the space between columns, in pixels
+ *
+ * Since: 1.2
+ */
+void
+clutter_flow_layout_set_column_spacing (ClutterFlowLayout *layout,
+ gfloat spacing)
+{
+ ClutterFlowLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->col_spacing != spacing)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->col_spacing = spacing;
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (layout), "column-spacing");
+ }
+}
+
+/**
+ * clutter_flow_layout_get_column_spacing:
+ * @layout: a #ClutterFlowLayout
+ *
+ * Retrieves the spacing between columns
+ *
+ * Return value: the spacing between columns of the #ClutterFlowLayout,
+ * in pixels
+ *
+ * Since: 1.2
+ */
+gfloat
+clutter_flow_layout_get_column_spacing (ClutterFlowLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), 0.0);
+
+ return layout->priv->col_spacing;
+}
+
+/**
+ * clutter_flow_layout_set_row_spacing:
+ * @layout: a #ClutterFlowLayout
+ * @spacing: the space between rows
+ *
+ * Sets the spacing between rows, in pixels
+ *
+ * Since: 1.2
+ */
+void
+clutter_flow_layout_set_row_spacing (ClutterFlowLayout *layout,
+ gfloat spacing)
+{
+ ClutterFlowLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->row_spacing != spacing)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->row_spacing = spacing;
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (layout), "row-spacing");
+ }
+}
+
+/**
+ * clutter_flow_layout_get_row_spacing:
+ * @layout: a #ClutterFlowLayout
+ *
+ * Retrieves the spacing between rows
+ *
+ * Return value: the spacing between rows of the #ClutterFlowLayout,
+ * in pixels
+ *
+ * Since: 1.2
+ */
+gfloat
+clutter_flow_layout_get_row_spacing (ClutterFlowLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), 0.0);
+
+ return layout->priv->row_spacing;
+}
+
+/**
+ * clutter_flow_layout_set_column_width:
+ * @layout: a #ClutterFlowLayout
+ * @min_width: minimum width of a column
+ * @max_width: maximum width of a column
+ *
+ * Sets the minimum and maximum widths that a column can have
+ *
+ * Since: 1.2
+ */
+void
+clutter_flow_layout_set_column_width (ClutterFlowLayout *layout,
+ gfloat min_width,
+ gfloat max_width)
+{
+ ClutterFlowLayoutPrivate *priv;
+ gboolean notify_min = FALSE, notify_max = FALSE;
+
+ g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->min_col_width != min_width)
+ {
+ priv->min_col_width = min_width;
+
+ notify_min = TRUE;
+ }
+
+ if (priv->max_col_width != max_width)
+ {
+ priv->max_col_width = max_width;
+
+ notify_max = TRUE;
+ }
+
+ g_object_freeze_notify (G_OBJECT (layout));
+
+ if (notify_min || notify_max)
+ {
+ ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout);
+
+ clutter_layout_manager_layout_changed (manager);
+ }
+
+ if (notify_min)
+ g_object_notify (G_OBJECT (layout), "min-column-width");
+
+ if (notify_max)
+ g_object_notify (G_OBJECT (layout), "max-column-width");
+
+ g_object_thaw_notify (G_OBJECT (layout));
+}
+
+/**
+ * clutter_flow_layout_get_column_width:
+ * @layout: a #ClutterFlowLayout
+ * @min_width: (out): return location for the minimum column width, or %NULL
+ * @max_width: (out): return location for the maximum column width, or %NULL
+ *
+ * Retrieves the minimum and maximum column widths
+ *
+ * Since: 1.2
+ */
+void
+clutter_flow_layout_get_column_width (ClutterFlowLayout *layout,
+ gfloat *min_width,
+ gfloat *max_width)
+{
+ g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout));
+
+ if (min_width)
+ *min_width = layout->priv->min_col_width;
+
+ if (max_width)
+ *max_width = layout->priv->max_col_width;
+}
+
+/**
+ * clutter_flow_layout_set_row_height:
+ * @layout: a #ClutterFlowLayout
+ * @min_height: the minimum height of a row
+ * @max_height: the maximum height of a row
+ *
+ * Sets the minimum and maximum heights that a row can have
+ *
+ * Since: 1.2
+ */
+void
+clutter_flow_layout_set_row_height (ClutterFlowLayout *layout,
+ gfloat min_height,
+ gfloat max_height)
+{
+ ClutterFlowLayoutPrivate *priv;
+ gboolean notify_min = FALSE, notify_max = FALSE;
+
+ g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->min_row_height != min_height)
+ {
+ priv->min_row_height = min_height;
+
+ notify_min = TRUE;
+ }
+
+ if (priv->max_row_height != max_height)
+ {
+ priv->max_row_height = max_height;
+
+ notify_max = TRUE;
+ }
+
+ g_object_freeze_notify (G_OBJECT (layout));
+
+ if (notify_min || notify_max)
+ {
+ ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout);
+
+ clutter_layout_manager_layout_changed (manager);
+ }
+
+ if (notify_min)
+ g_object_notify (G_OBJECT (layout), "min-row-height");
+
+ if (notify_max)
+ g_object_notify (G_OBJECT (layout), "max-row-height");
+
+ g_object_thaw_notify (G_OBJECT (layout));
+}
+
+/**
+ * clutter_flow_layout_get_row_height:
+ * @layout: a #ClutterFlowLayout
+ * @min_height: (out): return location for the minimum row height, or %NULL
+ * @max_height: (out): return location for the maximum row height, or %NULL
+ *
+ * Retrieves the minimum and maximum row heights
+ *
+ * Since: 1.2
+ */
+void
+clutter_flow_layout_get_row_height (ClutterFlowLayout *layout,
+ gfloat *min_height,
+ gfloat *max_height)
+{
+ g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout));
+
+ if (min_height)
+ *min_height = layout->priv->min_row_height;
+
+ if (max_height)
+ *max_height = layout->priv->max_row_height;
+}
diff --git a/clutter/clutter-flow-layout.h b/clutter/clutter-flow-layout.h
new file mode 100644
index 000000000..5cbbcd359
--- /dev/null
+++ b/clutter/clutter-flow-layout.h
@@ -0,0 +1,127 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_FLOW_LAYOUT_H__
+#define __CLUTTER_FLOW_LAYOUT_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_FLOW_LAYOUT (clutter_flow_layout_get_type ())
+#define CLUTTER_FLOW_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayout))
+#define CLUTTER_IS_FLOW_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_FLOW_LAYOUT))
+#define CLUTTER_FLOW_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayoutClass))
+#define CLUTTER_IS_FLOW_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_FLOW_LAYOUT))
+#define CLUTTER_FLOW_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayoutClass))
+
+typedef struct _ClutterFlowLayout ClutterFlowLayout;
+typedef struct _ClutterFlowLayoutPrivate ClutterFlowLayoutPrivate;
+typedef struct _ClutterFlowLayoutClass ClutterFlowLayoutClass;
+
+/**
+ * ClutterFlowOrientation:
+ * @CLUTTER_FLOW_HORIZONTAL: Arrange the children of the flow layout
+ * horizontally first
+ * @CLUTTER_FLOW_VERTICAL: Arrange the children of the flow layout
+ * vertically first
+ *
+ * The direction of the arrangement of the children inside
+ * a #ClutterFlowLayout
+ *
+ * Since: 1.2
+ */
+typedef enum { /*< prefix=CLUTTER_FLOW >*/
+ CLUTTER_FLOW_HORIZONTAL,
+ CLUTTER_FLOW_VERTICAL
+} ClutterFlowOrientation;
+
+/**
+ * ClutterFlowLayout:
+ *
+ * The #ClutterFlowLayout structure contains only private data
+ * and should be accessed using the provided API
+ *
+ * Since: 1.2
+ */
+struct _ClutterFlowLayout
+{
+ /*< private >*/
+ ClutterLayoutManager parent_instance;
+
+ ClutterFlowLayoutPrivate *priv;
+};
+
+/**
+ * ClutterFlowLayoutClass:
+ *
+ * The #ClutterFlowLayoutClass structure contains only private data
+ * and should be accessed using the provided API
+ *
+ * Since: 1.2
+ */
+struct _ClutterFlowLayoutClass
+{
+ /*< private >*/
+ ClutterLayoutManagerClass parent_class;
+};
+
+GType clutter_flow_layout_get_type (void) G_GNUC_CONST;
+
+ClutterLayoutManager * clutter_flow_layout_new (ClutterFlowOrientation orientation);
+
+void clutter_flow_layout_set_orientation (ClutterFlowLayout *layout,
+ ClutterFlowOrientation orientation);
+ClutterFlowOrientation clutter_flow_layout_get_orientation (ClutterFlowLayout *layout);
+void clutter_flow_layout_set_homogeneous (ClutterFlowLayout *layout,
+ gboolean homogeneous);
+gboolean clutter_flow_layout_get_homogeneous (ClutterFlowLayout *layout);
+
+void clutter_flow_layout_set_column_spacing (ClutterFlowLayout *layout,
+ gfloat spacing);
+gfloat clutter_flow_layout_get_column_spacing (ClutterFlowLayout *layout);
+void clutter_flow_layout_set_row_spacing (ClutterFlowLayout *layout,
+ gfloat spacing);
+gfloat clutter_flow_layout_get_row_spacing (ClutterFlowLayout *layout);
+
+void clutter_flow_layout_set_column_width (ClutterFlowLayout *layout,
+ gfloat min_width,
+ gfloat max_width);
+void clutter_flow_layout_get_column_width (ClutterFlowLayout *layout,
+ gfloat *min_width,
+ gfloat *max_width);
+void clutter_flow_layout_set_row_height (ClutterFlowLayout *layout,
+ gfloat min_height,
+ gfloat max_height);
+void clutter_flow_layout_get_row_height (ClutterFlowLayout *layout,
+ gfloat *min_height,
+ gfloat *max_height);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_FLOW_LAYOUT_H__ */
diff --git a/clutter/clutter-group.c b/clutter/clutter-group.c
index 918aa5ecd..94dfdddf0 100644
--- a/clutter/clutter-group.c
+++ b/clutter/clutter-group.c
@@ -45,6 +45,7 @@
#include "clutter-group.h"
#include "clutter-container.h"
+#include "clutter-fixed-layout.h"
#include "clutter-main.h"
#include "clutter-private.h"
#include "clutter-debug.h"
@@ -75,6 +76,8 @@ G_DEFINE_TYPE_WITH_CODE (ClutterGroup,
struct _ClutterGroupPrivate
{
GList *children;
+
+ ClutterLayoutManager *layout;
};
@@ -126,113 +129,19 @@ clutter_group_pick (ClutterActor *actor,
}
}
-static void
-clutter_fixed_layout_get_preferred_width (GList *children,
- gfloat *min_width_p,
- gfloat *natural_width_p)
-{
- GList *l;
- gdouble min_right, natural_right;
-
- /* We will always be at least 0 sized (ie, if all of the actors are
- to the left of the origin we won't return a negative size) */
- min_right = 0;
- natural_right = 0;
-
- for (l = children; l != NULL; l = l->next)
- {
- ClutterActor *child = l->data;
- gfloat child_x, child_min, child_natural;
-
- child_x = clutter_actor_get_x (child);
-
- clutter_actor_get_preferred_size (child,
- &child_min, NULL,
- &child_natural, NULL);
-
- /* Track the rightmost edge */
- if (child_x + child_min > min_right)
- min_right = child_x + child_min;
-
- if (child_x + child_natural > natural_right)
- natural_right = child_x + child_natural;
- }
-
- /* The size is defined as the distance from the origin to the
- right-hand edge of the rightmost actor */
- if (min_width_p)
- *min_width_p = min_right;
-
- if (natural_width_p)
- *natural_width_p = natural_right;
-}
-
-static void
-clutter_fixed_layout_get_preferred_height (GList *children,
- gfloat *min_height_p,
- gfloat *natural_height_p)
-{
- GList *l;
- gdouble min_bottom, natural_bottom;
-
- /* We will always be at least 0 sized (ie, if all of the actors are
- above the origin we won't return a negative size) */
- min_bottom = 0;
- natural_bottom = 0;
-
- for (l = children; l != NULL; l = l->next)
- {
- ClutterActor *child = l->data;
- gfloat child_y, child_min, child_natural;
-
- child_y = clutter_actor_get_y (child);
-
- clutter_actor_get_preferred_size (child,
- NULL, &child_min,
- NULL, &child_natural);
-
- /* Track the bottommost edge */
- if (child_y + child_min > min_bottom)
- min_bottom = child_y + child_min;
-
- if (child_y + child_natural > natural_bottom)
- natural_bottom = child_y + child_natural;
- }
-
- /* The size is defined as the distance from the origin to the bottom
- edge of the bottommost actor */
- if (min_height_p)
- *min_height_p = min_bottom;
-
- if (natural_height_p)
- *natural_height_p = natural_bottom;
-}
-
-static void
-clutter_fixed_layout_allocate (GList *children,
- ClutterAllocationFlags flags)
-{
- GList *l;
-
- for (l = children; l != NULL; l = l->next)
- {
- ClutterActor *child = l->data;
- clutter_actor_allocate_preferred_size (child, flags);
- }
-}
-
static void
clutter_group_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
+ ClutterContainer *container = CLUTTER_CONTAINER (self);
ClutterGroupPrivate *priv = CLUTTER_GROUP (self)->priv;
- /* for_height is irrelevant to the fixed layout, so it's not used */
- clutter_fixed_layout_get_preferred_width (priv->children,
- min_width_p,
- natural_width_p);
+ clutter_layout_manager_get_preferred_width (priv->layout, container,
+ for_height,
+ min_width_p,
+ natural_width_p);
}
static void
@@ -241,12 +150,13 @@ clutter_group_get_preferred_height (ClutterActor *self,
gfloat *min_height_p,
gfloat *natural_height_p)
{
+ ClutterContainer *container = CLUTTER_CONTAINER (self);
ClutterGroupPrivate *priv = CLUTTER_GROUP (self)->priv;
- /* for_width is irrelevant to the fixed layout, so it's not used */
- clutter_fixed_layout_get_preferred_height (priv->children,
- min_height_p,
- natural_height_p);
+ clutter_layout_manager_get_preferred_width (priv->layout, container,
+ for_width,
+ min_height_p,
+ natural_height_p);
}
static void
@@ -254,17 +164,16 @@ clutter_group_allocate (ClutterActor *self,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
+ ClutterContainer *container = CLUTTER_CONTAINER (self);
ClutterGroupPrivate *priv = CLUTTER_GROUP (self)->priv;
/* chain up to set actor->allocation */
CLUTTER_ACTOR_CLASS (clutter_group_parent_class)->allocate (self, box, flags);
- /* Note that fixed-layout allocation of children does not care what
- * allocation the container received, so "box" is not passed in
- * here. We do not require that children's allocations are completely
- * contained by our own.
- */
- clutter_fixed_layout_allocate (priv->children, flags);
+ if (priv->children == NULL)
+ return;
+
+ clutter_layout_manager_allocate (priv->layout, container, box, flags);
}
static void
@@ -281,6 +190,12 @@ clutter_group_dispose (GObject *object)
priv->children = NULL;
}
+ if (priv->layout)
+ {
+ g_object_unref (priv->layout);
+ priv->layout = NULL;
+ }
+
G_OBJECT_CLASS (clutter_group_parent_class)->dispose (object);
}
@@ -468,8 +383,10 @@ sort_z_order (gconstpointer a,
if (depth_a < depth_b)
return -1;
+
if (depth_a > depth_b)
return 1;
+
return 0;
}
@@ -521,6 +438,9 @@ static void
clutter_group_init (ClutterGroup *self)
{
self->priv = CLUTTER_GROUP_GET_PRIVATE (self);
+
+ self->priv->layout = clutter_fixed_layout_new ();
+ g_object_ref_sink (self->priv->layout);
}
/**
diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c
new file mode 100644
index 000000000..899c10305
--- /dev/null
+++ b/clutter/clutter-layout-manager.c
@@ -0,0 +1,887 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+/**
+ * SECTION:clutter-layout-manager
+ * @short_description: Layout managers base class
+ *
+ * #ClutterLayoutManager is a base abstract class for layout managers. A
+ * layout manager implements the layouting policy for a composite or a
+ * container actor: it controls the preferred size of the actor to which
+ * it has been paired, and it controls the allocation of its children.
+ *
+ * Any composite or container #ClutterActor subclass can delegate the
+ * layouting of its children to a #ClutterLayoutManager. Clutter provides
+ * a generic container using #ClutterLayoutManager called #ClutterBox.
+ *
+ * Clutter provides some simple #ClutterLayoutManager sub-classes, like
+ * #ClutterFixedLayout and #ClutterBinLayout.
+ *
+ *
+ * Using ClutterLayoutManager inside an Actor
+ * In order to use a #ClutterLayoutManager inside a #ClutterActor
+ * sub-class you should invoke clutter_layout_manager_get_preferred_width()
+ * inside the ClutterActor::get_preferred_width()
+ * virtual function and clutter_layout_manager_get_preferred_height()
+ * inside the ClutterActor::get_preferred_height()
+ * virtual function implementations. You should also call
+ * clutter_layout_manager_allocate() inside the implementation of the
+ * ClutterActor::allocate() virtual
+ * function.
+ * In order to receive notifications for changes in the layout
+ * manager policies you should also connect to the
+ * #ClutterLayoutManager::layout-changed signal and queue a relayout
+ * on your actor. The following code should be enough if the actor
+ * does not need to perform specific operations whenever a layout
+ * manager changes:
+ *
+ * g_signal_connect_swapped (layout_manager,
+ * "layout-changed",
+ * G_CALLBACK (clutter_actor_queue_relayout),
+ * actor);
+ *
+ *
+ *
+ *
+ * Implementing a ClutterLayoutManager
+ * The implementation of a layout manager does not differ from
+ * the implementation of the size requisition and allocation bits of
+ * #ClutterActor, so you should read the relative documentation
+ * for subclassing
+ * ClutterActor.
+ * The layout manager implementation can hold a back reference
+ * to the #ClutterContainer by implementing the set_container()
+ * virtual function. The layout manager should not hold a reference
+ * on the container actor, to avoid reference cycles.
+ * If the layout manager has properties affecting the layout
+ * policies then it should emit the #ClutterLayoutManager::layout-changed
+ * signal on itself by using the clutter_layout_manager_layout_changed()
+ * function.
+ *
+ *
+ * #ClutterLayoutManager is available since Clutter 1.2
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+#include
+
+#include "clutter-debug.h"
+#include "clutter-layout-manager.h"
+#include "clutter-layout-meta.h"
+#include "clutter-marshal.h"
+#include "clutter-private.h"
+
+#define LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED(m,method) G_STMT_START { \
+ GObject *_obj = G_OBJECT (m); \
+ g_warning ("Layout managers of type %s do not implement " \
+ "the ClutterLayoutManager::%s method", \
+ G_OBJECT_TYPE_NAME (_obj), \
+ (method)); } G_STMT_END
+
+enum
+{
+ LAYOUT_CHANGED,
+
+ LAST_SIGNAL
+};
+
+G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager,
+ clutter_layout_manager,
+ G_TYPE_INITIALLY_UNOWNED);
+
+static GQuark quark_layout_meta = 0;
+static guint manager_signals[LAST_SIGNAL] = { 0, };
+
+static void
+layout_manager_real_get_preferred_width (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *nat_width_p)
+{
+ LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "get_preferred_width");
+
+ if (min_width_p)
+ *min_width_p = 0.0;
+
+ if (nat_width_p)
+ *nat_width_p = 0.0;
+}
+
+static void
+layout_manager_real_get_preferred_height (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *nat_height_p)
+{
+ LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "get_preferred_height");
+
+ if (min_height_p)
+ *min_height_p = 0.0;
+
+ if (nat_height_p)
+ *nat_height_p = 0.0;
+}
+
+static void
+layout_manager_real_allocate (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags)
+{
+ LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "allocate");
+}
+
+static ClutterLayoutMeta *
+layout_manager_real_create_child_meta (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor)
+{
+ return NULL;
+}
+
+static void
+clutter_layout_manager_class_init (ClutterLayoutManagerClass *klass)
+{
+ quark_layout_meta =
+ g_quark_from_static_string ("clutter-layout-manager-child-meta");
+
+ klass->get_preferred_width = layout_manager_real_get_preferred_width;
+ klass->get_preferred_height = layout_manager_real_get_preferred_height;
+ klass->allocate = layout_manager_real_allocate;
+ klass->create_child_meta = layout_manager_real_create_child_meta;
+
+ /**
+ * ClutterLayoutManager::layout-changed:
+ * @manager: the #ClutterLayoutManager that emitted the signal
+ *
+ * The ::layout-changed signal is emitted each time a layout manager
+ * has been changed. Every #ClutterActor using the @manager instance
+ * as a layout manager should connect a handler to the ::layout-changed
+ * signal and queue a relayout on themselves:
+ *
+ * |[
+ * static void layout_changed (ClutterLayoutManager *manager,
+ * ClutterActor *self)
+ * {
+ * clutter_actor_queue_relayout (self);
+ * }
+ * ...
+ * self->manager = g_object_ref_sink (manager);
+ * g_signal_connect (self->manager, "layout-changed",
+ * G_CALLBACK (layout_changed),
+ * self);
+ * ]|
+ *
+ * Sub-classes of #ClutterLayoutManager that implement a layout that
+ * can be controlled or changed using parameters should emit the
+ * ::layout-changed signal whenever one of the parameters changes,
+ * by using clutter_layout_manager_layout_changed().
+ *
+ * Since: 1.2
+ */
+ manager_signals[LAYOUT_CHANGED] =
+ g_signal_new (I_("layout-changed"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ClutterLayoutManagerClass,
+ layout_changed),
+ NULL, NULL,
+ clutter_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+clutter_layout_manager_init (ClutterLayoutManager *manager)
+{
+}
+
+/**
+ * clutter_layout_manager_get_preferred_width:
+ * @manager: a #ClutterLayoutManager
+ * @container: the #ClutterContainer using @manager
+ * @for_height: the height for which the width should be computed, or -1
+ * @min_width_p: (out) (allow-none): return location for the minimum width
+ * of the layout, or %NULL
+ * @nat_width_p: (out) (allow-none): return location for the natural width
+ * of the layout, or %NULL
+ *
+ * Computes the minimum and natural widths of the @container according
+ * to @manager.
+ *
+ * See also clutter_actor_get_preferred_width()
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *nat_width_p)
+{
+ ClutterLayoutManagerClass *klass;
+
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+ g_return_if_fail (CLUTTER_IS_CONTAINER (container));
+
+ klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
+ klass->get_preferred_width (manager, container, for_height,
+ min_width_p,
+ nat_width_p);
+}
+
+/**
+ * clutter_layout_manager_get_preferred_height:
+ * @manager: a #ClutterLayoutManager
+ * @container: the #ClutterContainer using @manager
+ * @for_width: the width for which the height should be computed, or -1
+ * @min_height_p: (out) (allow-none): return location for the minimum height
+ * of the layout, or %NULL
+ * @nat_height_p: (out) (allow-none): return location for the natural height
+ * of the layout, or %NULL
+ *
+ * Computes the minimum and natural heights of the @container according
+ * to @manager.
+ *
+ * See also clutter_actor_get_preferred_height()
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *nat_height_p)
+{
+ ClutterLayoutManagerClass *klass;
+
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+ g_return_if_fail (CLUTTER_IS_CONTAINER (container));
+
+ klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
+ klass->get_preferred_height (manager, container, for_width,
+ min_height_p,
+ nat_height_p);
+}
+
+/**
+ * clutter_layout_manager_allocate:
+ * @manager: a #ClutterLayoutManager
+ * @container: the #ClutterContainer using @manager
+ * @allocation: the #ClutterActorBox containing the allocated area
+ * of @container
+ * @flags: the allocation flags
+ *
+ * Allocates the children of @container given an area
+ *
+ * See also clutter_actor_allocate()
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_allocate (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags)
+{
+ ClutterLayoutManagerClass *klass;
+
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+ g_return_if_fail (CLUTTER_IS_CONTAINER (container));
+ g_return_if_fail (allocation != NULL);
+
+ klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
+ klass->allocate (manager, container, allocation, flags);
+}
+
+/**
+ * clutter_layout_manager_layout_changed:
+ * @manager: a #ClutterLayoutManager
+ *
+ * Emits the #ClutterLayoutManager::layout-changed signal on @manager
+ *
+ * This function should only be called by implementations of the
+ * #ClutterLayoutManager class
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_layout_changed (ClutterLayoutManager *manager)
+{
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+
+ g_signal_emit (manager, manager_signals[LAYOUT_CHANGED], 0);
+}
+
+/**
+ * clutter_layout_manager_set_container:
+ * @manager: a #ClutterLayoutManager
+ * @container: (allow-none): a #ClutterContainer using @manager
+ *
+ * If the #ClutterLayoutManager sub-class allows it, allow
+ * adding a weak reference of the @container using @manager
+ * from within the layout manager
+ *
+ * The layout manager should not increase the reference
+ * count of the @container
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_set_container (ClutterLayoutManager *manager,
+ ClutterContainer *container)
+{
+ ClutterLayoutManagerClass *klass;
+
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+ g_return_if_fail (container == NULL || CLUTTER_IS_CONTAINER (container));
+
+ klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
+ if (klass->set_container)
+ klass->set_container (manager, container);
+}
+
+static inline ClutterLayoutMeta *
+create_child_meta (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor)
+{
+ ClutterLayoutManagerClass *klass;
+
+ klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
+
+ return klass->create_child_meta (manager, container, actor);
+}
+
+static inline ClutterLayoutMeta *
+get_child_meta (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor)
+{
+ ClutterLayoutMeta *layout = NULL;
+
+ layout = g_object_get_qdata (G_OBJECT (actor), quark_layout_meta);
+ if (layout != NULL)
+ {
+ ClutterChildMeta *child = CLUTTER_CHILD_META (layout);
+
+ if (layout->manager == manager &&
+ child->container == container &&
+ child->actor == actor)
+ return layout;
+
+ /* if the LayoutMeta referenced is not attached to the
+ * layout manager then we simply ask the layout manager
+ * to replace it with the right one
+ */
+ layout = create_child_meta (manager, container, actor);
+ if (layout != NULL)
+ {
+ g_assert (CLUTTER_IS_LAYOUT_META (layout));
+ g_object_set_qdata_full (G_OBJECT (actor), quark_layout_meta,
+ layout,
+ (GDestroyNotify) g_object_unref);
+ }
+
+ return layout;
+ }
+
+ return NULL;
+}
+
+/**
+ * clutter_layout_manager_get_child_meta:
+ * @manager: a #ClutterLayoutManager
+ * @container: a #ClutterContainer using @manager
+ * @actor: a #ClutterActor child of @container
+ *
+ * Retrieves the #ClutterLayoutMeta that the layout @manager associated
+ * to the @actor child of @container
+ *
+ * Return value: a #ClutterLayoutMeta or %NULL
+ *
+ * Since: 1.0
+ */
+ClutterLayoutMeta *
+clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor)
+{
+ g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL);
+ g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), NULL);
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
+
+ return get_child_meta (manager, container, actor);
+}
+
+/**
+ * clutter_layout_manager_add_child_meta:
+ * @manager: a #ClutterLayoutManager
+ * @container: a #ClutterContainer using @manager
+ * @actor: a #ClutterActor child of @container
+ *
+ * Creates and binds a #ClutterLayoutMeta for @manager to
+ * a child of @container
+ *
+ * This function should only be used when implementing containers
+ * using #ClutterLayoutManager and not by application code
+ *
+ * Typically, containers should bind a #ClutterLayoutMeta created
+ * by a #ClutterLayoutManager when adding a new child, e.g.:
+ *
+ * |[
+ * static void
+ * my_container_add (ClutterContainer *container,
+ * ClutterActor *actor)
+ * {
+ * MyContainer *self = MY_CONTAINER (container);
+ *
+ * self->children = g_slist_append (self->children, actor);
+ * clutter_actor_set_parent (actor, CLUTTER_ACTOR (self));
+ *
+ * clutter_layout_manager_add_child_meta (self->layout,
+ * container,
+ * actor);
+ *
+ * clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+ *
+ * g_signal_emit_by_name (container, "actor-added");
+ * }
+ * ]|
+ *
+ * The #ClutterLayoutMeta should be removed when removing an
+ * actor; see clutter_layout_manager_remove_child_meta()
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_add_child_meta (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor)
+{
+ ClutterLayoutMeta *meta;
+
+ meta = create_child_meta (manager, container, actor);
+ if (meta == NULL)
+ return;
+
+ g_object_set_qdata_full (G_OBJECT (actor), quark_layout_meta,
+ meta,
+ (GDestroyNotify) g_object_unref);
+}
+
+/**
+ * clutter_layout_manager_remove_child_meta:
+ * @manager: a #ClutterLayoutManager
+ * @container: a #ClutterContainer using @manager
+ * @actor: a #ClutterActor child of @container
+ *
+ * Unbinds and unrefs a #ClutterLayoutMeta for @manager from
+ * a child of @container
+ *
+ * This function should only be used when implementing containers
+ * using #ClutterLayoutManager and not by application code
+ *
+ * Typically, containers should remove a #ClutterLayoutMeta created
+ * by a #ClutterLayoutManager when removing a child, e.g.:
+ *
+ * |[
+ * static void
+ * my_container_remove (ClutterContainer *container,
+ * ClutterActor *actor)
+ * {
+ * MyContainer *self = MY_CONTAINER (container);
+ *
+ * g_object_ref (actor);
+ *
+ * self->children = g_slist_remove (self->children, actor);
+ * clutter_actor_unparent (actor);
+ *
+ * clutter_layout_manager_remove_child_meta (self->layout,
+ * container,
+ * actor);
+ *
+ * clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+ *
+ * g_signal_emit_by_name (container, "actor-removed");
+ *
+ * g_object_unref (actor);
+ * }
+ * ]|
+ *
+ * See also clutter_layout_manager_add_child_meta()
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_remove_child_meta (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor)
+{
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+ g_return_if_fail (CLUTTER_IS_CONTAINER (container));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ if (get_child_meta (manager, container, actor))
+ g_object_set_qdata (G_OBJECT (actor), quark_layout_meta, NULL);
+}
+
+static inline gboolean
+layout_set_property_internal (ClutterLayoutManager *manager,
+ GObject *gobject,
+ GParamSpec *pspec,
+ const GValue *value)
+{
+ if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
+ {
+ g_warning ("%s: Child property '%s' of the layout manager of "
+ "type '%s' is constructor-only",
+ G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager));
+ return FALSE;
+ }
+
+ if (!(pspec->flags & G_PARAM_WRITABLE))
+ {
+ g_warning ("%s: Child property '%s' of the layout manager of "
+ "type '%s' is not writable",
+ G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager));
+ return FALSE;
+ }
+
+ g_object_set_property (gobject, pspec->name, value);
+
+ return TRUE;
+}
+
+static inline gboolean
+layout_get_property_internal (ClutterLayoutManager *manager,
+ GObject *gobject,
+ GParamSpec *pspec,
+ GValue *value)
+{
+ if (!(pspec->flags & G_PARAM_READABLE))
+ {
+ g_warning ("%s: Child property '%s' of the layout manager of "
+ "type '%s' is not readable",
+ G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager));
+ return FALSE;
+ }
+
+ g_object_get_property (gobject, pspec->name, value);
+
+ return TRUE;
+}
+
+/**
+ * clutter_layout_manager_child_set:
+ * @manager: a #ClutterLayoutManager
+ * @container: a #ClutterContainer using @manager
+ * @actor: a #ClutterActor child of @container
+ * @first_property: the first property name
+ * @Varargs: a list of property name and value pairs
+ *
+ * Sets a list of properties and their values on the #ClutterLayoutMeta
+ * associated by @manager to a child of @container
+ *
+ * Languages bindings should use clutter_layout_manager_child_set_property()
+ * instead
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_child_set (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor,
+ const gchar *first_property,
+ ...)
+{
+ ClutterLayoutMeta *meta;
+ GObjectClass *klass;
+ const gchar *pname;
+ va_list var_args;
+
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+ g_return_if_fail (CLUTTER_IS_CONTAINER (container));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+ g_return_if_fail (first_property != NULL);
+
+ meta = get_child_meta (manager, container, actor);
+ if (meta == NULL)
+ {
+ g_warning ("Layout managers of type '%s' do not support "
+ "child metadata",
+ g_type_name (G_OBJECT_TYPE (manager)));
+ return;
+ }
+
+ klass = G_OBJECT_GET_CLASS (meta);
+
+ va_start (var_args, first_property);
+
+ pname = first_property;
+ while (pname)
+ {
+ GValue value = { 0, };
+ GParamSpec *pspec;
+ gchar *error;
+ gboolean res;
+
+ pspec = g_object_class_find_property (klass, pname);
+ if (pspec == NULL)
+ {
+ g_warning ("%s: Layout managers of type '%s' have no child "
+ "property named '%s'",
+ G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname);
+ break;
+ }
+
+ g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ G_VALUE_COLLECT (&value, var_args, 0, &error);
+ if (error)
+ {
+ g_warning ("%s: %s", G_STRLOC, error);
+ g_free (error);
+ break;
+ }
+
+ res = layout_set_property_internal (manager, G_OBJECT (meta),
+ pspec,
+ &value);
+
+ g_value_unset (&value);
+
+ if (!res)
+ break;
+
+ pname = va_arg (var_args, gchar*);
+ }
+
+ va_end (var_args);
+}
+
+/**
+ * clutter_layout_manager_child_set_property:
+ * @manager: a #ClutterLayoutManager
+ * @container: a #ClutterContainer using @manager
+ * @actor: a #ClutterActor child of @container
+ * @property_name: the name of the property to set
+ * @value: a #GValue with the value of the property to set
+ *
+ * Sets a property on the #ClutterLayoutMeta created by @manager and
+ * attached to a child of @container
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_child_set_property (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor,
+ const gchar *property_name,
+ const GValue *value)
+{
+ ClutterLayoutMeta *meta;
+ GObjectClass *klass;
+ GParamSpec *pspec;
+
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+ g_return_if_fail (CLUTTER_IS_CONTAINER (container));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+ g_return_if_fail (property_name != NULL);
+ g_return_if_fail (value != NULL);
+
+ meta = get_child_meta (manager, container, actor);
+ if (meta == NULL)
+ {
+ g_warning ("Layout managers of type '%s' do not support "
+ "child metadata",
+ g_type_name (G_OBJECT_TYPE (manager)));
+ return;
+ }
+
+ klass = G_OBJECT_GET_CLASS (meta);
+
+ pspec = g_object_class_find_property (klass, property_name);
+ if (pspec == NULL)
+ {
+ g_warning ("%s: Layout managers of type '%s' have no child "
+ "property named '%s'",
+ G_STRLOC, G_OBJECT_TYPE_NAME (manager), property_name);
+ return;
+ }
+
+ layout_set_property_internal (manager, G_OBJECT (meta), pspec, value);
+}
+
+/**
+ * clutter_layout_manager_child_get:
+ * @manager: a #ClutterLayoutManager
+ * @container: a #ClutterContainer using @manager
+ * @actor: a #ClutterActor child of @container
+ * @first_property: the name of the first property
+ * @Varargs: a list of property name and return location for the value pairs
+ *
+ * Retrieves the values for a list of properties out of the
+ * #ClutterLayoutMeta created by @manager and attached to the
+ * child of a @container
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_child_get (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor,
+ const gchar *first_property,
+ ...)
+{
+ ClutterLayoutMeta *meta;
+ GObjectClass *klass;
+ const gchar *pname;
+ va_list var_args;
+
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+ g_return_if_fail (CLUTTER_IS_CONTAINER (container));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+ g_return_if_fail (first_property != NULL);
+
+ meta = get_child_meta (manager, container, actor);
+ if (meta == NULL)
+ {
+ g_warning ("Layout managers of type '%s' do not support "
+ "child metadata",
+ g_type_name (G_OBJECT_TYPE (manager)));
+ return;
+ }
+
+ klass = G_OBJECT_GET_CLASS (meta);
+
+ va_start (var_args, first_property);
+
+ pname = first_property;
+ while (pname)
+ {
+ GValue value = { 0, };
+ GParamSpec *pspec;
+ gchar *error;
+ gboolean res;
+
+ pspec = g_object_class_find_property (klass, pname);
+ if (pspec == NULL)
+ {
+ g_warning ("%s: Layout managers of type '%s' have no child "
+ "property named '%s'",
+ G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname);
+ break;
+ }
+
+ g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+
+ res = layout_get_property_internal (manager, G_OBJECT (meta),
+ pspec,
+ &value);
+ if (!res)
+ {
+ g_value_unset (&value);
+ break;
+ }
+
+ G_VALUE_LCOPY (&value, var_args, 0, &error);
+ if (error)
+ {
+ g_warning ("%s: %s", G_STRLOC, error);
+ g_free (error);
+ g_value_unset (&value);
+ break;
+ }
+
+ g_value_unset (&value);
+
+ pname = va_arg (var_args, gchar*);
+ }
+
+ va_end (var_args);
+}
+
+/**
+ * clutter_layout_manager_child_get_property:
+ * @manager: a #ClutterLayoutManager
+ * @container: a #ClutterContainer using @manager
+ * @actor: a #ClutterActor child of @container
+ * @property_name: the name of the property to get
+ * @value: a #GValue with the value of the property to get
+ *
+ * Gets a property on the #ClutterLayoutMeta created by @manager and
+ * attached to a child of @container
+ *
+ * The #GValue must already be initialized to the type of the property
+ * and has to be unset with g_value_unset() after extracting the real
+ * value out of it
+ *
+ * Since: 1.2
+ */
+void
+clutter_layout_manager_child_get_property (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor,
+ const gchar *property_name,
+ GValue *value)
+{
+ ClutterLayoutMeta *meta;
+ GObjectClass *klass;
+ GParamSpec *pspec;
+
+ g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
+ g_return_if_fail (CLUTTER_IS_CONTAINER (container));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+ g_return_if_fail (property_name != NULL);
+ g_return_if_fail (value != NULL);
+
+ meta = get_child_meta (manager, container, actor);
+ if (meta == NULL)
+ {
+ g_warning ("Layout managers of type %s do not support "
+ "child metadata",
+ g_type_name (G_OBJECT_TYPE (manager)));
+ return;
+ }
+
+ klass = G_OBJECT_GET_CLASS (meta);
+
+ pspec = g_object_class_find_property (klass, property_name);
+ if (pspec == NULL)
+ {
+ g_warning ("%s: Layout managers of type '%s' have no child "
+ "property named '%s'",
+ G_STRLOC, G_OBJECT_TYPE_NAME (manager), property_name);
+ return;
+ }
+
+ layout_get_property_internal (manager, G_OBJECT (meta), pspec, value);
+}
diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h
new file mode 100644
index 000000000..d3333c7cb
--- /dev/null
+++ b/clutter/clutter-layout-manager.h
@@ -0,0 +1,187 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_LAYOUT_MANAGER_H__
+#define __CLUTTER_LAYOUT_MANAGER_H__
+
+#include
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_LAYOUT_MANAGER (clutter_layout_manager_get_type ())
+#define CLUTTER_LAYOUT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_LAYOUT_MANAGER, ClutterLayoutManager))
+#define CLUTTER_IS_LAYOUT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_LAYOUT_MANAGER))
+#define CLUTTER_LAYOUT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_LAYOUT_MANAGER, ClutterLayoutManagerClass))
+#define CLUTTER_IS_LAYOUT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_LAYOUT_MANAGER))
+#define CLUTTER_LAYOUT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_LAYOUT_MANAGER, ClutterLayoutManagerClass))
+
+typedef struct _ClutterLayoutManager ClutterLayoutManager;
+typedef struct _ClutterLayoutManagerClass ClutterLayoutManagerClass;
+
+/**
+ * ClutterLayoutManager:
+ *
+ * The #ClutterLayoutManager structure contains only private data
+ * and should be accessed using the provided API
+ *
+ * Since: 1.2
+ */
+struct _ClutterLayoutManager
+{
+ /*< private >*/
+ GInitiallyUnowned parent_instance;
+
+ /* padding for future expansion */
+ gpointer dummy;
+};
+
+/**
+ * ClutterLayoutManagerClass:
+ * @get_preferred_width: virtual function; override to provide a preferred
+ * width for the layout manager. See also the get_preferred_width()
+ * virtual function in #ClutterActor
+ * @get_preferred_height: virtual function; override to provide a preferred
+ * height for the layout manager. See also the get_preferred_height()
+ * virtual function in #ClutterActor
+ * @allocate: virtual function; override to allocate the children of the
+ * layout manager. See also the allocate() virtual function in
+ * #ClutterActor
+ * @set_container: virtual function; override to set a back pointer
+ * on the #ClutterContainer using the layout manager. The implementation
+ * should not take a reference on the container, but just take a weak
+ * reference, to avoid potential leaks due to reference cycles
+ * @create_child_meta: virtual function; override to create a
+ * #ClutterChildMeta instance associated to a #ClutterContainer and a
+ * child #ClutterActor, used to maintain layout manager specific properties
+ * @layout_changed: class handler for the #ClutterLayoutManager::layout-changed
+ * signal
+ *
+ * The #ClutterLayoutManagerClass structure contains only private
+ * data and should be accessed using the provided API
+ *
+ * Since: 1.2
+ */
+struct _ClutterLayoutManagerClass
+{
+ /*< private >*/
+ GInitiallyUnownedClass parent_class;
+
+ /*< public >*/
+ void (* get_preferred_width) (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_height,
+ gfloat *minimum_width_p,
+ gfloat *natural_width_p);
+ void (* get_preferred_height) (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_width,
+ gfloat *minimum_height_p,
+ gfloat *natural_height_p);
+ void (* allocate) (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags);
+
+ void (* set_container) (ClutterLayoutManager *manager,
+ ClutterContainer *container);
+
+ ClutterLayoutMeta *(* create_child_meta) (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor);
+
+ void (* layout_changed) (ClutterLayoutManager *manager);
+
+ /*< private >*/
+ /* padding for future expansion */
+ void (* _clutter_padding_1) (void);
+ void (* _clutter_padding_2) (void);
+ void (* _clutter_padding_3) (void);
+ void (* _clutter_padding_4) (void);
+ void (* _clutter_padding_5) (void);
+ void (* _clutter_padding_6) (void);
+ void (* _clutter_padding_7) (void);
+ void (* _clutter_padding_8) (void);
+};
+
+GType clutter_layout_manager_get_type (void) G_GNUC_CONST;
+
+void clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *nat_width_p);
+void clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *nat_height_p);
+void clutter_layout_manager_allocate (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags);
+
+void clutter_layout_manager_set_container (ClutterLayoutManager *manager,
+ ClutterContainer *container);
+void clutter_layout_manager_layout_changed (ClutterLayoutManager *manager);
+
+ClutterLayoutMeta *clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor);
+void clutter_layout_manager_add_child_meta (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor);
+void clutter_layout_manager_remove_child_meta (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor);
+
+void clutter_layout_manager_child_set (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor,
+ const gchar *first_property,
+ ...) G_GNUC_NULL_TERMINATED;
+void clutter_layout_manager_child_get (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor,
+ const gchar *first_property,
+ ...) G_GNUC_NULL_TERMINATED;
+void clutter_layout_manager_child_set_property (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor,
+ const gchar *property_name,
+ const GValue *value);
+void clutter_layout_manager_child_get_property (ClutterLayoutManager *manager,
+ ClutterContainer *container,
+ ClutterActor *actor,
+ const gchar *property_name,
+ GValue *value);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_LAYOUT_MANAGER_H__ */
diff --git a/clutter/clutter-layout-meta.c b/clutter/clutter-layout-meta.c
new file mode 100644
index 000000000..f7bf423f0
--- /dev/null
+++ b/clutter/clutter-layout-meta.c
@@ -0,0 +1,142 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+/**
+ * SECTION:clutter-layout-meta
+ * @short_description: Wrapper for actors inside a layout manager
+ *
+ * #ClutterLayoutMeta is a wrapper object created by #ClutterLayoutManager
+ * implementations in order to store child-specific data and properties.
+ *
+ * A #ClutterLayoutMeta wraps a #ClutterActor inside a #ClutterContainer
+ * using a #ClutterLayoutManager.
+ *
+ * #ClutterLayoutMeta is available since Clutter 1.2
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "clutter-layout-meta.h"
+#include "clutter-debug.h"
+#include "clutter-private.h"
+
+G_DEFINE_ABSTRACT_TYPE (ClutterLayoutMeta,
+ clutter_layout_meta,
+ CLUTTER_TYPE_CHILD_META);
+
+enum
+{
+ PROP_0,
+
+ PROP_MANAGER
+};
+
+static void
+clutter_layout_meta_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterLayoutMeta *layout_meta = CLUTTER_LAYOUT_META (object);
+
+ switch (prop_id)
+ {
+ case PROP_MANAGER:
+ layout_meta->manager = g_value_get_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_layout_meta_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterLayoutMeta *layout_meta = CLUTTER_LAYOUT_META (object);
+
+ switch (prop_id)
+ {
+ case PROP_MANAGER:
+ g_value_set_object (value, layout_meta->manager);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_layout_meta_class_init (ClutterLayoutMetaClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ gobject_class->set_property = clutter_layout_meta_set_property;
+ gobject_class->get_property = clutter_layout_meta_get_property;
+
+ /**
+ * ClutterLayoutMeta:manager:
+ *
+ * The #ClutterLayoutManager that created this #ClutterLayoutMeta.
+ *
+ * Since: 1.2
+ */
+ pspec = g_param_spec_object ("manager",
+ "Manager",
+ "The manager that created this data",
+ CLUTTER_TYPE_LAYOUT_MANAGER,
+ G_PARAM_CONSTRUCT_ONLY |
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_MANAGER, pspec);
+}
+
+static void
+clutter_layout_meta_init (ClutterLayoutMeta *self)
+{
+}
+
+/**
+ * clutter_layout_meta_get_manager:
+ * @data: a #ClutterLayoutMeta
+ *
+ * Retrieves the actor wrapped by @data
+ *
+ * Return value: (transfer none): a #ClutterLayoutManager
+ *
+ * Since: 1.2
+ */
+ClutterLayoutManager *
+clutter_layout_meta_get_manager (ClutterLayoutMeta *data)
+{
+ g_return_val_if_fail (CLUTTER_IS_LAYOUT_META (data), NULL);
+
+ return data->manager;
+}
diff --git a/clutter/clutter-layout-meta.h b/clutter/clutter-layout-meta.h
new file mode 100644
index 000000000..ac34daeea
--- /dev/null
+++ b/clutter/clutter-layout-meta.h
@@ -0,0 +1,85 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_LAYOUT_META_H__
+#define __CLUTTER_LAYOUT_META_H__
+
+#include
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_LAYOUT_META (clutter_layout_meta_get_type ())
+#define CLUTTER_LAYOUT_META(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_LAYOUT_META, ClutterLayoutMeta))
+#define CLUTTER_IS_LAYOUT_META(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_LAYOUT_META))
+#define CLUTTER_LAYOUT_META_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_LAYOUT_META, ClutterLayoutMetaClass))
+#define CLUTTER_IS_LAYOUT_META_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_LAYOUT_META))
+#define CLUTTER_LAYOUT_META_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_LAYOUT_META, ClutterLayoutMetaClass))
+
+/* ClutterLayoutMeta is defined in clutter-types.h */
+
+typedef struct _ClutterLayoutMetaClass ClutterLayoutMetaClass;
+
+/**
+ * ClutterLayoutMeta
+ * @manager: the layout manager handling this data
+ *
+ * Sub-class of #ClutterChildMeta specific for layout managers
+ *
+ * A #ClutterLayoutManager sub-class should create a #ClutterLayoutMeta
+ * instance by overriding the #ClutterLayoutManager::create_child_meta()
+ * virtual function
+ *
+ * Since: 1.2
+ */
+struct _ClutterLayoutMeta
+{
+ /*< private >*/
+ ClutterChildMeta parent_instance;
+
+ /*< public >*/
+ ClutterLayoutManager *manager;
+
+ /*< private >*/
+ /* padding */
+ gpointer dummy;
+};
+
+struct _ClutterLayoutMetaClass
+{
+ ClutterChildMetaClass parent_class;
+};
+
+GType clutter_layout_meta_get_type (void) G_GNUC_CONST;
+
+ClutterLayoutManager *clutter_layout_meta_get_manager (ClutterLayoutMeta *data);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_LAYOUT_META_H__ */
diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h
index 69d6aa129..13f120307 100644
--- a/clutter/clutter-types.h
+++ b/clutter/clutter-types.h
@@ -43,6 +43,7 @@ typedef struct _ClutterActor ClutterActor;
typedef struct _ClutterStage ClutterStage;
typedef struct _ClutterContainer ClutterContainer; /* dummy */
typedef struct _ClutterChildMeta ClutterChildMeta;
+typedef struct _ClutterLayoutMeta ClutterLayoutMeta;
/**
* ClutterGravity:
diff --git a/clutter/clutter.h b/clutter/clutter.h
index 701cf3176..087d93d40 100644
--- a/clutter/clutter.h
+++ b/clutter/clutter.h
@@ -43,6 +43,9 @@
#include "clutter-behaviour-rotate.h"
#include "clutter-behaviour-scale.h"
#include "clutter-binding-pool.h"
+#include "clutter-bin-layout.h"
+#include "clutter-box.h"
+#include "clutter-box-layout.h"
#include "clutter-cairo-texture.h"
#include "clutter-child-meta.h"
#include "clutter-clone.h"
@@ -50,10 +53,14 @@
#include "clutter-container.h"
#include "clutter-event.h"
#include "clutter-feature.h"
+#include "clutter-fixed-layout.h"
+#include "clutter-flow-layout.h"
#include "clutter-frame-source.h"
#include "clutter-group.h"
#include "clutter-interval.h"
#include "clutter-keysyms.h"
+#include "clutter-layout-manager.h"
+#include "clutter-layout-meta.h"
#include "clutter-list-model.h"
#include "clutter-main.h"
#include "clutter-media.h"
diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in
index 1a6409105..d00ec1d5c 100644
--- a/doc/reference/clutter/clutter-docs.xml.in
+++ b/doc/reference/clutter/clutter-docs.xml.in
@@ -55,6 +55,8 @@
+
+
@@ -71,6 +73,16 @@
+
+
+
+
+ Layout managers
+
+
+
+
+
@@ -198,13 +210,13 @@
Object Hierarchy
-
+
Object Index
-
+
diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt
index ca9afbee5..36dc187e3 100644
--- a/doc/reference/clutter/clutter-sections.txt
+++ b/doc/reference/clutter/clutter-sections.txt
@@ -305,6 +305,8 @@ clutter_actor_get_preferred_width
clutter_actor_get_preferred_height
clutter_actor_set_fixed_position_set
clutter_actor_get_fixed_position_set
+clutter_actor_set_request_mode
+clutter_actor_get_request_mode
clutter_actor_set_geometry
@@ -1726,3 +1728,195 @@ CLUTTER_STAGE_MANAGER_GET_CLASS
clutter_stage_manager_get_type
+
+
+Layout Managers
+clutter-layout-manager
+ClutterLayoutManager
+ClutterLayoutManagerClass
+clutter_layout_manager_get_preferred_width
+clutter_layout_manager_get_preferred_height
+clutter_layout_manager_allocate
+clutter_layout_manager_layout_changed
+clutter_layout_manager_set_container
+
+
+clutter_layout_manager_add_child_meta
+clutter_layout_manager_remove_child_meta
+clutter_layout_manager_get_child_meta
+clutter_layout_manager_child_set
+clutter_layout_manager_child_set_property
+clutter_layout_manager_child_get
+clutter_layout_manager_child_get_property
+
+
+CLUTTER_TYPE_LAYOUT_MANAGER
+CLUTTER_LAYOUT_MANAGER
+CLUTTER_LAYOUT_MANAGER_CLASS
+CLUTTER_IS_LAYOUT_MANAGER
+CLUTTER_IS_LAYOUT_MANAGER_CLASS
+CLUTTER_LAYOUT_MANAGER_GET_CLASS
+
+
+clutter_layout_manager_get_type
+
+
+
+ClutterFixedLayout
+clutter-fixed-layout
+ClutterFixedLayout
+ClutterFixedLayoutClass
+clutter_fixed_layout_new
+
+
+CLUTTER_TYPE_FIXED_LAYOUT
+CLUTTER_FIXED_LAYOUT
+CLUTTER_FIXED_LAYOUT_CLASS
+CLUTTER_IS_FIXED_LAYOUT
+CLUTTER_IS_FIXED_LAYOUT_CLASS
+CLUTTER_FIXED_LAYOUT_GET_CLASS
+
+
+clutter_fixed_layout_get_type
+
+
+
+ClutterBinLayout
+clutter-bin-layout
+ClutterBinAlignment
+ClutterBinLayout
+ClutterBinLayoutClass
+clutter_bin_layout_new
+clutter_bin_layout_set_alignment
+clutter_bin_layout_get_alignment
+clutter_bin_layout_add
+
+
+CLUTTER_TYPE_BIN_LAYOUT
+CLUTTER_BIN_LAYOUT
+CLUTTER_BIN_LAYOUT_CLASS
+CLUTTER_IS_BIN_LAYOUT
+CLUTTER_IS_BIN_LAYOUT_CLASS
+CLUTTER_BIN_LAYOUT_GET_CLASS
+
+
+ClutterBinLayoutPrivate
+clutter_bin_layout_get_type
+
+
+
+ClutterBox
+clutter-box
+ClutterBox
+ClutterBoxClass
+clutter_box_new
+clutter_box_set_layout_manager
+clutter_box_get_layout_manager
+clutter_box_set_color
+clutter_box_get_color
+
+
+clutter_box_pack
+clutter_box_packv
+
+
+CLUTTER_TYPE_BOX
+CLUTTER_BOX
+CLUTTER_BOX_CLASS
+CLUTTER_IS_BOX
+CLUTTER_IS_BOX_CLASS
+CLUTTER_BOX_GET_CLASS
+
+
+ClutterBoxPrivate
+clutter_box_get_type
+
+
+
+ClutterLayoutMeta
+clutter-layout-meta
+ClutterLayoutMeta
+ClutterLayoutMetaClass
+clutter_layout_meta_get_manager
+
+
+CLUTTER_TYPE_LAYOUT_META
+CLUTTER_LAYOUT_META
+CLUTTER_LAYOUT_META_CLASS
+CLUTTER_IS_LAYOUT_META
+CLUTTER_IS_LAYOUT_META_CLASS
+CLUTTER_LAYOUT_META_GET_CLASS
+
+
+clutter_layout_meta_get_type
+
+
+
+clutter-flow-layout
+ClutterFlowLayout
+ClutterFlowOrientation
+ClutterFlowLayout
+ClutterFlowLayoutClass
+clutter_flow_layout_new
+clutter_flow_layout_set_orientation
+clutter_flow_layout_get_orientation
+
+
+clutter_flow_layout_set_column_spacing
+clutter_flow_layout_get_column_spacing
+clutter_flow_layout_set_row_spacing
+clutter_flow_layout_get_row_spacing
+clutter_flow_layout_set_column_width
+clutter_flow_layout_get_column_width
+clutter_flow_layout_set_row_height
+clutter_flow_layout_get_row_height
+
+
+CLUTTER_TYPE_FLOW_LAYOUT
+CLUTTER_FLOW_LAYOUT
+CLUTTER_FLOW_LAYOUT_CLASS
+CLUTTER_IS_FLOW_LAYOUT
+CLUTTER_IS_FLOW_LAYOUT_CLASS
+CLUTTER_FLOW_LAYOUT_GET_CLASS
+
+ClutterFlowLayoutPrivate
+clutter_flow_layout_get_type
+
+
+
+ClutterBoxLayout
+clutter-box-layout
+ClutterBoxAlignment
+ClutterBoxLayout
+ClutterBoxLayoutClass
+clutter_box_layout_new
+clutter_box_layout_set_pack_start
+clutter_box_layout_get_pack_start
+clutter_box_layout_set_spacing
+clutter_box_layout_get_spacing
+clutter_box_layout_set_vertical
+clutter_box_layout_get_vertical
+
+
+clutter_box_layout_pack
+
+
+clutter_box_layout_set_alignment
+clutter_box_layout_get_alignment
+clutter_box_layout_set_expand
+clutter_box_layout_get_expand
+clutter_box_layout_set_fill
+clutter_box_layout_get_fill
+
+
+CLUTTER_TYPE_BOX_LAYOUT
+CLUTTER_BOX_LAYOUT
+CLUTTER_BOX_LAYOUT_CLASS
+CLUTTER_IS_BOX_LAYOUT
+CLUTTER_IS_BOX_LAYOUT_CLASS
+CLUTTER_BOX_LAYOUT_GET_CLASS
+
+
+ClutterBoxLayoutPrivate
+clutter_box_layout_get_type
+
diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types
index 5a243947f..9c62cb757 100644
--- a/doc/reference/clutter/clutter.types
+++ b/doc/reference/clutter/clutter.types
@@ -33,3 +33,10 @@ clutter_animation_get_type
clutter_interval_get_type
clutter_stage_manager_get_type
clutter_binding_pool_get_type
+clutter_box_get_type
+clutter_layout_manager_get_type
+clutter_layout_meta_get_type
+clutter_fixed_layout_get_type
+clutter_bin_layout_get_type
+clutter_flow_layout_get_type
+clutter_box_layout_get_type
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index ae8c3e7d9..4693ef002 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -43,7 +43,10 @@ UNIT_TESTS = \
test-text.c \
test-text-field.c \
test-clutter-cairo-flowers.c \
- test-cogl-vertex-buffer.c
+ test-cogl-vertex-buffer.c \
+ test-bin-layout.c \
+ test-flow-layout.c \
+ test-box-layout.c
if X11_TESTS
UNIT_TESTS += test-pixmap.c
diff --git a/tests/interactive/test-bin-layout.c b/tests/interactive/test-bin-layout.c
new file mode 100644
index 000000000..8fcc0515c
--- /dev/null
+++ b/tests/interactive/test-bin-layout.c
@@ -0,0 +1,185 @@
+#include
+#include
+#include
+#include
+
+static ClutterActor *
+make_background (const ClutterColor *color,
+ gfloat width,
+ gfloat height)
+{
+ ClutterActor *tex = clutter_cairo_texture_new (width, height);
+ cairo_t *cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (tex));
+ cairo_pattern_t *pat;
+ gfloat x, y;
+
+#define BG_ROUND_RADIUS 12
+
+ x = y = 0;
+
+ cairo_move_to (cr, BG_ROUND_RADIUS, y);
+ cairo_line_to (cr, width - BG_ROUND_RADIUS, y);
+ cairo_curve_to (cr, width, y, width, y, width, BG_ROUND_RADIUS);
+ cairo_line_to (cr, width, height - BG_ROUND_RADIUS);
+ cairo_curve_to (cr, width, height, width, height, width - BG_ROUND_RADIUS, height);
+ cairo_line_to (cr, BG_ROUND_RADIUS, height);
+ cairo_curve_to (cr, x, height, x, height, x, height - BG_ROUND_RADIUS);
+ cairo_line_to (cr, x, BG_ROUND_RADIUS);
+ cairo_curve_to (cr, x, y, x, y, BG_ROUND_RADIUS, y);
+
+ cairo_close_path (cr);
+
+ clutter_cairo_set_source_color (cr, color);
+ cairo_stroke (cr);
+
+ x += 4;
+ y += 4;
+ width -= 4;
+ height -= 4;
+
+ cairo_move_to (cr, BG_ROUND_RADIUS, y);
+ cairo_line_to (cr, width - BG_ROUND_RADIUS, y);
+ cairo_curve_to (cr, width, y, width, y, width, BG_ROUND_RADIUS);
+ cairo_line_to (cr, width, height - BG_ROUND_RADIUS);
+ cairo_curve_to (cr, width, height, width, height, width - BG_ROUND_RADIUS, height);
+ cairo_line_to (cr, BG_ROUND_RADIUS, height);
+ cairo_curve_to (cr, x, height, x, height, x, height - BG_ROUND_RADIUS);
+ cairo_line_to (cr, x, BG_ROUND_RADIUS);
+ cairo_curve_to (cr, x, y, x, y, BG_ROUND_RADIUS, y);
+
+ cairo_close_path (cr);
+
+ pat = cairo_pattern_create_linear (0, 0, 0, height);
+ cairo_pattern_add_color_stop_rgba (pat, 1, .85, .85, .85, 1);
+ cairo_pattern_add_color_stop_rgba (pat, .95, 1, 1, 1, 1);
+ cairo_pattern_add_color_stop_rgba (pat, .05, 1, 1, 1, 1);
+ cairo_pattern_add_color_stop_rgba (pat, 0, .85, .85, .85, 1);
+
+ cairo_set_source (cr, pat);
+ cairo_fill (cr);
+
+ cairo_pattern_destroy (pat);
+ cairo_destroy (cr);
+
+#undef BG_ROUND_RADIUS
+
+ return tex;
+}
+
+static gboolean
+on_box_enter (ClutterActor *box,
+ ClutterEvent *event,
+ ClutterActor *emblem)
+{
+ clutter_actor_animate (emblem, CLUTTER_LINEAR, 150,
+ "opacity", 255,
+ NULL);
+
+ return TRUE;
+}
+
+static gboolean
+on_box_leave (ClutterActor *box,
+ ClutterEvent *event,
+ ClutterActor *emblem)
+{
+ clutter_actor_animate (emblem, CLUTTER_LINEAR, 150,
+ "opacity", 0,
+ NULL);
+
+ return TRUE;
+}
+
+G_MODULE_EXPORT int
+test_bin_layout_main (int argc, char *argv[])
+{
+ ClutterActor *stage, *box, *rect;
+ ClutterLayoutManager *layout;
+ ClutterColor stage_color = { 0xe0, 0xf2, 0xfc, 0xff };
+ ClutterColor bg_color = { 0xcc, 0xcc, 0xcc, 0x99 };
+ ClutterColor *color;
+
+ clutter_init (&argc, &argv);
+
+ stage = clutter_stage_get_default ();
+ clutter_stage_set_title (CLUTTER_STAGE (stage), "Box test");
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+ clutter_actor_set_size (stage, 640, 480);
+
+ layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_BIN_ALIGNMENT_CENTER);
+
+ box = clutter_box_new (layout);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
+ clutter_actor_set_anchor_point_from_gravity (box, CLUTTER_GRAVITY_CENTER);
+ clutter_actor_set_position (box, 320, 240);
+ clutter_actor_set_reactive (box, TRUE);
+ clutter_actor_set_name (box, "box");
+
+ rect = make_background (&bg_color, 200, 200);
+
+ /* first method: use clutter_box_pack() */
+ clutter_box_pack (CLUTTER_BOX (box), rect,
+ "x-align", CLUTTER_BIN_ALIGNMENT_FILL,
+ "y-align", CLUTTER_BIN_ALIGNMENT_FILL,
+ NULL);
+
+ clutter_actor_lower_bottom (rect);
+ clutter_actor_set_name (rect, "background");
+
+
+ {
+ ClutterActor *tex;
+ GError *error;
+
+ error = NULL;
+ tex = clutter_texture_new_from_file ("redhand.png", &error);
+ if (error)
+ g_error ("Unable to create texture: %s", error->message);
+
+ clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (tex), TRUE);
+
+ /* second method: use clutter_bin_layout_add() */
+ clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), tex,
+ CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_BIN_ALIGNMENT_CENTER);
+
+ clutter_actor_raise (tex, rect);
+ clutter_actor_set_width (tex, 175);
+ clutter_actor_set_name (tex, "texture");
+ }
+
+ color = clutter_color_new (g_random_int_range (0, 255),
+ g_random_int_range (0, 255),
+ g_random_int_range (0, 255),
+ 224);
+
+ rect = clutter_rectangle_new_with_color (color);
+
+ /* third method: container_add() and set_alignment() */
+ clutter_container_add_actor (CLUTTER_CONTAINER (box), rect);
+ clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout), rect,
+ CLUTTER_BIN_ALIGNMENT_END,
+ CLUTTER_BIN_ALIGNMENT_END);
+
+ clutter_actor_set_size (rect, 50, 50);
+ clutter_actor_set_opacity (rect, 0);
+ clutter_actor_raise_top (rect);
+ clutter_actor_set_name (rect, "emblem");
+
+
+ g_signal_connect (box,
+ "enter-event", G_CALLBACK (on_box_enter),
+ rect);
+ g_signal_connect (box,
+ "leave-event", G_CALLBACK (on_box_leave),
+ rect);
+
+ clutter_actor_show_all (stage);
+
+ clutter_main ();
+
+ clutter_color_free (color);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/interactive/test-box-layout.c b/tests/interactive/test-box-layout.c
new file mode 100644
index 000000000..9903fcfa9
--- /dev/null
+++ b/tests/interactive/test-box-layout.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+#include
+#include
+
+#include
+
+#include
+
+static ClutterActor *hover_actor = NULL;
+
+static void
+enter_event (ClutterActor *actor,
+ ClutterEvent *event,
+ gpointer data)
+{
+ ClutterColor color = { 0x00, 0x00, 0x00, 0xff };
+
+ clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (actor), 2);
+ clutter_rectangle_set_border_color (CLUTTER_RECTANGLE (actor), &color);
+
+ hover_actor = actor;
+}
+
+static void
+leave_event (ClutterActor *actor,
+ ClutterEvent *event,
+ gpointer data)
+{
+ clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (actor), 0);
+
+ hover_actor = NULL;
+}
+
+static gboolean
+button_release_event (ClutterActor *actor,
+ ClutterEvent *event,
+ ClutterBoxLayout *box)
+{
+ gboolean xfill, yfill;
+ ClutterBoxAlignment xalign, yalign;
+ gint button;
+
+ button = clutter_event_get_button (event);
+
+ if (button == 1)
+ {
+ clutter_box_layout_get_fill (box, actor, &xfill, &yfill);
+ clutter_box_layout_set_fill (box, actor,
+ xfill ? FALSE : TRUE,
+ yfill ? FALSE : TRUE);
+ }
+ else
+ {
+ clutter_box_layout_get_alignment (box, actor, &xalign, &yalign);
+
+ if (xalign < 2)
+ xalign += 1;
+ else
+ xalign = 0;
+
+ if (yalign < 2)
+ yalign += 1;
+ else
+ yalign = 0;
+
+ clutter_box_layout_set_alignment (box, actor, xalign, yalign);
+ }
+
+ return TRUE;
+}
+
+static void
+add_actor (ClutterBoxLayout *box)
+{
+ ClutterActor *rect;
+ ClutterColor color = { 0xff, 0xff, 0xff, 255 };
+ static gboolean expand = TRUE;
+
+ clutter_color_from_hls (&color,
+ g_random_double_range (0.0, 360.0),
+ 0.5,
+ 0.5);
+
+ rect = clutter_rectangle_new_with_color (&color);
+ clutter_actor_set_size (rect, 32, 64);
+ clutter_box_layout_pack (box, rect, expand,
+ FALSE, /* x-fill */
+ FALSE, /* y-fill */
+ CLUTTER_BOX_ALIGNMENT_CENTER,
+ CLUTTER_BOX_ALIGNMENT_CENTER);
+
+ clutter_actor_set_reactive (rect, TRUE);
+ g_signal_connect (rect, "enter-event", G_CALLBACK (enter_event), NULL);
+ g_signal_connect (rect, "leave-event", G_CALLBACK (leave_event), NULL);
+ g_signal_connect (rect, "button-release-event",
+ G_CALLBACK (button_release_event),
+ box);
+
+ expand = !expand;
+}
+
+static gboolean
+key_release_cb (ClutterActor *actor,
+ ClutterEvent *event,
+ ClutterBoxLayout *layout)
+{
+ gboolean toggle;
+ guint spacing;
+
+ switch (clutter_event_get_key_symbol (event))
+ {
+ case CLUTTER_v:
+ toggle = clutter_box_layout_get_vertical (layout);
+ clutter_box_layout_set_vertical (layout, !toggle);
+ break;
+
+ case CLUTTER_p:
+ toggle = clutter_box_layout_get_pack_start (layout);
+ clutter_box_layout_set_pack_start (layout, !toggle);
+ break;
+
+ case CLUTTER_s:
+ spacing = clutter_box_layout_get_spacing (layout);
+
+ if (spacing > 12)
+ spacing = 0;
+ else
+ spacing++;
+
+ clutter_box_layout_set_spacing (layout, spacing);
+ break;
+
+ case '+':
+ add_actor (layout);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+stage_size_changed_cb (ClutterActor *stage,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags,
+ ClutterActor *box)
+{
+ gfloat width, height;
+
+ clutter_actor_box_get_size (allocation, &width, &height);
+ clutter_actor_set_size (box, width - 100, height - 100);
+}
+
+G_MODULE_EXPORT int
+test_box_layout_main (int argc, char *argv[])
+{
+ ClutterActor *stage, *box;
+ ClutterLayoutManager *layout;
+ gint i;
+
+ clutter_init (&argc, &argv);
+
+ stage = clutter_stage_get_default ();
+ clutter_stage_set_title (CLUTTER_STAGE (stage), "Box Layout");
+ clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
+ clutter_actor_set_size (stage, 640, 480);
+
+ layout = clutter_box_layout_new ();
+
+ box = clutter_box_new (layout);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
+
+ for (i = 0; i < 5; i++)
+ add_actor (CLUTTER_BOX_LAYOUT (layout));
+
+ g_signal_connect (stage, "key-release-event",
+ G_CALLBACK (key_release_cb),
+ layout);
+ g_signal_connect (stage, "allocation-changed",
+ G_CALLBACK (stage_size_changed_cb),
+ box);
+
+ clutter_actor_show (stage);
+
+ clutter_main ();
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/interactive/test-flow-layout.c b/tests/interactive/test-flow-layout.c
new file mode 100644
index 000000000..f8131ee3e
--- /dev/null
+++ b/tests/interactive/test-flow-layout.c
@@ -0,0 +1,171 @@
+#include
+#include
+#include
+#include
+
+#define N_RECTS 20
+
+static gboolean is_homogeneous = FALSE;
+static gboolean vertical = FALSE;
+static gboolean random_size = FALSE;
+
+static gint n_rects = N_RECTS;
+static gint x_spacing = 0;
+static gint y_spacing = 0;
+
+static GOptionEntry entries[] = {
+ {
+ "random-size", 'r',
+ 0,
+ G_OPTION_ARG_NONE,
+ &random_size,
+ "Randomly size the rectangles", NULL
+ },
+ {
+ "num-rects", 'n',
+ 0,
+ G_OPTION_ARG_INT,
+ &n_rects,
+ "Number of rectangles", "RECTS"
+ },
+ {
+ "vertical", 'v',
+ 0,
+ G_OPTION_ARG_NONE,
+ &vertical,
+ "Set vertical orientation", NULL
+ },
+ {
+ "homogeneous", 'h',
+ 0,
+ G_OPTION_ARG_NONE,
+ &is_homogeneous,
+ "Whether the layout should be homogeneous", NULL
+ },
+ {
+ "x-spacing", 0,
+ 0,
+ G_OPTION_ARG_INT,
+ &x_spacing,
+ "Horizontal spacing between elements", "PX"
+ },
+ {
+ "y-spacing", 0,
+ 0,
+ G_OPTION_ARG_INT,
+ &y_spacing,
+ "Vertical spacing between elements", "PX"
+ },
+ { NULL }
+};
+
+static void
+on_stage_resize (ClutterActor *stage,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags,
+ ClutterActor *box)
+{
+ gfloat width, height;
+
+ clutter_actor_box_get_size (allocation, &width, &height);
+
+ if (vertical)
+ clutter_actor_set_height (box, height);
+ else
+ clutter_actor_set_width (box, width);
+}
+
+G_MODULE_EXPORT int
+test_flow_layout_main (int argc, char *argv[])
+{
+ ClutterActor *stage, *box;
+ ClutterLayoutManager *layout;
+ ClutterColor stage_color = { 0xe0, 0xf2, 0xfc, 0xff };
+ ClutterColor box_color = { 255, 255, 255, 255 };
+ GError *error;
+ gint i;
+
+ error = NULL;
+ clutter_init_with_args (&argc, &argv,
+ NULL,
+ entries,
+ NULL,
+ &error);
+ if (error)
+ {
+ g_print ("Unable to run test-flow: %s", error->message);
+ g_error_free (error);
+
+ return EXIT_FAILURE;
+ }
+
+ stage = clutter_stage_get_default ();
+ clutter_stage_set_title (CLUTTER_STAGE (stage), "Flow Layout");
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+ clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
+ clutter_actor_set_size (stage, 640, 480);
+
+ layout = clutter_flow_layout_new (vertical ? CLUTTER_FLOW_VERTICAL
+ : CLUTTER_FLOW_HORIZONTAL);
+ clutter_flow_layout_set_homogeneous (CLUTTER_FLOW_LAYOUT (layout),
+ is_homogeneous);
+ clutter_flow_layout_set_column_spacing (CLUTTER_FLOW_LAYOUT (layout),
+ x_spacing);
+ clutter_flow_layout_set_row_spacing (CLUTTER_FLOW_LAYOUT (layout),
+ y_spacing);
+
+ box = clutter_box_new (layout);
+ clutter_box_set_color (CLUTTER_BOX (box), &box_color);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
+ clutter_actor_set_position (box, 0, 0);
+
+ if (vertical)
+ clutter_actor_set_height (box, 480);
+ else
+ clutter_actor_set_width (box, 640);
+
+ clutter_actor_set_name (box, "box");
+
+ for (i = 0; i < n_rects; i++)
+ {
+ ClutterColor color = { 255, 255, 255, 224 };
+ ClutterActor *rect;
+ gchar *name;
+ gfloat width, height;
+
+ name = g_strdup_printf ("rect%02d", i);
+
+ clutter_color_from_hls (&color,
+ 360.0 / n_rects * i,
+ 0.5,
+ 0.8);
+ rect = clutter_rectangle_new_with_color (&color);
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (box), rect);
+
+ if (random_size)
+ {
+ width = g_random_int_range (50, 100);
+ height = g_random_int_range (50, 100);
+ }
+ else
+ {
+ width = height = 50;
+ }
+
+ clutter_actor_set_size (rect, width, height);
+ clutter_actor_set_name (rect, name);
+
+ g_free (name);
+ }
+
+ g_signal_connect (stage,
+ "allocation-changed", G_CALLBACK (on_stage_resize),
+ box);
+
+ clutter_actor_show_all (stage);
+
+ clutter_main ();
+
+ return EXIT_SUCCESS;
+}