1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27 * @Short_description: A container box
29 * @See_also: #GtkFrame, #GtkGrid, #GtkLayout
31 * The GtkBox widget organizes child widgets into a rectangular area.
33 * The rectangular area of a GtkBox is organized into either a single row
34 * or a single column of child widgets depending upon the orientation.
35 * Thus, all children of a GtkBox are allocated one dimension in common,
36 * which is the height of a row, or the width of a column.
38 * GtkBox uses a notion of <emphasis>packing</emphasis>. Packing refers
39 * to adding widgets with reference to a particular position in a
40 * #GtkContainer. For a GtkBox, there are two reference positions: the
41 * <emphasis>start</emphasis> and the <emphasis>end</emphasis> of the box.
42 * For a vertical #GtkBox, the start is defined as the top of the box and
43 * the end is defined as the bottom. For a horizontal #GtkBox the start
44 * is defined as the left side and the end is defined as the right side.
46 * Use repeated calls to gtk_box_pack_start() to pack widgets into a
47 * GtkBox from start to end. Use gtk_box_pack_end() to add widgets from
48 * end to start. You may intersperse these calls and add widgets from
49 * both ends of the same GtkBox.
51 * Because GtkBox is a #GtkContainer, you may also use gtk_container_add()
52 * to insert widgets into the box, and they will be packed with the default
53 * values for #GtkBox:expand and #GtkBox:fill. Use gtk_container_remove()
54 * to remove widgets from the GtkBox.
56 * Use gtk_box_set_homogeneous() to specify whether or not all children
57 * of the GtkBox are forced to get the same amount of space.
59 * Use gtk_box_set_spacing() to determine how much space will be
60 * minimally placed between all children in the GtkBox. Note that
61 * spacing is added <emphasis>between</emphasis> the children, while
62 * padding added by gtk_box_pack_start() or gtk_box_pack_end() is added
63 * <emphasis>on either side</emphasis> of the widget it belongs to.
65 * Use gtk_box_reorder_child() to move a GtkBox child to a different
68 * Use gtk_box_set_child_packing() to reset the #GtkBox:expand,
69 * #GtkBox:fill and #GtkBox:padding child properties.
70 * Use gtk_box_query_child_packing() to query these fields.
73 * Note that a single-row or single-column #GtkGrid provides exactly
74 * the same functionality as #GtkBox.
81 #include "gtkboxprivate.h"
83 #include "gtkorientable.h"
84 #include "gtkorientableprivate.h"
85 #include "gtkprivate.h"
86 #include "gtktypebuiltins.h"
87 #include "gtksizerequest.h"
88 #include "gtkwidgetpath.h"
89 #include "gtkwidgetprivate.h"
90 #include "a11y/gtkcontaineraccessible.h"
105 CHILD_PROP_PACK_TYPE,
109 struct _GtkBoxPrivate
113 GtkOrientation orientation;
116 guint default_expand : 1;
117 guint homogeneous : 1;
118 guint spacing_set : 1;
121 typedef struct _GtkBoxChild GtkBoxChild;
125 * @widget: the child widget, packed into the GtkBox.
126 * @padding: the number of extra pixels to put between this child and its
127 * neighbors, set when packed, zero by default.
128 * @expand: flag indicates whether extra space should be given to this child.
129 * Any extra space given to the parent GtkBox is divided up among all children
130 * with this attribute set to %TRUE; set when packed, %TRUE by default.
131 * @fill: flag indicates whether any extra space given to this child due to its
132 * @expand attribute being set is actually allocated to the child, rather than
133 * being used as padding around the widget; set when packed, %TRUE by default.
134 * @pack: one of #GtkPackType indicating whether the child is packed with
135 * reference to the start (top/left) or end (bottom/right) of the GtkBox.
148 static void gtk_box_size_allocate (GtkWidget *widget,
149 GtkAllocation *allocation);
151 static void gtk_box_compute_expand (GtkWidget *widget,
154 static void gtk_box_direction_changed (GtkWidget *widget,
155 GtkTextDirection previous_direction);
157 static void gtk_box_set_property (GObject *object,
161 static void gtk_box_get_property (GObject *object,
165 static void gtk_box_add (GtkContainer *container,
167 static void gtk_box_remove (GtkContainer *container,
169 static void gtk_box_forall (GtkContainer *container,
170 gboolean include_internals,
171 GtkCallback callback,
172 gpointer callback_data);
173 static void gtk_box_set_child_property (GtkContainer *container,
178 static void gtk_box_get_child_property (GtkContainer *container,
183 static GType gtk_box_child_type (GtkContainer *container);
184 static GtkWidgetPath * gtk_box_get_path_for_child
185 (GtkContainer *container,
189 static void gtk_box_get_preferred_width (GtkWidget *widget,
192 static void gtk_box_get_preferred_height (GtkWidget *widget,
195 static void gtk_box_get_preferred_width_for_height (GtkWidget *widget,
198 gint *natural_width);
199 static void gtk_box_get_preferred_height_for_width (GtkWidget *widget,
201 gint *minimum_height,
202 gint *natural_height);
205 G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
206 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
210 gtk_box_class_init (GtkBoxClass *class)
212 GObjectClass *object_class = G_OBJECT_CLASS (class);
213 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
214 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
216 object_class->set_property = gtk_box_set_property;
217 object_class->get_property = gtk_box_get_property;
219 widget_class->size_allocate = gtk_box_size_allocate;
220 widget_class->get_preferred_width = gtk_box_get_preferred_width;
221 widget_class->get_preferred_height = gtk_box_get_preferred_height;
222 widget_class->get_preferred_height_for_width = gtk_box_get_preferred_height_for_width;
223 widget_class->get_preferred_width_for_height = gtk_box_get_preferred_width_for_height;
224 widget_class->compute_expand = gtk_box_compute_expand;
225 widget_class->direction_changed = gtk_box_direction_changed;
227 container_class->add = gtk_box_add;
228 container_class->remove = gtk_box_remove;
229 container_class->forall = gtk_box_forall;
230 container_class->child_type = gtk_box_child_type;
231 container_class->set_child_property = gtk_box_set_child_property;
232 container_class->get_child_property = gtk_box_get_child_property;
233 container_class->get_path_for_child = gtk_box_get_path_for_child;
234 gtk_container_class_handle_border_width (container_class);
236 g_object_class_override_property (object_class,
240 g_object_class_install_property (object_class,
242 g_param_spec_int ("spacing",
244 P_("The amount of space between children"),
248 GTK_PARAM_READWRITE));
250 g_object_class_install_property (object_class,
252 g_param_spec_boolean ("homogeneous",
254 P_("Whether the children should all be the same size"),
256 GTK_PARAM_READWRITE));
261 * Whether the child should receive extra space when the parent grows.
263 * Note that the default value for this property is %FALSE for GtkBox,
264 * but #GtkHBox, #GtkVBox and other subclasses use the old default
267 * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
268 * and #GtkWidget:vexpand properties are the preferred way to influence
269 * child size allocation in containers.
271 gtk_container_class_install_child_property (container_class,
273 g_param_spec_boolean ("expand",
275 P_("Whether the child should receive extra space when the parent grows"),
277 GTK_PARAM_READWRITE));
282 * Whether the child should receive extra space when the parent grows.
284 * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
285 * and #GtkWidget:vexpand properties are the preferred way to influence
286 * child size allocation in containers.
288 gtk_container_class_install_child_property (container_class,
290 g_param_spec_boolean ("fill",
292 P_("Whether extra space given to the child should be allocated to the child or used as padding"),
294 GTK_PARAM_READWRITE));
296 gtk_container_class_install_child_property (container_class,
298 g_param_spec_uint ("padding",
300 P_("Extra space to put between the child and its neighbors, in pixels"),
302 GTK_PARAM_READWRITE));
303 gtk_container_class_install_child_property (container_class,
304 CHILD_PROP_PACK_TYPE,
305 g_param_spec_enum ("pack-type",
307 P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
308 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
309 GTK_PARAM_READWRITE));
310 gtk_container_class_install_child_property (container_class,
312 g_param_spec_int ("position",
314 P_("The index of the child in the parent"),
316 GTK_PARAM_READWRITE));
318 g_type_class_add_private (object_class, sizeof (GtkBoxPrivate));
320 gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_FILLER);
324 gtk_box_init (GtkBox *box)
326 GtkBoxPrivate *private;
328 box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
333 gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
334 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE);
336 private->orientation = GTK_ORIENTATION_HORIZONTAL;
337 private->children = NULL;
339 private->default_expand = FALSE;
340 private->homogeneous = FALSE;
341 private->spacing = 0;
342 private->spacing_set = FALSE;
346 gtk_box_set_property (GObject *object,
351 GtkBox *box = GTK_BOX (object);
352 GtkBoxPrivate *private = box->priv;
356 case PROP_ORIENTATION:
357 private->orientation = g_value_get_enum (value);
358 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (box));
359 gtk_widget_queue_resize (GTK_WIDGET (box));
362 gtk_box_set_spacing (box, g_value_get_int (value));
364 case PROP_HOMOGENEOUS:
365 gtk_box_set_homogeneous (box, g_value_get_boolean (value));
368 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
374 gtk_box_get_property (GObject *object,
379 GtkBox *box = GTK_BOX (object);
380 GtkBoxPrivate *private = box->priv;
384 case PROP_ORIENTATION:
385 g_value_set_enum (value, private->orientation);
388 g_value_set_int (value, private->spacing);
390 case PROP_HOMOGENEOUS:
391 g_value_set_boolean (value, private->homogeneous);
394 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
401 count_expand_children (GtkBox *box,
402 gint *visible_children,
403 gint *expand_children)
405 GtkBoxPrivate *private = box->priv;
409 *visible_children = *expand_children = 0;
411 for (children = private->children; children; children = children->next)
413 child = children->data;
415 if (gtk_widget_get_visible (child->widget))
417 *visible_children += 1;
418 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
419 *expand_children += 1;
425 gtk_box_size_allocate (GtkWidget *widget,
426 GtkAllocation *allocation)
428 GtkBox *box = GTK_BOX (widget);
429 GtkBoxPrivate *private = box->priv;
433 gint nexpand_children;
435 GtkTextDirection direction;
436 GtkAllocation child_allocation;
437 GtkRequestedSize *sizes;
443 gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
444 gint x = 0, y = 0, i;
448 gtk_widget_set_allocation (widget, allocation);
450 count_expand_children (box, &nvis_children, &nexpand_children);
452 /* If there is no visible child, simply return. */
453 if (nvis_children <= 0)
456 direction = gtk_widget_get_direction (widget);
457 sizes = g_newa (GtkRequestedSize, nvis_children);
459 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
460 size = allocation->width - (nvis_children - 1) * private->spacing;
462 size = allocation->height - (nvis_children - 1) * private->spacing;
464 /* Retrieve desired size for visible children. */
465 for (i = 0, children = private->children; children; children = children->next)
467 child = children->data;
469 if (!gtk_widget_get_visible (child->widget))
472 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
473 gtk_widget_get_preferred_width_for_height (child->widget,
475 &sizes[i].minimum_size,
476 &sizes[i].natural_size);
478 gtk_widget_get_preferred_height_for_width (child->widget,
480 &sizes[i].minimum_size,
481 &sizes[i].natural_size);
484 /* Assert the api is working properly */
485 if (sizes[i].minimum_size < 0)
486 g_error ("GtkBox child %s minimum %s: %d < 0 for %s %d",
487 gtk_widget_get_name (GTK_WIDGET (child->widget)),
488 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
489 sizes[i].minimum_size,
490 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
491 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
493 if (sizes[i].natural_size < sizes[i].minimum_size)
494 g_error ("GtkBox child %s natural %s: %d < minimum %d for %s %d",
495 gtk_widget_get_name (GTK_WIDGET (child->widget)),
496 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
497 sizes[i].natural_size,
498 sizes[i].minimum_size,
499 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
500 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
502 size -= sizes[i].minimum_size;
503 size -= child->padding * 2;
505 sizes[i].data = child;
510 if (private->homogeneous)
512 /* If were homogenous we still need to run the above loop to get the
513 * minimum sizes for children that are not going to fill
515 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
516 size = allocation->width - (nvis_children - 1) * private->spacing;
518 size = allocation->height - (nvis_children - 1) * private->spacing;
520 extra = size / nvis_children;
521 n_extra_widgets = size % nvis_children;
525 /* Bring children up to size first */
526 size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
528 /* Calculate space which hasn't distributed yet,
529 * and is available for expanding children.
531 if (nexpand_children > 0)
533 extra = size / nexpand_children;
534 n_extra_widgets = size % nexpand_children;
540 /* Allocate child positions. */
541 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
543 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
545 child_allocation.y = allocation->y;
546 child_allocation.height = MAX (1, allocation->height);
547 if (packing == GTK_PACK_START)
550 x = allocation->x + allocation->width;
554 child_allocation.x = allocation->x;
555 child_allocation.width = MAX (1, allocation->width);
556 if (packing == GTK_PACK_START)
559 y = allocation->y + allocation->height;
562 for (i = 0, children = private->children;
564 children = children->next)
566 child = children->data;
568 /* If widget is not visible, skip it. */
569 if (!gtk_widget_get_visible (child->widget))
572 /* If widget is packed differently skip it, but still increment i,
573 * since widget is visible and will be handled in next loop iteration.
575 if (child->pack != packing)
581 /* Assign the child's size. */
582 if (private->homogeneous)
586 if (n_extra_widgets > 0)
594 child_size = sizes[i].minimum_size + child->padding * 2;
596 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
600 if (n_extra_widgets > 0)
608 /* Assign the child's position. */
609 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
613 child_allocation.width = MAX (1, child_size - child->padding * 2);
614 child_allocation.x = x + child->padding;
618 child_allocation.width = sizes[i].minimum_size;
619 child_allocation.x = x + (child_size - child_allocation.width) / 2;
622 if (packing == GTK_PACK_START)
624 x += child_size + private->spacing;
628 x -= child_size + private->spacing;
630 child_allocation.x -= child_size;
633 if (direction == GTK_TEXT_DIR_RTL)
634 child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
637 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
641 child_allocation.height = MAX (1, child_size - child->padding * 2);
642 child_allocation.y = y + child->padding;
646 child_allocation.height = sizes[i].minimum_size;
647 child_allocation.y = y + (child_size - child_allocation.height) / 2;
650 if (packing == GTK_PACK_START)
652 y += child_size + private->spacing;
656 y -= child_size + private->spacing;
658 child_allocation.y -= child_size;
661 gtk_widget_size_allocate (child->widget, &child_allocation);
669 gtk_box_compute_expand (GtkWidget *widget,
673 GtkBoxPrivate *private = GTK_BOX (widget)->priv;
677 gboolean opposite_expand;
678 GtkOrientation opposite_orientation;
680 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
681 opposite_orientation = GTK_ORIENTATION_VERTICAL;
683 opposite_orientation = GTK_ORIENTATION_HORIZONTAL;
686 opposite_expand = FALSE;
688 for (children = private->children; children; children = children->next)
690 child = children->data;
692 /* we don't recurse into children anymore as soon as we know
693 * expand=TRUE in an orientation
696 if (child->expand || (!our_expand && gtk_widget_compute_expand (child->widget, private->orientation)))
699 if (!opposite_expand && gtk_widget_compute_expand (child->widget, opposite_orientation))
700 opposite_expand = TRUE;
702 if (our_expand && opposite_expand)
706 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
708 *hexpand_p = our_expand;
709 *vexpand_p = opposite_expand;
713 *hexpand_p = opposite_expand;
714 *vexpand_p = our_expand;
719 gtk_box_child_type (GtkContainer *container)
721 return GTK_TYPE_WIDGET;
725 gtk_box_set_child_property (GtkContainer *container,
734 GtkPackType pack_type = 0;
736 if (property_id != CHILD_PROP_POSITION)
737 gtk_box_query_child_packing (GTK_BOX (container),
745 case CHILD_PROP_EXPAND:
746 gtk_box_set_child_packing (GTK_BOX (container),
748 g_value_get_boolean (value),
753 case CHILD_PROP_FILL:
754 gtk_box_set_child_packing (GTK_BOX (container),
757 g_value_get_boolean (value),
761 case CHILD_PROP_PADDING:
762 gtk_box_set_child_packing (GTK_BOX (container),
766 g_value_get_uint (value),
769 case CHILD_PROP_PACK_TYPE:
770 gtk_box_set_child_packing (GTK_BOX (container),
775 g_value_get_enum (value));
777 case CHILD_PROP_POSITION:
778 gtk_box_reorder_child (GTK_BOX (container),
780 g_value_get_int (value));
783 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
789 gtk_box_get_child_property (GtkContainer *container,
795 gboolean expand = FALSE;
796 gboolean fill = FALSE;
798 GtkPackType pack_type = 0;
801 if (property_id != CHILD_PROP_POSITION)
802 gtk_box_query_child_packing (GTK_BOX (container),
811 case CHILD_PROP_EXPAND:
812 g_value_set_boolean (value, expand);
814 case CHILD_PROP_FILL:
815 g_value_set_boolean (value, fill);
817 case CHILD_PROP_PADDING:
818 g_value_set_uint (value, padding);
820 case CHILD_PROP_PACK_TYPE:
821 g_value_set_enum (value, pack_type);
823 case CHILD_PROP_POSITION:
825 for (list = GTK_BOX (container)->priv->children; list; list = list->next)
827 GtkBoxChild *child_entry;
829 child_entry = list->data;
830 if (child_entry->widget == child)
834 g_value_set_int (value, list ? i : -1);
837 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
842 typedef struct _CountingData CountingData;
843 struct _CountingData {
851 count_widget_position (GtkWidget *widget,
854 CountingData *count = data;
856 if (!gtk_widget_get_visible (widget))
859 if (count->widget == widget)
861 else if (count->found)
868 gtk_box_get_visible_position (GtkBox *box,
871 CountingData count = { child, FALSE, 0, 0 };
873 /* foreach iterates in visible order */
874 gtk_container_foreach (GTK_CONTAINER (box),
875 count_widget_position,
878 /* the child wasn't found, it's likely an internal child of some
879 * subclass, return -1 to indicate that there is no sibling relation
880 * to the regular box children
885 if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
886 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
892 static GtkWidgetPath *
893 gtk_box_get_path_for_child (GtkContainer *container,
896 GtkWidgetPath *path, *sibling_path;
898 GtkBoxPrivate *private;
899 GList *list, *children;
901 box = GTK_BOX (container);
904 path = _gtk_widget_create_path (GTK_WIDGET (container));
906 if (gtk_widget_get_visible (child))
910 sibling_path = gtk_widget_path_new ();
912 /* get_children works in visible order */
913 children = gtk_container_get_children (container);
914 if (private->orientation == GTK_ORIENTATION_HORIZONTAL &&
915 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
916 children = g_list_reverse (children);
918 for (list = children; list; list = list->next)
920 if (!gtk_widget_get_visible (list->data))
923 gtk_widget_path_append_for_widget (sibling_path, list->data);
926 g_list_free (children);
928 position = gtk_box_get_visible_position (box, child);
931 gtk_widget_path_append_with_siblings (path, sibling_path, position);
933 gtk_widget_path_append_for_widget (path, child);
935 gtk_widget_path_unref (sibling_path);
938 gtk_widget_path_append_for_widget (path, child);
944 gtk_box_invalidate_order_foreach (GtkWidget *widget)
946 _gtk_widget_invalidate_style_context (widget, GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
950 gtk_box_invalidate_order (GtkBox *box)
952 gtk_container_foreach (GTK_CONTAINER (box),
953 (GtkCallback) gtk_box_invalidate_order_foreach,
958 gtk_box_direction_changed (GtkWidget *widget,
959 GtkTextDirection previous_direction)
961 gtk_box_invalidate_order (GTK_BOX (widget));
965 box_child_visibility_notify_cb (GObject *obj,
969 GtkBox *box = user_data;
971 gtk_box_invalidate_order (box);
975 gtk_box_pack (GtkBox *box,
980 GtkPackType pack_type)
982 GtkBoxPrivate *private = box->priv;
983 GtkBoxChild *child_info;
985 g_return_if_fail (GTK_IS_BOX (box));
986 g_return_if_fail (GTK_IS_WIDGET (child));
987 g_return_if_fail (gtk_widget_get_parent (child) == NULL);
989 child_info = g_new (GtkBoxChild, 1);
990 child_info->widget = child;
991 child_info->padding = padding;
992 child_info->expand = expand ? TRUE : FALSE;
993 child_info->fill = fill ? TRUE : FALSE;
994 child_info->pack = pack_type;
996 private->children = g_list_append (private->children, child_info);
998 gtk_widget_freeze_child_notify (child);
1000 gtk_box_invalidate_order (box);
1001 gtk_widget_set_parent (child, GTK_WIDGET (box));
1003 g_signal_connect (child, "notify::visible",
1004 G_CALLBACK (box_child_visibility_notify_cb), box);
1006 gtk_widget_child_notify (child, "expand");
1007 gtk_widget_child_notify (child, "fill");
1008 gtk_widget_child_notify (child, "padding");
1009 gtk_widget_child_notify (child, "pack-type");
1010 gtk_widget_child_notify (child, "position");
1011 gtk_widget_thaw_child_notify (child);
1015 gtk_box_get_size (GtkWidget *widget,
1016 GtkOrientation orientation,
1021 GtkBoxPrivate *private;
1024 gint minimum, natural;
1026 box = GTK_BOX (widget);
1027 private = box->priv;
1029 minimum = natural = 0;
1033 for (children = private->children; children; children = children->next)
1035 GtkBoxChild *child = children->data;
1037 if (gtk_widget_get_visible (child->widget))
1039 gint child_minimum, child_natural;
1041 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1042 gtk_widget_get_preferred_width (child->widget,
1043 &child_minimum, &child_natural);
1045 gtk_widget_get_preferred_height (child->widget,
1046 &child_minimum, &child_natural);
1048 if (private->orientation == orientation)
1050 if (private->homogeneous)
1054 largest = child_minimum + child->padding * 2;
1055 minimum = MAX (minimum, largest);
1057 largest = child_natural + child->padding * 2;
1058 natural = MAX (natural, largest);
1062 minimum += child_minimum + child->padding * 2;
1063 natural += child_natural + child->padding * 2;
1068 /* The biggest mins and naturals in the opposing orientation */
1069 minimum = MAX (minimum, child_minimum);
1070 natural = MAX (natural, child_natural);
1077 if (nvis_children > 0 && private->orientation == orientation)
1079 if (private->homogeneous)
1081 minimum *= nvis_children;
1082 natural *= nvis_children;
1084 minimum += (nvis_children - 1) * private->spacing;
1085 natural += (nvis_children - 1) * private->spacing;
1089 *minimum_size = minimum;
1092 *natural_size = natural;
1096 gtk_box_get_preferred_width (GtkWidget *widget,
1100 gtk_box_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
1104 gtk_box_get_preferred_height (GtkWidget *widget,
1108 gtk_box_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
1112 gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
1117 GtkBoxPrivate *private = box->priv;
1121 gint nexpand_children;
1122 gint computed_minimum = 0, computed_natural = 0;
1123 GtkRequestedSize *sizes;
1124 GtkPackType packing;
1125 gint size, extra, i;
1126 gint child_size, child_minimum, child_natural;
1127 gint n_extra_widgets = 0;
1129 count_expand_children (box, &nvis_children, &nexpand_children);
1131 if (nvis_children <= 0)
1134 sizes = g_newa (GtkRequestedSize, nvis_children);
1135 size = avail_size - (nvis_children - 1) * private->spacing;
1137 /* Retrieve desired size for visible children */
1138 for (i = 0, children = private->children; children; children = children->next)
1140 child = children->data;
1142 if (gtk_widget_get_visible (child->widget))
1144 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1145 gtk_widget_get_preferred_width (child->widget,
1146 &sizes[i].minimum_size,
1147 &sizes[i].natural_size);
1149 gtk_widget_get_preferred_height (child->widget,
1150 &sizes[i].minimum_size,
1151 &sizes[i].natural_size);
1153 /* Assert the api is working properly */
1154 if (sizes[i].minimum_size < 0)
1155 g_error ("GtkBox child %s minimum %s: %d < 0",
1156 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1157 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1158 sizes[i].minimum_size);
1160 if (sizes[i].natural_size < sizes[i].minimum_size)
1161 g_error ("GtkBox child %s natural %s: %d < minimum %d",
1162 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1163 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1164 sizes[i].natural_size,
1165 sizes[i].minimum_size);
1167 size -= sizes[i].minimum_size;
1168 size -= child->padding * 2;
1170 sizes[i].data = child;
1176 if (private->homogeneous)
1178 /* If were homogenous we still need to run the above loop to get the
1179 * minimum sizes for children that are not going to fill
1181 size = avail_size - (nvis_children - 1) * private->spacing;
1182 extra = size / nvis_children;
1183 n_extra_widgets = size % nvis_children;
1187 /* Bring children up to size first */
1188 size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
1190 /* Calculate space which hasn't distributed yet,
1191 * and is available for expanding children.
1193 if (nexpand_children > 0)
1195 extra = size / nexpand_children;
1196 n_extra_widgets = size % nexpand_children;
1202 /* Allocate child positions. */
1203 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1205 for (i = 0, children = private->children;
1207 children = children->next)
1209 child = children->data;
1211 /* If widget is not visible, skip it. */
1212 if (!gtk_widget_get_visible (child->widget))
1215 /* If widget is packed differently skip it, but still increment i,
1216 * since widget is visible and will be handled in next loop iteration.
1218 if (child->pack != packing)
1224 if (child->pack == packing)
1226 /* Assign the child's size. */
1227 if (private->homogeneous)
1231 if (n_extra_widgets > 0)
1239 child_size = sizes[i].minimum_size + child->padding * 2;
1241 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
1243 child_size += extra;
1245 if (n_extra_widgets > 0)
1255 child_size = MAX (1, child_size - child->padding * 2);
1259 child_size = sizes[i].minimum_size;
1263 /* Assign the child's position. */
1264 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1265 gtk_widget_get_preferred_height_for_width (child->widget,
1266 child_size, &child_minimum, &child_natural);
1267 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1268 gtk_widget_get_preferred_width_for_height (child->widget,
1269 child_size, &child_minimum, &child_natural);
1272 computed_minimum = MAX (computed_minimum, child_minimum);
1273 computed_natural = MAX (computed_natural, child_natural);
1280 *minimum_size = computed_minimum;
1282 *natural_size = computed_natural;
1286 gtk_box_compute_size_for_orientation (GtkBox *box,
1291 GtkBoxPrivate *private = box->priv;
1293 gint nvis_children = 0;
1294 gint required_size = 0, required_natural = 0, child_size, child_natural;
1295 gint largest_child = 0, largest_natural = 0;
1297 for (children = private->children; children != NULL;
1298 children = children->next)
1300 GtkBoxChild *child = children->data;
1302 if (gtk_widget_get_visible (child->widget))
1305 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1306 gtk_widget_get_preferred_width_for_height (child->widget,
1307 avail_size, &child_size, &child_natural);
1309 gtk_widget_get_preferred_height_for_width (child->widget,
1310 avail_size, &child_size, &child_natural);
1313 child_size += child->padding * 2;
1314 child_natural += child->padding * 2;
1316 if (child_size > largest_child)
1317 largest_child = child_size;
1319 if (child_natural > largest_natural)
1320 largest_natural = child_natural;
1322 required_size += child_size;
1323 required_natural += child_natural;
1329 if (nvis_children > 0)
1331 if (private->homogeneous)
1333 required_size = largest_child * nvis_children;
1334 required_natural = largest_natural * nvis_children;
1337 required_size += (nvis_children - 1) * private->spacing;
1338 required_natural += (nvis_children - 1) * private->spacing;
1342 *minimum_size = required_size;
1345 *natural_size = required_natural;
1349 gtk_box_get_preferred_width_for_height (GtkWidget *widget,
1351 gint *minimum_width,
1352 gint *natural_width)
1354 GtkBox *box = GTK_BOX (widget);
1355 GtkBoxPrivate *private = box->priv;
1357 if (private->orientation == GTK_ORIENTATION_VERTICAL)
1358 gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width);
1360 gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width);
1364 gtk_box_get_preferred_height_for_width (GtkWidget *widget,
1366 gint *minimum_height,
1367 gint *natural_height)
1369 GtkBox *box = GTK_BOX (widget);
1370 GtkBoxPrivate *private = box->priv;
1372 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1373 gtk_box_compute_size_for_opposing_orientation (box, width, minimum_height, natural_height);
1375 gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height);
1380 * @orientation: the box's orientation.
1381 * @spacing: the number of pixels to place by default between children.
1383 * Creates a new #GtkBox.
1385 * Return value: a new #GtkBox.
1390 gtk_box_new (GtkOrientation orientation,
1393 return g_object_new (GTK_TYPE_BOX,
1394 "orientation", orientation,
1400 * gtk_box_pack_start:
1402 * @child: the #GtkWidget to be added to @box
1403 * @expand: %TRUE if the new child is to be given extra space allocated
1404 * to @box. The extra space will be divided evenly between all children
1405 * that use this option
1406 * @fill: %TRUE if space given to @child by the @expand option is
1407 * actually allocated to @child, rather than just padding it. This
1408 * parameter has no effect if @expand is set to %FALSE. A child is
1409 * always allocated the full height of a horizontal #GtkBox and the full width
1410 * of a vertical #GtkBox. This option affects the other dimension
1411 * @padding: extra space in pixels to put between this child and its
1412 * neighbors, over and above the global amount specified by
1413 * #GtkBox:spacing property. If @child is a widget at one of the
1414 * reference ends of @box, then @padding pixels are also put between
1415 * @child and the reference edge of @box
1417 * Adds @child to @box, packed with reference to the start of @box.
1418 * The @child is packed after any other child packed with reference
1419 * to the start of @box.
1422 gtk_box_pack_start (GtkBox *box,
1428 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
1434 * @child: the #GtkWidget to be added to @box
1435 * @expand: %TRUE if the new child is to be given extra space allocated
1436 * to @box. The extra space will be divided evenly between all children
1437 * of @box that use this option
1438 * @fill: %TRUE if space given to @child by the @expand option is
1439 * actually allocated to @child, rather than just padding it. This
1440 * parameter has no effect if @expand is set to %FALSE. A child is
1441 * always allocated the full height of a horizontal #GtkBox and the full width
1442 * of a vertical #GtkBox. This option affects the other dimension
1443 * @padding: extra space in pixels to put between this child and its
1444 * neighbors, over and above the global amount specified by
1445 * #GtkBox:spacing property. If @child is a widget at one of the
1446 * reference ends of @box, then @padding pixels are also put between
1447 * @child and the reference edge of @box
1449 * Adds @child to @box, packed with reference to the end of @box.
1450 * The @child is packed after (away from end of) any other child
1451 * packed with reference to the end of @box.
1454 gtk_box_pack_end (GtkBox *box,
1460 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
1464 * gtk_box_set_homogeneous:
1466 * @homogeneous: a boolean value, %TRUE to create equal allotments,
1467 * %FALSE for variable allotments
1469 * Sets the #GtkBox:homogeneous property of @box, controlling
1470 * whether or not all children of @box are given equal space
1474 gtk_box_set_homogeneous (GtkBox *box,
1475 gboolean homogeneous)
1477 GtkBoxPrivate *private;
1479 g_return_if_fail (GTK_IS_BOX (box));
1481 private = box->priv;
1483 if ((homogeneous ? TRUE : FALSE) != private->homogeneous)
1485 private->homogeneous = homogeneous ? TRUE : FALSE;
1486 g_object_notify (G_OBJECT (box), "homogeneous");
1487 gtk_widget_queue_resize (GTK_WIDGET (box));
1492 * gtk_box_get_homogeneous:
1495 * Returns whether the box is homogeneous (all children are the
1496 * same size). See gtk_box_set_homogeneous().
1498 * Return value: %TRUE if the box is homogeneous.
1501 gtk_box_get_homogeneous (GtkBox *box)
1503 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1505 return box->priv->homogeneous;
1509 * gtk_box_set_spacing:
1511 * @spacing: the number of pixels to put between children
1513 * Sets the #GtkBox:spacing property of @box, which is the
1514 * number of pixels to place between children of @box.
1517 gtk_box_set_spacing (GtkBox *box,
1520 GtkBoxPrivate *private;
1522 g_return_if_fail (GTK_IS_BOX (box));
1524 private = box->priv;
1526 if (spacing != private->spacing)
1528 private->spacing = spacing;
1529 _gtk_box_set_spacing_set (box, TRUE);
1531 g_object_notify (G_OBJECT (box), "spacing");
1533 gtk_widget_queue_resize (GTK_WIDGET (box));
1538 * gtk_box_get_spacing:
1541 * Gets the value set by gtk_box_set_spacing().
1543 * Return value: spacing between children
1546 gtk_box_get_spacing (GtkBox *box)
1548 g_return_val_if_fail (GTK_IS_BOX (box), 0);
1550 return box->priv->spacing;
1554 _gtk_box_set_spacing_set (GtkBox *box,
1555 gboolean spacing_set)
1557 GtkBoxPrivate *private;
1559 g_return_if_fail (GTK_IS_BOX (box));
1561 private = box->priv;
1563 private->spacing_set = spacing_set ? TRUE : FALSE;
1567 _gtk_box_get_spacing_set (GtkBox *box)
1569 GtkBoxPrivate *private;
1571 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1573 private = box->priv;
1575 return private->spacing_set;
1579 * gtk_box_reorder_child:
1581 * @child: the #GtkWidget to move
1582 * @position: the new position for @child in the list of children
1583 * of @box, starting from 0. If negative, indicates the end of
1586 * Moves @child to a new @position in the list of @box children.
1587 * The list is the <structfield>children</structfield> field of
1588 * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START
1589 * as well as widgets packed #GTK_PACK_END, in the order that these
1590 * widgets were added to @box.
1592 * A widget's position in the @box children list determines where
1593 * the widget is packed into @box. A child widget at some position
1594 * in the list will be packed just after all other widgets of the
1595 * same packing type that appear earlier in the list.
1598 gtk_box_reorder_child (GtkBox *box,
1602 GtkBoxPrivate *priv;
1605 GtkBoxChild *child_info = NULL;
1608 g_return_if_fail (GTK_IS_BOX (box));
1609 g_return_if_fail (GTK_IS_WIDGET (child));
1613 old_link = priv->children;
1617 child_info = old_link->data;
1618 if (child_info->widget == child)
1621 old_link = old_link->next;
1625 g_return_if_fail (old_link != NULL);
1627 if (position == old_position)
1630 priv->children = g_list_delete_link (priv->children, old_link);
1635 new_link = g_list_nth (priv->children, position);
1637 priv->children = g_list_insert_before (priv->children, new_link, child_info);
1639 gtk_widget_child_notify (child, "position");
1640 if (gtk_widget_get_visible (child)
1641 && gtk_widget_get_visible (GTK_WIDGET (box)))
1643 gtk_box_invalidate_order (box);
1644 gtk_widget_queue_resize (child);
1649 * gtk_box_query_child_packing:
1651 * @child: the #GtkWidget of the child to query
1652 * @expand: (out): pointer to return location for #GtkBox:expand child
1654 * @fill: (out): pointer to return location for #GtkBox:fill child
1656 * @padding: (out): pointer to return location for #GtkBox:padding
1658 * @pack_type: (out): pointer to return location for #GtkBox:pack-type
1661 * Obtains information about how @child is packed into @box.
1664 gtk_box_query_child_packing (GtkBox *box,
1669 GtkPackType *pack_type)
1671 GtkBoxPrivate *private;
1673 GtkBoxChild *child_info = NULL;
1675 g_return_if_fail (GTK_IS_BOX (box));
1676 g_return_if_fail (GTK_IS_WIDGET (child));
1678 private = box->priv;
1680 list = private->children;
1683 child_info = list->data;
1684 if (child_info->widget == child)
1693 *expand = child_info->expand;
1695 *fill = child_info->fill;
1697 *padding = child_info->padding;
1699 *pack_type = child_info->pack;
1704 * gtk_box_set_child_packing:
1706 * @child: the #GtkWidget of the child to set
1707 * @expand: the new value of the #GtkBox:expand child property
1708 * @fill: the new value of the #GtkBox:fill child property
1709 * @padding: the new value of the #GtkBox:padding child property
1710 * @pack_type: the new value of the #GtkBox:pack-type child property
1712 * Sets the way @child is packed into @box.
1715 gtk_box_set_child_packing (GtkBox *box,
1720 GtkPackType pack_type)
1722 GtkBoxPrivate *private;
1724 GtkBoxChild *child_info = NULL;
1726 g_return_if_fail (GTK_IS_BOX (box));
1727 g_return_if_fail (GTK_IS_WIDGET (child));
1729 private = box->priv;
1731 list = private->children;
1734 child_info = list->data;
1735 if (child_info->widget == child)
1741 gtk_widget_freeze_child_notify (child);
1746 expanded = expand != FALSE;
1748 /* avoid setting expand if unchanged, since queue_compute_expand
1749 * can be expensive-ish
1751 if (child_info->expand != expanded)
1753 child_info->expand = expand != FALSE;
1754 gtk_widget_queue_compute_expand (GTK_WIDGET (box));
1755 gtk_widget_child_notify (child, "expand");
1758 child_info->fill = fill != FALSE;
1759 gtk_widget_child_notify (child, "fill");
1760 child_info->padding = padding;
1761 gtk_widget_child_notify (child, "padding");
1762 if (pack_type != GTK_PACK_END)
1763 pack_type = GTK_PACK_START;
1764 if (child_info->pack != pack_type)
1766 child_info->pack = pack_type;
1767 gtk_widget_child_notify (child, "pack-type");
1768 gtk_box_invalidate_order (box);
1771 if (gtk_widget_get_visible (child)
1772 && gtk_widget_get_visible (GTK_WIDGET (box)))
1773 gtk_widget_queue_resize (child);
1775 gtk_widget_thaw_child_notify (child);
1779 _gtk_box_set_old_defaults (GtkBox *box)
1781 GtkBoxPrivate *private;
1783 g_return_if_fail (GTK_IS_BOX (box));
1785 private = box->priv;
1787 private->default_expand = TRUE;
1791 gtk_box_add (GtkContainer *container,
1794 GtkBoxPrivate *priv = GTK_BOX (container)->priv;
1796 gtk_box_pack_start (GTK_BOX (container), widget,
1797 priv->default_expand,
1803 gtk_box_remove (GtkContainer *container,
1806 GtkBox *box = GTK_BOX (container);
1807 GtkBoxPrivate *priv = box->priv;
1811 children = priv->children;
1814 child = children->data;
1816 if (child->widget == widget)
1818 gboolean was_visible;
1820 g_signal_handlers_disconnect_by_func (widget,
1821 box_child_visibility_notify_cb,
1824 was_visible = gtk_widget_get_visible (widget);
1825 gtk_widget_unparent (widget);
1827 priv->children = g_list_remove_link (priv->children, children);
1828 g_list_free (children);
1831 /* queue resize regardless of gtk_widget_get_visible (container),
1832 * since that's what is needed by toplevels.
1836 gtk_box_invalidate_order (box);
1837 gtk_widget_queue_resize (GTK_WIDGET (container));
1843 children = children->next;
1848 gtk_box_forall (GtkContainer *container,
1849 gboolean include_internals,
1850 GtkCallback callback,
1851 gpointer callback_data)
1853 GtkBox *box = GTK_BOX (container);
1854 GtkBoxPrivate *priv = box->priv;
1858 children = priv->children;
1861 child = children->data;
1862 children = children->next;
1864 if (child->pack == GTK_PACK_START)
1865 (* callback) (child->widget, callback_data);
1868 children = g_list_last (priv->children);
1871 child = children->data;
1872 children = children->prev;
1874 if (child->pack == GTK_PACK_END)
1875 (* callback) (child->widget, callback_data);
1880 _gtk_box_get_children (GtkBox *box)
1882 GtkBoxPrivate *priv;
1885 GList *retval = NULL;
1887 g_return_val_if_fail (GTK_IS_BOX (box), NULL);
1891 children = priv->children;
1894 child = children->data;
1895 children = children->next;
1897 retval = g_list_prepend (retval, child->widget);
1900 return g_list_reverse (retval);