diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 12b6ca1b5..660a33dd2 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -86,6 +86,7 @@ source_h = \ $(srcdir)/clutter-fixed-layout.h \ $(srcdir)/clutter-flow-layout.h \ $(srcdir)/clutter-gesture-action.h \ + $(srcdir)/clutter-grid-layout.h \ $(srcdir)/clutter-group.h \ $(srcdir)/clutter-image.h \ $(srcdir)/clutter-input-device.h \ @@ -164,6 +165,7 @@ source_c = \ $(srcdir)/clutter-flatten-effect.c \ $(srcdir)/clutter-flow-layout.c \ $(srcdir)/clutter-gesture-action.c \ + $(srcdir)/clutter-grid-layout.c \ $(srcdir)/clutter-image.c \ $(srcdir)/clutter-input-device.c \ $(srcdir)/clutter-interval.c \ diff --git a/clutter/clutter-enums.h b/clutter/clutter-enums.h index 62ea3bc48..7faf2d07c 100644 --- a/clutter/clutter-enums.h +++ b/clutter/clutter-enums.h @@ -1237,6 +1237,24 @@ typedef enum { /*< prefix=CLUTTER_SCROLL >*/ CLUTTER_SCROLL_BOTH = CLUTTER_SCROLL_HORIZONTALLY | CLUTTER_SCROLL_VERTICALLY } ClutterScrollMode; +/** + * ClutterGridPosition: + * @CLUTTER_GRID_POSITION_LEFT: left position + * @CLUTTER_GRID_POSITION_RIGHT: right position + * @CLUTTER_GRID_POSITION_TOP: top position + * @CLUTTER_GRID_POSITION_BOTTOM: bottom position + * + * Grid position modes. + * + * Since: 1.12 + */ +typedef enum { + CLUTTER_GRID_POSITION_LEFT, + CLUTTER_GRID_POSITION_RIGHT, + CLUTTER_GRID_POSITION_TOP, + CLUTTER_GRID_POSITION_BOTTOM +} ClutterGridPosition; + G_END_DECLS #endif /* __CLUTTER_ENUMS_H__ */ diff --git a/clutter/clutter-grid-layout.c b/clutter/clutter-grid-layout.c new file mode 100644 index 000000000..b339156de --- /dev/null +++ b/clutter/clutter-grid-layout.c @@ -0,0 +1,2199 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2012 Bastian Winkler + * + * 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: + * Bastian Winkler + * + * Based on GtkGrid widget by: + * Matthias Clasen + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "clutter-grid-layout.h" + +#include "clutter-actor-private.h" +#include "clutter-container.h" +#include "clutter-debug.h" +#include "clutter-enum-types.h" +#include "clutter-layout-meta.h" +#include "clutter-private.h" + +/** + * SECTION:clutter-grid-layout + * @Short_description: Pack widgets in a rows and columns + * @Title: ClutterGridLayout + * @See_also: #ClutterTableLayout, #ClutterBoxLayout + * + * #ClutterGridLayout is a layout manager which arranges its child widgets in + * rows and columns. It is a very similar to #ClutterTableLayout and + * #ClutterBoxLayout, but it consistently uses #ClutterActor's + * alignment and expansion flags instead of custom child properties. + * + * Children are added using clutter_grid_layout_attach(). They can span + * multiple rows or columns. It is also possible to add a child next to an + * existing child, using clutter_grid_layout_attach_next_to(). The behaviour of + * #ClutterGridLayout when several children occupy the same grid cell is undefined. + * + * #ClutterGridLayout can be used like a #ClutterBoxLayout by just using + * clutter_actor_add_child(), which will place children next to each other in + * the direction determined by the #ClutterGridLayout:orientation property. + */ + +#define CLUTTER_TYPE_GRID_CHILD (clutter_grid_child_get_type ()) +#define CLUTTER_GRID_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GRID_CHILD, ClutterGridChild)) +#define CLUTTER_IS_GRID_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GRID_CHILD)) +#define CLUTTER_GRID_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayoutPrivate)) + +typedef struct _ClutterGridChild ClutterGridChild; +typedef struct _ClutterLayoutMetaClass ClutterGridChildClass; + +typedef struct _ClutterGridAttach ClutterGridAttach; +typedef struct _ClutterGridLine ClutterGridLine; +typedef struct _ClutterGridLines ClutterGridLines; +typedef struct _ClutterGridLineData ClutterGridLineData; +typedef struct _ClutterGridRequest ClutterGridRequest; + + +struct _ClutterGridAttach +{ + gint pos; + gint span; +}; + +struct _ClutterGridChild +{ + ClutterLayoutMeta parent_instance; + + ClutterGridAttach attach[2]; +}; + +#define CHILD_LEFT(child) ((child)->attach[CLUTTER_ORIENTATION_HORIZONTAL].pos) +#define CHILD_WIDTH(child) ((child)->attach[CLUTTER_ORIENTATION_HORIZONTAL].span) +#define CHILD_TOP(child) ((child)->attach[CLUTTER_ORIENTATION_VERTICAL].pos) +#define CHILD_HEIGHT(child) ((child)->attach[CLUTTER_ORIENTATION_VERTICAL].span) + +/* A ClutterGridLineData struct contains row/column specific parts + * of the grid. + */ +struct _ClutterGridLineData +{ + gfloat spacing; + guint homogeneous : 1; +}; + +struct _ClutterGridLayoutPrivate +{ + ClutterContainer *container; + ClutterOrientation orientation; + + ClutterGridLineData linedata[2]; +}; + +#define ROWS(priv) (&(priv)->linedata[CLUTTER_ORIENTATION_HORIZONTAL]) +#define COLUMNS(priv) (&(priv)->linedata[CLUTTER_ORIENTATION_VERTICAL]) + +/* A ClutterGridLine struct represents a single row or column + * during size requests + */ +struct _ClutterGridLine +{ + gfloat minimum; + gfloat natural; + gfloat position; + gfloat allocation; + + guint need_expand : 1; + guint expand : 1; + guint empty : 1; +}; + +struct _ClutterGridLines +{ + ClutterGridLine *lines; + gint min, max; +}; + +struct _ClutterGridRequest +{ + ClutterGridLayout *grid; + ClutterGridLines lines[2]; +}; + +enum +{ + PROP_0, + + PROP_ORIENTATION, + PROP_ROW_SPACING, + PROP_COLUMN_SPACING, + PROP_ROW_HOMOGENEOUS, + PROP_COLUMN_HOMOGENEOUS, + + PROP_LAST +}; +static GParamSpec *obj_props[PROP_LAST]; + +enum +{ + PROP_CHILD_0, + + PROP_CHILD_LEFT_ATTACH, + PROP_CHILD_TOP_ATTACH, + PROP_CHILD_WIDTH, + PROP_CHILD_HEIGHT, + + PROP_CHILD_LAST +}; +static GParamSpec *child_props[PROP_CHILD_LAST]; + +GType clutter_grid_child_get_type (void); + +G_DEFINE_TYPE (ClutterGridChild, clutter_grid_child, + CLUTTER_TYPE_LAYOUT_META); +G_DEFINE_TYPE (ClutterGridLayout, clutter_grid_layout, + CLUTTER_TYPE_LAYOUT_MANAGER); + + +#define GET_GRID_CHILD(grid, child) \ + (CLUTTER_GRID_CHILD(clutter_layout_manager_get_child_meta \ + (CLUTTER_LAYOUT_MANAGER((grid)),\ + CLUTTER_GRID_LAYOUT((grid))->priv->container,(child)))) + +static void +grid_attach (ClutterGridLayout *self, + ClutterActor *actor, + gint left, + gint top, + gint width, + gint height) +{ + ClutterGridChild *grid_child; + + grid_child = GET_GRID_CHILD (self, actor); + + CHILD_LEFT (grid_child) = left; + CHILD_TOP (grid_child) = top; + CHILD_WIDTH (grid_child) = width; + CHILD_HEIGHT (grid_child) = height; +} + +/* Find the position 'touching' existing + * children. @orientation and @max determine + * from which direction to approach (horizontal + * + max = right, vertical + !max = top, etc). + * @op_pos, @op_span determine the rows/columns + * in which the touching has to happen. + */ +static gint +find_attach_position (ClutterGridLayout *self, + ClutterOrientation orientation, + gint op_pos, + gint op_span, + gboolean max) +{ + ClutterGridLayoutPrivate *priv = self->priv; + ClutterGridChild *grid_child; + ClutterGridAttach *attach; + ClutterGridAttach *opposite; + ClutterActorIter iter; + ClutterActor *child; + gint pos; + gboolean hit; + + if (max) + pos = -G_MAXINT; + else + pos = G_MAXINT; + + hit = FALSE; + + if (!priv->container) + return -1; + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + { + grid_child = GET_GRID_CHILD (self, child); + + attach = &grid_child->attach[orientation]; + opposite = &grid_child->attach[1 - orientation]; + + /* check if the ranges overlap */ + if (opposite->pos <= op_pos + op_span && op_pos <= opposite->pos + opposite->span) + { + hit = TRUE; + + if (max) + pos = MAX (pos, attach->pos + attach->span); + else + pos = MIN (pos, attach->pos); + } + } + + if (!hit) + pos = 0; + + return pos; +} +static void +grid_attach_next_to (ClutterGridLayout *layout, + ClutterActor *child, + ClutterActor *sibling, + ClutterGridPosition side, + gint width, + gint height) +{ + ClutterGridChild *grid_sibling; + gint left, top; + + if (sibling) + { + grid_sibling = GET_GRID_CHILD (layout, sibling); + + switch (side) + { + case CLUTTER_GRID_POSITION_LEFT: + left = CHILD_LEFT (grid_sibling) - width; + top = CHILD_TOP (grid_sibling); + break; + + case CLUTTER_GRID_POSITION_RIGHT: + left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling); + top = CHILD_TOP (grid_sibling); + break; + + case CLUTTER_GRID_POSITION_TOP: + left = CHILD_LEFT (grid_sibling); + top = CHILD_TOP (grid_sibling) - height; + break; + + case CLUTTER_GRID_POSITION_BOTTOM: + left = CHILD_LEFT (grid_sibling); + top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling); + break; + + default: + g_assert_not_reached (); + } + } + else + { + switch (side) + { + case CLUTTER_GRID_POSITION_LEFT: + left = find_attach_position (layout, CLUTTER_ORIENTATION_HORIZONTAL, + 0, height, FALSE); + left -= width; + top = 0; + break; + + case CLUTTER_GRID_POSITION_RIGHT: + left = find_attach_position (layout, CLUTTER_ORIENTATION_HORIZONTAL, + 0, height, TRUE); + top = 0; + break; + + case CLUTTER_GRID_POSITION_TOP: + left = 0; + top = find_attach_position (layout, CLUTTER_ORIENTATION_VERTICAL, + 0, width, FALSE); + top -= height; + break; + + case CLUTTER_GRID_POSITION_BOTTOM: + left = 0; + top = find_attach_position (layout, CLUTTER_ORIENTATION_VERTICAL, + 0, width, TRUE); + break; + + default: + g_assert_not_reached (); + } + } + + grid_attach (layout, child, left, top, width, height); +} + +static void +clutter_grid_request_update_child_attach (ClutterGridRequest *request, + ClutterActor *actor) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridChild *grid_child; + + grid_child = GET_GRID_CHILD (request->grid, actor); + + if (CHILD_LEFT (grid_child) == -1 || CHILD_TOP (grid_child) == -1) + { + ClutterGridPosition side; + ClutterActor *sibling; + + if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL) + { + ClutterTextDirection td; + gboolean rtl; + ClutterActor *container = CLUTTER_ACTOR (priv->container); + + td = clutter_actor_get_text_direction (container); + rtl = (td == CLUTTER_TEXT_DIRECTION_RTL) ? TRUE : FALSE; + side = rtl ? CLUTTER_GRID_POSITION_RIGHT : CLUTTER_GRID_POSITION_LEFT; + } + else + { + /* XXX: maybe we should also add a :pack-start property to modify + * this */ + side = CLUTTER_GRID_POSITION_BOTTOM; + } + + sibling = clutter_actor_get_previous_sibling (actor); + grid_attach_next_to (request->grid, actor, sibling, side, + CHILD_WIDTH (grid_child), + CHILD_HEIGHT (grid_child)); + } +} + +static void +clutter_grid_request_update_attach (ClutterGridRequest *request) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterActorIter iter; + ClutterActor *child; + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + clutter_grid_request_update_child_attach (request, child); +} + +/* Calculates the min and max numbers for both orientations. + */ +static void +clutter_grid_request_count_lines (ClutterGridRequest *request) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridChild *grid_child; + ClutterGridAttach *attach; + ClutterActorIter iter; + ClutterActor *child; + gint min[2]; + gint max[2]; + + min[0] = min[1] = G_MAXINT; + max[0] = max[1] = G_MININT; + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + { + grid_child = GET_GRID_CHILD (request->grid, child); + attach = grid_child->attach; + + min[0] = MIN (min[0], attach[0].pos); + max[0] = MAX (max[0], attach[0].pos + attach[0].span); + min[1] = MIN (min[1], attach[1].pos); + max[1] = MAX (max[1], attach[1].pos + attach[1].span); + } + + request->lines[0].min = min[0]; + request->lines[0].max = max[0]; + request->lines[1].min = min[1]; + request->lines[1].max = max[1]; +} + +/* Sets line sizes to 0 and marks lines as expand + * if they have a non-spanning expanding child. + */ +static void +clutter_grid_request_init (ClutterGridRequest *request, + ClutterOrientation orientation) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridChild *grid_child; + ClutterGridAttach *attach; + ClutterGridLines *lines; + ClutterActorIter iter; + ClutterActor *child; + gint i; + + lines = &request->lines[orientation]; + + for (i = 0; i < lines->max - lines->min; i++) + { + lines->lines[i].minimum = 0; + lines->lines[i].natural = 0; + lines->lines[i].expand = FALSE; + } + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + { + grid_child = GET_GRID_CHILD (request->grid, child); + attach = &grid_child->attach[orientation]; + if (attach->span == 1 && clutter_actor_needs_expand (child, orientation)) + lines->lines[attach->pos - lines->min].expand = TRUE; + } +} + +/* Sums allocations for lines spanned by child and their spacing. + */ +static gfloat +compute_allocation_for_child (ClutterGridRequest *request, + ClutterActor *child, + ClutterOrientation orientation) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridChild *grid_child; + ClutterGridLineData *linedata; + ClutterGridLines *lines; + ClutterGridLine *line; + ClutterGridAttach *attach; + gfloat size; + gint i; + + grid_child = GET_GRID_CHILD (request->grid, child); + linedata = &priv->linedata[orientation]; + lines = &request->lines[orientation]; + attach = &grid_child->attach[orientation]; + + size = (attach->span - 1) * linedata->spacing; + for (i = 0; i < attach->span; i++) + { + line = &lines->lines[attach->pos - lines->min + i]; + size += line->allocation; + } + + return size; +} + +static void +compute_request_for_child (ClutterGridRequest *request, + ClutterActor *child, + ClutterOrientation orientation, + gboolean contextual, + gfloat *minimum, + gfloat *natural) +{ + if (contextual) + { + gfloat size; + + size = compute_allocation_for_child (request, child, 1 - orientation); + if (orientation == CLUTTER_ORIENTATION_HORIZONTAL) + clutter_actor_get_preferred_width (child, size, minimum, natural); + else + clutter_actor_get_preferred_height (child, size, minimum, natural); + } + else + { + if (orientation == CLUTTER_ORIENTATION_VERTICAL) + clutter_actor_get_preferred_width (child, -1, minimum, natural); + else + clutter_actor_get_preferred_height (child, -1, minimum, natural); + } +} + +/* Sets requisition to max. of non-spanning children. + * If contextual is TRUE, requires allocations of + * lines in the opposite orientation to be set. + */ +static void +clutter_grid_request_non_spanning (ClutterGridRequest *request, + ClutterOrientation orientation, + gboolean contextual) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridChild *grid_child; + ClutterGridAttach *attach; + ClutterGridLines *lines; + ClutterGridLine *line; + ClutterActorIter iter; + ClutterActor *child; + gfloat minimum; + gfloat natural; + + lines = &request->lines[orientation]; + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + { + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + grid_child = GET_GRID_CHILD (request->grid, child); + + attach = &grid_child->attach[orientation]; + if (attach->span != 1) + continue; + + compute_request_for_child (request, child, orientation, contextual, &minimum, &natural); + + line = &lines->lines[attach->pos - lines->min]; + line->minimum = MAX (line->minimum, minimum); + line->natural = MAX (line->natural, natural); + } +} + +/* Enforce homogeneous sizes. + */ +static void +clutter_grid_request_homogeneous (ClutterGridRequest *request, + ClutterOrientation orientation) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridLineData *linedata; + ClutterGridLines *lines; + gfloat minimum, natural; + gint i; + + linedata = &priv->linedata[orientation]; + lines = &request->lines[orientation]; + + if (!linedata->homogeneous) + return; + + minimum = 0.0f; + natural = 0.0f; + + for (i = 0; i < lines->max - lines->min; i++) + { + minimum = MAX (minimum, lines->lines[i].minimum); + natural = MAX (natural, lines->lines[i].natural); + } + + for (i = 0; i < lines->max - lines->min; i++) + { + lines->lines[i].minimum = minimum; + lines->lines[i].natural = natural; + } +} + +/* Deals with spanning children. + * Requires expand fields of lines to be set for + * non-spanning children. + */ +static void +clutter_grid_request_spanning (ClutterGridRequest *request, + ClutterOrientation orientation, + gboolean contextual) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridChild *grid_child; + ClutterActor *child; + ClutterActorIter iter; + ClutterGridAttach *attach; + ClutterGridLineData *linedata; + ClutterGridLines *lines; + ClutterGridLine *line; + gfloat minimum; + gfloat natural; + gint span_minimum; + gint span_natural; + gint span_expand; + gboolean force_expand; + gint extra; + gint expand; + gint line_extra; + gint i; + + linedata = &priv->linedata[orientation]; + lines = &request->lines[orientation]; + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + { + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + grid_child = GET_GRID_CHILD (request->grid, child); + + attach = &grid_child->attach[orientation]; + if (attach->span == 1) + continue; + + compute_request_for_child (request, child, orientation, contextual, + &minimum, &natural); + + span_minimum = (attach->span - 1) * linedata->spacing; + span_natural = (attach->span - 1) * linedata->spacing; + span_expand = 0; + force_expand = FALSE; + for (i = 0; i < attach->span; i++) + { + line = &lines->lines[attach->pos - lines->min + i]; + span_minimum += line->minimum; + span_natural += line->natural; + if (line->expand) + span_expand += 1; + } + if (span_expand == 0) + { + span_expand = attach->span; + force_expand = TRUE; + } + + /* If we need to request more space for this child to fill + * its requisition, then divide up the needed space amongst the + * lines it spans, favoring expandable lines if any. + * + * When doing homogeneous allocation though, try to keep the + * line allocations even, since we're going to force them to + * be the same anyway, and we don't want to introduce unnecessary + * extra space. + */ + if (span_minimum < minimum) + { + if (linedata->homogeneous) + { + gint total, m; + + total = minimum - (attach->span - 1) * linedata->spacing; + m = total / attach->span + (total % attach->span ? 1 : 0); + for (i = 0; i < attach->span; i++) + { + line = &lines->lines[attach->pos - lines->min + i]; + line->minimum = MAX(line->minimum, m); + } + } + else + { + extra = minimum - span_minimum; + expand = span_expand; + for (i = 0; i < attach->span; i++) + { + line = &lines->lines[attach->pos - lines->min + i]; + if (force_expand || line->expand) + { + line_extra = extra / expand; + line->minimum += line_extra; + extra -= line_extra; + expand -= 1; + } + } + } + } + + if (span_natural < natural) + { + if (linedata->homogeneous) + { + gint total, n; + + total = natural - (attach->span - 1) * linedata->spacing; + n = total / attach->span + (total % attach->span ? 1 : 0); + for (i = 0; i < attach->span; i++) + { + line = &lines->lines[attach->pos - lines->min + i]; + line->natural = MAX(line->natural, n); + } + } + else + { + extra = natural - span_natural; + expand = span_expand; + for (i = 0; i < attach->span; i++) + { + line = &lines->lines[attach->pos - lines->min + i]; + if (force_expand || line->expand) + { + line_extra = extra / expand; + line->natural += line_extra; + extra -= line_extra; + expand -= 1; + } + } + } + } + } +} + +/* Marks empty and expanding lines and counts them. + */ +static void +clutter_grid_request_compute_expand (ClutterGridRequest *request, + ClutterOrientation orientation, + gint *nonempty_lines, + gint *expand_lines) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridChild *grid_child; + ClutterGridAttach *attach; + ClutterActorIter iter; + ClutterActor *child; + gint i; + ClutterGridLines *lines; + ClutterGridLine *line; + gboolean has_expand; + gint expand; + gint empty; + + lines = &request->lines[orientation]; + + for (i = 0; i < lines->max - lines->min; i++) + { + lines->lines[i].need_expand = FALSE; + lines->lines[i].expand = FALSE; + lines->lines[i].empty = TRUE; + } + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + { + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + grid_child = GET_GRID_CHILD (request->grid, child); + + attach = &grid_child->attach[orientation]; + if (attach->span != 1) + continue; + + line = &lines->lines[attach->pos - lines->min]; + line->empty = FALSE; + if (clutter_actor_needs_expand (child, orientation)) + line->expand = TRUE; + } + + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + { + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + grid_child = GET_GRID_CHILD (request->grid, child); + + attach = &grid_child->attach[orientation]; + if (attach->span == 1) + continue; + + has_expand = FALSE; + for (i = 0; i < attach->span; i++) + { + line = &lines->lines[attach->pos - lines->min + i]; + line->empty = FALSE; + if (line->expand) + has_expand = TRUE; + } + + if (!has_expand && clutter_actor_needs_expand (child, orientation)) + { + for (i = 0; i < attach->span; i++) + { + line = &lines->lines[attach->pos - lines->min + i]; + line->need_expand = TRUE; + } + } + } + + empty = 0; + expand = 0; + for (i = 0; i < lines->max - lines->min; i++) + { + line = &lines->lines[i]; + + if (line->need_expand) + line->expand = TRUE; + + if (line->empty) + empty += 1; + + if (line->expand) + expand += 1; + } + + if (nonempty_lines) + *nonempty_lines = lines->max - lines->min - empty; + + if (expand_lines) + *expand_lines = expand; +} + +/* Sums the minimum and natural fields of lines and their spacing. + */ +static void +clutter_grid_request_sum (ClutterGridRequest *request, + ClutterOrientation orientation, + gfloat *minimum, + gfloat *natural) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridLineData *linedata; + ClutterGridLines *lines; + gint i; + gfloat min, nat; + gint nonempty; + + clutter_grid_request_compute_expand (request, orientation, &nonempty, NULL); + + linedata = &priv->linedata[orientation]; + lines = &request->lines[orientation]; + + min = 0; + nat = 0; + if (nonempty > 0) + { + min = (nonempty - 1) * linedata->spacing; + nat = (nonempty - 1) * linedata->spacing; + } + + for (i = 0; i < lines->max - lines->min; i++) + { + min += lines->lines[i].minimum; + nat += lines->lines[i].natural; + } + + if (minimum) + *minimum = min; + + if (natural) + *natural = nat; +} + +/* Computes minimum and natural fields of lines. + * When contextual is TRUE, requires allocation of + * lines in the opposite orientation to be set. + */ +static void +clutter_grid_request_run (ClutterGridRequest *request, + ClutterOrientation orientation, + gboolean contextual) +{ + clutter_grid_request_init (request, orientation); + clutter_grid_request_non_spanning (request, orientation, contextual); + clutter_grid_request_homogeneous (request, orientation); + clutter_grid_request_spanning (request, orientation, contextual); + clutter_grid_request_homogeneous (request, orientation); +} + +typedef struct _RequestedSize +{ + gpointer data; + + gfloat minimum_size; + gfloat natural_size; +} RequestedSize; + + +/* Pulled from gtksizerequest.c from Gtk+ */ +static gint +compare_gap (gconstpointer p1, + gconstpointer p2, + gpointer data) +{ + RequestedSize *sizes = data; + const guint *c1 = p1; + const guint *c2 = p2; + + const gint d1 = MAX (sizes[*c1].natural_size - + sizes[*c1].minimum_size, + 0); + const gint d2 = MAX (sizes[*c2].natural_size - + sizes[*c2].minimum_size, + 0); + + gint delta = (d2 - d1); + + if (0 == delta) + delta = (*c2 - *c1); + + return delta; +} + +/* + * distribute_natural_allocation: + * @extra_space: Extra space to redistribute among children after subtracting + * minimum sizes and any child padding from the overall allocation + * @n_requested_sizes: Number of requests to fit into the allocation + * @sizes: An array of structs with a client pointer and a minimum/natural size + * in the orientation of the allocation. + * + * Distributes @extra_space to child @sizes by bringing smaller + * children up to natural size first. + * + * The remaining space will be added to the @minimum_size member of the + * RequestedSize struct. If all sizes reach their natural size then + * the remaining space is returned. + * + * Returns: The remainder of @extra_space after redistributing space + * to @sizes. + * + * Pulled from gtksizerequest.c from Gtk+ + */ +static gint +distribute_natural_allocation (gint extra_space, + guint n_requested_sizes, + RequestedSize *sizes) +{ + guint *spreading; + gint i; + + g_return_val_if_fail (extra_space >= 0, 0); + + spreading = g_newa (guint, n_requested_sizes); + + for (i = 0; i < n_requested_sizes; i++) + spreading[i] = i; + + /* Distribute the container's extra space c_gap. We want to assign + * this space such that the sum of extra space assigned to children + * (c^i_gap) is equal to c_cap. The case that there's not enough + * space for all children to take their natural size needs some + * attention. The goals we want to achieve are: + * + * a) Maximize number of children taking their natural size. + * b) The allocated size of children should be a continuous + * function of c_gap. That is, increasing the container size by + * one pixel should never make drastic changes in the distribution. + * c) If child i takes its natural size and child j doesn't, + * child j should have received at least as much gap as child i. + * + * The following code distributes the additional space by following + * these rules. + */ + + /* Sort descending by gap and position. */ + g_qsort_with_data (spreading, + n_requested_sizes, sizeof (guint), + compare_gap, sizes); + + /* Distribute available space. + * This master piece of a loop was conceived by Behdad Esfahbod. + */ + for (i = n_requested_sizes - 1; extra_space > 0 && i >= 0; --i) + { + /* Divide remaining space by number of remaining children. + * Sort order and reducing remaining space by assigned space + * ensures that space is distributed equally. + */ + gint glue = (extra_space + i) / (i + 1); + gint gap = sizes[(spreading[i])].natural_size + - sizes[(spreading[i])].minimum_size; + + gint extra = MIN (glue, gap); + + sizes[spreading[i]].minimum_size += extra; + + extra_space -= extra; + } + + return extra_space; +} + +/* Requires that the minimum and natural fields of lines + * have been set, computes the allocation field of lines + * by distributing total_size among lines. + */ +static void +clutter_grid_request_allocate (ClutterGridRequest *request, + ClutterOrientation orientation, + gfloat total_size) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridLineData *linedata; + ClutterGridLines *lines; + ClutterGridLine *line; + gint nonempty; + gint expand; + gint i, j; + RequestedSize *sizes; + gint extra; + gint rest; + gint size; + + clutter_grid_request_compute_expand (request, orientation, &nonempty, &expand); + + if (nonempty == 0) + return; + + linedata = &priv->linedata[orientation]; + lines = &request->lines[orientation]; + + size = total_size - (nonempty - 1) * linedata->spacing; + + if (linedata->homogeneous) + { + extra = size / nonempty; + rest = size % nonempty; + + for (i = 0; i < lines->max - lines->min; i++) + { + line = &lines->lines[i]; + if (line->empty) + continue; + + line->allocation = extra; + if (rest > 0) + { + line->allocation += 1; + rest -= 1; + } + } + } + else + { + sizes = g_newa (RequestedSize, nonempty); + + j = 0; + for (i = 0; i < lines->max - lines->min; i++) + { + line = &lines->lines[i]; + if (line->empty) + continue; + + size -= line->minimum; + + sizes[j].minimum_size = line->minimum; + sizes[j].natural_size = line->natural; + sizes[j].data = line; + j++; + } + + size = distribute_natural_allocation (MAX (0, size), nonempty, sizes); + + if (expand > 0) + { + extra = size / expand; + rest = size % expand; + } + else + { + extra = 0; + rest = 0; + } + + j = 0; + for (i = 0; i < lines->max - lines->min; i++) + { + line = &lines->lines[i]; + if (line->empty) + continue; + + g_assert (line == sizes[j].data); + + line->allocation = sizes[j].minimum_size; + if (line->expand) + { + line->allocation += extra; + if (rest > 0) + { + line->allocation += 1; + rest -= 1; + } + } + + j++; + } + } +} + +/* Computes the position fields from allocation and spacing. + */ +static void +clutter_grid_request_position (ClutterGridRequest *request, + ClutterOrientation orientation) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridLineData *linedata; + ClutterGridLines *lines; + ClutterGridLine *line; + gfloat position; + gint i; + + linedata = &priv->linedata[orientation]; + lines = &request->lines[orientation]; + + position = 0.f; + for (i = 0; i < lines->max - lines->min; i++) + { + line = &lines->lines[i]; + if (!line->empty) + { + line->position = position; + position += line->allocation + linedata->spacing; + } + } +} + +static void +clutter_grid_child_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterGridChild *grid_child = CLUTTER_GRID_CHILD (gobject); + ClutterLayoutManager *manager; + + manager = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (gobject)); + + switch (prop_id) + { + case PROP_CHILD_LEFT_ATTACH: + CHILD_LEFT (grid_child) = g_value_get_int (value); + clutter_layout_manager_layout_changed (manager); + break; + + case PROP_CHILD_TOP_ATTACH: + CHILD_TOP (grid_child) = g_value_get_int (value); + clutter_layout_manager_layout_changed (manager); + break; + + case PROP_CHILD_WIDTH: + CHILD_WIDTH (grid_child) = g_value_get_int (value); + clutter_layout_manager_layout_changed (manager); + break; + + case PROP_CHILD_HEIGHT: + CHILD_HEIGHT (grid_child) = g_value_get_int (value); + clutter_layout_manager_layout_changed (manager); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_grid_child_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterGridChild *grid_child = CLUTTER_GRID_CHILD (gobject); + + switch (prop_id) + { + case PROP_CHILD_LEFT_ATTACH: + g_value_set_int (value, CHILD_LEFT (grid_child)); + break; + + case PROP_CHILD_TOP_ATTACH: + g_value_set_int (value, CHILD_TOP (grid_child)); + break; + + case PROP_CHILD_WIDTH: + g_value_set_int (value, CHILD_WIDTH (grid_child)); + break; + + case PROP_CHILD_HEIGHT: + g_value_set_int (value, CHILD_HEIGHT (grid_child)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_grid_child_class_init (ClutterGridChildClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = clutter_grid_child_set_property; + gobject_class->get_property = clutter_grid_child_get_property; + + child_props[PROP_CHILD_LEFT_ATTACH] = + g_param_spec_int ("left-attach", + P_("Left attachment"), + P_("The column number to attach the left side of the " + "child to"), + -G_MAXINT, G_MAXINT, 0, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + child_props[PROP_CHILD_TOP_ATTACH] = + g_param_spec_int ("top-attach", + P_("Top attachment"), + P_("The row number to attach the top side of a child " + "widget to"), + -G_MAXINT, G_MAXINT, 0, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + child_props[PROP_CHILD_WIDTH] = + g_param_spec_int ("width", + P_("Width"), + P_("The number of columns that a child spans"), + -G_MAXINT, G_MAXINT, 1, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + child_props[PROP_CHILD_HEIGHT] = + g_param_spec_int ("height", + P_("Height"), + P_("The number of rows that a child spans"), + -G_MAXINT, G_MAXINT, 1, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + g_object_class_install_properties (gobject_class, PROP_CHILD_LAST, + child_props); +} + +static void +clutter_grid_child_init (ClutterGridChild *self) +{ + CHILD_LEFT (self) = -1; + CHILD_TOP (self) = -1; + CHILD_WIDTH (self) = 1; + CHILD_HEIGHT (self) = 1; +} + +static void +clutter_grid_layout_set_container (ClutterLayoutManager *self, + ClutterContainer *container) +{ + ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv; + ClutterLayoutManagerClass *parent_class; + + 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_ORIENTATION_VERTICAL + ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH + : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT; + clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container), + request_mode); + } + + parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_grid_layout_parent_class); + parent_class->set_container (self, container); +} + +static void +clutter_grid_layout_get_preferred_width (ClutterLayoutManager *self, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *nat_width_p) +{ + ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv; + ClutterGridRequest request; + ClutterGridLines *lines; + + if (min_width_p) + *min_width_p = 0.0f; + if (nat_width_p) + *nat_width_p = 0.0f; + + request.grid = CLUTTER_GRID_LAYOUT (self); + clutter_grid_request_update_attach (&request); + clutter_grid_request_count_lines (&request); + lines = &request.lines[priv->orientation]; + lines->lines = g_newa (ClutterGridLine, lines->max - lines->min); + memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine)); + + clutter_grid_request_run (&request, priv->orientation, FALSE); + clutter_grid_request_sum (&request, priv->orientation, + min_width_p, nat_width_p); +} + +static void +clutter_grid_layout_get_preferred_height (ClutterLayoutManager *self, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *nat_height_p) +{ + ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv; + ClutterGridRequest request; + ClutterGridLines *lines; + + if (min_height_p) + *min_height_p = 0.0f; + if (nat_height_p) + *nat_height_p = 0.0f; + + request.grid = CLUTTER_GRID_LAYOUT (self); + clutter_grid_request_update_attach (&request); + clutter_grid_request_count_lines (&request); + lines = &request.lines[priv->orientation]; + lines->lines = g_newa (ClutterGridLine, lines->max - lines->min); + memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine)); + + clutter_grid_request_run (&request, priv->orientation, FALSE); + clutter_grid_request_sum (&request, priv->orientation, + min_height_p, nat_height_p); +} + +static void +allocate_child (ClutterGridRequest *request, + ClutterOrientation orientation, + ClutterGridChild *child, + gfloat *position, + gfloat *size) +{ + ClutterGridLayoutPrivate *priv = request->grid->priv; + ClutterGridLineData *linedata; + ClutterGridLines *lines; + ClutterGridLine *line; + ClutterGridAttach *attach; + gint i; + + linedata = &priv->linedata[orientation]; + lines = &request->lines[orientation]; + attach = &child->attach[orientation]; + + *position = lines->lines[attach->pos - lines->min].position; + + *size = (attach->span - 1) * linedata->spacing; + for (i = 0; i < attach->span; i++) + { + line = &lines->lines[attach->pos - lines->min + i]; + *size += line->allocation; + } +} + +#define GET_SIZE(allocation, orientation) \ + (orientation == CLUTTER_ORIENTATION_HORIZONTAL \ + ? clutter_actor_box_get_width ((allocation)) \ + : clutter_actor_box_get_height ((allocation))) + +static void +clutter_grid_layout_allocate (ClutterLayoutManager *layout, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags) +{ + ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (layout); + ClutterGridLayoutPrivate *priv = self->priv; + ClutterGridRequest request; + ClutterGridLines *lines; + ClutterActorIter iter; + ClutterActor *child; + gboolean use_animations; + ClutterAnimationMode mode; + guint duration, delay; + + request.grid = self; + + clutter_grid_request_update_attach (&request); + clutter_grid_request_count_lines (&request); + lines = &request.lines[0]; + lines->lines = g_newa (ClutterGridLine, lines->max - lines->min); + memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine)); + lines = &request.lines[1]; + lines->lines = g_newa (ClutterGridLine, lines->max - lines->min); + memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine)); + + clutter_grid_request_run (&request, 1 - priv->orientation, FALSE); + clutter_grid_request_allocate (&request, 1 - priv->orientation, GET_SIZE (allocation, 1 - priv->orientation)); + clutter_grid_request_run (&request, priv->orientation, TRUE); + clutter_grid_request_allocate (&request, priv->orientation, GET_SIZE (allocation, priv->orientation)); + + clutter_grid_request_position (&request, 0); + clutter_grid_request_position (&request, 1); + + use_animations = clutter_layout_manager_get_easing_state (layout, + &mode, + &duration, + &delay); + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container)); + while (clutter_actor_iter_next (&iter, &child)) + { + ClutterActorBox child_allocation; + gfloat x, y, width, height; + ClutterGridChild *grid_child; + + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + grid_child = GET_GRID_CHILD (self, child); + allocate_child (&request, CLUTTER_ORIENTATION_HORIZONTAL, grid_child, + &x, &width); + allocate_child (&request, CLUTTER_ORIENTATION_VERTICAL, grid_child, + &y, &height); + x += allocation->x1; + y += allocation->y1; + clutter_actor_box_set_origin (&child_allocation, x, y); + clutter_actor_box_set_size (&child_allocation, width, height); + + CLUTTER_NOTE (LAYOUT, "Allocation for %s { %.2f, %.2f - %.2f x %.2f }", + _clutter_actor_get_debug_name (child), + x, y, width, height); + + if (use_animations) + { + clutter_actor_save_easing_state (child); + clutter_actor_set_easing_mode (child, mode); + clutter_actor_set_easing_duration (child, duration); + clutter_actor_set_easing_delay (child, delay); + } + + clutter_actor_allocate (child, &child_allocation, flags); + + if (use_animations) + clutter_actor_restore_easing_state (child); + } +} + +static GType +clutter_grid_layout_get_child_meta_type (ClutterLayoutManager *self) +{ + return CLUTTER_TYPE_GRID_CHILD; +} + +static void +clutter_grid_layout_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (gobject); + + switch (prop_id) + { + case PROP_ORIENTATION: + clutter_grid_layout_set_orientation (self, g_value_get_enum (value)); + break; + + case PROP_ROW_SPACING: + clutter_grid_layout_set_row_spacing (self, g_value_get_uint (value)); + break; + + case PROP_COLUMN_SPACING: + clutter_grid_layout_set_column_spacing (self, g_value_get_uint (value)); + break; + + case PROP_ROW_HOMOGENEOUS: + clutter_grid_layout_set_row_homogeneous (self, + g_value_get_boolean (value)); + break; + + case PROP_COLUMN_HOMOGENEOUS: + clutter_grid_layout_set_column_homogeneous (self, + g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_grid_layout_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (gobject)->priv; + + switch (prop_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, priv->orientation); + break; + + case PROP_ROW_SPACING: + g_value_set_uint (value, COLUMNS (priv)->spacing); + break; + + case PROP_COLUMN_SPACING: + g_value_set_uint (value, ROWS (priv)->spacing); + break; + + case PROP_ROW_HOMOGENEOUS: + g_value_set_boolean (value, COLUMNS (priv)->homogeneous); + break; + + case PROP_COLUMN_HOMOGENEOUS: + g_value_set_boolean (value, ROWS (priv)->homogeneous); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_grid_layout_class_init (ClutterGridLayoutClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ClutterLayoutManagerClass *layout_class; + + layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ClutterGridLayoutPrivate)); + + object_class->set_property = clutter_grid_layout_set_property; + object_class->get_property = clutter_grid_layout_get_property; + + layout_class->set_container = clutter_grid_layout_set_container; + layout_class->get_preferred_width = clutter_grid_layout_get_preferred_width; + layout_class->get_preferred_height = clutter_grid_layout_get_preferred_height; + layout_class->allocate = clutter_grid_layout_allocate; + layout_class->get_child_meta_type = clutter_grid_layout_get_child_meta_type; + + /** + * ClutterGridLayout:orientation: + * + * The orientation of the layout, either horizontal or vertical + * + * Since: 1.12 + */ + obj_props[PROP_ORIENTATION] = + g_param_spec_enum ("orientation", + P_("Orientation"), + P_("The orientation of the layout"), + CLUTTER_TYPE_ORIENTATION, + CLUTTER_ORIENTATION_HORIZONTAL, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + /** + * ClutterGridLayout:row-spacing: + * + * The amount of space in pixels between two consecutive rows + * + * Since: 1.12 + */ + obj_props[PROP_ROW_SPACING] = + g_param_spec_uint ("row-spacing", + P_("Row spacing"), + P_("The amount of space between two consecutive rows"), + 0, G_MAXUINT, 0, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + /** + * ClutterGridLayout:column-spacing: + * + * The amount of space in pixels between two consecutive columns + * + * Since: 1.12 + */ + obj_props[PROP_COLUMN_SPACING] = + g_param_spec_uint ("column-spacing", + P_("Column spacing"), + P_("The amount of space between two consecutive " + "columns"), + 0, G_MAXUINT, 0, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + /** + * ClutterGridLayout:row-homogeneous: + * + * Whether all rows of the layout should have the same height + * + * Since: 1.12 + */ + obj_props[PROP_ROW_HOMOGENEOUS] = + g_param_spec_boolean ("row-homogeneous", + P_("Row Homogeneous"), + P_("If TRUE, the rows are all the same height"), + FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + /** + * ClutterGridLayout:column-homogeneous: + * + * Whether all columns of the layout should have the same width + * + * Since: 1.12 + */ + obj_props[PROP_COLUMN_HOMOGENEOUS] = + g_param_spec_boolean ("column-homogeneous", + P_("Column Homogeneous"), + P_("If TRUE, the columns are all the same width"), + FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + g_object_class_install_properties (object_class, PROP_LAST, obj_props); +} + +static void +clutter_grid_layout_init (ClutterGridLayout *self) +{ + ClutterGridLayoutPrivate *priv; + + self->priv = priv = CLUTTER_GRID_LAYOUT_GET_PRIVATE (self); + + priv->orientation = CLUTTER_ORIENTATION_HORIZONTAL; + + priv->linedata[0].spacing = 0; + priv->linedata[1].spacing = 0; + + priv->linedata[0].homogeneous = FALSE; + priv->linedata[1].homogeneous = FALSE; +} + +/** + * clutter_grid_layout_new: + * + * Creates a new #ClutterGridLayout + * + * Return value: the new #ClutterGridLayout + */ +ClutterLayoutManager * +clutter_grid_layout_new (void) +{ + return g_object_new (CLUTTER_TYPE_GRID_LAYOUT, NULL); +} + +/** + * clutter_grid_layout_attach: + * @layout: a #ClutterGridLayout + * @child: the #ClutterActor to add + * @left: the column number to attach the left side of @child to + * @top: the row number to attach the top side of @child to + * @width: the number of columns that @child will span + * @height: the number of rows that @child will span + * + * Adds a widget to the grid. + * + * The position of @child is determined by @left and @top. The + * number of 'cells' that @child will occupy is determined by + * @width and @height. + * + * Since: 1.12 + */ +void +clutter_grid_layout_attach (ClutterGridLayout *layout, + ClutterActor *child, + gint left, + gint top, + gint width, + gint height) +{ + ClutterGridLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); + + priv = layout->priv; + + if (!priv->container) + return; + + grid_attach (layout, child, left, top, width, height); + clutter_actor_add_child (CLUTTER_ACTOR (priv->container), child); +} + +/** + * clutter_grid_layout_attach_next_to: + * @layout: a #ClutterGridLayout + * @child: the actor to add + * @sibling: (allow-none): the child of @layout that @child will be placed + * next to, or %NULL to place @child at the beginning or end + * @side: the side of @sibling that @child is positioned next to + * @width: the number of columns that @child will span + * @height: the number of rows that @child will span + * + * Adds a actor to the grid. + * + * The actor is placed next to @sibling, on the side determined by + * @side. When @sibling is %NULL, the actor is placed in row (for + * left or right placement) or column 0 (for top or bottom placement), + * at the end indicated by @side. + * + * Attaching widgets labeled [1], [2], [3] with @sibling == %NULL and + * @side == %CLUTTER_GRID_POSITION_LEFT yields a layout of [3][2][1]. + * + * Since: 1.12 + */ +void +clutter_grid_layout_attach_next_to (ClutterGridLayout *layout, + ClutterActor *child, + ClutterActor *sibling, + ClutterGridPosition side, + gint width, + gint height) +{ + ClutterGridLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); + g_return_if_fail (CLUTTER_IS_ACTOR (child)); + g_return_if_fail (clutter_actor_get_parent (child) == NULL); + g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling)); + g_return_if_fail (width > 0); + g_return_if_fail (height > 0); + + priv = layout->priv; + + if (!priv->container) + return; + + grid_attach_next_to (layout, child, sibling, side, width, height); + clutter_actor_add_child (CLUTTER_ACTOR (priv->container), child); +} + +/** + * clutter_grid_layout_set_orientation: + * @layout: a #ClutterGridLayout + * @orientation: the orientation of the #ClutterGridLayout + * + * Sets the orientation of the @layout + * + * Since: 1.12 + */ +void +clutter_grid_layout_set_orientation (ClutterGridLayout *layout, + ClutterOrientation orientation) +{ + ClutterGridLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->orientation != orientation) + { + priv->orientation = orientation; + + clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); + g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_ORIENTATION]); + } +} + +/** + * clutter_grid_layout_get_child_at: + * @layout: a #ClutterGridLayout + * @left: the left edge of the cell + * @top: the top edge of the cell + * + * Gets the child of @layout whose area covers the grid + * cell whose upper left corner is at @left, @top. + * + * Returns: (transfer none): the child at the given position, or %NULL + * + * Since: 1.12 + */ +ClutterActor * +clutter_grid_layout_get_child_at (ClutterGridLayout *layout, + gint left, + gint top) +{ + ClutterGridLayoutPrivate *priv; + ClutterGridChild *grid_child; + ClutterActorIter iter; + ClutterActor *child; + + g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), NULL); + + priv = layout->priv; + + if (!priv->container) + return NULL; + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + { + grid_child = GET_GRID_CHILD (layout, child); + + if (CHILD_LEFT (grid_child) <= left && + CHILD_LEFT (grid_child) + CHILD_WIDTH (grid_child) > left && + CHILD_TOP (grid_child) <= top && + CHILD_TOP (grid_child) + CHILD_HEIGHT (grid_child) > top) + return child; + } + + return NULL; +} + +/** + * clutter_grid_layout_insert_row: + * @layout: a #ClutterGridLayout + * @position: the position to insert the row at + * + * Inserts a row at the specified position. + * + * Children which are attached at or below this position + * are moved one row down. Children which span across this + * position are grown to span the new row. + * + * Since: 1.12 + */ +void +clutter_grid_layout_insert_row (ClutterGridLayout *layout, + gint position) +{ + ClutterGridLayoutPrivate *priv; + ClutterGridChild *grid_child; + ClutterActorIter iter; + ClutterActor *child; + gint top, height; + + g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); + + priv = layout->priv; + + if (!priv->container) + return; + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + { + grid_child = GET_GRID_CHILD (layout, child); + + top = CHILD_TOP (grid_child); + height = CHILD_HEIGHT (grid_child); + + if (top >= position) + { + CHILD_TOP (grid_child) = top + 1; + g_object_notify_by_pspec (G_OBJECT (grid_child), + child_props[PROP_CHILD_TOP_ATTACH]); + } + else if (top + height > position) + { + CHILD_HEIGHT (grid_child) = height + 1; + g_object_notify_by_pspec (G_OBJECT (grid_child), + child_props[PROP_CHILD_HEIGHT]); + } + } + clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); +} + +/** + * clutter_grid_layout_insert_column: + * @layout: a #ClutterGridLayout + * @position: the position to insert the column at + * + * Inserts a column at the specified position. + * + * Children which are attached at or to the right of this position + * are moved one column to the right. Children which span across this + * position are grown to span the new column. + * + * Since: 1.12 + */ +void +clutter_grid_layout_insert_column (ClutterGridLayout *layout, + gint position) +{ + ClutterGridLayoutPrivate *priv; + ClutterGridChild *grid_child; + ClutterActorIter iter; + ClutterActor *child; + gint left, width; + + g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); + + priv = layout->priv; + + if (!priv->container) + return; + + clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); + while (clutter_actor_iter_next (&iter, &child)) + { + grid_child = GET_GRID_CHILD (layout, child); + + left = CHILD_LEFT (grid_child); + width = CHILD_WIDTH (grid_child); + + if (left >= position) + { + CHILD_LEFT (grid_child) = left + 1; + g_object_notify_by_pspec (G_OBJECT (grid_child), + child_props[PROP_CHILD_LEFT_ATTACH]); + } + else if (left + width > position) + { + CHILD_WIDTH (grid_child) = width + 1; + g_object_notify_by_pspec (G_OBJECT (grid_child), + child_props[PROP_CHILD_WIDTH]); + } + } + clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); +} + +/** + * clutter_grid_layout_insert_next_to: + * @layout: a #ClutterGridLayout + * @sibling: the child of @layout that the new row or column will be + * placed next to + * @side: the side of @sibling that @child is positioned next to + * + * Inserts a row or column at the specified position. + * + * The new row or column is placed next to @sibling, on the side + * determined by @side. If @side is %CLUTTER_GRID_POSITION_LEFT or + * %CLUTTER_GRID_POSITION_BOTTOM, a row is inserted. If @side is + * %CLUTTER_GRID_POSITION_LEFT of %CLUTTER_GRID_POSITION_RIGHT, + * a column is inserted. + * + * Since: 1.12 + */ +void +clutter_grid_layout_insert_next_to (ClutterGridLayout *layout, + ClutterActor *sibling, + ClutterGridPosition side) +{ + ClutterGridChild *grid_child; + + g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); + g_return_if_fail (CLUTTER_IS_ACTOR (sibling)); + + grid_child = GET_GRID_CHILD (layout, sibling); + + switch (side) + { + case CLUTTER_GRID_POSITION_LEFT: + clutter_grid_layout_insert_column (layout, CHILD_LEFT (grid_child)); + break; + + case CLUTTER_GRID_POSITION_RIGHT: + clutter_grid_layout_insert_column (layout, CHILD_LEFT (grid_child) + + CHILD_WIDTH (grid_child)); + break; + + case CLUTTER_GRID_POSITION_TOP: + clutter_grid_layout_insert_row (layout, CHILD_TOP (grid_child)); + break; + + case CLUTTER_GRID_POSITION_BOTTOM: + clutter_grid_layout_insert_row (layout, CHILD_TOP (grid_child) + + CHILD_HEIGHT (grid_child)); + break; + + default: + g_assert_not_reached (); + } +} + +/** + * clutter_grid_layout_get_orientation: + * @layout: a #ClutterGridLayout + * + * Retrieves the orientation of the @layout. + * + * Return value: the orientation of the layout + * + * Since: 1.12 + */ +ClutterOrientation +clutter_grid_layout_get_orientation (ClutterGridLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), + CLUTTER_ORIENTATION_HORIZONTAL); + + return layout->priv->orientation; +} + +/** + * clutter_grid_layout_set_row_spacing: + * @layout: a #ClutterGridLayout + * @spacing: the spacing between rows of the layout, in pixels + * + * Sets the spacing between rows of @layout + * + * Since: 1.12 + */ +void +clutter_grid_layout_set_row_spacing (ClutterGridLayout *layout, + guint spacing) +{ + ClutterGridLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); + + priv = layout->priv; + + if (COLUMNS (priv)->spacing != spacing) + { + COLUMNS (priv)->spacing = spacing; + + clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); + g_object_notify_by_pspec (G_OBJECT (layout), + obj_props[PROP_ROW_SPACING]); + } +} + +/** + * clutter_grid_layout_get_row_spacing: + * @layout: a #ClutterGridLayout + * + * Retrieves the spacing set using clutter_grid_layout_set_row_spacing() + * + * Return value: the spacing between rows of @layout + * + * Since: 1.12 + */ +guint +clutter_grid_layout_get_row_spacing (ClutterGridLayout *layout) +{ + ClutterGridLayoutPrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), 0); + + priv = layout->priv; + + return COLUMNS (priv)->spacing; +} + +/** + * clutter_grid_layout_set_column_spacing: + * @layout: a #ClutterGridLayout + * @spacing: the spacing between columns of the layout, in pixels + * + * Sets the spacing between columns of @layout + * + * Since: 1.12 + */ +void +clutter_grid_layout_set_column_spacing (ClutterGridLayout *layout, + guint spacing) +{ + ClutterGridLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); + + priv = layout->priv; + + if (ROWS (priv)->spacing != spacing) + { + ROWS (priv)->spacing = spacing; + + clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); + g_object_notify_by_pspec (G_OBJECT (layout), + obj_props[PROP_COLUMN_SPACING]); + } +} + +/** + * clutter_grid_layout_get_column_spacing: + * @layout: a #ClutterGridLayout + * + * Retrieves the spacing set using clutter_grid_layout_set_column_spacing() + * + * Return value: the spacing between coluns of @layout + * + * Since: 1.12 + */ +guint +clutter_grid_layout_get_column_spacing (ClutterGridLayout *layout) +{ + ClutterGridLayoutPrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), 0); + + priv = layout->priv; + + return ROWS (priv)->spacing; +} + +/** + * clutter_grid_layout_set_column_homogeneous: + * @layout: a #ClutterGridLayout + * @homogeneous: %TRUE to make columns homogeneous + * + * Sets whether all columns of @layout will have the same width. + * + * Since: 1.12 + */ +void +clutter_grid_layout_set_column_homogeneous (ClutterGridLayout *layout, + gboolean homogeneous) +{ + ClutterGridLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); + + priv = layout->priv; + + if (ROWS (priv)->homogeneous != homogeneous) + { + ROWS (priv)->homogeneous = homogeneous; + + clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); + g_object_notify_by_pspec (G_OBJECT (layout), + obj_props[PROP_COLUMN_HOMOGENEOUS]); + } +} + +/** + * clutter_grid_layout_get_column_homogeneous: + * @layout: a #ClutterGridLayout + * + * Returns whether all columns of @layout have the same width. + * + * Returns: whether all columns of @layout have the same width. + */ +gboolean +clutter_grid_layout_get_column_homogeneous (ClutterGridLayout *layout) +{ + ClutterGridLayoutPrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), FALSE); + + priv = layout->priv; + + return ROWS (priv)->homogeneous; +} + +/** + * clutter_grid_layout_set_row_homogeneous: + * @layout: a #ClutterGridLayout + * @homogeneous: %TRUE to make rows homogeneous + * + * Sets whether all rows of @layout will have the same height. + * + * Since: 1.12 + */ +void +clutter_grid_layout_set_row_homogeneous (ClutterGridLayout *layout, + gboolean homogeneous) +{ + ClutterGridLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); + + priv = layout->priv; + + if (COLUMNS (priv)->homogeneous != homogeneous) + { + COLUMNS (priv)->homogeneous = homogeneous; + + clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); + g_object_notify_by_pspec (G_OBJECT (layout), + obj_props[PROP_ROW_HOMOGENEOUS]); + } +} + +/** + * clutter_grid_layout_get_row_homogeneous: + * @layout: a #ClutterGridLayout + * + * Returns whether all rows of @layout have the same height. + * + * Returns: whether all rows of @layout have the same height. + * + * Since: 1.12 + */ +gboolean +clutter_grid_layout_get_row_homogeneous (ClutterGridLayout *layout) +{ + ClutterGridLayoutPrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), FALSE); + + priv = layout->priv; + + return COLUMNS (priv)->homogeneous; +} diff --git a/clutter/clutter-grid-layout.h b/clutter/clutter-grid-layout.h new file mode 100644 index 000000000..1c7644a2b --- /dev/null +++ b/clutter/clutter-grid-layout.h @@ -0,0 +1,161 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2012 Bastian Winkler + * + * 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: + * Bastian Winkler + * + * Based on GtkGrid widget by: + * Matthias Clasen + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_GRID_LAYOUT_H__ +#define __CLUTTER_GRID_LAYOUT_H__ + +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_GRID_LAYOUT (clutter_grid_layout_get_type ()) +#define CLUTTER_GRID_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayout)) +#define CLUTTER_IS_GRID_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GRID_LAYOUT)) +#define CLUTTER_GRID_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayoutClass)) +#define CLUTTER_IS_GRID_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_GRID_LAYOUT)) +#define CLUTTER_GRID_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayoutClass)) + +typedef struct _ClutterGridLayout ClutterGridLayout; +typedef struct _ClutterGridLayoutPrivate ClutterGridLayoutPrivate; +typedef struct _ClutterGridLayoutClass ClutterGridLayoutClass; + +/** + * ClutterGridLayout: + * + * The #ClutterGridLayout structure contains only private data + * and should be accessed using the provided API + * + * Since: 1.12 + */ +struct _ClutterGridLayout +{ + /*< private >*/ + ClutterLayoutManager parent_instance; + + ClutterGridLayoutPrivate *priv; +}; + +/** + * ClutterGridLayoutClass: + * + * The #ClutterGridLayoutClass structure contains only private + * data and should be accessed using the provided API + * + * Since: 1.12 + */ +struct _ClutterGridLayoutClass +{ + /*< private >*/ + ClutterLayoutManagerClass parent_class; + + gpointer _padding[8]; +}; + +CLUTTER_AVAILABLE_IN_1_12 +GType clutter_grid_layout_get_type (void) G_GNUC_CONST; + +CLUTTER_AVAILABLE_IN_1_12 +ClutterLayoutManager * clutter_grid_layout_new (void); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_grid_layout_attach (ClutterGridLayout *layout, + ClutterActor *child, + gint left, + gint top, + gint width, + gint height); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_grid_layout_attach_next_to (ClutterGridLayout *layout, + ClutterActor *child, + ClutterActor *sibling, + ClutterGridPosition side, + gint width, + gint height); + +CLUTTER_AVAILABLE_IN_1_12 +ClutterActor * clutter_grid_layout_get_child_at (ClutterGridLayout *layout, + gint left, + gint top); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_grid_layout_insert_row (ClutterGridLayout *layout, + gint position); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_grid_layout_insert_column (ClutterGridLayout *layout, + gint position); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_grid_layout_insert_next_to (ClutterGridLayout *layout, + ClutterActor *sibling, + ClutterGridPosition side); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_grid_layout_set_orientation (ClutterGridLayout *layout, + ClutterOrientation orientation); + +CLUTTER_AVAILABLE_IN_1_12 +ClutterOrientation clutter_grid_layout_get_orientation (ClutterGridLayout *layout); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_grid_layout_set_column_spacing (ClutterGridLayout *layout, + guint spacing); + +CLUTTER_AVAILABLE_IN_1_12 +guint clutter_grid_layout_get_column_spacing (ClutterGridLayout *layout); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_grid_layout_set_row_spacing (ClutterGridLayout *layout, + guint spacing); + +CLUTTER_AVAILABLE_IN_1_12 +guint clutter_grid_layout_get_row_spacing (ClutterGridLayout *layout); + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_grid_layout_set_column_homogeneous (ClutterGridLayout *layout, + gboolean homogeneous); + +CLUTTER_AVAILABLE_IN_1_12 +gboolean clutter_grid_layout_get_column_homogeneous (ClutterGridLayout *layout); + + +CLUTTER_AVAILABLE_IN_1_12 +void clutter_grid_layout_set_row_homogeneous (ClutterGridLayout *layout, + gboolean homogeneous); + +CLUTTER_AVAILABLE_IN_1_12 +gboolean clutter_grid_layout_get_row_homogeneous (ClutterGridLayout *layout); + +G_END_DECLS + +#endif /* __CLUTTER_GRID_LAYOUT_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index b4a624ade..821a58bdf 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -66,6 +66,7 @@ #include "clutter-fixed-layout.h" #include "clutter-flow-layout.h" #include "clutter-gesture-action.h" +#include "clutter-grid-layout.h" #include "clutter-group.h" #include "clutter-image.h" #include "clutter-input-device.h" diff --git a/clutter/clutter.symbols b/clutter/clutter.symbols index c7c257880..ad6f0a4c4 100644 --- a/clutter/clutter.symbols +++ b/clutter/clutter.symbols @@ -745,6 +745,24 @@ clutter_grab_keyboard clutter_grab_pointer clutter_grab_pointer_for_device clutter_gravity_get_type +clutter_grid_layout_get_type +clutter_grid_layout_new +clutter_grid_layout_attach +clutter_grid_layout_attach_next_to +clutter_grid_layout_get_child_at +clutter_grid_layout_insert_row +clutter_grid_layout_insert_column +clutter_grid_layout_insert_next_to +clutter_grid_layout_set_orientation +clutter_grid_layout_get_orientation +clutter_grid_layout_set_column_spacing +clutter_grid_layout_get_column_spacing +clutter_grid_layout_set_row_spacing +clutter_grid_layout_get_row_spacing +clutter_grid_layout_set_column_homogeneous +clutter_grid_layout_get_column_homogeneous +clutter_grid_layout_set_row_homogeneous +clutter_grid_layout_get_row_homogeneous clutter_group_get_nth_child clutter_group_get_n_children clutter_group_get_type diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in index 3540ad356..6869f73ce 100644 --- a/doc/reference/clutter/clutter-docs.xml.in +++ b/doc/reference/clutter/clutter-docs.xml.in @@ -86,6 +86,7 @@ + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 073442c47..92991c305 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -2292,6 +2292,45 @@ ClutterFlowLayoutPrivate clutter_flow_layout_get_type +
+clutter-grid-layout +ClutterGridLayout +ClutterGridPosition +ClutterGridLayout +ClutterGridLayoutClass +clutter_grid_layout_new +clutter_grid_layout_attach +clutter_grid_layout_attach_next_to +clutter_grid_layout_get_child_at +clutter_grid_layout_insert_column +clutter_grid_layout_insert_row +clutter_grid_layout_insert_next_to + + +clutter_grid_layout_set_orientation +clutter_grid_layout_get_orientation +clutter_grid_layout_set_column_homogeneous +clutter_grid_layout_get_column_homogeneous +clutter_grid_layout_set_row_homogeneous +clutter_grid_layout_get_row_homogeneous +clutter_grid_layout_set_column_spacing +clutter_grid_layout_get_column_spacing +clutter_grid_layout_set_row_spacing +clutter_grid_layout_get_row_spacing + + +CLUTTER_TYPE_GRID_LAYOUT +CLUTTER_GRID_LAYOUT +CLUTTER_GRID_LAYOUT_CLASS +CLUTTER_GRID_LAYOUT_GET_CLASS +CLUTTER_IS_GRID_LAYOUT +CLUTTER_IS_GRID_LAYOUT_CLASS + + +ClutterGridLayoutPrivate +clutter_grid_layout_get_type +
+
ClutterBoxLayout clutter-box-layout diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index 533713fa7..5e4f281d0 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -40,6 +40,7 @@ clutter_effect_get_type clutter_fixed_layout_get_type clutter_flow_layout_get_type clutter_gesture_action_get_type +clutter_grid_layout_get_type clutter_group_get_type clutter_input_device_get_type clutter_interval_get_type