4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2010 Red Hat, Inc.
7 * Copyright (C) 2012 Bastian Winkler <buz@netbuz.org>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
23 * Bastian Winkler <buz@netbuz.org>
25 * Based on GtkGrid widget by:
36 #include "clutter-grid-layout.h"
38 #include "clutter-actor-private.h"
39 #include "clutter-container.h"
40 #include "clutter-debug.h"
41 #include "clutter-enum-types.h"
42 #include "clutter-layout-meta.h"
43 #include "clutter-private.h"
46 * SECTION:clutter-grid-layout
47 * @Short_description: Pack widgets in a rows and columns
48 * @Title: ClutterGridLayout
49 * @See_also: #ClutterTableLayout, #ClutterBoxLayout
51 * #ClutterGridLayout is a layout manager which arranges its child widgets in
52 * rows and columns. It is a very similar to #ClutterTableLayout and
53 * #ClutterBoxLayout, but it consistently uses #ClutterActor's
54 * alignment and expansion flags instead of custom child properties.
56 * Children are added using clutter_grid_layout_attach(). They can span
57 * multiple rows or columns. It is also possible to add a child next to an
58 * existing child, using clutter_grid_layout_attach_next_to(). The behaviour of
59 * #ClutterGridLayout when several children occupy the same grid cell is undefined.
61 * #ClutterGridLayout can be used like a #ClutterBoxLayout by just using
62 * clutter_actor_add_child(), which will place children next to each other in
63 * the direction determined by the #ClutterGridLayout:orientation property.
66 #define CLUTTER_TYPE_GRID_CHILD (clutter_grid_child_get_type ())
67 #define CLUTTER_GRID_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GRID_CHILD, ClutterGridChild))
68 #define CLUTTER_IS_GRID_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GRID_CHILD))
69 #define CLUTTER_GRID_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayoutPrivate))
71 typedef struct _ClutterGridChild ClutterGridChild;
72 typedef struct _ClutterLayoutMetaClass ClutterGridChildClass;
74 typedef struct _ClutterGridAttach ClutterGridAttach;
75 typedef struct _ClutterGridLine ClutterGridLine;
76 typedef struct _ClutterGridLines ClutterGridLines;
77 typedef struct _ClutterGridLineData ClutterGridLineData;
78 typedef struct _ClutterGridRequest ClutterGridRequest;
81 struct _ClutterGridAttach
87 struct _ClutterGridChild
89 ClutterLayoutMeta parent_instance;
91 ClutterGridAttach attach[2];
94 #define CHILD_LEFT(child) ((child)->attach[CLUTTER_ORIENTATION_HORIZONTAL].pos)
95 #define CHILD_WIDTH(child) ((child)->attach[CLUTTER_ORIENTATION_HORIZONTAL].span)
96 #define CHILD_TOP(child) ((child)->attach[CLUTTER_ORIENTATION_VERTICAL].pos)
97 #define CHILD_HEIGHT(child) ((child)->attach[CLUTTER_ORIENTATION_VERTICAL].span)
99 /* A ClutterGridLineData struct contains row/column specific parts
102 struct _ClutterGridLineData
105 guint homogeneous : 1;
108 struct _ClutterGridLayoutPrivate
110 ClutterContainer *container;
111 ClutterOrientation orientation;
113 ClutterGridLineData linedata[2];
116 #define ROWS(priv) (&(priv)->linedata[CLUTTER_ORIENTATION_HORIZONTAL])
117 #define COLUMNS(priv) (&(priv)->linedata[CLUTTER_ORIENTATION_VERTICAL])
119 /* A ClutterGridLine struct represents a single row or column
120 * during size requests
122 struct _ClutterGridLine
129 guint need_expand : 1;
134 struct _ClutterGridLines
136 ClutterGridLine *lines;
140 struct _ClutterGridRequest
142 ClutterGridLayout *grid;
143 ClutterGridLines lines[2];
153 PROP_ROW_HOMOGENEOUS,
154 PROP_COLUMN_HOMOGENEOUS,
158 static GParamSpec *obj_props[PROP_LAST];
164 PROP_CHILD_LEFT_ATTACH,
165 PROP_CHILD_TOP_ATTACH,
171 static GParamSpec *child_props[PROP_CHILD_LAST];
173 GType clutter_grid_child_get_type (void);
175 G_DEFINE_TYPE (ClutterGridChild, clutter_grid_child,
176 CLUTTER_TYPE_LAYOUT_META);
177 G_DEFINE_TYPE (ClutterGridLayout, clutter_grid_layout,
178 CLUTTER_TYPE_LAYOUT_MANAGER);
181 #define GET_GRID_CHILD(grid, child) \
182 (CLUTTER_GRID_CHILD(clutter_layout_manager_get_child_meta \
183 (CLUTTER_LAYOUT_MANAGER((grid)),\
184 CLUTTER_GRID_LAYOUT((grid))->priv->container,(child))))
187 grid_attach (ClutterGridLayout *self,
194 ClutterGridChild *grid_child;
196 grid_child = GET_GRID_CHILD (self, actor);
198 CHILD_LEFT (grid_child) = left;
199 CHILD_TOP (grid_child) = top;
200 CHILD_WIDTH (grid_child) = width;
201 CHILD_HEIGHT (grid_child) = height;
204 /* Find the position 'touching' existing
205 * children. @orientation and @max determine
206 * from which direction to approach (horizontal
207 * + max = right, vertical + !max = top, etc).
208 * @op_pos, @op_span determine the rows/columns
209 * in which the touching has to happen.
212 find_attach_position (ClutterGridLayout *self,
213 ClutterOrientation orientation,
218 ClutterGridLayoutPrivate *priv = self->priv;
219 ClutterGridChild *grid_child;
220 ClutterGridAttach *attach;
221 ClutterGridAttach *opposite;
222 ClutterActorIter iter;
234 if (!priv->container)
237 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
238 while (clutter_actor_iter_next (&iter, &child))
240 grid_child = GET_GRID_CHILD (self, child);
242 attach = &grid_child->attach[orientation];
243 opposite = &grid_child->attach[1 - orientation];
245 /* check if the ranges overlap */
246 if (opposite->pos <= op_pos + op_span && op_pos <= opposite->pos + opposite->span)
251 pos = MAX (pos, attach->pos + attach->span);
253 pos = MIN (pos, attach->pos);
263 grid_attach_next_to (ClutterGridLayout *layout,
265 ClutterActor *sibling,
266 ClutterGridPosition side,
270 ClutterGridChild *grid_sibling;
275 grid_sibling = GET_GRID_CHILD (layout, sibling);
279 case CLUTTER_GRID_POSITION_LEFT:
280 left = CHILD_LEFT (grid_sibling) - width;
281 top = CHILD_TOP (grid_sibling);
284 case CLUTTER_GRID_POSITION_RIGHT:
285 left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
286 top = CHILD_TOP (grid_sibling);
289 case CLUTTER_GRID_POSITION_TOP:
290 left = CHILD_LEFT (grid_sibling);
291 top = CHILD_TOP (grid_sibling) - height;
294 case CLUTTER_GRID_POSITION_BOTTOM:
295 left = CHILD_LEFT (grid_sibling);
296 top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
300 g_assert_not_reached ();
307 case CLUTTER_GRID_POSITION_LEFT:
308 left = find_attach_position (layout, CLUTTER_ORIENTATION_HORIZONTAL,
314 case CLUTTER_GRID_POSITION_RIGHT:
315 left = find_attach_position (layout, CLUTTER_ORIENTATION_HORIZONTAL,
320 case CLUTTER_GRID_POSITION_TOP:
322 top = find_attach_position (layout, CLUTTER_ORIENTATION_VERTICAL,
327 case CLUTTER_GRID_POSITION_BOTTOM:
329 top = find_attach_position (layout, CLUTTER_ORIENTATION_VERTICAL,
334 g_assert_not_reached ();
338 grid_attach (layout, child, left, top, width, height);
342 clutter_grid_request_update_child_attach (ClutterGridRequest *request,
345 ClutterGridLayoutPrivate *priv = request->grid->priv;
346 ClutterGridChild *grid_child;
348 grid_child = GET_GRID_CHILD (request->grid, actor);
350 if (CHILD_LEFT (grid_child) == -1 || CHILD_TOP (grid_child) == -1)
352 ClutterGridPosition side;
353 ClutterActor *sibling;
355 if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL)
357 ClutterTextDirection td;
359 ClutterActor *container = CLUTTER_ACTOR (priv->container);
361 td = clutter_actor_get_text_direction (container);
362 rtl = (td == CLUTTER_TEXT_DIRECTION_RTL) ? TRUE : FALSE;
363 side = rtl ? CLUTTER_GRID_POSITION_RIGHT : CLUTTER_GRID_POSITION_LEFT;
367 /* XXX: maybe we should also add a :pack-start property to modify
369 side = CLUTTER_GRID_POSITION_BOTTOM;
372 sibling = clutter_actor_get_previous_sibling (actor);
373 grid_attach_next_to (request->grid, actor, sibling, side,
374 CHILD_WIDTH (grid_child),
375 CHILD_HEIGHT (grid_child));
380 clutter_grid_request_update_attach (ClutterGridRequest *request)
382 ClutterGridLayoutPrivate *priv = request->grid->priv;
383 ClutterActorIter iter;
386 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
387 while (clutter_actor_iter_next (&iter, &child))
388 clutter_grid_request_update_child_attach (request, child);
391 /* Calculates the min and max numbers for both orientations.
394 clutter_grid_request_count_lines (ClutterGridRequest *request)
396 ClutterGridLayoutPrivate *priv = request->grid->priv;
397 ClutterGridChild *grid_child;
398 ClutterGridAttach *attach;
399 ClutterActorIter iter;
404 min[0] = min[1] = G_MAXINT;
405 max[0] = max[1] = G_MININT;
407 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
408 while (clutter_actor_iter_next (&iter, &child))
410 grid_child = GET_GRID_CHILD (request->grid, child);
411 attach = grid_child->attach;
413 min[0] = MIN (min[0], attach[0].pos);
414 max[0] = MAX (max[0], attach[0].pos + attach[0].span);
415 min[1] = MIN (min[1], attach[1].pos);
416 max[1] = MAX (max[1], attach[1].pos + attach[1].span);
419 request->lines[0].min = min[0];
420 request->lines[0].max = max[0];
421 request->lines[1].min = min[1];
422 request->lines[1].max = max[1];
425 /* Sets line sizes to 0 and marks lines as expand
426 * if they have a non-spanning expanding child.
429 clutter_grid_request_init (ClutterGridRequest *request,
430 ClutterOrientation orientation)
432 ClutterGridLayoutPrivate *priv = request->grid->priv;
433 ClutterGridChild *grid_child;
434 ClutterGridAttach *attach;
435 ClutterGridLines *lines;
436 ClutterActorIter iter;
440 lines = &request->lines[orientation];
442 for (i = 0; i < lines->max - lines->min; i++)
444 lines->lines[i].minimum = 0;
445 lines->lines[i].natural = 0;
446 lines->lines[i].expand = FALSE;
449 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
450 while (clutter_actor_iter_next (&iter, &child))
452 grid_child = GET_GRID_CHILD (request->grid, child);
453 attach = &grid_child->attach[orientation];
454 if (attach->span == 1 && clutter_actor_needs_expand (child, orientation))
455 lines->lines[attach->pos - lines->min].expand = TRUE;
459 /* Sums allocations for lines spanned by child and their spacing.
462 compute_allocation_for_child (ClutterGridRequest *request,
464 ClutterOrientation orientation)
466 ClutterGridLayoutPrivate *priv = request->grid->priv;
467 ClutterGridChild *grid_child;
468 ClutterGridLineData *linedata;
469 ClutterGridLines *lines;
470 ClutterGridLine *line;
471 ClutterGridAttach *attach;
475 grid_child = GET_GRID_CHILD (request->grid, child);
476 linedata = &priv->linedata[orientation];
477 lines = &request->lines[orientation];
478 attach = &grid_child->attach[orientation];
480 size = (attach->span - 1) * linedata->spacing;
481 for (i = 0; i < attach->span; i++)
483 line = &lines->lines[attach->pos - lines->min + i];
484 size += line->allocation;
491 compute_request_for_child (ClutterGridRequest *request,
493 ClutterOrientation orientation,
502 size = compute_allocation_for_child (request, child, 1 - orientation);
503 if (orientation == CLUTTER_ORIENTATION_HORIZONTAL)
504 clutter_actor_get_preferred_width (child, size, minimum, natural);
506 clutter_actor_get_preferred_height (child, size, minimum, natural);
510 if (orientation == CLUTTER_ORIENTATION_VERTICAL)
511 clutter_actor_get_preferred_width (child, -1, minimum, natural);
513 clutter_actor_get_preferred_height (child, -1, minimum, natural);
517 /* Sets requisition to max. of non-spanning children.
518 * If contextual is TRUE, requires allocations of
519 * lines in the opposite orientation to be set.
522 clutter_grid_request_non_spanning (ClutterGridRequest *request,
523 ClutterOrientation orientation,
526 ClutterGridLayoutPrivate *priv = request->grid->priv;
527 ClutterGridChild *grid_child;
528 ClutterGridAttach *attach;
529 ClutterGridLines *lines;
530 ClutterGridLine *line;
531 ClutterActorIter iter;
536 lines = &request->lines[orientation];
538 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
539 while (clutter_actor_iter_next (&iter, &child))
541 if (!CLUTTER_ACTOR_IS_VISIBLE (child))
544 grid_child = GET_GRID_CHILD (request->grid, child);
546 attach = &grid_child->attach[orientation];
547 if (attach->span != 1)
550 compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
552 line = &lines->lines[attach->pos - lines->min];
553 line->minimum = MAX (line->minimum, minimum);
554 line->natural = MAX (line->natural, natural);
558 /* Enforce homogeneous sizes.
561 clutter_grid_request_homogeneous (ClutterGridRequest *request,
562 ClutterOrientation orientation)
564 ClutterGridLayoutPrivate *priv = request->grid->priv;
565 ClutterGridLineData *linedata;
566 ClutterGridLines *lines;
567 gfloat minimum, natural;
570 linedata = &priv->linedata[orientation];
571 lines = &request->lines[orientation];
573 if (!linedata->homogeneous)
579 for (i = 0; i < lines->max - lines->min; i++)
581 minimum = MAX (minimum, lines->lines[i].minimum);
582 natural = MAX (natural, lines->lines[i].natural);
585 for (i = 0; i < lines->max - lines->min; i++)
587 lines->lines[i].minimum = minimum;
588 lines->lines[i].natural = natural;
592 /* Deals with spanning children.
593 * Requires expand fields of lines to be set for
594 * non-spanning children.
597 clutter_grid_request_spanning (ClutterGridRequest *request,
598 ClutterOrientation orientation,
601 ClutterGridLayoutPrivate *priv = request->grid->priv;
602 ClutterGridChild *grid_child;
604 ClutterActorIter iter;
605 ClutterGridAttach *attach;
606 ClutterGridLineData *linedata;
607 ClutterGridLines *lines;
608 ClutterGridLine *line;
614 gboolean force_expand;
620 linedata = &priv->linedata[orientation];
621 lines = &request->lines[orientation];
623 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
624 while (clutter_actor_iter_next (&iter, &child))
626 if (!CLUTTER_ACTOR_IS_VISIBLE (child))
629 grid_child = GET_GRID_CHILD (request->grid, child);
631 attach = &grid_child->attach[orientation];
632 if (attach->span == 1)
635 compute_request_for_child (request, child, orientation, contextual,
638 span_minimum = (attach->span - 1) * linedata->spacing;
639 span_natural = (attach->span - 1) * linedata->spacing;
641 force_expand = FALSE;
642 for (i = 0; i < attach->span; i++)
644 line = &lines->lines[attach->pos - lines->min + i];
645 span_minimum += line->minimum;
646 span_natural += line->natural;
650 if (span_expand == 0)
652 span_expand = attach->span;
656 /* If we need to request more space for this child to fill
657 * its requisition, then divide up the needed space amongst the
658 * lines it spans, favoring expandable lines if any.
660 * When doing homogeneous allocation though, try to keep the
661 * line allocations even, since we're going to force them to
662 * be the same anyway, and we don't want to introduce unnecessary
665 if (span_minimum < minimum)
667 if (linedata->homogeneous)
671 total = minimum - (attach->span - 1) * linedata->spacing;
672 m = total / attach->span + (total % attach->span ? 1 : 0);
673 for (i = 0; i < attach->span; i++)
675 line = &lines->lines[attach->pos - lines->min + i];
676 line->minimum = MAX(line->minimum, m);
681 extra = minimum - span_minimum;
682 expand = span_expand;
683 for (i = 0; i < attach->span; i++)
685 line = &lines->lines[attach->pos - lines->min + i];
686 if (force_expand || line->expand)
688 line_extra = extra / expand;
689 line->minimum += line_extra;
697 if (span_natural < natural)
699 if (linedata->homogeneous)
703 total = natural - (attach->span - 1) * linedata->spacing;
704 n = total / attach->span + (total % attach->span ? 1 : 0);
705 for (i = 0; i < attach->span; i++)
707 line = &lines->lines[attach->pos - lines->min + i];
708 line->natural = MAX(line->natural, n);
713 extra = natural - span_natural;
714 expand = span_expand;
715 for (i = 0; i < attach->span; i++)
717 line = &lines->lines[attach->pos - lines->min + i];
718 if (force_expand || line->expand)
720 line_extra = extra / expand;
721 line->natural += line_extra;
731 /* Marks empty and expanding lines and counts them.
734 clutter_grid_request_compute_expand (ClutterGridRequest *request,
735 ClutterOrientation orientation,
736 gint *nonempty_lines,
739 ClutterGridLayoutPrivate *priv = request->grid->priv;
740 ClutterGridChild *grid_child;
741 ClutterGridAttach *attach;
742 ClutterActorIter iter;
745 ClutterGridLines *lines;
746 ClutterGridLine *line;
751 lines = &request->lines[orientation];
753 for (i = 0; i < lines->max - lines->min; i++)
755 lines->lines[i].need_expand = FALSE;
756 lines->lines[i].expand = FALSE;
757 lines->lines[i].empty = TRUE;
760 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
761 while (clutter_actor_iter_next (&iter, &child))
763 if (!CLUTTER_ACTOR_IS_VISIBLE (child))
766 grid_child = GET_GRID_CHILD (request->grid, child);
768 attach = &grid_child->attach[orientation];
769 if (attach->span != 1)
772 line = &lines->lines[attach->pos - lines->min];
774 if (clutter_actor_needs_expand (child, orientation))
779 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
780 while (clutter_actor_iter_next (&iter, &child))
782 if (!CLUTTER_ACTOR_IS_VISIBLE (child))
785 grid_child = GET_GRID_CHILD (request->grid, child);
787 attach = &grid_child->attach[orientation];
788 if (attach->span == 1)
792 for (i = 0; i < attach->span; i++)
794 line = &lines->lines[attach->pos - lines->min + i];
800 if (!has_expand && clutter_actor_needs_expand (child, orientation))
802 for (i = 0; i < attach->span; i++)
804 line = &lines->lines[attach->pos - lines->min + i];
805 line->need_expand = TRUE;
812 for (i = 0; i < lines->max - lines->min; i++)
814 line = &lines->lines[i];
816 if (line->need_expand)
827 *nonempty_lines = lines->max - lines->min - empty;
830 *expand_lines = expand;
833 /* Sums the minimum and natural fields of lines and their spacing.
836 clutter_grid_request_sum (ClutterGridRequest *request,
837 ClutterOrientation orientation,
841 ClutterGridLayoutPrivate *priv = request->grid->priv;
842 ClutterGridLineData *linedata;
843 ClutterGridLines *lines;
848 clutter_grid_request_compute_expand (request, orientation, &nonempty, NULL);
850 linedata = &priv->linedata[orientation];
851 lines = &request->lines[orientation];
857 min = (nonempty - 1) * linedata->spacing;
858 nat = (nonempty - 1) * linedata->spacing;
861 for (i = 0; i < lines->max - lines->min; i++)
863 min += lines->lines[i].minimum;
864 nat += lines->lines[i].natural;
874 /* Computes minimum and natural fields of lines.
875 * When contextual is TRUE, requires allocation of
876 * lines in the opposite orientation to be set.
879 clutter_grid_request_run (ClutterGridRequest *request,
880 ClutterOrientation orientation,
883 clutter_grid_request_init (request, orientation);
884 clutter_grid_request_non_spanning (request, orientation, contextual);
885 clutter_grid_request_homogeneous (request, orientation);
886 clutter_grid_request_spanning (request, orientation, contextual);
887 clutter_grid_request_homogeneous (request, orientation);
890 typedef struct _RequestedSize
899 /* Pulled from gtksizerequest.c from Gtk+ */
901 compare_gap (gconstpointer p1,
905 RequestedSize *sizes = data;
906 const guint *c1 = p1;
907 const guint *c2 = p2;
909 const gint d1 = MAX (sizes[*c1].natural_size -
910 sizes[*c1].minimum_size,
912 const gint d2 = MAX (sizes[*c2].natural_size -
913 sizes[*c2].minimum_size,
916 gint delta = (d2 - d1);
925 * distribute_natural_allocation:
926 * @extra_space: Extra space to redistribute among children after subtracting
927 * minimum sizes and any child padding from the overall allocation
928 * @n_requested_sizes: Number of requests to fit into the allocation
929 * @sizes: An array of structs with a client pointer and a minimum/natural size
930 * in the orientation of the allocation.
932 * Distributes @extra_space to child @sizes by bringing smaller
933 * children up to natural size first.
935 * The remaining space will be added to the @minimum_size member of the
936 * RequestedSize struct. If all sizes reach their natural size then
937 * the remaining space is returned.
939 * Returns: The remainder of @extra_space after redistributing space
942 * Pulled from gtksizerequest.c from Gtk+
945 distribute_natural_allocation (gint extra_space,
946 guint n_requested_sizes,
947 RequestedSize *sizes)
952 g_return_val_if_fail (extra_space >= 0, 0);
954 spreading = g_newa (guint, n_requested_sizes);
956 for (i = 0; i < n_requested_sizes; i++)
959 /* Distribute the container's extra space c_gap. We want to assign
960 * this space such that the sum of extra space assigned to children
961 * (c^i_gap) is equal to c_cap. The case that there's not enough
962 * space for all children to take their natural size needs some
963 * attention. The goals we want to achieve are:
965 * a) Maximize number of children taking their natural size.
966 * b) The allocated size of children should be a continuous
967 * function of c_gap. That is, increasing the container size by
968 * one pixel should never make drastic changes in the distribution.
969 * c) If child i takes its natural size and child j doesn't,
970 * child j should have received at least as much gap as child i.
972 * The following code distributes the additional space by following
976 /* Sort descending by gap and position. */
977 g_qsort_with_data (spreading,
978 n_requested_sizes, sizeof (guint),
981 /* Distribute available space.
982 * This master piece of a loop was conceived by Behdad Esfahbod.
984 for (i = n_requested_sizes - 1; extra_space > 0 && i >= 0; --i)
986 /* Divide remaining space by number of remaining children.
987 * Sort order and reducing remaining space by assigned space
988 * ensures that space is distributed equally.
990 gint glue = (extra_space + i) / (i + 1);
991 gint gap = sizes[(spreading[i])].natural_size
992 - sizes[(spreading[i])].minimum_size;
994 gint extra = MIN (glue, gap);
996 sizes[spreading[i]].minimum_size += extra;
998 extra_space -= extra;
1004 /* Requires that the minimum and natural fields of lines
1005 * have been set, computes the allocation field of lines
1006 * by distributing total_size among lines.
1009 clutter_grid_request_allocate (ClutterGridRequest *request,
1010 ClutterOrientation orientation,
1013 ClutterGridLayoutPrivate *priv = request->grid->priv;
1014 ClutterGridLineData *linedata;
1015 ClutterGridLines *lines;
1016 ClutterGridLine *line;
1020 RequestedSize *sizes;
1025 clutter_grid_request_compute_expand (request, orientation, &nonempty, &expand);
1030 linedata = &priv->linedata[orientation];
1031 lines = &request->lines[orientation];
1033 size = total_size - (nonempty - 1) * linedata->spacing;
1035 if (linedata->homogeneous)
1037 extra = size / nonempty;
1038 rest = size % nonempty;
1040 for (i = 0; i < lines->max - lines->min; i++)
1042 line = &lines->lines[i];
1046 line->allocation = extra;
1049 line->allocation += 1;
1056 sizes = g_newa (RequestedSize, nonempty);
1059 for (i = 0; i < lines->max - lines->min; i++)
1061 line = &lines->lines[i];
1065 size -= line->minimum;
1067 sizes[j].minimum_size = line->minimum;
1068 sizes[j].natural_size = line->natural;
1069 sizes[j].data = line;
1073 size = distribute_natural_allocation (MAX (0, size), nonempty, sizes);
1077 extra = size / expand;
1078 rest = size % expand;
1087 for (i = 0; i < lines->max - lines->min; i++)
1089 line = &lines->lines[i];
1093 g_assert (line == sizes[j].data);
1095 line->allocation = sizes[j].minimum_size;
1098 line->allocation += extra;
1101 line->allocation += 1;
1111 /* Computes the position fields from allocation and spacing.
1114 clutter_grid_request_position (ClutterGridRequest *request,
1115 ClutterOrientation orientation)
1117 ClutterGridLayoutPrivate *priv = request->grid->priv;
1118 ClutterGridLineData *linedata;
1119 ClutterGridLines *lines;
1120 ClutterGridLine *line;
1124 linedata = &priv->linedata[orientation];
1125 lines = &request->lines[orientation];
1128 for (i = 0; i < lines->max - lines->min; i++)
1130 line = &lines->lines[i];
1133 line->position = position;
1134 position += line->allocation + linedata->spacing;
1140 clutter_grid_child_set_property (GObject *gobject,
1142 const GValue *value,
1145 ClutterGridChild *grid_child = CLUTTER_GRID_CHILD (gobject);
1146 ClutterLayoutManager *manager;
1148 manager = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (gobject));
1152 case PROP_CHILD_LEFT_ATTACH:
1153 CHILD_LEFT (grid_child) = g_value_get_int (value);
1154 clutter_layout_manager_layout_changed (manager);
1157 case PROP_CHILD_TOP_ATTACH:
1158 CHILD_TOP (grid_child) = g_value_get_int (value);
1159 clutter_layout_manager_layout_changed (manager);
1162 case PROP_CHILD_WIDTH:
1163 CHILD_WIDTH (grid_child) = g_value_get_int (value);
1164 clutter_layout_manager_layout_changed (manager);
1167 case PROP_CHILD_HEIGHT:
1168 CHILD_HEIGHT (grid_child) = g_value_get_int (value);
1169 clutter_layout_manager_layout_changed (manager);
1173 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1179 clutter_grid_child_get_property (GObject *gobject,
1184 ClutterGridChild *grid_child = CLUTTER_GRID_CHILD (gobject);
1188 case PROP_CHILD_LEFT_ATTACH:
1189 g_value_set_int (value, CHILD_LEFT (grid_child));
1192 case PROP_CHILD_TOP_ATTACH:
1193 g_value_set_int (value, CHILD_TOP (grid_child));
1196 case PROP_CHILD_WIDTH:
1197 g_value_set_int (value, CHILD_WIDTH (grid_child));
1200 case PROP_CHILD_HEIGHT:
1201 g_value_set_int (value, CHILD_HEIGHT (grid_child));
1205 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1211 clutter_grid_child_class_init (ClutterGridChildClass *klass)
1213 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1215 gobject_class->set_property = clutter_grid_child_set_property;
1216 gobject_class->get_property = clutter_grid_child_get_property;
1218 child_props[PROP_CHILD_LEFT_ATTACH] =
1219 g_param_spec_int ("left-attach",
1220 P_("Left attachment"),
1221 P_("The column number to attach the left side of the "
1223 -G_MAXINT, G_MAXINT, 0,
1224 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1226 child_props[PROP_CHILD_TOP_ATTACH] =
1227 g_param_spec_int ("top-attach",
1228 P_("Top attachment"),
1229 P_("The row number to attach the top side of a child "
1231 -G_MAXINT, G_MAXINT, 0,
1232 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1234 child_props[PROP_CHILD_WIDTH] =
1235 g_param_spec_int ("width",
1237 P_("The number of columns that a child spans"),
1238 -G_MAXINT, G_MAXINT, 1,
1239 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1241 child_props[PROP_CHILD_HEIGHT] =
1242 g_param_spec_int ("height",
1244 P_("The number of rows that a child spans"),
1245 -G_MAXINT, G_MAXINT, 1,
1246 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1248 g_object_class_install_properties (gobject_class, PROP_CHILD_LAST,
1253 clutter_grid_child_init (ClutterGridChild *self)
1255 CHILD_LEFT (self) = -1;
1256 CHILD_TOP (self) = -1;
1257 CHILD_WIDTH (self) = 1;
1258 CHILD_HEIGHT (self) = 1;
1262 clutter_grid_layout_set_container (ClutterLayoutManager *self,
1263 ClutterContainer *container)
1265 ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv;
1266 ClutterLayoutManagerClass *parent_class;
1268 priv->container = container;
1270 if (priv->container != NULL)
1272 ClutterRequestMode request_mode;
1274 /* we need to change the :request-mode of the container
1275 * to match the orientation
1277 request_mode = priv->orientation == CLUTTER_ORIENTATION_VERTICAL
1278 ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH
1279 : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT;
1280 clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container),
1284 parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_grid_layout_parent_class);
1285 parent_class->set_container (self, container);
1289 clutter_grid_layout_get_preferred_width (ClutterLayoutManager *self,
1290 ClutterContainer *container,
1292 gfloat *min_width_p,
1293 gfloat *nat_width_p)
1295 ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv;
1296 ClutterGridRequest request;
1297 ClutterGridLines *lines;
1300 *min_width_p = 0.0f;
1302 *nat_width_p = 0.0f;
1304 request.grid = CLUTTER_GRID_LAYOUT (self);
1305 clutter_grid_request_update_attach (&request);
1306 clutter_grid_request_count_lines (&request);
1307 lines = &request.lines[priv->orientation];
1308 lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
1309 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
1311 clutter_grid_request_run (&request, priv->orientation, FALSE);
1312 clutter_grid_request_sum (&request, priv->orientation,
1313 min_width_p, nat_width_p);
1317 clutter_grid_layout_get_preferred_height (ClutterLayoutManager *self,
1318 ClutterContainer *container,
1320 gfloat *min_height_p,
1321 gfloat *nat_height_p)
1323 ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv;
1324 ClutterGridRequest request;
1325 ClutterGridLines *lines;
1328 *min_height_p = 0.0f;
1330 *nat_height_p = 0.0f;
1332 request.grid = CLUTTER_GRID_LAYOUT (self);
1333 clutter_grid_request_update_attach (&request);
1334 clutter_grid_request_count_lines (&request);
1335 lines = &request.lines[priv->orientation];
1336 lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
1337 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
1339 clutter_grid_request_run (&request, priv->orientation, FALSE);
1340 clutter_grid_request_sum (&request, priv->orientation,
1341 min_height_p, nat_height_p);
1345 allocate_child (ClutterGridRequest *request,
1346 ClutterOrientation orientation,
1347 ClutterGridChild *child,
1351 ClutterGridLayoutPrivate *priv = request->grid->priv;
1352 ClutterGridLineData *linedata;
1353 ClutterGridLines *lines;
1354 ClutterGridLine *line;
1355 ClutterGridAttach *attach;
1358 linedata = &priv->linedata[orientation];
1359 lines = &request->lines[orientation];
1360 attach = &child->attach[orientation];
1362 *position = lines->lines[attach->pos - lines->min].position;
1364 *size = (attach->span - 1) * linedata->spacing;
1365 for (i = 0; i < attach->span; i++)
1367 line = &lines->lines[attach->pos - lines->min + i];
1368 *size += line->allocation;
1372 #define GET_SIZE(allocation, orientation) \
1373 (orientation == CLUTTER_ORIENTATION_HORIZONTAL \
1374 ? clutter_actor_box_get_width ((allocation)) \
1375 : clutter_actor_box_get_height ((allocation)))
1378 clutter_grid_layout_allocate (ClutterLayoutManager *layout,
1379 ClutterContainer *container,
1380 const ClutterActorBox *allocation,
1381 ClutterAllocationFlags flags)
1383 ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (layout);
1384 ClutterGridLayoutPrivate *priv = self->priv;
1385 ClutterGridRequest request;
1386 ClutterGridLines *lines;
1387 ClutterActorIter iter;
1388 ClutterActor *child;
1389 gboolean use_animations;
1390 ClutterAnimationMode mode;
1391 guint duration, delay;
1393 request.grid = self;
1395 clutter_grid_request_update_attach (&request);
1396 clutter_grid_request_count_lines (&request);
1397 lines = &request.lines[0];
1398 lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
1399 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
1400 lines = &request.lines[1];
1401 lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
1402 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
1404 clutter_grid_request_run (&request, 1 - priv->orientation, FALSE);
1405 clutter_grid_request_allocate (&request, 1 - priv->orientation, GET_SIZE (allocation, 1 - priv->orientation));
1406 clutter_grid_request_run (&request, priv->orientation, TRUE);
1407 clutter_grid_request_allocate (&request, priv->orientation, GET_SIZE (allocation, priv->orientation));
1409 clutter_grid_request_position (&request, 0);
1410 clutter_grid_request_position (&request, 1);
1412 use_animations = clutter_layout_manager_get_easing_state (layout,
1417 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
1418 while (clutter_actor_iter_next (&iter, &child))
1420 ClutterActorBox child_allocation;
1421 gfloat x, y, width, height;
1422 ClutterGridChild *grid_child;
1424 if (!CLUTTER_ACTOR_IS_VISIBLE (child))
1427 grid_child = GET_GRID_CHILD (self, child);
1428 allocate_child (&request, CLUTTER_ORIENTATION_HORIZONTAL, grid_child,
1430 allocate_child (&request, CLUTTER_ORIENTATION_VERTICAL, grid_child,
1432 x += allocation->x1;
1433 y += allocation->y1;
1434 clutter_actor_box_set_origin (&child_allocation, x, y);
1435 clutter_actor_box_set_size (&child_allocation, width, height);
1437 CLUTTER_NOTE (LAYOUT, "Allocation for %s { %.2f, %.2f - %.2f x %.2f }",
1438 _clutter_actor_get_debug_name (child),
1439 x, y, width, height);
1443 clutter_actor_save_easing_state (child);
1444 clutter_actor_set_easing_mode (child, mode);
1445 clutter_actor_set_easing_duration (child, duration);
1446 clutter_actor_set_easing_delay (child, delay);
1449 clutter_actor_allocate (child, &child_allocation, flags);
1452 clutter_actor_restore_easing_state (child);
1457 clutter_grid_layout_get_child_meta_type (ClutterLayoutManager *self)
1459 return CLUTTER_TYPE_GRID_CHILD;
1463 clutter_grid_layout_set_property (GObject *gobject,
1465 const GValue *value,
1468 ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (gobject);
1472 case PROP_ORIENTATION:
1473 clutter_grid_layout_set_orientation (self, g_value_get_enum (value));
1476 case PROP_ROW_SPACING:
1477 clutter_grid_layout_set_row_spacing (self, g_value_get_uint (value));
1480 case PROP_COLUMN_SPACING:
1481 clutter_grid_layout_set_column_spacing (self, g_value_get_uint (value));
1484 case PROP_ROW_HOMOGENEOUS:
1485 clutter_grid_layout_set_row_homogeneous (self,
1486 g_value_get_boolean (value));
1489 case PROP_COLUMN_HOMOGENEOUS:
1490 clutter_grid_layout_set_column_homogeneous (self,
1491 g_value_get_boolean (value));
1495 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1501 clutter_grid_layout_get_property (GObject *gobject,
1506 ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (gobject)->priv;
1510 case PROP_ORIENTATION:
1511 g_value_set_enum (value, priv->orientation);
1514 case PROP_ROW_SPACING:
1515 g_value_set_uint (value, COLUMNS (priv)->spacing);
1518 case PROP_COLUMN_SPACING:
1519 g_value_set_uint (value, ROWS (priv)->spacing);
1522 case PROP_ROW_HOMOGENEOUS:
1523 g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
1526 case PROP_COLUMN_HOMOGENEOUS:
1527 g_value_set_boolean (value, ROWS (priv)->homogeneous);
1531 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1537 clutter_grid_layout_class_init (ClutterGridLayoutClass *klass)
1539 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1540 ClutterLayoutManagerClass *layout_class;
1542 layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
1544 g_type_class_add_private (klass, sizeof (ClutterGridLayoutPrivate));
1546 object_class->set_property = clutter_grid_layout_set_property;
1547 object_class->get_property = clutter_grid_layout_get_property;
1549 layout_class->set_container = clutter_grid_layout_set_container;
1550 layout_class->get_preferred_width = clutter_grid_layout_get_preferred_width;
1551 layout_class->get_preferred_height = clutter_grid_layout_get_preferred_height;
1552 layout_class->allocate = clutter_grid_layout_allocate;
1553 layout_class->get_child_meta_type = clutter_grid_layout_get_child_meta_type;
1556 * ClutterGridLayout:orientation:
1558 * The orientation of the layout, either horizontal or vertical
1562 obj_props[PROP_ORIENTATION] =
1563 g_param_spec_enum ("orientation",
1565 P_("The orientation of the layout"),
1566 CLUTTER_TYPE_ORIENTATION,
1567 CLUTTER_ORIENTATION_HORIZONTAL,
1568 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1571 * ClutterGridLayout:row-spacing:
1573 * The amount of space in pixels between two consecutive rows
1577 obj_props[PROP_ROW_SPACING] =
1578 g_param_spec_uint ("row-spacing",
1580 P_("The amount of space between two consecutive rows"),
1582 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1585 * ClutterGridLayout:column-spacing:
1587 * The amount of space in pixels between two consecutive columns
1591 obj_props[PROP_COLUMN_SPACING] =
1592 g_param_spec_uint ("column-spacing",
1593 P_("Column spacing"),
1594 P_("The amount of space between two consecutive "
1597 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1600 * ClutterGridLayout:row-homogeneous:
1602 * Whether all rows of the layout should have the same height
1606 obj_props[PROP_ROW_HOMOGENEOUS] =
1607 g_param_spec_boolean ("row-homogeneous",
1608 P_("Row Homogeneous"),
1609 P_("If TRUE, the rows are all the same height"),
1611 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1614 * ClutterGridLayout:column-homogeneous:
1616 * Whether all columns of the layout should have the same width
1620 obj_props[PROP_COLUMN_HOMOGENEOUS] =
1621 g_param_spec_boolean ("column-homogeneous",
1622 P_("Column Homogeneous"),
1623 P_("If TRUE, the columns are all the same width"),
1625 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1627 g_object_class_install_properties (object_class, PROP_LAST, obj_props);
1631 clutter_grid_layout_init (ClutterGridLayout *self)
1633 ClutterGridLayoutPrivate *priv;
1635 self->priv = priv = CLUTTER_GRID_LAYOUT_GET_PRIVATE (self);
1637 priv->orientation = CLUTTER_ORIENTATION_HORIZONTAL;
1639 priv->linedata[0].spacing = 0;
1640 priv->linedata[1].spacing = 0;
1642 priv->linedata[0].homogeneous = FALSE;
1643 priv->linedata[1].homogeneous = FALSE;
1647 * clutter_grid_layout_new:
1649 * Creates a new #ClutterGridLayout
1651 * Return value: the new #ClutterGridLayout
1653 ClutterLayoutManager *
1654 clutter_grid_layout_new (void)
1656 return g_object_new (CLUTTER_TYPE_GRID_LAYOUT, NULL);
1660 * clutter_grid_layout_attach:
1661 * @layout: a #ClutterGridLayout
1662 * @child: the #ClutterActor to add
1663 * @left: the column number to attach the left side of @child to
1664 * @top: the row number to attach the top side of @child to
1665 * @width: the number of columns that @child will span
1666 * @height: the number of rows that @child will span
1668 * Adds a widget to the grid.
1670 * The position of @child is determined by @left and @top. The
1671 * number of 'cells' that @child will occupy is determined by
1672 * @width and @height.
1677 clutter_grid_layout_attach (ClutterGridLayout *layout,
1678 ClutterActor *child,
1684 ClutterGridLayoutPrivate *priv;
1686 g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1688 priv = layout->priv;
1690 if (!priv->container)
1693 grid_attach (layout, child, left, top, width, height);
1694 clutter_actor_add_child (CLUTTER_ACTOR (priv->container), child);
1698 * clutter_grid_layout_attach_next_to:
1699 * @layout: a #ClutterGridLayout
1700 * @child: the actor to add
1701 * @sibling: (allow-none): the child of @layout that @child will be placed
1702 * next to, or %NULL to place @child at the beginning or end
1703 * @side: the side of @sibling that @child is positioned next to
1704 * @width: the number of columns that @child will span
1705 * @height: the number of rows that @child will span
1707 * Adds a actor to the grid.
1709 * The actor is placed next to @sibling, on the side determined by
1710 * @side. When @sibling is %NULL, the actor is placed in row (for
1711 * left or right placement) or column 0 (for top or bottom placement),
1712 * at the end indicated by @side.
1714 * Attaching widgets labeled [1], [2], [3] with @sibling == %NULL and
1715 * @side == %CLUTTER_GRID_POSITION_LEFT yields a layout of [3][2][1].
1720 clutter_grid_layout_attach_next_to (ClutterGridLayout *layout,
1721 ClutterActor *child,
1722 ClutterActor *sibling,
1723 ClutterGridPosition side,
1727 ClutterGridLayoutPrivate *priv;
1729 g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1730 g_return_if_fail (CLUTTER_IS_ACTOR (child));
1731 g_return_if_fail (clutter_actor_get_parent (child) == NULL);
1732 g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
1733 g_return_if_fail (width > 0);
1734 g_return_if_fail (height > 0);
1736 priv = layout->priv;
1738 if (!priv->container)
1741 grid_attach_next_to (layout, child, sibling, side, width, height);
1742 clutter_actor_add_child (CLUTTER_ACTOR (priv->container), child);
1746 * clutter_grid_layout_set_orientation:
1747 * @layout: a #ClutterGridLayout
1748 * @orientation: the orientation of the #ClutterGridLayout
1750 * Sets the orientation of the @layout
1755 clutter_grid_layout_set_orientation (ClutterGridLayout *layout,
1756 ClutterOrientation orientation)
1758 ClutterGridLayoutPrivate *priv;
1760 g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1762 priv = layout->priv;
1764 if (priv->orientation != orientation)
1766 priv->orientation = orientation;
1768 clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
1769 g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_ORIENTATION]);
1774 * clutter_grid_layout_get_child_at:
1775 * @layout: a #ClutterGridLayout
1776 * @left: the left edge of the cell
1777 * @top: the top edge of the cell
1779 * Gets the child of @layout whose area covers the grid
1780 * cell whose upper left corner is at @left, @top.
1782 * Returns: (transfer none): the child at the given position, or %NULL
1787 clutter_grid_layout_get_child_at (ClutterGridLayout *layout,
1791 ClutterGridLayoutPrivate *priv;
1792 ClutterGridChild *grid_child;
1793 ClutterActorIter iter;
1794 ClutterActor *child;
1796 g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), NULL);
1798 priv = layout->priv;
1800 if (!priv->container)
1803 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
1804 while (clutter_actor_iter_next (&iter, &child))
1806 grid_child = GET_GRID_CHILD (layout, child);
1808 if (CHILD_LEFT (grid_child) <= left &&
1809 CHILD_LEFT (grid_child) + CHILD_WIDTH (grid_child) > left &&
1810 CHILD_TOP (grid_child) <= top &&
1811 CHILD_TOP (grid_child) + CHILD_HEIGHT (grid_child) > top)
1819 * clutter_grid_layout_insert_row:
1820 * @layout: a #ClutterGridLayout
1821 * @position: the position to insert the row at
1823 * Inserts a row at the specified position.
1825 * Children which are attached at or below this position
1826 * are moved one row down. Children which span across this
1827 * position are grown to span the new row.
1832 clutter_grid_layout_insert_row (ClutterGridLayout *layout,
1835 ClutterGridLayoutPrivate *priv;
1836 ClutterGridChild *grid_child;
1837 ClutterActorIter iter;
1838 ClutterActor *child;
1841 g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1843 priv = layout->priv;
1845 if (!priv->container)
1848 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
1849 while (clutter_actor_iter_next (&iter, &child))
1851 grid_child = GET_GRID_CHILD (layout, child);
1853 top = CHILD_TOP (grid_child);
1854 height = CHILD_HEIGHT (grid_child);
1856 if (top >= position)
1858 CHILD_TOP (grid_child) = top + 1;
1859 g_object_notify_by_pspec (G_OBJECT (grid_child),
1860 child_props[PROP_CHILD_TOP_ATTACH]);
1862 else if (top + height > position)
1864 CHILD_HEIGHT (grid_child) = height + 1;
1865 g_object_notify_by_pspec (G_OBJECT (grid_child),
1866 child_props[PROP_CHILD_HEIGHT]);
1869 clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
1873 * clutter_grid_layout_insert_column:
1874 * @layout: a #ClutterGridLayout
1875 * @position: the position to insert the column at
1877 * Inserts a column at the specified position.
1879 * Children which are attached at or to the right of this position
1880 * are moved one column to the right. Children which span across this
1881 * position are grown to span the new column.
1886 clutter_grid_layout_insert_column (ClutterGridLayout *layout,
1889 ClutterGridLayoutPrivate *priv;
1890 ClutterGridChild *grid_child;
1891 ClutterActorIter iter;
1892 ClutterActor *child;
1895 g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1897 priv = layout->priv;
1899 if (!priv->container)
1902 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
1903 while (clutter_actor_iter_next (&iter, &child))
1905 grid_child = GET_GRID_CHILD (layout, child);
1907 left = CHILD_LEFT (grid_child);
1908 width = CHILD_WIDTH (grid_child);
1910 if (left >= position)
1912 CHILD_LEFT (grid_child) = left + 1;
1913 g_object_notify_by_pspec (G_OBJECT (grid_child),
1914 child_props[PROP_CHILD_LEFT_ATTACH]);
1916 else if (left + width > position)
1918 CHILD_WIDTH (grid_child) = width + 1;
1919 g_object_notify_by_pspec (G_OBJECT (grid_child),
1920 child_props[PROP_CHILD_WIDTH]);
1923 clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
1927 * clutter_grid_layout_insert_next_to:
1928 * @layout: a #ClutterGridLayout
1929 * @sibling: the child of @layout that the new row or column will be
1931 * @side: the side of @sibling that @child is positioned next to
1933 * Inserts a row or column at the specified position.
1935 * The new row or column is placed next to @sibling, on the side
1936 * determined by @side. If @side is %CLUTTER_GRID_POSITION_LEFT or
1937 * %CLUTTER_GRID_POSITION_BOTTOM, a row is inserted. If @side is
1938 * %CLUTTER_GRID_POSITION_LEFT of %CLUTTER_GRID_POSITION_RIGHT,
1939 * a column is inserted.
1944 clutter_grid_layout_insert_next_to (ClutterGridLayout *layout,
1945 ClutterActor *sibling,
1946 ClutterGridPosition side)
1948 ClutterGridChild *grid_child;
1950 g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1951 g_return_if_fail (CLUTTER_IS_ACTOR (sibling));
1953 grid_child = GET_GRID_CHILD (layout, sibling);
1957 case CLUTTER_GRID_POSITION_LEFT:
1958 clutter_grid_layout_insert_column (layout, CHILD_LEFT (grid_child));
1961 case CLUTTER_GRID_POSITION_RIGHT:
1962 clutter_grid_layout_insert_column (layout, CHILD_LEFT (grid_child) +
1963 CHILD_WIDTH (grid_child));
1966 case CLUTTER_GRID_POSITION_TOP:
1967 clutter_grid_layout_insert_row (layout, CHILD_TOP (grid_child));
1970 case CLUTTER_GRID_POSITION_BOTTOM:
1971 clutter_grid_layout_insert_row (layout, CHILD_TOP (grid_child) +
1972 CHILD_HEIGHT (grid_child));
1976 g_assert_not_reached ();
1981 * clutter_grid_layout_get_orientation:
1982 * @layout: a #ClutterGridLayout
1984 * Retrieves the orientation of the @layout.
1986 * Return value: the orientation of the layout
1991 clutter_grid_layout_get_orientation (ClutterGridLayout *layout)
1993 g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout),
1994 CLUTTER_ORIENTATION_HORIZONTAL);
1996 return layout->priv->orientation;
2000 * clutter_grid_layout_set_row_spacing:
2001 * @layout: a #ClutterGridLayout
2002 * @spacing: the spacing between rows of the layout, in pixels
2004 * Sets the spacing between rows of @layout
2009 clutter_grid_layout_set_row_spacing (ClutterGridLayout *layout,
2012 ClutterGridLayoutPrivate *priv;
2014 g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
2016 priv = layout->priv;
2018 if (COLUMNS (priv)->spacing != spacing)
2020 COLUMNS (priv)->spacing = spacing;
2022 clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
2023 g_object_notify_by_pspec (G_OBJECT (layout),
2024 obj_props[PROP_ROW_SPACING]);
2029 * clutter_grid_layout_get_row_spacing:
2030 * @layout: a #ClutterGridLayout
2032 * Retrieves the spacing set using clutter_grid_layout_set_row_spacing()
2034 * Return value: the spacing between rows of @layout
2039 clutter_grid_layout_get_row_spacing (ClutterGridLayout *layout)
2041 ClutterGridLayoutPrivate *priv;
2043 g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), 0);
2045 priv = layout->priv;
2047 return COLUMNS (priv)->spacing;
2051 * clutter_grid_layout_set_column_spacing:
2052 * @layout: a #ClutterGridLayout
2053 * @spacing: the spacing between columns of the layout, in pixels
2055 * Sets the spacing between columns of @layout
2060 clutter_grid_layout_set_column_spacing (ClutterGridLayout *layout,
2063 ClutterGridLayoutPrivate *priv;
2065 g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
2067 priv = layout->priv;
2069 if (ROWS (priv)->spacing != spacing)
2071 ROWS (priv)->spacing = spacing;
2073 clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
2074 g_object_notify_by_pspec (G_OBJECT (layout),
2075 obj_props[PROP_COLUMN_SPACING]);
2080 * clutter_grid_layout_get_column_spacing:
2081 * @layout: a #ClutterGridLayout
2083 * Retrieves the spacing set using clutter_grid_layout_set_column_spacing()
2085 * Return value: the spacing between coluns of @layout
2090 clutter_grid_layout_get_column_spacing (ClutterGridLayout *layout)
2092 ClutterGridLayoutPrivate *priv;
2094 g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), 0);
2096 priv = layout->priv;
2098 return ROWS (priv)->spacing;
2102 * clutter_grid_layout_set_column_homogeneous:
2103 * @layout: a #ClutterGridLayout
2104 * @homogeneous: %TRUE to make columns homogeneous
2106 * Sets whether all columns of @layout will have the same width.
2111 clutter_grid_layout_set_column_homogeneous (ClutterGridLayout *layout,
2112 gboolean homogeneous)
2114 ClutterGridLayoutPrivate *priv;
2116 g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
2118 priv = layout->priv;
2120 if (ROWS (priv)->homogeneous != homogeneous)
2122 ROWS (priv)->homogeneous = homogeneous;
2124 clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
2125 g_object_notify_by_pspec (G_OBJECT (layout),
2126 obj_props[PROP_COLUMN_HOMOGENEOUS]);
2131 * clutter_grid_layout_get_column_homogeneous:
2132 * @layout: a #ClutterGridLayout
2134 * Returns whether all columns of @layout have the same width.
2136 * Returns: whether all columns of @layout have the same width.
2139 clutter_grid_layout_get_column_homogeneous (ClutterGridLayout *layout)
2141 ClutterGridLayoutPrivate *priv;
2143 g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), FALSE);
2145 priv = layout->priv;
2147 return ROWS (priv)->homogeneous;
2151 * clutter_grid_layout_set_row_homogeneous:
2152 * @layout: a #ClutterGridLayout
2153 * @homogeneous: %TRUE to make rows homogeneous
2155 * Sets whether all rows of @layout will have the same height.
2160 clutter_grid_layout_set_row_homogeneous (ClutterGridLayout *layout,
2161 gboolean homogeneous)
2163 ClutterGridLayoutPrivate *priv;
2165 g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
2167 priv = layout->priv;
2169 if (COLUMNS (priv)->homogeneous != homogeneous)
2171 COLUMNS (priv)->homogeneous = homogeneous;
2173 clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
2174 g_object_notify_by_pspec (G_OBJECT (layout),
2175 obj_props[PROP_ROW_HOMOGENEOUS]);
2180 * clutter_grid_layout_get_row_homogeneous:
2181 * @layout: a #ClutterGridLayout
2183 * Returns whether all rows of @layout have the same height.
2185 * Returns: whether all rows of @layout have the same height.
2190 clutter_grid_layout_get_row_homogeneous (ClutterGridLayout *layout)
2192 ClutterGridLayoutPrivate *priv;
2194 g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), FALSE);
2196 priv = layout->priv;
2198 return COLUMNS (priv)->homogeneous;