[gtk+] Initial support for baselines



commit 852cbb62b8b98bb3345604ce1dcf4186613f74e8
Author: Alexander Larsson <alexl redhat com>
Date:   Tue Mar 5 14:54:03 2013 +0100

    Initial support for baselines
    
    This modifies the size machinery in order to allow baseline support.
    
    We add a new widget vfunc get_preferred_height_and_baseline_for_width
    which queries the normal height_for_width (or non-for-width if width
    is -1) and additionally returns optional (-1 means "no baseline")
    baselines for the minimal and natural heights.
    
    We also add a new gtk_widget_size_allocate_with_baseline() which
    baseline-aware containers can use to allocate children with a specific
    baseline, either one inherited from the parent, or one introduced due
    to requested baseline alignment in the container
    itself. size_allocate_with_baseline() works just like a normal size
    allocation, except the baseline gets recorded so that the child can
    access it via gtk_widget_get_allocated_baseline() when it aligns
    itself.
    
    There are also adjust_baseline_request/allocation similar to the
    allocation adjustment, and we extend the size request cache to also
    store the baselines.

 gtk/gtkcontainer.c               |  50 ++++++++
 gtk/gtksizerequest.c             | 246 +++++++++++++++++++++++++++++++--------
 gtk/gtksizerequestcache.c        |  18 ++-
 gtk/gtksizerequestcacheprivate.h |  10 +-
 gtk/gtkwidget.c                  | 203 +++++++++++++++++++++++++++++---
 gtk/gtkwidget.h                  |  35 +++++-
 gtk/gtkwidgetprivate.h           |   4 +-
 7 files changed, 493 insertions(+), 73 deletions(-)
---
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index ce16fe4..09bfffa 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -309,12 +309,17 @@ static void     gtk_container_adjust_size_request  (GtkWidget         *widget,
                                                     GtkOrientation     orientation,
                                                     gint              *minimum_size,
                                                     gint              *natural_size);
+static void     gtk_container_adjust_baseline_request (GtkWidget      *widget,
+                                                      gint           *minimum_baseline,
+                                                      gint           *natural_baseline);
 static void     gtk_container_adjust_size_allocation (GtkWidget       *widget,
                                                       GtkOrientation   orientation,
                                                       gint            *minimum_size,
                                                       gint            *natural_size,
                                                       gint            *allocated_pos,
                                                       gint            *allocated_size);
+static void     gtk_container_adjust_baseline_allocation (GtkWidget      *widget,
+                                                         gint           *baseline);
 static GtkSizeRequestMode gtk_container_get_request_mode (GtkWidget   *widget);
 
 static gchar* gtk_container_child_default_composite_name (GtkContainer *container,
@@ -444,7 +449,9 @@ gtk_container_class_init (GtkContainerClass *class)
   widget_class->focus = gtk_container_focus;
 
   widget_class->adjust_size_request = gtk_container_adjust_size_request;
+  widget_class->adjust_baseline_request = gtk_container_adjust_baseline_request;
   widget_class->adjust_size_allocation = gtk_container_adjust_size_allocation;
+  widget_class->adjust_baseline_allocation = gtk_container_adjust_baseline_allocation;
   widget_class->get_request_mode = gtk_container_get_request_mode;
 
   class->add = gtk_container_add_unimplemented;
@@ -1918,6 +1925,28 @@ gtk_container_adjust_size_request (GtkWidget         *widget,
 }
 
 static void
+gtk_container_adjust_baseline_request (GtkWidget         *widget,
+                                      gint              *minimum_baseline,
+                                      gint              *natural_baseline)
+{
+  GtkContainer *container;
+
+  container = GTK_CONTAINER (widget);
+
+  if (GTK_CONTAINER_GET_CLASS (widget)->_handle_border_width)
+    {
+      int border_width;
+
+      border_width = container->priv->border_width;
+
+      *minimum_baseline += border_width;
+      *natural_baseline += border_width;
+    }
+
+  parent_class->adjust_baseline_request (widget, minimum_baseline, natural_baseline);
+}
+
+static void
 gtk_container_adjust_size_allocation (GtkWidget         *widget,
                                       GtkOrientation     orientation,
                                       gint              *minimum_size,
@@ -1952,6 +1981,27 @@ gtk_container_adjust_size_allocation (GtkWidget         *widget,
                                         allocated_size);
 }
 
+static void
+gtk_container_adjust_baseline_allocation (GtkWidget         *widget,
+                                         gint              *baseline)
+{
+  GtkContainer *container;
+  int border_width;
+
+  container = GTK_CONTAINER (widget);
+
+  if (GTK_CONTAINER_GET_CLASS (widget)->_handle_border_width)
+    {
+      border_width = container->priv->border_width;
+
+      if (*baseline >= 0)
+       *baseline -= border_width;
+    }
+
+  parent_class->adjust_baseline_allocation (widget, baseline);
+}
+
+
 typedef struct {
   gint hfw;
   gint wfh;
diff --git a/gtk/gtksizerequest.c b/gtk/gtksizerequest.c
index 36d24f2..4e47d88 100644
--- a/gtk/gtksizerequest.c
+++ b/gtk/gtksizerequest.c
@@ -98,11 +98,16 @@ gtk_widget_query_size_for_orientation (GtkWidget        *widget,
                                        GtkOrientation    orientation,
                                        gint              for_size,
                                        gint             *minimum_size,
-                                       gint             *natural_size)
+                                       gint             *natural_size,
+                                       gint             *minimum_baseline,
+                                       gint             *natural_baseline)
 {
   SizeRequestCache *cache;
+  GtkWidgetClass *widget_class;
   gint min_size = 0;
   gint nat_size = 0;
+  gint min_baseline = -1;
+  gint nat_baseline = -1;
   gboolean found_in_cache;
 
   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_CONSTANT_SIZE)
@@ -113,7 +118,11 @@ gtk_widget_query_size_for_orientation (GtkWidget        *widget,
                                                    orientation,
                                                    for_size,
                                                    &min_size,
-                                                   &nat_size);
+                                                   &nat_size,
+                                                  &min_baseline,
+                                                  &nat_baseline);
+
+  widget_class = GTK_WIDGET_GET_CLASS (widget);
   
   if (!found_in_cache)
     {
@@ -128,7 +137,7 @@ gtk_widget_query_size_for_orientation (GtkWidget        *widget,
           if (for_size < 0)
             {
              push_recursion_check (widget, orientation, for_size);
-              GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_size, &nat_size);
+              widget_class->get_preferred_width (widget, &min_size, &nat_size);
              pop_recursion_check (widget, orientation);
             }
           else
@@ -142,17 +151,17 @@ gtk_widget_query_size_for_orientation (GtkWidget        *widget,
              gtk_widget_get_preferred_height (widget, &minimum_height, &natural_height);
 
               /* convert for_size to unadjusted height (for_size is a proposed allocation) */
-              GTK_WIDGET_GET_CLASS (widget)->adjust_size_allocation (widget,
-                                                                     GTK_ORIENTATION_VERTICAL,
-                                                                     &minimum_height,
-                                                                    &natural_height,
-                                                                     &ignored_position,
-                                                                     &adjusted_for_size);
+              widget_class->adjust_size_allocation (widget,
+                                                   GTK_ORIENTATION_VERTICAL,
+                                                   &minimum_height,
+                                                   &natural_height,
+                                                   &ignored_position,
+                                                   &adjusted_for_size);
 
              push_recursion_check (widget, orientation, for_size);
-              GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, 
-                                                                            MAX (adjusted_for_size, 
minimum_height),
-                                                                            &min_size, &nat_size);
+              widget_class->get_preferred_width_for_height (widget,
+                                                           MAX (adjusted_for_size, minimum_height),
+                                                           &min_size, &nat_size);
              pop_recursion_check (widget, orientation);
             }
         }
