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.
This commit is contained in:
Tristan Van Berkom 2010-10-23 00:04:46 +09:00
parent a46dec176e
commit d26ac6421b
5 changed files with 162 additions and 141 deletions

View File

@ -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);
}
/**

View File

@ -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);

View File

@ -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)
{

View File

@ -157,7 +157,10 @@ struct _GtkWidget
* and alignment properties of #GtkWidget. Chain up
* <emphasis>before</emphasis> 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 >*/

View File

@ -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);