From 1041a186c73d705e6aecad1b8c288083e3eaf278 Mon Sep 17 00:00:00 2001 From: Tristan Van Berkom Date: Fri, 9 Apr 2010 21:50:33 -0400 Subject: Added get_height_for_width/get_width_for_height implementation to GtkBox. In order for natural size information to cascade correctly up through the ancestry GtkBox needs to report height-for-width and width-for-height, this patch includes an implementation for both in both orientations, one of them is commented for now as its much too cpu intensive to actually use. --- gtk/gtkbox.c | 477 +++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 351 insertions(+), 126 deletions(-) (limited to 'gtk/gtkbox.c') diff --git a/gtk/gtkbox.c b/gtk/gtkbox.c index 9daf89aad0..144124a6a6 100644 --- a/gtk/gtkbox.c +++ b/gtk/gtkbox.c @@ -76,12 +76,8 @@ struct _GtkBoxSpreading gint index; }; -static void gtk_box_get_desired_size (GtkExtendedLayout *layout, - GtkRequisition *minimum_size, - GtkRequisition *natural_size); static void gtk_box_size_allocate (GtkWidget *widget, GtkAllocation *allocation); -static void gtk_box_layout_interface_init (GtkExtendedLayoutIface *iface); static void gtk_box_set_property (GObject *object, guint prop_id, @@ -113,11 +109,26 @@ static void gtk_box_get_child_property (GtkContainer *container, static GType gtk_box_child_type (GtkContainer *container); +static void gtk_box_extended_layout_init (GtkExtendedLayoutIface *iface); +static void gtk_box_get_desired_size (GtkExtendedLayout *layout, + GtkRequisition *minimum_size, + GtkRequisition *natural_size); +static void gtk_box_get_width_for_height (GtkExtendedLayout *layout, + gint height, + gint *minimum_width, + gint *natural_width); +static void gtk_box_get_height_for_width (GtkExtendedLayout *layout, + gint width, + gint *minimum_height, + gint *natural_height); + +GtkExtendedLayoutIface *parent_extended_layout_iface; + G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL) G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT, - gtk_box_layout_interface_init)); + gtk_box_extended_layout_init)); static void gtk_box_class_init (GtkBoxClass *class) @@ -199,12 +210,6 @@ gtk_box_class_init (GtkBoxClass *class) g_type_class_add_private (object_class, sizeof (GtkBoxPrivate)); } -static void -gtk_box_layout_interface_init (GtkExtendedLayoutIface *iface) -{ - iface->get_desired_size = gtk_box_get_desired_size; -} - static void gtk_box_init (GtkBox *box) { @@ -275,121 +280,6 @@ gtk_box_get_property (GObject *object, } } -static void -gtk_box_get_desired_size (GtkExtendedLayout *layout, - GtkRequisition *minimum_size, - GtkRequisition *natural_size) -{ - GtkBox *box; - GtkBoxPrivate *private; - GList *children; - gint nvis_children; - gint border_width; - - box = GTK_BOX (layout); - private = GTK_BOX_GET_PRIVATE (box); - border_width = GTK_CONTAINER (box)->border_width; - - minimum_size->width = minimum_size->height = 0; - natural_size->width = natural_size->height = 0; - - nvis_children = 0; - children = box->children; - while (children) - { - GtkBoxChild *child; - - child = children->data; - children = children->next; - - if (gtk_widget_get_visible (child->widget)) - { - GtkRequisition child_minimum_size; - GtkRequisition child_natural_size; - - gtk_extended_layout_get_desired_size (GTK_EXTENDED_LAYOUT (child->widget), - &child_minimum_size, - &child_natural_size); - - if (private->orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (box->homogeneous) - { - gint width; - - width = child_minimum_size.width + child->padding * 2; - minimum_size->width = MAX (minimum_size->width, width); - - width = child_natural_size.width + child->padding * 2; - natural_size->width = MAX (natural_size->width, width); - } - else - { - minimum_size->width += child_minimum_size.width + child->padding * 2; - natural_size->width += child_natural_size.width + child->padding * 2; - } - - minimum_size->height = MAX (minimum_size->height, child_minimum_size.height); - natural_size->height = MAX (natural_size->height, child_natural_size.height); - } - else - { - if (box->homogeneous) - { - gint height; - - height = child_minimum_size.height + child->padding * 2; - minimum_size->height = MAX (minimum_size->height, height); - - height = child_natural_size.height + child->padding * 2; - natural_size->height = MAX (natural_size->height, height); - } - else - { - minimum_size->height += child_minimum_size.height + child->padding * 2; - natural_size->height += child_natural_size.height + child->padding * 2; - } - - minimum_size->width = MAX (minimum_size->width, child_minimum_size.width); - natural_size->width = MAX (natural_size->width, child_natural_size.width); - } - - - nvis_children += 1; - } - } - if (nvis_children > 0) - { - if (private->orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (box->homogeneous) - { - minimum_size->width *= nvis_children; - natural_size->width *= nvis_children; - } - - minimum_size->width += (nvis_children - 1) * box->spacing; - natural_size->width += (nvis_children - 1) * box->spacing; - } - else - { - if (box->homogeneous) - { - minimum_size->height *= nvis_children; - natural_size->height *= nvis_children; - } - - minimum_size->height += (nvis_children - 1) * box->spacing; - natural_size->height += (nvis_children - 1) * box->spacing; - } - } - - minimum_size->width += border_width * 2; - minimum_size->height += border_width * 2; - - natural_size->width += border_width * 2; - natural_size->height += border_width * 2; -} static gint gtk_box_compare_gap (gconstpointer p1, @@ -415,6 +305,7 @@ gtk_box_compare_gap (gconstpointer p1, return delta; } + static void gtk_box_size_allocate (GtkWidget *widget, GtkAllocation *allocation) @@ -849,6 +740,340 @@ gtk_box_pack (GtkBox *box, gtk_widget_thaw_child_notify (child); } + + +static void +gtk_box_extended_layout_init (GtkExtendedLayoutIface *iface) +{ + parent_extended_layout_iface = g_type_interface_peek_parent (iface); + + iface->get_desired_size = gtk_box_get_desired_size; + iface->get_height_for_width = gtk_box_get_height_for_width; + iface->get_width_for_height = gtk_box_get_width_for_height; +} + +static void +gtk_box_get_desired_size (GtkExtendedLayout *layout, + GtkRequisition *minimum_size, + GtkRequisition *natural_size) +{ + GtkBox *box; + GtkBoxPrivate *private; + GList *children; + gint nvis_children; + gint border_width; + + box = GTK_BOX (layout); + private = GTK_BOX_GET_PRIVATE (box); + border_width = GTK_CONTAINER (box)->border_width; + + minimum_size->width = minimum_size->height = 0; + natural_size->width = natural_size->height = 0; + + nvis_children = 0; + children = box->children; + while (children) + { + GtkBoxChild *child; + + child = children->data; + children = children->next; + + if (gtk_widget_get_visible (child->widget)) + { + GtkRequisition child_minimum_size; + GtkRequisition child_natural_size; + + gtk_extended_layout_get_desired_size (GTK_EXTENDED_LAYOUT (child->widget), + &child_minimum_size, + &child_natural_size); + + if (private->orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (box->homogeneous) + { + gint width; + + width = child_minimum_size.width + child->padding * 2; + minimum_size->width = MAX (minimum_size->width, width); + + width = child_natural_size.width + child->padding * 2; + natural_size->width = MAX (natural_size->width, width); + } + else + { + minimum_size->width += child_minimum_size.width + child->padding * 2; + natural_size->width += child_natural_size.width + child->padding * 2; + } + + minimum_size->height = MAX (minimum_size->height, child_minimum_size.height); + natural_size->height = MAX (natural_size->height, child_natural_size.height); + } + else + { + if (box->homogeneous) + { + gint height; + + height = child_minimum_size.height + child->padding * 2; + minimum_size->height = MAX (minimum_size->height, height); + + height = child_natural_size.height + child->padding * 2; + natural_size->height = MAX (natural_size->height, height); + } + else + { + minimum_size->height += child_minimum_size.height + child->padding * 2; + natural_size->height += child_natural_size.height + child->padding * 2; + } + + minimum_size->width = MAX (minimum_size->width, child_minimum_size.width); + natural_size->width = MAX (natural_size->width, child_natural_size.width); + } + + + nvis_children += 1; + } + } + if (nvis_children > 0) + { + if (private->orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (box->homogeneous) + { + minimum_size->width *= nvis_children; + natural_size->width *= nvis_children; + } + + minimum_size->width += (nvis_children - 1) * box->spacing; + natural_size->width += (nvis_children - 1) * box->spacing; + } + else + { + if (box->homogeneous) + { + minimum_size->height *= nvis_children; + natural_size->height *= nvis_children; + } + + minimum_size->height += (nvis_children - 1) * box->spacing; + natural_size->height += (nvis_children - 1) * box->spacing; + } + } + + minimum_size->width += border_width * 2; + minimum_size->height += border_width * 2; + + natural_size->width += border_width * 2; + natural_size->height += border_width * 2; +} + + +/** + * size_fits_for_dimension: + * @box: a GtkBox + * @avail_size: the allocated size in @box's opposing orientation + * @check_size: the size in @box's orientation to check + * @check_natural: whether to check natural sizes or minimum sizes. + * + * This checks if the required size of @box and its children fit into @avail_size + * in @box's opposing orientation if @box were given @check_size as an allocation + * in @box's orientation. + * + * In context: A GtkVBox will check if it fits into the available allocated height + * if it were given the @check_size in width. + * + */ +static gboolean +size_fits_for_dimension (GtkBox *box, + gint avail_size, + gint check_size, + gboolean check_natural) +{ + GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (box); + GList *children; + gint nvis_children = 0; + gint required_size = 0, child_size; + gint largest_child = 0; + + avail_size -= GTK_CONTAINER (box)->border_width * 2; + + for (children = box->children; children != NULL; + children = children->next, nvis_children++) + { + GtkBoxChild *child = children->data; + + if (gtk_widget_get_visible (child->widget)) + { + + if (private->orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (check_natural) + gtk_extended_layout_get_width_for_height (GTK_EXTENDED_LAYOUT (child->widget), + avail_size, NULL, &child_size); + else + gtk_extended_layout_get_width_for_height (GTK_EXTENDED_LAYOUT (child->widget), + avail_size, &child_size, NULL); + } + else + { + if (check_natural) + gtk_extended_layout_get_height_for_width (GTK_EXTENDED_LAYOUT (child->widget), + avail_size, NULL, &child_size); + else + gtk_extended_layout_get_height_for_width (GTK_EXTENDED_LAYOUT (child->widget), + avail_size, &child_size, NULL); + } + + child_size += child->padding * 2; + + if (child_size > largest_child) + largest_child = child_size; + + required_size += child_size; + } + } + + if (nvis_children > 0) + { + if (box->homogeneous) + required_size = largest_child * nvis_children; + + required_size += (nvis_children - 1) * box->spacing; + } + + required_size += GTK_CONTAINER (box)->border_width * 2; + + return required_size <= check_size; +} + + +static void +gtk_box_compute_size_for_awkward_orientation (GtkBox *box, + gint avail_size, + gint *minimum_size, + gint *natural_size) +{ + gint minimum, natural; + + /* Find the smallest possible fit in the orientation of 'box' where 'avail_size' is the available + * size in the opposing orientation + */ + for (minimum = 1; !size_fits_for_dimension (box, avail_size, minimum, FALSE); minimum++); + + /* Starting from the minimum available size, do the same to find the smallest allocation where + * all children receive their natural size + */ + for (natural = minimum; !size_fits_for_dimension (box, avail_size, natural, TRUE); natural++); + + if (minimum_size) + *minimum_size = minimum; + + if (natural_size) + *natural_size = natural; + +} + +static void +gtk_box_compute_size_for_orientation (GtkBox *box, + gint avail_size, + gint *minimum_size, + gint *natural_size) +{ + GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (box); + GList *children; + gint nvis_children = 0; + gint required_size = 0, required_natural = 0, child_size, child_natural; + gint largest_child = 0, largest_natural = 0; + + avail_size -= GTK_CONTAINER (box)->border_width * 2; + + for (children = box->children; children != NULL; + children = children->next, nvis_children++) + { + GtkBoxChild *child = children->data; + + if (gtk_widget_get_visible (child->widget)) + { + + if (private->orientation == GTK_ORIENTATION_HORIZONTAL) + gtk_extended_layout_get_width_for_height (GTK_EXTENDED_LAYOUT (child->widget), + avail_size, &child_size, &child_natural); + else + gtk_extended_layout_get_height_for_width (GTK_EXTENDED_LAYOUT (child->widget), + avail_size, &child_size, &child_natural); + + + child_size += child->padding * 2; + child_natural += child->padding * 2; + + if (child_size > largest_child) + largest_child = child_size; + + if (child_natural > largest_natural) + largest_natural = child_natural; + + required_size += child_size; + required_natural += child_natural; + } + } + + if (nvis_children > 0) + { + if (box->homogeneous) + { + required_size = largest_child * nvis_children; + required_natural = largest_natural * nvis_children; + } + + required_size += (nvis_children - 1) * box->spacing; + required_natural += (nvis_children - 1) * box->spacing; + } + + required_size += GTK_CONTAINER (box)->border_width * 2; + required_natural += GTK_CONTAINER (box)->border_width * 2; + + if (minimum_size) + *minimum_size = required_size; + + if (natural_size) + *natural_size = required_natural; +} + +static void +gtk_box_get_width_for_height (GtkExtendedLayout *layout, + gint height, + gint *minimum_width, + gint *natural_width) +{ + GtkBox *box = GTK_BOX (layout); + GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (layout); + + if (private->orientation == GTK_ORIENTATION_VERTICAL) +/* gtk_box_compute_size_for_awkward_orientation (box, height, minimum_width, natural_width); */ + /* Have the base class return the values previously computed by get_desired_size() */ + parent_extended_layout_iface->get_width_for_height (layout, height, minimum_width, natural_width); + else + gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width); +} + +static void +gtk_box_get_height_for_width (GtkExtendedLayout *layout, + gint width, + gint *minimum_height, + gint *natural_height) +{ + GtkBox *box = GTK_BOX (layout); + GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (layout); + + if (private->orientation == GTK_ORIENTATION_HORIZONTAL) +/* gtk_box_compute_size_for_awkward_orientation (box, width, minimum_height, natural_height); */ + /* Have the base class return the values previously computed by get_desired_size() */ + parent_extended_layout_iface->get_height_for_width (layout, width, minimum_height, natural_height); + else + gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height); +} + /** * gtk_box_new: * @orientation: the box' orientation. -- cgit v1.2.1