From d26ac6421b7163bccbd635ee0b1b7745263a3f9d Mon Sep 17 00:00:00 2001 From: Tristan Van Berkom Date: Sat, 23 Oct 2010 00:04:46 +0900 Subject: [PATCH] Fixed problems with combination of height-for-width apis and alignment/margin vfuncs adjust_size_request/allocation Now get_height_for_width() will internally update the for_width before passing it to the real height_for_width() vfunc, allowing margins and extra space for alignments to be stripped, thus requesting sufficient height for greater than natural widths (and also accounting for margins properly). Test case adjusted in testadjustsize to ensure proper behavior. --- gtk/gtkcontainer.c | 51 ++++++----- gtk/gtksizerequest.c | 49 +++++++++-- gtk/gtkwidget.c | 187 +++++++++++++++++------------------------ gtk/gtkwidget.h | 11 ++- tests/testadjustsize.c | 5 +- 5 files changed, 162 insertions(+), 141 deletions(-) diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c index 0cfedfb793..5b2d3baa30 100644 --- a/gtk/gtkcontainer.c +++ b/gtk/gtkcontainer.c @@ -323,11 +323,13 @@ static void gtk_container_map (GtkWidget *widget); static void gtk_container_unmap (GtkWidget *widget); static void gtk_container_adjust_size_request (GtkWidget *widget, GtkOrientation orientation, - gint for_size, gint *minimum_size, gint *natural_size); static void gtk_container_adjust_size_allocation (GtkWidget *widget, - GtkAllocation *allocation); + GtkOrientation orientation, + gint *natural_size, + gint *allocated_pos, + gint *allocated_size); static gchar* gtk_container_child_default_composite_name (GtkContainer *container, GtkWidget *child); @@ -1776,7 +1778,6 @@ gtk_container_resize_children (GtkContainer *container) static void gtk_container_adjust_size_request (GtkWidget *widget, GtkOrientation orientation, - gint for_size, gint *minimum_size, gint *natural_size) { @@ -1797,28 +1798,33 @@ gtk_container_adjust_size_request (GtkWidget *widget, /* chain up last so gtk_widget_set_size_request() values * will have a chance to overwrite our border width. */ - parent_class->adjust_size_request (widget, orientation, for_size, + parent_class->adjust_size_request (widget, orientation, minimum_size, natural_size); } static void gtk_container_adjust_size_allocation (GtkWidget *widget, - GtkAllocation *allocation) + GtkOrientation orientation, + gint *natural_size, + gint *allocated_pos, + gint *allocated_size) { GtkContainer *container; int border_width; container = GTK_CONTAINER (widget); - parent_class->adjust_size_allocation (widget, allocation); - if (!GTK_CONTAINER_GET_CLASS (widget)->handle_border_width) - return; + { + parent_class->adjust_size_allocation (widget, orientation, + natural_size, allocated_pos, + allocated_size); + return; + } border_width = container->priv->border_width; - allocation->width -= border_width * 2; - allocation->height -= border_width * 2; + *allocated_size -= border_width * 2; /* If we get a pathological too-small allocation to hold * even the border width, leave all allocation to the actual @@ -1828,23 +1834,26 @@ gtk_container_adjust_size_allocation (GtkWidget *widget, * As long as we have space, set x,y properly. */ - if (allocation->width < 1) + if (*allocated_size < 1) { - allocation->width += border_width * 2; + *allocated_size += border_width * 2; } else { - allocation->x += border_width; + *allocated_pos += border_width; + *natural_size -= border_width * 2; } - if (allocation->height < 1) - { - allocation->height += border_width * 2; - } - else - { - allocation->y += border_width; - } + /* Chain up to GtkWidgetClass *after* removing our border width from + * the proposed allocation size. This is because it's possible that the + * widget was allocated more space than it needs in a said orientation, + * if GtkWidgetClass does any alignments and thus limits the size to the + * natural size... then we need that to be done *after* removing any margins + * and padding values. + */ + parent_class->adjust_size_allocation (widget, orientation, + natural_size, allocated_pos, + allocated_size); } /** diff --git a/gtk/gtksizerequest.c b/gtk/gtksizerequest.c index 55979f41b5..da91d0c13c 100644 --- a/gtk/gtksizerequest.c +++ b/gtk/gtksizerequest.c @@ -226,20 +226,56 @@ compute_size_for_orientation (GtkWidget *request, requisition_size = requisition.width; if (for_size < 0) - GTK_WIDGET_GET_CLASS (request)->get_preferred_width (request, &min_size, &nat_size); + { + GTK_WIDGET_GET_CLASS (request)->get_preferred_width (request, &min_size, &nat_size); + } else - GTK_WIDGET_GET_CLASS (request)->get_preferred_width_for_height (request, for_size, - &min_size, &nat_size); + { + int ignored_position = 0; + int natural_height; + + /* Pull the base natural height from the cache as it's needed to adjust + * the proposed 'for_size' */ + gtk_widget_get_preferred_height (widget, NULL, &natural_height); + + /* convert for_size to unadjusted height (for_size is a proposed allocation) */ + GTK_WIDGET_GET_CLASS (request)->adjust_size_allocation (widget, + GTK_ORIENTATION_VERTICAL, + &natural_height, + &ignored_position, + &for_size); + + GTK_WIDGET_GET_CLASS (request)->get_preferred_width_for_height (request, for_size, + &min_size, &nat_size); + } } else { requisition_size = requisition.height; if (for_size < 0) - GTK_WIDGET_GET_CLASS (request)->get_preferred_height (request, &min_size, &nat_size); + { + GTK_WIDGET_GET_CLASS (request)->get_preferred_height (request, &min_size, &nat_size); + } else - GTK_WIDGET_GET_CLASS (request)->get_preferred_height_for_width (request, for_size, - &min_size, &nat_size); + { + int ignored_position = 0; + int natural_width; + + /* Pull the base natural width from the cache as it's needed to adjust + * the proposed 'for_size' */ + gtk_widget_get_preferred_width (widget, NULL, &natural_width); + + /* convert for_size to unadjusted width (for_size is a proposed allocation) */ + GTK_WIDGET_GET_CLASS (request)->adjust_size_allocation (widget, + GTK_ORIENTATION_HORIZONTAL, + &natural_width, + &ignored_position, + &for_size); + + GTK_WIDGET_GET_CLASS (request)->get_preferred_height_for_width (request, for_size, + &min_size, &nat_size); + } } pop_recursion_check (request, orientation); @@ -270,7 +306,6 @@ compute_size_for_orientation (GtkWidget *request, orientation == GTK_SIZE_GROUP_HORIZONTAL ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, - cached_size->for_size, &adjusted_min, &adjusted_natural); diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 6d7faf8abc..43f8c377ba 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -685,11 +685,13 @@ static void gtk_widget_queue_tooltip_query (GtkWidget *widg static void gtk_widget_real_adjust_size_request (GtkWidget *widget, GtkOrientation orientation, - gint for_size, gint *minimum_size, gint *natural_size); static void gtk_widget_real_adjust_size_allocation (GtkWidget *widget, - GtkAllocation *allocation); + GtkOrientation orientation, + gint *natural_size, + gint *allocated_pos, + gint *allocated_size); static void gtk_widget_set_usize_internal (GtkWidget *widget, gint width, @@ -4609,6 +4611,8 @@ gtk_widget_size_allocate (GtkWidget *widget, gboolean alloc_needed; gboolean size_changed; gboolean position_changed; + gint natural_width, natural_height; + gint min_width, min_height; priv = widget->priv; @@ -4645,7 +4649,37 @@ gtk_widget_size_allocate (GtkWidget *widget, real_allocation = *allocation; adjusted_allocation = real_allocation; - GTK_WIDGET_GET_CLASS (widget)->adjust_size_allocation (widget, &adjusted_allocation); + if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH) + { + /* Go ahead and request the height for allocated width, note that the internals + * of get_height_for_width will internally limit the for_size to natural size + * when aligning implicitly. + */ + gtk_widget_get_preferred_width (widget, &min_width, &natural_width); + gtk_widget_get_preferred_height_for_width (widget, real_allocation.width, NULL, &natural_height); + } + else + { + /* Go ahead and request the width for allocated height, note that the internals + * of get_width_for_height will internally limit the for_size to natural size + * when aligning implicitly. + */ + gtk_widget_get_preferred_height (widget, &min_height, &natural_height); + gtk_widget_get_preferred_width_for_height (widget, real_allocation.height, NULL, &natural_width); + } + + /* Now that we have the right natural height and width, go ahead and remove any margins from the + * allocated sizes and possibly limit them to the natural sizes */ + GTK_WIDGET_GET_CLASS (widget)->adjust_size_allocation (widget, + GTK_ORIENTATION_HORIZONTAL, + &natural_width, + &adjusted_allocation.x, + &adjusted_allocation.width); + GTK_WIDGET_GET_CLASS (widget)->adjust_size_allocation (widget, + GTK_ORIENTATION_VERTICAL, + &natural_height, + &adjusted_allocation.y, + &adjusted_allocation.height); if (adjusted_allocation.x < real_allocation.x || adjusted_allocation.y < real_allocation.y || @@ -4920,140 +4954,76 @@ gtk_widget_real_size_allocate (GtkWidget *widget, } static void -get_span_inside_border (GtkWidget *widget, - GtkAlign align, - int start_pad, - int end_pad, - int allocated_outside_size, - int natural_inside_size, - int *coord_inside_p, - int *size_inside_p) +adjust_for_align(GtkAlign align, + gint *natural_size, + gint *allocated_pos, + gint *allocated_size) { - int inside_allocated; - int content_size; - int coord, size; - - inside_allocated = allocated_outside_size - start_pad - end_pad; - - content_size = natural_inside_size; - if (content_size > inside_allocated) - { - /* didn't get full natural size */ - content_size = inside_allocated; - } - - coord = size = 0; /* silence compiler */ switch (align) { case GTK_ALIGN_FILL: - coord = start_pad; - size = inside_allocated; + /* change nothing */ break; case GTK_ALIGN_START: - coord = start_pad; - size = content_size; + /* keep *allocated_pos where it is */ + *allocated_size = MIN (*allocated_size, *natural_size); break; case GTK_ALIGN_END: - coord = allocated_outside_size - end_pad - content_size; - size = content_size; + if (*allocated_size > *natural_size) + { + *allocated_pos += (*allocated_size - *natural_size); + *allocated_size = *natural_size; + } break; case GTK_ALIGN_CENTER: - coord = start_pad + (inside_allocated - content_size) / 2; - size = content_size; + if (*allocated_size > *natural_size) + { + *allocated_pos += (*allocated_size - *natural_size) / 2; + *allocated_size = MIN (*allocated_size, *natural_size); + } break; } - - if (coord_inside_p) - *coord_inside_p = coord; - - if (size_inside_p) - *size_inside_p = size; } static void -get_span_inside_border_horizontal (GtkWidget *widget, - const GtkWidgetAuxInfo *aux_info, - int allocated_outside_width, - int natural_inside_width, - int *x_inside_p, - int *width_inside_p) +adjust_for_margin(gint start_margin, + gint end_margin, + gint *natural_size, + gint *allocated_pos, + gint *allocated_size) { - get_span_inside_border (widget, - aux_info->halign, - aux_info->margin.left, - aux_info->margin.right, - allocated_outside_width, - natural_inside_width, - x_inside_p, - width_inside_p); -} - -static void -get_span_inside_border_vertical (GtkWidget *widget, - const GtkWidgetAuxInfo *aux_info, - int allocated_outside_height, - int natural_inside_height, - int *y_inside_p, - int *height_inside_p) -{ - get_span_inside_border (widget, - aux_info->valign, - aux_info->margin.top, - aux_info->margin.bottom, - allocated_outside_height, - natural_inside_height, - y_inside_p, - height_inside_p); + *natural_size -= (start_margin + end_margin); + *allocated_pos += start_margin; + *allocated_size -= (start_margin + end_margin); } static void gtk_widget_real_adjust_size_allocation (GtkWidget *widget, - GtkAllocation *allocation) + GtkOrientation orientation, + gint *natural_size, + gint *allocated_pos, + gint *allocated_size) { const GtkWidgetAuxInfo *aux_info; - gint natural_width; - gint natural_height; - int x, y, w, h; aux_info = _gtk_widget_get_aux_info_or_defaults (widget); - if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH) + if (orientation == GTK_ORIENTATION_HORIZONTAL) { - gtk_widget_get_preferred_width (widget, NULL, &natural_width); - get_span_inside_border_horizontal (widget, - aux_info, - allocation->width, - natural_width, - &x, &w); - - gtk_widget_get_preferred_height_for_width (widget, w, NULL, &natural_height); - get_span_inside_border_vertical (widget, - aux_info, - allocation->height, - natural_height, - &y, &h); + adjust_for_margin (aux_info->margin.left, + aux_info->margin.right, + natural_size, allocated_pos, allocated_size); + adjust_for_align (aux_info->halign, + natural_size, allocated_pos, allocated_size); } - else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */ + else { - gtk_widget_get_preferred_height (widget, NULL, &natural_height); - get_span_inside_border_vertical (widget, - aux_info, - allocation->height, - natural_height, - &y, &h); - - gtk_widget_get_preferred_width_for_height (widget, h, NULL, &natural_width); - get_span_inside_border_horizontal (widget, - aux_info, - allocation->width, - natural_width, - &x, &w); + adjust_for_margin (aux_info->margin.top, + aux_info->margin.bottom, + natural_size, allocated_pos, allocated_size); + adjust_for_align (aux_info->valign, + natural_size, allocated_pos, allocated_size); } - - allocation->x += x; - allocation->y += y; - allocation->width = w; - allocation->height = h; } static gboolean @@ -9960,7 +9930,6 @@ gtk_widget_real_size_request (GtkWidget *widget, static void gtk_widget_real_adjust_size_request (GtkWidget *widget, GtkOrientation orientation, - gint for_size, gint *minimum_size, gint *natural_size) { diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 576f085c01..6e5b020d37 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -157,7 +157,10 @@ struct _GtkWidget * and alignment properties of #GtkWidget. Chain up * before performing your own adjustments so your * own adjustments remove more allocation after the #GtkWidget base - * class has already removed margin and alignment. + * class has already removed margin and alignment. The natural size + * passed in should be adjusted in the same way as the allocated size, + * which allows adjustments to perform alignments or other changes + * based on natural size. */ struct _GtkWidgetClass { @@ -372,11 +375,13 @@ struct _GtkWidgetClass void (* adjust_size_request) (GtkWidget *widget, GtkOrientation orientation, - gint for_size, gint *minimum_size, gint *natural_size); void (* adjust_size_allocation) (GtkWidget *widget, - GtkAllocation *allocation); + GtkOrientation orientation, + gint *natural_size, + gint *allocated_pos, + gint *allocated_size); /*< private >*/ diff --git a/tests/testadjustsize.c b/tests/testadjustsize.c index 889ea52c72..13ec944986 100644 --- a/tests/testadjustsize.c +++ b/tests/testadjustsize.c @@ -408,6 +408,9 @@ open_valigned_label_window (void) gtk_widget_show (box); gtk_container_add (GTK_CONTAINER (window), box); + label = gtk_label_new ("Both labels expand"); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0); label = gtk_label_new ("Some wrapping text with width-chars = 15 and max-width-chars = 35"); gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); @@ -421,7 +424,7 @@ open_valigned_label_window (void) gtk_container_add (GTK_CONTAINER (frame), label); gtk_widget_set_valign (frame, GTK_ALIGN_CENTER); - gtk_widget_set_halign (frame, GTK_ALIGN_FILL); + gtk_widget_set_halign (frame, GTK_ALIGN_CENTER); gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);