@@ -161,7 +170,9 @@ gtk_widget_query_size_for_orientation (GtkWidget        *widget,
           if (for_size < 0)
             {
              push_recursion_check (widget, orientation, for_size);
-              GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_size, &nat_size);
+             widget_class->get_preferred_height_and_baseline_for_width (widget, -1,
+                                                                        &min_size, &nat_size,
+                                                                        &min_baseline, &nat_baseline);
              pop_recursion_check (widget, orientation);
             }
           else
@@ -175,17 +186,17 @@ gtk_widget_query_size_for_orientation (GtkWidget        *widget,
              gtk_widget_get_preferred_width (widget, &minimum_width, &natural_width);
 
               /* convert for_size to unadjusted width (for_size is a proposed allocation) */
-              GTK_WIDGET_GET_CLASS (widget)->adjust_size_allocation (widget,
-                                                                     GTK_ORIENTATION_HORIZONTAL,
-                                                                    &minimum_width,
-                                                                     &natural_width,
-                                                                     &ignored_position,
-                                                                     &adjusted_for_size);
+              widget_class->adjust_size_allocation (widget,
+                                                   GTK_ORIENTATION_HORIZONTAL,
+                                                   &minimum_width,
+                                                   &natural_width,
+                                                   &ignored_position,
+                                                   &adjusted_for_size);
 
              push_recursion_check (widget, orientation, for_size);
-              GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, 
-                                                                            MAX (adjusted_for_size, 
minimum_width),
-                                                                            &min_size, &nat_size);
+             widget_class->get_preferred_height_and_baseline_for_width (widget, MAX (adjusted_for_size, 
minimum_width),
+                                                                        &min_size, &nat_size,
+                                                                        &min_baseline, &nat_baseline);
              pop_recursion_check (widget, orientation);
             }
         }
@@ -198,10 +209,10 @@ gtk_widget_query_size_for_orientation (GtkWidget        *widget,
 
       adjusted_min     = min_size;
       adjusted_natural = nat_size;
-      GTK_WIDGET_GET_CLASS (widget)->adjust_size_request (widget,
-                                                          orientation,
-                                                          &adjusted_min,
-                                                          &adjusted_natural);
+      widget_class->adjust_size_request (widget,
+                                        orientation,
+                                        &adjusted_min,
+                                        &adjusted_natural);
 
       if (adjusted_min < min_size ||
           adjusted_natural < nat_size)
@@ -229,11 +240,42 @@ gtk_widget_query_size_for_orientation (GtkWidget        *widget,
           nat_size = adjusted_natural;
         }
 
+      if (min_baseline != -1 || nat_baseline != -1)
+       {
+         if (orientation == GTK_ORIENTATION_HORIZONTAL)
+           {
+             g_warning ("%s %p reported a horizontal baseline",
+                        G_OBJECT_TYPE_NAME (widget), widget);
+             min_baseline = -1;
+             nat_baseline = -1;
+           }
+         else if (min_baseline == -1 || nat_baseline == -1)
+           {
+             g_warning ("%s %p reported baseline for only one of min/natural (min: %d, natural: %d)",
+                        G_OBJECT_TYPE_NAME (widget), widget,
+                        min_baseline, nat_baseline);
+             min_baseline = -1;
+             nat_baseline = -1;
+           }
+         else if (gtk_widget_get_valign_with_baseline (widget) != GTK_ALIGN_BASELINE)
+           {
+             /* Ignore requested baseline for non-aligned widgets */
+             min_baseline = -1;
+             nat_baseline = -1;
+           }
+         else
+           widget_class->adjust_baseline_request (widget,
+                                                  &min_baseline,
+                                                  &nat_baseline);
+       }
+
       _gtk_size_request_cache_commit (cache,
                                       orientation,
                                       for_size,
                                       min_size,
-                                      nat_size);
+                                      nat_size,
+                                     min_baseline,
+                                     nat_baseline);
     }
 
   if (minimum_size)
