[layout] Initial implementation of FlowLayout
authorEmmanuele Bassi <ebassi@linux.intel.com>
Fri, 18 Sep 2009 16:28:02 +0000 (17:28 +0100)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Wed, 14 Oct 2009 10:31:30 +0000 (11:31 +0100)
FlowLayout is a layout manager that arranges its children in a
reflowing line; the orientation controls the major axis for the
layout: horizontal, for reflow on the Y axis, and vertical, for
reflow on the X axis.

.gitignore
clutter/Makefile.am
clutter/clutter-flow-layout.c [new file with mode: 0644]
clutter/clutter-flow-layout.h [new file with mode: 0644]
clutter/clutter.h
doc/reference/clutter/clutter-docs.xml.in
doc/reference/clutter/clutter-sections.txt
doc/reference/clutter/clutter.types
tests/interactive/Makefile.am
tests/interactive/test-flow.c [new file with mode: 0644]

index e492b15..48bf770 100644 (file)
@@ -134,6 +134,7 @@ TAGS
 /tests/interactive/test-script.json
 /tests/interactive/test-clutter-cairo-flowers
 /tests/interactive/test-box
+/tests/interactive/test-flow
 /tests/conform/stamp-test-conformance
 /tests/conform/test-anchors
 /tests/conform/test-conformance
