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.
27 #include "gtkorientableprivate.h"
28 #include "gtksizerequest.h"
29 #include "gtkprivate.h"
35 * @Short_description: Pack widgets in a rows and columns
37 * @See_also: #GtkTable, #GtkHBox, #GtkVBox
39 * GtkGrid is a container which arranges its child widgets in
40 * rows and columns. It is a very similar to #GtkTable and #GtkBox,
41 * but it consistently uses #GtkWidget's #GtkWidget:margin and #GtkWidget:expand
42 * properties instead of custom child properties, and it fully supports
43 * <link linkend="geometry-management">height-for-width geometry management</link>.
45 * Children are added using gtk_grid_attach(). They can span multiple
46 * rows or columns. It is also possible to add a child next to an
47 * existing child, using gtk_grid_attach_next_to().
49 * GtkGrid can be used like a #GtkBox by just using gtk_container_add(),
50 * which will place children next to each other in the direction determined
51 * by the #GtkOrientable:orientation property.
54 typedef struct _GtkGridChild GtkGridChild;
55 typedef struct _GtkGridChildAttach GtkGridChildAttach;
56 typedef struct _GtkGridLine GtkGridLine;
57 typedef struct _GtkGridLines GtkGridLines;
58 typedef struct _GtkGridLineData GtkGridLineData;
59 typedef struct _GtkGridRequest GtkGridRequest;
61 struct _GtkGridChildAttach
70 GtkGridChildAttach attach[2];
73 #define CHILD_LEFT(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].pos)
74 #define CHILD_WIDTH(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].span)
75 #define CHILD_TOP(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].pos)
76 #define CHILD_HEIGHT(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].span)
78 /* A GtkGridLineData struct contains row/column specific parts
81 struct _GtkGridLineData
84 guint homogeneous : 1;
87 struct _GtkGridPrivate
91 GtkOrientation orientation;
93 GtkGridLineData linedata[2];
96 #define ROWS(priv) (&(priv)->linedata[GTK_ORIENTATION_HORIZONTAL])
97 #define COLUMNS(priv) (&(priv)->linedata[GTK_ORIENTATION_VERTICAL])
99 /* A GtkGridLine struct represents a single row or column
100 * during size requests
109 guint need_expand : 1;
120 struct _GtkGridRequest
123 GtkGridLines lines[2];
133 PROP_ROW_HOMOGENEOUS,
134 PROP_COLUMN_HOMOGENEOUS
140 CHILD_PROP_LEFT_ATTACH,
141 CHILD_PROP_TOP_ATTACH,
146 G_DEFINE_TYPE_WITH_CODE (GtkGrid, gtk_grid, GTK_TYPE_CONTAINER,
147 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
151 gtk_grid_get_property (GObject *object,
156 GtkGrid *grid = GTK_GRID (object);
157 GtkGridPrivate *priv = grid->priv;
161 case PROP_ORIENTATION:
162 g_value_set_enum (value, priv->orientation);
165 case PROP_ROW_SPACING:
166 g_value_set_int (value, ROWS (priv)->spacing);
169 case PROP_COLUMN_SPACING:
170 g_value_set_int (value, COLUMNS (priv)->spacing);
173 case PROP_ROW_HOMOGENEOUS:
174 g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
177 case PROP_COLUMN_HOMOGENEOUS:
178 g_value_set_boolean (value, ROWS (priv)->homogeneous);
182 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
188 gtk_grid_set_orientation (GtkGrid *grid,
189 GtkOrientation orientation)
191 GtkGridPrivate *priv = grid->priv;
193 if (priv->orientation != orientation)
195 priv->orientation = orientation;
196 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (grid));
198 g_object_notify (G_OBJECT (grid), "orientation");
203 gtk_grid_set_property (GObject *object,
208 GtkGrid *grid = GTK_GRID (object);
212 case PROP_ORIENTATION:
213 gtk_grid_set_orientation (grid, g_value_get_enum (value));
216 case PROP_ROW_SPACING:
217 gtk_grid_set_row_spacing (grid, g_value_get_int (value));
220 case PROP_COLUMN_SPACING:
221 gtk_grid_set_column_spacing (grid, g_value_get_int (value));
224 case PROP_ROW_HOMOGENEOUS:
225 gtk_grid_set_row_homogeneous (grid, g_value_get_boolean (value));
228 case PROP_COLUMN_HOMOGENEOUS:
229 gtk_grid_set_column_homogeneous (grid, g_value_get_boolean (value));
233 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
238 static GtkGridChild *
239 find_grid_child (GtkGrid *grid,
242 GtkGridPrivate *priv = grid->priv;
246 for (list = priv->children; list; list = list->next)
250 if (child->widget == widget)
258 gtk_grid_get_child_property (GtkContainer *container,
264 GtkGrid *grid = GTK_GRID (container);
265 GtkGridChild *grid_child;
267 grid_child = find_grid_child (grid, child);
269 if (grid_child == NULL)
271 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
277 case CHILD_PROP_LEFT_ATTACH:
278 g_value_set_int (value, CHILD_LEFT (grid_child));
281 case CHILD_PROP_TOP_ATTACH:
282 g_value_set_int (value, CHILD_TOP (grid_child));
285 case CHILD_PROP_WIDTH:
286 g_value_set_int (value, CHILD_WIDTH (grid_child));
289 case CHILD_PROP_HEIGHT:
290 g_value_set_int (value, CHILD_HEIGHT (grid_child));
294 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
300 gtk_grid_set_child_property (GtkContainer *container,
306 GtkGrid *grid = GTK_GRID (container);
307 GtkGridChild *grid_child;
309 grid_child = find_grid_child (grid, child);
311 if (grid_child == NULL)
313 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
319 case CHILD_PROP_LEFT_ATTACH:
320 CHILD_LEFT (grid_child) = g_value_get_int (value);
323 case CHILD_PROP_TOP_ATTACH:
324 CHILD_TOP (grid_child) = g_value_get_int (value);
327 case CHILD_PROP_WIDTH:
328 CHILD_WIDTH (grid_child) = g_value_get_int (value);
331 case CHILD_PROP_HEIGHT:
332 CHILD_HEIGHT (grid_child) = g_value_get_int (value);
336 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
340 if (gtk_widget_get_visible (child) &&
341 gtk_widget_get_visible (GTK_WIDGET (grid)))
342 gtk_widget_queue_resize (child);
346 gtk_grid_init (GtkGrid *grid)
348 GtkGridPrivate *priv;
350 grid->priv = G_TYPE_INSTANCE_GET_PRIVATE (grid, GTK_TYPE_GRID, GtkGridPrivate);
353 gtk_widget_set_has_window (GTK_WIDGET (grid), FALSE);
354 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (grid), FALSE);
356 priv->children = NULL;
357 priv->orientation = GTK_ORIENTATION_HORIZONTAL;
359 priv->linedata[0].spacing = 0;
360 priv->linedata[1].spacing = 0;
362 priv->linedata[0].homogeneous = FALSE;
363 priv->linedata[1].homogeneous = FALSE;
366 static void grid_attach (GtkGrid *grid,
374 gtk_grid_add (GtkContainer *container,
377 GtkGrid *grid = GTK_GRID (container);
378 GtkGridPrivate *priv = grid->priv;
379 GtkGridChild *grid_child;
380 GtkGridChildAttach *attach;
381 GtkGridChildAttach *opposite;
386 for (list = priv->children; list; list = list->next)
388 grid_child = list->data;
390 attach = &grid_child->attach[priv->orientation];
391 opposite = &grid_child->attach[1 - priv->orientation];
393 if (opposite->pos <= 0 && opposite->pos + opposite->span > 0)
394 pos = MAX (pos, attach->pos + attach->span);
397 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
398 grid_attach (grid, child, pos, 0, 1, 1);
400 grid_attach (grid, child, 0, pos, 1, 1);
404 gtk_grid_remove (GtkContainer *container,
407 GtkGrid *grid = GTK_GRID (container);
408 GtkGridPrivate *priv = grid->priv;
409 GtkGridChild *grid_child;
412 for (list = priv->children; list; list = list->next)
414 grid_child = list->data;
416 if (grid_child->widget == child)
418 gboolean was_visible = gtk_widget_get_visible (child);
420 gtk_widget_unparent (child);
422 priv->children = g_list_remove (priv->children, grid_child);
424 g_slice_free (GtkGridChild, grid_child);
426 if (was_visible && gtk_widget_get_visible (GTK_WIDGET (grid)))
427 gtk_widget_queue_resize (GTK_WIDGET (grid));
435 gtk_grid_forall (GtkContainer *container,
436 gboolean include_internals,
437 GtkCallback callback,
438 gpointer callback_data)
440 GtkGrid *grid = GTK_GRID (container);
441 GtkGridPrivate *priv = grid->priv;
445 list = priv->children;
451 (* callback) (child->widget, callback_data);
456 gtk_grid_child_type (GtkContainer *container)
458 return GTK_TYPE_WIDGET;
461 /* Calculates the min and max numbers for both orientations.
464 gtk_grid_request_count_lines (GtkGridRequest *request)
466 GtkGridPrivate *priv = request->grid->priv;
468 GtkGridChildAttach *attach;
473 min[0] = min[1] = G_MAXINT;
474 max[0] = max[1] = G_MININT;
476 for (list = priv->children; list; list = list->next)
479 attach = child->attach;
481 min[0] = MIN (min[0], attach[0].pos);
482 max[0] = MAX (max[0], attach[0].pos + attach[0].span);
483 min[1] = MIN (min[1], attach[1].pos);
484 max[1] = MAX (max[1], attach[1].pos + attach[1].span);
487 request->lines[0].min = min[0];
488 request->lines[0].max = max[0];
489 request->lines[1].min = min[1];
490 request->lines[1].max = max[1];
493 /* Sets line sizes to 0 and marks lines as expand
494 * if they have a non-spanning expanding child.
497 gtk_grid_request_init (GtkGridRequest *request,
498 GtkOrientation orientation)
500 GtkGridPrivate *priv = request->grid->priv;
502 GtkGridChildAttach *attach;
507 lines = &request->lines[orientation];
509 for (i = 0; i < lines->max - lines->min; i++)
511 lines->lines[i].minimum = 0;
512 lines->lines[i].natural = 0;
513 lines->lines[i].expand = FALSE;
516 for (list = priv->children; list; list = list->next)
520 attach = &child->attach[orientation];
521 if (attach->span == 1 && gtk_widget_compute_expand (child->widget, orientation))
522 lines->lines[attach->pos - lines->min].expand = TRUE;
526 /* Sums allocations for lines spanned by child and their spacing.
529 compute_allocation_for_child (GtkGridRequest *request,
531 GtkOrientation orientation)
533 GtkGridPrivate *priv = request->grid->priv;
534 GtkGridLineData *linedata;
537 GtkGridChildAttach *attach;
541 linedata = &priv->linedata[orientation];
542 lines = &request->lines[orientation];
543 attach = &child->attach[orientation];
545 size = (attach->span - 1) * linedata->spacing;
546 for (i = 0; i < attach->span; i++)
548 line = &lines->lines[attach->pos - lines->min + i];
549 size += line->allocation;
556 compute_request_for_child (GtkGridRequest *request,
558 GtkOrientation orientation,
567 size = compute_allocation_for_child (request, child, 1 - orientation);
568 if (orientation == GTK_ORIENTATION_HORIZONTAL)
569 gtk_widget_get_preferred_width_for_height (child->widget,
573 gtk_widget_get_preferred_height_for_width (child->widget,
579 if (orientation == GTK_ORIENTATION_HORIZONTAL)
580 gtk_widget_get_preferred_width (child->widget, minimum, natural);
582 gtk_widget_get_preferred_height (child->widget, minimum, natural);
586 /* Sets requisition to max. of non-spanning children.
587 * If contextual is TRUE, requires allocations of
588 * lines in the opposite orientation to be set.
591 gtk_grid_request_non_spanning (GtkGridRequest *request,
592 GtkOrientation orientation,
595 GtkGridPrivate *priv = request->grid->priv;
597 GtkGridChildAttach *attach;
604 lines = &request->lines[orientation];
606 for (list = priv->children; list; list = list->next)
610 if (!gtk_widget_get_visible (child->widget))
613 attach = &child->attach[orientation];
614 if (attach->span != 1)
617 compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
619 line = &lines->lines[attach->pos - lines->min];
620 line->minimum = MAX (line->minimum, minimum);
621 line->natural = MAX (line->natural, natural);
625 /* Enforce homogeneous sizes.
628 gtk_grid_request_homogeneous (GtkGridRequest *request,
629 GtkOrientation orientation)
631 GtkGridPrivate *priv = request->grid->priv;
632 GtkGridLineData *linedata;
634 gint minimum, natural;
637 linedata = &priv->linedata[orientation];
638 lines = &request->lines[orientation];
640 if (!linedata->homogeneous)
646 for (i = 0; i < lines->max - lines->min; i++)
648 minimum = MAX (minimum, lines->lines[i].minimum);
649 natural = MAX (natural, lines->lines[i].natural);
652 for (i = 0; i < lines->max - lines->min; i++)
654 lines->lines[i].minimum = minimum;
655 lines->lines[i].natural = natural;
659 /* Deals with spanning children.
660 * Requires expand fields of lines to be set for
661 * non-spanning children.
664 gtk_grid_request_spanning (GtkGridRequest *request,
665 GtkOrientation orientation,
668 GtkGridPrivate *priv = request->grid->priv;
671 GtkGridChildAttach *attach;
672 GtkGridLineData *linedata;
680 gboolean force_expand;
686 linedata = &priv->linedata[orientation];
687 lines = &request->lines[orientation];
689 for (list = priv->children; list; list = list->next)
693 if (!gtk_widget_get_visible (child->widget))
696 attach = &child->attach[orientation];
697 if (attach->span == 1)
700 compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
702 span_minimum = (attach->span - 1) * linedata->spacing;
703 span_natural = (attach->span - 1) * linedata->spacing;
705 force_expand = FALSE;
706 for (i = 0; i < attach->span; i++)
708 line = &lines->lines[attach->pos - lines->min + i];
709 span_minimum += line->minimum;
710 span_natural += line->natural;
714 if (span_expand == 0)
716 span_expand = attach->span;
720 /* If we need to request more space for this child to fill
721 * its requisition, then divide up the needed space amongst the
722 * lines it spans, favoring expandable lines if any.
724 if (span_minimum < minimum)
726 extra = minimum - span_minimum;
727 expand = span_expand;
728 for (i = 0; i < attach->span; i++)
730 line = &lines->lines[attach->pos - lines->min + i];
731 if (force_expand || line->expand)
733 line_extra = extra / expand;
734 line->minimum += line_extra;
741 if (span_natural < natural)
743 extra = natural - span_natural;
744 expand = span_expand;
745 for (i = 0; i < attach->span; i++)
747 line = &lines->lines[attach->pos - lines->min + i];
748 if (force_expand || line->expand)
750 line_extra = extra / expand;
751 line->natural += line_extra;
760 /* Marks empty and expanding lines and counts them.
763 gtk_grid_request_compute_expand (GtkGridRequest *request,
764 GtkOrientation orientation,
765 gint *nonempty_lines,
768 GtkGridPrivate *priv = request->grid->priv;
770 GtkGridChildAttach *attach;
779 lines = &request->lines[orientation];
781 for (i = 0; i < lines->max - lines->min; i++)
783 lines->lines[i].need_expand = FALSE;
784 lines->lines[i].expand = FALSE;
785 lines->lines[i].empty = TRUE;
788 for (list = priv->children; list; list = list->next)
792 if (!gtk_widget_get_visible (child->widget))
795 attach = &child->attach[orientation];
796 if (attach->span != 1)
799 line = &lines->lines[attach->pos - lines->min];
801 if (gtk_widget_compute_expand (child->widget, orientation))
805 for (list = priv->children; list; list = list->next)
809 if (!gtk_widget_get_visible (child->widget))
812 attach = &child->attach[orientation];
813 if (attach->span == 1)
817 for (i = 0; i < attach->span; i++)
819 line = &lines->lines[attach->pos - lines->min + i];
825 if (!has_expand && gtk_widget_compute_expand (child->widget, orientation))
827 for (i = 0; i < attach->span; i++)
829 line = &lines->lines[attach->pos - lines->min + i];
830 line->need_expand = TRUE;
837 for (i = 0; i < lines->max - lines->min; i++)
839 line = &lines->lines[i];
841 if (line->need_expand)
852 *nonempty_lines = lines->max - lines->min - empty;
855 *expand_lines = expand;
858 /* Sums the minimum and natural fields of lines and their spacing.
861 gtk_grid_request_sum (GtkGridRequest *request,
862 GtkOrientation orientation,
866 GtkGridPrivate *priv = request->grid->priv;
867 GtkGridLineData *linedata;
873 gtk_grid_request_compute_expand (request, orientation, &nonempty, NULL);
875 linedata = &priv->linedata[orientation];
876 lines = &request->lines[orientation];
878 min = (nonempty - 1) * linedata->spacing;
879 nat = (nonempty - 1) * linedata->spacing;
881 for (i = 0; i < lines->max - lines->min; i++)
883 min += lines->lines[i].minimum;
884 nat += lines->lines[i].natural;
894 /* Computes minimum and natural fields of lines.
895 * When contextual is TRUE, requires allocation of
896 * lines in the opposite orientation to be set.
899 gtk_grid_request_run (GtkGridRequest *request,
900 GtkOrientation orientation,
903 gtk_grid_request_init (request, orientation);
904 gtk_grid_request_non_spanning (request, orientation, contextual);
905 gtk_grid_request_homogeneous (request, orientation);
906 gtk_grid_request_spanning (request, orientation, contextual);
907 gtk_grid_request_homogeneous (request, orientation);
910 /* Requires that the minimum and natural fields of lines
911 * have been set, computes the allocation field of lines
912 * by distributing total_size among lines.
915 gtk_grid_request_allocate (GtkGridRequest *request,
916 GtkOrientation orientation,
919 GtkGridPrivate *priv = request->grid->priv;
920 GtkGridLineData *linedata;
926 GtkRequestedSize *sizes;
931 gtk_grid_request_compute_expand (request, orientation, &nonempty, &expand);
933 linedata = &priv->linedata[orientation];
934 lines = &request->lines[orientation];
936 size = total_size - (nonempty - 1) * linedata->spacing;
938 if (linedata->homogeneous)
940 extra = size / nonempty;
941 rest = size % nonempty;
943 for (i = 0; i < lines->max - lines->min; i++)
945 line = &lines->lines[i];
949 line->allocation = extra;
952 line->allocation += 1;
959 sizes = g_newa (GtkRequestedSize, nonempty);
962 for (i = 0; i < lines->max - lines->min; i++)
964 line = &lines->lines[i];
968 size -= line->minimum;
970 sizes[j].minimum_size = line->minimum;
971 sizes[j].natural_size = line->natural;
972 sizes[j].data = line;
976 size = gtk_distribute_natural_allocation (MAX (0, size), nonempty, sizes);
980 extra = size / expand;
981 rest = size % expand;
990 for (i = 0; i < lines->max - lines->min; i++)
992 line = &lines->lines[i];
996 g_assert (line == sizes[j].data);
998 line->allocation = sizes[j].minimum_size;
1001 line->allocation += extra;
1004 line->allocation += 1;
1014 /* Computes the position fields from allocation and spacing.
1017 gtk_grid_request_position (GtkGridRequest *request,
1018 GtkOrientation orientation)
1020 GtkGridPrivate *priv = request->grid->priv;
1021 GtkGridLineData *linedata;
1022 GtkGridLines *lines;
1027 linedata = &priv->linedata[orientation];
1028 lines = &request->lines[orientation];
1031 for (i = 0; i < lines->max - lines->min; i++)
1033 line = &lines->lines[i];
1036 line->position = position;
1037 position += line->allocation + linedata->spacing;
1043 gtk_grid_get_size (GtkGrid *grid,
1044 GtkOrientation orientation,
1048 GtkGridRequest request;
1049 GtkGridLines *lines;
1057 if (grid->priv->children == NULL)
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);
1064 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1066 gtk_grid_request_run (&request, orientation, FALSE);
1067 gtk_grid_request_sum (&request, orientation, minimum, natural);
1071 gtk_grid_get_size_for_size (GtkGrid *grid,
1072 GtkOrientation orientation,
1077 GtkGridRequest request;
1078 GtkGridLines *lines;
1087 if (grid->priv->children == NULL)
1090 request.grid = grid;
1091 gtk_grid_request_count_lines (&request);
1092 lines = &request.lines[0];
1093 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1094 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1095 lines = &request.lines[1];
1096 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1097 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1099 gtk_grid_request_run (&request, 1 - orientation, FALSE);
1100 gtk_grid_request_sum (&request, 1 - orientation, &min_size, NULL);
1101 gtk_grid_request_allocate (&request, 1 - orientation, MAX (size, min_size));
1103 gtk_grid_request_run (&request, orientation, TRUE);
1104 gtk_grid_request_sum (&request, orientation, minimum, natural);
1108 gtk_grid_get_preferred_width (GtkWidget *widget,
1112 GtkGrid *grid = GTK_GRID (widget);
1114 if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1115 gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, 0, minimum, natural);
1117 gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
1121 gtk_grid_get_preferred_height (GtkWidget *widget,
1125 GtkGrid *grid = GTK_GRID (widget);
1127 if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1128 gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, 0, minimum, natural);
1130 gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
1134 gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
1139 GtkGrid *grid = GTK_GRID (widget);
1141 if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1142 gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
1144 gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
1148 gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
1153 GtkGrid *grid = GTK_GRID (widget);
1155 if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1156 gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
1158 gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
1162 allocate_child (GtkGridRequest *request,
1163 GtkOrientation orientation,
1164 GtkGridChild *child,
1168 GtkGridPrivate *priv = request->grid->priv;
1169 GtkGridLineData *linedata;
1170 GtkGridLines *lines;
1172 GtkGridChildAttach *attach;
1175 linedata = &priv->linedata[orientation];
1176 lines = &request->lines[orientation];
1177 attach = &child->attach[orientation];
1179 *position = lines->lines[attach->pos - lines->min].position;
1181 *size = (attach->span - 1) * linedata->spacing;
1182 for (i = 0; i < attach->span; i++)
1184 line = &lines->lines[attach->pos - lines->min + i];
1185 *size += line->allocation;
1190 gtk_grid_request_allocate_children (GtkGridRequest *request)
1192 GtkGridPrivate *priv = request->grid->priv;
1194 GtkGridChild *child;
1195 GtkAllocation allocation;
1196 GtkAllocation child_allocation;
1197 gint x, y, width, height;
1199 gtk_widget_get_allocation (GTK_WIDGET (request->grid), &allocation);
1201 for (list = priv->children; list; list = list->next)
1205 if (!gtk_widget_get_visible (child->widget))
1208 allocate_child (request, GTK_ORIENTATION_HORIZONTAL, child, &x, &width);
1209 allocate_child (request, GTK_ORIENTATION_VERTICAL, child, &y, &height);
1211 child_allocation.x = allocation.x + x;
1212 child_allocation.y = allocation.y + y;
1213 child_allocation.width = MAX (1, width);
1214 child_allocation.height = MAX (1, height);
1216 if (gtk_widget_get_direction (GTK_WIDGET (request->grid)) == GTK_TEXT_DIR_RTL)
1217 child_allocation.x = allocation.x + allocation.width
1218 - (child_allocation.x - allocation.x) - child_allocation.width;
1220 gtk_widget_size_allocate (child->widget, &child_allocation);
1224 #define GET_SIZE(allocation, orientation) (orientation == GTK_ORIENTATION_HORIZONTAL ? allocation->width : allocation->height)
1227 gtk_grid_size_allocate (GtkWidget *widget,
1228 GtkAllocation *allocation)
1230 GtkGrid *grid = GTK_GRID (widget);
1231 GtkGridPrivate *priv = grid->priv;
1232 GtkGridRequest request;
1233 GtkGridLines *lines;
1234 GtkOrientation orientation;
1236 if (priv->children == NULL)
1238 gtk_widget_set_allocation (widget, allocation);
1242 request.grid = grid;
1244 gtk_grid_request_count_lines (&request);
1245 lines = &request.lines[0];
1246 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1247 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1248 lines = &request.lines[1];
1249 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1250 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1252 gtk_widget_set_allocation (widget, allocation);
1254 if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1255 orientation = GTK_ORIENTATION_HORIZONTAL;
1257 orientation = GTK_ORIENTATION_VERTICAL;
1259 gtk_grid_request_run (&request, 1 - orientation, FALSE);
1260 gtk_grid_request_allocate (&request, 1 - orientation, GET_SIZE (allocation, 1 - orientation));
1261 gtk_grid_request_run (&request, orientation, TRUE);
1262 gtk_grid_request_allocate (&request, orientation, GET_SIZE (allocation, orientation));
1264 gtk_grid_request_position (&request, 0);
1265 gtk_grid_request_position (&request, 1);
1267 gtk_grid_request_allocate_children (&request);
1271 gtk_grid_class_init (GtkGridClass *class)
1273 GObjectClass *object_class = G_OBJECT_CLASS (class);
1274 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1275 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
1277 object_class->get_property = gtk_grid_get_property;
1278 object_class->set_property = gtk_grid_set_property;
1280 widget_class->size_allocate = gtk_grid_size_allocate;
1281 widget_class->get_preferred_width = gtk_grid_get_preferred_width;
1282 widget_class->get_preferred_height = gtk_grid_get_preferred_height;
1283 widget_class->get_preferred_width_for_height = gtk_grid_get_preferred_width_for_height;
1284 widget_class->get_preferred_height_for_width = gtk_grid_get_preferred_height_for_width;
1286 container_class->add = gtk_grid_add;
1287 container_class->remove = gtk_grid_remove;
1288 container_class->forall = gtk_grid_forall;
1289 container_class->child_type = gtk_grid_child_type;
1290 container_class->set_child_property = gtk_grid_set_child_property;
1291 container_class->get_child_property = gtk_grid_get_child_property;
1292 gtk_container_class_handle_border_width (container_class);
1294 g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
1296 g_object_class_install_property (object_class, PROP_ROW_SPACING,
1297 g_param_spec_int ("row-spacing",
1299 P_("The amount of space between two consecutive rows"),
1301 GTK_PARAM_READWRITE));
1303 g_object_class_install_property (object_class, PROP_COLUMN_SPACING,
1304 g_param_spec_int ("column-spacing",
1305 P_("Column spacing"),
1306 P_("The amount of space between two consecutive columns"),
1308 GTK_PARAM_READWRITE));
1310 g_object_class_install_property (object_class, PROP_ROW_HOMOGENEOUS,
1311 g_param_spec_boolean ("row-homogeneous",
1312 P_("Row Homogeneous"),
1313 P_("If TRUE, the rows are all the same height"),
1315 GTK_PARAM_READWRITE));
1317 g_object_class_install_property (object_class, PROP_COLUMN_HOMOGENEOUS,
1318 g_param_spec_boolean ("column-homogeneous",
1319 P_("Column Homogeneous"),
1320 P_("If TRUE, the columns are all the same width"),
1322 GTK_PARAM_READWRITE));
1324 gtk_container_class_install_child_property (container_class, CHILD_PROP_LEFT_ATTACH,
1325 g_param_spec_int ("left-attach",
1326 P_("Left attachment"),
1327 P_("The column number to attach the left side of the child to"),
1328 G_MININT, G_MAXINT, 0,
1329 GTK_PARAM_READWRITE));
1331 gtk_container_class_install_child_property (container_class, CHILD_PROP_TOP_ATTACH,
1332 g_param_spec_int ("top-attach",
1333 P_("Top attachment"),
1334 P_("The row number to attach the top side of a child widget to"),
1335 G_MININT, G_MAXINT, 0,
1336 GTK_PARAM_READWRITE));
1338 gtk_container_class_install_child_property (container_class, CHILD_PROP_WIDTH,
1339 g_param_spec_int ("width",
1341 P_("The number of columns that a child spans"),
1343 GTK_PARAM_READWRITE));
1345 gtk_container_class_install_child_property (container_class, CHILD_PROP_HEIGHT,
1346 g_param_spec_int ("height",
1348 P_("The number of rows that a child spans"),
1350 GTK_PARAM_READWRITE));
1352 g_type_class_add_private (class, sizeof (GtkGridPrivate));
1358 * Creates a new grid widget.
1360 * Returns: the new #GtkGrid
1365 return g_object_new (GTK_TYPE_GRID, NULL);
1369 grid_attach (GtkGrid *grid,
1376 GtkGridPrivate *priv = grid->priv;
1377 GtkGridChild *child;
1379 child = g_slice_new (GtkGridChild);
1380 child->widget = widget;
1381 CHILD_LEFT (child) = left;
1382 CHILD_TOP (child) = top;
1383 CHILD_WIDTH (child) = width;
1384 CHILD_HEIGHT (child) = height;
1386 priv->children = g_list_prepend (priv->children, child);
1388 gtk_widget_set_parent (widget, GTK_WIDGET (grid));
1394 * @child: the widget to add
1395 * @left: the column number to attach the left side of @child to
1396 * @top: the row number to attach the top side of @child to
1397 * @width: the number of columns that @child will span
1398 * @height: the number of rows that @child will span
1400 * Adds a widget to the grid.
1402 * The position of @child is determined by @left and @top. The
1403 * number of 'cells' that @child will occupy is determined by
1404 * @width and @height.
1407 gtk_grid_attach (GtkGrid *grid,
1414 g_return_if_fail (GTK_IS_GRID (grid));
1415 g_return_if_fail (GTK_IS_WIDGET (child));
1416 g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1417 g_return_if_fail (width > 0);
1418 g_return_if_fail (height > 0);
1420 grid_attach (grid, child, left, top, width, height);
1424 * gtk_grid_attach_next_to:
1426 * @child: the widget to add
1427 * @sibling: the child of @grid that @child will be placed next to
1428 * @side: the side of @sibling that @child is positioned next to
1429 * @width: the number of columns that @child will span
1430 * @height: the number of rows that @child will span
1432 * Adds a widget to the grid.
1434 * The widget is placed next to @sibling, on the side determined by
1438 gtk_grid_attach_next_to (GtkGrid *grid,
1441 GtkPositionType side,
1445 GtkGridChild *grid_sibling;
1448 g_return_if_fail (GTK_IS_GRID (grid));
1449 g_return_if_fail (GTK_IS_WIDGET (child));
1450 g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1451 g_return_if_fail (gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
1452 g_return_if_fail (width > 0);
1453 g_return_if_fail (height > 0);
1455 grid_sibling = find_grid_child (grid, sibling);
1460 left = CHILD_LEFT (grid_sibling) - width;
1461 top = CHILD_TOP (grid_sibling);
1464 left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
1465 top = CHILD_TOP (grid_sibling);
1468 left = CHILD_LEFT (grid_sibling);
1469 top = CHILD_TOP (grid_sibling) - height;
1471 case GTK_POS_BOTTOM:
1472 left = CHILD_LEFT (grid_sibling);
1473 top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
1476 g_assert_not_reached ();
1479 grid_attach (grid, child, left, top, width, height);
1483 * gtk_grid_set_row_homogeneous:
1485 * @homogeneous: %TRUE to make rows homogeneous
1487 * Sets whether all rows of @grid will have the same height.
1490 gtk_grid_set_row_homogeneous (GtkGrid *grid,
1491 gboolean homogeneous)
1493 GtkGridPrivate *priv;
1494 g_return_if_fail (GTK_IS_GRID (grid));
1498 /* Yes, homogeneous rows means all the columns have the same size */
1499 if (COLUMNS (priv)->homogeneous != homogeneous)
1501 COLUMNS (priv)->homogeneous = homogeneous;
1503 if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1504 gtk_widget_queue_resize (GTK_WIDGET (grid));
1506 g_object_notify (G_OBJECT (grid), "row-homogeneous");
1511 * gtk_grid_get_row_homogeneous:
1514 * Returns whether all rows of @grid have the same height.
1516 * Returns: whether all rows of @grid have the same height.
1519 gtk_grid_get_row_homogeneous (GtkGrid *grid)
1521 GtkGridPrivate *priv;
1522 g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1526 return COLUMNS (priv)->homogeneous;
1530 * gtk_grid_set_column_homogeneous:
1532 * @homogeneous: %TRUE to make columns homogeneous
1534 * Sets whether all columns of @grid will have the same width.
1537 gtk_grid_set_column_homogeneous (GtkGrid *grid,
1538 gboolean homogeneous)
1540 GtkGridPrivate *priv;
1541 g_return_if_fail (GTK_IS_GRID (grid));
1545 /* Yes, homogeneous columns means all the rows have the same size */
1546 if (ROWS (priv)->homogeneous != homogeneous)
1548 ROWS (priv)->homogeneous = homogeneous;
1550 if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1551 gtk_widget_queue_resize (GTK_WIDGET (grid));
1553 g_object_notify (G_OBJECT (grid), "column-homogeneous");
1558 * gtk_grid_get_column_homogeneous:
1561 * Returns whether all columns of @grid have the same width.
1563 * Returns: whether all columns of @grid have the same width.
1566 gtk_grid_get_column_homogeneous (GtkGrid *grid)
1568 GtkGridPrivate *priv;
1569 g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1573 return ROWS (priv)->homogeneous;
1577 * gtk_grid_set_row_spacing:
1579 * @spacing: the amount of space to insert between rows
1581 * Sets the amount of space between rows of @grid.
1584 gtk_grid_set_row_spacing (GtkGrid *grid,
1587 GtkGridPrivate *priv;
1588 g_return_if_fail (GTK_IS_GRID (grid));
1589 g_return_if_fail (spacing <= G_MAXINT16);
1593 if (ROWS (priv)->spacing != spacing)
1595 ROWS (priv)->spacing = spacing;
1597 if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1598 gtk_widget_queue_resize (GTK_WIDGET (grid));
1600 g_object_notify (G_OBJECT (grid), "row-spacing");
1605 * gtk_grid_get_row_spacing:
1608 * Returns the amount of space between the rows of @grid.
1610 * Returns: the row spacing of @grid
1613 gtk_grid_get_row_spacing (GtkGrid *grid)
1615 GtkGridPrivate *priv;
1616 g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1620 return ROWS (priv)->spacing;
1624 * gtk_grid_set_column_spacing:
1626 * @spacing: the amount of space to insert between columns
1628 * Sets the amount of space between columns of @grid.
1631 gtk_grid_set_column_spacing (GtkGrid *grid,
1634 GtkGridPrivate *priv;
1635 g_return_if_fail (GTK_IS_GRID (grid));
1636 g_return_if_fail (spacing <= G_MAXINT16);
1640 if (COLUMNS (priv)->spacing != spacing)
1642 COLUMNS (priv)->spacing = spacing;
1644 if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1645 gtk_widget_queue_resize (GTK_WIDGET (grid));
1647 g_object_notify (G_OBJECT (grid), "column-spacing");
1652 * gtk_grid_get_column_spacing:
1655 * Returns the amount of space between the columns of @grid.
1657 * Returns: the column spacing of @grid
1660 gtk_grid_get_column_spacing (GtkGrid *grid)
1662 GtkGridPrivate *priv;
1664 g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1668 return COLUMNS (priv)->spacing;