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 "gtkprivate.h"
85 #include "gtktypebuiltins.h"
86 #include "gtksizerequest.h"
87 #include "gtkwidgetpath.h"
88 #include "a11y/gtkboxaccessible.h"
103 CHILD_PROP_PACK_TYPE,
107 struct _GtkBoxPrivate
111 GtkOrientation orientation;
114 guint default_expand : 1;
115 guint homogeneous : 1;
116 guint spacing_set : 1;
119 typedef struct _GtkBoxChild GtkBoxChild;
123 * @widget: the child widget, packed into the GtkBox.
124 * @padding: the number of extra pixels to put between this child and its
125 * neighbors, set when packed, zero by default.
126 * @expand: flag indicates whether extra space should be given to this child.
127 * Any extra space given to the parent GtkBox is divided up among all children
128 * with this attribute set to %TRUE; set when packed, %TRUE by default.
129 * @fill: flag indicates whether any extra space given to this child due to its
130 * @expand attribute being set is actually allocated to the child, rather than
131 * being used as padding around the widget; set when packed, %TRUE by default.
132 * @pack: one of #GtkPackType indicating whether the child is packed with
133 * reference to the start (top/left) or end (bottom/right) of the GtkBox.
146 static void gtk_box_size_allocate (GtkWidget *widget,
147 GtkAllocation *allocation);
149 static void gtk_box_compute_expand (GtkWidget *widget,
152 static void gtk_box_direction_changed (GtkWidget *widget,
153 GtkTextDirection previous_direction);
155 static void gtk_box_set_property (GObject *object,
159 static void gtk_box_get_property (GObject *object,
163 static void gtk_box_add (GtkContainer *container,
165 static void gtk_box_remove (GtkContainer *container,
167 static void gtk_box_forall (GtkContainer *container,
168 gboolean include_internals,
169 GtkCallback callback,
170 gpointer callback_data);
171 static void gtk_box_set_child_property (GtkContainer *container,
176 static void gtk_box_get_child_property (GtkContainer *container,
181 static GType gtk_box_child_type (GtkContainer *container);
182 static GtkWidgetPath * gtk_box_get_path_for_child
183 (GtkContainer *container,
187 static void gtk_box_get_preferred_width (GtkWidget *widget,
190 static void gtk_box_get_preferred_height (GtkWidget *widget,
193 static void gtk_box_get_preferred_width_for_height (GtkWidget *widget,
196 gint *natural_width);
197 static void gtk_box_get_preferred_height_for_width (GtkWidget *widget,
199 gint *minimum_height,
200 gint *natural_height);
203 G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
204 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
208 gtk_box_class_init (GtkBoxClass *class)
210 GObjectClass *object_class = G_OBJECT_CLASS (class);
211 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
212 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
214 object_class->set_property = gtk_box_set_property;
215 object_class->get_property = gtk_box_get_property;
217 widget_class->size_allocate = gtk_box_size_allocate;
218 widget_class->get_preferred_width = gtk_box_get_preferred_width;
219 widget_class->get_preferred_height = gtk_box_get_preferred_height;
220 widget_class->get_preferred_height_for_width = gtk_box_get_preferred_height_for_width;
221 widget_class->get_preferred_width_for_height = gtk_box_get_preferred_width_for_height;
222 widget_class->compute_expand = gtk_box_compute_expand;
223 widget_class->direction_changed = gtk_box_direction_changed;
225 container_class->add = gtk_box_add;
226 container_class->remove = gtk_box_remove;
227 container_class->forall = gtk_box_forall;
228 container_class->child_type = gtk_box_child_type;
229 container_class->set_child_property = gtk_box_set_child_property;
230 container_class->get_child_property = gtk_box_get_child_property;
231 container_class->get_path_for_child = gtk_box_get_path_for_child;
232 gtk_container_class_handle_border_width (container_class);
234 g_object_class_override_property (object_class,
238 g_object_class_install_property (object_class,
240 g_param_spec_int ("spacing",
242 P_("The amount of space between children"),
246 GTK_PARAM_READWRITE));
248 g_object_class_install_property (object_class,
250 g_param_spec_boolean ("homogeneous",
252 P_("Whether the children should all be the same size"),
254 GTK_PARAM_READWRITE));
259 * Whether the child should receive extra space when the parent grows.
261 * Note that the default value for this property is %FALSE for GtkBox,
262 * but #GtkHBox, #GtkVBox and other subclasses use the old default
265 * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
266 * and #GtkWidget:vexpand properties are the preferred way to influence
267 * child size allocation in containers.
269 gtk_container_class_install_child_property (container_class,
271 g_param_spec_boolean ("expand",
273 P_("Whether the child should receive extra space when the parent grows"),
275 GTK_PARAM_READWRITE));
280 * Whether the child should receive extra space when the parent grows.
282 * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
283 * and #GtkWidget:vexpand properties are the preferred way to influence
284 * child size allocation in containers.
286 gtk_container_class_install_child_property (container_class,
288 g_param_spec_boolean ("fill",
290 P_("Whether extra space given to the child should be allocated to the child or used as padding"),
292 GTK_PARAM_READWRITE));
294 gtk_container_class_install_child_property (container_class,
296 g_param_spec_uint ("padding",
298 P_("Extra space to put between the child and its neighbors, in pixels"),
300 GTK_PARAM_READWRITE));
301 gtk_container_class_install_child_property (container_class,
302 CHILD_PROP_PACK_TYPE,
303 g_param_spec_enum ("pack-type",
305 P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
306 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
307 GTK_PARAM_READWRITE));
308 gtk_container_class_install_child_property (container_class,
310 g_param_spec_int ("position",
312 P_("The index of the child in the parent"),
314 GTK_PARAM_READWRITE));
316 g_type_class_add_private (object_class, sizeof (GtkBoxPrivate));
318 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_BOX_ACCESSIBLE);
322 gtk_box_init (GtkBox *box)
324 GtkBoxPrivate *private;
326 box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
331 gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
332 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE);
334 private->orientation = GTK_ORIENTATION_HORIZONTAL;
335 private->children = NULL;
337 private->default_expand = FALSE;
338 private->homogeneous = FALSE;
339 private->spacing = 0;
340 private->spacing_set = FALSE;
344 gtk_box_set_property (GObject *object,
349 GtkBox *box = GTK_BOX (object);
350 GtkBoxPrivate *private = box->priv;
354 case PROP_ORIENTATION:
355 private->orientation = g_value_get_enum (value);
356 gtk_widget_queue_resize (GTK_WIDGET (box));
359 gtk_box_set_spacing (box, g_value_get_int (value));
361 case PROP_HOMOGENEOUS:
362 gtk_box_set_homogeneous (box, g_value_get_boolean (value));
365 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371 gtk_box_get_property (GObject *object,
376 GtkBox *box = GTK_BOX (object);
377 GtkBoxPrivate *private = box->priv;
381 case PROP_ORIENTATION:
382 g_value_set_enum (value, private->orientation);
385 g_value_set_int (value, private->spacing);
387 case PROP_HOMOGENEOUS:
388 g_value_set_boolean (value, private->homogeneous);
391 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
398 count_expand_children (GtkBox *box,
399 gint *visible_children,
400 gint *expand_children)
402 GtkBoxPrivate *private = box->priv;
406 *visible_children = *expand_children = 0;
408 for (children = private->children; children; children = children->next)
410 child = children->data;
412 if (gtk_widget_get_visible (child->widget))
414 *visible_children += 1;
415 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
416 *expand_children += 1;
422 gtk_box_size_allocate (GtkWidget *widget,
423 GtkAllocation *allocation)
425 GtkBox *box = GTK_BOX (widget);
426 GtkBoxPrivate *private = box->priv;
430 gint nexpand_children;
432 GtkTextDirection direction;
433 GtkAllocation child_allocation;
434 GtkRequestedSize *sizes;
440 gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
441 gint x = 0, y = 0, i;
445 gtk_widget_set_allocation (widget, allocation);
447 count_expand_children (box, &nvis_children, &nexpand_children);
449 /* If there is no visible child, simply return. */
450 if (nvis_children <= 0)
453 direction = gtk_widget_get_direction (widget);
454 sizes = g_newa (GtkRequestedSize, nvis_children);
456 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
457 size = allocation->width - (nvis_children - 1) * private->spacing;
459 size = allocation->height - (nvis_children - 1) * private->spacing;
461 /* Retrieve desired size for visible children. */
462 for (i = 0, children = private->children; children; children = children->next)
464 child = children->data;
466 if (!gtk_widget_get_visible (child->widget))
469 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
470 gtk_widget_get_preferred_width_for_height (child->widget,
472 &sizes[i].minimum_size,
473 &sizes[i].natural_size);
475 gtk_widget_get_preferred_height_for_width (child->widget,
477 &sizes[i].minimum_size,
478 &sizes[i].natural_size);
481 /* Assert the api is working properly */
482 if (sizes[i].minimum_size < 0)
483 g_error ("GtkBox child %s minimum %s: %d < 0 for %s %d",
484 gtk_widget_get_name (GTK_WIDGET (child->widget)),
485 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
486 sizes[i].minimum_size,
487 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
488 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
490 if (sizes[i].natural_size < sizes[i].minimum_size)
491 g_error ("GtkBox child %s natural %s: %d < minimum %d for %s %d",
492 gtk_widget_get_name (GTK_WIDGET (child->widget)),
493 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
494 sizes[i].natural_size,
495 sizes[i].minimum_size,
496 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
497 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
499 size -= sizes[i].minimum_size;
500 size -= child->padding * 2;
502 sizes[i].data = child;
507 if (private->homogeneous)
509 /* If were homogenous we still need to run the above loop to get the
510 * minimum sizes for children that are not going to fill
512 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
513 size = allocation->width - (nvis_children - 1) * private->spacing;
515 size = allocation->height - (nvis_children - 1) * private->spacing;
517 extra = size / nvis_children;
518 n_extra_widgets = size % nvis_children;
522 /* Bring children up to size first */
523 size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
525 /* Calculate space which hasn't distributed yet,
526 * and is available for expanding children.
528 if (nexpand_children > 0)
530 extra = size / nexpand_children;
531 n_extra_widgets = size % nexpand_children;
537 /* Allocate child positions. */
538 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
540 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
542 child_allocation.y = allocation->y;
543 child_allocation.height = MAX (1, allocation->height);
544 if (packing == GTK_PACK_START)
547 x = allocation->x + allocation->width;
551 child_allocation.x = allocation->x;
552 child_allocation.width = MAX (1, allocation->width);
553 if (packing == GTK_PACK_START)
556 y = allocation->y + allocation->height;
559 for (i = 0, children = private->children;
561 children = children->next)
563 child = children->data;
565 /* If widget is not visible, skip it. */
566 if (!gtk_widget_get_visible (child->widget))
569 /* If widget is packed differently skip it, but still increment i,
570 * since widget is visible and will be handled in next loop iteration.
572 if (child->pack != packing)
578 /* Assign the child's size. */
579 if (private->homogeneous)
583 if (n_extra_widgets > 0)
591 child_size = sizes[i].minimum_size + child->padding * 2;
593 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
597 if (n_extra_widgets > 0)
605 /* Assign the child's position. */
606 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
610 child_allocation.width = MAX (1, child_size - child->padding * 2);
611 child_allocation.x = x + child->padding;
615 child_allocation.width = sizes[i].minimum_size;
616 child_allocation.x = x + (child_size - child_allocation.width) / 2;
619 if (packing == GTK_PACK_START)
621 x += child_size + private->spacing;
625 x -= child_size + private->spacing;
627 child_allocation.x -= child_size;
630 if (direction == GTK_TEXT_DIR_RTL)
631 child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
634 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
638 child_allocation.height = MAX (1, child_size - child->padding * 2);
639 child_allocation.y = y + child->padding;
643 child_allocation.height = sizes[i].minimum_size;
644 child_allocation.y = y + (child_size - child_allocation.height) / 2;
647 if (packing == GTK_PACK_START)
649 y += child_size + private->spacing;
653 y -= child_size + private->spacing;
655 child_allocation.y -= child_size;
658 gtk_widget_size_allocate (child->widget, &child_allocation);
666 gtk_box_compute_expand (GtkWidget *widget,
670 GtkBoxPrivate *private = GTK_BOX (widget)->priv;
674 gboolean opposite_expand;
675 GtkOrientation opposite_orientation;
677 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
678 opposite_orientation = GTK_ORIENTATION_VERTICAL;
680 opposite_orientation = GTK_ORIENTATION_HORIZONTAL;
683 opposite_expand = FALSE;
685 for (children = private->children; children; children = children->next)
687 child = children->data;
689 /* we don't recurse into children anymore as soon as we know
690 * expand=TRUE in an orientation
693 if (child->expand || (!our_expand && gtk_widget_compute_expand (child->widget, private->orientation)))
696 if (!opposite_expand && gtk_widget_compute_expand (child->widget, opposite_orientation))
697 opposite_expand = TRUE;
699 if (our_expand && opposite_expand)
703 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
705 *hexpand_p = our_expand;
706 *vexpand_p = opposite_expand;
710 *hexpand_p = opposite_expand;
711 *vexpand_p = our_expand;
716 gtk_box_child_type (GtkContainer *container)
718 return GTK_TYPE_WIDGET;
722 gtk_box_set_child_property (GtkContainer *container,
731 GtkPackType pack_type = 0;
733 if (property_id != CHILD_PROP_POSITION)
734 gtk_box_query_child_packing (GTK_BOX (container),
742 case CHILD_PROP_EXPAND:
743 gtk_box_set_child_packing (GTK_BOX (container),
745 g_value_get_boolean (value),
750 case CHILD_PROP_FILL:
751 gtk_box_set_child_packing (GTK_BOX (container),
754 g_value_get_boolean (value),
758 case CHILD_PROP_PADDING:
759 gtk_box_set_child_packing (GTK_BOX (container),
763 g_value_get_uint (value),
766 case CHILD_PROP_PACK_TYPE:
767 gtk_box_set_child_packing (GTK_BOX (container),
772 g_value_get_enum (value));
774 case CHILD_PROP_POSITION:
775 gtk_box_reorder_child (GTK_BOX (container),
777 g_value_get_int (value));
780 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
786 gtk_box_get_child_property (GtkContainer *container,
792 gboolean expand = FALSE;
793 gboolean fill = FALSE;
795 GtkPackType pack_type = 0;
798 if (property_id != CHILD_PROP_POSITION)
799 gtk_box_query_child_packing (GTK_BOX (container),
808 case CHILD_PROP_EXPAND:
809 g_value_set_boolean (value, expand);
811 case CHILD_PROP_FILL:
812 g_value_set_boolean (value, fill);
814 case CHILD_PROP_PADDING:
815 g_value_set_uint (value, padding);
817 case CHILD_PROP_PACK_TYPE:
818 g_value_set_enum (value, pack_type);
820 case CHILD_PROP_POSITION:
822 for (list = GTK_BOX (container)->priv->children; list; list = list->next)
824 GtkBoxChild *child_entry;
826 child_entry = list->data;
827 if (child_entry->widget == child)
831 g_value_set_int (value, list ? i : -1);
834 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
839 typedef struct _CountingData CountingData;
840 struct _CountingData {
848 count_widget_position (GtkWidget *widget,
851 CountingData *count = data;
853 if (!gtk_widget_get_visible (widget))
856 if (count->widget == widget)
858 else if (count->found)
865 gtk_box_get_visible_position (GtkBox *box,
868 CountingData count = { child, FALSE, 0, 0 };
870 /* foreach iterates in visible order */
871 gtk_container_foreach (GTK_CONTAINER (box),
872 count_widget_position,
875 /* the child wasn't found, it's likely an internal child of some
876 * subclass, return -1 to indicate that there is no sibling relation
877 * to the regular box children
882 if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
883 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
889 static GtkWidgetPath *
890 gtk_box_get_path_for_child (GtkContainer *container,
893 GtkWidgetPath *path, *sibling_path;
895 GtkBoxPrivate *private;
896 GList *list, *children;
898 box = GTK_BOX (container);
901 path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (container)));
903 if (gtk_widget_get_visible (child))
907 sibling_path = gtk_widget_path_new ();
909 /* get_children works in visible order */
910 children = gtk_container_get_children (container);
911 if (private->orientation == GTK_ORIENTATION_HORIZONTAL &&
912 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
913 children = g_list_reverse (children);
915 for (list = children; list; list = list->next)
917 if (!gtk_widget_get_visible (list->data))
920 gtk_widget_path_append_for_widget (sibling_path, list->data);
923 g_list_free (children);
925 position = gtk_box_get_visible_position (box, child);
928 gtk_widget_path_append_with_siblings (path, sibling_path, position);
930 gtk_widget_path_append_for_widget (path, child);
932 gtk_widget_path_unref (sibling_path);
935 gtk_widget_path_append_for_widget (path, child);
941 gtk_box_invalidate_order (GtkBox *box)
943 gtk_container_foreach (GTK_CONTAINER (box),
944 (GtkCallback) gtk_widget_reset_style,
949 gtk_box_direction_changed (GtkWidget *widget,
950 GtkTextDirection previous_direction)
952 gtk_box_invalidate_order (GTK_BOX (widget));
956 box_child_visibility_notify_cb (GObject *obj,
960 GtkBox *box = user_data;
962 gtk_box_invalidate_order (box);
966 gtk_box_pack (GtkBox *box,
971 GtkPackType pack_type)
973 GtkBoxPrivate *private = box->priv;
974 GtkBoxChild *child_info;
976 g_return_if_fail (GTK_IS_BOX (box));
977 g_return_if_fail (GTK_IS_WIDGET (child));
978 g_return_if_fail (gtk_widget_get_parent (child) == NULL);
980 child_info = g_new (GtkBoxChild, 1);
981 child_info->widget = child;
982 child_info->padding = padding;
983 child_info->expand = expand ? TRUE : FALSE;
984 child_info->fill = fill ? TRUE : FALSE;
985 child_info->pack = pack_type;
987 private->children = g_list_append (private->children, child_info);
989 gtk_widget_freeze_child_notify (child);
991 gtk_box_invalidate_order (box);
992 gtk_widget_set_parent (child, GTK_WIDGET (box));
994 g_signal_connect (child, "notify::visible",
995 G_CALLBACK (box_child_visibility_notify_cb), box);
997 gtk_widget_child_notify (child, "expand");
998 gtk_widget_child_notify (child, "fill");
999 gtk_widget_child_notify (child, "padding");
1000 gtk_widget_child_notify (child, "pack-type");
1001 gtk_widget_child_notify (child, "position");
1002 gtk_widget_thaw_child_notify (child);
1006 gtk_box_get_size (GtkWidget *widget,
1007 GtkOrientation orientation,
1012 GtkBoxPrivate *private;
1015 gint minimum, natural;
1017 box = GTK_BOX (widget);
1018 private = box->priv;
1020 minimum = natural = 0;
1024 for (children = private->children; children; children = children->next)
1026 GtkBoxChild *child = children->data;
1028 if (gtk_widget_get_visible (child->widget))
1030 gint child_minimum, child_natural;
1032 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1033 gtk_widget_get_preferred_width (child->widget,
1034 &child_minimum, &child_natural);
1036 gtk_widget_get_preferred_height (child->widget,
1037 &child_minimum, &child_natural);
1039 if (private->orientation == orientation)
1041 if (private->homogeneous)
1045 largest = child_minimum + child->padding * 2;
1046 minimum = MAX (minimum, largest);
1048 largest = child_natural + child->padding * 2;
1049 natural = MAX (natural, largest);
1053 minimum += child_minimum + child->padding * 2;
1054 natural += child_natural + child->padding * 2;
1059 /* The biggest mins and naturals in the opposing orientation */
1060 minimum = MAX (minimum, child_minimum);
1061 natural = MAX (natural, child_natural);
1068 if (nvis_children > 0 && private->orientation == orientation)
1070 if (private->homogeneous)
1072 minimum *= nvis_children;
1073 natural *= nvis_children;
1075 minimum += (nvis_children - 1) * private->spacing;
1076 natural += (nvis_children - 1) * private->spacing;
1080 *minimum_size = minimum;
1083 *natural_size = natural;
1087 gtk_box_get_preferred_width (GtkWidget *widget,
1091 gtk_box_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
1095 gtk_box_get_preferred_height (GtkWidget *widget,
1099 gtk_box_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
1103 gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
1108 GtkBoxPrivate *private = box->priv;
1112 gint nexpand_children;
1113 gint computed_minimum = 0, computed_natural = 0;
1114 GtkRequestedSize *sizes;
1115 GtkPackType packing;
1116 gint size, extra, i;
1117 gint child_size, child_minimum, child_natural;
1118 gint n_extra_widgets = 0;
1120 count_expand_children (box, &nvis_children, &nexpand_children);
1122 if (nvis_children <= 0)
1125 sizes = g_newa (GtkRequestedSize, nvis_children);
1126 size = avail_size - (nvis_children - 1) * private->spacing;
1128 /* Retrieve desired size for visible children */
1129 for (i = 0, children = private->children; children; children = children->next)
1131 child = children->data;
1133 if (gtk_widget_get_visible (child->widget))
1135 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1136 gtk_widget_get_preferred_width (child->widget,
1137 &sizes[i].minimum_size,
1138 &sizes[i].natural_size);
1140 gtk_widget_get_preferred_height (child->widget,
1141 &sizes[i].minimum_size,
1142 &sizes[i].natural_size);
1144 /* Assert the api is working properly */
1145 if (sizes[i].minimum_size < 0)
1146 g_error ("GtkBox child %s minimum %s: %d < 0",
1147 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1148 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1149 sizes[i].minimum_size);
1151 if (sizes[i].natural_size < sizes[i].minimum_size)
1152 g_error ("GtkBox child %s natural %s: %d < minimum %d",
1153 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1154 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1155 sizes[i].natural_size,
1156 sizes[i].minimum_size);
1158 size -= sizes[i].minimum_size;
1159 size -= child->padding * 2;
1161 sizes[i].data = child;
1167 if (private->homogeneous)
1169 /* If were homogenous we still need to run the above loop to get the
1170 * minimum sizes for children that are not going to fill
1172 size = avail_size - (nvis_children - 1) * private->spacing;
1173 extra = size / nvis_children;
1174 n_extra_widgets = size % nvis_children;
1178 /* Bring children up to size first */
1179 size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
1181 /* Calculate space which hasn't distributed yet,
1182 * and is available for expanding children.
1184 if (nexpand_children > 0)
1186 extra = size / nexpand_children;
1187 n_extra_widgets = size % nexpand_children;
1193 /* Allocate child positions. */
1194 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1196 for (i = 0, children = private->children;
1198 children = children->next)
1200 child = children->data;
1202 /* If widget is not visible, skip it. */
1203 if (!gtk_widget_get_visible (child->widget))
1206 /* If widget is packed differently skip it, but still increment i,
1207 * since widget is visible and will be handled in next loop iteration.
1209 if (child->pack != packing)
1215 if (child->pack == packing)
1217 /* Assign the child's size. */
1218 if (private->homogeneous)
1222 if (n_extra_widgets > 0)
1230 child_size = sizes[i].minimum_size + child->padding * 2;
1232 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
1234 child_size += extra;
1236 if (n_extra_widgets > 0)
1246 child_size = MAX (1, child_size - child->padding * 2);
1250 child_size = sizes[i].minimum_size;
1254 /* Assign the child's position. */
1255 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1256 gtk_widget_get_preferred_height_for_width (child->widget,
1257 child_size, &child_minimum, &child_natural);
1258 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1259 gtk_widget_get_preferred_width_for_height (child->widget,
1260 child_size, &child_minimum, &child_natural);
1263 computed_minimum = MAX (computed_minimum, child_minimum);
1264 computed_natural = MAX (computed_natural, child_natural);
1271 *minimum_size = computed_minimum;
1273 *natural_size = computed_natural;
1277 gtk_box_compute_size_for_orientation (GtkBox *box,
1282 GtkBoxPrivate *private = box->priv;
1284 gint nvis_children = 0;
1285 gint required_size = 0, required_natural = 0, child_size, child_natural;
1286 gint largest_child = 0, largest_natural = 0;
1288 for (children = private->children; children != NULL;
1289 children = children->next)
1291 GtkBoxChild *child = children->data;
1293 if (gtk_widget_get_visible (child->widget))
1296 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1297 gtk_widget_get_preferred_width_for_height (child->widget,
1298 avail_size, &child_size, &child_natural);
1300 gtk_widget_get_preferred_height_for_width (child->widget,
1301 avail_size, &child_size, &child_natural);
1304 child_size += child->padding * 2;
1305 child_natural += child->padding * 2;
1307 if (child_size > largest_child)
1308 largest_child = child_size;
1310 if (child_natural > largest_natural)
1311 largest_natural = child_natural;
1313 required_size += child_size;
1314 required_natural += child_natural;
1320 if (nvis_children > 0)
1322 if (private->homogeneous)
1324 required_size = largest_child * nvis_children;
1325 required_natural = largest_natural * nvis_children;
1328 required_size += (nvis_children - 1) * private->spacing;
1329 required_natural += (nvis_children - 1) * private->spacing;
1333 *minimum_size = required_size;
1336 *natural_size = required_natural;
1340 gtk_box_get_preferred_width_for_height (GtkWidget *widget,
1342 gint *minimum_width,
1343 gint *natural_width)
1345 GtkBox *box = GTK_BOX (widget);
1346 GtkBoxPrivate *private = box->priv;
1348 if (private->orientation == GTK_ORIENTATION_VERTICAL)
1349 gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width);
1351 gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width);
1355 gtk_box_get_preferred_height_for_width (GtkWidget *widget,
1357 gint *minimum_height,
1358 gint *natural_height)
1360 GtkBox *box = GTK_BOX (widget);
1361 GtkBoxPrivate *private = box->priv;
1363 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1364 gtk_box_compute_size_for_opposing_orientation (box, width, minimum_height, natural_height);
1366 gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height);
1371 * @orientation: the box's orientation.
1372 * @spacing: the number of pixels to place by default between children.
1374 * Creates a new #GtkBox.
1376 * Return value: a new #GtkBox.
1381 gtk_box_new (GtkOrientation orientation,
1384 return g_object_new (GTK_TYPE_BOX,
1385 "orientation", orientation,
1391 * gtk_box_pack_start:
1393 * @child: the #GtkWidget to be added to @box
1394 * @expand: %TRUE if the new child is to be given extra space allocated
1395 * to @box. The extra space will be divided evenly between all children
1396 * that use this option
1397 * @fill: %TRUE if space given to @child by the @expand option is
1398 * actually allocated to @child, rather than just padding it. This
1399 * parameter has no effect if @expand is set to %FALSE. A child is
1400 * always allocated the full height of a horizontal #GtkBox and the full width
1401 * of a vertical #GtkBox. This option affects the other dimension
1402 * @padding: extra space in pixels to put between this child and its
1403 * neighbors, over and above the global amount specified by
1404 * #GtkBox:spacing property. If @child is a widget at one of the
1405 * reference ends of @box, then @padding pixels are also put between
1406 * @child and the reference edge of @box
1408 * Adds @child to @box, packed with reference to the start of @box.
1409 * The @child is packed after any other child packed with reference
1410 * to the start of @box.
1413 gtk_box_pack_start (GtkBox *box,
1419 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
1425 * @child: the #GtkWidget to be added to @box
1426 * @expand: %TRUE if the new child is to be given extra space allocated
1427 * to @box. The extra space will be divided evenly between all children
1428 * of @box that use this option
1429 * @fill: %TRUE if space given to @child by the @expand option is
1430 * actually allocated to @child, rather than just padding it. This
1431 * parameter has no effect if @expand is set to %FALSE. A child is
1432 * always allocated the full height of a horizontal #GtkBox and the full width
1433 * of a vertical #GtkBox. This option affects the other dimension
1434 * @padding: extra space in pixels to put between this child and its
1435 * neighbors, over and above the global amount specified by
1436 * #GtkBox:spacing property. If @child is a widget at one of the
1437 * reference ends of @box, then @padding pixels are also put between
1438 * @child and the reference edge of @box
1440 * Adds @child to @box, packed with reference to the end of @box.
1441 * The @child is packed after (away from end of) any other child
1442 * packed with reference to the end of @box.
1445 gtk_box_pack_end (GtkBox *box,
1451 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
1455 * gtk_box_set_homogeneous:
1457 * @homogeneous: a boolean value, %TRUE to create equal allotments,
1458 * %FALSE for variable allotments
1460 * Sets the #GtkBox:homogeneous property of @box, controlling
1461 * whether or not all children of @box are given equal space
1465 gtk_box_set_homogeneous (GtkBox *box,
1466 gboolean homogeneous)
1468 GtkBoxPrivate *private;
1470 g_return_if_fail (GTK_IS_BOX (box));
1472 private = box->priv;
1474 if ((homogeneous ? TRUE : FALSE) != private->homogeneous)
1476 private->homogeneous = homogeneous ? TRUE : FALSE;
1477 g_object_notify (G_OBJECT (box), "homogeneous");
1478 gtk_widget_queue_resize (GTK_WIDGET (box));
1483 * gtk_box_get_homogeneous:
1486 * Returns whether the box is homogeneous (all children are the
1487 * same size). See gtk_box_set_homogeneous().
1489 * Return value: %TRUE if the box is homogeneous.
1492 gtk_box_get_homogeneous (GtkBox *box)
1494 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1496 return box->priv->homogeneous;
1500 * gtk_box_set_spacing:
1502 * @spacing: the number of pixels to put between children
1504 * Sets the #GtkBox:spacing property of @box, which is the
1505 * number of pixels to place between children of @box.
1508 gtk_box_set_spacing (GtkBox *box,
1511 GtkBoxPrivate *private;
1513 g_return_if_fail (GTK_IS_BOX (box));
1515 private = box->priv;
1517 if (spacing != private->spacing)
1519 private->spacing = spacing;
1520 _gtk_box_set_spacing_set (box, TRUE);
1522 g_object_notify (G_OBJECT (box), "spacing");
1524 gtk_widget_queue_resize (GTK_WIDGET (box));
1529 * gtk_box_get_spacing:
1532 * Gets the value set by gtk_box_set_spacing().
1534 * Return value: spacing between children
1537 gtk_box_get_spacing (GtkBox *box)
1539 g_return_val_if_fail (GTK_IS_BOX (box), 0);
1541 return box->priv->spacing;
1545 _gtk_box_set_spacing_set (GtkBox *box,
1546 gboolean spacing_set)
1548 GtkBoxPrivate *private;
1550 g_return_if_fail (GTK_IS_BOX (box));
1552 private = box->priv;
1554 private->spacing_set = spacing_set ? TRUE : FALSE;
1558 _gtk_box_get_spacing_set (GtkBox *box)
1560 GtkBoxPrivate *private;
1562 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1564 private = box->priv;
1566 return private->spacing_set;
1570 * gtk_box_reorder_child:
1572 * @child: the #GtkWidget to move
1573 * @position: the new position for @child in the list of children
1574 * of @box, starting from 0. If negative, indicates the end of
1577 * Moves @child to a new @position in the list of @box children.
1578 * The list is the <structfield>children</structfield> field of
1579 * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START
1580 * as well as widgets packed #GTK_PACK_END, in the order that these
1581 * widgets were added to @box.
1583 * A widget's position in the @box children list determines where
1584 * the widget is packed into @box. A child widget at some position
1585 * in the list will be packed just after all other widgets of the
1586 * same packing type that appear earlier in the list.
1589 gtk_box_reorder_child (GtkBox *box,
1593 GtkBoxPrivate *priv;
1596 GtkBoxChild *child_info = NULL;
1599 g_return_if_fail (GTK_IS_BOX (box));
1600 g_return_if_fail (GTK_IS_WIDGET (child));
1604 old_link = priv->children;
1608 child_info = old_link->data;
1609 if (child_info->widget == child)
1612 old_link = old_link->next;
1616 g_return_if_fail (old_link != NULL);
1618 if (position == old_position)
1621 priv->children = g_list_delete_link (priv->children, old_link);
1626 new_link = g_list_nth (priv->children, position);
1628 priv->children = g_list_insert_before (priv->children, new_link, child_info);
1630 gtk_widget_child_notify (child, "position");
1631 if (gtk_widget_get_visible (child)
1632 && gtk_widget_get_visible (GTK_WIDGET (box)))
1634 gtk_box_invalidate_order (box);
1635 gtk_widget_queue_resize (child);
1640 * gtk_box_query_child_packing:
1642 * @child: the #GtkWidget of the child to query
1643 * @expand: (out): pointer to return location for #GtkBox:expand child
1645 * @fill: (out): pointer to return location for #GtkBox:fill child
1647 * @padding: (out): pointer to return location for #GtkBox:padding
1649 * @pack_type: (out): pointer to return location for #GtkBox:pack-type
1652 * Obtains information about how @child is packed into @box.
1655 gtk_box_query_child_packing (GtkBox *box,
1660 GtkPackType *pack_type)
1662 GtkBoxPrivate *private;
1664 GtkBoxChild *child_info = NULL;
1666 g_return_if_fail (GTK_IS_BOX (box));
1667 g_return_if_fail (GTK_IS_WIDGET (child));
1669 private = box->priv;
1671 list = private->children;
1674 child_info = list->data;
1675 if (child_info->widget == child)
1684 *expand = child_info->expand;
1686 *fill = child_info->fill;
1688 *padding = child_info->padding;
1690 *pack_type = child_info->pack;
1695 * gtk_box_set_child_packing:
1697 * @child: the #GtkWidget of the child to set
1698 * @expand: the new value of the #GtkBox:expand child property
1699 * @fill: the new value of the #GtkBox:fill child property
1700 * @padding: the new value of the #GtkBox:padding child property
1701 * @pack_type: the new value of the #GtkBox:pack-type child property
1703 * Sets the way @child is packed into @box.
1706 gtk_box_set_child_packing (GtkBox *box,
1711 GtkPackType pack_type)
1713 GtkBoxPrivate *private;
1715 GtkBoxChild *child_info = NULL;
1717 g_return_if_fail (GTK_IS_BOX (box));
1718 g_return_if_fail (GTK_IS_WIDGET (child));
1720 private = box->priv;
1722 list = private->children;
1725 child_info = list->data;
1726 if (child_info->widget == child)
1732 gtk_widget_freeze_child_notify (child);
1737 expanded = expand != FALSE;
1739 /* avoid setting expand if unchanged, since queue_compute_expand
1740 * can be expensive-ish
1742 if (child_info->expand != expanded)
1744 child_info->expand = expand != FALSE;
1745 gtk_widget_queue_compute_expand (GTK_WIDGET (box));
1746 gtk_widget_child_notify (child, "expand");
1749 child_info->fill = fill != FALSE;
1750 gtk_widget_child_notify (child, "fill");
1751 child_info->padding = padding;
1752 gtk_widget_child_notify (child, "padding");
1753 if (pack_type != GTK_PACK_END)
1754 pack_type = GTK_PACK_START;
1755 if (child_info->pack != pack_type)
1757 child_info->pack = GTK_PACK_END;
1758 gtk_widget_child_notify (child, "pack-type");
1759 gtk_box_invalidate_order (box);
1762 if (gtk_widget_get_visible (child)
1763 && gtk_widget_get_visible (GTK_WIDGET (box)))
1764 gtk_widget_queue_resize (child);
1766 gtk_widget_thaw_child_notify (child);
1770 _gtk_box_set_old_defaults (GtkBox *box)
1772 GtkBoxPrivate *private;
1774 g_return_if_fail (GTK_IS_BOX (box));
1776 private = box->priv;
1778 private->default_expand = TRUE;
1782 gtk_box_add (GtkContainer *container,
1785 GtkBoxPrivate *priv = GTK_BOX (container)->priv;
1787 gtk_box_pack_start (GTK_BOX (container), widget,
1788 priv->default_expand,
1794 gtk_box_remove (GtkContainer *container,
1797 GtkBox *box = GTK_BOX (container);
1798 GtkBoxPrivate *priv = box->priv;
1802 children = priv->children;
1805 child = children->data;
1807 if (child->widget == widget)
1809 gboolean was_visible;
1811 g_signal_handlers_disconnect_by_func (widget,
1812 box_child_visibility_notify_cb,
1815 was_visible = gtk_widget_get_visible (widget);
1816 gtk_widget_unparent (widget);
1818 priv->children = g_list_remove_link (priv->children, children);
1819 g_list_free (children);
1822 /* queue resize regardless of gtk_widget_get_visible (container),
1823 * since that's what is needed by toplevels.
1827 gtk_box_invalidate_order (box);
1828 gtk_widget_queue_resize (GTK_WIDGET (container));
1834 children = children->next;
1839 gtk_box_forall (GtkContainer *container,
1840 gboolean include_internals,
1841 GtkCallback callback,
1842 gpointer callback_data)
1844 GtkBox *box = GTK_BOX (container);
1845 GtkBoxPrivate *priv = box->priv;
1849 children = priv->children;
1852 child = children->data;
1853 children = children->next;
1855 if (child->pack == GTK_PACK_START)
1856 (* callback) (child->widget, callback_data);
1859 children = g_list_last (priv->children);
1862 child = children->data;
1863 children = children->prev;
1865 if (child->pack == GTK_PACK_END)
1866 (* callback) (child->widget, callback_data);
1871 _gtk_box_get_children (GtkBox *box)
1873 GtkBoxPrivate *priv;
1876 GList *retval = NULL;
1878 g_return_val_if_fail (GTK_IS_BOX (box), NULL);
1882 children = priv->children;
1885 child = children->data;
1886 children = children->next;
1888 retval = g_list_prepend (retval, child->widget);
1891 return g_list_reverse (retval);