[gtk+] Fixed GtkWrapBox to allocate variable row AND column heights when in ALIGNED mode
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] Fixed GtkWrapBox to allocate variable row AND column heights when in ALIGNED mode
- Date: Mon, 13 Sep 2010 17:30:32 +0000 (UTC)
commit 153bfacde0d9c7a533c0936f3078db58cf672aac
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date: Tue Sep 14 02:28:23 2010 +0900
Fixed GtkWrapBox to allocate variable row AND column heights when in ALIGNED mode
With this new approach at request and allocate time, the average child size
is used to determine a good guess at how many columns will fit the box
width; afterwards extra columns are appended and checked to fit.
Then the row heights are calculated based on height-for-width of each
child in the row which now may have individual widths.
gtk/gtkwrapbox.c | 369 ++++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 290 insertions(+), 79 deletions(-)
---
diff --git a/gtk/gtkwrapbox.c b/gtk/gtkwrapbox.c
index 8287870..2aac13f 100644
--- a/gtk/gtkwrapbox.c
+++ b/gtk/gtkwrapbox.c
@@ -574,14 +574,14 @@ get_largest_size_for_opposing_orientation (GtkWrapBox *box,
* (used to get the largest line heights for a fixed item width and the opposite
* while itterating over a list of children, note the new index is returned) */
static GList *
-get_largest_size_for_line_in_opposing_orientation (GtkWrapBox *box,
- GtkOrientation orientation,
- GList *cursor,
- gint line_length,
- gint item_size,
- gint extra_pixels,
- gint *min_item_size,
- gint *nat_item_size)
+get_largest_size_for_line_in_opposing_orientation (GtkWrapBox *box,
+ GtkOrientation orientation,
+ GList *cursor,
+ gint line_length,
+ GtkRequestedSize *item_sizes,
+ gint extra_pixels,
+ gint *min_item_size,
+ gint *nat_item_size)
{
GtkWrapBoxPrivate *priv = box->priv;
GList *list;
@@ -599,7 +599,7 @@ get_largest_size_for_line_in_opposing_orientation (GtkWrapBox *box,
/* Distribute the extra pixels to the first children in the line
* (could be fancier and spread them out more evenly) */
- this_item_size = item_size;
+ this_item_size = item_sizes[i].minimum_size;
if (extra_pixels > 0 &&
priv->spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
{
@@ -909,6 +909,111 @@ allocate_child (GtkWrapBox *box,
gtk_widget_size_allocate (child->widget, &child_allocation);
}
+/* fit_aligned_item_requests() helper */
+static gint
+gather_aligned_item_requests (GtkWrapBox *box,
+ GtkOrientation orientation,
+ gint line_length,
+ gint item_spacing,
+ gint n_children,
+ GtkRequestedSize *item_sizes)
+{
+ GtkWrapBoxPrivate *priv = box->priv;
+ GList *list;
+ gint i;
+ gint extra_items, natural_line_size = 0;
+
+ extra_items = n_children % line_length;
+
+ for (list = priv->children, i = 0; list; list = list->next, i++)
+ {
+ GtkWrapBoxChild *child = list->data;
+ gint child_min, child_nat;
+ gint position;
+
+ if (!gtk_widget_get_visible (child->widget))
+ continue;
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ gtk_size_request_get_width (GTK_SIZE_REQUEST (child->widget),
+ &child_min, &child_nat);
+ child_min += child->hpadding * 2;
+ child_nat += child->hpadding * 2;
+ }
+ else
+ {
+ gtk_size_request_get_height (GTK_SIZE_REQUEST (child->widget),
+ &child_min, &child_nat);
+ child_min += child->vpadding * 2;
+ child_nat += child->vpadding * 2;
+ }
+
+
+ /* Get the index and push it over for the last line when spreading to the end */
+ position = i % line_length;
+
+ if (priv->spreading == GTK_WRAP_BOX_SPREAD_END && i >= n_children - extra_items)
+ position += line_length - extra_items;
+
+ /* Round up the size of every column/row */
+ item_sizes[position].minimum_size = MAX (item_sizes[position].minimum_size, child_min);
+ item_sizes[position].natural_size = MAX (item_sizes[position].natural_size, child_nat);
+ }
+
+ for (i = 0; i < line_length; i++)
+ natural_line_size += item_sizes[i].natural_size;
+
+ natural_line_size += (line_length - 1) * item_spacing;
+
+ return natural_line_size;
+}
+
+static GtkRequestedSize *
+fit_aligned_item_requests (GtkWrapBox *box,
+ GtkOrientation orientation,
+ gint avail_size,
+ gint item_spacing,
+ gint *line_length, /* in-out */
+ gint n_children)
+{
+ GtkRequestedSize *sizes, *try_sizes;
+ gint natural_line_size, try_line_size, try_length;
+
+ sizes = g_new0 (GtkRequestedSize, *line_length);
+
+ /* get the sizes for the initial guess */
+ try_line_size = natural_line_size =
+ gather_aligned_item_requests (box, orientation, *line_length, item_spacing, n_children, sizes);
+
+ /* Try columnizing the whole thing and adding an item to the end of the line;
+ * try to fit as many columns into the available size as possible */
+ for (try_length = *line_length + 1; try_line_size < avail_size; try_length++)
+ {
+ try_sizes = g_new0 (GtkRequestedSize, try_length);
+ try_line_size = gather_aligned_item_requests (box, orientation, try_length, item_spacing,
+ n_children, try_sizes);
+
+ if (try_line_size < avail_size)
+ {
+ natural_line_size = try_line_size;
+
+ *line_length = try_length;
+
+ g_free (sizes);
+ sizes = try_sizes;
+ }
+ else
+ {
+ /* oops, this one failed; stick to the last size that fit and then return */
+ g_free (try_sizes);
+ break;
+ }
+ }
+
+ return sizes;
+}
+
typedef struct {
GArray *requested;
@@ -948,14 +1053,15 @@ gtk_wrap_box_size_allocate (GtkWidget *widget,
if (priv->mode == GTK_WRAP_ALLOCATE_ALIGNED ||
priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
{
- GtkRequestedSize *sizes = NULL;
+ GtkRequestedSize *line_sizes = NULL;
+ GtkRequestedSize *item_sizes = NULL;
GList *list;
gint min_item_size, nat_item_size;
gint line_length;
- gint item_size;
+ gint item_size = 0;
gint line_size = 0, min_fixed_line_size = 0, nat_fixed_line_size = 0;
- gint line_offset, n_children, n_lines, line_count;
- gint extra_pixels, extra_per_item, extra_extra;
+ gint line_offset, item_offset, n_children, n_lines, line_count;
+ gint extra_pixels, extra_per_item = 0, extra_extra = 0;
gint i;
get_average_item_size (box, priv->orientation, &min_item_size, &nat_item_size);
@@ -971,30 +1077,26 @@ gtk_wrap_box_size_allocate (GtkWidget *widget,
* minimum item wrap length */
line_length = MAX (min_items, line_length);
- /* Now we need the real item allocation size */
- item_size = (avail_size - (line_length - 1) * item_spacing) / line_length;
-
- /* Cut out the expand space if we're not distributing any */
- if (priv->spreading != GTK_WRAP_BOX_SPREAD_EXPAND)
- item_size = MIN (item_size, nat_item_size);
-
- /* Get the real extra pixels incase of GTK_WRAP_BOX_SPREAD_START lines */
- extra_pixels = avail_size - (line_length - 1) * item_spacing - item_size * line_length;
- extra_per_item = extra_pixels / MAX (line_length -1, 1);
- extra_extra = extra_pixels % MAX (line_length -1, 1);
-
- /* Get how many lines that wraps to */
+ /* Get how many lines we'll be needing to wrap */
n_children = get_visible_children (box);
- n_lines = n_children / line_length;
- if ((n_children % line_length) > 0)
- n_lines++;
-
- n_lines = MAX (n_lines, 1);
/* Here we just use the largest height-for-width and use that for the height
* of all lines */
if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
{
+ n_lines = n_children / line_length;
+ if ((n_children % line_length) > 0)
+ n_lines++;
+
+ n_lines = MAX (n_lines, 1);
+
+ /* Now we need the real item allocation size */
+ item_size = (avail_size - (line_length - 1) * item_spacing) / line_length;
+
+ /* Cut out the expand space if we're not distributing any */
+ if (priv->spreading != GTK_WRAP_BOX_SPREAD_EXPAND)
+ item_size = MIN (item_size, nat_item_size);
+
get_largest_size_for_opposing_orientation (box, priv->orientation, item_size,
&min_fixed_line_size,
&nat_fixed_line_size);
@@ -1002,53 +1104,91 @@ gtk_wrap_box_size_allocate (GtkWidget *widget,
/* resolve a fixed 'line_size' */
line_size = (avail_other_size - (n_lines - 1) * line_spacing) / n_lines;
line_size = MIN (line_size, nat_fixed_line_size);
+
+ /* Get the real extra pixels incase of GTK_WRAP_BOX_SPREAD_START lines */
+ extra_pixels = avail_size - (line_length - 1) * item_spacing - item_size * line_length;
}
else /* GTK_WRAP_ALLOCATE_ALIGNED */
{
- /* spread out the available size in the opposing orientation into an array of
- * lines (and then allocate those lines naturally) */
GList *list;
gboolean first_line = TRUE;
- sizes = g_new0 (GtkRequestedSize, n_lines);
-
- /* In ALIGNED mode, all items have the same size in the box's orientation except
- * individual lines may have a different size */
+ /* Find the amount of columns that can fit aligned into the available space
+ * and collect their requests.
+ */
+ item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size,
+ item_spacing, &line_length, n_children);
+
+ /* Calculate the number of lines after determining the final line_length */
+ n_lines = n_children / line_length;
+ if ((n_children % line_length) > 0)
+ n_lines++;
+
+ n_lines = MAX (n_lines, 1);
+ line_sizes = g_new0 (GtkRequestedSize, n_lines);
+
+ /* Get the available remaining size */
+ avail_size -= (line_length - 1) * item_spacing;
+ for (i = 0; i < line_length; i++)
+ avail_size -= item_sizes[i].minimum_size;
+
+ /* Perform a natural allocation on the columnized items and get the remaining pixels */
+ extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
+
+ /* Now that we have the size of each column of items find the size of each individual
+ * line based on the aligned item sizes.
+ */
for (i = 0, list = priv->children; list != NULL; i++)
{
list =
get_largest_size_for_line_in_opposing_orientation (box, priv->orientation,
list, line_length,
- item_size, extra_pixels,
- &sizes[i].minimum_size,
- &sizes[i].natural_size);
+ item_sizes, extra_pixels,
+ &line_sizes[i].minimum_size,
+ &line_sizes[i].natural_size);
/* Its possible a line is made of completely invisible children */
- if (sizes[i].natural_size > 0)
+ if (line_sizes[i].natural_size > 0)
{
if (first_line)
first_line = FALSE;
else
avail_other_size -= line_spacing;
- avail_other_size -= sizes[i].minimum_size;
+ avail_other_size -= line_sizes[i].minimum_size;
- sizes[i].data = GINT_TO_POINTER (i);
+ line_sizes[i].data = GINT_TO_POINTER (i);
}
}
/* Distribute space among lines naturally */
- avail_other_size = gtk_distribute_natural_allocation (avail_other_size, n_lines, sizes);
+ avail_other_size = gtk_distribute_natural_allocation (avail_other_size, n_lines, line_sizes);
}
- line_offset = border_width;
+ /* Calculate expand space per item (used diferently depending on spreading mode) */
+ if (priv->spreading == GTK_WRAP_BOX_SPREAD_EVEN)
+ {
+ extra_per_item = extra_pixels / MAX (line_length -1, 1);
+ extra_extra = extra_pixels % MAX (line_length -1, 1);
+ }
+ else if (priv->spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
+ {
+ extra_per_item = extra_pixels / line_length;
+ extra_extra = extra_pixels % line_length;
+ }
+
+ line_offset = item_offset = border_width;
+
+ /* prepend extra space to item_offset for SPREAD_END */
+ if (priv->spreading == GTK_WRAP_BOX_SPREAD_END)
+ item_offset += extra_pixels;
for (i = 0, line_count = 0, list = priv->children; list; list = list->next)
{
GtkWrapBoxChild *child = list->data;
- gint position, this_line_size, item_offset;
+ gint position, this_line_size;
gint this_item_size;
if (!gtk_widget_get_visible (child->widget))
@@ -1063,17 +1203,40 @@ gtk_wrap_box_size_allocate (GtkWidget *widget,
if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
line_offset += line_size + line_spacing;
else /* aligned mode */
- line_offset += sizes[line_count].minimum_size + line_spacing;
+ line_offset += line_sizes[line_count].minimum_size + line_spacing;
line_count++;
- }
- /* We could be smarter here and distribute the extra pixels more
- * evenly across the children */
- if (position < extra_pixels && priv->spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
- this_item_size = item_size + 1;
- else
- this_item_size = item_size;
+ item_offset = border_width;
+
+ if (priv->spreading == GTK_WRAP_BOX_SPREAD_END)
+ {
+ item_offset += extra_pixels;
+
+ /* If we're on the last line, prepend the space for
+ * any leading items */
+ if (line_count == n_lines -1)
+ {
+ gint extra_items = n_children % line_length;
+
+ if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
+ {
+ item_offset += item_size * (line_length - extra_items);
+ item_offset += item_spacing * (line_length - extra_items);
+ }
+ else
+ {
+ gint j;
+
+ for (j = 0; j < (line_length - extra_items); j++)
+ {
+ item_offset += item_sizes[j].minimum_size;
+ item_offset += item_spacing;
+ }
+ }
+ }
+ }
+ }
/* Push the index along for the last line when spreading to the end */
if (priv->spreading == GTK_WRAP_BOX_SPREAD_END &&
@@ -1084,29 +1247,47 @@ gtk_wrap_box_size_allocate (GtkWidget *widget,
position += line_length - extra_items;
}
- item_offset = border_width + (position * item_size) + (position * item_spacing);
+ if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
+ this_item_size = item_size;
+ else /* aligned mode */
+ this_item_size = item_sizes[position].minimum_size;
- if (priv->spreading == GTK_WRAP_BOX_SPREAD_EVEN)
- {
- item_offset += position * extra_per_item;
- item_offset += MIN (position, extra_extra);
- }
- else if (priv->spreading == GTK_WRAP_BOX_SPREAD_END)
- item_offset += extra_pixels;
- else if (priv->spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
- item_offset += MIN (position, extra_pixels);
+ if (priv->spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
+ {
+ this_item_size += extra_per_item;
+
+ /* We could be smarter here and distribute the extra pixels more
+ * evenly across the children */
+ if (position < extra_extra)
+ this_item_size++;
+ }
/* Get the allocation size for this line */
if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
this_line_size = line_size;
else
- this_line_size = sizes[line_count].minimum_size;
+ this_line_size = line_sizes[line_count].minimum_size;
/* Do the actual allocation */
allocate_child (box, child, item_offset, line_offset, this_item_size, this_line_size);
+ item_offset += this_item_size;
+ item_offset += item_spacing;
+
+ /* deal with extra spacing here */
+ if (priv->spreading == GTK_WRAP_BOX_SPREAD_EVEN)
+ {
+ item_offset += extra_per_item;
+
+ if (position < extra_extra)
+ item_offset++;
+ }
+
i++;
}
+
+ g_free (item_sizes);
+ g_free (line_sizes);
}
else /* GTK_WRAP_ALLOCATE_FREE */
{
@@ -1623,7 +1804,7 @@ gtk_wrap_box_get_height_for_width (GtkSizeRequest *widget,
gint min_item_width, nat_item_width;
gint min_items;
gint min_height, nat_height;
- gint avail_size;
+ gint avail_size, n_children;
min_items = MAX (1, priv->minimum_line_children);
@@ -1634,6 +1815,8 @@ gtk_wrap_box_get_height_for_width (GtkSizeRequest *widget,
{
gint min_width;
+ n_children = get_visible_children (box);
+
/* Make sure its no smaller than the minimum */
GTK_SIZE_REQUEST_GET_IFACE (widget)->get_width (widget, &min_width, NULL);
@@ -1677,7 +1860,7 @@ gtk_wrap_box_get_height_for_width (GtkSizeRequest *widget,
if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
{
gint min_item_height, nat_item_height;
- gint lines, n_children;
+ gint lines;
/* Here we just use the largest height-for-width and
* add up the size accordingly */
@@ -1685,7 +1868,6 @@ gtk_wrap_box_get_height_for_width (GtkSizeRequest *widget,
&min_item_height, &nat_item_height);
/* Round up how many lines we need to allocate for */
- n_children = get_visible_children (box);
lines = n_children / line_length;
if ((n_children % line_length) > 0)
lines++;
@@ -1699,20 +1881,32 @@ gtk_wrap_box_get_height_for_width (GtkSizeRequest *widget,
else /* GTK_WRAP_ALLOCATE_ALIGNED */
{
GList *list = priv->children;
- gint min_line_height, nat_line_height;
+ gint min_line_height, nat_line_height, i;
gboolean first_line = TRUE;
+ GtkRequestedSize *item_sizes;
+
+ /* First get the size each set of items take to span the line
+ * when aligning the items above and below after wrapping.
+ */
+ item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size,
+ priv->horizontal_spacing, &line_length, n_children);
+
+
+ /* Get the available remaining size */
+ avail_size -= (line_length - 1) * priv->horizontal_spacing;
+ for (i = 0; i < line_length; i++)
+ avail_size -= item_sizes[i].minimum_size;
+
+ extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
- /* In ALIGNED mode, all items have the same size in the box's orientation except
- * individual rows may have a different size */
while (list != NULL)
{
list =
get_largest_size_for_line_in_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL,
list, line_length,
- item_size, extra_pixels,
+ item_sizes, extra_pixels,
&min_line_height, &nat_line_height);
-
/* Its possible the line only had invisible widgets */
if (nat_line_height > 0)
{
@@ -1728,6 +1922,8 @@ gtk_wrap_box_get_height_for_width (GtkSizeRequest *widget,
nat_height += nat_line_height;
}
}
+
+ g_free (item_sizes);
}
}
else /* GTK_WRAP_ALLOCATE_FREE */
@@ -1791,7 +1987,7 @@ gtk_wrap_box_get_width_for_height (GtkSizeRequest *widget,
gint min_item_height, nat_item_height;
gint min_items;
gint min_width, nat_width;
- gint avail_size;
+ gint avail_size, n_children;
min_items = MAX (1, priv->minimum_line_children);
@@ -1807,6 +2003,8 @@ gtk_wrap_box_get_width_for_height (GtkSizeRequest *widget,
{
gint min_height;
+ n_children = get_visible_children (box);
+
/* Make sure its no smaller than the minimum */
GTK_SIZE_REQUEST_GET_IFACE (widget)->get_height (widget, &min_height, NULL);
@@ -1850,7 +2048,7 @@ gtk_wrap_box_get_width_for_height (GtkSizeRequest *widget,
if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
{
gint min_item_width, nat_item_width;
- gint lines, n_children;
+ gint lines;
/* Here we just use the largest height-for-width and
* add up the size accordingly */
@@ -1872,17 +2070,29 @@ gtk_wrap_box_get_width_for_height (GtkSizeRequest *widget,
else /* GTK_WRAP_ALLOCATE_ALIGNED */
{
GList *list = priv->children;
- gint min_line_width, nat_line_width;
+ gint min_line_width, nat_line_width, i;
gboolean first_line = TRUE;
+ GtkRequestedSize *item_sizes;
+
+ /* First get the size each set of items take to span the line
+ * when aligning the items above and below after wrapping.
+ */
+ item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size,
+ priv->vertical_spacing, &line_length, n_children);
+
+ /* Get the available remaining size */
+ avail_size -= (line_length - 1) * priv->horizontal_spacing;
+ for (i = 0; i < line_length; i++)
+ avail_size -= item_sizes[i].minimum_size;
+
+ extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
- /* In ALIGNED mode, all items have the same size in the box's orientation except
- * individual rows may have a different size */
while (list != NULL)
{
list =
get_largest_size_for_line_in_opposing_orientation (box, GTK_ORIENTATION_VERTICAL,
list, line_length,
- item_size, extra_pixels,
+ item_sizes, extra_pixels,
&min_line_width, &nat_line_width);
/* Its possible the last line only had invisible widgets */
@@ -1900,6 +2110,7 @@ gtk_wrap_box_get_width_for_height (GtkSizeRequest *widget,
nat_width += nat_line_width;
}
}
+ g_free (item_sizes);
}
}
else /* GTK_WRAP_ALLOCATE_FREE */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]