[gtk+/wip/baseline3: 11/17] GtkGrid: Support baseline alignment in GtkGrid



commit 44fbf3eed1e24a6a96c6d2eb57518a767a521870
Author: Alexander Larsson <alexl redhat com>
Date:   Thu Mar 21 11:09:43 2013 +0100

    GtkGrid: Support baseline alignment in GtkGrid
    
    We support a local baseline in each row, as well as selecting
    a specific row for the global baseline of the entire GtkGrid.

 gtk/gtkgrid.c |  714 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 gtk/gtkgrid.h |   12 +
 2 files changed, 617 insertions(+), 109 deletions(-)
---
diff --git a/gtk/gtkgrid.c b/gtk/gtkgrid.c
index d392fb1..30a5e01 100644
--- a/gtk/gtkgrid.c
+++ b/gtk/gtkgrid.c
@@ -52,6 +52,7 @@
 
 typedef struct _GtkGridChild GtkGridChild;
 typedef struct _GtkGridChildAttach GtkGridChildAttach;
+typedef struct _GtkGridRowProperties GtkGridRowProperties;
 typedef struct _GtkGridLine GtkGridLine;
 typedef struct _GtkGridLines GtkGridLines;
 typedef struct _GtkGridLineData GtkGridLineData;
@@ -63,6 +64,17 @@ struct _GtkGridChildAttach
   gint span;
 };
 
