iconview: Reduce complexity of sizing code

Always assume max-columns and min-rows. The old approach was kinda
insane.

As an example, try to write an algorithm that optimizes the minimum size
for infinite (take a reasonably large number like 2520) word-wrapped
Monospace text cells containing the text "XXXXX XXX XXX XXXXX" (keep in
mind that this is the easy problem, because it's assuming equal cell
renderers). There's 4 ways to reasonably lay out this text:
19 glyphs (19x1):
  XXXXX XXX XXX XXXXX
18 glyphs (9x2):
  XXXXX XXX
  XXX XXXXX
21 glyphs (7x3):
  XXXXX
  XXX XXX
  XXXXX
20 glyphs (5x4):
  XXXXX
  XXX
  XXX
  XXXXX
The best thing to do usually is using the 9x2 approach, but that's
neither the one using the natural nor the one using the minimum size.

As a side note, this does not include spacing and padding, which might
also influence the decision. Nor does it include height-for-width
considerations. Look at this table (numbers given in glyphs, not pixels,
as for pixel-sizes it gets even more interesting):
  given  best solution
  width  columns  sizing  glyphs per cell
   6      1       6x4           20
   7      1       7x3           21
   8      1       7x3           24
   9      1       9x2           18
  10      1/2     9x2/5x4       20
  11      1/2     9x2/5x4       22
  12      1/2     9x2/5x4       24
  13      1/2     9x2/5x4       26
  14      2       7x3           21
  15      3       5x4           20
  16      3       5x4           21.3
  17      3       5x4           22.7
  18      2       9x2           18
  19      1/2    19x1/8x2       19
  20      1/2/4  19x1/8x2/5x4   20
  21      1-4     any           21
  22      1-4     any           22
  23      1-4     any           23
  24      1-4     any           24
  25      5       5x4           20
  26      5       5x4           20.8
  27      3       9x2           18
  28      3       9x2           18.7
  29      3       9x2           19.3
  30      3/6     9x2/5x4       20

Now of course, nobody wants the number of columns to randomly change in
inexplicable ways while they enlarge or shrink an iconview, so we not
only have to optimize for smallest or other size measurements, but we
also have to optimize for "most pleasing to the eye".

And last but not least, I'd like to once again remind you - if you kept
up until now - that this discussion was for identically-sized cells
only.
This commit is contained in:
Benjamin Otte 2012-05-06 00:51:14 +02:00
parent 2da7a6d803
commit d406bf96d4

View File

@ -1463,16 +1463,31 @@ static void
gtk_icon_view_compute_n_items_for_size (GtkIconView *icon_view,
GtkOrientation orientation,
gint size,
gint *min_columns,
gint *max_columns)
gint *min_items,
gint *max_items)
{
GtkIconViewPrivate *priv = icon_view->priv;
int minimum, natural;
if (priv->columns > 0)
{
*min_columns = priv->columns;
*max_columns = priv->columns;
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
if (min_items)
*min_items = priv->columns;
if (max_items)
*max_items = priv->columns;
}
else
{
int n_items = gtk_icon_view_get_n_items (icon_view);
if (min_items)
*min_items = (n_items + priv->columns - 1) / priv->columns;
if (max_items)
*max_items = (n_items + priv->columns - 1) / priv->columns;
}
return;
}
@ -1493,14 +1508,21 @@ gtk_icon_view_compute_n_items_for_size (GtkIconView *icon_view,
natural += priv->row_spacing;
}
if (size <= minimum)
*max_columns = 1;
else
*max_columns = size / minimum;
if (size <= natural)
*min_columns = 1;
else
*min_columns = size / natural;
if (max_items)
{
if (size <= minimum)
*max_items = 1;
else
*max_items = size / minimum;
}
if (min_items)
{
if (size <= natural)
*min_items = 1;
else
*min_items = size / natural;
}
}
static GtkSizeRequestMode
@ -1545,17 +1567,16 @@ gtk_icon_view_get_preferred_width_for_height (GtkWidget *widget,
{
GtkIconView *icon_view = GTK_ICON_VIEW (widget);
GtkIconViewPrivate *priv = icon_view->priv;
int item_min, item_nat, min_rows, max_rows, n_items;
int item_min, item_nat, rows, n_items;
gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_VERTICAL, height, &min_rows, &max_rows);
gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_VERTICAL, height, &rows, NULL);
n_items = gtk_icon_view_get_n_items (icon_view);
height = height + priv->row_spacing - 2 * priv->margin;
gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_HORIZONTAL, height / max_rows - priv->row_spacing, &item_min, NULL);
*minimum = item_min * ((n_items + max_rows - 1) / max_rows);
gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_HORIZONTAL, height / min_rows - priv->row_spacing, NULL, &item_nat);
*natural = item_nat * ((n_items + min_rows - 1) / min_rows);
gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_HORIZONTAL, height / rows - priv->row_spacing, &item_min, &item_nat);
*minimum = item_min * ((n_items + rows - 1) / rows);
*natural = item_nat * ((n_items + rows - 1) / rows);
*minimum += 2 * priv->margin;
*natural += 2 * priv->margin;
@ -1598,17 +1619,16 @@ gtk_icon_view_get_preferred_height_for_width (GtkWidget *widget,
{
GtkIconView *icon_view = GTK_ICON_VIEW (widget);
GtkIconViewPrivate *priv = icon_view->priv;
int item_min, item_nat, min_columns, max_columns, n_items;
int item_min, item_nat, columns, n_items;
gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_HORIZONTAL, width, &min_columns, &max_columns);
gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_HORIZONTAL, width, NULL, &columns);
n_items = gtk_icon_view_get_n_items (icon_view);
width = width + priv->column_spacing - 2 * priv->margin;
gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_VERTICAL, width / max_columns - priv->column_spacing, &item_min, NULL);
*minimum = (item_min + priv->row_spacing) * ((n_items + max_columns - 1) / max_columns) - priv->row_spacing;
gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_VERTICAL, width / min_columns - priv->column_spacing, NULL, &item_nat);
*natural = (item_nat + priv->row_spacing) * ((n_items + min_columns - 1) / min_columns) - priv->row_spacing;
gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_VERTICAL, width / columns - priv->column_spacing, &item_min, &item_nat);
*minimum = (item_min + priv->row_spacing) * ((n_items + columns - 1) / columns) - priv->row_spacing;
*natural = (item_nat + priv->row_spacing) * ((n_items + columns - 1) / columns) - priv->row_spacing;
*minimum += 2 * priv->margin;
*natural += 2 * priv->margin;