[gtk+/grid-widget] Implement height-for-width in GtkGrid



commit 9c80c9353404778f59325c86ba6e557ff5dfbb0f
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Oct 10 01:14:53 2010 -0400

    Implement height-for-width in GtkGrid

 gtk/gtkgrid.c |  498 +++++++++++++++++++++++++++++++++++----------------------
 1 files changed, 308 insertions(+), 190 deletions(-)
---
diff --git a/gtk/gtkgrid.c b/gtk/gtkgrid.c
index 8386a3d..957e20e 100644
--- a/gtk/gtkgrid.c
+++ b/gtk/gtkgrid.c
@@ -1,6 +1,6 @@
 /* TODO
- * - wfh
  * - magic expand
+ * - infinite span
  */
 
 /* GTK - The GIMP Toolkit
@@ -28,6 +28,7 @@
 #include "gtkgrid.h"
 
 #include "gtkorientable.h"
+#include "gtksizerequest.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
 
@@ -415,8 +416,8 @@ gtk_grid_init (GtkGrid *grid)
   priv->children = NULL;
   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
 
-  init_lines (ROWS(priv));
-  init_lines (COLUMNS(priv));
+  init_lines (ROWS (priv));
+  init_lines (COLUMNS (priv));
 }
 
 static void grid_attach (GtkGrid   *grid,
@@ -512,12 +513,13 @@ gtk_grid_child_type (GtkContainer *container)
 }
 
 static void
-gtk_grid_allocate_lines (GtkGrid        *grid,
-                         GtkOrientation  orientation)
+gtk_grid_ensure_lines (GtkGrid        *grid,
+                       GtkOrientation  orientation)
 {
   GtkGridPrivate *priv = grid->priv;
   GtkGridLines *lines;
-  GtkGridChild *grid_child;
+  GtkGridChild *child;
+  GtkGridChildAttach *attach;
   GList *list;
   gint min, max;
 
@@ -530,10 +532,11 @@ gtk_grid_allocate_lines (GtkGrid        *grid,
   max = G_MININT;
   for (list = priv->children; list; list = list->next)
     {
-      grid_child = list->data;
+      child = list->data;
+      attach = &child->attach[orientation];
 
-      min = MIN (min, grid_child->attach[orientation].pos);
-      max = MAX (max, grid_child->attach[orientation].pos + grid_child->attach[orientation].span);
+      min = MIN (min, attach->pos);
+      max = MAX (max, attach->pos + attach->span);
     }
 
   lines->min = min;
@@ -541,12 +544,12 @@ gtk_grid_allocate_lines (GtkGrid        *grid,
   lines->lines = g_new (GtkGridLine, max - min);
 }
 
-/* set requisitions to 0, mark rows/cols as expand
- * if they have a non-spanning exanding child
+/* set line sizes to 0, mark lines as expand
+ * if they have a non-spanning expanding child
  */
 static void
-gtk_grid_size_request_init (GtkGrid        *grid,
-                            GtkOrientation  orientation)
+gtk_grid_request_init (GtkGrid        *grid,
+                       GtkOrientation  orientation)
 {
   GtkGridPrivate *priv = grid->priv;
   GtkGridChild *child;
@@ -574,32 +577,36 @@ gtk_grid_size_request_init (GtkGrid        *grid,
     }
 }
 
-static void
-get_preferred_size (GtkWidget      *widget,
-                    GtkOrientation  orientation,
-                    gint           *minimum,
-                    gint           *natural)
+static gint
+compute_allocation_for_span (GtkGrid        *grid,
+                             GtkOrientation  orientation,
+                             GtkGridChild   *child)
 {
-  GtkRequisition minimum_req;
-  GtkRequisition natural_req;
+  GtkGridPrivate *priv = grid->priv;
+  GtkGridLines *lines;
+  GtkGridLine *line;
+  GtkGridChildAttach *attach;
+  gint size;
+  gint i;
 
-  gtk_widget_get_preferred_size (widget, &minimum_req, &natural_req);
+  lines = &priv->lines[orientation];
+  attach = &child->attach[orientation];
 
-  if (minimum)
-    *minimum = orientation == GTK_ORIENTATION_HORIZONTAL
-               ? minimum_req.width
-               : minimum_req.height;
+  size = (attach->span - 1) * lines->spacing;
+  for (i = 0; i < attach->span; i++)
+    {
+      line = &lines->lines[attach->pos - lines->min + i];
+      size += line->allocation;
+    }
 
-  if (natural)
-    *natural = orientation == GTK_ORIENTATION_HORIZONTAL
-               ? natural_req.width
-               : natural_req.height;
+  return size;
 }
 
 /* set requisition to max of non-spanning children */
 static void