+struct _GtkGridRowProperties
+{
+  gint row;
+  GtkBaselinePosition baseline_position;
+};
+
+static const GtkGridRowProperties gtk_grid_row_properties_default = {
+  0,
+  GTK_BASELINE_POSITION_CENTER
+};
+
 struct _GtkGridChild
 {
   GtkWidget *widget;
@@ -86,8 +98,10 @@ struct _GtkGridLineData
 struct _GtkGridPrivate
 {
   GList *children;
+  GList *row_properties;
 
   GtkOrientation orientation;
+  gint baseline_row;
 
   GtkGridLineData linedata[2];
 };
@@ -102,8 +116,14 @@ struct _GtkGridLine
 {
   gint minimum;
   gint natural;
+  gint minimum_above;
+  gint minimum_below;
+  gint natural_above;
+  gint natural_below;
+
   gint position;
   gint allocation;
+  gint allocated_baseline;
 
   guint need_expand : 1;
   guint expand      : 1;
@@ -130,7 +150,8 @@ enum
   PROP_ROW_SPACING,
   PROP_COLUMN_SPACING,
   PROP_ROW_HOMOGENEOUS,
-  PROP_COLUMN_HOMOGENEOUS
+  PROP_COLUMN_HOMOGENEOUS,
+  PROP_BASELINE_ROW
 };
 
 enum
@@ -146,6 +167,8 @@ G_DEFINE_TYPE_WITH_CODE (GtkGrid, gtk_grid, GTK_TYPE_CONTAINER,
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
 
 
+static void gtk_grid_row_properties_free (GtkGridRowProperties *props);
+
 static void
 gtk_grid_get_property (GObject    *object,
                        guint       prop_id,
@@ -177,6 +200,10 @@ gtk_grid_get_property (GObject    *object,
       g_value_set_boolean (value, ROWS (priv)->homogeneous);
       break;
 
+    case PROP_BASELINE_ROW:
+      g_value_set_int (value, priv->baseline_row);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -228,6 +255,10 @@ gtk_grid_set_property (GObject      *object,
       gtk_grid_set_column_homogeneous (grid, g_value_get_boolean (value));
       break;
 
+    case PROP_BASELINE_ROW:
+      gtk_grid_set_baseline_row (grid, g_value_get_int (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -354,6 +385,7 @@ gtk_grid_init (GtkGrid *grid)
 
   priv->children = NULL;
   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+  priv->baseline_row = 0;
 
   priv->linedata[0].spacing = 0;
   priv->linedata[1].spacing = 0;
@@ -363,6 +395,17 @@ gtk_grid_init (GtkGrid *grid)
 }
 
 static void
+gtk_grid_finalize (GObject *object)
+{
+  GtkGrid *grid = GTK_GRID (object);
+  GtkGridPrivate *priv = grid->priv;
+
+  g_list_free_full (priv->row_properties, (GDestroyNotify)gtk_grid_row_properties_free);
+
+  G_OBJECT_CLASS (gtk_grid_parent_class)->finalize (object);
+}
+
+static void
 grid_attach (GtkGrid   *grid,
              GtkWidget *widget,
              gint       left,
@@ -561,6 +604,10 @@ gtk_grid_request_init (GtkGridRequest *request,
     {
       lines->lines[i].minimum = 0;
       lines->lines[i].natural = 0;
+      lines->lines[i].minimum_above = -1;
+      lines->lines[i].minimum_below = -1;
+      lines->lines[i].natural_above = -1;
+      lines->lines[i].natural_below = -1;
       lines->lines[i].expand = FALSE;
       lines->lines[i].empty = TRUE;
     }
@@ -620,8 +667,14 @@ compute_request_for_child (GtkGridRequest *request,
                            GtkOrientation  orientation,
                            gboolean        contextual,
                            gint           *minimum,
-                           gint           *natural)
+                           gint           *natural,
+                          gint           *minimum_baseline,
+                           gint           *natural_baseline)
 {
+  if (minimum_baseline)
+    *minimum_baseline = -1;
+  if (natural_baseline)
+    *natural_baseline = -1;
   if (contextual)
     {
       gint size;
@@ -632,16 +685,20 @@ compute_request_for_child (GtkGridRequest *request,
                                                    size,
                                                    minimum, natural);
       else
-        gtk_widget_get_preferred_height_for_width (child->widget,
-                                                   size,
-                                                   minimum, natural);
+        gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
+                                                               size,
+                                                               minimum, natural,
+                                                               minimum_baseline, natural_baseline);
     }
   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);
+        gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
+                                                               -1,
+                                                               minimum, natural,
+                                                               minimum_baseline, natural_baseline);
     }
 }
 
@@ -660,8 +717,10 @@ gtk_grid_request_non_spanning (GtkGridRequest *request,
   GtkGridLines *lines;
   GtkGridLine *line;
   GList *list;
-  gint minimum;
-  gint natural;
+  gint i;
+  GtkBaselinePosition baseline_pos;
+  gint minimum, minimum_baseline;
+  gint natural, natural_baseline;
 
   lines = &request->lines[orientation];
 
@@ -676,11 +735,57 @@ gtk_grid_request_non_spanning (GtkGridRequest *request,
       if (attach->span != 1)
         continue;
 
-      compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
+      compute_request_for_child (request, child, orientation, contextual, &minimum, &natural, 
&minimum_baseline, &natural_baseline);
 
       line = &lines->lines[attach->pos - lines->min];
-      line->minimum = MAX (line->minimum, minimum);
-      line->natural = MAX (line->natural, natural);
+
+      if (minimum_baseline != -1)
+       {
+         line->minimum_above = MAX (line->minimum_above, minimum_baseline);
+         line->minimum_below = MAX (line->minimum_below, minimum - minimum_baseline);
+         line->natural_above = MAX (line->natural_above, natural_baseline);
+         line->natural_below = MAX (line->natural_below, natural - natural_baseline);
+       }
+      else
+       {
+         line->minimum = MAX (line->minimum, minimum);
+         line->natural = MAX (line->natural, natural);
+       }
+    }
+
+  for (i = 0; i < lines->max - lines->min; i++)
+    {
+      line = &lines->lines[i];
+
+      if (line->minimum_above != -1)
+       {
+         line->minimum = MAX (line->minimum, line->minimum_above + line->minimum_below);
+         line->natural = MAX (line->natural, line->natural_above + line->natural_below);
+
+         baseline_pos = gtk_grid_get_row_baseline_position (request->grid, i + lines->min);
+
+         switch (baseline_pos)
+           {
+           case GTK_BASELINE_POSITION_TOP:
+             line->minimum_above += 0;
+             line->minimum_below += line->minimum - (line->minimum_above + line->minimum_below);
+             line->natural_above += 0;
+             line->natural_below += line->natural - (line->natural_above + line->natural_below);
+             break;
+           case GTK_BASELINE_POSITION_CENTER:
+             line->minimum_above += (line->minimum - (line->minimum_above + line->minimum_below))/2;
+             line->minimum_below += (line->minimum - (line->minimum_above + line->minimum_below))/2;
+             line->natural_above += (line->natural - (line->natural_above + line->natural_below))/2;
+             line->natural_below += (line->natural - (line->natural_above + line->natural_below))/2;
+             break;
+           case GTK_BASELINE_POSITION_BOTTOM:
+             line->minimum_above += line->minimum - (line->minimum_above + line->minimum_below);
+             line->minimum_below += 0;
+             line->natural_above += line->natural - (line->natural_above + line->natural_below);
+             line->natural_below += 0;
+             break;
+           }
+       }
     }
 }
 
@@ -715,6 +820,8 @@ gtk_grid_request_homogeneous (GtkGridRequest *request,
     {
       lines->lines[i].minimum = minimum;
       lines->lines[i].natural = natural;
+      /* TODO: Do we want to adjust the baseline here too?
+        And if so, also in the homogenous resize. */
     }
 }
 
@@ -759,7 +866,8 @@ gtk_grid_request_spanning (GtkGridRequest *request,
       if (attach->span == 1)
         continue;
 
-      compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
+      /* We ignore baselines for spanning children */
+      compute_request_for_child (request, child, orientation, contextual, &minimum, &natural, NULL, NULL);
 
       span_minimum = (attach->span - 1) * linedata->spacing;
       span_natural = (attach->span - 1) * linedata->spacing;
@@ -859,6 +967,8 @@ gtk_grid_request_spanning (GtkGridRequest *request,
 static void
 gtk_grid_request_compute_expand (GtkGridRequest *request,
                                  GtkOrientation  orientation,
+                                gint            min,
+                                gint            max,
                                  gint           *nonempty_lines,
                                  gint           *expand_lines)
 {
@@ -875,7 +985,10 @@ gtk_grid_request_compute_expand (GtkGridRequest *request,
 
   lines = &request->lines[orientation];
 
-  for (i = 0; i < lines->max - lines->min; i++)
+  min = MAX (min, lines->min);
+  max = MIN (max, lines->max);
+
+  for (i = min - lines->min; i < max - lines->min; i++)
     {
       lines->lines[i].need_expand = FALSE;
       lines->lines[i].expand = FALSE;
@@ -893,6 +1006,9 @@ gtk_grid_request_compute_expand (GtkGridRequest *request,
       if (attach->span != 1)
         continue;
 
+      if (attach->pos >= max || attach->pos < min)
+       continue;
+
       line = &lines->lines[attach->pos - lines->min];
       line->empty = FALSE;
       if (gtk_widget_compute_expand (child->widget, orientation))
@@ -914,17 +1030,25 @@ gtk_grid_request_compute_expand (GtkGridRequest *request,
       for (i = 0; i < attach->span; i++)
         {
           line = &lines->lines[attach->pos - lines->min + i];
+
+          if (line->expand)
+            has_expand = TRUE;
+
+         if (attach->pos + i >= max || attach->pos + 1 < min)
+           continue;
+
           if (line->empty)
             line->expand = TRUE;
           line->empty = FALSE;
-          if (line->expand)
-            has_expand = TRUE;
         }
 
       if (!has_expand && gtk_widget_compute_expand (child->widget, orientation))
         {
           for (i = 0; i < attach->span; i++)
             {
+             if (attach->pos + i >= max || attach->pos + 1 < min)
+               continue;
+
               line = &lines->lines[attach->pos - lines->min + i];
               line->need_expand = TRUE;
             }
@@ -933,7 +1057,7 @@ gtk_grid_request_compute_expand (GtkGridRequest *request,
 
   empty = 0;
   expand = 0;
-  for (i = 0; i < lines->max - lines->min; i++)
+  for (i = min - lines->min; i < max - lines->min; i++)
     {
       line = &lines->lines[i];
 
@@ -948,7 +1072,7 @@ gtk_grid_request_compute_expand (GtkGridRequest *request,
     }
 
   if (nonempty_lines)
-    *nonempty_lines = lines->max - lines->min - empty;
+    *nonempty_lines = max - min - empty;
 
   if (expand_lines)
     *expand_lines = expand;
@@ -960,7 +1084,9 @@ static void
 gtk_grid_request_sum (GtkGridRequest *request,
                       GtkOrientation  orientation,
                       gint           *minimum,
-                      gint           *natural)
+                      gint           *natural,
+                     gint           *minimum_baseline,
+                     gint           *natural_baseline)
 {
   GtkGridPrivate *priv = request->grid->priv;
   GtkGridLineData *linedata;
@@ -969,23 +1095,40 @@ gtk_grid_request_sum (GtkGridRequest *request,
   gint min, nat;
   gint nonempty;
 
-  gtk_grid_request_compute_expand (request, orientation, &nonempty, NULL);
+  gtk_grid_request_compute_expand (request, orientation, G_MININT, G_MAXINT, &nonempty, NULL);
 
   linedata = &priv->linedata[orientation];
   lines = &request->lines[orientation];
 
   min = 0;
   nat = 0;
-  if (nonempty > 0)
-    {
-      min = (nonempty - 1) * linedata->spacing;
-      nat = (nonempty - 1) * linedata->spacing;
-    }
-
   for (i = 0; i < lines->max - lines->min; i++)
     {
+      if (orientation == GTK_ORIENTATION_VERTICAL &&
+         lines->min + i == priv->baseline_row &&
+         lines->lines[i].minimum_above != -1)
+       {
+         if (minimum_baseline)
+           *minimum_baseline = min + lines->lines[i].minimum_above;
+         if (natural_baseline)
+           *natural_baseline = nat + lines->lines[i].natural_above;
+       }
+
       min += lines->lines[i].minimum;
       nat += lines->lines[i].natural;
+
+      if (!lines->lines[i].empty)
+       {
+         min += linedata->spacing;
+         nat += linedata->spacing;
+       }
+    }
+
+  /* Remove last spacing, if any was applied */
+  if (nonempty > 0)
+    {
+      min -= linedata->spacing;
+      nat -= linedata->spacing;
     }
 
   if (minimum)
@@ -1011,6 +1154,77 @@ gtk_grid_request_run (GtkGridRequest *request,
   gtk_grid_request_homogeneous (request, orientation);
 }
 
+static void
+gtk_grid_distribute_non_homogeneous (GtkGridLines *lines,
+                                    gint nonempty,
+                                    gint expand,
+                                    gint size,
+                                    gint min,
+                                    gint max)
+{
+  GtkRequestedSize *sizes;
+  GtkGridLine *line;
+  gint extra;
+  gint rest;
+  int i, j;
+
+  if (nonempty == 0)
+    return;
+
+  sizes = g_newa (GtkRequestedSize, nonempty);
+
+  j = 0;
+  for (i = min - lines->min; i < max - lines->min; i++)
+    {
+      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;
+    }
+
+  j = 0;
+  for (i = min - lines->min; i < 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)
+       {
+         line->allocation += extra;
+         if (rest > 0)
+           {
+             line->allocation += 1;
+             rest -= 1;
+           }
+       }
+
+      j++;
+    }
+}
+
 /* Requires that the minimum and natural fields of lines
  * have been set, computes the allocation field of lines
  * by distributing total_size among lines.
@@ -1024,28 +1238,75 @@ gtk_grid_request_allocate (GtkGridRequest *request,
   GtkGridLineData *linedata;
   GtkGridLines *lines;
   GtkGridLine *line;
-  gint nonempty;
-  gint expand;
-  gint i, j;
-  GtkRequestedSize *sizes;
-  gint extra;
+  gint nonempty1, nonempty2;
+  gint expand1, expand2;
+  gint i;
+  GtkBaselinePosition baseline_pos;
+  gint baseline;
+  gint extra, extra2;
   gint rest;
-  gint size;
-
-  gtk_grid_request_compute_expand (request, orientation, &nonempty, &expand);
-
-  if (nonempty == 0)
-    return;
+  gint size1, size2;
+  gint split, split_pos;
 
   linedata = &priv->linedata[orientation];
   lines = &request->lines[orientation];
 
-  size = total_size - (nonempty - 1) * linedata->spacing;
+  baseline = gtk_widget_get_allocated_baseline (GTK_WIDGET (request->grid));
+
+  if (orientation == GTK_ORIENTATION_VERTICAL && baseline != -1 &&
+      priv->baseline_row >= lines->min && priv->baseline_row < lines->max &&
+      lines->lines[priv->baseline_row - lines->min].minimum_above != -1)
+    {
+      split = priv->baseline_row;
+      split_pos = baseline - lines->lines[priv->baseline_row - lines->min].minimum_above;
+      gtk_grid_request_compute_expand (request, orientation, lines->min, split, &nonempty1, &expand1);
+      gtk_grid_request_compute_expand (request, orientation, split, lines->max, &nonempty2, &expand2);
+
+      if (nonempty2 > 0)
+       {
+         size1 = split_pos - (nonempty1) * linedata->spacing;
+         size2 = (total_size - split_pos) - (nonempty2 - 1) * linedata->spacing;
+       }
+      else
+       {
+         size1 = total_size - (nonempty1 - 1) * linedata->spacing;
+         size2 = 0;
+       }
+    }
+  else
+    {
+      gtk_grid_request_compute_expand (request, orientation, lines->min, lines->max, &nonempty1, &expand1);
+      nonempty2 = expand2 = 0;
+      split = lines->max;
+
+      size1 = total_size - (nonempty1 - 1) * linedata->spacing;
+      size2 = 0;
+    }
+
+  if (nonempty1 == 0 && nonempty2 == 0)
+    return;
 
   if (linedata->homogeneous)
     {
-      extra = size / nonempty;
-      rest = size % nonempty;
+      if (nonempty1 > 0)
+       {
+         extra = size1 / nonempty1;
+         rest = size1 % nonempty1;
+       }
+      else
+       {
+         extra = 0;
+         rest = 0;
+       }
+      if (nonempty2 > 0)
+       {
+         extra2 = size2 / nonempty2;
+         if (extra2 < extra || nonempty1 == 0)
+           {
+             extra = extra2;
+             rest = size2 % nonempty2;
+           }
+       }
 
       for (i = 0; i < lines->max - lines->min; i++)
         {
@@ -1063,58 +1324,50 @@ gtk_grid_request_allocate (GtkGridRequest *request,
     }
   else
     {
-      sizes = g_newa (GtkRequestedSize, nonempty);
-
-      j = 0;
-      for (i = 0; i < lines->max - lines->min; i++)
-        {
-          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);
+      gtk_grid_distribute_non_homogeneous (lines,
+                                          nonempty1,
+                                          expand1,
+                                          size1,
+                                          lines->min,
+                                          split);
+      gtk_grid_distribute_non_homogeneous (lines,
+                                          nonempty2,
+                                          expand2,
+                                          size2,
+                                          split,
+                                          lines->max);
+    }
 
-      if (expand > 0)
-        {
-          extra = size / expand;
-          rest = size % expand;
-        }
+  for (i = 0; i < lines->max - lines->min; i++)
+    {
+      line = &lines->lines[i];
+      if (line->empty)
+       continue;
+
+      if (line->minimum_above != -1)
+       {
+         /* Note: This is overridden in gtk_grid_request_position for the allocated baseline */
+         baseline_pos = gtk_grid_get_row_baseline_position (request->grid, i + lines->min);
+
+         switch (baseline_pos)
+           {
+           case GTK_BASELINE_POSITION_TOP:
+             line->allocated_baseline =
+               line->minimum_above;
+             break;
+           case GTK_BASELINE_POSITION_CENTER:
+             line->allocated_baseline =
+               line->minimum_above +
+               (line->allocation - (line->minimum_above + line->minimum_below)) / 2;
+             break;
+           case GTK_BASELINE_POSITION_BOTTOM:
+             line->allocated_baseline =
+               line->allocation - line->minimum_below;
+             break;
+           }
+       }
       else
-        {
-          extra = 0;
-          rest = 0;
-        }
-
-      j = 0;
-      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)
-            {
-              line->allocation += extra;
-              if (rest > 0)
-                {
-                  line->allocation += 1;
-                  rest -= 1;
-                }
-            }
-
-          j++;
-        }
+       line->allocated_baseline = -1;
     }
 }
 
@@ -1128,20 +1381,46 @@ gtk_grid_request_position (GtkGridRequest *request,
   GtkGridLineData *linedata;
   GtkGridLines *lines;
   GtkGridLine *line;
-  gint position;
-  gint i;
+  gint position, old_position;
+  int allocated_baseline;
+  gint i, j;
 
   linedata = &priv->linedata[orientation];
   lines = &request->lines[orientation];
 
+  allocated_baseline = gtk_widget_get_allocated_baseline (GTK_WIDGET(request->grid));
+
   position = 0;
   for (i = 0; i < lines->max - lines->min; i++)
     {
       line = &lines->lines[i];
+
+      if (orientation == GTK_ORIENTATION_VERTICAL &&
+         i + lines->min == priv->baseline_row &&
+         allocated_baseline != -1 &&
+         lines->lines[i].minimum_above != -1)
+       {
+         old_position = position;
+         position = allocated_baseline - line->minimum_above;
+
+         /* Back-patch previous rows */
+         for (j = 0; j < i; j++)
+           {
+             if (!lines->lines[j].empty)
+               lines->lines[j].position += position - old_position;
+           }
+       }
+
       if (!line->empty)
         {
           line->position = position;
           position += line->allocation + linedata->spacing;
+
+         if (orientation == GTK_ORIENTATION_VERTICAL &&
+             i + lines->min == priv->baseline_row &&
+             allocated_baseline != -1 &&
+             lines->lines[i].minimum_above != -1)
+           line->allocated_baseline = allocated_baseline - line->position;
         }
     }
 }
@@ -1150,7 +1429,9 @@ static void
 gtk_grid_get_size (GtkGrid        *grid,
                    GtkOrientation  orientation,
                    gint           *minimum,
-                   gint           *natural)
+                   gint           *natural,
+                  gint           *minimum_baseline,
+                  gint           *natural_baseline)
 {
   GtkGridRequest request;
   GtkGridLines *lines;
@@ -1161,6 +1442,12 @@ gtk_grid_get_size (GtkGrid        *grid,
   if (natural)
     *natural = 0;
 
+  if (minimum_baseline)
+    *minimum_baseline = -1;
+
+  if (natural_baseline)
+    *natural_baseline = -1;
+
   if (grid->priv->children == NULL)
     return;
 
@@ -1171,7 +1458,8 @@ gtk_grid_get_size (GtkGrid        *grid,
   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
 
   gtk_grid_request_run (&request, orientation, FALSE);
-  gtk_grid_request_sum (&request, orientation, minimum, natural);
+  gtk_grid_request_sum (&request, orientation, minimum, natural,
+                       minimum_baseline, natural_baseline);
 }
 
 static void
@@ -1179,7 +1467,9 @@ gtk_grid_get_size_for_size (GtkGrid        *grid,
                             GtkOrientation  orientation,
                             gint            size,
                             gint           *minimum,
-                            gint           *natural)
+                            gint           *natural,
+                           gint           *minimum_baseline,
+                            gint           *natural_baseline)
 {
   GtkGridRequest request;
   GtkGridLines *lines;
@@ -1191,6 +1481,12 @@ gtk_grid_get_size_for_size (GtkGrid        *grid,
   if (natural)
     *natural = 0;
 
+  if (minimum_baseline)
+    *minimum_baseline = -1;
+
+  if (natural_baseline)
+    *natural_baseline = -1;
+
   if (grid->priv->children == NULL)
     return;
 
@@ -1204,11 +1500,11 @@ gtk_grid_get_size_for_size (GtkGrid        *grid,
   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
 
   gtk_grid_request_run (&request, 1 - orientation, FALSE);
-  gtk_grid_request_sum (&request, 1 - orientation, &min_size, NULL);
+  gtk_grid_request_sum (&request, 1 - orientation, &min_size, NULL, NULL, NULL);
   gtk_grid_request_allocate (&request, 1 - orientation, MAX (size, min_size));
 
   gtk_grid_request_run (&request, orientation, TRUE);
-  gtk_grid_request_sum (&request, orientation, minimum, natural);
+  gtk_grid_request_sum (&request, orientation, minimum, natural, minimum_baseline, natural_baseline);
 }
 
 static void
@@ -1219,9 +1515,9 @@ gtk_grid_get_preferred_width (GtkWidget *widget,
   GtkGrid *grid = GTK_GRID (widget);
 
   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
-    gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, 0, minimum, natural);
+    gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, 0, minimum, natural, NULL, NULL);
   else
-    gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
+    gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural, NULL, NULL);
 }
 
 static void
@@ -1232,9 +1528,9 @@ gtk_grid_get_preferred_height (GtkWidget *widget,
   GtkGrid *grid = GTK_GRID (widget);
 
   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
-    gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, 0, minimum, natural);
+    gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, 0, minimum, natural, NULL, NULL);
   else
-    gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
+    gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural, NULL, NULL);
 }
 
 static void
@@ -1246,9 +1542,9 @@ gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
   GtkGrid *grid = GTK_GRID (widget);
 
   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
-    gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
+    gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural, NULL, NULL);
   else
-    gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
+    gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural, NULL, NULL);
 }
 
 static void
@@ -1260,17 +1556,35 @@ gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
   GtkGrid *grid = GTK_GRID (widget);
 
   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
-    gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
+    gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, width, minimum, natural, NULL, NULL);
+  else
+    gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural, NULL, NULL);
+}
+
+static void
+gtk_grid_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
+                                                     gint       width,
+                                                     gint      *minimum,
+                                                     gint      *natural,
+                                                     gint      *minimum_baseline,
+                                                     gint      *natural_baseline)
+{
+  GtkGrid *grid = GTK_GRID (widget);
+
+  if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH && width != -1)
+    gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, width, minimum, natural, minimum_baseline, 
natural_baseline);
   else
