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, #GtkTable, #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 "gtkorientable.h"
84 #include "gtksizerequest.h"
85 #include "gtktypebuiltins.h"
86 #include "gtkprivate.h"
102 CHILD_PROP_PACK_TYPE,
106 struct _GtkBoxPrivate
110 GtkOrientation orientation;
113 guint default_expand : 1;
114 guint homogeneous : 1;
115 guint spacing_set : 1;
118 typedef struct _GtkBoxChild GtkBoxChild;
122 * @widget: the child widget, packed into the GtkBox.
123 * @padding: the number of extra pixels to put between this child and its
124 * neighbors, set when packed, zero by default.
125 * @expand: flag indicates whether extra space should be given to this child.
126 * Any extra space given to the parent GtkBox is divided up among all children
127 * with this attribute set to %TRUE; set when packed, %TRUE by default.
128 * @fill: flag indicates whether any extra space given to this child due to its
129 * @expand attribute being set is actually allocated to the child, rather than
130 * being used as padding around the widget; set when packed, %TRUE by default.
131 * @pack: one of #GtkPackType indicating whether the child is packed with
132 * reference to the start (top/left) or end (bottom/right) of the GtkBox.
145 static void gtk_box_size_allocate (GtkWidget *widget,
146 GtkAllocation *allocation);
148 static void gtk_box_compute_expand (GtkWidget *widget,
151 static void gtk_box_direction_changed (GtkWidget *widget,
152 GtkTextDirection previous_direction);
154 static void gtk_box_set_property (GObject *object,
158 static void gtk_box_get_property (GObject *object,
162 static void gtk_box_add (GtkContainer *container,
164 static void gtk_box_remove (GtkContainer *container,
166 static void gtk_box_forall (GtkContainer *container,
167 gboolean include_internals,
168 GtkCallback callback,
169 gpointer callback_data);
170 static void gtk_box_set_child_property (GtkContainer *container,
175 static void gtk_box_get_child_property (GtkContainer *container,
180 static GType gtk_box_child_type (GtkContainer *container);
181 static GtkWidgetPath * gtk_box_get_path_for_child
182 (GtkContainer *container,
186 static void gtk_box_get_preferred_width (GtkWidget *widget,
189 static void gtk_box_get_preferred_height (GtkWidget *widget,
192 static void gtk_box_get_preferred_width_for_height (GtkWidget *widget,
195 gint *natural_width);
196 static void gtk_box_get_preferred_height_for_width (GtkWidget *widget,
198 gint *minimum_height,
199 gint *natural_height);
201 G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
202 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
206 gtk_box_class_init (GtkBoxClass *class)
208 GObjectClass *object_class = G_OBJECT_CLASS (class);
209 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
210 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
212 object_class->set_property = gtk_box_set_property;
213 object_class->get_property = gtk_box_get_property;
215 widget_class->size_allocate = gtk_box_size_allocate;
216 widget_class->get_preferred_width = gtk_box_get_preferred_width;
217 widget_class->get_preferred_height = gtk_box_get_preferred_height;
218 widget_class->get_preferred_height_for_width = gtk_box_get_preferred_height_for_width;
219 widget_class->get_preferred_width_for_height = gtk_box_get_preferred_width_for_height;
220 widget_class->compute_expand = gtk_box_compute_expand;
221 widget_class->direction_changed = gtk_box_direction_changed;
223 container_class->add = gtk_box_add;
224 container_class->remove = gtk_box_remove;
225 container_class->forall = gtk_box_forall;
226 container_class->child_type = gtk_box_child_type;
227 container_class->set_child_property = gtk_box_set_child_property;
228 container_class->get_child_property = gtk_box_get_child_property;
229 container_class->get_path_for_child = gtk_box_get_path_for_child;
230 gtk_container_class_handle_border_width (container_class);
232 g_object_class_override_property (object_class,
236 g_object_class_install_property (object_class,
238 g_param_spec_int ("spacing",
240 P_("The amount of space between children"),
244 GTK_PARAM_READWRITE));
246 g_object_class_install_property (object_class,
248 g_param_spec_boolean ("homogeneous",
250 P_("Whether the children should all be the same size"),
252 GTK_PARAM_READWRITE));
257 * Whether the child should receive extra space when the parent grows.
259 * Note that the default value for this property is %FALSE for GtkBox,
260 * but #GtkHBox, #GtkVBox and other subclasses use the old default
263 * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
264 * and #GtkWidget:vexpand properties are the preferred way to influence
265 * child size allocation in containers.
267 gtk_container_class_install_child_property (container_class,
269 g_param_spec_boolean ("expand",
271 P_("Whether the child should receive extra space when the parent grows"),
273 GTK_PARAM_READWRITE));
278 * Whether the child should receive extra space when the parent grows.
280 * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
281 * and #GtkWidget:vexpand properties are the preferred way to influence
282 * child size allocation in containers.
284 gtk_container_class_install_child_property (container_class,
286 g_param_spec_boolean ("fill",
288 P_("Whether extra space given to the child should be allocated to the child or used as padding"),
290 GTK_PARAM_READWRITE));
292 gtk_container_class_install_child_property (container_class,
294 g_param_spec_uint ("padding",
296 P_("Extra space to put between the child and its neighbors, in pixels"),
298 GTK_PARAM_READWRITE));
299 gtk_container_class_install_child_property (container_class,
300 CHILD_PROP_PACK_TYPE,
301 g_param_spec_enum ("pack-type",
303 P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
304 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
305 GTK_PARAM_READWRITE));
306 gtk_container_class_install_child_property (container_class,
308 g_param_spec_int ("position",
310 P_("The index of the child in the parent"),
312 GTK_PARAM_READWRITE));
314 g_type_class_add_private (object_class, sizeof (GtkBoxPrivate));
318 gtk_box_init (GtkBox *box)
320 GtkBoxPrivate *private;
322 box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
327 gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
328 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE);
330 private->orientation = GTK_ORIENTATION_HORIZONTAL;
331 private->children = NULL;
333 private->default_expand = FALSE;
334 private->homogeneous = FALSE;
335 private->spacing = 0;
336 private->spacing_set = FALSE;
340 gtk_box_set_property (GObject *object,
345 GtkBox *box = GTK_BOX (object);
346 GtkBoxPrivate *private = box->priv;
350 case PROP_ORIENTATION:
351 private->orientation = g_value_get_enum (value);
352 gtk_widget_queue_resize (GTK_WIDGET (box));
355 gtk_box_set_spacing (box, g_value_get_int (value));
357 case PROP_HOMOGENEOUS:
358 gtk_box_set_homogeneous (box, g_value_get_boolean (value));
361 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
367 gtk_box_get_property (GObject *object,
372 GtkBox *box = GTK_BOX (object);
373 GtkBoxPrivate *private = box->priv;
377 case PROP_ORIENTATION:
378 g_value_set_enum (value, private->orientation);
381 g_value_set_int (value, private->spacing);
383 case PROP_HOMOGENEOUS:
384 g_value_set_boolean (value, private->homogeneous);
387 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
394 count_expand_children (GtkBox *box,
395 gint *visible_children,
396 gint *expand_children)
398 GtkBoxPrivate *private = box->priv;
402 *visible_children = *expand_children = 0;
404 for (children = private->children; children; children = children->next)
406 child = children->data;
408 if (gtk_widget_get_visible (child->widget))
410 *visible_children += 1;
411 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
412 *expand_children += 1;
418 gtk_box_size_allocate (GtkWidget *widget,
419 GtkAllocation *allocation)
421 GtkBox *box = GTK_BOX (widget);
422 GtkBoxPrivate *private = box->priv;
426 gint nexpand_children;
428 GtkTextDirection direction;
429 GtkAllocation child_allocation;
430 GtkRequestedSize *sizes;
436 gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
437 gint x = 0, y = 0, i;
441 gtk_widget_set_allocation (widget, allocation);
443 count_expand_children (box, &nvis_children, &nexpand_children);
445 /* If there is no visible child, simply return. */
446 if (nvis_children <= 0)
449 direction = gtk_widget_get_direction (widget);
450 sizes = g_newa (GtkRequestedSize, nvis_children);
452 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
453 size = allocation->width - (nvis_children - 1) * private->spacing;
455 size = allocation->height - (nvis_children - 1) * private->spacing;
457 /* Retrieve desired size for visible children. */
458 for (i = 0, children = private->children; children; children = children->next)
460 child = children->data;
462 if (!gtk_widget_get_visible (child->widget))
465 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
466 gtk_widget_get_preferred_width_for_height (child->widget,
468 &sizes[i].minimum_size,
469 &sizes[i].natural_size);
471 gtk_widget_get_preferred_height_for_width (child->widget,
473 &sizes[i].minimum_size,
474 &sizes[i].natural_size);
477 /* Assert the api is working properly */
478 if (sizes[i].minimum_size < 0)
479 g_error ("GtkBox child %s minimum %s: %d < 0 for %s %d",
480 gtk_widget_get_name (GTK_WIDGET (child->widget)),
481 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
482 sizes[i].minimum_size,
483 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
484 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
486 if (sizes[i].natural_size < sizes[i].minimum_size)
487 g_error ("GtkBox child %s natural %s: %d < minimum %d for %s %d",
488 gtk_widget_get_name (GTK_WIDGET (child->widget)),
489 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
490 sizes[i].natural_size,
491 sizes[i].minimum_size,
492 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
493 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
495 size -= sizes[i].minimum_size;
496 size -= child->padding * 2;
498 sizes[i].data = child;
503 if (private->homogeneous)
505 /* If were homogenous we still need to run the above loop to get the
506 * minimum sizes for children that are not going to fill
508 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
509 size = allocation->width - (nvis_children - 1) * private->spacing;
511 size = allocation->height - (nvis_children - 1) * private->spacing;
513 extra = size / nvis_children;
514 n_extra_widgets = size % nvis_children;
518 /* Bring children up to size first */
519 size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
521 /* Calculate space which hasn't distributed yet,
522 * and is available for expanding children.
524 if (nexpand_children > 0)
526 extra = size / nexpand_children;
527 n_extra_widgets = size % nexpand_children;
533 /* Allocate child positions. */
534 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
536 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
538 child_allocation.y = allocation->y;
539 child_allocation.height = MAX (1, allocation->height);
540 if (packing == GTK_PACK_START)
543 x = allocation->x + allocation->width;
547 child_allocation.x = allocation->x;
548 child_allocation.width = MAX (1, allocation->width);
549 if (packing == GTK_PACK_START)
552 y = allocation->y + allocation->height;
555 for (i = 0, children = private->children;
557 children = children->next)
559 child = children->data;
561 /* If widget is not visible, skip it. */
562 if (!gtk_widget_get_visible (child->widget))
565 /* If widget is packed differently skip it, but still increment i,
566 * since widget is visible and will be handled in next loop iteration.
568 if (child->pack != packing)
574 /* Assign the child's size. */
575 if (private->homogeneous)
579 if (n_extra_widgets > 0)
587 child_size = sizes[i].minimum_size + child->padding * 2;
589 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
593 if (n_extra_widgets > 0)
601 /* Assign the child's position. */
602 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
606 child_allocation.width = MAX (1, child_size - child->padding * 2);
607 child_allocation.x = x + child->padding;
611 child_allocation.width = sizes[i].minimum_size;
612 child_allocation.x = x + (child_size - child_allocation.width) / 2;
615 if (packing == GTK_PACK_START)
617 x += child_size + private->spacing;
621 x -= child_size + private->spacing;
623 child_allocation.x -= child_size;
626 if (direction == GTK_TEXT_DIR_RTL)
627 child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
630 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
634 child_allocation.height = MAX (1, child_size - child->padding * 2);
635 child_allocation.y = y + child->padding;
639 child_allocation.height = sizes[i].minimum_size;
640 child_allocation.y = y + (child_size - child_allocation.height) / 2;
643 if (packing == GTK_PACK_START)
645 y += child_size + private->spacing;
649 y -= child_size + private->spacing;
651 child_allocation.y -= child_size;
654 gtk_widget_size_allocate (child->widget, &child_allocation);
662 gtk_box_compute_expand (GtkWidget *widget,
666 GtkBoxPrivate *private = GTK_BOX (widget)->priv;
670 gboolean opposite_expand;
671 GtkOrientation opposite_orientation;
673 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
674 opposite_orientation = GTK_ORIENTATION_VERTICAL;
676 opposite_orientation = GTK_ORIENTATION_HORIZONTAL;
679 opposite_expand = FALSE;
681 for (children = private->children; children; children = children->next)
683 child = children->data;
685 /* we don't recurse into children anymore as soon as we know
686 * expand=TRUE in an orientation
689 if (child->expand || (!our_expand && gtk_widget_compute_expand (child->widget, private->orientation)))
692 if (!opposite_expand && gtk_widget_compute_expand (child->widget, opposite_orientation))
693 opposite_expand = TRUE;
695 if (our_expand && opposite_expand)
699 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
701 *hexpand_p = our_expand;
702 *vexpand_p = opposite_expand;
706 *hexpand_p = opposite_expand;
707 *vexpand_p = our_expand;
712 gtk_box_child_type (GtkContainer *container)
714 return GTK_TYPE_WIDGET;
718 gtk_box_set_child_property (GtkContainer *container,
727 GtkPackType pack_type = 0;
729 if (property_id != CHILD_PROP_POSITION)
730 gtk_box_query_child_packing (GTK_BOX (container),
738 case CHILD_PROP_EXPAND:
739 gtk_box_set_child_packing (GTK_BOX (container),
741 g_value_get_boolean (value),
746 case CHILD_PROP_FILL:
747 gtk_box_set_child_packing (GTK_BOX (container),
750 g_value_get_boolean (value),
754 case CHILD_PROP_PADDING:
755 gtk_box_set_child_packing (GTK_BOX (container),
759 g_value_get_uint (value),
762 case CHILD_PROP_PACK_TYPE:
763 gtk_box_set_child_packing (GTK_BOX (container),
768 g_value_get_enum (value));
770 case CHILD_PROP_POSITION:
771 gtk_box_reorder_child (GTK_BOX (container),
773 g_value_get_int (value));
776 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
782 gtk_box_get_child_property (GtkContainer *container,
788 gboolean expand = FALSE;
789 gboolean fill = FALSE;
791 GtkPackType pack_type = 0;
794 if (property_id != CHILD_PROP_POSITION)
795 gtk_box_query_child_packing (GTK_BOX (container),
804 case CHILD_PROP_EXPAND:
805 g_value_set_boolean (value, expand);
807 case CHILD_PROP_FILL:
808 g_value_set_boolean (value, fill);
810 case CHILD_PROP_PADDING:
811 g_value_set_uint (value, padding);
813 case CHILD_PROP_PACK_TYPE:
814 g_value_set_enum (value, pack_type);
816 case CHILD_PROP_POSITION:
818 for (list = GTK_BOX (container)->priv->children; list; list = list->next)
820 GtkBoxChild *child_entry;
822 child_entry = list->data;
823 if (child_entry->widget == child)
827 g_value_set_int (value, list ? i : -1);
830 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
835 typedef struct _CountingData CountingData;
836 struct _CountingData {
844 count_widget_position (GtkWidget *widget,
847 CountingData *count = data;
849 if (!gtk_widget_get_visible (widget))
852 if (count->widget == widget)
854 else if (count->found)
861 gtk_box_get_visible_position (GtkBox *box,
864 CountingData count = { child, FALSE, 0, 0 };
866 /* forall iterates in visible order */
867 gtk_container_forall (GTK_CONTAINER (box),
868 count_widget_position,
871 g_assert (count.found);
872 if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
873 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
879 static GtkWidgetPath *
880 gtk_box_get_path_for_child (GtkContainer *container,
883 GtkWidgetPath *path, *sibling_path;
885 GtkBoxPrivate *private;
886 GList *list, *children;
888 box = GTK_BOX (container);
891 path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (container)));
893 if (gtk_widget_get_visible (child))
895 sibling_path = gtk_widget_path_new ();
897 /* get_children works in visible order */
898 children = gtk_container_get_children (container);
899 if (private->orientation == GTK_ORIENTATION_HORIZONTAL &&
900 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
901 children = g_list_reverse (children);
903 for (list = children; list; list = list->next)
905 if (!gtk_widget_get_visible (list->data))
908 gtk_widget_path_append_for_widget (sibling_path, list->data);
910 g_list_free (children);
912 gtk_widget_path_append_with_siblings (path,
914 gtk_box_get_visible_position (box,
916 gtk_widget_path_unref (sibling_path);
919 gtk_widget_path_append_for_widget (path, child);
925 gtk_box_invalidate_order (GtkBox *box)
927 gtk_container_foreach (GTK_CONTAINER (box),
928 (GtkCallback) gtk_widget_reset_style,
933 gtk_box_direction_changed (GtkWidget *widget,
934 GtkTextDirection previous_direction)
936 gtk_box_invalidate_order (GTK_BOX (widget));
940 box_child_visibility_notify_cb (GObject *obj,
944 GtkBox *box = user_data;
946 gtk_box_invalidate_order (box);
950 gtk_box_pack (GtkBox *box,
955 GtkPackType pack_type)
957 GtkBoxPrivate *private = box->priv;
958 GtkBoxChild *child_info;
960 g_return_if_fail (GTK_IS_BOX (box));
961 g_return_if_fail (GTK_IS_WIDGET (child));
962 g_return_if_fail (gtk_widget_get_parent (child) == NULL);
964 child_info = g_new (GtkBoxChild, 1);
965 child_info->widget = child;
966 child_info->padding = padding;
967 child_info->expand = expand ? TRUE : FALSE;
968 child_info->fill = fill ? TRUE : FALSE;
969 child_info->pack = pack_type;
971 private->children = g_list_append (private->children, child_info);
973 gtk_widget_freeze_child_notify (child);
975 gtk_box_invalidate_order (box);
976 gtk_widget_set_parent (child, GTK_WIDGET (box));
978 g_signal_connect (child, "notify::visible",
979 G_CALLBACK (box_child_visibility_notify_cb), box);
981 gtk_widget_child_notify (child, "expand");
982 gtk_widget_child_notify (child, "fill");
983 gtk_widget_child_notify (child, "padding");
984 gtk_widget_child_notify (child, "pack-type");
985 gtk_widget_child_notify (child, "position");
986 gtk_widget_thaw_child_notify (child);
990 gtk_box_get_size (GtkWidget *widget,
991 GtkOrientation orientation,
996 GtkBoxPrivate *private;
999 gint minimum, natural;
1001 box = GTK_BOX (widget);
1002 private = box->priv;
1004 minimum = natural = 0;
1008 for (children = private->children; children; children = children->next)
1010 GtkBoxChild *child = children->data;
1012 if (gtk_widget_get_visible (child->widget))
1014 gint child_minimum, child_natural;
1016 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1017 gtk_widget_get_preferred_width (child->widget,
1018 &child_minimum, &child_natural);
1020 gtk_widget_get_preferred_height (child->widget,
1021 &child_minimum, &child_natural);
1023 if (private->orientation == orientation)
1025 if (private->homogeneous)
1029 largest = child_minimum + child->padding * 2;
1030 minimum = MAX (minimum, largest);
1032 largest = child_natural + child->padding * 2;
1033 natural = MAX (natural, largest);
1037 minimum += child_minimum + child->padding * 2;
1038 natural += child_natural + child->padding * 2;
1043 /* The biggest mins and naturals in the opposing orientation */
1044 minimum = MAX (minimum, child_minimum);
1045 natural = MAX (natural, child_natural);
1052 if (nvis_children > 0 && private->orientation == orientation)
1054 if (private->homogeneous)
1056 minimum *= nvis_children;
1057 natural *= nvis_children;
1059 minimum += (nvis_children - 1) * private->spacing;
1060 natural += (nvis_children - 1) * private->spacing;
1064 *minimum_size = minimum;
1067 *natural_size = natural;
1071 gtk_box_get_preferred_width (GtkWidget *widget,
1075 gtk_box_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
1079 gtk_box_get_preferred_height (GtkWidget *widget,
1083 gtk_box_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
1087 gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
1092 GtkBoxPrivate *private = box->priv;
1096 gint nexpand_children;
1097 gint computed_minimum = 0, computed_natural = 0;
1098 GtkRequestedSize *sizes;
1099 GtkPackType packing;
1100 gint size, extra, i;
1101 gint child_size, child_minimum, child_natural;
1102 gint n_extra_widgets = 0;
1104 count_expand_children (box, &nvis_children, &nexpand_children);
1106 if (nvis_children <= 0)
1109 sizes = g_newa (GtkRequestedSize, nvis_children);
1110 size = avail_size - (nvis_children - 1) * private->spacing;
1112 /* Retrieve desired size for visible children */
1113 for (i = 0, children = private->children; children; children = children->next)
1115 child = children->data;
1117 if (gtk_widget_get_visible (child->widget))
1119 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1120 gtk_widget_get_preferred_width (child->widget,
1121 &sizes[i].minimum_size,
1122 &sizes[i].natural_size);
1124 gtk_widget_get_preferred_height (child->widget,
1125 &sizes[i].minimum_size,
1126 &sizes[i].natural_size);
1128 /* Assert the api is working properly */
1129 if (sizes[i].minimum_size < 0)
1130 g_error ("GtkBox child %s minimum %s: %d < 0",
1131 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1132 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1133 sizes[i].minimum_size);
1135 if (sizes[i].natural_size < sizes[i].minimum_size)
1136 g_error ("GtkBox child %s natural %s: %d < minimum %d",
1137 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1138 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1139 sizes[i].natural_size,
1140 sizes[i].minimum_size);
1142 size -= sizes[i].minimum_size;
1143 size -= child->padding * 2;
1145 sizes[i].data = child;
1151 if (private->homogeneous)
1153 /* If were homogenous we still need to run the above loop to get the
1154 * minimum sizes for children that are not going to fill
1156 size = avail_size - (nvis_children - 1) * private->spacing;
1157 extra = size / nvis_children;
1158 n_extra_widgets = size % nvis_children;
1162 /* Bring children up to size first */
1163 size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
1165 /* Calculate space which hasn't distributed yet,
1166 * and is available for expanding children.
1168 if (nexpand_children > 0)
1170 extra = size / nexpand_children;
1171 n_extra_widgets = size % nexpand_children;
1177 /* Allocate child positions. */
1178 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1180 for (i = 0, children = private->children;
1182 children = children->next)
1184 child = children->data;
1186 /* If widget is not visible, skip it. */
1187 if (!gtk_widget_get_visible (child->widget))
1190 /* If widget is packed differently skip it, but still increment i,
1191 * since widget is visible and will be handled in next loop iteration.
1193 if (child->pack != packing)
1199 if (child->pack == packing)
1201 /* Assign the child's size. */
1202 if (private->homogeneous)
1206 if (n_extra_widgets > 0)
1214 child_size = sizes[i].minimum_size + child->padding * 2;
1216 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
1218 child_size += extra;
1220 if (n_extra_widgets > 0)
1230 child_size = MAX (1, child_size - child->padding * 2);
1234 child_size = sizes[i].minimum_size;
1238 /* Assign the child's position. */
1239 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1240 gtk_widget_get_preferred_height_for_width (child->widget,
1241 child_size, &child_minimum, &child_natural);
1242 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1243 gtk_widget_get_preferred_width_for_height (child->widget,
1244 child_size, &child_minimum, &child_natural);
1247 computed_minimum = MAX (computed_minimum, child_minimum);
1248 computed_natural = MAX (computed_natural, child_natural);
1255 *minimum_size = computed_minimum;
1257 *natural_size = computed_natural;
1261 gtk_box_compute_size_for_orientation (GtkBox *box,
1266 GtkBoxPrivate *private = box->priv;
1268 gint nvis_children = 0;
1269 gint required_size = 0, required_natural = 0, child_size, child_natural;
1270 gint largest_child = 0, largest_natural = 0;
1272 for (children = private->children; children != NULL;
1273 children = children->next)
1275 GtkBoxChild *child = children->data;
1277 if (gtk_widget_get_visible (child->widget))
1280 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1281 gtk_widget_get_preferred_width_for_height (child->widget,
1282 avail_size, &child_size, &child_natural);
1284 gtk_widget_get_preferred_height_for_width (child->widget,
1285 avail_size, &child_size, &child_natural);
1288 child_size += child->padding * 2;
1289 child_natural += child->padding * 2;
1291 if (child_size > largest_child)
1292 largest_child = child_size;
1294 if (child_natural > largest_natural)
1295 largest_natural = child_natural;
1297 required_size += child_size;
1298 required_natural += child_natural;
1304 if (nvis_children > 0)
1306 if (private->homogeneous)
1308 required_size = largest_child * nvis_children;
1309 required_natural = largest_natural * nvis_children;
1312 required_size += (nvis_children - 1) * private->spacing;
1313 required_natural += (nvis_children - 1) * private->spacing;
1317 *minimum_size = required_size;
1320 *natural_size = required_natural;
1324 gtk_box_get_preferred_width_for_height (GtkWidget *widget,
1326 gint *minimum_width,
1327 gint *natural_width)
1329 GtkBox *box = GTK_BOX (widget);
1330 GtkBoxPrivate *private = box->priv;
1332 if (private->orientation == GTK_ORIENTATION_VERTICAL)
1333 gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width);
1335 gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width);
1339 gtk_box_get_preferred_height_for_width (GtkWidget *widget,
1341 gint *minimum_height,
1342 gint *natural_height)
1344 GtkBox *box = GTK_BOX (widget);
1345 GtkBoxPrivate *private = box->priv;
1347 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1348 gtk_box_compute_size_for_opposing_orientation (box, width, minimum_height, natural_height);
1350 gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height);
1355 * @orientation: the box's orientation.
1356 * @spacing: the number of pixels to place by default between children.
1358 * Creates a new #GtkBox.
1360 * Return value: a new #GtkBox.
1365 gtk_box_new (GtkOrientation orientation,
1368 return g_object_new (GTK_TYPE_BOX,
1369 "orientation", orientation,
1375 * gtk_box_pack_start:
1377 * @child: the #GtkWidget to be added to @box
1378 * @expand: %TRUE if the new child is to be given extra space allocated
1379 * to @box. The extra space will be divided evenly between all children
1380 * that use this option
1381 * @fill: %TRUE if space given to @child by the @expand option is
1382 * actually allocated to @child, rather than just padding it. This
1383 * parameter has no effect if @expand is set to %FALSE. A child is
1384 * always allocated the full height of a horizontal #GtkBox and the full width
1385 * of a vertical #GtkBox. This option affects the other dimension
1386 * @padding: extra space in pixels to put between this child and its
1387 * neighbors, over and above the global amount specified by
1388 * #GtkBox:spacing property. If @child is a widget at one of the
1389 * reference ends of @box, then @padding pixels are also put between
1390 * @child and the reference edge of @box
1392 * Adds @child to @box, packed with reference to the start of @box.
1393 * The @child is packed after any other child packed with reference
1394 * to the start of @box.
1397 gtk_box_pack_start (GtkBox *box,
1403 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
1409 * @child: the #GtkWidget to be added to @box
1410 * @expand: %TRUE if the new child is to be given extra space allocated
1411 * to @box. The extra space will be divided evenly between all children
1412 * of @box that use this option
1413 * @fill: %TRUE if space given to @child by the @expand option is
1414 * actually allocated to @child, rather than just padding it. This
1415 * parameter has no effect if @expand is set to %FALSE. A child is
1416 * always allocated the full height of a horizontal #GtkBox and the full width
1417 * of a vertical #GtkBox. This option affects the other dimension
1418 * @padding: extra space in pixels to put between this child and its
1419 * neighbors, over and above the global amount specified by
1420 * #GtkBox:spacing property. If @child is a widget at one of the
1421 * reference ends of @box, then @padding pixels are also put between
1422 * @child and the reference edge of @box
1424 * Adds @child to @box, packed with reference to the end of @box.
1425 * The @child is packed after (away from end of) any other child
1426 * packed with reference to the end of @box.
1429 gtk_box_pack_end (GtkBox *box,
1435 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
1439 * gtk_box_set_homogeneous:
1441 * @homogeneous: a boolean value, %TRUE to create equal allotments,
1442 * %FALSE for variable allotments
1444 * Sets the #GtkBox:homogeneous property of @box, controlling
1445 * whether or not all children of @box are given equal space
1449 gtk_box_set_homogeneous (GtkBox *box,
1450 gboolean homogeneous)
1452 GtkBoxPrivate *private;
1454 g_return_if_fail (GTK_IS_BOX (box));
1456 private = box->priv;
1458 if ((homogeneous ? TRUE : FALSE) != private->homogeneous)
1460 private->homogeneous = homogeneous ? TRUE : FALSE;
1461 g_object_notify (G_OBJECT (box), "homogeneous");
1462 gtk_widget_queue_resize (GTK_WIDGET (box));
1467 * gtk_box_get_homogeneous:
1470 * Returns whether the box is homogeneous (all children are the
1471 * same size). See gtk_box_set_homogeneous().
1473 * Return value: %TRUE if the box is homogeneous.
1476 gtk_box_get_homogeneous (GtkBox *box)
1478 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1480 return box->priv->homogeneous;
1484 * gtk_box_set_spacing:
1486 * @spacing: the number of pixels to put between children
1488 * Sets the #GtkBox:spacing property of @box, which is the
1489 * number of pixels to place between children of @box.
1492 gtk_box_set_spacing (GtkBox *box,
1495 GtkBoxPrivate *private;
1497 g_return_if_fail (GTK_IS_BOX (box));
1499 private = box->priv;
1501 if (spacing != private->spacing)
1503 private->spacing = spacing;
1504 _gtk_box_set_spacing_set (box, TRUE);
1506 g_object_notify (G_OBJECT (box), "spacing");
1508 gtk_widget_queue_resize (GTK_WIDGET (box));
1513 * gtk_box_get_spacing:
1516 * Gets the value set by gtk_box_set_spacing().
1518 * Return value: spacing between children
1521 gtk_box_get_spacing (GtkBox *box)
1523 g_return_val_if_fail (GTK_IS_BOX (box), 0);
1525 return box->priv->spacing;
1529 _gtk_box_set_spacing_set (GtkBox *box,
1530 gboolean spacing_set)
1532 GtkBoxPrivate *private;
1534 g_return_if_fail (GTK_IS_BOX (box));
1536 private = box->priv;
1538 private->spacing_set = spacing_set ? TRUE : FALSE;
1542 _gtk_box_get_spacing_set (GtkBox *box)
1544 GtkBoxPrivate *private;
1546 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1548 private = box->priv;
1550 return private->spacing_set;
1554 * gtk_box_reorder_child:
1556 * @child: the #GtkWidget to move
1557 * @position: the new position for @child in the list of children
1558 * of @box, starting from 0. If negative, indicates the end of
1561 * Moves @child to a new @position in the list of @box children.
1562 * The list is the <structfield>children</structfield> field of
1563 * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START
1564 * as well as widgets packed #GTK_PACK_END, in the order that these
1565 * widgets were added to @box.
1567 * A widget's position in the @box children list determines where
1568 * the widget is packed into @box. A child widget at some position
1569 * in the list will be packed just after all other widgets of the
1570 * same packing type that appear earlier in the list.
1573 gtk_box_reorder_child (GtkBox *box,
1577 GtkBoxPrivate *priv;
1580 GtkBoxChild *child_info = NULL;
1583 g_return_if_fail (GTK_IS_BOX (box));
1584 g_return_if_fail (GTK_IS_WIDGET (child));
1588 old_link = priv->children;
1592 child_info = old_link->data;
1593 if (child_info->widget == child)
1596 old_link = old_link->next;
1600 g_return_if_fail (old_link != NULL);
1602 if (position == old_position)
1605 priv->children = g_list_delete_link (priv->children, old_link);
1610 new_link = g_list_nth (priv->children, position);
1612 priv->children = g_list_insert_before (priv->children, new_link, child_info);
1614 gtk_widget_child_notify (child, "position");
1615 if (gtk_widget_get_visible (child)
1616 && gtk_widget_get_visible (GTK_WIDGET (box)))
1618 gtk_box_invalidate_order (box);
1619 gtk_widget_queue_resize (child);
1624 * gtk_box_query_child_packing:
1626 * @child: the #GtkWidget of the child to query
1627 * @expand: (out): pointer to return location for #GtkBox:expand child
1629 * @fill: (out): pointer to return location for #GtkBox:fill child
1631 * @padding: (out): pointer to return location for #GtkBox:padding
1633 * @pack_type: (out): pointer to return location for #GtkBox:pack-type
1636 * Obtains information about how @child is packed into @box.
1639 gtk_box_query_child_packing (GtkBox *box,
1644 GtkPackType *pack_type)
1646 GtkBoxPrivate *private;
1648 GtkBoxChild *child_info = NULL;
1650 g_return_if_fail (GTK_IS_BOX (box));
1651 g_return_if_fail (GTK_IS_WIDGET (child));
1653 private = box->priv;
1655 list = private->children;
1658 child_info = list->data;
1659 if (child_info->widget == child)
1668 *expand = child_info->expand;
1670 *fill = child_info->fill;
1672 *padding = child_info->padding;
1674 *pack_type = child_info->pack;
1679 * gtk_box_set_child_packing:
1681 * @child: the #GtkWidget of the child to set
1682 * @expand: the new value of the #GtkBox:expand child property
1683 * @fill: the new value of the #GtkBox:fill child property
1684 * @padding: the new value of the #GtkBox:padding child property
1685 * @pack_type: the new value of the #GtkBox:pack-type child property
1687 * Sets the way @child is packed into @box.
1690 gtk_box_set_child_packing (GtkBox *box,
1695 GtkPackType pack_type)
1697 GtkBoxPrivate *private;
1699 GtkBoxChild *child_info = NULL;
1701 g_return_if_fail (GTK_IS_BOX (box));
1702 g_return_if_fail (GTK_IS_WIDGET (child));
1704 private = box->priv;
1706 list = private->children;
1709 child_info = list->data;
1710 if (child_info->widget == child)
1716 gtk_widget_freeze_child_notify (child);
1721 expanded = expand != FALSE;
1723 /* avoid setting expand if unchanged, since queue_compute_expand
1724 * can be expensive-ish
1726 if (child_info->expand != expanded)
1728 child_info->expand = expand != FALSE;
1729 gtk_widget_queue_compute_expand (GTK_WIDGET (box));
1730 gtk_widget_child_notify (child, "expand");
1733 child_info->fill = fill != FALSE;
1734 gtk_widget_child_notify (child, "fill");
1735 child_info->padding = padding;
1736 gtk_widget_child_notify (child, "padding");
1737 if (pack_type != GTK_PACK_END)
1738 pack_type = GTK_PACK_START;
1739 if (child_info->pack != pack_type)
1741 child_info->pack = GTK_PACK_END;
1742 gtk_widget_child_notify (child, "pack-type");
1743 gtk_box_invalidate_order (box);
1746 if (gtk_widget_get_visible (child)
1747 && gtk_widget_get_visible (GTK_WIDGET (box)))
1748 gtk_widget_queue_resize (child);
1750 gtk_widget_thaw_child_notify (child);
1754 _gtk_box_set_old_defaults (GtkBox *box)
1756 GtkBoxPrivate *private;
1758 g_return_if_fail (GTK_IS_BOX (box));
1760 private = box->priv;
1762 private->default_expand = TRUE;
1766 gtk_box_add (GtkContainer *container,
1769 GtkBoxPrivate *priv = GTK_BOX (container)->priv;
1771 gtk_box_pack_start (GTK_BOX (container), widget,
1772 priv->default_expand,
1778 gtk_box_remove (GtkContainer *container,
1781 GtkBox *box = GTK_BOX (container);
1782 GtkBoxPrivate *priv = box->priv;
1786 children = priv->children;
1789 child = children->data;
1791 if (child->widget == widget)
1793 gboolean was_visible;
1795 was_visible = gtk_widget_get_visible (widget);
1796 gtk_widget_unparent (widget);
1798 g_signal_handlers_disconnect_by_func (widget,
1799 box_child_visibility_notify_cb,
1802 priv->children = g_list_remove_link (priv->children, children);
1803 g_list_free (children);
1806 /* queue resize regardless of gtk_widget_get_visible (container),
1807 * since that's what is needed by toplevels.
1811 gtk_box_invalidate_order (box);
1812 gtk_widget_queue_resize (GTK_WIDGET (container));
1818 children = children->next;
1823 gtk_box_forall (GtkContainer *container,
1824 gboolean include_internals,
1825 GtkCallback callback,
1826 gpointer callback_data)
1828 GtkBox *box = GTK_BOX (container);
1829 GtkBoxPrivate *priv = box->priv;
1833 children = priv->children;
1836 child = children->data;
1837 children = children->next;
1839 if (child->pack == GTK_PACK_START)
1840 (* callback) (child->widget, callback_data);
1843 children = g_list_last (priv->children);
1846 child = children->data;
1847 children = children->prev;
1849 if (child->pack == GTK_PACK_END)
1850 (* callback) (child->widget, callback_data);
1855 _gtk_box_get_children (GtkBox *box)
1857 GtkBoxPrivate *priv;
1860 GList *retval = NULL;
1862 g_return_val_if_fail (GTK_IS_BOX (box), NULL);
1866 children = priv->children;
1869 child = children->data;
1870 children = children->next;
1872 retval = g_list_prepend (retval, child->widget);
1875 return g_list_reverse (retval);