GtkVBox's purpose in life is
size negotiation; it passes size requests up from its
children, and then divides a size allocation among them.
This leads to the layout behavior described in the section called
GtkBox in the chapter
called GTK+ Basics.
Here is the size request implementation:
static void
gtk_vbox_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
GtkBox *box;
GtkBoxChild *child;
GtkRequisition child_requisition;
GList *children;
gint nvis_children;
gint height;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_VBOX (widget));
g_return_if_fail (requisition != NULL);
box = GTK_BOX (widget);
requisition->width = 0;
requisition->height = 0;
nvis_children = 0;
children = box->children;
while (children)
{
child = children->data;
children = children->next;
if (GTK_WIDGET_VISIBLE (child->widget))
{
gtk_widget_size_request (child->widget, &child_requisition);
if (box->homogeneous)
{
height = child_requisition.height + child->padding * 2;
requisition->height = MAX (requisition->height, height);
}
else
{
requisition->height += child_requisition.height + child->padding * 2;
}
requisition->width = MAX (requisition->width, child_requisition.width);
nvis_children += 1;
}
}
if (nvis_children > 0)
{
if (box->homogeneous)
requisition->height *= nvis_children;
requisition->height += (nvis_children - 1) * box->spacing;
}
requisition->width += GTK_CONTAINER (box)->border_width * 2;
requisition->height += GTK_CONTAINER (box)->border_width * 2;
}
|
If the box is homogenous, it multiplies the maximum child
requisition times the number of children; otherwise, it
sums the child requisitions. Then it adds padding,
spacing, and border width, as appropriate. Recall that
all containers must honor their border width, set with
gtk_container_set_border_width() and available as
the border_width field in
the GtkContainer instance
struct.
When reading this code, it may help to know that GtkBox stores a small struct for
each child widget in its
children field. The struct looks like this:
typedef struct _GtkBoxChild GtkBoxChild;
struct _GtkBoxChild
{
GtkWidget *widget;
guint16 padding;
guint expand : 1;
guint fill : 1;
guint pack : 1;
};
|
Size allocation is more complex; here, all the
box-packing flags come into play. It will probably take
you a while to fully understand this function, but there
is no need to; the important thing is to see how layout
takes place via size allocation.
static void
gtk_vbox_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkBox *box;
GtkBoxChild *child;
GList *children;
GtkAllocation child_allocation;
gint nvis_children;
gint nexpand_children;
gint child_height;
gint height;
gint extra;
gint y;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_VBOX (widget));
g_return_if_fail (allocation != NULL);
box = GTK_BOX (widget);
widget->allocation = *allocation;
nvis_children = 0;
nexpand_children = 0;
children = box->children;
while (children)
{
child = children->data;
children = children->next;
if (GTK_WIDGET_VISIBLE (child->widget))
{
nvis_children += 1;
if (child->expand)
nexpand_children += 1;
}
}
if (nvis_children > 0)
{
if (box->homogeneous)
{
height = (allocation->height -
GTK_CONTAINER (box)->border_width * 2 -
(nvis_children - 1) * box->spacing);
extra = height / nvis_children;
}
else if (nexpand_children > 0)
{
height = (gint) allocation->height - (gint) widget->requisition.height;
extra = height / nexpand_children;
}
else
{
height = 0;
extra = 0;
}
y = allocation->y + GTK_CONTAINER (box)->border_width;
child_allocation.x = allocation->x + GTK_CONTAINER (box)->border_width;
child_allocation.width = MAX (1, (gint) allocation->width - (gint) GTK_CONTAINER (box)->border_width * 2);
children = box->children;
while (children)
{
child = children->data;
children = children->next;
if ((child->pack == GTK_PACK_START) && GTK_WIDGET_VISIBLE (child->widget))
{
if (box->homogeneous)
{
if (nvis_children == 1)
child_height = height;
else
child_height = extra;
nvis_children -= 1;
height -= extra;
}
else
{
GtkRequisition child_requisition;
gtk_widget_get_child_requisition (child->widget, &child_requisition);
child_height = child_requisition.height + child->padding * 2;
if (child->expand)
{
if (nexpand_children == 1)
child_height += height;
else
child_height += extra;
nexpand_children -= 1;
height -= extra;
}
}
if (child->fill)
{
child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
child_allocation.y = y + child->padding;
}
else
{
GtkRequisition child_requisition;
gtk_widget_get_child_requisition (child->widget, &child_requisition);
child_allocation.height = child_requisition.height;
child_allocation.y = y + (child_height - child_allocation.height) / 2;
}
gtk_widget_size_allocate (child->widget, &child_allocation);
y += child_height + box->spacing;
}
}
y = allocation->y + allocation->height - GTK_CONTAINER (box)->border_width;
children = box->children;
while (children)
{
child = children->data;
children = children->next;
if ((child->pack == GTK_PACK_END) && GTK_WIDGET_VISIBLE (child->widget))
{
GtkRequisition child_requisition;
gtk_widget_get_child_requisition (child->widget, &child_requisition);
if (box->homogeneous)
{
if (nvis_children == 1)
child_height = height;
else
child_height = extra;
nvis_children -= 1;
height -= extra;
}
else
{
child_height = child_requisition.height + child->padding * 2;
if (child->expand)
{
if (nexpand_children == 1)
child_height += height;
else
child_height += extra;
nexpand_children -= 1;
height -= extra;
}
}
if (child->fill)
{
child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
child_allocation.y = y + child->padding - child_height;
}
else
{
child_allocation.height = child_requisition.height;
child_allocation.y = y + (child_height - child_allocation.height) / 2 - child_height;
}
gtk_widget_size_allocate (child->widget, &child_allocation);
y -= (child_height + box->spacing);
}
}
}
}
|