-    gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
+    gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural, minimum_baseline, natural_baseline);
 }
 
+
 static void
 allocate_child (GtkGridRequest *request,
                 GtkOrientation  orientation,
                 GtkGridChild   *child,
                 gint           *position,
-                gint           *size)
+                gint           *size,
+               gint           *baseline)
 {
   GtkGridPrivate *priv = request->grid->priv;
   GtkGridLineData *linedata;
@@ -1284,6 +1598,10 @@ allocate_child (GtkGridRequest *request,
   attach = &child->attach[orientation];
 
   *position = lines->lines[attach->pos - lines->min].position;
+  if (attach->span == 1)
+    *baseline = lines->lines[attach->pos - lines->min].allocated_baseline;
+  else
+    *baseline = -1;
 
   *size = (attach->span - 1) * linedata->spacing;
   for (i = 0; i < attach->span; i++)
@@ -1301,7 +1619,7 @@ gtk_grid_request_allocate_children (GtkGridRequest *request)
   GtkGridChild *child;
   GtkAllocation allocation;
   GtkAllocation child_allocation;
-  gint x, y, width, height;
+  gint x, y, width, height, baseline, ignore;
 
   gtk_widget_get_allocation (GTK_WIDGET (request->grid), &allocation);
 
@@ -1312,8 +1630,8 @@ gtk_grid_request_allocate_children (GtkGridRequest *request)
       if (!gtk_widget_get_visible (child->widget))
         continue;
 
-      allocate_child (request, GTK_ORIENTATION_HORIZONTAL, child, &x, &width);
-      allocate_child (request, GTK_ORIENTATION_VERTICAL, child, &y, &height);
+      allocate_child (request, GTK_ORIENTATION_HORIZONTAL, child, &x, &width, &ignore);
+      allocate_child (request, GTK_ORIENTATION_VERTICAL, child, &y, &height, &baseline);
 
       child_allocation.x = allocation.x + x;
       child_allocation.y = allocation.y + y;
@@ -1324,7 +1642,7 @@ gtk_grid_request_allocate_children (GtkGridRequest *request)
         child_allocation.x = allocation.x + allocation.width
                              - (child_allocation.x - allocation.x) - child_allocation.width;
 
-      gtk_widget_size_allocate (child->widget, &child_allocation);
+      gtk_widget_size_allocate_with_baseline (child->widget, &child_allocation, baseline);
     }
 }
 