-gtk_grid_size_request_pass1 (GtkGrid        *grid,
-                             GtkOrientation  orientation)
+gtk_grid_request_non_spanning (GtkGrid        *grid,
+                               GtkOrientation  orientation,
+                               gboolean        for_size)
 {
   GtkGridPrivate *priv = grid->priv;
   GtkGridChild *child;
@@ -609,6 +616,7 @@ gtk_grid_size_request_pass1 (GtkGrid        *grid,
   GList *list;
   gint minimum;
   gint natural;
+  gint size;
 
   lines = &priv->lines[orientation];
 
@@ -623,7 +631,25 @@ gtk_grid_size_request_pass1 (GtkGrid        *grid,
       if (attach->span != 1)
         continue;
 
-      get_preferred_size (child->widget, orientation, &minimum, &natural);
+      if (for_size)
+        {
+          size = compute_allocation_for_span (grid, 1 - orientation, child);
+          if (orientation == GTK_ORIENTATION_HORIZONTAL)
+            gtk_widget_get_preferred_width_for_height (child->widget,
+                                                       size,
+                                                       &minimum, &natural);
+          else
+            gtk_widget_get_preferred_height_for_width (child->widget,
+                                                       size,
+                                                       &minimum, &natural);
+        }
+      else
+        {
+          if (orientation == GTK_ORIENTATION_HORIZONTAL)
+            gtk_widget_get_preferred_width (child->widget, &minimum, &natural);
+          else
+            gtk_widget_get_preferred_height (child->widget, &minimum, &natural);
+        }
 
       line = &lines->lines[attach->pos - lines->min];
       line->minimum = MAX (line->minimum, minimum);
@@ -633,8 +659,8 @@ gtk_grid_size_request_pass1 (GtkGrid        *grid,
 
 /* force homogeneous sizes */
 static void
-gtk_grid_size_request_pass2 (GtkGrid        *grid,
-                             GtkOrientation  orientation)
+gtk_grid_request_homogeneous (GtkGrid        *grid,
+                              GtkOrientation  orientation)
 {
   GtkGridPrivate *priv = grid->priv;
   GtkGridLines *lines;
@@ -664,8 +690,9 @@ gtk_grid_size_request_pass2 (GtkGrid        *grid,
 
 /* deal with spanning children */
 static void
-gtk_grid_size_request_pass3 (GtkGrid        *grid,
-                             GtkOrientation  orientation)
+gtk_grid_request_spanning (GtkGrid        *grid,
+                           GtkOrientation  orientation,
+                           gboolean        for_size)
 {
   GtkGridPrivate *priv = grid->priv;
   GList *list;
@@ -684,6 +711,7 @@ gtk_grid_size_request_pass3 (GtkGrid        *grid,
   gboolean force;
   gint i;
   gint extra;
+  gint size;
 
   lines = &priv->lines[orientation];
 
@@ -710,7 +738,25 @@ gtk_grid_size_request_pass3 (GtkGrid        *grid,
             span_expand += 1;
         }
 
-      get_preferred_size (child->widget, orientation, &child_minimum, &child_natural);
+      if (for_size)
+        {
+          size = compute_allocation_for_span (grid, 1 - orientation, child);
+          if (orientation == GTK_ORIENTATION_HORIZONTAL)
+            gtk_widget_get_preferred_width_for_height (child->widget,
+                                                       size,
+                                                       &child_minimum, &child_natural);
+          else
+            gtk_widget_get_preferred_height_for_width (child->widget,
+                                                       size,
+                                                       &child_minimum, &child_natural);
+        }
+      else
+        {
+          if (orientation == GTK_ORIENTATION_HORIZONTAL)
+            gtk_widget_get_preferred_width (child->widget, &child_minimum, &child_natural);
+          else
+            gtk_widget_get_preferred_height (child->widget, &child_minimum, &child_natural);
+        }
 
       /* If we need to request more space for this child to fill
        * its requisition, then divide up the needed space amongst the
@@ -764,91 +810,12 @@ gtk_grid_size_request_pass3 (GtkGrid        *grid,
     }
 }
 
-static void
-gtk_grid_get_size (GtkGrid        *grid,
-                   GtkOrientation  orientation,
-                   gint           *minimum_size,
-                   gint           *natural_size)
-{
-  GtkGridPrivate *priv = grid->priv;
-  GtkGridLines *lines;
-  gint i;
-  gint minimum, natural;
-
-  gtk_grid_allocate_lines (grid, orientation);
-
-  gtk_grid_size_request_init (grid, orientation);
-  gtk_grid_size_request_pass1 (grid, orientation);
-  gtk_grid_size_request_pass2 (grid, orientation);
-  gtk_grid_size_request_pass3 (grid, orientation);
-  gtk_grid_size_request_pass2 (grid, orientation);
-
-  lines = &priv->lines[orientation];
-
-  minimum = (lines->max - lines->min - 1) * lines->spacing;
-  natural = (lines->max - lines->min - 1) * lines->spacing;
-
-  for (i = 0; i < lines->max - lines->min; i++)
-    {
-      minimum += lines->lines[i].minimum;
-      natural += lines->lines[i].natural;
-    }
-
-  if (minimum_size)
-    *minimum_size = minimum;
-
-  if (natural_size)
-    *natural_size = natural;
-}
-
-static void
-gtk_grid_get_preferred_width (GtkWidget *widget,
-                              gint      *minimum,
-                              gint      *natural)
-{
-  gtk_grid_get_size (GTK_GRID (widget),
-                     GTK_ORIENTATION_HORIZONTAL,
-                     minimum,
-                     natural);
-}
-
-static void
-gtk_grid_get_preferred_height (GtkWidget *widget,
-                               gint      *minimum,
-                               gint      *natural)
-{
-  gtk_grid_get_size (GTK_GRID (widget),
-                     GTK_ORIENTATION_VERTICAL,
-                     minimum,
-                     natural);
-}
-
-#if 0
-static void
-gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
-                                         gint       height,
-                                         gint      *minimum,
-                                         gint      *natural)
-{
-  GtkGrid *grid = GTK_GRID (widget);
-  GtkGridPrivate *priv = grid->priv;
-}
-
-static void
-gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
-                                         gint       width,
-                                         gint      *minimum,
-                                         gint      *natural)
-{
-  GtkGrid *grid = GTK_GRID (widget);
-  GtkGridPrivate *priv = grid->priv;
-}
-#endif
-
 /* find empty and expanding lines */
 static void
-gtk_grid_size_allocate_init (GtkGrid        *grid,
-                             GtkOrientation  orientation)
+gtk_grid_compute_expand (GtkGrid        *grid,
+                         GtkOrientation  orientation,
+                         gint           *nonempty_lines,
+                         gint           *expand_lines)
 {
   GtkGridPrivate *priv = grid->priv;
   GtkGridChild *child;
@@ -858,14 +825,13 @@ gtk_grid_size_allocate_init (GtkGrid        *grid,
   GtkGridLines *lines;
   GtkGridLine *line;
   gboolean has_expand;
-
-  gtk_grid_allocate_lines (grid, orientation);
+  gint expand;
+  gint empty;
 
   lines = &priv->lines[orientation];
 
   for (i = 0; i < lines->max - lines->min; i++)
     {
-      lines->lines[i].allocation = lines->lines[i].minimum;
       lines->lines[i].need_expand = FALSE;
       lines->lines[i].expand = FALSE;
       lines->lines[i].empty = TRUE;
@@ -918,94 +884,248 @@ gtk_grid_size_allocate_init (GtkGrid        *grid,
         }
     }
 
+  empty = 0;
+  expand = 0;
   for (i = 0; i < lines->max - lines->min; i++)
     {
-      if (lines->lines[i].need_expand)
-        lines->lines[i].expand = TRUE;
+      line = &lines->lines[i];
+
+      if (line->need_expand)
+        line->expand = TRUE;
+
+      if (line->empty)
+        empty += 1;
+
+      if (line->expand)
+        expand += 1;
     }
+
+  if (nonempty_lines)
+    *nonempty_lines = lines->max - lines->min - empty;
+
+  if (expand_lines)
+    *expand_lines = expand;
 }
 
-/* distribute space over lines */
 static void
-gtk_grid_size_allocate_pass1 (GtkGrid        *grid,
-                              GtkOrientation  orientation)
+gtk_grid_request_sum (GtkGrid        *grid,
+                      GtkOrientation  orientation,
+                      gint           *minimum,
+                      gint           *natural)
 {
   GtkGridPrivate *priv = grid->priv;
-  GtkAllocation allocation;
-  gint extra;
+  GtkGridLines *lines;
   gint i;
-  gint nonempty, expand;
+  gint min, nat;
+  gint nonempty;
+
+  gtk_grid_compute_expand (grid, orientation, &nonempty, NULL);
+
+  lines = &priv->lines[orientation];
+
+  min = (nonempty - 1) * lines->spacing;
+  nat = (nonempty - 1) * lines->spacing;
+
+  for (i = 0; i < lines->max - lines->min; i++)
+    {
+      min += lines->lines[i].minimum;
+      nat += lines->lines[i].natural;
+    }
+
+  if (minimum)
+    *minimum = min;
+
+  if (natural)
+    *natural = nat;
+}
+
+static void
+gtk_grid_request_lines (GtkGrid        *grid,
+                        GtkOrientation  orientation,
+                        gboolean        for_size)
+{
+  gtk_grid_ensure_lines (grid, orientation);
+
+  gtk_grid_request_init (grid, orientation);
+  gtk_grid_request_non_spanning (grid, orientation, for_size);
+  gtk_grid_request_homogeneous (grid, orientation);
+  gtk_grid_request_spanning (grid, orientation, for_size);
+  gtk_grid_request_homogeneous (grid, orientation);
+}
+
+static void
+gtk_grid_get_size (GtkGrid        *grid,
+                   GtkOrientation  orientation,
+                   gint           *minimum,
+                   gint           *natural)
+{
+  gtk_grid_request_lines (grid, orientation, FALSE);
+  gtk_grid_request_sum (grid, orientation, minimum, natural);
+}
+
+static void
+gtk_grid_allocate_lines (GtkGrid        *grid,
+                         GtkOrientation  orientation,
+                         gint            total_size)
+{
+  GtkGridPrivate *priv = grid->priv;
   GtkGridLines *lines;
+  GtkGridLine *line;
+  gint nonempty;
+  gint expand;
+  gint i, j;
+  GtkRequestedSize *sizes;
+  gint extra;
+  gint rest;
   gint size;
-  gint alloc_size;
 
-  if (priv->children == NULL)
-    return;
-
-  gtk_widget_get_allocation (GTK_WIDGET (grid), &allocation);
+  gtk_grid_compute_expand (grid, orientation, &nonempty, &expand);
 
   lines = &priv->lines[orientation];
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    alloc_size = allocation.width;
-  else
-    alloc_size = allocation.height;
+
+  size = total_size - (nonempty - 1) * lines->spacing;
 
   if (lines->homogeneous)
     {
-      nonempty = 0;
-      expand = 0;
+      extra = size / nonempty;
+      rest = size % nonempty;
+
       for (i = 0; i < lines->max - lines->min; i++)
         {
-          if (!lines->lines[i].empty)
-            nonempty += 1;
-          if (lines->lines[i].expand)
-            expand += 1;
-        }
+          line = &lines->lines[i];
+          if (line->empty)
+            continue;
 
-      size = alloc_size - (nonempty - 1) * lines->spacing;
-      if (expand > 0)
-        {
-          for (i = 0; i < lines->max - lines->min; i++)
+          line->allocation = extra;
+          if (rest > 0)
             {
-              extra = size / (lines->max - lines->min - i);
-              lines->lines[i].allocation = MAX (1, extra);
-              size -= extra;
+              line->allocation += 1;
+              rest -= 1;
             }
         }
     }
   else
     {
-      size = 0;
-      expand = 0;
-      nonempty = 0;
+      sizes = g_newa (GtkRequestedSize, nonempty);
+
+      j = 0;
       for (i = 0; i < lines->max - lines->min; i++)
         {
-          size += lines->lines[i].minimum;
-          if (!lines->lines[i].empty)
-            nonempty += 1;
-          if (lines->lines[i].expand)
-            expand += 1;
+          line = &lines->lines[i];
+          if (line->empty)
+            continue;
+
+          size -= line->minimum;
+
+          sizes[j].minimum_size = line->minimum;
+          sizes[j].natural_size = line->natural;
+          sizes[j].data = line;
+          j++;
+        }
+
+      size = gtk_distribute_natural_allocation (MAX (0, size), nonempty, sizes);
+
+      if (expand > 0)
+        {
+          extra = size / expand;
+          rest = size % expand;
+        }
+      else
+        {
+          extra = 0;
+          rest = 0;
         }
-      size += (nonempty - 1) * lines->spacing;
 
-      if (size < alloc_size && expand > 0)
+      j = 0;
+      for (i = 0; i < lines->max - lines->min; i++)
         {
-          size = alloc_size - size;
-          for (i = 0; i < lines->max - lines->min; i++)
+          line = &lines->lines[i];
+          if (line->empty)
+            continue;
+
+          g_assert (line == sizes[j].data);
+
+          line->allocation = sizes[j].minimum_size;
+          if (line->expand)
             {
-              if (lines->lines[i].expand)
+              line->allocation += extra;
+              if (rest > 0)
                 {
-                  extra = size / expand;
-                  lines->lines[i].allocation += extra;
-                  size -= extra;
-                  expand -= 1;
+                  line->allocation += 1;
+                  rest -= 1;
                 }
             }
+
+          j++;
         }
     }
 }
 
 static void
+gtk_grid_get_size_for_orientation (GtkGrid        *grid,
+                                   GtkOrientation  orientation,
+                                   gint            size,
+                                   gint           *minimum,
+                                   gint           *natural)
+{
+  gtk_grid_request_lines (grid, 1 - orientation, FALSE);
+  gtk_grid_allocate_lines (grid, 1 - orientation, size);
+  gtk_grid_request_lines (grid, orientation, TRUE);
+  gtk_grid_request_sum (grid, orientation, minimum, natural);
+}
+
+static void
+gtk_grid_get_preferred_width (GtkWidget *widget,
+                              gint      *minimum,
+                              gint      *natural)
+{
+  gtk_grid_get_size (GTK_GRID (widget), GTK_ORIENTATION_HORIZONTAL, minimum, natural);
+}
+
+static void
+gtk_grid_get_preferred_height (GtkWidget *widget,
+                               gint      *minimum,
+                               gint      *natural)
+{
+  gtk_grid_get_size (GTK_GRID (widget), GTK_ORIENTATION_VERTICAL, minimum, natural);
+}
+
+static GtkSizeRequestMode
+gtk_grid_get_request_mode (GtkWidget *widget)
+{
+  GtkGridPrivate *priv = GTK_GRID (widget)->priv;
+
+  if (priv->orientation == GTK_ORIENTATION_VERTICAL)
+    return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+  else
+    return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
+}
+
+static void
+gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
+                                         gint       height,
+                                         gint      *minimum,
+                                         gint      *natural)
+{
+  if (gtk_grid_get_request_mode (GTK_GRID (widget)) != GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
+    gtk_grid_get_preferred_height (widget, &height, NULL);
+
+  gtk_grid_get_size_for_orientation (GTK_GRID (widget), GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
+}
+
+static void
+gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
+                                         gint       width,
+                                         gint      *minimum,
+                                         gint      *natural)
+{
+  if (gtk_grid_get_request_mode (GTK_GRID (widget)) != GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+    gtk_grid_get_preferred_width (widget, &width, NULL);
+
+  gtk_grid_get_size_for_orientation (GTK_GRID (widget), GTK_ORIENTATION_VERTICAL, width, minimum, natural);
+}
+
+static void
 allocate_child (GtkGrid        *grid,
                 GtkOrientation  orientation,
                 GtkGridChild   *child,
@@ -1037,9 +1157,8 @@ allocate_child (GtkGrid        *grid,
     }
 }
 
-/* allocate children */
 static void
-gtk_grid_size_allocate_pass2 (GtkGrid *grid)
+gtk_grid_allocate_children (GtkGrid *grid)
 {
   GtkGridPrivate *priv = grid->priv;
   GList *list;
@@ -1074,25 +1193,26 @@ gtk_grid_size_allocate (GtkWidget     *widget,
                         GtkAllocation *allocation)
 {
   GtkGrid *grid = GTK_GRID (widget);
+  GtkGridPrivate *priv = grid->priv;
 
   gtk_widget_set_allocation (widget, allocation);
 
-  gtk_grid_size_allocate_init (grid, GTK_ORIENTATION_HORIZONTAL);
-  gtk_grid_size_allocate_init (grid, GTK_ORIENTATION_VERTICAL);
-  gtk_grid_size_allocate_pass1 (grid, GTK_ORIENTATION_HORIZONTAL);
-  gtk_grid_size_allocate_pass1 (grid, GTK_ORIENTATION_VERTICAL);
-  gtk_grid_size_allocate_pass2 (grid);
-}
-
-static GtkSizeRequestMode
-gtk_grid_get_request_mode (GtkWidget *widget)
-{
-  GtkGridPrivate *priv = GTK_GRID (widget)->priv;
-
-  if (priv->orientation == GTK_ORIENTATION_VERTICAL)
-    return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      gtk_grid_request_lines (grid, GTK_ORIENTATION_VERTICAL, FALSE);
+      gtk_grid_allocate_lines (grid, GTK_ORIENTATION_VERTICAL, allocation->height);
+      gtk_grid_request_lines (grid, GTK_ORIENTATION_HORIZONTAL, TRUE);
+      gtk_grid_allocate_lines (grid, GTK_ORIENTATION_HORIZONTAL, allocation->width);
+    }
   else
-    return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
+    {
+      gtk_grid_request_lines (grid, GTK_ORIENTATION_HORIZONTAL, FALSE);
+      gtk_grid_allocate_lines (grid, GTK_ORIENTATION_HORIZONTAL, allocation->width);
+      gtk_grid_request_lines (grid, GTK_ORIENTATION_VERTICAL, TRUE);
+      gtk_grid_allocate_lines (grid, GTK_ORIENTATION_VERTICAL, allocation->height);
+    }
+
+  gtk_grid_allocate_children (grid);
 }
 
 static void
@@ -1110,10 +1230,8 @@ gtk_grid_class_init (GtkGridClass *class)
   widget_class->get_preferred_width = gtk_grid_get_preferred_width;
   widget_class->get_preferred_height = gtk_grid_get_preferred_height;
   widget_class->get_request_mode = gtk_grid_get_request_mode;
-#if 0
   widget_class->get_preferred_width_for_height = gtk_grid_get_preferred_width_for_height;
   widget_class->get_preferred_height_for_width = gtk_grid_get_preferred_height_for_width;
-#endif
 
   container_class->add = gtk_grid_add;
   container_class->remove = gtk_grid_remove;



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