1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
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.
8 *
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.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25 /**
26 * SECTION:gtkbox
27 * @Short_description: A container for packing widgets in a single row or column
28 * @Title: GtkBox
29 * @See_also: #GtkGrid
30 *
31 * The GtkBox widget arranges child widgets into a single row or column,
32 * depending upon the value of its #GtkOrientable:orientation property. Within
33 * the other dimension, all children are allocated the same size. Of course,
34 * the #GtkWidget:halign and #GtkWidget:valign properties can be used on
35 * the children to influence their allocation.
36 *
37 * GtkBox uses a notion of packing. Packing refers
38 * to adding widgets with reference to a particular position in a
39 * #GtkContainer. For a GtkBox, there are two reference positions: the
40 * start and the end of the box.
41 * For a vertical #GtkBox, the start is defined as the top of the box and
42 * the end is defined as the bottom. For a horizontal #GtkBox the start
43 * is defined as the left side and the end is defined as the right side.
44 *
45 * Use repeated calls to gtk_box_pack_start() to pack widgets into a
46 * GtkBox from start to end. Use gtk_box_pack_end() to add widgets from
47 * end to start. You may intersperse these calls and add widgets from
48 * both ends of the same GtkBox.
49 *
50 * Because GtkBox is a #GtkContainer, you may also use gtk_container_add()
51 * to insert widgets into the box, and they will be packed with the default
52 * values for expand and fill child properties. Use gtk_container_remove()
53 * to remove widgets from the GtkBox.
54 *
55 * Use gtk_box_set_homogeneous() to specify whether or not all children
56 * of the GtkBox are forced to get the same amount of space.
57 *
58 * Use gtk_box_set_spacing() to determine how much space will be
59 * minimally placed between all children in the GtkBox. Note that
60 * spacing is added between the children, while
61 * padding added by gtk_box_pack_start() or gtk_box_pack_end() is added
62 * on either side of the widget it belongs to.
63 *
64 * Use gtk_box_reorder_child() to move a GtkBox child to a different
65 * place in the box.
66 *
67 * Use gtk_box_set_child_packing() to reset the expand,
68 * fill and padding child properties.
69 * Use gtk_box_query_child_packing() to query these fields.
70 *
71 * # CSS nodes
72 *
73 * GtkBox uses a single CSS node with name box.
74 *
75 * In horizontal orientation, the nodes of the children are always arranged
76 * from left to right. So :first-child will always select the leftmost child,
77 * regardless of text direction.
78 */
79
80 #include "config.h"
81
82 #include "gtkbox.h"
83 #include "gtkboxprivate.h"
84 #include "gtkcontainerprivate.h"
85 #include "gtkcsscustomgadgetprivate.h"
86 #include "gtkcssnodeprivate.h"
87 #include "gtkintl.h"
88 #include "gtkorientable.h"
89 #include "gtkorientableprivate.h"
90 #include "gtkprivate.h"
91 #include "gtktypebuiltins.h"
92 #include "gtksizerequest.h"
93 #include "gtkwidgetpath.h"
94 #include "gtkwidgetprivate.h"
95 #include "a11y/gtkcontaineraccessible.h"
96
97
98 enum {
99 PROP_0,
100 PROP_SPACING,
101 PROP_HOMOGENEOUS,
102 PROP_BASELINE_POSITION,
103
104 /* orientable */
105 PROP_ORIENTATION,
106 LAST_PROP = PROP_ORIENTATION
107 };
108
109 enum {
110 CHILD_PROP_0,
111 CHILD_PROP_EXPAND,
112 CHILD_PROP_FILL,
113 CHILD_PROP_PADDING,
114 CHILD_PROP_PACK_TYPE,
115 CHILD_PROP_POSITION,
116 LAST_CHILD_PROP
117 };
118
119 typedef struct _GtkBoxChild GtkBoxChild;
120
121 struct _GtkBoxPrivate
122 {
123 GList *children;
124 GtkBoxChild *center;
125 GtkCssGadget *gadget;
126
127 GtkOrientation orientation;
128 gint16 spacing;
129
130 guint default_expand : 1;
131 guint homogeneous : 1;
132 guint spacing_set : 1;
133 guint baseline_pos : 2;
134 };
135
136 static GParamSpec *props[LAST_PROP] = { NULL, };
137 static GParamSpec *child_props[LAST_CHILD_PROP] = { NULL, };
138
139 /*
140 * GtkBoxChild:
141 * @widget: the child widget, packed into the GtkBox.
142 * @padding: the number of extra pixels to put between this child and its
143 * neighbors, set when packed, zero by default.
144 * @expand: flag indicates whether extra space should be given to this child.
145 * Any extra space given to the parent GtkBox is divided up among all children
146 * with this attribute set to %TRUE; set when packed, %TRUE by default.
147 * @fill: flag indicates whether any extra space given to this child due to its
148 * @expand attribute being set is actually allocated to the child, rather than
149 * being used as padding around the widget; set when packed, %TRUE by default.
150 * @pack: one of #GtkPackType indicating whether the child is packed with
151 * reference to the start (top/left) or end (bottom/right) of the GtkBox.
152 */
153 struct _GtkBoxChild
154 {
155 GtkWidget *widget;
156
157 guint16 padding;
158
159 guint expand : 1;
160 guint fill : 1;
161 guint pack : 1;
162 };
163
164 static void gtk_box_size_allocate (GtkWidget *widget,
165 GtkAllocation *allocation);
166 static gboolean gtk_box_draw (GtkWidget *widget,
167 cairo_t *cr);
168
169 static void gtk_box_direction_changed (GtkWidget *widget,
170 GtkTextDirection previous_direction);
171
172 static void gtk_box_set_property (GObject *object,
173 guint prop_id,
174 const GValue *value,
175 GParamSpec *pspec);
176 static void gtk_box_get_property (GObject *object,
177 guint prop_id,
178 GValue *value,
179 GParamSpec *pspec);
180 static void gtk_box_add (GtkContainer *container,
181 GtkWidget *widget);
182 static void gtk_box_remove (GtkContainer *container,
183 GtkWidget *widget);
184 static void gtk_box_forall (GtkContainer *container,
185 gboolean include_internals,
186 GtkCallback callback,
187 gpointer callback_data);
188 static void gtk_box_set_child_property (GtkContainer *container,
189 GtkWidget *child,
190 guint property_id,
191 const GValue *value,
192 GParamSpec *pspec);
193 static void gtk_box_get_child_property (GtkContainer *container,
194 GtkWidget *child,
195 guint property_id,
196 GValue *value,
197 GParamSpec *pspec);
198 static GType gtk_box_child_type (GtkContainer *container);
199 static GtkWidgetPath * gtk_box_get_path_for_child
200 (GtkContainer *container,
201 GtkWidget *child);
202
203
204 static void gtk_box_get_preferred_width (GtkWidget *widget,
205 gint *minimum_size,
206 gint *natural_size);
207 static void gtk_box_get_preferred_height (GtkWidget *widget,
208 gint *minimum_size,
209 gint *natural_size);
210 static void gtk_box_get_preferred_width_for_height (GtkWidget *widget,
211 gint height,
212 gint *minimum_width,
213 gint *natural_width);
214 static void gtk_box_get_preferred_height_for_width (GtkWidget *widget,
215 gint width,
216 gint *minimum_height,
217 gint *natural_height);
218 static void gtk_box_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
219 gint width,
220 gint *minimum_height,
221 gint *natural_height,
222 gint *minimum_baseline,
223 gint *natural_baseline);
224
225 static void gtk_box_buildable_init (GtkBuildableIface *iface);
226
G_DEFINE_TYPE_WITH_CODE(GtkBox,gtk_box,GTK_TYPE_CONTAINER,G_ADD_PRIVATE (GtkBox)G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,NULL)G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_box_buildable_init))227 G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
228 G_ADD_PRIVATE (GtkBox)
229 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
230 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_box_buildable_init))
231
232 static void
233 gtk_box_dispose (GObject *object)
234 {
235 GtkBox *box = GTK_BOX (object);
236 GtkBoxPrivate *priv = box->priv;
237
238 g_clear_object (&priv->gadget);
239
240 G_OBJECT_CLASS (gtk_box_parent_class)->dispose (object);
241 }
242
243 static void
gtk_box_class_init(GtkBoxClass * class)244 gtk_box_class_init (GtkBoxClass *class)
245 {
246 GObjectClass *object_class = G_OBJECT_CLASS (class);
247 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
248 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
249
250 object_class->set_property = gtk_box_set_property;
251 object_class->get_property = gtk_box_get_property;
252 object_class->dispose = gtk_box_dispose;
253
254 widget_class->draw = gtk_box_draw;
255 widget_class->size_allocate = gtk_box_size_allocate;
256 widget_class->get_preferred_width = gtk_box_get_preferred_width;
257 widget_class->get_preferred_height = gtk_box_get_preferred_height;
258 widget_class->get_preferred_height_for_width = gtk_box_get_preferred_height_for_width;
259 widget_class->get_preferred_height_and_baseline_for_width = gtk_box_get_preferred_height_and_baseline_for_width;
260 widget_class->get_preferred_width_for_height = gtk_box_get_preferred_width_for_height;
261 widget_class->direction_changed = gtk_box_direction_changed;
262
263 container_class->add = gtk_box_add;
264 container_class->remove = gtk_box_remove;
265 container_class->forall = gtk_box_forall;
266 container_class->child_type = gtk_box_child_type;
267 container_class->set_child_property = gtk_box_set_child_property;
268 container_class->get_child_property = gtk_box_get_child_property;
269 container_class->get_path_for_child = gtk_box_get_path_for_child;
270 gtk_container_class_handle_border_width (container_class);
271
272 g_object_class_override_property (object_class,
273 PROP_ORIENTATION,
274 "orientation");
275
276 props[PROP_SPACING] =
277 g_param_spec_int ("spacing",
278 P_("Spacing"),
279 P_("The amount of space between children"),
280 0, G_MAXINT, 0,
281 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
282
283 props[PROP_HOMOGENEOUS] =
284 g_param_spec_boolean ("homogeneous",
285 P_("Homogeneous"),
286 P_("Whether the children should all be the same size"),
287 FALSE,
288 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
289
290 props[PROP_BASELINE_POSITION] =
291 g_param_spec_enum ("baseline-position",
292 P_("Baseline position"),
293 P_("The position of the baseline aligned widgets if extra space is available"),
294 GTK_TYPE_BASELINE_POSITION,
295 GTK_BASELINE_POSITION_CENTER,
296 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
297
298 g_object_class_install_properties (object_class, LAST_PROP, props);
299
300 /**
301 * GtkBox:expand:
302 *
303 * Whether the child should receive extra space when the parent grows.
304 *
305 * Note that the default value for this property is %FALSE for GtkBox,
306 * but #GtkHBox, #GtkVBox and other subclasses use the old default
307 * of %TRUE.
308 *
309 * Note: The #GtkWidget:hexpand or #GtkWidget:vexpand properties are the
310 * preferred way to influence whether the child receives extra space, by
311 * setting the child’s expand property corresponding to the box’s orientation.
312 *
313 * In contrast to #GtkWidget:hexpand, the expand child property does
314 * not cause the box to expand itself.
315 */
316 child_props[CHILD_PROP_EXPAND] =
317 g_param_spec_boolean ("expand",
318 P_("Expand"),
319 P_("Whether the child should receive extra space when the parent grows"),
320 FALSE,
321 GTK_PARAM_READWRITE);
322
323 /**
324 * GtkBox:fill:
325 *
326 * Whether the child should fill extra space or use it as padding.
327 *
328 * Note: The #GtkWidget:halign or #GtkWidget:valign properties are the
329 * preferred way to influence whether the child fills available space, by
330 * setting the child’s align property corresponding to the box’s orientation
331 * to %GTK_ALIGN_FILL to fill, or to something else to refrain from filling.
332 */
333 child_props[CHILD_PROP_FILL] =
334 g_param_spec_boolean ("fill",
335 P_("Fill"),
336 P_("Whether extra space given to the child should be allocated to the child or used as padding"),
337 TRUE,
338 GTK_PARAM_READWRITE);
339
340 /**
341 * GtkBox:padding:
342 *
343 * Extra space to put between the child and its neighbors, in pixels.
344 *
345 * Note: The CSS padding properties are the preferred way to add space among
346 * widgets, by setting the paddings corresponding to the box’s orientation.
347 */
348 child_props[CHILD_PROP_PADDING] =
349 g_param_spec_uint ("padding",
350 P_("Padding"),
351 P_("Extra space to put between the child and its neighbors, in pixels"),
352 0, G_MAXINT,
353 0,
354 GTK_PARAM_READWRITE);
355
356 child_props[CHILD_PROP_PACK_TYPE] =
357 g_param_spec_enum ("pack-type",
358 P_("Pack type"),
359 P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
360 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
361 GTK_PARAM_READWRITE);
362
363 child_props[CHILD_PROP_POSITION] =
364 g_param_spec_int ("position",
365 P_("Position"),
366 P_("The index of the child in the parent"),
367 -1, G_MAXINT,
368 0,
369 GTK_PARAM_READWRITE);
370
371 gtk_container_class_install_child_properties (container_class, LAST_CHILD_PROP, child_props);
372
373 gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_FILLER);
374 gtk_widget_class_set_css_name (widget_class, "box");
375 }
376
377 static void
gtk_box_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)378 gtk_box_set_property (GObject *object,
379 guint prop_id,
380 const GValue *value,
381 GParamSpec *pspec)
382 {
383 GtkBox *box = GTK_BOX (object);
384 GtkBoxPrivate *private = box->priv;
385
386 switch (prop_id)
387 {
388 case PROP_ORIENTATION:
389 {
390 GtkOrientation orientation = g_value_get_enum (value);
391 if (private->orientation != orientation)
392 {
393 private->orientation = orientation;
394 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (box));
395 gtk_widget_queue_resize (GTK_WIDGET (box));
396 g_object_notify (object, "orientation");
397 }
398 }
399 break;
400 case PROP_SPACING:
401 gtk_box_set_spacing (box, g_value_get_int (value));
402 break;
403 case PROP_BASELINE_POSITION:
404 gtk_box_set_baseline_position (box, g_value_get_enum (value));
405 break;
406 case PROP_HOMOGENEOUS:
407 gtk_box_set_homogeneous (box, g_value_get_boolean (value));
408 break;
409 default:
410 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
411 break;
412 }
413 }
414
415 static void
gtk_box_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)416 gtk_box_get_property (GObject *object,
417 guint prop_id,
418 GValue *value,
419 GParamSpec *pspec)
420 {
421 GtkBox *box = GTK_BOX (object);
422 GtkBoxPrivate *private = box->priv;
423
424 switch (prop_id)
425 {
426 case PROP_ORIENTATION:
427 g_value_set_enum (value, private->orientation);
428 break;
429 case PROP_SPACING:
430 g_value_set_int (value, private->spacing);
431 break;
432 case PROP_BASELINE_POSITION:
433 g_value_set_enum (value, private->baseline_pos);
434 break;
435 case PROP_HOMOGENEOUS:
436 g_value_set_boolean (value, private->homogeneous);
437 break;
438 default:
439 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
440 break;
441 }
442 }
443
444 static gboolean
gtk_box_draw_contents(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer unused)445 gtk_box_draw_contents (GtkCssGadget *gadget,
446 cairo_t *cr,
447 int x,
448 int y,
449 int width,
450 int height,
451 gpointer unused)
452 {
453 GTK_WIDGET_CLASS (gtk_box_parent_class)->draw (gtk_css_gadget_get_owner (gadget), cr);
454
455 return FALSE;
456 }
457
458 static gboolean
gtk_box_draw(GtkWidget * widget,cairo_t * cr)459 gtk_box_draw (GtkWidget *widget,
460 cairo_t *cr)
461 {
462 gtk_css_gadget_draw (GTK_BOX (widget)->priv->gadget, cr);
463
464 return FALSE;
465 }
466
467
468 static void
count_expand_children(GtkBox * box,gint * visible_children,gint * expand_children)469 count_expand_children (GtkBox *box,
470 gint *visible_children,
471 gint *expand_children)
472 {
473 GtkBoxPrivate *private = box->priv;
474 GList *children;
475 GtkBoxChild *child;
476
477 *visible_children = *expand_children = 0;
478
479 for (children = private->children; children; children = children->next)
480 {
481 child = children->data;
482
483 if (_gtk_widget_get_visible (child->widget))
484 {
485 *visible_children += 1;
486 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
487 *expand_children += 1;
488 }
489 }
490 }
491
492 static void
gtk_box_size_allocate_no_center(GtkWidget * widget,const GtkAllocation * allocation)493 gtk_box_size_allocate_no_center (GtkWidget *widget,
494 const GtkAllocation *allocation)
495 {
496 GtkBox *box = GTK_BOX (widget);
497 GtkBoxPrivate *private = box->priv;
498 GtkBoxChild *child;
499 GList *children;
500 gint nvis_children;
501 gint nexpand_children;
502
503 GtkTextDirection direction;
504 GtkAllocation child_allocation;
505 GtkRequestedSize *sizes;
506 gint child_minimum_baseline, child_natural_baseline;
507 gint minimum_above, natural_above;
508 gint minimum_below, natural_below;
509 gboolean have_baseline;
510 gint baseline;
511
512 GtkPackType packing;
513
514 gint size;
515 gint extra;
516 gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
517 gint x = 0, y = 0, i;
518 gint child_size;
519
520
521 count_expand_children (box, &nvis_children, &nexpand_children);
522
523 /* If there is no visible child, simply return. */
524 if (nvis_children <= 0)
525 return;
526
527 direction = gtk_widget_get_direction (widget);
528 sizes = g_newa (GtkRequestedSize, nvis_children);
529 memset (sizes, 0, nvis_children * sizeof (GtkRequestedSize));
530
531 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
532 size = allocation->width - (nvis_children - 1) * private->spacing;
533 else
534 size = allocation->height - (nvis_children - 1) * private->spacing;
535
536 have_baseline = FALSE;
537 minimum_above = natural_above = 0;
538 minimum_below = natural_below = 0;
539
540 /* Retrieve desired size for visible children. */
541 for (i = 0, children = private->children; children; children = children->next)
542 {
543 child = children->data;
544
545 if (!_gtk_widget_get_visible (child->widget))
546 continue;
547
548 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
549 gtk_widget_get_preferred_width_for_height (child->widget,
550 allocation->height,
551 &sizes[i].minimum_size,
552 &sizes[i].natural_size);
553 else
554 gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
555 allocation->width,
556 &sizes[i].minimum_size,
557 &sizes[i].natural_size,
558 NULL, NULL);
559
560 /* Assert the api is working properly */
561 if (sizes[i].minimum_size < 0)
562 g_error ("GtkBox child %s minimum %s: %d < 0 for %s %d",
563 gtk_widget_get_name (GTK_WIDGET (child->widget)),
564 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
565 sizes[i].minimum_size,
566 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
567 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
568
569 if (sizes[i].natural_size < sizes[i].minimum_size)
570 g_error ("GtkBox child %s natural %s: %d < minimum %d for %s %d",
571 gtk_widget_get_name (GTK_WIDGET (child->widget)),
572 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
573 sizes[i].natural_size,
574 sizes[i].minimum_size,
575 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
576 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
577
578 size -= sizes[i].minimum_size;
579 size -= child->padding * 2;
580
581 sizes[i].data = child;
582
583 i++;
584 }
585
586 if (private->homogeneous)
587 {
588 /* If were homogenous we still need to run the above loop to get the
589 * minimum sizes for children that are not going to fill
590 */
591 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
592 size = allocation->width - (nvis_children - 1) * private->spacing;
593 else
594 size = allocation->height - (nvis_children - 1) * private->spacing;
595
596 extra = size / nvis_children;
597 n_extra_widgets = size % nvis_children;
598 }
599 else
600 {
601 /* Bring children up to size first */
602 size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
603
604 /* Calculate space which hasn't distributed yet,
605 * and is available for expanding children.
606 */
607 if (nexpand_children > 0)
608 {
609 extra = size / nexpand_children;
610 n_extra_widgets = size % nexpand_children;
611 }
612 else
613 extra = 0;
614 }
615
616 /* Allocate child sizes. */
617 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
618 {
619 for (i = 0, children = private->children;
620 children;
621 children = children->next)
622 {
623 child = children->data;
624
625 /* If widget is not visible, skip it. */
626 if (!_gtk_widget_get_visible (child->widget))
627 continue;
628
629 /* If widget is packed differently skip it, but still increment i,
630 * since widget is visible and will be handled in next loop iteration.
631 */
632 if (child->pack != packing)
633 {
634 i++;
635 continue;
636 }
637
638 /* Assign the child's size. */
639 if (private->homogeneous)
640 {
641 child_size = extra;
642
643 if (n_extra_widgets > 0)
644 {
645 child_size++;
646 n_extra_widgets--;
647 }
648 }
649 else
650 {
651 child_size = sizes[i].minimum_size + child->padding * 2;
652
653 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
654 {
655 child_size += extra;
656
657 if (n_extra_widgets > 0)
658 {
659 child_size++;
660 n_extra_widgets--;
661 }
662 }
663 }
664
665 sizes[i].natural_size = child_size;
666
667 if (private->orientation == GTK_ORIENTATION_HORIZONTAL &&
668 gtk_widget_get_valign_with_baseline (child->widget) == GTK_ALIGN_BASELINE)
669 {
670 int child_allocation_width;
671 int child_minimum_height, child_natural_height;
672
673 if (child->fill)
674 child_allocation_width = MAX (1, child_size - child->padding * 2);
675 else
676 child_allocation_width = sizes[i].minimum_size;
677
678 child_minimum_baseline = -1;
679 child_natural_baseline = -1;
680 gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
681 child_allocation_width,
682 &child_minimum_height, &child_natural_height,
683 &child_minimum_baseline, &child_natural_baseline);
684
685 if (child_minimum_baseline >= 0)
686 {
687 have_baseline = TRUE;
688 minimum_below = MAX (minimum_below, child_minimum_height - child_minimum_baseline);
689 natural_below = MAX (natural_below, child_natural_height - child_natural_baseline);
690 minimum_above = MAX (minimum_above, child_minimum_baseline);
691 natural_above = MAX (natural_above, child_natural_baseline);
692 }
693 }
694
695 i++;
696 }
697 }
698
699 baseline = gtk_widget_get_allocated_baseline (widget);
700 if (baseline == -1 && have_baseline)
701 {
702 gint height = MAX (1, allocation->height);
703
704 /* TODO: This is purely based on the minimum baseline, when things fit we should
705 use the natural one? */
706
707 switch (private->baseline_pos)
708 {
709 case GTK_BASELINE_POSITION_TOP:
710 baseline = minimum_above;
711 break;
712 case GTK_BASELINE_POSITION_CENTER:
713 baseline = minimum_above + (height - (minimum_above + minimum_below)) / 2;
714 break;
715 case GTK_BASELINE_POSITION_BOTTOM:
716 baseline = height - minimum_below;
717 break;
718 }
719 }
720
721 /* Allocate child positions. */
722 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
723 {
724 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
725 {
726 child_allocation.y = allocation->y;
727 child_allocation.height = MAX (1, allocation->height);
728 if (packing == GTK_PACK_START)
729 x = allocation->x;
730 else
731 x = allocation->x + allocation->width;
732 }
733 else
734 {
735 child_allocation.x = allocation->x;
736 child_allocation.width = MAX (1, allocation->width);
737 if (packing == GTK_PACK_START)
738 y = allocation->y;
739 else
740 y = allocation->y + allocation->height;
741 }
742
743 for (i = 0, children = private->children;
744 children;
745 children = children->next)
746 {
747 child = children->data;
748
749 /* If widget is not visible, skip it. */
750 if (!_gtk_widget_get_visible (child->widget))
751 continue;
752
753 /* If widget is packed differently skip it, but still increment i,
754 * since widget is visible and will be handled in next loop iteration.
755 */
756 if (child->pack != packing)
757 {
758 i++;
759 continue;
760 }
761
762 child_size = sizes[i].natural_size;
763
764 /* Assign the child's position. */
765 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
766 {
767 if (child->fill)
768 {
769 child_allocation.width = MAX (1, child_size - child->padding * 2);
770 child_allocation.x = x + child->padding;
771 }
772 else
773 {
774 child_allocation.width = sizes[i].minimum_size;
775 child_allocation.x = x + (child_size - child_allocation.width) / 2;
776 }
777
778 if (packing == GTK_PACK_START)
779 {
780 x += child_size + private->spacing;
781 }
782 else
783 {
784 x -= child_size + private->spacing;
785
786 child_allocation.x -= child_size;
787 }
788
789 if (direction == GTK_TEXT_DIR_RTL)
790 child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
791
792 }
793 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
794 {
795 if (child->fill)
796 {
797 child_allocation.height = MAX (1, child_size - child->padding * 2);
798 child_allocation.y = y + child->padding;
799 }
800 else
801 {
802 child_allocation.height = sizes[i].minimum_size;
803 child_allocation.y = y + (child_size - child_allocation.height) / 2;
804 }
805
806 if (packing == GTK_PACK_START)
807 {
808 y += child_size + private->spacing;
809 }
810 else
811 {
812 y -= child_size + private->spacing;
813
814 child_allocation.y -= child_size;
815 }
816 }
817 gtk_widget_size_allocate_with_baseline (child->widget, &child_allocation, baseline);
818
819 i++;
820 }
821 }
822
823 _gtk_widget_set_simple_clip (widget, NULL);
824 }
825
826 static void
gtk_box_size_allocate_with_center(GtkWidget * widget,const GtkAllocation * allocation)827 gtk_box_size_allocate_with_center (GtkWidget *widget,
828 const GtkAllocation *allocation)
829 {
830 GtkBox *box = GTK_BOX (widget);
831 GtkBoxPrivate *priv = box->priv;
832 GtkBoxChild *child;
833 GList *children;
834 gint nvis[2];
835 gint nexp[2];
836 GtkTextDirection direction;
837 GtkAllocation child_allocation;
838 GtkRequestedSize *sizes[2];
839 GtkRequestedSize center_req = {0, 0};
840 gint child_minimum_baseline, child_natural_baseline;
841 gint minimum_above, natural_above;
842 gint minimum_below, natural_below;
843 gboolean have_baseline;
844 gint baseline;
845 gint idx[2];
846 gint center_pos;
847 gint center_size;
848 gint box_size;
849 gint side[2];
850 GtkPackType packing;
851 gint min_size[2];
852 gint nat_size[2];
853 gint extra[2];
854 gint n_extra_widgets[2];
855 gint x = 0, y = 0, i;
856 gint child_size;
857
858 nvis[0] = nvis[1] = 0;
859 nexp[0] = nexp[1] = 0;
860 for (children = priv->children; children; children = children->next)
861 {
862 child = children->data;
863
864 if (child != priv->center &&
865 _gtk_widget_get_visible (child->widget))
866 {
867 nvis[child->pack] += 1;
868 if (child->expand || gtk_widget_compute_expand (child->widget, priv->orientation))
869 nexp[child->pack] += 1;
870 }
871 }
872
873 direction = gtk_widget_get_direction (widget);
874 sizes[0] = g_newa (GtkRequestedSize, nvis[0]);
875 sizes[1] = g_newa (GtkRequestedSize, nvis[1]);
876
877 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
878 box_size = allocation->width;
879 else
880 box_size = allocation->height;
881
882 have_baseline = FALSE;
883 minimum_above = natural_above = 0;
884 minimum_below = natural_below = 0;
885
886 min_size[0] = nat_size[0] = nvis[0] * priv->spacing;
887 min_size[1] = nat_size[1] = nvis[1] * priv->spacing;
888
889 /* Retrieve desired size for visible children. */
890 idx[0] = idx[1] = 0;
891 for (children = priv->children; children; children = children->next)
892 {
893 GtkRequestedSize *req;
894
895 child = children->data;
896
897 if (!_gtk_widget_get_visible (child->widget))
898 continue;
899
900 if (child == priv->center)
901 req = ¢er_req;
902 else
903 req = &(sizes[child->pack][idx[child->pack]]);
904
905 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
906 gtk_widget_get_preferred_width_for_height (child->widget,
907 allocation->height,
908 &req->minimum_size,
909 &req->natural_size);
910 else
911 gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
912 allocation->width,
913 &req->minimum_size,
914 &req->natural_size,
915 NULL, NULL);
916
917 if (child != priv->center)
918 {
919 min_size[child->pack] += req->minimum_size + 2 * child->padding;
920 nat_size[child->pack] += req->natural_size + 2 * child->padding;
921
922 idx[child->pack] += 1;
923 }
924
925 req->data = child;
926 }
927
928 /* Determine size of center */
929 if (priv->center->expand)
930 center_size = MAX (box_size - 2 * MAX (nat_size[0], nat_size[1]), center_req.minimum_size);
931 else
932 center_size = MAX (MIN (center_req.natural_size, box_size - min_size[0] - min_size[1]), center_req.minimum_size);
933
934 if (priv->homogeneous)
935 {
936 extra[0] = nvis[0] ? ((box_size - center_size) / 2 - nvis[0] * priv->spacing) / nvis[0] : 0;
937 extra[1] = nvis[1] ? ((box_size - center_size) / 2 - nvis[1] * priv->spacing) / nvis[1] : 0;
938 extra[0] = MIN (extra[0], extra[1]);
939 n_extra_widgets[0] = 0;
940 }
941 else
942 {
943 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; packing++)
944 {
945 gint s;
946 /* Distribute the remainder naturally on each side */
947 s = MIN ((box_size - center_size) / 2 - min_size[packing], box_size - center_size - min_size[0] - min_size[1]);
948 s = gtk_distribute_natural_allocation (MAX (0, s), nvis[packing], sizes[packing]);
949
950 /* Calculate space which hasn't distributed yet,
951 * and is available for expanding children.
952 */
953 if (nexp[packing] > 0)
954 {
955 extra[packing] = s / nexp[packing];
956 n_extra_widgets[packing] = s % nexp[packing];
957 }
958 else
959 extra[packing] = 0;
960 }
961 }
962
963 /* Allocate child sizes. */
964 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
965 {
966 for (i = 0, children = priv->children; children; children = children->next)
967 {
968 child = children->data;
969
970 /* If widget is not visible, skip it. */
971 if (!_gtk_widget_get_visible (child->widget))
972 continue;
973
974 /* Skip the center widget */
975 if (child == priv->center)
976 continue;
977
978 /* If widget is packed differently, skip it. */
979 if (child->pack != packing)
980 continue;
981
982 /* Assign the child's size. */
983 if (priv->homogeneous)
984 {
985 child_size = extra[0];
986
987 if (n_extra_widgets[0] > 0)
988 {
989 child_size++;
990 n_extra_widgets[0]--;
991 }
992 }
993 else
994 {
995 child_size = sizes[packing][i].minimum_size + child->padding * 2;
996
997 if (child->expand || gtk_widget_compute_expand (child->widget, priv->orientation))
998 {
999 child_size += extra[packing];
1000
1001 if (n_extra_widgets[packing] > 0)
1002 {
1003 child_size++;
1004 n_extra_widgets[packing]--;
1005 }
1006 }
1007 }
1008
1009 sizes[packing][i].natural_size = child_size;
1010
1011 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1012 gtk_widget_get_valign_with_baseline (child->widget) == GTK_ALIGN_BASELINE)
1013 {
1014 gint child_allocation_width;
1015 gint child_minimum_height, child_natural_height;
1016
1017 if (child->fill)
1018 child_allocation_width = MAX (1, child_size - child->padding * 2);
1019 else
1020 child_allocation_width = sizes[packing][i].minimum_size;
1021
1022 child_minimum_baseline = -1;
1023 child_natural_baseline = -1;
1024 gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
1025 child_allocation_width,
1026 &child_minimum_height, &child_natural_height,
1027 &child_minimum_baseline, &child_natural_baseline);
1028
1029 if (child_minimum_baseline >= 0)
1030 {
1031 have_baseline = TRUE;
1032 minimum_below = MAX (minimum_below, child_minimum_height - child_minimum_baseline);
1033 natural_below = MAX (natural_below, child_natural_height - child_natural_baseline);
1034 minimum_above = MAX (minimum_above, child_minimum_baseline);
1035 natural_above = MAX (natural_above, child_natural_baseline);
1036 }
1037 }
1038
1039 i++;
1040 }
1041 }
1042
1043 baseline = gtk_widget_get_allocated_baseline (widget);
1044 if (baseline == -1 && have_baseline)
1045 {
1046 gint height = MAX (1, allocation->height);
1047
1048 /* TODO: This is purely based on the minimum baseline, when things fit we should
1049 * use the natural one?
1050 */
1051 switch (priv->baseline_pos)
1052 {
1053 case GTK_BASELINE_POSITION_TOP:
1054 baseline = minimum_above;
1055 break;
1056 case GTK_BASELINE_POSITION_CENTER:
1057 baseline = minimum_above + (height - (minimum_above + minimum_below)) / 2;
1058 break;
1059 case GTK_BASELINE_POSITION_BOTTOM:
1060 baseline = height - minimum_below;
1061 break;
1062 }
1063 }
1064
1065 /* Allocate child positions. */
1066 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1067 {
1068 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1069 {
1070 child_allocation.y = allocation->y;
1071 child_allocation.height = MAX (1, allocation->height);
1072 if ((packing == GTK_PACK_START && direction == GTK_TEXT_DIR_LTR) ||
1073 (packing == GTK_PACK_END && direction == GTK_TEXT_DIR_RTL))
1074 x = allocation->x;
1075 else
1076 x = allocation->x + allocation->width;
1077 }
1078 else
1079 {
1080 child_allocation.x = allocation->x;
1081 child_allocation.width = MAX (1, allocation->width);
1082 if (packing == GTK_PACK_START)
1083 y = allocation->y;
1084 else
1085 y = allocation->y + allocation->height;
1086 }
1087
1088 for (i = 0, children = priv->children; children; children = children->next)
1089 {
1090 child = children->data;
1091
1092 /* If widget is not visible, skip it. */
1093 if (!_gtk_widget_get_visible (child->widget))
1094 continue;
1095
1096 /* Skip the center widget */
1097 if (child == priv->center)
1098 continue;
1099
1100 /* If widget is packed differently, skip it. */
1101 if (child->pack != packing)
1102 continue;
1103
1104 child_size = sizes[packing][i].natural_size;
1105
1106 /* Assign the child's position. */
1107 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1108 {
1109 if (child->fill)
1110 {
1111 child_allocation.width = MAX (1, child_size - child->padding * 2);
1112 child_allocation.x = x + child->padding;
1113 }
1114 else
1115 {
1116 child_allocation.width = sizes[packing][i].minimum_size;
1117 child_allocation.x = x + (child_size - child_allocation.width) / 2;
1118 }
1119
1120 if ((packing == GTK_PACK_START && direction == GTK_TEXT_DIR_LTR) ||
1121 (packing == GTK_PACK_END && direction == GTK_TEXT_DIR_RTL))
1122 {
1123 x += child_size + priv->spacing;
1124 }
1125 else
1126 {
1127 x -= child_size + priv->spacing;
1128 child_allocation.x -= child_size;
1129 }
1130 }
1131 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1132 {
1133 if (child->fill)
1134 {
1135 child_allocation.height = MAX (1, child_size - child->padding * 2);
1136 child_allocation.y = y + child->padding;
1137 }
1138 else
1139 {
1140 child_allocation.height = sizes[packing][i].minimum_size;
1141 child_allocation.y = y + (child_size - child_allocation.height) / 2;
1142 }
1143
1144 if (packing == GTK_PACK_START)
1145 {
1146 y += child_size + priv->spacing;
1147 }
1148 else
1149 {
1150 y -= child_size + priv->spacing;
1151 child_allocation.y -= child_size;
1152 }
1153 }
1154 gtk_widget_size_allocate_with_baseline (child->widget, &child_allocation, baseline);
1155
1156 i++;
1157 }
1158
1159 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1160 side[packing] = x;
1161 else
1162 side[packing] = y;
1163 }
1164
1165 /* Allocate the center widget */
1166 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1167 center_pos = allocation->x + (box_size - center_size) / 2;
1168 else
1169 center_pos = allocation->y + (box_size - center_size) / 2;
1170
1171 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1172 direction == GTK_TEXT_DIR_RTL)
1173 packing = GTK_PACK_END;
1174 else
1175 packing = GTK_PACK_START;
1176
1177 if (center_pos < side[packing])
1178 center_pos = side[packing];
1179 else if (center_pos + center_size > side[1 - packing])
1180 center_pos = side[1 - packing] - center_size;
1181
1182 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1183 {
1184 child_allocation.x = center_pos;
1185 child_allocation.width = center_size;
1186 }
1187 else
1188 {
1189 child_allocation.y = center_pos;
1190 child_allocation.height = center_size;
1191 }
1192 gtk_widget_size_allocate_with_baseline (priv->center->widget, &child_allocation, baseline);
1193
1194 _gtk_widget_set_simple_clip (widget, NULL);
1195 }
1196
1197 static void
gtk_box_allocate_contents(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer unused)1198 gtk_box_allocate_contents (GtkCssGadget *gadget,
1199 const GtkAllocation *allocation,
1200 int baseline,
1201 GtkAllocation *out_clip,
1202 gpointer unused)
1203 {
1204 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
1205 GtkBox *box = GTK_BOX (widget);
1206
1207 if (box->priv->center &&
1208 _gtk_widget_get_visible (box->priv->center->widget))
1209 gtk_box_size_allocate_with_center (widget, allocation);
1210 else
1211 gtk_box_size_allocate_no_center (widget, allocation);
1212
1213 gtk_container_get_children_clip (GTK_CONTAINER (box), out_clip);
1214 }
1215
1216 static void
gtk_box_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1217 gtk_box_size_allocate (GtkWidget *widget,
1218 GtkAllocation *allocation)
1219 {
1220 GtkBoxPrivate *priv = GTK_BOX (widget)->priv;
1221 GtkAllocation clip;
1222
1223 gtk_widget_set_allocation (widget, allocation);
1224
1225 gtk_css_gadget_allocate (priv->gadget,
1226 allocation,
1227 gtk_widget_get_allocated_baseline (widget),
1228 &clip);
1229
1230 gtk_widget_set_clip (widget, &clip);
1231 }
1232
1233 static GType
gtk_box_child_type(GtkContainer * container)1234 gtk_box_child_type (GtkContainer *container)
1235 {
1236 return GTK_TYPE_WIDGET;
1237 }
1238
1239 static void
gtk_box_set_child_property(GtkContainer * container,GtkWidget * child,guint property_id,const GValue * value,GParamSpec * pspec)1240 gtk_box_set_child_property (GtkContainer *container,
1241 GtkWidget *child,
1242 guint property_id,
1243 const GValue *value,
1244 GParamSpec *pspec)
1245 {
1246 gboolean expand = 0;
1247 gboolean fill = 0;
1248 guint padding = 0;
1249 GtkPackType pack_type = 0;
1250
1251 if (property_id != CHILD_PROP_POSITION)
1252 gtk_box_query_child_packing (GTK_BOX (container),
1253 child,
1254 &expand,
1255 &fill,
1256 &padding,
1257 &pack_type);
1258 switch (property_id)
1259 {
1260 case CHILD_PROP_EXPAND:
1261 gtk_box_set_child_packing (GTK_BOX (container),
1262 child,
1263 g_value_get_boolean (value),
1264 fill,
1265 padding,
1266 pack_type);
1267 break;
1268 case CHILD_PROP_FILL:
1269 gtk_box_set_child_packing (GTK_BOX (container),
1270 child,
1271 expand,
1272 g_value_get_boolean (value),
1273 padding,
1274 pack_type);
1275 break;
1276 case CHILD_PROP_PADDING:
1277 gtk_box_set_child_packing (GTK_BOX (container),
1278 child,
1279 expand,
1280 fill,
1281 g_value_get_uint (value),
1282 pack_type);
1283 break;
1284 case CHILD_PROP_PACK_TYPE:
1285 gtk_box_set_child_packing (GTK_BOX (container),
1286 child,
1287 expand,
1288 fill,
1289 padding,
1290 g_value_get_enum (value));
1291 break;
1292 case CHILD_PROP_POSITION:
1293 gtk_box_reorder_child (GTK_BOX (container),
1294 child,
1295 g_value_get_int (value));
1296 break;
1297 default:
1298 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
1299 break;
1300 }
1301 }
1302
1303 static void
gtk_box_get_child_property(GtkContainer * container,GtkWidget * child,guint property_id,GValue * value,GParamSpec * pspec)1304 gtk_box_get_child_property (GtkContainer *container,
1305 GtkWidget *child,
1306 guint property_id,
1307 GValue *value,
1308 GParamSpec *pspec)
1309 {
1310 gboolean expand = FALSE;
1311 gboolean fill = FALSE;
1312 guint padding = 0;
1313 GtkPackType pack_type = 0;
1314 GList *list;
1315
1316 if (property_id != CHILD_PROP_POSITION)
1317 gtk_box_query_child_packing (GTK_BOX (container),
1318 child,
1319 &expand,
1320 &fill,
1321 &padding,
1322 &pack_type);
1323 switch (property_id)
1324 {
1325 guint i;
1326 case CHILD_PROP_EXPAND:
1327 g_value_set_boolean (value, expand);
1328 break;
1329 case CHILD_PROP_FILL:
1330 g_value_set_boolean (value, fill);
1331 break;
1332 case CHILD_PROP_PADDING:
1333 g_value_set_uint (value, padding);
1334 break;
1335 case CHILD_PROP_PACK_TYPE:
1336 g_value_set_enum (value, pack_type);
1337 break;
1338 case CHILD_PROP_POSITION:
1339 i = 0;
1340 for (list = GTK_BOX (container)->priv->children; list; list = list->next)
1341 {
1342 GtkBoxChild *child_entry;
1343
1344 child_entry = list->data;
1345 if (child_entry->widget == child)
1346 break;
1347 i++;
1348 }
1349 g_value_set_int (value, list ? i : -1);
1350 break;
1351 default:
1352 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
1353 break;
1354 }
1355 }
1356
1357 typedef struct _CountingData CountingData;
1358 struct _CountingData {
1359 GtkWidget *widget;
1360 gboolean found;
1361 guint before;
1362 guint after;
1363 };
1364
1365 static void
count_widget_position(GtkWidget * widget,gpointer data)1366 count_widget_position (GtkWidget *widget,
1367 gpointer data)
1368 {
1369 CountingData *count = data;
1370
1371 if (!_gtk_widget_get_visible (widget))
1372 return;
1373
1374 if (count->widget == widget)
1375 count->found = TRUE;
1376 else if (count->found)
1377 count->after++;
1378 else
1379 count->before++;
1380 }
1381
1382 static gint
gtk_box_get_visible_position(GtkBox * box,GtkWidget * child)1383 gtk_box_get_visible_position (GtkBox *box,
1384 GtkWidget *child)
1385 {
1386 CountingData count = { child, FALSE, 0, 0 };
1387
1388 /* foreach iterates in visible order */
1389 gtk_container_foreach (GTK_CONTAINER (box),
1390 count_widget_position,
1391 &count);
1392
1393 /* the child wasn't found, it's likely an internal child of some
1394 * subclass, return -1 to indicate that there is no sibling relation
1395 * to the regular box children
1396 */
1397 if (!count.found)
1398 return -1;
1399
1400 if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1401 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
1402 return count.after;
1403 else
1404 return count.before;
1405 }
1406
1407 static GtkWidgetPath *
gtk_box_get_path_for_child(GtkContainer * container,GtkWidget * child)1408 gtk_box_get_path_for_child (GtkContainer *container,
1409 GtkWidget *child)
1410 {
1411 GtkWidgetPath *path, *sibling_path;
1412 GtkBox *box;
1413 GtkBoxPrivate *private;
1414 GList *list, *children;
1415
1416 box = GTK_BOX (container);
1417 private = box->priv;
1418
1419 path = _gtk_widget_create_path (GTK_WIDGET (container));
1420
1421 if (_gtk_widget_get_visible (child))
1422 {
1423 gint position;
1424
1425 sibling_path = gtk_widget_path_new ();
1426
1427 /* get_children works in visible order */
1428 children = gtk_container_get_children (container);
1429 if (private->orientation == GTK_ORIENTATION_HORIZONTAL &&
1430 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
1431 children = g_list_reverse (children);
1432
1433 for (list = children; list; list = list->next)
1434 {
1435 if (!_gtk_widget_get_visible (list->data))
1436 continue;
1437
1438 gtk_widget_path_append_for_widget (sibling_path, list->data);
1439 }
1440
1441 g_list_free (children);
1442
1443 position = gtk_box_get_visible_position (box, child);
1444
1445 if (position >= 0)
1446 gtk_widget_path_append_with_siblings (path, sibling_path, position);
1447 else
1448 gtk_widget_path_append_for_widget (path, child);
1449
1450 gtk_widget_path_unref (sibling_path);
1451 }
1452 else
1453 gtk_widget_path_append_for_widget (path, child);
1454
1455 return path;
1456 }
1457
1458 static void
gtk_box_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * type)1459 gtk_box_buildable_add_child (GtkBuildable *buildable,
1460 GtkBuilder *builder,
1461 GObject *child,
1462 const gchar *type)
1463 {
1464 if (type && strcmp (type, "center") == 0)
1465 gtk_box_set_center_widget (GTK_BOX (buildable), GTK_WIDGET (child));
1466 else if (!type)
1467 gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
1468 else
1469 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_BOX (buildable), type);
1470 }
1471
1472 static void
gtk_box_buildable_init(GtkBuildableIface * iface)1473 gtk_box_buildable_init (GtkBuildableIface *iface)
1474 {
1475 iface->add_child = gtk_box_buildable_add_child;
1476 }
1477
1478 static void
gtk_box_update_child_css_position(GtkBox * box,GtkBoxChild * child_info)1479 gtk_box_update_child_css_position (GtkBox *box,
1480 GtkBoxChild *child_info)
1481 {
1482 GtkBoxPrivate *priv = box->priv;
1483 GtkBoxChild *prev;
1484 gboolean reverse;
1485 GList *l;
1486
1487 prev = NULL;
1488 for (l = priv->children; l->data != child_info; l = l->next)
1489 {
1490 GtkBoxChild *cur = l->data;
1491
1492 if (cur->pack == child_info->pack)
1493 prev = cur;
1494 }
1495
1496 reverse = child_info->pack == GTK_PACK_END;
1497 if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1498 gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
1499 reverse = !reverse;
1500
1501 if (reverse)
1502 gtk_css_node_insert_before (gtk_widget_get_css_node (GTK_WIDGET (box)),
1503 gtk_widget_get_css_node (child_info->widget),
1504 prev ? gtk_widget_get_css_node (prev->widget) : NULL);
1505 else
1506 gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (box)),
1507 gtk_widget_get_css_node (child_info->widget),
1508 prev ? gtk_widget_get_css_node (prev->widget) : NULL);
1509 }
1510
1511 static void
gtk_box_direction_changed(GtkWidget * widget,GtkTextDirection previous_direction)1512 gtk_box_direction_changed (GtkWidget *widget,
1513 GtkTextDirection previous_direction)
1514 {
1515 GtkBox *box = GTK_BOX (widget);
1516
1517 if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1518 gtk_css_node_reverse_children (gtk_widget_get_css_node (widget));
1519 }
1520
1521 static GtkBoxChild *
gtk_box_pack(GtkBox * box,GtkWidget * child,gboolean expand,gboolean fill,guint padding,GtkPackType pack_type)1522 gtk_box_pack (GtkBox *box,
1523 GtkWidget *child,
1524 gboolean expand,
1525 gboolean fill,
1526 guint padding,
1527 GtkPackType pack_type)
1528 {
1529 GtkContainer *container;
1530 GtkBoxPrivate *private;
1531 GtkBoxChild *child_info;
1532
1533 g_return_val_if_fail (GTK_IS_BOX (box), NULL);
1534 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
1535 g_return_val_if_fail (_gtk_widget_get_parent (child) == NULL, NULL);
1536
1537 container = GTK_CONTAINER (box);
1538 private = box->priv;
1539
1540 child_info = g_new (GtkBoxChild, 1);
1541 child_info->widget = child;
1542 child_info->padding = padding;
1543 child_info->expand = expand ? TRUE : FALSE;
1544 child_info->fill = fill ? TRUE : FALSE;
1545 child_info->pack = pack_type;
1546
1547 private->children = g_list_append (private->children, child_info);
1548 gtk_box_update_child_css_position (box, child_info);
1549
1550 gtk_widget_freeze_child_notify (child);
1551
1552 gtk_widget_set_parent (child, GTK_WIDGET (box));
1553
1554 if (expand)
1555 gtk_container_child_notify_by_pspec (container, child, child_props[CHILD_PROP_EXPAND]);
1556 if (!fill)
1557 gtk_container_child_notify_by_pspec (container, child, child_props[CHILD_PROP_FILL]);
1558 if (padding != 0)
1559 gtk_container_child_notify_by_pspec (container, child, child_props[CHILD_PROP_PADDING]);
1560 if (pack_type != GTK_PACK_START)
1561 gtk_container_child_notify_by_pspec (container, child, child_props[CHILD_PROP_PACK_TYPE]);
1562 gtk_container_child_notify_by_pspec (container, child, child_props[CHILD_PROP_POSITION]);
1563
1564 gtk_widget_thaw_child_notify (child);
1565
1566 return child_info;
1567 }
1568
1569 static void
gtk_box_get_size(GtkWidget * widget,GtkOrientation orientation,gint * minimum_size,gint * natural_size,gint * minimum_baseline,gint * natural_baseline)1570 gtk_box_get_size (GtkWidget *widget,
1571 GtkOrientation orientation,
1572 gint *minimum_size,
1573 gint *natural_size,
1574 gint *minimum_baseline,
1575 gint *natural_baseline)
1576 {
1577 GtkBox *box;
1578 GtkBoxPrivate *private;
1579 GList *children;
1580 gint nvis_children;
1581 gint minimum, natural;
1582 gint minimum_above, natural_above;
1583 gint minimum_below, natural_below;
1584 gboolean have_baseline;
1585 gint min_baseline, nat_baseline;
1586 gint center_min, center_nat;
1587
1588 box = GTK_BOX (widget);
1589 private = box->priv;
1590
1591 have_baseline = FALSE;
1592 minimum = natural = 0;
1593 minimum_above = natural_above = 0;
1594 minimum_below = natural_below = 0;
1595 min_baseline = nat_baseline = -1;
1596
1597 nvis_children = 0;
1598
1599 center_min = center_nat = 0;
1600
1601 for (children = private->children; children; children = children->next)
1602 {
1603 GtkBoxChild *child = children->data;
1604
1605 if (_gtk_widget_get_visible (child->widget))
1606 {
1607 gint child_minimum, child_natural;
1608 gint child_minimum_baseline = -1, child_natural_baseline = -1;
1609
1610 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1611 gtk_widget_get_preferred_width (child->widget,
1612 &child_minimum, &child_natural);
1613 else
1614 gtk_widget_get_preferred_height_and_baseline_for_width (child->widget, -1,
1615 &child_minimum, &child_natural,
1616 &child_minimum_baseline, &child_natural_baseline);
1617
1618 if (private->orientation == orientation)
1619 {
1620 if (private->homogeneous)
1621 {
1622 if (child == private->center)
1623 {
1624 center_min = child_minimum + child->padding * 2;
1625 center_nat = child_natural + child->padding * 2;
1626 }
1627 else
1628 {
1629 gint largest;
1630
1631 largest = child_minimum + child->padding * 2;
1632 minimum = MAX (minimum, largest);
1633
1634 largest = child_natural + child->padding * 2;
1635 natural = MAX (natural, largest);
1636 }
1637 }
1638 else
1639 {
1640 minimum += child_minimum + child->padding * 2;
1641 natural += child_natural + child->padding * 2;
1642 }
1643 }
1644 else
1645 {
1646 if (child_minimum_baseline >= 0)
1647 {
1648 have_baseline = TRUE;
1649 minimum_below = MAX (minimum_below, child_minimum - child_minimum_baseline);
1650 natural_below = MAX (natural_below, child_natural - child_natural_baseline);
1651 minimum_above = MAX (minimum_above, child_minimum_baseline);
1652 natural_above = MAX (natural_above, child_natural_baseline);
1653 }
1654 else
1655 {
1656 /* The biggest mins and naturals in the opposing orientation */
1657 minimum = MAX (minimum, child_minimum);
1658 natural = MAX (natural, child_natural);
1659 }
1660 }
1661
1662 nvis_children += 1;
1663 }
1664 }
1665
1666 if (nvis_children > 0 && private->orientation == orientation)
1667 {
1668 if (private->homogeneous)
1669 {
1670 if (center_min > 0)
1671 {
1672 minimum = minimum * (nvis_children - 1) + center_min;
1673 natural = natural * (nvis_children - 1) + center_nat;
1674 }
1675 else
1676 {
1677 minimum *= nvis_children;
1678 natural *= nvis_children;
1679 }
1680 }
1681 minimum += (nvis_children - 1) * private->spacing;
1682 natural += (nvis_children - 1) * private->spacing;
1683 }
1684
1685 minimum = MAX (minimum, minimum_below + minimum_above);
1686 natural = MAX (natural, natural_below + natural_above);
1687
1688 if (have_baseline)
1689 {
1690 switch (private->baseline_pos)
1691 {
1692 case GTK_BASELINE_POSITION_TOP:
1693 min_baseline = minimum_above;
1694 nat_baseline = natural_above;
1695 break;
1696 case GTK_BASELINE_POSITION_CENTER:
1697 min_baseline = minimum_above + (minimum - (minimum_above + minimum_below)) / 2;
1698 nat_baseline = natural_above + (natural - (natural_above + natural_below)) / 2;
1699 break;
1700 case GTK_BASELINE_POSITION_BOTTOM:
1701 min_baseline = minimum - minimum_below;
1702 nat_baseline = natural - natural_below;
1703 break;
1704 }
1705 }
1706
1707 if (minimum_size)
1708 *minimum_size = minimum;
1709
1710 if (natural_size)
1711 *natural_size = natural;
1712
1713 if (minimum_baseline)
1714 *minimum_baseline = min_baseline;
1715
1716 if (natural_baseline)
1717 *natural_baseline = nat_baseline;
1718 }
1719
1720 static void
gtk_box_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)1721 gtk_box_get_preferred_width (GtkWidget *widget,
1722 gint *minimum,
1723 gint *natural)
1724 {
1725 gtk_css_gadget_get_preferred_size (GTK_BOX (widget)->priv->gadget,
1726 GTK_ORIENTATION_HORIZONTAL,
1727 -1,
1728 minimum, natural,
1729 NULL, NULL);
1730 }
1731
1732 static void
gtk_box_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)1733 gtk_box_get_preferred_height (GtkWidget *widget,
1734 gint *minimum,
1735 gint *natural)
1736 {
1737 gtk_css_gadget_get_preferred_size (GTK_BOX (widget)->priv->gadget,
1738 GTK_ORIENTATION_VERTICAL,
1739 -1,
1740 minimum, natural,
1741 NULL, NULL);
1742 }
1743
1744 static void
gtk_box_compute_size_for_opposing_orientation(GtkBox * box,gint avail_size,gint * minimum_size,gint * natural_size,gint * minimum_baseline,gint * natural_baseline)1745 gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
1746 gint avail_size,
1747 gint *minimum_size,
1748 gint *natural_size,
1749 gint *minimum_baseline,
1750 gint *natural_baseline)
1751 {
1752 GtkBoxPrivate *private = box->priv;
1753 GtkBoxChild *child;
1754 GList *children;
1755 gint nvis_children;
1756 gint nexpand_children;
1757 gint computed_minimum = 0, computed_natural = 0;
1758 gint computed_minimum_above = 0, computed_natural_above = 0;
1759 gint computed_minimum_below = 0, computed_natural_below = 0;
1760 gint computed_minimum_baseline = -1, computed_natural_baseline = -1;
1761 GtkRequestedSize *sizes;
1762 GtkPackType packing;
1763 gint size, extra, i;
1764 gint child_size, child_minimum, child_natural;
1765 gint child_minimum_baseline, child_natural_baseline;
1766 gint n_extra_widgets = 0;
1767 gboolean have_baseline;
1768
1769 count_expand_children (box, &nvis_children, &nexpand_children);
1770
1771 if (nvis_children <= 0)
1772 return;
1773
1774 sizes = g_newa (GtkRequestedSize, nvis_children);
1775 memset (sizes, 0, nvis_children * sizeof (GtkRequestedSize));
1776 size = avail_size - (nvis_children - 1) * private->spacing;
1777
1778 /* Retrieve desired size for visible children */
1779 for (i = 0, children = private->children; children; children = children->next)
1780 {
1781 child = children->data;
1782
1783 if (_gtk_widget_get_visible (child->widget))
1784 {
1785 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1786 gtk_widget_get_preferred_width (child->widget,
1787 &sizes[i].minimum_size,
1788 &sizes[i].natural_size);
1789 else
1790 gtk_widget_get_preferred_height (child->widget,
1791 &sizes[i].minimum_size,
1792 &sizes[i].natural_size);
1793
1794 /* Assert the api is working properly */
1795 if (sizes[i].minimum_size < 0)
1796 g_error ("GtkBox child %s minimum %s: %d < 0",
1797 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1798 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1799 sizes[i].minimum_size);
1800
1801 if (sizes[i].natural_size < sizes[i].minimum_size)
1802 g_error ("GtkBox child %s natural %s: %d < minimum %d",
1803 gtk_widget_get_name (GTK_WIDGET (child->widget)),
1804 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1805 sizes[i].natural_size,
1806 sizes[i].minimum_size);
1807
1808 size -= sizes[i].minimum_size;
1809 size -= child->padding * 2;
1810
1811 sizes[i].data = child;
1812
1813 i += 1;
1814 }
1815 }
1816
1817 if (private->homogeneous)
1818 {
1819 /* If were homogenous we still need to run the above loop to get the
1820 * minimum sizes for children that are not going to fill
1821 */
1822 size = avail_size - (nvis_children - 1) * private->spacing;
1823 extra = size / nvis_children;
1824 n_extra_widgets = size % nvis_children;
1825 }
1826 else
1827 {
1828 /* Bring children up to size first */
1829 size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
1830
1831 /* Calculate space which hasn't distributed yet,
1832 * and is available for expanding children.
1833 */
1834 if (nexpand_children > 0)
1835 {
1836 extra = size / nexpand_children;
1837 n_extra_widgets = size % nexpand_children;
1838 }
1839 else
1840 extra = 0;
1841 }
1842
1843 have_baseline = FALSE;
1844 /* Allocate child positions. */
1845 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1846 {
1847 for (i = 0, children = private->children;
1848 children;
1849 children = children->next)
1850 {
1851 child = children->data;
1852
1853 /* If widget is not visible, skip it. */
1854 if (!_gtk_widget_get_visible (child->widget))
1855 continue;
1856
1857 /* If widget is packed differently skip it, but still increment i,
1858 * since widget is visible and will be handled in next loop iteration.
1859 */
1860 if (child->pack != packing)
1861 {
1862 i++;
1863 continue;
1864 }
1865
1866 if (child->pack == packing)
1867 {
1868 /* Assign the child's size. */
1869 if (private->homogeneous)
1870 {
1871 child_size = extra;
1872
1873 if (n_extra_widgets > 0)
1874 {
1875 child_size++;
1876 n_extra_widgets--;
1877 }
1878 }
1879 else
1880 {
1881 child_size = sizes[i].minimum_size + child->padding * 2;
1882
1883 if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
1884 {
1885 child_size += extra;
1886
1887 if (n_extra_widgets > 0)
1888 {
1889 child_size++;
1890 n_extra_widgets--;
1891 }
1892 }
1893 }
1894
1895 if (child->fill)
1896 {
1897 child_size = MAX (1, child_size - child->padding * 2);
1898 }
1899 else
1900 {
1901 child_size = sizes[i].minimum_size;
1902 }
1903
1904
1905 child_minimum_baseline = child_natural_baseline = -1;
1906 /* Assign the child's position. */
1907 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1908 gtk_widget_get_preferred_height_and_baseline_for_width (child->widget, child_size,
1909 &child_minimum, &child_natural,
1910 &child_minimum_baseline, &child_natural_baseline);
1911 else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1912 gtk_widget_get_preferred_width_for_height (child->widget,
1913 child_size, &child_minimum, &child_natural);
1914
1915 if (child_minimum_baseline >= 0)
1916 {
1917 have_baseline = TRUE;
1918 computed_minimum_below = MAX (computed_minimum_below, child_minimum - child_minimum_baseline);
1919 computed_natural_below = MAX (computed_natural_below, child_natural - child_natural_baseline);
1920 computed_minimum_above = MAX (computed_minimum_above, child_minimum_baseline);
1921 computed_natural_above = MAX (computed_natural_above, child_natural_baseline);
1922 }
1923 else
1924 {
1925 computed_minimum = MAX (computed_minimum, child_minimum);
1926 computed_natural = MAX (computed_natural, child_natural);
1927 }
1928 }
1929 i += 1;
1930 }
1931 }
1932
1933 if (have_baseline)
1934 {
1935 computed_minimum = MAX (computed_minimum, computed_minimum_below + computed_minimum_above);
1936 computed_natural = MAX (computed_natural, computed_natural_below + computed_natural_above);
1937 switch (private->baseline_pos)
1938 {
1939 case GTK_BASELINE_POSITION_TOP:
1940 computed_minimum_baseline = computed_minimum_above;
1941 computed_natural_baseline = computed_natural_above;
1942 break;
1943 case GTK_BASELINE_POSITION_CENTER:
1944 computed_minimum_baseline = computed_minimum_above + MAX((computed_minimum - (computed_minimum_above + computed_minimum_below)) / 2, 0);
1945 computed_natural_baseline = computed_natural_above + MAX((computed_natural - (computed_natural_above + computed_natural_below)) / 2, 0);
1946 break;
1947 case GTK_BASELINE_POSITION_BOTTOM:
1948 computed_minimum_baseline = computed_minimum - computed_minimum_below;
1949 computed_natural_baseline = computed_natural - computed_natural_below;
1950 break;
1951 }
1952 }
1953
1954 if (minimum_baseline)
1955 *minimum_baseline = computed_minimum_baseline;
1956 if (natural_baseline)
1957 *natural_baseline = computed_natural_baseline;
1958
1959 if (minimum_size)
1960 *minimum_size = computed_minimum;
1961 if (natural_size)
1962 *natural_size = MAX (computed_natural, computed_natural_below + computed_natural_above);
1963 }
1964
1965 static void
gtk_box_compute_size_for_orientation(GtkBox * box,gint avail_size,gint * minimum_size,gint * natural_size)1966 gtk_box_compute_size_for_orientation (GtkBox *box,
1967 gint avail_size,
1968 gint *minimum_size,
1969 gint *natural_size)
1970 {
1971 GtkBoxPrivate *private = box->priv;
1972 GList *children;
1973 gint nvis_children = 0;
1974 gint required_size = 0, required_natural = 0, child_size, child_natural;
1975 gint largest_child = 0, largest_natural = 0;
1976
1977 for (children = private->children; children != NULL;
1978 children = children->next)
1979 {
1980 GtkBoxChild *child = children->data;
1981
1982 if (_gtk_widget_get_visible (child->widget))
1983 {
1984
1985 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1986 gtk_widget_get_preferred_width_for_height (child->widget,
1987 avail_size, &child_size, &child_natural);
1988 else
1989 gtk_widget_get_preferred_height_for_width (child->widget,
1990 avail_size, &child_size, &child_natural);
1991
1992
1993 child_size += child->padding * 2;
1994 child_natural += child->padding * 2;
1995
1996 if (child_size > largest_child)
1997 largest_child = child_size;
1998
1999 if (child_natural > largest_natural)
2000 largest_natural = child_natural;
2001
2002 required_size += child_size;
2003 required_natural += child_natural;
2004
2005 nvis_children += 1;
2006 }
2007 }
2008
2009 if (nvis_children > 0)
2010 {
2011 if (private->homogeneous)
2012 {
2013 required_size = largest_child * nvis_children;
2014 required_natural = largest_natural * nvis_children;
2015 }
2016
2017 required_size += (nvis_children - 1) * private->spacing;
2018 required_natural += (nvis_children - 1) * private->spacing;
2019 }
2020
2021 if (minimum_size)
2022 *minimum_size = required_size;
2023
2024 if (natural_size)
2025 *natural_size = required_natural;
2026 }
2027
2028 static void
gtk_box_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum,gint * natural)2029 gtk_box_get_preferred_width_for_height (GtkWidget *widget,
2030 gint height,
2031 gint *minimum,
2032 gint *natural)
2033 {
2034 gtk_css_gadget_get_preferred_size (GTK_BOX (widget)->priv->gadget,
2035 GTK_ORIENTATION_HORIZONTAL,
2036 height,
2037 minimum, natural,
2038 NULL, NULL);
2039 }
2040
2041 static void
gtk_box_get_preferred_height_and_baseline_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)2042 gtk_box_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
2043 gint width,
2044 gint *minimum,
2045 gint *natural,
2046 gint *minimum_baseline,
2047 gint *natural_baseline)
2048 {
2049 gtk_css_gadget_get_preferred_size (GTK_BOX (widget)->priv->gadget,
2050 GTK_ORIENTATION_VERTICAL,
2051 width,
2052 minimum, natural,
2053 minimum_baseline, natural_baseline);
2054 }
2055
2056 static void
gtk_box_get_content_size(GtkCssGadget * gadget,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline,gpointer unused)2057 gtk_box_get_content_size (GtkCssGadget *gadget,
2058 GtkOrientation orientation,
2059 gint for_size,
2060 gint *minimum,
2061 gint *natural,
2062 gint *minimum_baseline,
2063 gint *natural_baseline,
2064 gpointer unused)
2065 {
2066 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
2067 GtkBox *box = GTK_BOX (widget);
2068 GtkBoxPrivate *private = box->priv;
2069
2070 if (for_size < 0)
2071 gtk_box_get_size (widget, orientation, minimum, natural, minimum_baseline, natural_baseline);
2072 else
2073 {
2074 if (private->orientation != orientation)
2075 gtk_box_compute_size_for_opposing_orientation (box, for_size, minimum, natural, minimum_baseline, natural_baseline);
2076 else
2077 {
2078 if (minimum_baseline)
2079 *minimum_baseline = -1;
2080 if (natural_baseline)
2081 *natural_baseline = -1;
2082 gtk_box_compute_size_for_orientation (box, for_size, minimum, natural);
2083 }
2084 }
2085 }
2086
2087 static void
gtk_box_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum_height,gint * natural_height)2088 gtk_box_get_preferred_height_for_width (GtkWidget *widget,
2089 gint width,
2090 gint *minimum_height,
2091 gint *natural_height)
2092 {
2093 gtk_box_get_preferred_height_and_baseline_for_width (widget, width, minimum_height, natural_height, NULL, NULL);
2094 }
2095
2096 static void
gtk_box_init(GtkBox * box)2097 gtk_box_init (GtkBox *box)
2098 {
2099 GtkBoxPrivate *private;
2100
2101 box->priv = gtk_box_get_instance_private (box);
2102 private = box->priv;
2103
2104 gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
2105
2106 private->orientation = GTK_ORIENTATION_HORIZONTAL;
2107 private->children = NULL;
2108
2109 private->default_expand = FALSE;
2110 private->homogeneous = FALSE;
2111 private->spacing = 0;
2112 private->spacing_set = FALSE;
2113 private->baseline_pos = GTK_BASELINE_POSITION_CENTER;
2114
2115 private->gadget = gtk_css_custom_gadget_new_for_node (gtk_widget_get_css_node (GTK_WIDGET (box)),
2116 GTK_WIDGET (box),
2117 gtk_box_get_content_size,
2118 gtk_box_allocate_contents,
2119 gtk_box_draw_contents,
2120 NULL,
2121 NULL);
2122
2123 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (box));
2124 }
2125
2126 GtkCssGadget *
gtk_box_get_gadget(GtkBox * box)2127 gtk_box_get_gadget (GtkBox *box)
2128 {
2129 return box->priv->gadget;
2130 }
2131
2132 /**
2133 * gtk_box_new:
2134 * @orientation: the box’s orientation.
2135 * @spacing: the number of pixels to place by default between children.
2136 *
2137 * Creates a new #GtkBox.
2138 *
2139 * Returns: a new #GtkBox.
2140 *
2141 * Since: 3.0
2142 **/
2143 GtkWidget*
gtk_box_new(GtkOrientation orientation,gint spacing)2144 gtk_box_new (GtkOrientation orientation,
2145 gint spacing)
2146 {
2147 return g_object_new (GTK_TYPE_BOX,
2148 "orientation", orientation,
2149 "spacing", spacing,
2150 NULL);
2151 }
2152
2153 /**
2154 * gtk_box_pack_start:
2155 * @box: a #GtkBox
2156 * @child: the #GtkWidget to be added to @box
2157 * @expand: %TRUE if the new child is to be given extra space allocated
2158 * to @box. The extra space will be divided evenly between all children
2159 * that use this option
2160 * @fill: %TRUE if space given to @child by the @expand option is
2161 * actually allocated to @child, rather than just padding it. This
2162 * parameter has no effect if @expand is set to %FALSE. A child is
2163 * always allocated the full height of a horizontal #GtkBox and the full width
2164 * of a vertical #GtkBox. This option affects the other dimension
2165 * @padding: extra space in pixels to put between this child and its
2166 * neighbors, over and above the global amount specified by
2167 * #GtkBox:spacing property. If @child is a widget at one of the
2168 * reference ends of @box, then @padding pixels are also put between
2169 * @child and the reference edge of @box
2170 *
2171 * Adds @child to @box, packed with reference to the start of @box.
2172 * The @child is packed after any other child packed with reference
2173 * to the start of @box.
2174 */
2175 void
gtk_box_pack_start(GtkBox * box,GtkWidget * child,gboolean expand,gboolean fill,guint padding)2176 gtk_box_pack_start (GtkBox *box,
2177 GtkWidget *child,
2178 gboolean expand,
2179 gboolean fill,
2180 guint padding)
2181 {
2182 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
2183 }
2184
2185 /**
2186 * gtk_box_pack_end:
2187 * @box: a #GtkBox
2188 * @child: the #GtkWidget to be added to @box
2189 * @expand: %TRUE if the new child is to be given extra space allocated
2190 * to @box. The extra space will be divided evenly between all children
2191 * of @box that use this option
2192 * @fill: %TRUE if space given to @child by the @expand option is
2193 * actually allocated to @child, rather than just padding it. This
2194 * parameter has no effect if @expand is set to %FALSE. A child is
2195 * always allocated the full height of a horizontal #GtkBox and the full width
2196 * of a vertical #GtkBox. This option affects the other dimension
2197 * @padding: extra space in pixels to put between this child and its
2198 * neighbors, over and above the global amount specified by
2199 * #GtkBox:spacing property. If @child is a widget at one of the
2200 * reference ends of @box, then @padding pixels are also put between
2201 * @child and the reference edge of @box
2202 *
2203 * Adds @child to @box, packed with reference to the end of @box.
2204 * The @child is packed after (away from end of) any other child
2205 * packed with reference to the end of @box.
2206 */
2207 void
gtk_box_pack_end(GtkBox * box,GtkWidget * child,gboolean expand,gboolean fill,guint padding)2208 gtk_box_pack_end (GtkBox *box,
2209 GtkWidget *child,
2210 gboolean expand,
2211 gboolean fill,
2212 guint padding)
2213 {
2214 gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
2215 }
2216
2217 /**
2218 * gtk_box_set_homogeneous:
2219 * @box: a #GtkBox
2220 * @homogeneous: a boolean value, %TRUE to create equal allotments,
2221 * %FALSE for variable allotments
2222 *
2223 * Sets the #GtkBox:homogeneous property of @box, controlling
2224 * whether or not all children of @box are given equal space
2225 * in the box.
2226 */
2227 void
gtk_box_set_homogeneous(GtkBox * box,gboolean homogeneous)2228 gtk_box_set_homogeneous (GtkBox *box,
2229 gboolean homogeneous)
2230 {
2231 GtkBoxPrivate *private;
2232
2233 g_return_if_fail (GTK_IS_BOX (box));
2234
2235 private = box->priv;
2236
2237 homogeneous = homogeneous != FALSE;
2238
2239 if (private->homogeneous != homogeneous)
2240 {
2241 private->homogeneous = homogeneous;
2242 g_object_notify_by_pspec (G_OBJECT (box), props[PROP_HOMOGENEOUS]);
2243 gtk_widget_queue_resize (GTK_WIDGET (box));
2244 }
2245 }
2246
2247 /**
2248 * gtk_box_get_homogeneous:
2249 * @box: a #GtkBox
2250 *
2251 * Returns whether the box is homogeneous (all children are the
2252 * same size). See gtk_box_set_homogeneous().
2253 *
2254 * Returns: %TRUE if the box is homogeneous.
2255 **/
2256 gboolean
gtk_box_get_homogeneous(GtkBox * box)2257 gtk_box_get_homogeneous (GtkBox *box)
2258 {
2259 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
2260
2261 return box->priv->homogeneous;
2262 }
2263
2264 /**
2265 * gtk_box_set_spacing:
2266 * @box: a #GtkBox
2267 * @spacing: the number of pixels to put between children
2268 *
2269 * Sets the #GtkBox:spacing property of @box, which is the
2270 * number of pixels to place between children of @box.
2271 */
2272 void
gtk_box_set_spacing(GtkBox * box,gint spacing)2273 gtk_box_set_spacing (GtkBox *box,
2274 gint spacing)
2275 {
2276 GtkBoxPrivate *private;
2277
2278 g_return_if_fail (GTK_IS_BOX (box));
2279
2280 private = box->priv;
2281
2282 if (private->spacing != spacing)
2283 {
2284 private->spacing = spacing;
2285 _gtk_box_set_spacing_set (box, TRUE);
2286
2287 g_object_notify_by_pspec (G_OBJECT (box), props[PROP_SPACING]);
2288
2289 gtk_widget_queue_resize (GTK_WIDGET (box));
2290 }
2291 }
2292
2293 /**
2294 * gtk_box_get_spacing:
2295 * @box: a #GtkBox
2296 *
2297 * Gets the value set by gtk_box_set_spacing().
2298 *
2299 * Returns: spacing between children
2300 **/
2301 gint
gtk_box_get_spacing(GtkBox * box)2302 gtk_box_get_spacing (GtkBox *box)
2303 {
2304 g_return_val_if_fail (GTK_IS_BOX (box), 0);
2305
2306 return box->priv->spacing;
2307 }
2308
2309 /**
2310 * gtk_box_set_baseline_position:
2311 * @box: a #GtkBox
2312 * @position: a #GtkBaselinePosition
2313 *
2314 * Sets the baseline position of a box. This affects
2315 * only horizontal boxes with at least one baseline aligned
2316 * child. If there is more vertical space available than requested,
2317 * and the baseline is not allocated by the parent then
2318 * @position is used to allocate the baseline wrt the
2319 * extra space available.
2320 *
2321 * Since: 3.10
2322 */
2323 void
gtk_box_set_baseline_position(GtkBox * box,GtkBaselinePosition position)2324 gtk_box_set_baseline_position (GtkBox *box,
2325 GtkBaselinePosition position)
2326 {
2327 GtkBoxPrivate *private;
2328
2329 g_return_if_fail (GTK_IS_BOX (box));
2330
2331 private = box->priv;
2332
2333 if (private->baseline_pos != position)
2334 {
2335 private->baseline_pos = position;
2336
2337 g_object_notify_by_pspec (G_OBJECT (box), props[PROP_BASELINE_POSITION]);
2338
2339 gtk_widget_queue_resize (GTK_WIDGET (box));
2340 }
2341 }
2342
2343 /**
2344 * gtk_box_get_baseline_position:
2345 * @box: a #GtkBox
2346 *
2347 * Gets the value set by gtk_box_set_baseline_position().
2348 *
2349 * Returns: the baseline position
2350 *
2351 * Since: 3.10
2352 **/
2353 GtkBaselinePosition
gtk_box_get_baseline_position(GtkBox * box)2354 gtk_box_get_baseline_position (GtkBox *box)
2355 {
2356 g_return_val_if_fail (GTK_IS_BOX (box), GTK_BASELINE_POSITION_CENTER);
2357
2358 return box->priv->baseline_pos;
2359 }
2360
2361
2362 void
_gtk_box_set_spacing_set(GtkBox * box,gboolean spacing_set)2363 _gtk_box_set_spacing_set (GtkBox *box,
2364 gboolean spacing_set)
2365 {
2366 GtkBoxPrivate *private;
2367
2368 g_return_if_fail (GTK_IS_BOX (box));
2369
2370 private = box->priv;
2371
2372 private->spacing_set = spacing_set ? TRUE : FALSE;
2373 }
2374
2375 gboolean
_gtk_box_get_spacing_set(GtkBox * box)2376 _gtk_box_get_spacing_set (GtkBox *box)
2377 {
2378 GtkBoxPrivate *private;
2379
2380 g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
2381
2382 private = box->priv;
2383
2384 return private->spacing_set;
2385 }
2386
2387 /**
2388 * gtk_box_reorder_child:
2389 * @box: a #GtkBox
2390 * @child: the #GtkWidget to move
2391 * @position: the new position for @child in the list of children
2392 * of @box, starting from 0. If negative, indicates the end of
2393 * the list
2394 *
2395 * Moves @child to a new @position in the list of @box children.
2396 * The list contains widgets packed #GTK_PACK_START
2397 * as well as widgets packed #GTK_PACK_END, in the order that these
2398 * widgets were added to @box.
2399 *
2400 * A widget’s position in the @box children list determines where
2401 * the widget is packed into @box. A child widget at some position
2402 * in the list will be packed just after all other widgets of the
2403 * same packing type that appear earlier in the list.
2404 */
2405 void
gtk_box_reorder_child(GtkBox * box,GtkWidget * child,gint position)2406 gtk_box_reorder_child (GtkBox *box,
2407 GtkWidget *child,
2408 gint position)
2409 {
2410 GtkBoxPrivate *priv;
2411 GList *old_link;
2412 GList *new_link;
2413 GtkBoxChild *child_info = NULL;
2414 gint old_position;
2415
2416 g_return_if_fail (GTK_IS_BOX (box));
2417 g_return_if_fail (GTK_IS_WIDGET (child));
2418
2419 priv = box->priv;
2420
2421 old_link = priv->children;
2422 old_position = 0;
2423 while (old_link)
2424 {
2425 child_info = old_link->data;
2426 if (child_info->widget == child)
2427 break;
2428
2429 old_link = old_link->next;
2430 old_position++;
2431 }
2432
2433 g_return_if_fail (old_link != NULL);
2434
2435 if (position == old_position)
2436 return;
2437
2438 priv->children = g_list_delete_link (priv->children, old_link);
2439
2440 if (position < 0)
2441 new_link = NULL;
2442 else
2443 new_link = g_list_nth (priv->children, position);
2444
2445 priv->children = g_list_insert_before (priv->children, new_link, child_info);
2446 gtk_box_update_child_css_position (box, child_info);
2447
2448 gtk_container_child_notify_by_pspec (GTK_CONTAINER (box), child, child_props[CHILD_PROP_POSITION]);
2449 if (_gtk_widget_get_visible (child) &&
2450 _gtk_widget_get_visible (GTK_WIDGET (box)))
2451 {
2452 gtk_widget_queue_resize (child);
2453 }
2454 }
2455
2456 /**
2457 * gtk_box_query_child_packing:
2458 * @box: a #GtkBox
2459 * @child: the #GtkWidget of the child to query
2460 * @expand: (out): pointer to return location for expand child
2461 * property
2462 * @fill: (out): pointer to return location for fill child
2463 * property
2464 * @padding: (out): pointer to return location for padding
2465 * child property
2466 * @pack_type: (out): pointer to return location for pack-type
2467 * child property
2468 *
2469 * Obtains information about how @child is packed into @box.
2470 */
2471 void
gtk_box_query_child_packing(GtkBox * box,GtkWidget * child,gboolean * expand,gboolean * fill,guint * padding,GtkPackType * pack_type)2472 gtk_box_query_child_packing (GtkBox *box,
2473 GtkWidget *child,
2474 gboolean *expand,
2475 gboolean *fill,
2476 guint *padding,
2477 GtkPackType *pack_type)
2478 {
2479 GtkBoxPrivate *private;
2480 GList *list;
2481 GtkBoxChild *child_info = NULL;
2482
2483 g_return_if_fail (GTK_IS_BOX (box));
2484 g_return_if_fail (GTK_IS_WIDGET (child));
2485
2486 private = box->priv;
2487
2488 list = private->children;
2489 while (list)
2490 {
2491 child_info = list->data;
2492 if (child_info->widget == child)
2493 break;
2494
2495 list = list->next;
2496 }
2497
2498 if (list)
2499 {
2500 if (expand)
2501 *expand = child_info->expand;
2502 if (fill)
2503 *fill = child_info->fill;
2504 if (padding)
2505 *padding = child_info->padding;
2506 if (pack_type)
2507 *pack_type = child_info->pack;
2508 }
2509 }
2510
2511 /**
2512 * gtk_box_set_child_packing:
2513 * @box: a #GtkBox
2514 * @child: the #GtkWidget of the child to set
2515 * @expand: the new value of the expand child property
2516 * @fill: the new value of the fill child property
2517 * @padding: the new value of the padding child property
2518 * @pack_type: the new value of the pack-type child property
2519 *
2520 * Sets the way @child is packed into @box.
2521 */
2522 void
gtk_box_set_child_packing(GtkBox * box,GtkWidget * child,gboolean expand,gboolean fill,guint padding,GtkPackType pack_type)2523 gtk_box_set_child_packing (GtkBox *box,
2524 GtkWidget *child,
2525 gboolean expand,
2526 gboolean fill,
2527 guint padding,
2528 GtkPackType pack_type)
2529 {
2530 GtkBoxPrivate *private;
2531 GList *list;
2532 GtkBoxChild *child_info = NULL;
2533
2534 g_return_if_fail (GTK_IS_BOX (box));
2535 g_return_if_fail (GTK_IS_WIDGET (child));
2536
2537 private = box->priv;
2538
2539 list = private->children;
2540 while (list)
2541 {
2542 child_info = list->data;
2543 if (child_info->widget == child)
2544 break;
2545
2546 list = list->next;
2547 }
2548
2549 gtk_widget_freeze_child_notify (child);
2550 if (list)
2551 {
2552 expand = expand != FALSE;
2553
2554 if (child_info->expand != expand)
2555 {
2556 child_info->expand = expand;
2557 gtk_container_child_notify_by_pspec (GTK_CONTAINER (box), child, child_props[CHILD_PROP_EXPAND]);
2558 }
2559
2560 fill = fill != FALSE;
2561
2562 if (child_info->fill != fill)
2563 {
2564 child_info->fill = fill;
2565 gtk_container_child_notify_by_pspec (GTK_CONTAINER (box), child, child_props[CHILD_PROP_FILL]);
2566 }
2567
2568 if (child_info->padding != padding)
2569 {
2570 child_info->padding = padding;
2571 gtk_container_child_notify_by_pspec (GTK_CONTAINER (box), child, child_props[CHILD_PROP_PADDING]);
2572 }
2573
2574 if (pack_type != GTK_PACK_END)
2575 pack_type = GTK_PACK_START;
2576 if (child_info->pack != pack_type)
2577 {
2578 child_info->pack = pack_type;
2579 gtk_box_update_child_css_position (box, child_info);
2580 gtk_container_child_notify_by_pspec (GTK_CONTAINER (box), child, child_props[CHILD_PROP_PACK_TYPE]);
2581 }
2582
2583 if (_gtk_widget_get_visible (child) &&
2584 _gtk_widget_get_visible (GTK_WIDGET (box)))
2585 gtk_widget_queue_resize (child);
2586 }
2587 gtk_widget_thaw_child_notify (child);
2588 }
2589
2590 void
_gtk_box_set_old_defaults(GtkBox * box)2591 _gtk_box_set_old_defaults (GtkBox *box)
2592 {
2593 GtkBoxPrivate *private;
2594
2595 g_return_if_fail (GTK_IS_BOX (box));
2596
2597 private = box->priv;
2598
2599 private->default_expand = TRUE;
2600 }
2601
2602 static void
gtk_box_add(GtkContainer * container,GtkWidget * widget)2603 gtk_box_add (GtkContainer *container,
2604 GtkWidget *widget)
2605 {
2606 GtkBoxPrivate *priv = GTK_BOX (container)->priv;
2607
2608 gtk_box_pack_start (GTK_BOX (container), widget,
2609 priv->default_expand,
2610 TRUE,
2611 0);
2612 }
2613
2614 static void
gtk_box_remove(GtkContainer * container,GtkWidget * widget)2615 gtk_box_remove (GtkContainer *container,
2616 GtkWidget *widget)
2617 {
2618 GtkBox *box = GTK_BOX (container);
2619 GtkBoxPrivate *priv = box->priv;
2620 GtkBoxChild *child;
2621 GList *children;
2622
2623 children = priv->children;
2624 while (children)
2625 {
2626 child = children->data;
2627
2628 if (child->widget == widget)
2629 {
2630 gboolean was_visible;
2631
2632 if (priv->center == child)
2633 priv->center = NULL;
2634
2635 was_visible = _gtk_widget_get_visible (widget);
2636 gtk_widget_unparent (widget);
2637
2638 priv->children = g_list_remove_link (priv->children, children);
2639 g_list_free (children);
2640 g_free (child);
2641
2642 /* queue resize regardless of gtk_widget_get_visible (container),
2643 * since that's what is needed by toplevels.
2644 */
2645 if (was_visible)
2646 {
2647 gtk_widget_queue_resize (GTK_WIDGET (container));
2648 }
2649
2650 break;
2651 }
2652
2653 children = children->next;
2654 }
2655 }
2656
2657 static void
gtk_box_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)2658 gtk_box_forall (GtkContainer *container,
2659 gboolean include_internals,
2660 GtkCallback callback,
2661 gpointer callback_data)
2662 {
2663 GtkBox *box = GTK_BOX (container);
2664 GtkBoxPrivate *priv = box->priv;
2665 GtkBoxChild *child;
2666 GList *children;
2667
2668 children = priv->children;
2669 while (children)
2670 {
2671 child = children->data;
2672 children = children->next;
2673
2674 if (child == priv->center)
2675 continue;
2676
2677 if (child->pack == GTK_PACK_START)
2678 (* callback) (child->widget, callback_data);
2679 }
2680
2681 if (priv->center)
2682 (* callback) (priv->center->widget, callback_data);
2683
2684 children = g_list_last (priv->children);
2685 while (children)
2686 {
2687 child = children->data;
2688 children = children->prev;
2689
2690 if (child == priv->center)
2691 continue;
2692
2693 if (child->pack == GTK_PACK_END)
2694 (* callback) (child->widget, callback_data);
2695 }
2696 }
2697
2698 GList *
_gtk_box_get_children(GtkBox * box)2699 _gtk_box_get_children (GtkBox *box)
2700 {
2701 GtkBoxPrivate *priv;
2702 GtkBoxChild *child;
2703 GList *children;
2704 GList *retval = NULL;
2705
2706 g_return_val_if_fail (GTK_IS_BOX (box), NULL);
2707
2708 priv = box->priv;
2709
2710 children = priv->children;
2711 while (children)
2712 {
2713 child = children->data;
2714 children = children->next;
2715
2716 retval = g_list_prepend (retval, child->widget);
2717 }
2718
2719 return g_list_reverse (retval);
2720 }
2721
2722 /**
2723 * gtk_box_set_center_widget:
2724 * @box: a #GtkBox
2725 * @widget: (allow-none): the widget to center
2726 *
2727 * Sets a center widget; that is a child widget that will be
2728 * centered with respect to the full width of the box, even
2729 * if the children at either side take up different amounts
2730 * of space.
2731 *
2732 * Since: 3.12
2733 */
2734 void
gtk_box_set_center_widget(GtkBox * box,GtkWidget * widget)2735 gtk_box_set_center_widget (GtkBox *box,
2736 GtkWidget *widget)
2737 {
2738 GtkBoxPrivate *priv = box->priv;
2739 GtkWidget *old_center = NULL;
2740
2741 g_return_if_fail (GTK_IS_BOX (box));
2742
2743 if (priv->center)
2744 {
2745 old_center = g_object_ref (priv->center->widget);
2746 gtk_box_remove (GTK_CONTAINER (box), priv->center->widget);
2747 priv->center = NULL;
2748 }
2749
2750 if (widget)
2751 priv->center = gtk_box_pack (box, widget, FALSE, TRUE, 0, GTK_PACK_START);
2752
2753 if (old_center)
2754 g_object_unref (old_center);
2755 }
2756
2757 /**
2758 * gtk_box_get_center_widget:
2759 * @box: a #GtkBox
2760 *
2761 * Retrieves the center widget of the box.
2762 *
2763 * Returns: (transfer none) (nullable): the center widget
2764 * or %NULL in case no center widget is set.
2765 *
2766 * Since: 3.12
2767 */
2768 GtkWidget *
gtk_box_get_center_widget(GtkBox * box)2769 gtk_box_get_center_widget (GtkBox *box)
2770 {
2771 GtkBoxPrivate *priv = box->priv;
2772
2773 g_return_val_if_fail (GTK_IS_BOX (box), NULL);
2774
2775 if (priv->center)
2776 return priv->center->widget;
2777
2778 return NULL;
2779 }
2780