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: Base class for box containers
31 * @See_also:i #GtkHBox, #GtkVBox, #GtkFrame, #GtkTable, #GtkLayout
33 * GtkBox is an widget which encapsulates functionality for a
34 * particular kind of container, one that organizes a variable number of
35 * widgets into a rectangular area. GtkBox has a number of derived
36 * classes, e.g. #GtkHBox and #GtkVBox.
38 * The rectangular area of a GtkBox is organized into either a single row
39 * or a single column of child widgets depending upon whether the box is
40 * of type #GtkHBox or #GtkVBox, respectively. Thus, all children of a
41 * GtkBox are allocated one dimension in common, which is the height of a
42 * row, or the width of a column.
44 * GtkBox uses a notion of <emphasis>packing</emphasis>. Packing
45 * refers to adding widgets with reference to a particular position in a
46 * #GtkContainer. For a GtkBox, there are two reference positions: the
47 * <emphasis>start</emphasis> and the <emphasis>end</emphasis> of the box.
48 * For a #GtkVBox, the start is defined as the top of the box and the end is
49 * defined as the bottom. For a #GtkHBox the start is defined as the
50 * left side and the end is defined as the right side.
52 * Use repeated calls to gtk_box_pack_start() to pack widgets into a
53 * GtkBox from start to end. Use gtk_box_pack_end() to add widgets from
54 * end to start. You may intersperse these calls and add widgets from
55 * both ends of the same GtkBox.
57 * Because GtkBox is a #GtkContainer, you may also use
58 * gtk_container_add() to insert widgets into the box, and they will be
59 * packed with the default values for #GtkBox:expand and #GtkBox:fill.
60 * Use gtk_container_remove() to remove widgets from the GtkBox.
62 * Use gtk_box_set_homogeneous() to specify whether or not all children
63 * of the GtkBox are forced to get the same amount of space.
65 * Use gtk_box_set_spacing() to determine how much space will be
66 * minimally placed between all children in the GtkBox.
68 * Use gtk_box_reorder_child() to move a GtkBox child to a different
71 * Use gtk_box_set_child_packing() to reset the #GtkBox:expand,
72 * #GtkBox:fill and #GtkBox:padding child properties.
73 * Use gtk_box_query_child_packing() to query these fields.
79 #include "gtkorientable.h"
80 #include "gtkextendedlayout.h"
81 #include "gtkprivate.h"
102 typedef struct _GtkBoxPrivate GtkBoxPrivate;
104 struct _GtkBoxPrivate
106 GtkOrientation orientation;
107 guint default_expand : 1;
108 guint spacing_set : 1;
111 #define GTK_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_BOX, GtkBoxPrivate))
113 typedef struct _GtkBoxDesiredSizes GtkBoxDesiredSizes;
114 typedef struct _GtkBoxSpreading GtkBoxSpreading;
116 struct _GtkBoxDesiredSizes
122 struct _GtkBoxSpreading
128 static void gtk_box_size_allocate (GtkWidget *widget,
129 GtkAllocation *allocation);
131 static void gtk_box_set_property (GObject *object,
135 static void gtk_box_get_property (GObject *object,
140 static void gtk_box_add (GtkContainer *container,
142 static void gtk_box_remove (GtkContainer *container,
144 static void gtk_box_forall (GtkContainer *container,
145 gboolean include_internals,
146 GtkCallback callback,
147 gpointer callback_data);
148 static void gtk_box_set_child_property (GtkContainer *container,
153 static void gtk_box_get_child_property (GtkContainer *container,
158 static GType gtk_box_child_type (GtkContainer *container);
161 static void gtk_box_extended_layout_init (GtkExtendedLayoutIface *iface);
162 static gboolean gtk_box_is_height_for_width (GtkExtendedLayout *layout);
163 static void gtk_box_get_desired_width (GtkExtendedLayout *layout,
166 static void gtk_box_get_desired_height (GtkExtendedLayout *layout,
169 static void gtk_box_get_width_for_height (GtkExtendedLayout *layout,
172 gint *natural_width);
173 static void gtk_box_get_height_for_width (GtkExtendedLayout *layout,
175 gint *minimum_height,
176 gint *natural_height);
178 static GtkExtendedLayoutIface *parent_extended_layout_iface;
180 G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
181 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
183 G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
184 gtk_box_extended_layout_init));
187 gtk_box_class_init (GtkBoxClass *class)
189 GObjectClass *object_class = G_OBJECT_CLASS (class);
190 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
191 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
193 object_class->set_property = gtk_box_set_property;
194 object_class->get_property = gtk_box_get_property;
196 widget_class->size_allocate = gtk_box_size_allocate;
198 container_class->add = gtk_box_add;
199 container_class->remove = gtk_box_remove;
200 container_class->forall = gtk_box_forall;
201 container_class->child_type = gtk_box_child_type;
202 container_class->set_child_property = gtk_box_set_child_property;
203 container_class->get_child_property = gtk_box_get_child_property;
205 g_object_class_override_property (object_class,
209 g_object_class_install_property (object_class,
211 g_param_spec_int ("spacing",
213 P_("The amount of space between children"),
217 GTK_PARAM_READWRITE));
219 g_object_class_install_property (object_class,
221 g_param_spec_boolean ("homogeneous",
223 P_("Whether the children should all be the same size"),
225 GTK_PARAM_READWRITE));
230 * Whether the child should receive extra space when the parent grows.
232 * Note that the default value for this property is %FALSE for GtkBox,
233 * but #GtkHBox, #GtkVBox and other subclasses use the old default
236 gtk_container_class_install_child_property (container_class,
238 g_param_spec_boolean ("expand",
240 P_("Whether the child should receive extra space when the parent grows"),
242 GTK_PARAM_READWRITE));
247 * Whether the child should receive extra space when the parent grows.
249 * Note that the default value for this property is %FALSE for GtkBox,
250 * but #GtkHBox, #GtkVBox and other subclasses use the old default
253 gtk_container_class_install_child_property (container_class,
255 g_param_spec_boolean ("fill",
257 P_("Whether extra space given to the child should be allocated to the child or used as padding"),
259 GTK_PARAM_READWRITE));
261 gtk_container_class_install_child_property (container_class,
263 g_param_spec_uint ("padding",
265 P_("Extra space to put between the child and its neighbors, in pixels"),
267 GTK_PARAM_READWRITE));
268 gtk_container_class_install_child_property (container_class,
269 CHILD_PROP_PACK_TYPE,
270 g_param_spec_enum ("pack-type",
272 P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
273 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
274 GTK_PARAM_READWRITE));
275 gtk_container_class_install_child_property (container_class,
277 g_param_spec_int ("position",
279 P_("The index of the child in the parent"),
281 GTK_PARAM_READWRITE));
283 g_type_class_add_private (object_class, sizeof (GtkBoxPrivate));
287 gtk_box_init (GtkBox *box)
289 GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (box);
291 gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
292 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE);
294 box->children = NULL;
296 box->homogeneous = FALSE;
298 private->orientation = GTK_ORIENTATION_HORIZONTAL;
299 private->default_expand = FALSE;
300 private->spacing_set = FALSE;
304 gtk_box_set_property (GObject *object,
309 GtkBox *box = GTK_BOX (object);
310 GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (box);
314 case PROP_ORIENTATION:
315 private->orientation = g_value_get_enum (value);
316 gtk_widget_queue_resize (GTK_WIDGET (box));
319 gtk_box_set_spacing (box, g_value_get_int (value));
321 case PROP_HOMOGENEOUS:
322 gtk_box_set_homogeneous (box, g_value_get_boolean (value));
325 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
331 gtk_box_get_property (GObject *object,
336 GtkBox *box = GTK_BOX (object);
337 GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (box);
341 case PROP_ORIENTATION:
342 g_value_set_enum (value, private->orientation);
345 g_value_set_int (value, box->spacing);
347 case PROP_HOMOGENEOUS:
348 g_value_set_boolean (value, box->homogeneous);
351 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
358 count_expand_children (GtkBox *box, gint *visible_children, gint *expand_children)
363 *visible_children = *expand_children = 0;
365 for (children = box->children; children; children = children->next)
367 child = children->data;
369 if (gtk_widget_get_visible (child->widget))
371 *visible_children += 1;
373 *expand_children += 1;
379 gtk_box_compare_gap (gconstpointer p1,
383 GtkBoxDesiredSizes *sizes = data;
384 const GtkBoxSpreading *c1 = p1;
385 const GtkBoxSpreading *c2 = p2;
387 const gint d1 = MAX (sizes[c1->index].natural_size -
388 sizes[c1->index].minimum_size,
390 const gint d2 = MAX (sizes[c2->index].natural_size -
391 sizes[c2->index].minimum_size,
394 gint delta = (d2 - d1);
397 delta = (c2->index - c1->index);
403 gtk_box_size_allocate (GtkWidget *widget,
404 GtkAllocation *allocation)
406 GtkBox *box = GTK_BOX (widget);
407 GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (box);
411 gint nexpand_children;
413 widget->allocation = *allocation;
415 count_expand_children (box, &nvis_children, &nexpand_children);
417 if (nvis_children > 0)
419 gint border_width = GTK_CONTAINER (box)->border_width;
420 GtkTextDirection direction = gtk_widget_get_direction (widget);
421 GtkAllocation child_allocation;
422 GtkBoxSpreading *spreading = g_newa (GtkBoxSpreading, nvis_children);
423 GtkBoxDesiredSizes *sizes = g_newa (GtkBoxDesiredSizes, nvis_children);
429 gint x = 0, y = 0, i;
432 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
433 size = allocation->width - border_width * 2 - (nvis_children - 1) * box->spacing;
435 size = allocation->height - border_width * 2 - (nvis_children - 1) * box->spacing;
437 /* Retrieve desired size for visible children */
439 children = box->children;
442 child = children->data;
443 children = children->next;
445 if (gtk_widget_get_visible (child->widget))
447 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
448 gtk_extended_layout_get_width_for_height (GTK_EXTENDED_LAYOUT (child->widget),
450 &sizes[i].minimum_size,
451 &sizes[i].natural_size);
453 gtk_extended_layout_get_height_for_width (GTK_EXTENDED_LAYOUT (child->widget),
455 &sizes[i].minimum_size,
456 &sizes[i].natural_size);
459 /* Assert the api is working properly */
460 if (sizes[i].minimum_size < 0)
461 g_error ("GtkBox child %s minimum %s: %d < 0 for %s %d",
462 gtk_widget_get_name (GTK_WIDGET (child->widget)),
463 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
464 sizes[i].minimum_size,
465 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
466 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
468 if (sizes[i].natural_size < sizes[i].minimum_size)
469 g_error ("GtkBox child %s natural %s: %d < minimum %d for %s %d",
470 gtk_widget_get_name (GTK_WIDGET (child->widget)),
471 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
472 sizes[i].natural_size,
473 sizes[i].minimum_size,
474 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
475 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
477 size -= sizes[i].minimum_size;
478 size -= child->padding * 2;
480 spreading[i].index = i;
481 spreading[i].child = child;
487 if (box->homogeneous)
489 /* If were homogenous we still need to run the above loop to get the minimum sizes
490 * for children that are not going to fill
492 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
493 size = allocation->width - border_width * 2 - (nvis_children - 1) * box->spacing;
495 size = allocation->height - border_width * 2 - (nvis_children - 1) * box->spacing;
497 extra = size / nvis_children;
502 /* Distribute the container's extra space c_gap. We want to assign
503 * this space such that the sum of extra space assigned to children
504 * (c^i_gap) is equal to c_cap. The case that there's not enough
505 * space for all children to take their natural size needs some
506 * attention. The goals we want to achieve are:
508 * a) Maximize number of children taking their natural size.
509 * b) The allocated size of children should be a continuous
510 * function of c_gap. That is, increasing the container size by
511 * one pixel should never make drastic changes in the distribution.
512 * c) If child i takes its natural size and child j doesn't,
513 * child j should have received at least as much gap as child i.
515 * The following code distributes the additional space by following
519 /* Sort descending by gap and position. */
521 g_qsort_with_data (spreading,
522 nvis_children, sizeof (GtkBoxSpreading),
523 gtk_box_compare_gap, sizes);
525 /* Distribute available space.
526 * This master piece of a loop was conceived by Behdad Esfahbod.
528 for (i = nvis_children - 1; i >= 0; --i)
530 /* Divide remaining space by number of remaining children.
531 * Sort order and reducing remaining space by assigned space
532 * ensures that space is distributed equally.
534 gint glue = (size + i) / (i + 1);
535 gint gap = sizes[spreading[i].index].natural_size
536 - sizes[spreading[i].index].minimum_size;
538 extra = MIN (glue, gap);
539 sizes[spreading[i].index].minimum_size += extra;
544 /* Calculate space which hasn't distributed yet,
545 * and is available for expanding children.
547 if (nexpand_children > 0)
548 extra = size / nexpand_children;
553 /* Allocate child positions. */
555 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
557 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
559 child_allocation.y = allocation->y + border_width;
560 child_allocation.height = MAX (1, allocation->height - border_width * 2);
561 if (packing == GTK_PACK_START)
562 x = allocation->x + border_width;
564 x = allocation->x + allocation->width - border_width;
568 child_allocation.x = allocation->x + border_width;
569 child_allocation.width = MAX (1, allocation->width - border_width * 2);
570 if (packing == GTK_PACK_START)
571 y = allocation->y + border_width;
573 y = allocation->y + allocation->height - border_width;
577 children = box->children;
580 child = children->data;
581 children = children->next;
583 if (gtk_widget_get_visible (child->widget))
585 if (child->pack == packing)
587 /* Assign the child's size. */
588 if (box->homogeneous)
590 if (nvis_children == 1)
600 child_size = sizes[i].minimum_size + child->padding * 2;
604 if (nexpand_children == 1)
609 nexpand_children -= 1;
614 /* Assign the child's position. */
615 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
619 child_allocation.width = MAX (1, child_size - child->padding * 2);
620 child_allocation.x = x + child->padding;
624 child_allocation.width = sizes[i].minimum_size;
625 child_allocation.x = x + (child_size - child_allocation.width) / 2;
628 if (packing == GTK_PACK_START)
630 x += child_size + box->spacing;
634 x -= child_size + box->spacing;
636 child_allocation.x -= child_size;
639 if (direction == GTK_TEXT_DIR_RTL)
640 child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
643 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
647 child_allocation.height = MAX (1, child_size - child->padding * 2);
648 child_allocation.y = y + child->padding;
652 child_allocation.height = sizes[i].minimum_size;
653 child_allocation.y = y + (child_size - child_allocation.height) / 2;
656 if (packing == GTK_PACK_START)
658 y += child_size + box->spacing;
662 y -= child_size + box->spacing;
664 child_allocation.y -= child_size;
667 gtk_widget_size_allocate (child->widget, &child_allocation);
678 gtk_box_child_type (GtkContainer *container)
680 return GTK_TYPE_WIDGET;
684 gtk_box_set_child_property (GtkContainer *container,
693 GtkPackType pack_type = 0;
695 if (property_id != CHILD_PROP_POSITION)
696 gtk_box_query_child_packing (GTK_BOX (container),
704 case CHILD_PROP_EXPAND:
705 gtk_box_set_child_packing (GTK_BOX (container),
707 g_value_get_boolean (value),
712 case CHILD_PROP_FILL:
713 gtk_box_set_child_packing (GTK_BOX (container),
716 g_value_get_boolean (value),
720 case CHILD_PROP_PADDING:
721 gtk_box_set_child_packing (GTK_BOX (container),
725 g_value_get_uint (value),
728 case CHILD_PROP_PACK_TYPE:
729 gtk_box_set_child_packing (GTK_BOX (container),
734 g_value_get_enum (value));
736 case CHILD_PROP_POSITION:
737 gtk_box_reorder_child (GTK_BOX (container),
739 g_value_get_int (value));
742 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
748 gtk_box_get_child_property (GtkContainer *container,
754 gboolean expand = FALSE;
755 gboolean fill = FALSE;
757 GtkPackType pack_type = 0;
760 if (property_id != CHILD_PROP_POSITION)
761 gtk_box_query_child_packing (GTK_BOX (container),
770 case CHILD_PROP_EXPAND:
771 g_value_set_boolean (value, expand);
773 case CHILD_PROP_FILL:
774 g_value_set_boolean (value, fill);
776 case CHILD_PROP_PADDING:
777 g_value_set_uint (value, padding);
779 case CHILD_PROP_PACK_TYPE:
780 g_value_set_enum (value, pack_type);
782 case CHILD_PROP_POSITION:
784 for (list = GTK_BOX (container)->children; list; list = list->next)
786 GtkBoxChild *child_entry;
788 child_entry = list->data;
789 if (child_entry->widget == child)
793 g_value_set_int (value, list ? i : -1);
796 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
802 gtk_box_pack (GtkBox *box,
807 GtkPackType pack_type)
809 GtkBoxChild *child_info;
811 g_return_if_fail (GTK_IS_BOX (box));
812 g_return_if_fail (GTK_IS_WIDGET (child));
813 g_return_if_fail (child->parent == NULL);
815 child_info = g_new (GtkBoxChild, 1);
816 child_info->widget = child;
817 child_info->padding = padding;
818 child_info->expand = expand ? TRUE : FALSE;
819 child_info->fill = fill ? TRUE : FALSE;
820 child_info->pack = pack_type;
821 child_info->is_secondary = FALSE;
823 box->children = g_list_append (box->children, child_info);
825 gtk_widget_freeze_child_notify (child);
827 gtk_widget_set_parent (child, GTK_WIDGET (box));
829 gtk_widget_child_notify (child, "expand");
830 gtk_widget_child_notify (child, "fill");
831 gtk_widget_child_notify (child, "padding");
832 gtk_widget_child_notify (child, "pack-type");
833 gtk_widget_child_notify (child, "position");
834 gtk_widget_thaw_child_notify (child);
839 gtk_box_extended_layout_init (GtkExtendedLayoutIface *iface)
841 parent_extended_layout_iface = g_type_interface_peek_parent (iface);
843 iface->is_height_for_width = gtk_box_is_height_for_width;
844 iface->get_desired_width = gtk_box_get_desired_width;
845 iface->get_desired_height = gtk_box_get_desired_height;
846 iface->get_height_for_width = gtk_box_get_height_for_width;
847 iface->get_width_for_height = gtk_box_get_width_for_height;
851 gtk_box_is_height_for_width (GtkExtendedLayout *layout)
853 GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (layout);
855 return (private->orientation == GTK_ORIENTATION_VERTICAL);
859 gtk_box_get_desired_size (GtkExtendedLayout *layout,
860 GtkOrientation orientation,
865 GtkBoxPrivate *private;
869 gint minimum, natural;
871 box = GTK_BOX (layout);
872 private = GTK_BOX_GET_PRIVATE (box);
873 border_width = GTK_CONTAINER (box)->border_width;
875 minimum = natural = 0;
879 for (children = box->children; children; children = children->next)
881 GtkBoxChild *child = children->data;
883 if (gtk_widget_get_visible (child->widget))
885 gint child_minimum, child_natural;
887 if (orientation == GTK_ORIENTATION_HORIZONTAL)
888 gtk_extended_layout_get_desired_width (GTK_EXTENDED_LAYOUT (child->widget),
889 &child_minimum, &child_natural);
891 gtk_extended_layout_get_desired_height (GTK_EXTENDED_LAYOUT (child->widget),
892 &child_minimum, &child_natural);
894 if (private->orientation == orientation)
896 if (box->homogeneous)
900 largest = child_minimum + child->padding * 2;
901 minimum = MAX (minimum, largest);
903 largest = child_natural + child->padding * 2;
904 natural = MAX (natural, largest);
908 minimum += child_minimum + child->padding * 2;
909 natural += child_natural + child->padding * 2;
914 /* The biggest mins and naturals in the opposing orientation */
915 minimum = MAX (minimum, child_minimum);
916 natural = MAX (natural, child_natural);
923 if (nvis_children > 0 && private->orientation == orientation)
925 if (box->homogeneous)
927 minimum *= nvis_children;
928 natural *= nvis_children;
930 minimum += (nvis_children - 1) * box->spacing;
931 natural += (nvis_children - 1) * box->spacing;
934 minimum += border_width * 2;
935 natural += border_width * 2;
938 *minimum_size = minimum;
941 *natural_size = natural;
945 gtk_box_get_desired_width (GtkExtendedLayout *layout,
949 gtk_box_get_desired_size (layout, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
953 gtk_box_get_desired_height (GtkExtendedLayout *layout,
957 gtk_box_get_desired_size (layout, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
961 gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
966 GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (box);
970 gint nexpand_children;
971 gint computed_minimum = 0, computed_natural = 0;
972 gint border_width = GTK_CONTAINER (box)->border_width;
974 count_expand_children (box, &nvis_children, &nexpand_children);
976 if (nvis_children > 0)
978 GtkBoxSpreading *spreading = g_newa (GtkBoxSpreading, nvis_children);
979 GtkBoxDesiredSizes *sizes = g_newa (GtkBoxDesiredSizes, nvis_children);
983 gint child_size, child_minimum, child_natural;
985 size = avail_size - border_width * 2 - (nvis_children - 1) * box->spacing;
987 /* Retrieve desired size for visible children */
988 for (i = 0, children = box->children; children; children = children->next)
990 child = children->data;
992 if (gtk_widget_get_visible (child->widget))
994 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
995 gtk_extended_layout_get_desired_width (GTK_EXTENDED_LAYOUT (child->widget),
996 &sizes[i].minimum_size,
997 &sizes[i].natural_size);
999 gtk_extended_layout_get_desired_height (GTK_EXTENDED_LAYOUT (child->widget),
1000 &sizes[i].minimum_size,
1001 &sizes[i].natural_size);
1003 /* Assert the api is working properly */
1004 if (sizes[i].minimum_size < 0)
1005 g_error ("GtkBox child %s minimum %s: %d < 0",
1006 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1007 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1008 sizes[i].minimum_size);
1010 if (sizes[i].natural_size < sizes[i].minimum_size)
1011 g_error ("GtkBox child %s natural %s: %d < minimum %d",
1012 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1013 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1014 sizes[i].natural_size,
1015 sizes[i].minimum_size);
1017 size -= sizes[i].minimum_size;
1018 size -= child->padding * 2;
1020 spreading[i].index = i;
1021 spreading[i].child = child;
1027 if (box->homogeneous)
1029 /* If were homogenous we still need to run the above loop to get the minimum sizes
1030 * for children that are not going to fill
1032 size = avail_size - border_width * 2 - (nvis_children - 1) * box->spacing;
1033 extra = size / nvis_children;
1038 /* Distribute the container's extra space c_gap. We want to assign
1039 * this space such that the sum of extra space assigned to children
1040 * (c^i_gap) is equal to c_cap. The case that there's not enough
1041 * space for all children to take their natural size needs some
1042 * attention. The goals we want to achieve are:
1044 * a) Maximize number of children taking their natural size.
1045 * b) The allocated size of children should be a continuous
1046 * function of c_gap. That is, increasing the container size by
1047 * one pixel should never make drastic changes in the distribution.
1048 * c) If child i takes its natural size and child j doesn't,
1049 * child j should have received at least as much gap as child i.
1051 * The following code distributes the additional space by following
1055 /* Sort descending by gap and position. */
1057 g_qsort_with_data (spreading,
1058 nvis_children, sizeof (GtkBoxSpreading),
1059 gtk_box_compare_gap, sizes);
1061 /* Distribute available space.
1062 * This master piece of a loop was conceived by Behdad Esfahbod.
1064 for (i = nvis_children - 1; i >= 0; --i)
1066 /* Divide remaining space by number of remaining children.
1067 * Sort order and reducing remaining space by assigned space
1068 * ensures that space is distributed equally.
1070 gint glue = (size + i) / (i + 1);
1071 gint gap = sizes[spreading[i].index].natural_size
1072 - sizes[spreading[i].index].minimum_size;
1074 extra = MIN (glue, gap);
1075 sizes[spreading[i].index].minimum_size += extra;
1080 /* Calculate space which hasn't distributed yet,
1081 * and is available for expanding children.
1083 if (nexpand_children > 0)
1084 extra = size / nexpand_children;
1089 /* Allocate child positions. */
1090 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1092 for (i = 0, children = box->children; children; children = children->next)
1094 child = children->data;
1096 if (gtk_widget_get_visible (child->widget))
1098 if (child->pack == packing)
1100 /* Assign the child's size. */
1101 if (box->homogeneous)
1103 if (nvis_children == 1)
1113 child_size = sizes[i].minimum_size + child->padding * 2;
1117 if (nexpand_children == 1)
1120 child_size += extra;
1122 nexpand_children -= 1;
1129 child_size = MAX (1, child_size - child->padding * 2);
1133 child_size = sizes[i].minimum_size;
1137 /* Assign the child's position. */
1138 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1139 gtk_extended_layout_get_height_for_width (GTK_EXTENDED_LAYOUT (child->widget),
1140 child_size, &child_minimum, &child_natural);
1141 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1142 gtk_extended_layout_get_width_for_height (GTK_EXTENDED_LAYOUT (child->widget),
1143 child_size, &child_minimum, &child_natural);
1146 computed_minimum = MAX (computed_minimum, child_minimum);
1147 computed_natural = MAX (computed_natural, child_natural);
1155 computed_minimum += border_width * 2;
1156 computed_natural += border_width * 2;
1159 *minimum_size = computed_minimum;
1161 *natural_size = computed_natural;
1165 gtk_box_compute_size_for_orientation (GtkBox *box,
1170 GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (box);
1172 gint nvis_children = 0;
1173 gint required_size = 0, required_natural = 0, child_size, child_natural;
1174 gint largest_child = 0, largest_natural = 0;
1176 avail_size -= GTK_CONTAINER (box)->border_width * 2;
1178 for (children = box->children; children != NULL;
1179 children = children->next, nvis_children++)
1181 GtkBoxChild *child = children->data;
1183 if (gtk_widget_get_visible (child->widget))
1186 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1187 gtk_extended_layout_get_width_for_height (GTK_EXTENDED_LAYOUT (child->widget),
1188 avail_size, &child_size, &child_natural);
1190 gtk_extended_layout_get_height_for_width (GTK_EXTENDED_LAYOUT (child->widget),
1191 avail_size, &child_size, &child_natural);
1194 child_size += child->padding * 2;
1195 child_natural += child->padding * 2;
1197 if (child_size > largest_child)
1198 largest_child = child_size;
1200 if (child_natural > largest_natural)
1201 largest_natural = child_natural;
1203 required_size += child_size;
1204 required_natural += child_natural;
1208 if (nvis_children > 0)
1210 if (box->homogeneous)
1212 required_size = largest_child * nvis_children;
1213 required_natural = largest_natural * nvis_children;
1216 required_size += (nvis_children - 1) * box->spacing;
1217 required_natural += (nvis_children - 1) * box->spacing;
1220 required_size += GTK_CONTAINER (box)->border_width * 2;
1221 required_natural += GTK_CONTAINER (box)->border_width * 2;
1224 *minimum_size = required_size;
1227 *natural_size = required_natural;
1231 gtk_box_get_width_for_height (GtkExtendedLayout *layout,
1233 gint *minimum_width,
1234 gint *natural_width)
1236 GtkBox *box = GTK_BOX (layout);
1237 GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (layout);
1239 if (private->orientation == GTK_ORIENTATION_VERTICAL)
1240 gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width);
1242 gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width);
1246 gtk_box_get_height_for_width (GtkExtendedLayout *layout,
1248 gint *minimum_height,
1249 gint *natural_height)
1251 GtkBox *box = GTK_BOX (layout);
1252 GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (layout);
1254 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1255 gtk_box_compute_size_for_opposing_orientation (box, width, minimum_height, natural_height);
1257 gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height);
1262 * @orientation: the box' orientation.
1263 * @homogeneous: %TRUE if all children are to be given equal space allocations.
1264 * @spacing: the number of pixels to place by default between children.
1266 * Creates a new #GtkBox.
1268 * Return value: a new #GtkBox.
1273 gtk_box_new (GtkOrientation orientation,
1274 gboolean homogeneous,
1277 return g_object_new (GTK_TYPE_BOX,
1278 "orientation", orientation,
1280 "homogeneous", homogeneous ? TRUE : FALSE,
1285 * gtk_box_pack_start:
1287 * @child: the #GtkWidget to be added to @box
1288 * @expand: %TRUE if the new child is to be given extra space allocated to
1289 * @box. The extra space will be divided evenly between all children of
1290 * @box that use this option
1291 * @fill: %TRUE if space given to @child by the @expand option is
1292 * actually allocated to @child, rather than just padding it. This
1293 * parameter has no effect if @expand is set to %FALSE. A child is
1294 * always allocated the full height of a #GtkHBox and the full width
1295 * of a #GtkVBox. This option affects the other dimension
1296 * @padding: extra space in pixels to put between this child and its
1297 * neighbors, over and above the global amount specified by
1298 * #GtkBox:spacing property. If @child is a widget at one of the
1299 * reference ends of @box, then @padding pixels are also put between
1300 * @child and the reference edge of @box
1302 * Adds @child to @box, packed with reference to the start of @box.
1303 * The @child is packed after any other child packed with reference
1304 * to the start of @box.
1307 gtk_box_pack_start (GtkBox *box,
1313 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
1319 * @child: the #GtkWidget to be added to @box
1320 * @expand: %TRUE if the new child is to be given extra space allocated
1321 * to @box. The extra space will be divided evenly between all children
1322 * of @box that use this option
1323 * @fill: %TRUE if space given to @child by the @expand option is
1324 * actually allocated to @child, rather than just padding it. This
1325 * parameter has no effect if @expand is set to %FALSE. A child is
1326 * always allocated the full height of a #GtkHBox and the full width
1327 * of a #GtkVBox. This option affects the other dimension
1328 * @padding: extra space in pixels to put between this child and its
1329 * neighbors, over and above the global amount specified by
1330 * #GtkBox:spacing property. If @child is a widget at one of the
1331 * reference ends of @box, then @padding pixels are also put between
1332 * @child and the reference edge of @box
1334 * Adds @child to @box, packed with reference to the end of @box.
1335 * The @child is packed after (away from end of) any other child
1336 * packed with reference to the end of @box.
1339 gtk_box_pack_end (GtkBox *box,
1345 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
1349 * gtk_box_set_homogeneous:
1351 * @homogeneous: a boolean value, %TRUE to create equal allotments,
1352 * %FALSE for variable allotments
1354 * Sets the #GtkBox:homogeneous property of @box, controlling
1355 * whether or not all children of @box are given equal space
1359 gtk_box_set_homogeneous (GtkBox *box,
1360 gboolean homogeneous)
1362 g_return_if_fail (GTK_IS_BOX (box));
1364 if ((homogeneous ? TRUE : FALSE) != box->homogeneous)
1366 box->homogeneous = homogeneous ? TRUE : FALSE;
1367 g_object_notify (G_OBJECT (box), "homogeneous");
1368 gtk_widget_queue_resize (GTK_WIDGET (box));
1373 * gtk_box_get_homogeneous:
1376 * Returns whether the box is homogeneous (all children are the
1377 * same size). See gtk_box_set_homogeneous().
1379 * Return value: %TRUE if the box is homogeneous.
1382 gtk_box_get_homogeneous (GtkBox *box)
1384 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1386 return box->homogeneous;
1390 * gtk_box_set_spacing:
1392 * @spacing: the number of pixels to put between children
1394 * Sets the #GtkBox:spacing property of @box, which is the
1395 * number of pixels to place between children of @box.
1398 gtk_box_set_spacing (GtkBox *box,
1401 g_return_if_fail (GTK_IS_BOX (box));
1403 if (spacing != box->spacing)
1405 box->spacing = spacing;
1406 _gtk_box_set_spacing_set (box, TRUE);
1408 g_object_notify (G_OBJECT (box), "spacing");
1410 gtk_widget_queue_resize (GTK_WIDGET (box));
1415 * gtk_box_get_spacing:
1418 * Gets the value set by gtk_box_set_spacing().
1420 * Return value: spacing between children
1423 gtk_box_get_spacing (GtkBox *box)
1425 g_return_val_if_fail (GTK_IS_BOX (box), 0);
1427 return box->spacing;
1431 _gtk_box_set_spacing_set (GtkBox *box,
1432 gboolean spacing_set)
1434 GtkBoxPrivate *private;
1436 g_return_if_fail (GTK_IS_BOX (box));
1438 private = GTK_BOX_GET_PRIVATE (box);
1440 private->spacing_set = spacing_set ? TRUE : FALSE;
1444 _gtk_box_get_spacing_set (GtkBox *box)
1446 GtkBoxPrivate *private;
1448 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1450 private = GTK_BOX_GET_PRIVATE (box);
1452 return private->spacing_set;
1456 * gtk_box_reorder_child:
1458 * @child: the #GtkWidget to move
1459 * @position: the new position for @child in the list of children
1460 * of @box, starting from 0. If negative, indicates the end of
1463 * Moves @child to a new @position in the list of @box children.
1464 * The list is the <structfield>children</structfield> field of
1465 * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START
1466 * as well as widgets packed #GTK_PACK_END, in the order that these
1467 * widgets were added to @box.
1469 * A widget's position in the @box children list determines where
1470 * the widget is packed into @box. A child widget at some position
1471 * in the list will be packed just after all other widgets of the
1472 * same packing type that appear earlier in the list.
1475 gtk_box_reorder_child (GtkBox *box,
1481 GtkBoxChild *child_info = NULL;
1484 g_return_if_fail (GTK_IS_BOX (box));
1485 g_return_if_fail (GTK_IS_WIDGET (child));
1487 old_link = box->children;
1491 child_info = old_link->data;
1492 if (child_info->widget == child)
1495 old_link = old_link->next;
1499 g_return_if_fail (old_link != NULL);
1501 if (position == old_position)
1504 box->children = g_list_delete_link (box->children, old_link);
1509 new_link = g_list_nth (box->children, position);
1511 box->children = g_list_insert_before (box->children, new_link, child_info);
1513 gtk_widget_child_notify (child, "position");
1514 if (gtk_widget_get_visible (child)
1515 && gtk_widget_get_visible (GTK_WIDGET (box)))
1516 gtk_widget_queue_resize (child);
1520 * gtk_box_query_child_packing:
1522 * @child: the #GtkWidget of the child to query
1523 * @expand: pointer to return location for #GtkBox:expand child property
1524 * @fill: pointer to return location for #GtkBox:fill child property
1525 * @padding: pointer to return location for #GtkBox:padding child property
1526 * @pack_type: pointer to return location for #GtkBox:pack-type child property
1528 * Obtains information about how @child is packed into @box.
1531 gtk_box_query_child_packing (GtkBox *box,
1536 GtkPackType *pack_type)
1539 GtkBoxChild *child_info = NULL;
1541 g_return_if_fail (GTK_IS_BOX (box));
1542 g_return_if_fail (GTK_IS_WIDGET (child));
1544 list = box->children;
1547 child_info = list->data;
1548 if (child_info->widget == child)
1557 *expand = child_info->expand;
1559 *fill = child_info->fill;
1561 *padding = child_info->padding;
1563 *pack_type = child_info->pack;
1568 * gtk_box_set_child_packing:
1570 * @child: the #GtkWidget of the child to set
1571 * @expand: the new value of the #GtkBox:expand child property
1572 * @fill: the new value of the #GtkBox:fill child property
1573 * @padding: the new value of the #GtkBox:padding child property
1574 * @pack_type: the new value of the #GtkBox:pack-type child property
1576 * Sets the way @child is packed into @box.
1579 gtk_box_set_child_packing (GtkBox *box,
1584 GtkPackType pack_type)
1587 GtkBoxChild *child_info = NULL;
1589 g_return_if_fail (GTK_IS_BOX (box));
1590 g_return_if_fail (GTK_IS_WIDGET (child));
1592 list = box->children;
1595 child_info = list->data;
1596 if (child_info->widget == child)
1602 gtk_widget_freeze_child_notify (child);
1605 child_info->expand = expand != FALSE;
1606 gtk_widget_child_notify (child, "expand");
1607 child_info->fill = fill != FALSE;
1608 gtk_widget_child_notify (child, "fill");
1609 child_info->padding = padding;
1610 gtk_widget_child_notify (child, "padding");
1611 if (pack_type == GTK_PACK_END)
1612 child_info->pack = GTK_PACK_END;
1614 child_info->pack = GTK_PACK_START;
1615 gtk_widget_child_notify (child, "pack-type");
1617 if (gtk_widget_get_visible (child)
1618 && gtk_widget_get_visible (GTK_WIDGET (box)))
1619 gtk_widget_queue_resize (child);
1621 gtk_widget_thaw_child_notify (child);
1625 _gtk_box_set_old_defaults (GtkBox *box)
1627 GtkBoxPrivate *private;
1629 g_return_if_fail (GTK_IS_BOX (box));
1631 private = GTK_BOX_GET_PRIVATE (box);
1633 private->default_expand = TRUE;
1637 gtk_box_add (GtkContainer *container,
1640 GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (container);
1642 gtk_box_pack_start (GTK_BOX (container), widget,
1643 private->default_expand,
1644 private->default_expand,
1649 gtk_box_remove (GtkContainer *container,
1652 GtkBox *box = GTK_BOX (container);
1656 children = box->children;
1659 child = children->data;
1661 if (child->widget == widget)
1663 gboolean was_visible;
1665 was_visible = gtk_widget_get_visible (widget);
1666 gtk_widget_unparent (widget);
1668 box->children = g_list_remove_link (box->children, children);
1669 g_list_free (children);
1672 /* queue resize regardless of gtk_widget_get_visible (container),
1673 * since that's what is needed by toplevels.
1676 gtk_widget_queue_resize (GTK_WIDGET (container));
1681 children = children->next;
1686 gtk_box_forall (GtkContainer *container,
1687 gboolean include_internals,
1688 GtkCallback callback,
1689 gpointer callback_data)
1691 GtkBox *box = GTK_BOX (container);
1695 children = box->children;
1698 child = children->data;
1699 children = children->next;
1701 if (child->pack == GTK_PACK_START)
1702 (* callback) (child->widget, callback_data);
1705 children = g_list_last (box->children);
1708 child = children->data;
1709 children = children->prev;
1711 if (child->pack == GTK_PACK_END)
1712 (* callback) (child->widget, callback_data);
1716 #define __GTK_BOX_C__
1717 #include "gtkaliasdef.c"