Re: Bug in get_preferred_height_for_width() [was Re: Minimum height for minimum width]
- From: Havoc Pennington <hp pobox com>
- To: Tristan Van Berkom <tristanvb openismus com>
- Cc: gtk-devel-list gnome org
- Subject: Re: Bug in get_preferred_height_for_width() [was Re: Minimum height for minimum width]
- Date: Sun, 17 Oct 2010 23:39:58 -0400
Hi,
Here's roughly what I'm thinking about. This is an untested patch with
a big FIXME in it (FIXME must be fixed to have a hope of working). But
I thought since you were poking at this I better share sooner than
later. Maybe there is some glaring problem here but just wanted to
communicate the code I was trying to describe.
One test of having this right is that it should be possible to make
GtkMisc work with adjust_size_* and rip manual align and pad handling
out of its subclasses. Of course, the implementation of GtkMisc would
look a lot like the code in GtkWidget's adjust_size_* but since you
could then "stack" Misc and Widget align and pad, it'd be a sort of
torture test.
Havoc
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index 30930e0..ba398f2 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -327,7 +327,10 @@ static void gtk_container_adjust_size_request (GtkWidget *widget,
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);
@@ -1805,22 +1808,26 @@ gtk_container_adjust_size_request (GtkWidget *widget,
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);
+ parent_class->adjust_size_allocation (widget, orientation,
+ natural_size, allocated_pos,
+ allocated_size);
if (!GTK_CONTAINER_GET_CLASS (widget)->handle_border_width)
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
@@ -1830,22 +1837,14 @@ 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;
- }
-
- if (allocation->height < 1)
- {
- allocation->height += border_width * 2;
- }
- else
- {
- allocation->y += border_width;
+ *allocated_pos += border_width;
+ *natural_size -= border_width * 2;
}
}
diff --git a/gtk/gtksizerequest.c b/gtk/gtksizerequest.c
index c3d0398..9ebb908 100644
--- a/gtk/gtksizerequest.c
+++ b/gtk/gtksizerequest.c
@@ -223,20 +223,58 @@ 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;
+
+ /* FIXME getting unadjusted natural_height here is going
+ * to be annoying... recompute it? pass it in? is it in
+ * cache?
+ */
+
+ /* 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;
+
+ /* FIXME getting unadjusted natural_width here is going
+ * to be annoying... recompute it? pass it in? is it in
+ * cache?
+ */
+
+ /* 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);
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 114aafe..e95ba9b 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -575,7 +575,10 @@ static void gtk_widget_real_adjust_size_request (GtkWidget
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,
@@ -4541,6 +4544,7 @@ gtk_widget_size_allocate (GtkWidget *widget,
gboolean alloc_needed;
gboolean size_changed;
gboolean position_changed;
+ gint natural_width, natural_height;
priv = widget->priv;
@@ -4577,7 +4581,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)
+ {
+ int min_width;
+
+ gtk_widget_get_preferred_width (widget, &min_width, &natural_width);
+ /* "natural_width" is a white lie; it's reduced to the
+ * actual allocation but kept above min.
+ */
+ if (natural_width > real_allocation.width)
+ natural_width = MAX(real_allocation.width, min_width);
+ gtk_widget_get_preferred_height_for_width (widget, natural_width, NULL, &natural_height);
+ }
+ else
+ {
+ int min_height;
+ gtk_widget_get_preferred_height (widget, &min_height, &natural_height);
+ if (natural_height > real_allocation.height)
+ natural_height = MAX(real_allocation.height, min_height);
+ gtk_widget_get_preferred_width_for_height (widget, natural_height, NULL, &natural_width);
+ }
+
+ 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 ||
@@ -4852,140 +4886,70 @@ 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 = *natural_size;
break;
case GTK_ALIGN_END:
- coord = allocated_outside_size - end_pad - content_size;
- size = content_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;
+ *allocated_pos += (*allocated_size - *natural_size) / 2;
+ *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)
-{
- 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);
+adjust_for_margin(gint start_margin,
+ gint end_margin,
+ gint *natural_size,
+ gint *allocated_pos,
+ gint *allocated_size)
+{
+ *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)
+ {
+ 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_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);
- }
- else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
- {
- 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);
- }
-
- allocation->x += x;
- allocation->y += y;
- allocation->width = w;
- allocation->height = h;
+ 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);
+ }
}
static gboolean
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 2333653..6b5d16d 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -161,7 +161,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
{
@@ -377,7 +380,10 @@ struct _GtkWidgetClass
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 >*/
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]