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, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
29 * @Short_description: A container box
31 * @See_also: #GtkFrame, #GtkGrid, #GtkLayout
33 * The GtkBox widget organizes child widgets into a rectangular area.
35 * The rectangular area of a GtkBox is organized into either a single row
36 * or a single column of child widgets depending upon the orientation.
37 * Thus, all children of a GtkBox are allocated one dimension in common,
38 * which is the height of a row, or the width of a column.
40 * GtkBox uses a notion of <emphasis>packing</emphasis>. Packing refers
41 * to adding widgets with reference to a particular position in a
42 * #GtkContainer. For a GtkBox, there are two reference positions: the
43 * <emphasis>start</emphasis> and the <emphasis>end</emphasis> of the box.
44 * For a vertical #GtkBox, the start is defined as the top of the box and
45 * the end is defined as the bottom. For a horizontal #GtkBox the start
46 * is defined as the left side and the end is defined as the right side.
48 * Use repeated calls to gtk_box_pack_start() to pack widgets into a
49 * GtkBox from start to end. Use gtk_box_pack_end() to add widgets from
50 * end to start. You may intersperse these calls and add widgets from
51 * both ends of the same GtkBox.
53 * Because GtkBox is a #GtkContainer, you may also use gtk_container_add()
54 * to insert widgets into the box, and they will be packed with the default
55 * values for #GtkBox:expand and #GtkBox:fill. Use gtk_container_remove()
56 * to remove widgets from the GtkBox.
58 * Use gtk_box_set_homogeneous() to specify whether or not all children
59 * of the GtkBox are forced to get the same amount of space.
61 * Use gtk_box_set_spacing() to determine how much space will be
62 * minimally placed between all children in the GtkBox. Note that
63 * spacing is added <emphasis>between</emphasis> the children, while
64 * padding added by gtk_box_pack_start() or gtk_box_pack_end() is added
65 * <emphasis>on either side</emphasis> of the widget it belongs to.
67 * Use gtk_box_reorder_child() to move a GtkBox child to a different
70 * Use gtk_box_set_child_packing() to reset the #GtkBox:expand,
71 * #GtkBox:fill and #GtkBox:padding child properties.
72 * Use gtk_box_query_child_packing() to query these fields.
75 * Note that a single-row or single-column #GtkGrid provides exactly
76 * the same functionality as #GtkBox.
83 #include "gtkboxprivate.h"
84 #include "gtkorientable.h"
85 #include "gtksizerequest.h"
86 #include "gtktypebuiltins.h"
87 #include "gtkprivate.h"
89 #include "a11y/gtkboxaccessible.h"
104 CHILD_PROP_PACK_TYPE,
108 struct _GtkBoxPrivate
112 GtkOrientation orientation;
115 guint default_expand : 1;
116 guint homogeneous : 1;
117 guint spacing_set : 1;
120 typedef struct _GtkBoxChild GtkBoxChild;
124 * @widget: the child widget, packed into the GtkBox.
125 * @padding: the number of extra pixels to put between this child and its
126 * neighbors, set when packed, zero by default.
127 * @expand: flag indicates whether extra space should be given to this child.
128 * Any extra space given to the parent GtkBox is divided up among all children
129 * with this attribute set to %TRUE; set when packed, %TRUE by default.
130 * @fill: flag indicates whether any extra space given to this child due to its
131 * @expand attribute being set is actually allocated to the child, rather than
132 * being used as padding around the widget; set when packed, %TRUE by default.
133 * @pack: one of #GtkPackType indicating whether the child is packed with
134 * reference to the start (top/left) or end (bottom/right) of the GtkBox.
147 static void gtk_box_size_allocate (GtkWidget *widget,
148 GtkAllocation *allocation);
150 static void gtk_box_compute_expand (GtkWidget *widget,
153 static void gtk_box_direction_changed (GtkWidget *widget,
154 GtkTextDirection previous_direction);
156 static void gtk_box_set_property (GObject *object,
160 static void gtk_box_get_property (GObject *object,
164 static void gtk_box_add (GtkContainer *container,
166 static void gtk_box_remove (GtkContainer *container,
168 static void gtk_box_forall (GtkContainer *container,
169 gboolean include_internals,
170 GtkCallback callback,
171 gpointer callback_data);
172 static void gtk_box_set_child_property (GtkContainer *container,
177 static void gtk_box_get_child_property (GtkContainer *container,
182 static GType gtk_box_child_type (GtkContainer *container);
183 static GtkWidgetPath * gtk_box_get_path_for_child
184 (GtkContainer *container,
188 static void gtk_box_get_preferred_width (GtkWidget *widget,
191 static void gtk_box_get_preferred_height (GtkWidget *widget,
194 static void gtk_box_get_preferred_width_for_height (GtkWidget *widget,
197 gint *natural_width);
198 static void gtk_box_get_preferred_height_for_width (GtkWidget *widget,
200 gint *minimum_height,
201 gint *natural_height);
204 G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
205 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
209 gtk_box_class_init (GtkBoxClass *class)
211 GObjectClass *object_class = G_OBJECT_CLASS (class);
212 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
213 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
215 object_class->set_property = gtk_box_set_property;
216 object_class->get_property = gtk_box_get_property;
218 widget_class->size_allocate = gtk_box_size_allocate;
219 widget_class->get_preferred_width = gtk_box_get_preferred_width;
220 widget_class->get_preferred_height = gtk_box_get_preferred_height;
221 widget_class->get_preferred_height_for_width = gtk_box_get_preferred_height_for_width;
222 widget_class->get_preferred_width_for_height = gtk_box_get_preferred_width_for_height;
223 widget_class->compute_expand = gtk_box_compute_expand;
224 widget_class->direction_changed = gtk_box_direction_changed;
226 container_class->add = gtk_box_add;
227 container_class->remove = gtk_box_remove;
228 container_class->forall = gtk_box_forall;
229 container_class->child_type = gtk_box_child_type;
230 container_class->set_child_property = gtk_box_set_child_property;
231 container_class->get_child_property = gtk_box_get_child_property;
232 container_class->get_path_for_child = gtk_box_get_path_for_child;
233 gtk_container_class_handle_border_width (container_class);
235 g_object_class_override_property (object_class,
239 g_object_class_install_property (object_class,
241 g_param_spec_int ("spacing",
243 P_("The amount of space between children"),
247 GTK_PARAM_READWRITE));
249 g_object_class_install_property (object_class,
251 g_param_spec_boolean ("homogeneous",
253 P_("Whether the children should all be the same size"),
255 GTK_PARAM_READWRITE));
260 * Whether the child should receive extra space when the parent grows.
262 * Note that the default value for this property is %FALSE for GtkBox,
263 * but #GtkHBox, #GtkVBox and other subclasses use the old default
266 * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
267 * and #GtkWidget:vexpand properties are the preferred way to influence
268 * child size allocation in containers.
270 gtk_container_class_install_child_property (container_class,
272 g_param_spec_boolean ("expand",
274 P_("Whether the child should receive extra space when the parent grows"),
276 GTK_PARAM_READWRITE));
281 * Whether the child should receive extra space when the parent grows.
283 * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
284 * and #GtkWidget:vexpand properties are the preferred way to influence
285 * child size allocation in containers.
287 gtk_container_class_install_child_property (container_class,
289 g_param_spec_boolean ("fill",
291 P_("Whether extra space given to the child should be allocated to the child or used as padding"),
293 GTK_PARAM_READWRITE));
295 gtk_container_class_install_child_property (container_class,
297 g_param_spec_uint ("padding",
299 P_("Extra space to put between the child and its neighbors, in pixels"),
301 GTK_PARAM_READWRITE));
302 gtk_container_class_install_child_property (container_class,
303 CHILD_PROP_PACK_TYPE,
304 g_param_spec_enum ("pack-type",
306 P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
307 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
308 GTK_PARAM_READWRITE));
309 gtk_container_class_install_child_property (container_class,
311 g_param_spec_int ("position",
313 P_("The index of the child in the parent"),
315 GTK_PARAM_READWRITE));
317 g_type_class_add_private (object_class, sizeof (GtkBoxPrivate));
319 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_BOX_ACCESSIBLE);
323 gtk_box_init (GtkBox *box)
325 GtkBoxPrivate *private;
327 box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
332 gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
333 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE);
335 private->orientation = GTK_ORIENTATION_HORIZONTAL;
336 private->children = NULL;
338 private->default_expand = FALSE;
339 private->homogeneous = FALSE;
340 private->spacing = 0;
341 private->spacing_set = FALSE;
345 gtk_box_set_property (GObject *object,
350 GtkBox *box = GTK_BOX (object);
351 GtkBoxPrivate *private = box->priv;
355 case PROP_ORIENTATION:
356 private->orientation = g_value_get_enum (value);
357 gtk_widget_queue_resize (GTK_WIDGET (box));
360 gtk_box_set_spacing (box, g_value_get_int (value));
362 case PROP_HOMOGENEOUS:
363 gtk_box_set_homogeneous (box, g_value_get_boolean (value));
366 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372 gtk_box_get_property (GObject *object,
377 GtkBox *box = GTK_BOX (object);
378 GtkBoxPrivate *private = box->priv;
382 case PROP_ORIENTATION:
383 g_value_set_enum (value, private->orientation);
386 g_value_set_int (value, private->spacing);
388 case PROP_HOMOGENEOUS:
389 g_value_set_boolean (value, private->homogeneous);
392 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
399 count_expand_children (GtkBox *box,
400 gint *visible_children,
401 gint *expand_children)
403 GtkBoxPrivate *private = box->priv;
407 *visible_children = *expand_children = 0;
409 for (children = private->children; children; children = children->next)
411 child = children->data;
413 if (gtk_widget_get_visible (child->widget))
415 *visible_children += 1;
416 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
417 *expand_children += 1;
423 gtk_box_size_allocate (GtkWidget *widget,
424 GtkAllocation *allocation)
426 GtkBox *box = GTK_BOX (widget);
427 GtkBoxPrivate *private = box->priv;
431 gint nexpand_children;
433 GtkTextDirection direction;
434 GtkAllocation child_allocation;
435 GtkRequestedSize *sizes;
441 gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
442 gint x = 0, y = 0, i;
446 gtk_widget_set_allocation (widget, allocation);
448 count_expand_children (box, &nvis_children, &nexpand_children);
450 /* If there is no visible child, simply return. */
451 if (nvis_children <= 0)
454 direction = gtk_widget_get_direction (widget);
455 sizes = g_newa (GtkRequestedSize, nvis_children);
457 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
458 size = allocation->width - (nvis_children - 1) * private->spacing;
460 size = allocation->height - (nvis_children - 1) * private->spacing;
462 /* Retrieve desired size for visible children. */
463 for (i = 0, children = private->children; children; children = children->next)
465 child = children->data;
467 if (!gtk_widget_get_visible (child->widget))
470 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
471 gtk_widget_get_preferred_width_for_height (child->widget,
473 &sizes[i].minimum_size,
474 &sizes[i].natural_size);
476 gtk_widget_get_preferred_height_for_width (child->widget,
478 &sizes[i].minimum_size,
479 &sizes[i].natural_size);
482 /* Assert the api is working properly */
483 if (sizes[i].minimum_size < 0)
484 g_error ("GtkBox child %s minimum %s: %d < 0 for %s %d",
485 gtk_widget_get_name (GTK_WIDGET (child->widget)),
486 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
487 sizes[i].minimum_size,
488 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
489 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
491 if (sizes[i].natural_size < sizes[i].minimum_size)
492 g_error ("GtkBox child %s natural %s: %d < minimum %d for %s %d",
493 gtk_widget_get_name (GTK_WIDGET (child->widget)),
494 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
495 sizes[i].natural_size,
496 sizes[i].minimum_size,
497 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
498 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
500 size -= sizes[i].minimum_size;
501 size -= child->padding * 2;
503 sizes[i].data = child;
508 if (private->homogeneous)
510 /* If were homogenous we still need to run the above loop to get the
511 * minimum sizes for children that are not going to fill
513 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
514 size = allocation->width - (nvis_children - 1) * private->spacing;
516 size = allocation->height - (nvis_children - 1) * private->spacing;
518 extra = size / nvis_children;
519 n_extra_widgets = size % nvis_children;
523 /* Bring children up to size first */
524 size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
526 /* Calculate space which hasn't distributed yet,
527 * and is available for expanding children.
529 if (nexpand_children > 0)
531 extra = size / nexpand_children;
532 n_extra_widgets = size % nexpand_children;
538 /* Allocate child positions. */
539 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
541 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
543 child_allocation.y = allocation->y;
544 child_allocation.height = MAX (1, allocation->height);
545 if (packing == GTK_PACK_START)
548 x = allocation->x + allocation->width;
552 child_allocation.x = allocation->x;
553 child_allocation.width = MAX (1, allocation->width);
554 if (packing == GTK_PACK_START)
557 y = allocation->y + allocation->height;
560 for (i = 0, children = private->children;
562 children = children->next)
564 child = children->data;
566 /* If widget is not visible, skip it. */
567 if (!gtk_widget_get_visible (child->widget))
570 /* If widget is packed differently skip it, but still increment i,
571 * since widget is visible and will be handled in next loop iteration.
573 if (child->pack != packing)
579 /* Assign the child's size. */
580 if (private->homogeneous)
584 if (n_extra_widgets > 0)
592 child_size = sizes[i].minimum_size + child->padding * 2;
594 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
598 if (n_extra_widgets > 0)
606 /* Assign the child's position. */
607 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
611 child_allocation.width = MAX (1, child_size - child->padding * 2);
612 child_allocation.x = x + child->padding;
616 child_allocation.width = sizes[i].minimum_size;
617 child_allocation.x = x + (child_size - child_allocation.width) / 2;
620 if (packing == GTK_PACK_START)
622 x += child_size + private->spacing;
626 x -= child_size + private->spacing;
628 child_allocation.x -= child_size;
631 if (direction == GTK_TEXT_DIR_RTL)
632 child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
635 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
639 child_allocation.height = MAX (1, child_size - child->padding * 2);
640 child_allocation.y = y + child->padding;
644 child_allocation.height = sizes[i].minimum_size;
645 child_allocation.y = y + (child_size - child_allocation.height) / 2;
648 if (packing == GTK_PACK_START)
650 y += child_size + private->spacing;
654 y -= child_size + private->spacing;
656 child_allocation.y -= child_size;
659 gtk_widget_size_allocate (child->widget, &child_allocation);
667 gtk_box_compute_expand (GtkWidget *widget,
671 GtkBoxPrivate *private = GTK_BOX (widget)->priv;
675 gboolean opposite_expand;
676 GtkOrientation opposite_orientation;
678 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
679 opposite_orientation = GTK_ORIENTATION_VERTICAL;
681 opposite_orientation = GTK_ORIENTATION_HORIZONTAL;
684 opposite_expand = FALSE;
686 for (children = private->children; children; children = children->next)
688 child = children->data;
690 /* we don't recurse into children anymore as soon as we know
691 * expand=TRUE in an orientation
694 if (child->expand || (!our_expand && gtk_widget_compute_expand (child->widget, private->orientation)))
697 if (!opposite_expand && gtk_widget_compute_expand (child->widget, opposite_orientation))
698 opposite_expand = TRUE;
700 if (our_expand && opposite_expand)
704 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
706 *hexpand_p = our_expand;
707 *vexpand_p = opposite_expand;
711 *hexpand_p = opposite_expand;
712 *vexpand_p = our_expand;
717 gtk_box_child_type (GtkContainer *container)
719 return GTK_TYPE_WIDGET;
723 gtk_box_set_child_property (GtkContainer *container,
732 GtkPackType pack_type = 0;
734 if (property_id != CHILD_PROP_POSITION)
735 gtk_box_query_child_packing (GTK_BOX (container),
743 case CHILD_PROP_EXPAND:
744 gtk_box_set_child_packing (GTK_BOX (container),
746 g_value_get_boolean (value),
751 case CHILD_PROP_FILL:
752 gtk_box_set_child_packing (GTK_BOX (container),
755 g_value_get_boolean (value),
759 case CHILD_PROP_PADDING:
760 gtk_box_set_child_packing (GTK_BOX (container),
764 g_value_get_uint (value),
767 case CHILD_PROP_PACK_TYPE:
768 gtk_box_set_child_packing (GTK_BOX (container),
773 g_value_get_enum (value));
775 case CHILD_PROP_POSITION:
776 gtk_box_reorder_child (GTK_BOX (container),
778 g_value_get_int (value));
781 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
787 gtk_box_get_child_property (GtkContainer *container,
793 gboolean expand = FALSE;
794 gboolean fill = FALSE;
796 GtkPackType pack_type = 0;
799 if (property_id != CHILD_PROP_POSITION)
800 gtk_box_query_child_packing (GTK_BOX (container),
809 case CHILD_PROP_EXPAND:
810 g_value_set_boolean (value, expand);
812 case CHILD_PROP_FILL:
813 g_value_set_boolean (value, fill);
815 case CHILD_PROP_PADDING:
816 g_value_set_uint (value, padding);
818 case CHILD_PROP_PACK_TYPE:
819 g_value_set_enum (value, pack_type);
821 case CHILD_PROP_POSITION:
823 for (list = GTK_BOX (container)->priv->children; list; list = list->next)
825 GtkBoxChild *child_entry;
827 child_entry = list->data;
828 if (child_entry->widget == child)
832 g_value_set_int (value, list ? i : -1);
835 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
840 typedef struct _CountingData CountingData;
841 struct _CountingData {
849 count_widget_position (GtkWidget *widget,
852 CountingData *count = data;
854 if (!gtk_widget_get_visible (widget))
857 if (count->widget == widget)
859 else if (count->found)
866 gtk_box_get_visible_position (GtkBox *box,
869 CountingData count = { child, FALSE, 0, 0 };
871 /* foreach iterates in visible order */
872 gtk_container_foreach (GTK_CONTAINER (box),
873 count_widget_position,
876 /* the child wasn't found, it's likely an internal child of some
877 * subclass, return -1 to indicate that there is no sibling relation
878 * to the regular box children
883 if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
884 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
890 static GtkWidgetPath *
891 gtk_box_get_path_for_child (GtkContainer *container,
894 GtkWidgetPath *path, *sibling_path;
896 GtkBoxPrivate *private;
897 GList *list, *children;
899 box = GTK_BOX (container);
902 path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (container)));
904 if (gtk_widget_get_visible (child))
908 sibling_path = gtk_widget_path_new ();
910 /* get_children works in visible order */
911 children = gtk_container_get_children (container);
912 if (private->orientation == GTK_ORIENTATION_HORIZONTAL &&
913 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
914 children = g_list_reverse (children);
916 for (list = children; list; list = list->next)
918 if (!gtk_widget_get_visible (list->data))
921 gtk_widget_path_append_for_widget (sibling_path, list->data);
924 g_list_free (children);
926 position = gtk_box_get_visible_position (box, child);
929 gtk_widget_path_append_with_siblings (path, sibling_path, position);
931 gtk_widget_path_append_for_widget (path, child);
933 gtk_widget_path_unref (sibling_path);
936 gtk_widget_path_append_for_widget (path, child);
942 gtk_box_invalidate_order (GtkBox *box)
944 gtk_container_foreach (GTK_CONTAINER (box),
945 (GtkCallback) gtk_widget_reset_style,
950 gtk_box_direction_changed (GtkWidget *widget,
951 GtkTextDirection previous_direction)
953 gtk_box_invalidate_order (GTK_BOX (widget));
957 box_child_visibility_notify_cb (GObject *obj,
961 GtkBox *box = user_data;
963 gtk_box_invalidate_order (box);
967 gtk_box_pack (GtkBox *box,
972 GtkPackType pack_type)
974 GtkBoxPrivate *private = box->priv;
975 GtkBoxChild *child_info;
977 g_return_if_fail (GTK_IS_BOX (box));
978 g_return_if_fail (GTK_IS_WIDGET (child));
979 g_return_if_fail (gtk_widget_get_parent (child) == NULL);
981 child_info = g_new (GtkBoxChild, 1);
982 child_info->widget = child;
983 child_info->padding = padding;
984 child_info->expand = expand ? TRUE : FALSE;
985 child_info->fill = fill ? TRUE : FALSE;
986 child_info->pack = pack_type;
988 private->children = g_list_append (private->children, child_info);
990 gtk_widget_freeze_child_notify (child);
992 gtk_box_invalidate_order (box);
993 gtk_widget_set_parent (child, GTK_WIDGET (box));
995 g_signal_connect (child, "notify::visible",
996 G_CALLBACK (box_child_visibility_notify_cb), box);
998 gtk_widget_child_notify (child, "expand");
999 gtk_widget_child_notify (child, "fill");
1000 gtk_widget_child_notify (child, "padding");
1001 gtk_widget_child_notify (child, "pack-type");
1002 gtk_widget_child_notify (child, "position");
1003 gtk_widget_thaw_child_notify (child);
1007 gtk_box_get_size (GtkWidget *widget,
1008 GtkOrientation orientation,
1013 GtkBoxPrivate *private;
1016 gint minimum, natural;
1018 box = GTK_BOX (widget);
1019 private = box->priv;
1021 minimum = natural = 0;
1025 for (children = private->children; children; children = children->next)
1027 GtkBoxChild *child = children->data;
1029 if (gtk_widget_get_visible (child->widget))
1031 gint child_minimum, child_natural;
1033 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1034 gtk_widget_get_preferred_width (child->widget,
1035 &child_minimum, &child_natural);
1037 gtk_widget_get_preferred_height (child->widget,
1038 &child_minimum, &child_natural);
1040 if (private->orientation == orientation)
1042 if (private->homogeneous)
1046 largest = child_minimum + child->padding * 2;
1047 minimum = MAX (minimum, largest);
1049 largest = child_natural + child->padding * 2;
1050 natural = MAX (natural, largest);
1054 minimum += child_minimum + child->padding * 2;
1055 natural += child_natural + child->padding * 2;
1060 /* The biggest mins and naturals in the opposing orientation */
1061 minimum = MAX (minimum, child_minimum);
1062 natural = MAX (natural, child_natural);
1069 if (nvis_children > 0 && private->orientation == orientation)
1071 if (private->homogeneous)
1073 minimum *= nvis_children;
1074 natural *= nvis_children;
1076 minimum += (nvis_children - 1) * private->spacing;
1077 natural += (nvis_children - 1) * private->spacing;
1081 *minimum_size = minimum;
1084 *natural_size = natural;
1088 gtk_box_get_preferred_width (GtkWidget *widget,
1092 gtk_box_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
1096 gtk_box_get_preferred_height (GtkWidget *widget,
1100 gtk_box_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
1104 gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
1109 GtkBoxPrivate *private = box->priv;
1113 gint nexpand_children;
1114 gint computed_minimum = 0, computed_natural = 0;
1115 GtkRequestedSize *sizes;
1116 GtkPackType packing;
1117 gint size, extra, i;
1118 gint child_size, child_minimum, child_natural;
1119 gint n_extra_widgets = 0;
1121 count_expand_children (box, &nvis_children, &nexpand_children);
1123 if (nvis_children <= 0)
1126 sizes = g_newa (GtkRequestedSize, nvis_children);
1127 size = avail_size - (nvis_children - 1) * private->spacing;
1129 /* Retrieve desired size for visible children */
1130 for (i = 0, children = private->children; children; children = children->next)
1132 child = children->data;
1134 if (gtk_widget_get_visible (child->widget))
1136 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1137 gtk_widget_get_preferred_width (child->widget,
1138 &sizes[i].minimum_size,
1139 &sizes[i].natural_size);
1141 gtk_widget_get_preferred_height (child->widget,
1142 &sizes[i].minimum_size,
1143 &sizes[i].natural_size);
1145 /* Assert the api is working properly */
1146 if (sizes[i].minimum_size < 0)
1147 g_error ("GtkBox child %s minimum %s: %d < 0",
1148 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1149 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1150 sizes[i].minimum_size);
1152 if (sizes[i].natural_size < sizes[i].minimum_size)
1153 g_error ("GtkBox child %s natural %s: %d < minimum %d",
1154 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1155 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1156 sizes[i].natural_size,
1157 sizes[i].minimum_size);
1159 size -= sizes[i].minimum_size;
1160 size -= child->padding * 2;
1162 sizes[i].data = child;
1168 if (private->homogeneous)
1170 /* If were homogenous we still need to run the above loop to get the
1171 * minimum sizes for children that are not going to fill
1173 size = avail_size - (nvis_children - 1) * private->spacing;
1174 extra = size / nvis_children;
1175 n_extra_widgets = size % nvis_children;
1179 /* Bring children up to size first */
1180 size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
1182 /* Calculate space which hasn't distributed yet,
1183 * and is available for expanding children.
1185 if (nexpand_children > 0)
1187 extra = size / nexpand_children;
1188 n_extra_widgets = size % nexpand_children;
1194 /* Allocate child positions. */
1195 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1197 for (i = 0, children = private->children;
1199 children = children->next)
1201 child = children->data;
1203 /* If widget is not visible, skip it. */
1204 if (!gtk_widget_get_visible (child->widget))
1207 /* If widget is packed differently skip it, but still increment i,
1208 * since widget is visible and will be handled in next loop iteration.
1210 if (child->pack != packing)
1216 if (child->pack == packing)
1218 /* Assign the child's size. */
1219 if (private->homogeneous)
1223 if (n_extra_widgets > 0)
1231 child_size = sizes[i].minimum_size + child->padding * 2;
1233 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
1235 child_size += extra;
1237 if (n_extra_widgets > 0)
1247 child_size = MAX (1, child_size - child->padding * 2);
1251 child_size = sizes[i].minimum_size;
1255 /* Assign the child's position. */
1256 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1257 gtk_widget_get_preferred_height_for_width (child->widget,
1258 child_size, &child_minimum, &child_natural);
1259 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1260 gtk_widget_get_preferred_width_for_height (child->widget,
1261 child_size, &child_minimum, &child_natural);
1264 computed_minimum = MAX (computed_minimum, child_minimum);
1265 computed_natural = MAX (computed_natural, child_natural);
1272 *minimum_size = computed_minimum;
1274 *natural_size = computed_natural;
1278 gtk_box_compute_size_for_orientation (GtkBox *box,
1283 GtkBoxPrivate *private = box->priv;
1285 gint nvis_children = 0;
1286 gint required_size = 0, required_natural = 0, child_size, child_natural;
1287 gint largest_child = 0, largest_natural = 0;
1289 for (children = private->children; children != NULL;
1290 children = children->next)
1292 GtkBoxChild *child = children->data;
1294 if (gtk_widget_get_visible (child->widget))
1297 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1298 gtk_widget_get_preferred_width_for_height (child->widget,
1299 avail_size, &child_size, &child_natural);
1301 gtk_widget_get_preferred_height_for_width (child->widget,
1302 avail_size, &child_size, &child_natural);
1305 child_size += child->padding * 2;
1306 child_natural += child->padding * 2;
1308 if (child_size > largest_child)
1309 largest_child = child_size;
1311 if (child_natural > largest_natural)
1312 largest_natural = child_natural;
1314 required_size += child_size;
1315 required_natural += child_natural;
1321 if (nvis_children > 0)
1323 if (private->homogeneous)
1325 required_size = largest_child * nvis_children;
1326 required_natural = largest_natural * nvis_children;
1329 required_size += (nvis_children - 1) * private->spacing;
1330 required_natural += (nvis_children - 1) * private->spacing;
1334 *minimum_size = required_size;
1337 *natural_size = required_natural;
1341 gtk_box_get_preferred_width_for_height (GtkWidget *widget,
1343 gint *minimum_width,
1344 gint *natural_width)
1346 GtkBox *box = GTK_BOX (widget);
1347 GtkBoxPrivate *private = box->priv;
1349 if (private->orientation == GTK_ORIENTATION_VERTICAL)
1350 gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width);
1352 gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width);
1356 gtk_box_get_preferred_height_for_width (GtkWidget *widget,
1358 gint *minimum_height,
1359 gint *natural_height)
1361 GtkBox *box = GTK_BOX (widget);
1362 GtkBoxPrivate *private = box->priv;
1364 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1365 gtk_box_compute_size_for_opposing_orientation (box, width, minimum_height, natural_height);
1367 gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height);
1372 * @orientation: the box's orientation.
1373 * @spacing: the number of pixels to place by default between children.
1375 * Creates a new #GtkBox.
1377 * Return value: a new #GtkBox.
1382 gtk_box_new (GtkOrientation orientation,
1385 return g_object_new (GTK_TYPE_BOX,
1386 "orientation", orientation,
1392 * gtk_box_pack_start:
1394 * @child: the #GtkWidget to be added to @box
1395 * @expand: %TRUE if the new child is to be given extra space allocated
1396 * to @box. The extra space will be divided evenly between all children
1397 * that use this option
1398 * @fill: %TRUE if space given to @child by the @expand option is
1399 * actually allocated to @child, rather than just padding it. This
1400 * parameter has no effect if @expand is set to %FALSE. A child is
1401 * always allocated the full height of a horizontal #GtkBox and the full width
1402 * of a vertical #GtkBox. This option affects the other dimension
1403 * @padding: extra space in pixels to put between this child and its
1404 * neighbors, over and above the global amount specified by
1405 * #GtkBox:spacing property. If @child is a widget at one of the
1406 * reference ends of @box, then @padding pixels are also put between
1407 * @child and the reference edge of @box
1409 * Adds @child to @box, packed with reference to the start of @box.
1410 * The @child is packed after any other child packed with reference
1411 * to the start of @box.
1414 gtk_box_pack_start (GtkBox *box,
1420 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
1426 * @child: the #GtkWidget to be added to @box
1427 * @expand: %TRUE if the new child is to be given extra space allocated
1428 * to @box. The extra space will be divided evenly between all children
1429 * of @box that use this option
1430 * @fill: %TRUE if space given to @child by the @expand option is
1431 * actually allocated to @child, rather than just padding it. This
1432 * parameter has no effect if @expand is set to %FALSE. A child is
1433 * always allocated the full height of a horizontal #GtkBox and the full width
1434 * of a vertical #GtkBox. This option affects the other dimension
1435 * @padding: extra space in pixels to put between this child and its
1436 * neighbors, over and above the global amount specified by
1437 * #GtkBox:spacing property. If @child is a widget at one of the
1438 * reference ends of @box, then @padding pixels are also put between
1439 * @child and the reference edge of @box
1441 * Adds @child to @box, packed with reference to the end of @box.
1442 * The @child is packed after (away from end of) any other child
1443 * packed with reference to the end of @box.
1446 gtk_box_pack_end (GtkBox *box,
1452 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
1456 * gtk_box_set_homogeneous:
1458 * @homogeneous: a boolean value, %TRUE to create equal allotments,
1459 * %FALSE for variable allotments
1461 * Sets the #GtkBox:homogeneous property of @box, controlling
1462 * whether or not all children of @box are given equal space
1466 gtk_box_set_homogeneous (GtkBox *box,
1467 gboolean homogeneous)
1469 GtkBoxPrivate *private;
1471 g_return_if_fail (GTK_IS_BOX (box));
1473 private = box->priv;
1475 if ((homogeneous ? TRUE : FALSE) != private->homogeneous)
1477 private->homogeneous = homogeneous ? TRUE : FALSE;
1478 g_object_notify (G_OBJECT (box), "homogeneous");
1479 gtk_widget_queue_resize (GTK_WIDGET (box));
1484 * gtk_box_get_homogeneous:
1487 * Returns whether the box is homogeneous (all children are the
1488 * same size). See gtk_box_set_homogeneous().
1490 * Return value: %TRUE if the box is homogeneous.
1493 gtk_box_get_homogeneous (GtkBox *box)
1495 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1497 return box->priv->homogeneous;
1501 * gtk_box_set_spacing:
1503 * @spacing: the number of pixels to put between children
1505 * Sets the #GtkBox:spacing property of @box, which is the
1506 * number of pixels to place between children of @box.
1509 gtk_box_set_spacing (GtkBox *box,
1512 GtkBoxPrivate *private;
1514 g_return_if_fail (GTK_IS_BOX (box));
1516 private = box->priv;
1518 if (spacing != private->spacing)
1520 private->spacing = spacing;
1521 _gtk_box_set_spacing_set (box, TRUE);
1523 g_object_notify (G_OBJECT (box), "spacing");
1525 gtk_widget_queue_resize (GTK_WIDGET (box));
1530 * gtk_box_get_spacing:
1533 * Gets the value set by gtk_box_set_spacing().
1535 * Return value: spacing between children
1538 gtk_box_get_spacing (GtkBox *box)
1540 g_return_val_if_fail (GTK_IS_BOX (box), 0);
1542 return box->priv->spacing;
1546 _gtk_box_set_spacing_set (GtkBox *box,
1547 gboolean spacing_set)
1549 GtkBoxPrivate *private;
1551 g_return_if_fail (GTK_IS_BOX (box));
1553 private = box->priv;
1555 private->spacing_set = spacing_set ? TRUE : FALSE;
1559 _gtk_box_get_spacing_set (GtkBox *box)
1561 GtkBoxPrivate *private;
1563 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1565 private = box->priv;
1567 return private->spacing_set;
1571 * gtk_box_reorder_child:
1573 * @child: the #GtkWidget to move
1574 * @position: the new position for @child in the list of children
1575 * of @box, starting from 0. If negative, indicates the end of
1578 * Moves @child to a new @position in the list of @box children.
1579 * The list is the <structfield>children</structfield> field of
1580 * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START
1581 * as well as widgets packed #GTK_PACK_END, in the order that these
1582 * widgets were added to @box.
1584 * A widget's position in the @box children list determines where
1585 * the widget is packed into @box. A child widget at some position
1586 * in the list will be packed just after all other widgets of the
1587 * same packing type that appear earlier in the list.
1590 gtk_box_reorder_child (GtkBox *box,
1594 GtkBoxPrivate *priv;
1597 GtkBoxChild *child_info = NULL;
1600 g_return_if_fail (GTK_IS_BOX (box));
1601 g_return_if_fail (GTK_IS_WIDGET (child));
1605 old_link = priv->children;
1609 child_info = old_link->data;
1610 if (child_info->widget == child)
1613 old_link = old_link->next;
1617 g_return_if_fail (old_link != NULL);
1619 if (position == old_position)
1622 priv->children = g_list_delete_link (priv->children, old_link);
1627 new_link = g_list_nth (priv->children, position);
1629 priv->children = g_list_insert_before (priv->children, new_link, child_info);
1631 gtk_widget_child_notify (child, "position");
1632 if (gtk_widget_get_visible (child)
1633 && gtk_widget_get_visible (GTK_WIDGET (box)))
1635 gtk_box_invalidate_order (box);
1636 gtk_widget_queue_resize (child);
1641 * gtk_box_query_child_packing:
1643 * @child: the #GtkWidget of the child to query
1644 * @expand: (out): pointer to return location for #GtkBox:expand child
1646 * @fill: (out): pointer to return location for #GtkBox:fill child
1648 * @padding: (out): pointer to return location for #GtkBox:padding
1650 * @pack_type: (out): pointer to return location for #GtkBox:pack-type
1653 * Obtains information about how @child is packed into @box.
1656 gtk_box_query_child_packing (GtkBox *box,
1661 GtkPackType *pack_type)
1663 GtkBoxPrivate *private;
1665 GtkBoxChild *child_info = NULL;
1667 g_return_if_fail (GTK_IS_BOX (box));
1668 g_return_if_fail (GTK_IS_WIDGET (child));
1670 private = box->priv;
1672 list = private->children;
1675 child_info = list->data;
1676 if (child_info->widget == child)
1685 *expand = child_info->expand;
1687 *fill = child_info->fill;
1689 *padding = child_info->padding;
1691 *pack_type = child_info->pack;
1696 * gtk_box_set_child_packing:
1698 * @child: the #GtkWidget of the child to set
1699 * @expand: the new value of the #GtkBox:expand child property
1700 * @fill: the new value of the #GtkBox:fill child property
1701 * @padding: the new value of the #GtkBox:padding child property
1702 * @pack_type: the new value of the #GtkBox:pack-type child property
1704 * Sets the way @child is packed into @box.
1707 gtk_box_set_child_packing (GtkBox *box,
1712 GtkPackType pack_type)
1714 GtkBoxPrivate *private;
1716 GtkBoxChild *child_info = NULL;
1718 g_return_if_fail (GTK_IS_BOX (box));
1719 g_return_if_fail (GTK_IS_WIDGET (child));
1721 private = box->priv;
1723 list = private->children;
1726 child_info = list->data;
1727 if (child_info->widget == child)
1733 gtk_widget_freeze_child_notify (child);
1738 expanded = expand != FALSE;
1740 /* avoid setting expand if unchanged, since queue_compute_expand
1741 * can be expensive-ish
1743 if (child_info->expand != expanded)
1745 child_info->expand = expand != FALSE;
1746 gtk_widget_queue_compute_expand (GTK_WIDGET (box));
1747 gtk_widget_child_notify (child, "expand");
1750 child_info->fill = fill != FALSE;
1751 gtk_widget_child_notify (child, "fill");
1752 child_info->padding = padding;
1753 gtk_widget_child_notify (child, "padding");
1754 if (pack_type != GTK_PACK_END)
1755 pack_type = GTK_PACK_START;
1756 if (child_info->pack != pack_type)
1758 child_info->pack = GTK_PACK_END;
1759 gtk_widget_child_notify (child, "pack-type");
1760 gtk_box_invalidate_order (box);
1763 if (gtk_widget_get_visible (child)
1764 && gtk_widget_get_visible (GTK_WIDGET (box)))
1765 gtk_widget_queue_resize (child);
1767 gtk_widget_thaw_child_notify (child);
1771 _gtk_box_set_old_defaults (GtkBox *box)
1773 GtkBoxPrivate *private;
1775 g_return_if_fail (GTK_IS_BOX (box));
1777 private = box->priv;
1779 private->default_expand = TRUE;
1783 gtk_box_add (GtkContainer *container,
1786 GtkBoxPrivate *priv = GTK_BOX (container)->priv;
1788 gtk_box_pack_start (GTK_BOX (container), widget,
1789 priv->default_expand,
1795 gtk_box_remove (GtkContainer *container,
1798 GtkBox *box = GTK_BOX (container);
1799 GtkBoxPrivate *priv = box->priv;
1803 children = priv->children;
1806 child = children->data;
1808 if (child->widget == widget)
1810 gboolean was_visible;
1812 was_visible = gtk_widget_get_visible (widget);
1813 gtk_widget_unparent (widget);
1815 g_signal_handlers_disconnect_by_func (widget,
1816 box_child_visibility_notify_cb,
1819 priv->children = g_list_remove_link (priv->children, children);
1820 g_list_free (children);
1823 /* queue resize regardless of gtk_widget_get_visible (container),
1824 * since that's what is needed by toplevels.
1828 gtk_box_invalidate_order (box);
1829 gtk_widget_queue_resize (GTK_WIDGET (container));
1835 children = children->next;
1840 gtk_box_forall (GtkContainer *container,
1841 gboolean include_internals,
1842 GtkCallback callback,
1843 gpointer callback_data)
1845 GtkBox *box = GTK_BOX (container);
1846 GtkBoxPrivate *priv = box->priv;
1850 children = priv->children;
1853 child = children->data;
1854 children = children->next;
1856 if (child->pack == GTK_PACK_START)
1857 (* callback) (child->widget, callback_data);
1860 children = g_list_last (priv->children);
1863 child = children->data;
1864 children = children->prev;
1866 if (child->pack == GTK_PACK_END)
1867 (* callback) (child->widget, callback_data);
1872 _gtk_box_get_children (GtkBox *box)
1874 GtkBoxPrivate *priv;
1877 GList *retval = NULL;
1879 g_return_val_if_fail (GTK_IS_BOX (box), NULL);
1883 children = priv->children;
1886 child = children->data;
1887 children = children->next;
1889 retval = g_list_prepend (retval, child->widget);
1892 return g_list_reverse (retval);