Re: State of natural size, Havoc/Behdad style



Seems I didn't get all details of Behdad's algorithm.
Fixed. Like it much better now:

http://taschenorakel.de/media/movies/natural-size-behdad-style.ogg

TODO:

- get size-group-natural-size interaction halfway right.
- get agreement on code/API (commit?)
- factor out Behdad's/Havoc's natural size algorithm for reuse


Am Dienstag, den 08.01.2008, 19:17 +0100 schrieb Mathias Hasselmann:
> Attached the current state of natural size, with an API similiar to
> Havoc's wishes and implementing the natural size allocation they both
> suggested.
> 
> Havoc suggest to have two methods: get_desired_width() and
> get_desired_height(), but I implemented get_desired_size(), since I do
> not see how to implement this for GtkLabel, without recalculating the
> entire layout four times on each desired size request.
-- 
Mathias Hasselmann <mathias hasselmann gmx de>
Openismus GmbH: http://www.openismus.com/
Personal Site: http://taschenorakel.de/
diff -u b/gtk/gtkhbox.c b/gtk/gtkhbox.c
--- b/gtk/gtkhbox.c
+++ b/gtk/gtkhbox.c
@@ -169,7 +169,7 @@
 }
 
 static gint
-gtk_vbox_compare_gap (gconstpointer p1,
+gtk_hbox_compare_gap (gconstpointer p1,
                       gconstpointer p2,
                       gpointer      data)
 {
@@ -178,16 +178,14 @@
   const GtkBoxSpreading *c2 = p2;
 
   const gint d1 = MAX (sizes[c1->index].natural_size -
-                       sizes[c1->index].minimum_size,
-                       0);
+                       sizes[c1->index].minimum_size, 0);
   const gint d2 = MAX (sizes[c2->index].natural_size -
-                       sizes[c2->index].minimum_size,
-                       0);
+                       sizes[c2->index].minimum_size, 0);
 
-  gint delta = (d1 - d2);
+  gint delta = (d2 - d1); /* sort descending by gap... */
 
-  if (0 == delta)
-    delta = (c1->index - c2->index);
+  if (0 == delta) /* ...and position on draw. */
+    delta = (c2->index - c1->index);
 
   return delta;
 }
@@ -209,6 +207,8 @@
   nexpand_children = 0;
   children = box->children;
 
+  /* Count number of visible children. */
+
   while (children)
     {
       child = children->data;
@@ -236,7 +236,7 @@
 
       gint width;
       gint extra;
-      gint x, i;
+      gint i, x;
 
       width = (allocation->width - border_width * 2 -
                (nvis_children - 1) * box->spacing);
@@ -247,7 +247,7 @@
 	}
       else
         {
-          /* Retreive desired size for visible children */
+          /* Retreive desired size for visible children. */
 
           for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
             {
@@ -275,23 +275,51 @@
                 }
             }
 
-          /* Sort children by difference between natural and minimum size */
+          /* Distribute the container's extra space c_gap. We want to assign
+           * this space such that the sum of extra space assigned to children
+           * (c^i_gap) is equal to c_cap. The case that there's not enough
+           * space for all children to take their natural size needs some
+           * attention. The goals we want to achieve are:
+           *
+           *   a) Maximize number of children taking their natural size.
+           *   b) The allocated size of children should be a continuous
+           *   function of c_gap.  That is, increasing the container size by
+           *   one pixel should never make drastic changes in the distribution.
+           *   c) If child i takes its natural size and child j doesn't,
+           *   child j should have received at least as much gap as child i.
+           *
+           * The following code distributes the additional space by following
+           * this rules.
+           */
+
+          /* Sort descending by gap and position. */
 
           g_qsort_with_data (spreading,
                              nvis_children, sizeof (GtkBoxSpreading),
-                             gtk_vbox_compare_gap, sizes);
+                             gtk_hbox_compare_gap, sizes);
 
-          for (i = 0; width > 0 && i < nvis_children; ++i)
+          /* Distribute available space.
+           * This master piece of a loop was conceived by Behdad Esfahbod.
+           */
+          for (i = nvis_children - 1; i >= 0; --i)
             {
-              extra = sizes[spreading[i].index].natural_size
-                    - sizes[spreading[i].index].minimum_size;
-
-              extra = MIN (width, extra);
-              width -= extra;
+              /* Divide remaining space by number of remaining children.
+               * Sort order and reducing remaining space by assigned space
+               * ensures that space is distributed equally.
+               */
+              gint glue = (width + i) / (i + 1);
+              gint gap = sizes[spreading[i].index].natural_size
+                       - sizes[spreading[i].index].minimum_size;
 
+              extra = MIN (glue, gap);
               sizes[spreading[i].index].minimum_size += extra;
+
+              width -= extra;
             }
 
+          /* Calculate space which hasn't distributed yet,
+           * and is available for expanding children.
+           */
           if (nexpand_children > 0)
             extra = width / nexpand_children;
           else
@@ -308,7 +336,7 @@
           else
             x = allocation->x + allocation->width - border_width;;
 
-          /* Allocate child positions */
+          /* Allocate child positions. */
 
           i = 0;
           children = box->children;
@@ -319,6 +347,8 @@
 
               if (child->pack == packing && GTK_WIDGET_VISIBLE (child->widget))
                 {
+                  /* Assign the child's size. */
+
                   if (box->homogeneous)
                     {
                       if (nvis_children == 1)
@@ -345,6 +375,8 @@
                         }
                     }
 
+                  /* Assign the child's position. */
+
                   if (child->fill)
                     {
                       child_allocation.width = MAX (1, (gint) child_width - (gint) child->padding * 2);

Attachment: signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]