@@ -1366,6 +1684,7 @@ gtk_grid_size_allocate (GtkWidget     *widget,
   gtk_grid_request_run (&request, 1 - orientation, FALSE);
   gtk_grid_request_allocate (&request, 1 - orientation, GET_SIZE (allocation, 1 - orientation));
   gtk_grid_request_run (&request, orientation, TRUE);
+
   gtk_grid_request_allocate (&request, orientation, GET_SIZE (allocation, orientation));
 
   gtk_grid_request_position (&request, 0);
@@ -1383,12 +1702,14 @@ gtk_grid_class_init (GtkGridClass *class)
 
   object_class->get_property = gtk_grid_get_property;
   object_class->set_property = gtk_grid_set_property;
+  object_class->finalize = gtk_grid_finalize;
 
   widget_class->size_allocate = gtk_grid_size_allocate;
   widget_class->get_preferred_width = gtk_grid_get_preferred_width;
   widget_class->get_preferred_height = gtk_grid_get_preferred_height;
   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;
+  widget_class->get_preferred_height_and_baseline_for_width = 
gtk_grid_get_preferred_height_and_baseline_for_width;
 
   container_class->add = gtk_grid_add;
   container_class->remove = gtk_grid_remove;
@@ -1428,6 +1749,13 @@ gtk_grid_class_init (GtkGridClass *class)
                           FALSE,
                           GTK_PARAM_READWRITE));
 