index 6c8983a..61a7616 100644 (file)
@@ -77,6 +77,7 @@ source_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            \
@@ -146,6 +147,7 @@ source_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             \
diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c
new file mode 100644 (file)
index 0000000..54e9a17
--- /dev/null
@@ -0,0 +1,855 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi@linux.intel.com>
+ */
+
+/**
+ * SECTION:clutter-flow-layout
+ * @short_description: A reflowing layout manager
+ *
+ * #ClutterFlowLayout is a layout manager which implements the following
+ * policy:
+ *
+ * <itemizedlist>
+ * </itemizedlist>
+ *
+ * #ClutterFlowLayout is available since Clutter 1.2
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+
+#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;
+
+  /* cache the preferred size; this way, if we get what we asked
+   * or more then we don't have to recompute the layout
+   */
+  gfloat request_width;
+  gfloat request_height;
+
+  gint max_row_items;
+
+  guint layout_wrap : 1;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_ORIENTATION,
+
+  PROP_COLUMN_SPACING,
+  PROP_ROW_SPACING,
+
+  PROP_MIN_COLUMN_WIDTH,
+  PROP_MAX_COLUMN_WIDTH,
+  PROP_MIN_ROW_HEGHT,
+  PROP_MAX_ROW_HEIGHT,
+
+  PROP_WRAP
+};
+
+G_DEFINE_TYPE (ClutterFlowLayout,
+               clutter_flow_layout,
+               CLUTTER_TYPE_LAYOUT_MANAGER);
+
+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);
+  gfloat max_child_min_width, max_child_natural_width;
+  gfloat row_natural_width;
+
+  max_child_min_width = max_child_natural_width = 0;
+  row_natural_width = 0;
+
+  for (l = children; l != NULL; l = l->next)
+    {
+      ClutterActor *child = l->data;
+      gfloat child_min, child_natural;
+
+      clutter_actor_get_preferred_width (child, for_height,
+                                         &child_min,
+                                         &child_natural);
+
+      max_child_min_width = MAX (max_child_min_width, child_min);
+      max_child_natural_width = MAX (max_child_natural_width, child_natural);
+
+      if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
+        row_natural_width += (child_natural + priv->col_spacing);
+      else
+        row_natural_width = MAX (row_natural_width, max_child_natural_width);
+    }
+
+  g_list_free (children);
+
+  priv->request_width = row_natural_width;
+  priv->col_width = max_child_natural_width;
+
+  if (priv->max_col_width > 0 && priv->col_width > priv->max_col_width)
+    priv->col_width = MAX (priv->max_col_width, max_child_min_width);
+
+  if (priv->col_width < priv->min_col_width)
+    priv->col_width = priv->min_col_width;
+
+  if (min_width_p)
+    *min_width_p = ceilf (max_child_min_width);
+
+  if (nat_width_p)
+    *nat_width_p = ceilf (row_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);
+  gfloat max_child_min_height, max_child_natural_height;
+  gfloat col_natural_height;
+
+  max_child_min_height = max_child_natural_height = 0;
+  col_natural_height = 0;
+
+  for (l = children; l != NULL; l = l->next)
+    {
+      ClutterActor *child = l->data;
+      gfloat child_min, child_natural;
+
+      clutter_actor_get_preferred_height (child, for_width,
+                                          &child_min,
+                                          &child_natural);
+
+      max_child_min_height = MAX (max_child_min_height, child_min);
+      max_child_natural_height = MAX (max_child_natural_height, child_natural);
+
+      if (priv->orientation == CLUTTER_FLOW_VERTICAL)
+        col_natural_height += (child_natural + priv->row_spacing);
+      else
+        col_natural_height = MAX (col_natural_height, max_child_natural_height);
+    }
+
+  g_list_free (children);
+
+  priv->request_height = col_natural_height;
+  priv->row_height = max_child_natural_height;
+
+  if (priv->max_row_height > 0 && priv->row_height > priv->max_row_height)
+    priv->row_height = MAX (priv->max_row_height, max_child_min_height);
+
+  if (priv->row_height < priv->min_row_height)
+    priv->row_height = priv->min_row_height;
+
+  if (min_height_p)
+    *min_height_p = ceilf (max_child_min_height);
+
+  if (nat_height_p)
+    *nat_height_p = ceilf (col_natural_height);
+}
+
+static gint
+compute_lines (ClutterFlowLayout *self,
+               const GList       *children,
+               gfloat             avail_width,
+               gfloat             avail_height)
+{
+  ClutterFlowLayoutPrivate *priv = self->priv;
+  gint items_per_line;
+
+  if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
+    items_per_line = avail_width / (priv->col_width + priv->col_spacing);
+  else
+    items_per_line = avail_height / (priv->row_height + priv->row_spacing);
+
+  return items_per_line;
+}
+
+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_items_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),
+                                  children,
+                                  avail_width, avail_height);
+
+  item_x = item_y = 0;
+
+  line_items_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 child_min, child_natural;
+
+      if (line_items_count == items_per_line)
+        {
+          if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
+            {
+              item_x = 0;
+              item_y += priv->row_height + priv->row_spacing;
+            }
+          else
+            {
+              item_x += priv->col_width + priv->col_spacing;
+              item_y = 0;
+            }
+
+          line_items_count = 0;
+          line_index += 1;
+        }
+
+      clutter_actor_get_preferred_width (child, priv->row_height,
+                                         &child_min,
+                                         &child_natural);
+      item_width = MIN (child_natural, priv->col_width);
+
+      clutter_actor_get_preferred_height (child, item_width,
+                                          &child_min,
+                                          &child_natural);
+      item_height = MIN (child_natural, priv->row_height);
+
+      CLUTTER_NOTE (LAYOUT,
+                    "flow[line:%d, item:%d/%d] = { %.2f, %.2f, %.2f, %.2f }",
+                    line_index, line_items_count, 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 += item_width;
+      else
+        item_y += item_height;
+
+      line_items_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;
+}
+
+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_WRAP:
+      clutter_flow_layout_set_wrap (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_WRAP:
+      g_value_set_boolean (value, priv->layout_wrap);
+      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_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;
+
+  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.
+   * If #ClutterFlowLayout:wrap is set to %TRUE then this property
+   * will control the primary direction of the layout before
+   * wrapping takes place
+   *
+   * 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:wrap:
+   *
+   * Whether the layout should wrap the children to fit them
+   * in the allocation. A non-wrapping layout has a preferred
+   * size of the biggest child in the direction opposite to the
+   * #ClutterFlowLayout:orientation property, and the sum of
+   * the preferred sizes (taking into account spacing) of the
+   * children in the direction of the orientation
+   *
+   * Since: 1.2
+   */
+  pspec = g_param_spec_boolean ("wrap",
+                                "Wrap",
+                                "Whether the layout should wrap",
+                                FALSE,
+                                CLUTTER_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_WRAP, pspec);
+
+  /**
+   * ClutterFlowLayout:column-spacing:
+   *
+   * The spacing between columns, in pixels; the value of this
+   * property is honoured by horizontal non-wrapping layouts and
+   * by vertical wrapping 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-wrapping layouts and
+   * by horizontal wrapping 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;
+}
+
+/**
+ * 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);
+}
+
+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;
+
+      manager = CLUTTER_LAYOUT_MANAGER (layout);
+      clutter_layout_manager_layout_changed (manager);
+
+      g_object_notify (G_OBJECT (layout), "orientation");
+    }
+}
+
+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;
+}
+
+void
+clutter_flow_layout_set_wrap (ClutterFlowLayout *layout,
+                              gboolean           wrap)
+{
+  ClutterFlowLayoutPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout));
+
+  priv = layout->priv;
+
+  if (priv->layout_wrap != wrap)
+    {
+      ClutterLayoutManager *manager;
+
+      priv->layout_wrap = wrap;
+
+      manager = CLUTTER_LAYOUT_MANAGER (layout);
+      clutter_layout_manager_layout_changed (manager);
+
+      g_object_notify (G_OBJECT (layout), "wrap");
+    }
+}
+
+gboolean
+clutter_flow_layout_get_wrap (ClutterFlowLayout *layout)
+{
+  g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), FALSE);
+
+  return layout->priv->layout_wrap;
+}
+
+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");
+    }
+}
+
+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;
+}
+
+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");
+    }
+}
+
+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;
+}
+
+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;
+    }
+
+  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");
+}
+
+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;
+}
+
+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;
+    }
+
+  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");
+}
+
+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 (file)
index 0000000..95aee09
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi@linux.intel.com>
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <clutter/clutter.h> can be included directly."
+#endif
+
+#ifndef __CLUTTER_FLOW_LAYOUT_H__
+#define __CLUTTER_FLOW_LAYOUT_H__
+
+#include <clutter/clutter-layout-manager.h>
+
+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_wrap           (ClutterFlowLayout      *layout,
+                                                               gboolean                wrap);
+gboolean               clutter_flow_layout_get_wrap           (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__ */
index 1696262..5c31317 100644 (file)
@@ -53,6 +53,7 @@
 #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"
index ed019ea..5771a97 100644 (file)
@@ -81,6 +81,7 @@
 
       <xi:include href="xml/clutter-fixed-layout.xml"/>
       <xi:include href="xml/clutter-bin-layout.xml"/>
+      <xi:include href="xml/clutter-flow-layout.xml"/>
     </chapter>
 
   </part>
index cd1bba4..68c3f75 100644 (file)
@@ -1847,3 +1847,37 @@ CLUTTER_LAYOUT_META_GET_CLASS
 <SUBSECTION Private>
 clutter_layout_meta_get_type
 </SECTION>
+
+<SECTION>
+<FILE>clutter-flow-layout</FILE>
+<TITLE>ClutterFlowLayout</TITLE>
+ClutterFlowOrientation
+ClutterFlowLayout
+ClutterFlowLayoutClass
+clutter_flow_layout_new
+clutter_flow_layout_set_orientation
+clutter_flow_layout_get_orientation
+clutter_flow_layout_set_wrap
+clutter_flow_layout_get_wrap
+
+<SUBSECTION>
+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
+
+<SUBSECTION Standard>
+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
+<SUBSECTION Private>
+ClutterFlowLayoutPrivate
+clutter_flow_layout_get_type
+</SECTION>
index 82869b9..07164a4 100644 (file)
@@ -38,3 +38,4 @@ 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
index 3a2215f..193e113 100644 (file)
@@ -44,7 +44,8 @@ UNIT_TESTS = \
         test-text-field.c \
        test-clutter-cairo-flowers.c \
        test-cogl-vertex-buffer.c \
-       test-box.c
+       test-box.c \
+       test-flow.c
 
 if X11_TESTS
 UNIT_TESTS += test-pixmap.c
diff --git a/tests/interactive/test-flow.c b/tests/interactive/test-flow.c
new file mode 100644 (file)
index 0000000..838a5ec
--- /dev/null
@@ -0,0 +1,117 @@
+#include <stdlib.h>
+#include <gmodule.h>
+#include <cairo/cairo.h>
+#include <clutter/clutter.h>
+
+#define N_RECTS         20
+
+static gboolean vertical    = FALSE;
+static gboolean random_size = FALSE;
+static gint     n_rects     = N_RECTS;
+
+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
+  },
+  { NULL }
+};
+
+G_MODULE_EXPORT int
+test_flow_main (int argc, char *argv[])
+{
+  ClutterActor *stage, *box;
+  ClutterLayoutManager *layout;
+  ClutterColor stage_color = { 0xe0, 0xf2, 0xfc, 0xff };
+  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_actor_set_size (stage, 640, 480);
+
+  layout = clutter_flow_layout_new (vertical ? CLUTTER_FLOW_VERTICAL
+                                             : CLUTTER_FLOW_HORIZONTAL);
+
+  box = clutter_box_new (layout);
+  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, 255 };
+      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);
+    }
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  return EXIT_SUCCESS;
+}