1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Red Hat, Inc.
3 * Author: Matthias Clasen
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
25 #include "gtkorientable.h"
26 #include "gtksizerequest.h"
27 #include "gtkprivate.h"
33 * @Short_description: Pack widgets in a rows and columns
35 * @See_also: #GtkTable, #GtkHBox, #GtkVBox
37 * GtkGrid is a container which arranges its child widgets in
38 * rows and columns. It is a very similar to #GtkTable and #GtkBox,
39 * but it consistently uses #GtkWidget's #GtkWidget:margin and #GtkWidget:expand
40 * properties instead of custom child properties, and it fully supports
41 * <link linkend="geometry-management">height-for-width geometry management</link>.
43 * Children are added using gtk_grid_attach(). They can span multiple
44 * rows or columns. It is also possible to add a child next to an
45 * existing child, using gtk_grid_attach_next_to().
47 * GtkGrid can be used like a #GtkBox by just using gtk_container_add(),
48 * which will place children next to each other in the direction determined
49 * by the #GtkOrientable:orientation property.
52 typedef struct _GtkGridChild GtkGridChild;
53 typedef struct _GtkGridChildAttach GtkGridChildAttach;
54 typedef struct _GtkGridLine GtkGridLine;
55 typedef struct _GtkGridLines GtkGridLines;
56 typedef struct _GtkGridLineData GtkGridLineData;
57 typedef struct _GtkGridRequest GtkGridRequest;
59 struct _GtkGridChildAttach
68 GtkGridChildAttach attach[2];
71 #define CHILD_LEFT(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].pos)
72 #define CHILD_WIDTH(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].span)
73 #define CHILD_TOP(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].pos)
74 #define CHILD_HEIGHT(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].span)
76 /* A GtkGridLineData struct contains row/column specific parts
79 struct _GtkGridLineData
82 guint homogeneous : 1;
85 struct _GtkGridPrivate
89 GtkOrientation orientation;
91 GtkGridLineData linedata[2];
94 #define ROWS(priv) (&(priv)->linedata[GTK_ORIENTATION_HORIZONTAL])
95 #define COLUMNS(priv) (&(priv)->linedata[GTK_ORIENTATION_VERTICAL])
97 /* A GtkGridLine struct represents a single row or column
98 * during size requests
107 guint need_expand : 1;
118 struct _GtkGridRequest
121 GtkGridLines lines[2];
131 PROP_ROW_HOMOGENEOUS,
132 PROP_COLUMN_HOMOGENEOUS
138 CHILD_PROP_LEFT_ATTACH,
139 CHILD_PROP_TOP_ATTACH,
144 G_DEFINE_TYPE_WITH_CODE (GtkGrid, gtk_grid, GTK_TYPE_CONTAINER,
145 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
149 gtk_grid_get_property (GObject *object,
154 GtkGrid *grid = GTK_GRID (object);
155 GtkGridPrivate *priv = grid->priv;
159 case PROP_ORIENTATION:
160 g_value_set_enum (value, priv->orientation);
163 case PROP_ROW_SPACING:
164 g_value_set_int (value, ROWS (priv)->spacing);
167 case PROP_COLUMN_SPACING:
168 g_value_set_int (value, COLUMNS (priv)->spacing);
171 case PROP_ROW_HOMOGENEOUS:
172 g_value_set_boolean (value, ROWS (priv)->homogeneous);
175 case PROP_COLUMN_HOMOGENEOUS:
176 g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
186 gtk_grid_set_orientation (GtkGrid *grid,
187 GtkOrientation orientation)
189 GtkGridPrivate *priv = grid->priv;
191 if (priv->orientation != orientation)
193 priv->orientation = orientation;
194 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (grid));
196 g_object_notify (G_OBJECT (grid), "orientation");
201 gtk_grid_set_property (GObject *object,
206 GtkGrid *grid = GTK_GRID (object);
210 case PROP_ORIENTATION:
211 gtk_grid_set_orientation (grid, g_value_get_enum (value));
214 case PROP_ROW_SPACING:
215 gtk_grid_set_row_spacing (grid, g_value_get_int (value));
218 case PROP_COLUMN_SPACING:
219 gtk_grid_set_column_spacing (grid, g_value_get_int (value));
222 case PROP_ROW_HOMOGENEOUS:
223 gtk_grid_set_row_homogeneous (grid, g_value_get_boolean (value));
226 case PROP_COLUMN_HOMOGENEOUS:
227 gtk_grid_set_column_homogeneous (grid, g_value_get_boolean (value));
231 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
236 static GtkGridChild *
237 find_grid_child (GtkGrid *grid,
240 GtkGridPrivate *priv = grid->priv;
244 for (list = priv->children; list; list = list->next)
248 if (child->widget == widget)
256 gtk_grid_get_child_property (GtkContainer *container,
262 GtkGrid *grid = GTK_GRID (container);
263 GtkGridChild *grid_child;
265 grid_child = find_grid_child (grid, child);
267 if (grid_child == NULL)
269 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
275 case CHILD_PROP_LEFT_ATTACH:
276 g_value_set_int (value, CHILD_LEFT (grid_child));
279 case CHILD_PROP_TOP_ATTACH:
280 g_value_set_int (value, CHILD_TOP (grid_child));
283 case CHILD_PROP_WIDTH:
284 g_value_set_int (value, CHILD_WIDTH (grid_child));
287 case CHILD_PROP_HEIGHT:
288 g_value_set_int (value, CHILD_HEIGHT (grid_child));
292 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
298 gtk_grid_set_child_property (GtkContainer *container,
304 GtkGrid *grid = GTK_GRID (container);
305 GtkGridChild *grid_child;
307 grid_child = find_grid_child (grid, child);
309 if (grid_child == NULL)
311 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
317 case CHILD_PROP_LEFT_ATTACH:
318 CHILD_LEFT (grid_child) = g_value_get_int (value);
321 case CHILD_PROP_TOP_ATTACH:
322 CHILD_TOP (grid_child) = g_value_get_int (value);
325 case CHILD_PROP_WIDTH:
326 CHILD_WIDTH (grid_child) = g_value_get_int (value);
329 case CHILD_PROP_HEIGHT:
330 CHILD_HEIGHT (grid_child) = g_value_get_int (value);
334 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
338 if (gtk_widget_get_visible (child) &&
339 gtk_widget_get_visible (GTK_WIDGET (grid)))
340 gtk_widget_queue_resize (child);
344 gtk_grid_init (GtkGrid *grid)
346 GtkGridPrivate *priv;
348 grid->priv = G_TYPE_INSTANCE_GET_PRIVATE (grid, GTK_TYPE_GRID, GtkGridPrivate);
351 gtk_widget_set_has_window (GTK_WIDGET (grid), FALSE);
352 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (grid), FALSE);
354 priv->children = NULL;
355 priv->orientation = GTK_ORIENTATION_HORIZONTAL;
357 priv->linedata[0].spacing = 0;
358 priv->linedata[1].spacing = 0;
360 priv->linedata[0].homogeneous = FALSE;
361 priv->linedata[1].homogeneous = FALSE;
364 static void grid_attach (GtkGrid *grid,
372 gtk_grid_add (GtkContainer *container,
375 GtkGrid *grid = GTK_GRID (container);
376 GtkGridPrivate *priv = grid->priv;
377 GtkGridChild *grid_child;
378 GtkGridChildAttach *attach;
379 GtkGridChildAttach *opposite;
384 for (list = priv->children; list; list = list->next)
386 grid_child = list->data;
388 attach = &grid_child->attach[priv->orientation];
389 opposite = &grid_child->attach[1 - priv->orientation];
391 if (opposite->pos <= 0 && opposite->pos + opposite->span > 0)
392 pos = MAX (pos, attach->pos + attach->span);
395 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
396 grid_attach (grid, child, pos, 0, 1, 1);
398 grid_attach (grid, child, 0, pos, 1, 1);
402 gtk_grid_remove (GtkContainer *container,
405 GtkGrid *grid = GTK_GRID (container);
406 GtkGridPrivate *priv = grid->priv;
407 GtkGridChild *grid_child;
410 for (list = priv->children; list; list = list->next)
412 grid_child = list->data;
414 if (grid_child->widget == child)
416 gboolean was_visible = gtk_widget_get_visible (child);
418 gtk_widget_unparent (child);
420 priv->children = g_list_remove (priv->children, grid_child);
422 g_slice_free (GtkGridChild, grid_child);
424 if (was_visible && gtk_widget_get_visible (GTK_WIDGET (grid)))
425 gtk_widget_queue_resize (GTK_WIDGET (grid));
433 gtk_grid_forall (GtkContainer *container,
434 gboolean include_internals,
435 GtkCallback callback,
436 gpointer callback_data)
438 GtkGrid *grid = GTK_GRID (container);
439 GtkGridPrivate *priv = grid->priv;
443 list = priv->children;
449 (* callback) (child->widget, callback_data);
454 gtk_grid_child_type (GtkContainer *container)
456 return GTK_TYPE_WIDGET;
459 static GtkSizeRequestMode
460 gtk_grid_get_request_mode (GtkWidget *widget)
462 GtkGridPrivate *priv = GTK_GRID (widget)->priv;
464 if (priv->orientation == GTK_ORIENTATION_VERTICAL)
465 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
467 return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
470 /* Calculates the min and max numbers for both orientations.
473 gtk_grid_request_count_lines (GtkGridRequest *request)
475 GtkGridPrivate *priv = request->grid->priv;
477 GtkGridChildAttach *attach;
482 min[0] = min[1] = G_MAXINT;
483 max[0] = max[1] = G_MININT;
485 for (list = priv->children; list; list = list->next)
488 attach = child->attach;
490 min[0] = MIN (min[0], attach[0].pos);
491 max[0] = MAX (max[0], attach[0].pos + attach[0].span);
492 min[1] = MIN (min[1], attach[1].pos);
493 max[1] = MAX (max[1], attach[1].pos + attach[1].span);
496 request->lines[0].min = min[0];
497 request->lines[0].max = max[0];
498 request->lines[1].min = min[1];
499 request->lines[1].max = max[1];
502 /* Sets line sizes to 0 and marks lines as expand
503 * if they have a non-spanning expanding child.
506 gtk_grid_request_init (GtkGridRequest *request,
507 GtkOrientation orientation)
509 GtkGridPrivate *priv = request->grid->priv;
511 GtkGridChildAttach *attach;
516 lines = &request->lines[orientation];
518 for (i = 0; i < lines->max - lines->min; i++)
520 lines->lines[i].minimum = 0;
521 lines->lines[i].natural = 0;
522 lines->lines[i].expand = FALSE;
525 for (list = priv->children; list; list = list->next)
529 attach = &child->attach[orientation];
530 if (attach->span == 1 && gtk_widget_compute_expand (child->widget, orientation))
531 lines->lines[attach->pos - lines->min].expand = TRUE;
535 /* Sums allocations for lines spanned by child and their spacing.
538 compute_allocation_for_child (GtkGridRequest *request,
540 GtkOrientation orientation)
542 GtkGridPrivate *priv = request->grid->priv;
543 GtkGridLineData *linedata;
546 GtkGridChildAttach *attach;
550 linedata = &priv->linedata[orientation];
551 lines = &request->lines[orientation];
552 attach = &child->attach[orientation];
554 size = (attach->span - 1) * linedata->spacing;
555 for (i = 0; i < attach->span; i++)
557 line = &lines->lines[attach->pos - lines->min + i];
558 size += line->allocation;
565 compute_request_for_child (GtkGridRequest *request,
567 GtkOrientation orientation,
576 size = compute_allocation_for_child (request, child, 1 - orientation);
577 if (orientation == GTK_ORIENTATION_HORIZONTAL)
578 gtk_widget_get_preferred_width_for_height (child->widget,
582 gtk_widget_get_preferred_height_for_width (child->widget,
588 if (orientation == GTK_ORIENTATION_HORIZONTAL)
589 gtk_widget_get_preferred_width (child->widget, minimum, natural);
591 gtk_widget_get_preferred_height (child->widget, minimum, natural);
595 /* Sets requisition to max. of non-spanning children.
596 * If contextual is TRUE, requires allocations of
597 * lines in the opposite orientation to be set.
600 gtk_grid_request_non_spanning (GtkGridRequest *request,
601 GtkOrientation orientation,
604 GtkGridPrivate *priv = request->grid->priv;
606 GtkGridChildAttach *attach;
613 lines = &request->lines[orientation];
615 for (list = priv->children; list; list = list->next)
619 if (!gtk_widget_get_visible (child->widget))
622 attach = &child->attach[orientation];
623 if (attach->span != 1)
626 compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
628 line = &lines->lines[attach->pos - lines->min];
629 line->minimum = MAX (line->minimum, minimum);
630 line->natural = MAX (line->natural, natural);
634 /* Enforce homogeneous sizes.
637 gtk_grid_request_homogeneous (GtkGridRequest *request,
638 GtkOrientation orientation)
640 GtkGridPrivate *priv = request->grid->priv;
641 GtkGridLineData *linedata;
643 gint minimum, natural;
646 linedata = &priv->linedata[orientation];
647 lines = &request->lines[orientation];
649 if (!linedata->homogeneous)
655 for (i = 0; i < lines->max - lines->min; i++)
657 minimum = MAX (minimum, lines->lines[i].minimum);
658 natural = MAX (natural, lines->lines[i].natural);
661 for (i = 0; i < lines->max - lines->min; i++)
663 lines->lines[i].minimum = minimum;
664 lines->lines[i].natural = natural;
668 /* Deals with spanning children.
669 * Requires expand fields of lines to be set for
670 * non-spanning children.
673 gtk_grid_request_spanning (GtkGridRequest *request,
674 GtkOrientation orientation,
677 GtkGridPrivate *priv = request->grid->priv;
680 GtkGridChildAttach *attach;
681 GtkGridLineData *linedata;
689 gboolean force_expand;
695 linedata = &priv->linedata[orientation];
696 lines = &request->lines[orientation];
698 for (list = priv->children; list; list = list->next)
702 if (!gtk_widget_get_visible (child->widget))
705 attach = &child->attach[orientation];
706 if (attach->span == 1)
709 compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
711 span_minimum = (attach->span - 1) * linedata->spacing;
712 span_natural = (attach->span - 1) * linedata->spacing;
714 force_expand = FALSE;
715 for (i = 0; i < attach->span; i++)
717 line = &lines->lines[attach->pos - lines->min + i];
718 span_minimum += line->minimum;
719 span_natural += line->natural;
723 if (span_expand == 0)
725 span_expand = attach->span;
729 /* If we need to request more space for this child to fill
730 * its requisition, then divide up the needed space amongst the
731 * lines it spans, favoring expandable lines if any.
733 if (span_minimum < minimum)
735 extra = minimum - span_minimum;
736 expand = span_expand;
737 for (i = 0; i < attach->span; i++)
739 line = &lines->lines[attach->pos - lines->min + i];
740 if (force_expand || line->expand)
742 line_extra = extra / expand;
743 line->minimum += line_extra;
750 if (span_natural < natural)
752 extra = natural - span_natural;
753 expand = span_expand;
754 for (i = 0; i < attach->span; i++)
756 line = &lines->lines[attach->pos - lines->min + i];
757 if (force_expand || line->expand)
759 line_extra = extra / expand;
760 line->natural += line_extra;
769 /* Marks empty and expanding lines and counts them.
772 gtk_grid_request_compute_expand (GtkGridRequest *request,
773 GtkOrientation orientation,
774 gint *nonempty_lines,
777 GtkGridPrivate *priv = request->grid->priv;
779 GtkGridChildAttach *attach;
788 lines = &request->lines[orientation];
790 for (i = 0; i < lines->max - lines->min; i++)
792 lines->lines[i].need_expand = FALSE;
793 lines->lines[i].expand = FALSE;
794 lines->lines[i].empty = TRUE;
797 for (list = priv->children; list; list = list->next)
801 if (!gtk_widget_get_visible (child->widget))
804 attach = &child->attach[orientation];
805 if (attach->span != 1)
808 line = &lines->lines[attach->pos - lines->min];
810 if (gtk_widget_compute_expand (child->widget, orientation))
814 for (list = priv->children; list; list = list->next)
818 if (!gtk_widget_get_visible (child->widget))
821 attach = &child->attach[orientation];
822 if (attach->span == 1)
826 for (i = 0; i < attach->span; i++)
828 line = &lines->lines[attach->pos - lines->min + i];
834 if (!has_expand && gtk_widget_compute_expand (child->widget, orientation))
836 for (i = 0; i < attach->span; i++)
838 line = &lines->lines[attach->pos - lines->min + i];
839 line->need_expand = TRUE;
846 for (i = 0; i < lines->max - lines->min; i++)
848 line = &lines->lines[i];
850 if (line->need_expand)
861 *nonempty_lines = lines->max - lines->min - empty;
864 *expand_lines = expand;
867 /* Sums the minimum and natural fields of lines and their spacing.
870 gtk_grid_request_sum (GtkGridRequest *request,
871 GtkOrientation orientation,
875 GtkGridPrivate *priv = request->grid->priv;
876 GtkGridLineData *linedata;
882 gtk_grid_request_compute_expand (request, orientation, &nonempty, NULL);
884 linedata = &priv->linedata[orientation];
885 lines = &request->lines[orientation];
887 min = (nonempty - 1) * linedata->spacing;
888 nat = (nonempty - 1) * linedata->spacing;
890 for (i = 0; i < lines->max - lines->min; i++)
892 min += lines->lines[i].minimum;
893 nat += lines->lines[i].natural;
903 /* Computes minimum and natural fields of lines.
904 * When contextual is TRUE, requires allocation of
905 * lines in the opposite orientation to be set.
908 gtk_grid_request_run (GtkGridRequest *request,
909 GtkOrientation orientation,
912 gtk_grid_request_init (request, orientation);
913 gtk_grid_request_non_spanning (request, orientation, contextual);
914 gtk_grid_request_homogeneous (request, orientation);
915 gtk_grid_request_spanning (request, orientation, contextual);
916 gtk_grid_request_homogeneous (request, orientation);
919 /* Requires that the minimum and natural fields of lines
920 * have been set, computes the allocation field of lines
921 * by distributing total_size among lines.
924 gtk_grid_request_allocate (GtkGridRequest *request,
925 GtkOrientation orientation,
928 GtkGridPrivate *priv = request->grid->priv;
929 GtkGridLineData *linedata;
935 GtkRequestedSize *sizes;
940 gtk_grid_request_compute_expand (request, orientation, &nonempty, &expand);
942 linedata = &priv->linedata[orientation];
943 lines = &request->lines[orientation];
945 size = total_size - (nonempty - 1) * linedata->spacing;
947 if (linedata->homogeneous)
949 extra = size / nonempty;
950 rest = size % nonempty;
952 for (i = 0; i < lines->max - lines->min; i++)
954 line = &lines->lines[i];
958 line->allocation = extra;
961 line->allocation += 1;
968 sizes = g_newa (GtkRequestedSize, nonempty);
971 for (i = 0; i < lines->max - lines->min; i++)
973 line = &lines->lines[i];
977 size -= line->minimum;
979 sizes[j].minimum_size = line->minimum;
980 sizes[j].natural_size = line->natural;
981 sizes[j].data = line;
985 size = gtk_distribute_natural_allocation (MAX (0, size), nonempty, sizes);
989 extra = size / expand;
990 rest = size % expand;
999 for (i = 0; i < lines->max - lines->min; i++)
1001 line = &lines->lines[i];
1005 g_assert (line == sizes[j].data);
1007 line->allocation = sizes[j].minimum_size;
1010 line->allocation += extra;
1013 line->allocation += 1;
1023 /* Computes the position fields from allocation and spacing.
1026 gtk_grid_request_position (GtkGridRequest *request,
1027 GtkOrientation orientation)
1029 GtkGridPrivate *priv = request->grid->priv;
1030 GtkGridLineData *linedata;
1031 GtkGridLines *lines;
1036 linedata = &priv->linedata[orientation];
1037 lines = &request->lines[orientation];
1040 for (i = 0; i < lines->max - lines->min; i++)
1042 line = &lines->lines[i];
1045 line->position = position;
1046 position += line->allocation + linedata->spacing;
1052 gtk_grid_get_size (GtkGrid *grid,
1053 GtkOrientation orientation,
1057 GtkGridRequest request;
1058 GtkGridLines *lines;
1060 request.grid = grid;
1061 gtk_grid_request_count_lines (&request);
1062 lines = &request.lines[orientation];
1063 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1065 gtk_grid_request_run (&request, orientation, FALSE);
1066 gtk_grid_request_sum (&request, orientation, minimum, natural);
1070 gtk_grid_get_size_for_size (GtkGrid *grid,
1071 GtkOrientation orientation,
1076 GtkGridRequest request;
1077 GtkGridLines *lines;
1080 request.grid = grid;
1081 gtk_grid_request_count_lines (&request);
1082 lines = &request.lines[0];
1083 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1084 lines = &request.lines[1];
1085 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1087 gtk_grid_request_run (&request, 1 - orientation, FALSE);
1088 gtk_grid_request_sum (&request, 1 - orientation, &min_size, NULL);
1089 gtk_grid_request_allocate (&request, 1 - orientation, MAX (size, min_size));
1091 gtk_grid_request_run (&request, orientation, TRUE);
1092 gtk_grid_request_sum (&request, orientation, minimum, natural);
1096 gtk_grid_get_preferred_width (GtkWidget *widget,
1100 GtkGrid *grid = GTK_GRID (widget);
1102 if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1104 gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, 0, minimum, natural);
1106 gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
1110 gtk_grid_get_preferred_height (GtkWidget *widget,
1114 GtkGrid *grid = GTK_GRID (widget);
1116 if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1117 gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, 0, minimum, natural);
1119 gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
1123 gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
1128 GtkGrid *grid = GTK_GRID (widget);
1130 if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1131 gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
1133 gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
1137 gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
1142 GtkGrid *grid = GTK_GRID (widget);
1144 if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1145 gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
1147 gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
1151 allocate_child (GtkGridRequest *request,
1152 GtkOrientation orientation,
1153 GtkGridChild *child,
1157 GtkGridPrivate *priv = request->grid->priv;
1158 GtkGridLineData *linedata;
1159 GtkGridLines *lines;
1161 GtkGridChildAttach *attach;
1164 linedata = &priv->linedata[orientation];
1165 lines = &request->lines[orientation];
1166 attach = &child->attach[orientation];
1168 *position = lines->lines[attach->pos - lines->min].position;
1170 *size = (attach->span - 1) * linedata->spacing;
1171 for (i = 0; i < attach->span; i++)
1173 line = &lines->lines[attach->pos - lines->min + i];
1174 *size += line->allocation;
1179 gtk_grid_request_allocate_children (GtkGridRequest *request)
1181 GtkGridPrivate *priv = request->grid->priv;
1183 GtkGridChild *child;
1184 GtkAllocation allocation;
1185 GtkAllocation child_allocation;
1186 gint x, y, width, height;
1188 gtk_widget_get_allocation (GTK_WIDGET (request->grid), &allocation);
1190 for (list = priv->children; list; list = list->next)
1194 if (!gtk_widget_get_visible (child->widget))
1197 allocate_child (request, GTK_ORIENTATION_HORIZONTAL, child, &x, &width);
1198 allocate_child (request, GTK_ORIENTATION_VERTICAL, child, &y, &height);
1200 child_allocation.x = allocation.x + x;
1201 child_allocation.y = allocation.y + y;
1202 child_allocation.width = MAX (1, width);
1203 child_allocation.height = MAX (1, height);
1205 gtk_widget_size_allocate (child->widget, &child_allocation);
1209 #define GET_SIZE(allocation, orientation) (orientation == GTK_ORIENTATION_HORIZONTAL ? allocation->width : allocation->height)
1212 gtk_grid_size_allocate (GtkWidget *widget,
1213 GtkAllocation *allocation)
1215 GtkGrid *grid = GTK_GRID (widget);
1216 GtkGridPrivate *priv = grid->priv;
1217 GtkGridRequest request;
1218 GtkGridLines *lines;
1220 request.grid = grid;
1221 gtk_grid_request_count_lines (&request);
1222 lines = &request.lines[0];
1223 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1224 lines = &request.lines[1];
1225 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1227 gtk_widget_set_allocation (widget, allocation);
1229 gtk_grid_request_run (&request, 1 - priv->orientation, FALSE);
1230 gtk_grid_request_allocate (&request, 1 - priv->orientation, GET_SIZE (allocation, 1 - priv->orientation));
1231 gtk_grid_request_run (&request, priv->orientation, TRUE);
1232 gtk_grid_request_allocate (&request, priv->orientation, GET_SIZE (allocation, priv->orientation));
1234 gtk_grid_request_position (&request, 0);
1235 gtk_grid_request_position (&request, 1);
1237 gtk_grid_request_allocate_children (&request);
1241 gtk_grid_class_init (GtkGridClass *class)
1243 GObjectClass *object_class = G_OBJECT_CLASS (class);
1244 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1245 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
1247 object_class->get_property = gtk_grid_get_property;
1248 object_class->set_property = gtk_grid_set_property;
1250 widget_class->size_allocate = gtk_grid_size_allocate;
1251 widget_class->get_preferred_width = gtk_grid_get_preferred_width;
1252 widget_class->get_preferred_height = gtk_grid_get_preferred_height;
1253 widget_class->get_request_mode = gtk_grid_get_request_mode;
1254 widget_class->get_preferred_width_for_height = gtk_grid_get_preferred_width_for_height;
1255 widget_class->get_preferred_height_for_width = gtk_grid_get_preferred_height_for_width;
1257 container_class->add = gtk_grid_add;
1258 container_class->remove = gtk_grid_remove;
1259 container_class->forall = gtk_grid_forall;
1260 container_class->child_type = gtk_grid_child_type;
1261 container_class->set_child_property = gtk_grid_set_child_property;
1262 container_class->get_child_property = gtk_grid_get_child_property;
1263 gtk_container_class_handle_border_width (container_class);
1265 g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
1267 g_object_class_install_property (object_class, PROP_ROW_SPACING,
1268 g_param_spec_int ("row-spacing",
1270 P_("The amount of space between two consecutive rows"),
1272 GTK_PARAM_READWRITE));
1274 g_object_class_install_property (object_class, PROP_COLUMN_SPACING,
1275 g_param_spec_int ("column-spacing",
1276 P_("Column spacing"),
1277 P_("The amount of space between two consecutive columns"),
1279 GTK_PARAM_READWRITE));
1281 g_object_class_install_property (object_class, PROP_ROW_HOMOGENEOUS,
1282 g_param_spec_boolean ("row-homogeneous",
1283 P_("Row Homogeneous"),
1284 P_("If TRUE, the rows are all the same height"),
1286 GTK_PARAM_READWRITE));
1288 g_object_class_install_property (object_class, PROP_COLUMN_HOMOGENEOUS,
1289 g_param_spec_boolean ("column-homogeneous",
1290 P_("Column Homogeneous"),
1291 P_("If TRUE, the columns are all the same width"),
1293 GTK_PARAM_READWRITE));
1295 gtk_container_class_install_child_property (container_class, CHILD_PROP_LEFT_ATTACH,
1296 g_param_spec_int ("left-attach",
1297 P_("Left attachment"),
1298 P_("The column number to attach the left side of the child to"),
1299 G_MININT, G_MAXINT, 0,
1300 GTK_PARAM_READWRITE));
1302 gtk_container_class_install_child_property (container_class, CHILD_PROP_TOP_ATTACH,
1303 g_param_spec_int ("top-attach",
1304 P_("Top attachment"),
1305 P_("The row number to attach the top side of a child widget to"),
1306 G_MININT, G_MAXINT, 0,
1307 GTK_PARAM_READWRITE));
1309 gtk_container_class_install_child_property (container_class, CHILD_PROP_WIDTH,
1310 g_param_spec_int ("width",
1312 P_("The number of columns that a child spans"),
1314 GTK_PARAM_READWRITE));
1316 gtk_container_class_install_child_property (container_class, CHILD_PROP_HEIGHT,
1317 g_param_spec_int ("height",
1319 P_("The number of rows that a child spans"),
1321 GTK_PARAM_READWRITE));
1323 g_type_class_add_private (class, sizeof (GtkGridPrivate));
1329 * Creates a new grid widget.
1331 * Returns: the new #GtkGrid
1336 return g_object_new (GTK_TYPE_GRID, NULL);
1340 grid_attach (GtkGrid *grid,
1347 GtkGridPrivate *priv = grid->priv;
1348 GtkGridChild *child;
1350 child = g_slice_new (GtkGridChild);
1351 child->widget = widget;
1352 CHILD_LEFT (child) = left;
1353 CHILD_TOP (child) = top;
1354 CHILD_WIDTH (child) = width;
1355 CHILD_HEIGHT (child) = height;
1357 priv->children = g_list_prepend (priv->children, child);
1359 gtk_widget_set_parent (widget, GTK_WIDGET (grid));
1365 * @child: the widget to add
1366 * @left: the column number to attach the left side of @child to
1367 * @top: the row number to attach the top side of @child to
1368 * @width: the number of columns that @child will span
1369 * @height: the number of rows that @child will span
1371 * Adds a widget to the grid.
1373 * The position of @child is determined by @left and @top. The
1374 * number of 'cells' that @child will occupy is determined by
1375 * @width and @height.
1378 gtk_grid_attach (GtkGrid *grid,
1385 g_return_if_fail (GTK_IS_GRID (grid));
1386 g_return_if_fail (GTK_IS_WIDGET (child));
1387 g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1388 g_return_if_fail (width > 0);
1389 g_return_if_fail (height > 0);
1391 grid_attach (grid, child, left, top, width, height);
1395 * gtk_grid_attach_next_to:
1397 * @child: the widget to add
1398 * @sibling: the child of @grid that @child will be placed next to
1399 * @side: the side of @sibling that @child is positioned next to
1400 * @width: the number of columns that @child will span
1401 * @height: the number of rows that @child will span
1403 * Adds a widget to the grid.
1405 * The widget is placed next to @sibling, on the side determined by
1409 gtk_grid_attach_next_to (GtkGrid *grid,
1412 GtkPositionType side,
1416 GtkGridChild *grid_sibling;
1419 g_return_if_fail (GTK_IS_GRID (grid));
1420 g_return_if_fail (GTK_IS_WIDGET (child));
1421 g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1422 g_return_if_fail (gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
1423 g_return_if_fail (width > 0);
1424 g_return_if_fail (height > 0);
1426 grid_sibling = find_grid_child (grid, sibling);
1431 left = CHILD_LEFT (grid_sibling) - width;
1432 top = CHILD_TOP (grid_sibling);
1435 left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
1436 top = CHILD_TOP (grid_sibling);
1439 left = CHILD_LEFT (grid_sibling);
1440 top = CHILD_TOP (grid_sibling) - height;
1442 case GTK_POS_BOTTOM:
1443 left = CHILD_LEFT (grid_sibling);
1444 top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
1447 g_assert_not_reached ();
1450 grid_attach (grid, child, left, top, width, height);
1454 * gtk_grid_set_row_homogeneous:
1456 * @homogeneous: %TRUE to make rows homogeneous
1458 * Sets whether all rows of @grid will have the same height.
1461 gtk_grid_set_row_homogeneous (GtkGrid *grid,
1462 gboolean homogeneous)
1464 GtkGridPrivate *priv;
1465 g_return_if_fail (GTK_IS_GRID (grid));
1469 if (ROWS (priv)->homogeneous != homogeneous)
1471 ROWS (priv)->homogeneous = homogeneous;
1473 if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1474 gtk_widget_queue_resize (GTK_WIDGET (grid));
1476 g_object_notify (G_OBJECT (grid), "row-homogeneous");
1481 * gtk_grid_get_row_homogeneous:
1484 * Returns whether all rows of @grid have the same height.
1486 * Returns: whether all rows of @grid have the same height.
1489 gtk_grid_get_row_homogeneous (GtkGrid *grid)
1491 GtkGridPrivate *priv;
1492 g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1496 return ROWS (priv)->homogeneous;
1500 * gtk_grid_set_column_homogeneous:
1502 * @homogeneous: %TRUE to make columns homogeneous
1504 * Sets whether all columns of @grid will have the same width.
1507 gtk_grid_set_column_homogeneous (GtkGrid *grid,
1508 gboolean homogeneous)
1510 GtkGridPrivate *priv;
1511 g_return_if_fail (GTK_IS_GRID (grid));
1515 if (COLUMNS (priv)->homogeneous != homogeneous)
1517 COLUMNS (priv)->homogeneous = homogeneous;
1519 if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1520 gtk_widget_queue_resize (GTK_WIDGET (grid));
1522 g_object_notify (G_OBJECT (grid), "column-homogeneous");
1527 * gtk_grid_get_column_homogeneous:
1530 * Returns whether all columns of @grid have the same width.
1532 * Returns: whether all columns of @grid have the same width.
1535 gtk_grid_get_column_homogeneous (GtkGrid *grid)
1537 GtkGridPrivate *priv;
1538 g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1542 return COLUMNS (priv)->homogeneous;
1546 * gtk_grid_set_row_spacing:
1548 * @spacing: the amount of space to insert between rows
1550 * Sets the amount of space between rows of @grid.
1553 gtk_grid_set_row_spacing (GtkGrid *grid,
1556 GtkGridPrivate *priv;
1557 g_return_if_fail (GTK_IS_GRID (grid));
1558 g_return_if_fail (spacing <= G_MAXINT16);
1562 if (ROWS (priv)->spacing != spacing)
1564 ROWS (priv)->spacing = spacing;
1566 if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1567 gtk_widget_queue_resize (GTK_WIDGET (grid));
1569 g_object_notify (G_OBJECT (grid), "row-spacing");
1574 * gtk_grid_get_row_spacing:
1577 * Returns the amount of space between the rows of @grid.
1579 * Returns: the row spacing of @grid
1582 gtk_grid_get_row_spacing (GtkGrid *grid)
1584 GtkGridPrivate *priv;
1585 g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1589 return ROWS (priv)->spacing;
1593 * gtk_grid_set_column_spacing:
1595 * @spacing: the amount of space to insert between columns
1597 * Sets the amount of space between columns of @grid.
1600 gtk_grid_set_column_spacing (GtkGrid *grid,
1603 GtkGridPrivate *priv;
1604 g_return_if_fail (GTK_IS_GRID (grid));
1605 g_return_if_fail (spacing <= G_MAXINT16);
1609 if (COLUMNS (priv)->spacing != spacing)
1611 COLUMNS (priv)->spacing = spacing;
1613 if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1614 gtk_widget_queue_resize (GTK_WIDGET (grid));
1616 g_object_notify (G_OBJECT (grid), "column-spacing");
1621 * gtk_grid_get_column_spacing:
1624 * Returns the amount of space between the columns of @grid.
1626 * Returns: the column spacing of @grid
1629 gtk_grid_get_column_spacing (GtkGrid *grid)
1631 GtkGridPrivate *priv;
1633 g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1637 return COLUMNS (priv)->spacing;