+  g_object_class_install_property (object_class, PROP_BASELINE_ROW,
+    g_param_spec_int ("baseline-row",
+                      P_("Baseline Row"),
+                      P_("The row to align the to the baseline when valign is GTK_ALIGN_BASELINE"),
+                      0, G_MAXINT, 0,
+                      GTK_PARAM_READWRITE));
+
   gtk_container_class_install_child_property (container_class, CHILD_PROP_LEFT_ATTACH,
     g_param_spec_int ("left-attach",
                       P_("Left attachment"),
@@ -1682,6 +2010,14 @@ gtk_grid_insert_row (GtkGrid *grid,
           gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "height");
         }
     }
+
+  for (list = priv->row_properties; list != NULL; list = list->next)
+    {
+      GtkGridRowProperties *prop = list->data;
+
+      if (prop->row >= position)
+       prop->row += 1;
+    }
 }
 
 /**
@@ -2068,3 +2404,163 @@ gtk_grid_get_column_spacing (GtkGrid *grid)
 
   return ROWS (priv)->spacing;
 }
+
+static GtkGridRowProperties *
+find_row_properties (GtkGrid      *grid,
+                    gint          row)
+{
+  GList *l;
+
+  for (l = grid->priv->row_properties; l != NULL; l = l->next)
+    {
+      GtkGridRowProperties *prop = l->data;
+      if (prop->row == row)
+       return prop;
+    }
+
+  return NULL;
+}
+
+static void
+gtk_grid_row_properties_free (GtkGridRowProperties *props)
+{
+  g_slice_free (GtkGridRowProperties, props);
+}
+
+static GtkGridRowProperties *
+get_row_properties_or_create (GtkGrid      *grid,
+                             gint          row)
+{
+  GtkGridRowProperties *props;
+  GtkGridPrivate *priv = grid->priv;
+
+  props = find_row_properties (grid, row);
+  if (props)
+    return props;
+
+  props = g_slice_new (GtkGridRowProperties);
+  *props = gtk_grid_row_properties_default;
+  props->row = row;
+
+  priv->row_properties =
+    g_list_prepend (priv->row_properties, props);
+
+  return props;
+}
+
+static const GtkGridRowProperties *
+get_row_properties_or_default (GtkGrid      *grid,
+                              gint          row)
+{
+  GtkGridRowProperties *props;
+
+  props = find_row_properties (grid, row);
+  if (props)
+    return props;
+  return &gtk_grid_row_properties_default;
+}
+
+/**
+ * gtk_grid_set_row_baseline_position:
+ * @grid: a #GtkGrid
+ * @row: a row index
+ * @pos: a #GtkBaselinePosition
+ *
+ * Sets how the baseline should be positioned on @row of the
+ * grid, in case that row is assigned more space than is requested.
+ */
+void
+gtk_grid_set_row_baseline_position (GtkGrid            *grid,
+                                   gint                row,
+                                   GtkBaselinePosition pos)
+{
+  GtkGridRowProperties *props;
+
+  g_return_if_fail (GTK_IS_GRID (grid));
+
+  props = get_row_properties_or_create (grid, row);
+
+  if (props->baseline_position != pos)
+    {
+      props->baseline_position = pos;
+
+      if (gtk_widget_get_visible (GTK_WIDGET (grid)))
+        gtk_widget_queue_resize (GTK_WIDGET (grid));
+    }
+}
+
+/**
+ * gtk_grid_get_row_baseline_position:
+ * @grid: a #GtkGrid
+ * @row: a row index
+ *
+ * Returns the baseline position of @row as set
+ * by gtk_grid_set_row_baseline_position() or the default value
+ * %GTK_BASELINE_POSITION_CENTER.
+ *
+ * Returns: the baseline position of @row
+ */
+GtkBaselinePosition
+gtk_grid_get_row_baseline_position (GtkGrid      *grid,
+                                   gint          row)
+{
+  const GtkGridRowProperties *props;
+
+  g_return_val_if_fail (GTK_IS_GRID (grid), GTK_BASELINE_POSITION_CENTER);
+
+  props = get_row_properties_or_default (grid, row);
+
+  return props->baseline_position;
+}
+
+/**
+ * gtk_grid_set_baseline_row:
+ * @grid: a #GtkGrid
+ * @row: the row index
+ *
+ * Sets which row defines the global baseline for the entire grid.
+ * Each row in the grid can have its own local baseline, but only
+ * one of those is global, meaning it will be the baseline in the
+ * parent of the @grid.
+ *
+ * Returns: the row index defining the global baseline
+ */
+void
+gtk_grid_set_baseline_row (GtkGrid *grid,
+                          gint     row)
+{
+  GtkGridPrivate *priv;
+
+  g_return_if_fail (GTK_IS_GRID (grid));
+
+  priv =  grid->priv;
+
+  if (priv->baseline_row != row)
+    {
+      priv->baseline_row = row;
+
+      if (gtk_widget_get_visible (GTK_WIDGET (grid)))
+       gtk_widget_queue_resize (GTK_WIDGET (grid));
+      g_object_notify (G_OBJECT (grid), "baseline-row");
+    }
+}
+
+/**
+ * gtk_grid_set_baseline_row:
+ * @grid: a #GtkGrid
+ *
+ * Returns which row defines the global baseline of @grid.
+ *
+ * Returns: the row index defining the global baseline
+ */
+gint
+gtk_grid_get_baseline_row (GtkGrid         *grid)
+{
+  GtkGridPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_GRID (grid), 0);
+
+  priv = grid->priv;
+
+  return priv->baseline_row;
+}
diff --git a/gtk/gtkgrid.h b/gtk/gtkgrid.h
index f2d550f..9ba21e5 100644
--- a/gtk/gtkgrid.h
+++ b/gtk/gtkgrid.h
@@ -109,6 +109,18 @@ gboolean   gtk_grid_get_column_homogeneous (GtkGrid         *grid);
 void       gtk_grid_set_column_spacing     (GtkGrid         *grid,
                                             guint            spacing);
 guint      gtk_grid_get_column_spacing     (GtkGrid         *grid);
+GDK_AVAILABLE_IN_3_10
+void       gtk_grid_set_row_baseline_position (GtkGrid      *grid,
+                                              gint          row,
+                                              GtkBaselinePosition pos);
+GDK_AVAILABLE_IN_3_10
+GtkBaselinePosition gtk_grid_get_row_baseline_position (GtkGrid      *grid,
+                                                       gint          row);
+GDK_AVAILABLE_IN_3_10
+void       gtk_grid_set_baseline_row       (GtkGrid         *grid,
+                                           gint             row);
+GDK_AVAILABLE_IN_3_10
+gint       gtk_grid_get_baseline_row       (GtkGrid         *grid);
 
 
 G_END_DECLS


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