@@ -242,15 +284,26 @@ gtk_widget_query_size_for_orientation (GtkWidget        *widget,
   if (natural_size)
     *natural_size = nat_size;
 
+  if (minimum_baseline)
+    *minimum_baseline = min_baseline;
+
+  if (natural_baseline)
+    *natural_baseline = nat_baseline;
+
   g_assert (min_size <= nat_size);
 
   GTK_NOTE (SIZE_REQUEST,
-            g_print ("[%p] %s\t%s: %d is minimum %d and natural: %d (hit cache: %s)\n",
+            g_print ("[%p] %s\t%s: %d is minimum %d and natural: %d",
                      widget, G_OBJECT_TYPE_NAME (widget),
                      orientation == GTK_ORIENTATION_HORIZONTAL ?
                      "width for height" : "height for width" ,
-                     for_size, min_size, nat_size,
-                     found_in_cache ? "yes" : "no"));
+                     for_size, min_size, nat_size);
+           if (min_baseline != -1 || nat_baseline != -1)
+             g_print (", baseline %d/%d",
+                      min_baseline, nat_baseline);
+           g_print (" (hit cache: %s)\n",
+                    found_in_cache ? "yes" : "no")
+           );
 }
 
 /* This is the main function that checks for a cached size and
@@ -263,7 +316,9 @@ _gtk_widget_compute_size_for_orientation (GtkWidget        *widget,
                                           GtkOrientation    orientation,
                                           gint              for_size,
                                           gint             *minimum,
-                                          gint             *natural)
+                                          gint             *natural,
+                                         gint             *minimum_baseline,
+                                         gint             *natural_baseline)
 {
   GHashTable *widgets;
   GHashTableIter iter;
@@ -276,12 +331,17 @@ _gtk_widget_compute_size_for_orientation (GtkWidget        *widget,
         *minimum = 0;
       if (natural)
         *natural = 0;
+      if (minimum_baseline)
+        *minimum_baseline = -1;
+      if (natural_baseline)
+        *natural_baseline = -1;
       return;
     }
 
   if (G_LIKELY (!_gtk_widget_get_sizegroups (widget)))
     {
-      gtk_widget_query_size_for_orientation (widget, orientation, for_size, minimum, natural);
+      gtk_widget_query_size_for_orientation (widget, orientation, for_size, minimum, natural,
+                                            minimum_baseline, natural_baseline);
       return;
     }
 
@@ -295,7 +355,7 @@ _gtk_widget_compute_size_for_orientation (GtkWidget        *widget,
       GtkWidget *tmp_widget = key;
       gint min_dimension, nat_dimension;
 
-      gtk_widget_query_size_for_orientation (tmp_widget, orientation, for_size, &min_dimension, 
&nat_dimension);
+      gtk_widget_query_size_for_orientation (tmp_widget, orientation, for_size, &min_dimension, 
&nat_dimension, NULL, NULL);
 
       min_result = MAX (min_result, min_dimension);
       nat_result = MAX (nat_result, nat_dimension);
@@ -305,6 +365,13 @@ _gtk_widget_compute_size_for_orientation (GtkWidget        *widget,
 
   g_hash_table_destroy (widgets);
 
+  /* Baselines make no sense with sizegroups really */
+  if (minimum_baseline)
+    *minimum_baseline = -1;
+
+  if (natural_baseline)
+    *natural_baseline = -1;
+
   if (minimum)
     *minimum = min_result;
 
@@ -377,7 +444,8 @@ gtk_widget_get_preferred_width (GtkWidget *widget,
                                             GTK_ORIENTATION_HORIZONTAL,
                                             -1,
                                             minimum_width,
-                                            natural_width);
+                                            natural_width,
+                                           NULL, NULL);
 }
 
 
@@ -411,7 +479,8 @@ gtk_widget_get_preferred_height (GtkWidget *widget,
                                             GTK_ORIENTATION_VERTICAL,
                                             -1,
                                             minimum_height,
-                                            natural_height);
+                                            natural_height,
+                                           NULL, NULL);
 }
 
 
@@ -448,7 +517,8 @@ gtk_widget_get_preferred_width_for_height (GtkWidget *widget,
                                             GTK_ORIENTATION_HORIZONTAL,
                                             height,
                                             minimum_width,
-                                            natural_width);
+                                            natural_width,
+                                           NULL, NULL);
 }
 
 /**
@@ -483,17 +553,61 @@ gtk_widget_get_preferred_height_for_width (GtkWidget *widget,
                                             GTK_ORIENTATION_VERTICAL,
                                             width,
                                             minimum_height,
-                                            natural_height);
+                                            natural_height,
+                                           NULL, NULL);
 }
 
 /**
- * gtk_widget_get_preferred_size:
+ * gtk_widget_get_preferred_height_and_baseline_for_width:
+ * @widget: a #GtkWidget instance
+ * @width: the width which is available for allocation, or -1 if none
+ * @minimum_height: (out) (allow-none): location for storing the minimum height, or %NULL
+ * @natural_height: (out) (allow-none): location for storing the natural height, or %NULL
+ * @minimum_baseline: (out) (allow-none): location for storing the baseline for the minimum height, or %NULL
+ * @natural_baseline: (out) (allow-none): location for storing the baseline for the natural height, or %NULL
+ *
+ * Retrieves a widget's minimum and natural height and the corresponding baselines if it would be given
+ * the specified @width, or the default height if @width is -1. The baselines may be -1 which means
+ * that no baseline is requested for this widget.
+ *
+ * The returned request will be modified by the
+ * GtkWidgetClass::adjust_size_request and GtkWidgetClass::adjust_baseline_request virtual methods
+ * and by any #GtkSizeGroup<!-- -->s that have been applied. That is, the returned request
+ * is the one that should be used for layout, not necessarily the one
+ * returned by the widget itself.
+ *
+ * Since: 3.10
+ */
+void
+gtk_widget_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
+                                                       gint       width,
+                                                       gint      *minimum_height,
+                                                       gint      *natural_height,
+                                                       gint      *minimum_baseline,
+                                                       gint      *natural_baseline)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (minimum_height != NULL || natural_height != NULL);
+  g_return_if_fail (width >= -1);
+
+  _gtk_widget_compute_size_for_orientation (widget,
+                                            GTK_ORIENTATION_VERTICAL,
+                                            width,
+                                            minimum_height,
+                                            natural_height,
+                                           minimum_baseline,
+                                           natural_baseline);
+}
+
+/**
+ * gtk_widget_get_preferred_size_and_baseline:
  * @widget: a #GtkWidget instance
  * @minimum_size: (out) (allow-none): location for storing the minimum size, or %NULL
  * @natural_size: (out) (allow-none): location for storing the natural size, or %NULL
  *
- * Retrieves the minimum and natural size of a widget, taking
- * into account the widget's preference for height-for-width management.
+ * Retrieves the minimum and natural size  and the corresponding baselines of a widget, taking
+ * into account the widget's preference for height-for-width management. The baselines may
+ * be -1 which means that no baseline is requested for this widget.
  *
  * This is used to retrieve a suitable size by container widgets which do
  * not impose any restrictions on the child placement. It can be used
@@ -505,12 +619,14 @@ gtk_widget_get_preferred_height_for_width (GtkWidget *widget,
  * height for the natural width is generally smaller than the required height for
  * the minimum width.</para></note>
  *
- * Since: 3.0
+ * Since: 3.10
  */
 void
-gtk_widget_get_preferred_size (GtkWidget      *widget,
-                               GtkRequisition *minimum_size,
-                               GtkRequisition *natural_size)
+gtk_widget_get_preferred_size_and_baseline (GtkWidget      *widget,
+                                           GtkRequisition *minimum_size,
+                                           GtkRequisition *natural_size,
+                                           gint           *minimum_baseline,
+                                           gint           *natural_baseline)
 {
   gint min_width, nat_width;
   gint min_height, nat_height;
@@ -524,20 +640,20 @@ gtk_widget_get_preferred_size (GtkWidget      *widget,
       if (minimum_size)
        {
          minimum_size->width = min_width;
-         gtk_widget_get_preferred_height_for_width (widget, min_width,
-                                                     &minimum_size->height, NULL);
+         gtk_widget_get_preferred_height_and_baseline_for_width (widget, min_width,
+                                                                 &minimum_size->height, NULL, 
minimum_baseline, NULL);
        }
 
       if (natural_size)
        {
          natural_size->width = nat_width;
-         gtk_widget_get_preferred_height_for_width (widget, nat_width,
-                                                     NULL, &natural_size->height);
+         gtk_widget_get_preferred_height_and_baseline_for_width (widget, nat_width,
+                                                                 NULL, &natural_size->height, NULL, 
natural_baseline);
        }
     }
   else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT or CONSTANT_SIZE */
     {
-      gtk_widget_get_preferred_height (widget, &min_height, &nat_height);
+      gtk_widget_get_preferred_height_and_baseline_for_width (widget, -1, &min_height, &nat_height, 
minimum_baseline, natural_baseline);
 
       if (minimum_size)
        {
@@ -555,6 +671,38 @@ gtk_widget_get_preferred_size (GtkWidget      *widget,
     }
 }
 
+/**
+ * gtk_widget_get_preferred_size:
+ * @widget: a #GtkWidget instance
+ * @minimum_size: (out) (allow-none): location for storing the minimum size, or %NULL
+ * @natural_size: (out) (allow-none): location for storing the natural size, or %NULL
+ *
+ * Retrieves the minimum and natural size of a widget, taking
+ * into account the widget's preference for height-for-width management.
+ *
+ * This is used to retrieve a suitable size by container widgets which do
+ * not impose any restrictions on the child placement. It can be used
+ * to deduce toplevel window and menu sizes as well as child widgets in
+ * free-form containers such as GtkLayout.
+ *
+ * <note><para>Handle with care. Note that the natural height of a height-for-width
+ * widget will generally be a smaller size than the minimum height, since the required
+ * height for the natural width is generally smaller than the required height for
+ * the minimum width.</para></note>
+ *
+ * Use gtk_widget_get_preferred_size_and_baseline() if you want to support
+ * baseline alignment.
+ *
+ * Since: 3.0
+ */
+void
+gtk_widget_get_preferred_size (GtkWidget      *widget,
+                               GtkRequisition *minimum_size,
+                               GtkRequisition *natural_size)
+{
+  gtk_widget_get_preferred_size_and_baseline (widget, minimum_size, natural_size,
+                                             NULL, NULL);
+}
 
 static gint
 compare_gap (gconstpointer p1,
diff --git a/gtk/gtksizerequestcache.c b/gtk/gtksizerequestcache.c
index 102e2dd..e6107c7 100644
--- a/gtk/gtksizerequestcache.c
+++ b/gtk/gtksizerequestcache.c
@@ -67,7 +67,9 @@ _gtk_size_request_cache_commit (SizeRequestCache *cache,
                                 GtkOrientation    orientation,
                                 gint              for_size,
                                 gint              minimum_size,
-                                gint              natural_size)
+                                gint              natural_size,
+                               gint              minimum_baseline,
+                               gint              natural_baseline)
 {
   SizeRequest **cached_sizes;
   SizeRequest  *cached_size;
@@ -78,6 +80,8 @@ _gtk_size_request_cache_commit (SizeRequestCache *cache,
     {
       cache->cached_size[orientation].minimum_size = minimum_size;
       cache->cached_size[orientation].natural_size = natural_size;
+      cache->cached_size[orientation].minimum_baseline = minimum_baseline;
+      cache->cached_size[orientation].natural_baseline = natural_baseline;
       cache->flags[orientation].cached_size_valid = TRUE;
       return;
     }
@@ -92,7 +96,9 @@ _gtk_size_request_cache_commit (SizeRequestCache *cache,
   for (i = 0; i < n_sizes; i++)
     {
       if (cached_sizes[i]->cached_size.minimum_size == minimum_size &&
-         cached_sizes[i]->cached_size.natural_size == natural_size)
+         cached_sizes[i]->cached_size.natural_size == natural_size &&
+         cached_sizes[i]->cached_size.minimum_baseline == minimum_baseline &&
+         cached_sizes[i]->cached_size.natural_baseline == natural_baseline)
        {
          cached_sizes[i]->lower_for_size = MIN (cached_sizes[i]->lower_for_size, for_size);
          cached_sizes[i]->upper_for_size = MAX (cached_sizes[i]->upper_for_size, for_size);
@@ -125,6 +131,8 @@ _gtk_size_request_cache_commit (SizeRequestCache *cache,
   cached_size->upper_for_size = for_size;
   cached_size->cached_size.minimum_size = minimum_size;
   cached_size->cached_size.natural_size = natural_size;
+  cached_size->cached_size.minimum_baseline = minimum_baseline;
+  cached_size->cached_size.natural_baseline = natural_baseline;
 }
 
 /* looks for a cached size request for this for_size.
@@ -137,7 +145,9 @@ _gtk_size_request_cache_lookup (SizeRequestCache *cache,
                                 GtkOrientation    orientation,
                                 gint              for_size,
                                 gint             *minimum,
-                                gint             *natural)
+                                gint             *natural,
+                               gint             *minimum_baseline,
+                               gint             *natural_baseline)
 {
   CachedSize *result = NULL;
 
@@ -168,6 +178,8 @@ _gtk_size_request_cache_lookup (SizeRequestCache *cache,
     {
       *minimum = result->minimum_size;
       *natural = result->natural_size;
+      *minimum_baseline = result->minimum_baseline;
+      *natural_baseline = result->natural_baseline;
       return TRUE;
     }
   else
diff --git a/gtk/gtksizerequestcacheprivate.h b/gtk/gtksizerequestcacheprivate.h
index ac851dd..e458428 100644
--- a/gtk/gtksizerequestcacheprivate.h
+++ b/gtk/gtksizerequestcacheprivate.h
@@ -41,6 +41,8 @@ G_BEGIN_DECLS
 typedef struct {
   gint minimum_size;
   gint natural_size;
+  gint minimum_baseline;
+  gint natural_baseline;
 } CachedSize;
 
 typedef struct
@@ -72,12 +74,16 @@ void            _gtk_size_request_cache_commit                  (SizeRequestCach
                                                                  GtkOrientation          orientation,
                                                                  gint                    for_size,
                                                                  gint                    minimum_size,
-                                                                 gint                    natural_size);
+                                                                 gint                    natural_size,
+                                                                 gint                    minimum_baseline,
+                                                                 gint                    natural_baseline);
 gboolean        _gtk_size_request_cache_lookup                  (SizeRequestCache       *cache,
                                                                  GtkOrientation          orientation,
                                                                  gint                    for_size,
                                                                  gint                   *minimum,
-                                                                 gint                   *natural);
+                                                                 gint                   *natural,
+                                                                 gint                   *minimum_baseline,
+                                                                 gint                   *natural_baseline);
 
 G_END_DECLS
 
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index d1c9095..bbfdefa 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -95,6 +95,7 @@
  *   <listitem>#GtkWidgetClass.get_preferred_height()</listitem>
  *   <listitem>#GtkWidgetClass.get_preferred_height_for_width()</listitem>
  *   <listitem>#GtkWidgetClass.get_preferred_width_for_height()</listitem>
+ *   <listitem>#GtkWidgetClass.get_preferred_height_and_baseline_for_width()</listitem>
  * </itemizedlist>
  *
  * There are some important things to keep in mind when implementing
@@ -222,6 +223,26 @@
  * container, you <emphasis>must</emphasis> use the wrapper APIs.
  * Otherwise, you would not properly consider widget margins,
  * #GtkSizeGroup, and so forth.
+ *
+ * Since 3.10 Gtk+ also supports baseline vertical alignment of widgets. This
+ * means that widgets are positioned such that the typographical baseline of
+ * widgets in the same row are aligned. This happens if a widget supports baselines,
+ * has a vertical alignment of %GTK_ALIGN_BASELINE, and is inside a container
+ * that supports baselines and has a natural "row" that it aligns to the baseline,
+ * or a baseline assigned to it by the grandparent.
+ *
+ * Baseline alignment support for a widget is done by the 
#GtkWidgetClass.get_preferred_height_and_baseline_for_width()
+ * virtual function. It allows you to report a baseline in combination with the
+ * minimum and natural height. If there is no baseline you can return -1 to indicate
+ * this. The default implementation of this virtual function calls into the
+ * #GtkWidgetClass.get_preferred_height() and #GtkWidgetClass.get_preferred_height_for_width(),
+ * so if baselines are not supported it doesn't need to be implemented.
+ *
+ * If a widget ends up baseline aligned it will be allocated all the space in the parent
+ * as if it was %GTK_ALIGN_FILL, but the selected baseline can be found via 
gtk_widget_get_allocated_baseline().
+ * If this has a value other than -1 you need to align the widget such that the baseline
+ * appears at the position.
+ *
  * </para>
  * </refsect2>
  * <refsect2 id="style-properties">
@@ -478,6 +499,7 @@ struct _GtkWidgetPrivate
 
   /* The widget's allocated size */
   GtkAllocation allocation;
+  gint allocated_baseline;
 
   /* The widget's requested sizes */
   SizeRequestCache requests;
@@ -777,6 +799,12 @@ static void             gtk_widget_real_get_width               (GtkWidget
 static void             gtk_widget_real_get_height              (GtkWidget         *widget,
                                                                  gint              *minimum_size,
                                                                  gint              *natural_size);
+static void  gtk_widget_real_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
+                                                                         gint       width,
+                                                                         gint      *minimum_height,
+                                                                         gint      *natural_height,
+                                                                         gint      *minimum_baseline,
+                                                                         gint      *natural_baseline);
 
 static void             gtk_widget_queue_tooltip_query          (GtkWidget *widget);
 
@@ -785,12 +813,17 @@ static void             gtk_widget_real_adjust_size_request     (GtkWidget
                                                                  GtkOrientation     orientation,
                                                                  gint              *minimum_size,
                                                                  gint              *natural_size);
+static void             gtk_widget_real_adjust_baseline_request (GtkWidget         *widget,
+                                                                gint              *minimum_baseline,
+                                                                gint              *natural_baseline);
 static void             gtk_widget_real_adjust_size_allocation  (GtkWidget         *widget,
                                                                  GtkOrientation     orientation,
                                                                  gint              *minimum_size,
                                                                  gint              *natural_size,
                                                                  gint              *allocated_pos,
                                                                  gint              *allocated_size);
+static void             gtk_widget_real_adjust_baseline_allocation (GtkWidget         *widget,
+                                                                   gint              *baseline);
 
 /* --- functions dealing with template data structures --- */
 static AutomaticChildClass  *automatic_child_class_new          (const gchar          *name,
@@ -1091,6 +1124,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
   klass->get_preferred_height = gtk_widget_real_get_height;
   klass->get_preferred_width_for_height = gtk_widget_real_get_width_for_height;
   klass->get_preferred_height_for_width = gtk_widget_real_get_height_for_width;
+  klass->get_preferred_height_and_baseline_for_width = 
gtk_widget_real_get_preferred_height_and_baseline_for_width;
   klass->state_changed = NULL;
   klass->state_flags_changed = gtk_widget_real_state_flags_changed;
   klass->parent_set = NULL;
@@ -1150,7 +1184,9 @@ gtk_widget_class_init (GtkWidgetClass *klass)
   klass->get_accessible = gtk_widget_real_get_accessible;
 
   klass->adjust_size_request = gtk_widget_real_adjust_size_request;
+  klass->adjust_baseline_request = gtk_widget_real_adjust_baseline_request;
   klass->adjust_size_allocation = gtk_widget_real_adjust_size_allocation;
+  klass->adjust_baseline_allocation = gtk_widget_real_adjust_baseline_allocation;
 
   g_object_class_install_property (gobject_class,
                                   PROP_NAME,
@@ -5306,22 +5342,31 @@ gtk_widget_invalidate_widget_windows (GtkWidget *widget,
 }
 
 /**
- * gtk_widget_size_allocate:
+ * gtk_widget_size_allocate_with_baseline:
  * @widget: a #GtkWidget
  * @allocation: position and size to be allocated to @widget
+ * @baseline: The baseline of the child, or -1
  *
- * This function is only used by #GtkContainer subclasses, to assign a size
- * and position to their child widgets.
+ * This function is only used by #GtkContainer subclasses, to assign a size,
+ * position and (optionally) baseline to their child widgets.
  *
- * In this function, the allocation may be adjusted. It will be forced
- * to a 1x1 minimum size, and the adjust_size_allocation virtual
- * method on the child will be used to adjust the allocation. Standard
- * adjustments include removing the widget's margins, and applying the
- * widget's #GtkWidget:halign and #GtkWidget:valign properties.
+ * In this function, the allocation and baseline may be adjusted. It
+ * will be forced to a 1x1 minimum size, and the
+ * adjust_size_allocation virtual and adjust_baseline_allocation
+ * methods on the child will be used to adjust the allocation and
+ * baseline. Standard adjustments include removing the widget's
+ * margins, and applying the widget's #GtkWidget:halign and
+ * #GtkWidget:valign properties.
+ *
+ * If the child widget does not have a valign of %GTK_ALIGN_BASELINE the
+ * baseline argument is ignored and -1 is used instead.
+ *
+ * Since: 3.10
  **/
 void
-gtk_widget_size_allocate (GtkWidget    *widget,
-                         GtkAllocation *allocation)
+gtk_widget_size_allocate_with_baseline (GtkWidget     *widget,
+                                       GtkAllocation *allocation,
+                                       gint           baseline)
 {
   GtkWidgetPrivate *priv;
   GdkRectangle real_allocation;
@@ -5329,9 +5374,11 @@ gtk_widget_size_allocate (GtkWidget      *widget,
   GdkRectangle adjusted_allocation;
   gboolean alloc_needed;
   gboolean size_changed;
+  gboolean baseline_changed;
   gboolean position_changed;
   gint natural_width, natural_height, dummy;
   gint min_width, min_height;
+  gint old_baseline;
 
   priv = widget->priv;
 
@@ -5358,17 +5405,27 @@ gtk_widget_size_allocate (GtkWidget     *widget,
        }
 
       name = g_type_name (G_OBJECT_TYPE (G_OBJECT (widget)));
-      g_print ("gtk_widget_size_allocate: %*s%s %d %d\n",
+      g_print ("gtk_widget_size_allocate: %*s%s %d %d",
               2 * depth, " ", name,
               allocation->width, allocation->height);
+      if (baseline != -1)
+       g_print (" baseline: %d", baseline);
+      g_print ("\n");
     }
 #endif /* G_ENABLE_DEBUG */
 
+  /* Never pass a baseline to a child unless it requested it.
+     This means containers don't have to manually check for this. */
+  if (baseline != -1 &&
+      gtk_widget_get_valign_with_baseline (widget) != GTK_ALIGN_BASELINE)
+    baseline = -1;
+
   alloc_needed = priv->alloc_needed;
   /* Preserve request/allocate ordering */
   priv->alloc_needed = FALSE;
 
   old_allocation = priv->allocation;
+  old_baseline = priv->allocated_baseline;
   real_allocation = *allocation;
 
   adjusted_allocation = real_allocation;
@@ -5418,6 +5475,9 @@ gtk_widget_size_allocate (GtkWidget       *widget,
                                                         &natural_height,
                                                         &adjusted_allocation.y,
                                                         &adjusted_allocation.height);
+  if (baseline >= 0)
+    GTK_WIDGET_GET_CLASS (widget)->adjust_baseline_allocation (widget,
+                                                              &baseline);
 
   if (adjusted_allocation.x < real_allocation.x ||
       adjusted_allocation.y < real_allocation.y ||
@@ -5447,14 +5507,16 @@ gtk_widget_size_allocate (GtkWidget     *widget,
   real_allocation.width = MAX (real_allocation.width, 1);
   real_allocation.height = MAX (real_allocation.height, 1);
 
+  baseline_changed = old_baseline != baseline;
   size_changed = (old_allocation.width != real_allocation.width ||
                  old_allocation.height != real_allocation.height);
   position_changed = (old_allocation.x != real_allocation.x ||
                      old_allocation.y != real_allocation.y);
 
-  if (!alloc_needed && !size_changed && !position_changed)
+  if (!alloc_needed && !size_changed && !position_changed && !baseline_changed)
     goto out;
 
+  priv->allocated_baseline = baseline;
   g_signal_emit (widget, widget_signals[SIZE_ALLOCATE], 0, &real_allocation);
 
   /* Size allocation is god... after consulting god, no further requests or allocations are needed */
@@ -5473,7 +5535,7 @@ gtk_widget_size_allocate (GtkWidget       *widget,
          cairo_region_destroy (invalidate);
        }
 
-      if (size_changed)
+      if (size_changed || baseline_changed)
        {
          if (priv->redraw_on_alloc)
            {
@@ -5488,7 +5550,7 @@ gtk_widget_size_allocate (GtkWidget       *widget,
        }
     }
 
-  if ((size_changed || position_changed) && priv->parent &&
+  if ((size_changed || position_changed || baseline_changed) && priv->parent &&
       gtk_widget_get_realized (priv->parent) && _gtk_container_get_reallocate_redraws (GTK_CONTAINER 
(priv->parent)))
     {
       cairo_region_t *invalidate = cairo_region_create_rectangle (&priv->parent->priv->allocation);
@@ -5500,6 +5562,31 @@ out:
   gtk_widget_pop_verify_invariants (widget);
 }
 
+
+/**
+ * gtk_widget_size_allocate:
+ * @widget: a #GtkWidget
+ * @allocation: position and size to be allocated to @widget
+ *
+ * This function is only used by #GtkContainer subclasses, to assign a size
+ * and position to their child widgets.
+ *
+ * In this function, the allocation may be adjusted. It will be forced
+ * to a 1x1 minimum size, and the adjust_size_allocation virtual
+ * method on the child will be used to adjust the allocation. Standard
+ * adjustments include removing the widget's margins, and applying the
+ * widget's #GtkWidget:halign and #GtkWidget:valign properties.
+ *
+ * For baseline support in containers you need to use gtk_widget_size_allocate_with_baseline()
+ * instead.
+ **/
+void
+gtk_widget_size_allocate (GtkWidget    *widget,
+                         GtkAllocation *allocation)
+{
+  gtk_widget_size_allocate_with_baseline (widget, allocation, -1);
+}
+
 /**
  * gtk_widget_common_ancestor:
  * @widget_a: a #GtkWidget
@@ -5792,6 +5879,18 @@ gtk_widget_real_adjust_size_allocation (GtkWidget         *widget,
     }
 }
 
+static void
+gtk_widget_real_adjust_baseline_allocation (GtkWidget         *widget,
+                                           gint              *baseline)
+{
+  const GtkWidgetAuxInfo *aux_info;
+
+  aux_info = _gtk_widget_get_aux_info_or_defaults (widget);
+
+  if (baseline >= 0)
+    *baseline -= aux_info->margin.top;
+}
+
 static gboolean
 gtk_widget_real_can_activate_accel (GtkWidget *widget,
                                     guint      signal_id)
@@ -11294,6 +11393,28 @@ gtk_widget_real_adjust_size_request (GtkWidget         *widget,
     }
 }
 
+static void
+gtk_widget_real_adjust_baseline_request (GtkWidget         *widget,
+                                        gint              *minimum_baseline,
+                                        gint              *natural_baseline)
+{
+  const GtkWidgetAuxInfo *aux_info;
+
+  aux_info =_gtk_widget_get_aux_info_or_defaults (widget);
+
+  if (aux_info->height >= 0)
+    {
+      /* No baseline support for explicitly set height */
+      *minimum_baseline = -1;
+      *natural_baseline = -1;
+    }
+  else
+    {
+      *minimum_baseline += aux_info->margin.top;
+      *natural_baseline += aux_info->margin.top;
+    }
+}
+
 /**
  * _gtk_widget_peek_request_cache:
  *
@@ -13478,6 +13599,25 @@ gtk_widget_real_get_width_for_height (GtkWidget *widget,
   GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
 }
 
+static void
+gtk_widget_real_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
+                                                            gint       width,
+                                                            gint      *minimum_height,
+                                                            gint      *natural_height,
+                                                            gint      *minimum_baseline,
+                                                            gint      *natural_baseline)
+{
+  if (width == -1)
+    GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
+  else
+    GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, width, minimum_height, 
natural_height);
+
+  if (minimum_baseline)
+    *minimum_baseline = -1;
+  if (natural_baseline)
+    *natural_baseline = -1;
+}
+
 /**
  * gtk_widget_get_halign:
  * @widget: a #GtkWidget
@@ -13519,6 +13659,15 @@ gtk_widget_set_halign (GtkWidget *widget,
   g_object_notify (G_OBJECT (widget), "halign");
 }
 
+/**
+ * gtk_widget_get_valign_with_baseline:
+ * @widget: a #GtkWidget
+ *
+ * Gets the value of the #GtkWidget:valign property, including
+ * %GTK_ALIGN_BASELINE.
+ *
+ * Returns: the vertical alignment of @widget
+ */
 GtkAlign
 gtk_widget_get_valign_with_baseline (GtkWidget *widget)
 {
@@ -13532,7 +13681,12 @@ gtk_widget_get_valign_with_baseline (GtkWidget *widget)
  *
  * Gets the value of the #GtkWidget:valign property.
  *
- * Returns: the vertical alignment of @widget
+ * For backwards compatibility reasons this method will never return
+ * %GTK_ALIGN_BASELINE, but instead it will convert it to
+ * %GTK_ALIGN_FILL. If your widget want to support baseline aligned
+ * children it must use gtk_widget_get_valign_with_baseline().
+ *
+ * Returns: the vertical alignment of @widget, ignoring baseline alignment
  */
 GtkAlign
 gtk_widget_get_valign (GtkWidget *widget)
@@ -14320,6 +14474,25 @@ gtk_widget_get_allocated_height (GtkWidget *widget)
 }
 
 /**
+ * gtk_widget_get_allocated_baseline:
+ * @widget: the widget to query
+ *
+ * Returns the baseline that has currently been allocated to @widget.
+ * This function is intended to be used when implementing handlers
+ * for the #GtkWidget::draw function, and when allocating child
+ * widgets in #GtkWidget::size_allocate.
+ *
+ * Returns: the baseline of the @widget, or -1 if none
+ **/
+int
+gtk_widget_get_allocated_baseline (GtkWidget *widget)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+
+  return widget->priv->allocated_baseline;
+}
+
+/**
  * gtk_widget_get_requisition:
  * @widget: a #GtkWidget
  * @requisition: (out): a pointer to a #GtkRequisition to copy to
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 97e9f7a..58bcea6 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -433,14 +433,23 @@ struct _GtkWidgetClass
   gboolean     (* touch_event)            (GtkWidget     *widget,
                                            GdkEventTouch *event);
 
+  void         (* get_preferred_height_and_baseline_for_width)  (GtkWidget     *widget,
+                                                                gint           width,
+                                                                gint          *minimum_height,
+                                                                gint          *natural_height,
+                                                                gint          *minimum_baseline,
+                                                                gint          *natural_baseline);
+  void         (* adjust_baseline_request)(GtkWidget         *widget,
+                                           gint              *minimum_baseline,
+                                           gint              *natural_baseline);
+  void         (* adjust_baseline_allocation) (GtkWidget         *widget,
+                                              gint              *baseline);
+
   /*< private >*/
 
   GtkWidgetClassPrivate *priv;
 
   /* Padding for future expansion */
-  void (*_gtk_reserved2) (void);
-  void (*_gtk_reserved3) (void);
-  void (*_gtk_reserved4) (void);
   void (*_gtk_reserved5) (void);
   void (*_gtk_reserved6) (void);
   void (*_gtk_reserved7) (void);
@@ -498,6 +507,10 @@ void       gtk_widget_size_request        (GtkWidget           *widget,
                                            GtkRequisition      *requisition);
 void      gtk_widget_size_allocate       (GtkWidget           *widget,
                                           GtkAllocation       *allocation);
+GDK_AVAILABLE_IN_3_10
+void      gtk_widget_size_allocate_with_baseline         (GtkWidget           *widget,
+                                                          GtkAllocation       *allocation,
+                                                          gint                 baseline);
 
 GtkSizeRequestMode  gtk_widget_get_request_mode               (GtkWidget      *widget);
 void                gtk_widget_get_preferred_width            (GtkWidget      *widget,
@@ -514,9 +527,22 @@ void                gtk_widget_get_preferred_width_for_height (GtkWidget      *w
                                                                gint            height,
                                                                gint           *minimum_width,
                                                                gint           *natural_width);
+GDK_AVAILABLE_IN_3_10
+void   gtk_widget_get_preferred_height_and_baseline_for_width (GtkWidget     *widget,
+                                                              gint           width,
+                                                              gint          *minimum_height,
+                                                              gint          *natural_height,
+                                                              gint          *minimum_baseline,
+                                                              gint          *natural_baseline);
 void                gtk_widget_get_preferred_size             (GtkWidget      *widget,
                                                                GtkRequisition *minimum_size,
                                                                GtkRequisition *natural_size);
+GDK_AVAILABLE_IN_3_10
+void                gtk_widget_get_preferred_size_and_baseline (GtkWidget      *widget,
+                                                               GtkRequisition *minimum_size,
+                                                               GtkRequisition *natural_size,
+                                                               gint           *minimum_baseline,
+                                                               gint           *natural_baseline);
 
 GDK_DEPRECATED_IN_3_0_FOR(gtk_widget_get_preferred_size)
 void       gtk_widget_get_child_requisition (GtkWidget         *widget,
@@ -662,6 +688,8 @@ void                  gtk_widget_unregister_window      (GtkWidget    *widget,
 
 int                   gtk_widget_get_allocated_width    (GtkWidget     *widget);
 int                   gtk_widget_get_allocated_height   (GtkWidget     *widget);
+GDK_AVAILABLE_IN_3_10
+int                   gtk_widget_get_allocated_baseline (GtkWidget     *widget);
 
 void                  gtk_widget_get_allocation         (GtkWidget     *widget,
                                                          GtkAllocation *allocation);
@@ -760,6 +788,7 @@ GtkAlign gtk_widget_get_halign        (GtkWidget *widget);
 void     gtk_widget_set_halign        (GtkWidget *widget,
                                        GtkAlign   align);
 GtkAlign gtk_widget_get_valign        (GtkWidget *widget);
+GDK_AVAILABLE_IN_3_10
 GtkAlign gtk_widget_get_valign_with_baseline (GtkWidget *widget);
 void     gtk_widget_set_valign        (GtkWidget *widget,
                                        GtkAlign   align);
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 84cad64..7927813 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -69,7 +69,9 @@ void _gtk_widget_compute_size_for_orientation  (GtkWidget         *widget,
                                                 GtkOrientation     orientation,
                                                 gint               for_size,
                                                 gint              *minimum_size,
-                                                gint              *natural_size);
+                                                gint              *natural_size,
+                                               gint              *minimum_baseline,
+                                               gint              *natural_baseline);
 void _gtk_widget_get_preferred_size_for_size   (GtkWidget         *widget,
                                                 GtkOrientation     orientation,
                                                 gint               size,


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