[gtk+/native-layout] Fixed height-for-width implementation for combobox/cellview



commit 94f7cf03476bb1634783ab219e391e1eea83656d
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Tue Aug 17 01:33:11 2010 -0400

    Fixed height-for-width implementation for combobox/cellview
    
    Now the combo box requests height-for-width properly taking
    into account all of its cells at request/allocation time.
    
    Note an implementation of menu-items is needed to get the
    the combo's drop-down menu to condense to a proper (smaller)
    height when given enough width.

 gtk/gtkcellview.c |  417 ++++++++++++++++++++++++++++++++++-------------------
 gtk/gtkcellview.h |    9 +-
 gtk/gtkcombobox.c |  355 +++++++++++++++++++++++++++++++--------------
 3 files changed, 519 insertions(+), 262 deletions(-)
---
diff --git a/gtk/gtkcellview.c b/gtk/gtkcellview.c
index 0bc91b5..12b4efd 100644
--- a/gtk/gtkcellview.c
+++ b/gtk/gtkcellview.c
@@ -128,6 +128,14 @@ static void       gtk_cell_view_get_width                      (GtkSizeRequest
 static void       gtk_cell_view_get_height                     (GtkSizeRequest        *widget,
 								gint                  *minimum_size,
 								gint                  *natural_size);
+static void       gtk_cell_view_get_width_for_height           (GtkSizeRequest        *widget,
+								gint                   avail_size,
+								gint                  *minimum_size,
+								gint                  *natural_size);
+static void       gtk_cell_view_get_height_for_width           (GtkSizeRequest        *widget,
+								gint                   avail_size,
+								gint                  *minimum_size,
+								gint                  *natural_size);
 
 static GtkBuildableIface *parent_buildable_iface;
 
@@ -329,20 +337,27 @@ gtk_cell_view_size_allocate (GtkWidget     *widget,
                              GtkAllocation *allocation)
 {
   GtkCellView *cellview;
-  GList *i;
-  gint nexpand_cells = 0;
-  gint requested_width = 0;
-  gint natural_width = 0;
-  gint available, natural, extra;
+  GtkRequestedSize *sizes;
+  GArray           *array;
+  GList            *list;
+  gint              nexpand_cells = 0;
+  gint              avail_width = 0;
+  gint              extra_per_cell, extra_extra, i;
+  gboolean          first_cell = TRUE;
 
   widget->allocation = *allocation;
 
   cellview = GTK_CELL_VIEW (widget);
 
+  avail_width = allocation->width;
+
+  array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
+
   /* checking how much extra space we have */
-  for (i = cellview->priv->cell_list; i; i = i->next)
+  for (list = cellview->priv->cell_list; list; list = list->next)
     {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
+      GtkRequestedSize requested;
 
       if (!gtk_cell_renderer_get_visible (info->cell))
         continue;
@@ -350,41 +365,54 @@ gtk_cell_view_size_allocate (GtkWidget     *widget,
       if (info->expand)
         nexpand_cells++;
 
-      requested_width += info->requested_width;
-      natural_width += info->natural_width - info->requested_width;
+      requested.data = info;
+      requested.minimum_size = info->requested_width;
+      requested.natural_size = info->natural_width;
+      g_array_append_val (array, requested);
+
+      if (!first_cell)
+	avail_width -= cellview->priv->spacing;
+
+      avail_width -= requested.minimum_size;
+
+      first_cell = FALSE;
     }
 
-  available = MAX (0, widget->allocation.width - requested_width);
-  natural = MIN (available, natural_width);
-  available -= natural;
+  sizes       = (GtkRequestedSize *)array->data;
+  avail_width = gtk_distribute_natural_allocation (MAX (0, avail_width), array->len, sizes);
 
+  /* Deal with any expand space... */
   if (nexpand_cells > 0)
-    extra = available / nexpand_cells;
+    {
+      extra_per_cell = avail_width / nexpand_cells;
+      extra_extra    = avail_width % nexpand_cells;
+    }
   else
-    extra = 0;
+    /* Everything just left-aligned if no cells expand */
+    extra_per_cell = extra_extra = 0;
 
-  for (i = cellview->priv->cell_list; i; i = i->next)
+  for (i = 0, list = cellview->priv->cell_list; list; list = list->next)
     {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
 
       if (!gtk_cell_renderer_get_visible (info->cell))
         continue;
 
-      info->real_width = info->requested_width;
-
-      if (natural_width > 0)
-          info->real_width += natural * (info->natural_width - info->requested_width) / natural_width;
+      info->real_width = sizes[i].minimum_size;
 
       if (info->expand)
         {
-          if (nexpand_cells == 1)
-            info->real_width += available;
-          else
-            info->real_width += extra;
+	  info->real_width += extra_per_cell;
 
-         nexpand_cells -= 1;
-         available -= extra;
-       }
+	  if (extra_extra)
+	    {
+	      info->real_width++;
+	      extra_extra--;
+	    }
+	}
+      
+      /* increment index into sizes for visible children */
+      i++;
     }
 }
 
@@ -392,12 +420,13 @@ static gboolean
 gtk_cell_view_expose (GtkWidget      *widget,
                       GdkEventExpose *event)
 {
-  GList *i;
+  GList *list;
   GtkCellView *cellview;
   GdkRectangle area;
   GtkCellRendererState state;
   gboolean rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
-
+  GtkPackType packing;
+  
   cellview = GTK_CELL_VIEW (widget);
 
   if (!gtk_widget_is_drawable (widget))
@@ -428,7 +457,6 @@ gtk_cell_view_expose (GtkWidget      *widget,
   area = widget->allocation;
 
   /* we draw on our very own window, initialize x and y to zero */
-  area.x = widget->allocation.x + (rtl ? widget->allocation.width : 0); 
   area.y = widget->allocation.y;
 
   if (gtk_widget_get_state (widget) == GTK_STATE_PRELIGHT)
@@ -438,55 +466,44 @@ gtk_cell_view_expose (GtkWidget      *widget,
   else
     state = 0;
       
-  /* PACK_START */
-  for (i = cellview->priv->cell_list; i; i = i->next)
+  for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
     {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
-
-      if (info->pack == GTK_PACK_END)
-        continue;
+      if (packing == GTK_PACK_START)
+	area.x = widget->allocation.x + (rtl ? widget->allocation.width : 0); 
+      else
+	area.x = rtl ? widget->allocation.x : (widget->allocation.x + widget->allocation.width);  
 
-      if (!gtk_cell_renderer_get_visible (info->cell))
-        continue;
-
-      area.width = info->real_width;
-      if (rtl)                                             
-         area.x -= area.width;
+      for (list = cellview->priv->cell_list; list; list = list->next)
+	{
+	  GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
 
-      gtk_cell_renderer_render (info->cell,
-                                event->window,
-                                widget,
-                                /* FIXME! */
-                                &area, &area, &event->area, state);
+	  if (info->pack != packing)
+	    continue;
 
-      if (!rtl)                                           
-         area.x += info->real_width;
-    }
+	  if (!gtk_cell_renderer_get_visible (info->cell))
+	    continue;
 
-   area.x = rtl ? widget->allocation.x : (widget->allocation.x + widget->allocation.width);  
+	  area.width = info->real_width;
 
-  /* PACK_END */
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
+	  if ((packing == GTK_PACK_START && rtl) ||
+	      (packing == GTK_PACK_END && !rtl))
+	    area.x -= area.width;
 
-      if (info->pack == GTK_PACK_START)
-        continue;
+	  gtk_cell_renderer_render (info->cell,
+				    event->window,
+				    widget,
+				    /* FIXME! */
+				    &area, &area, &event->area, state);
 
-      if (!gtk_cell_renderer_get_visible (info->cell))
-        continue;
-
-      area.width = info->real_width;
-      if (!rtl)
-         area.x -= area.width;   
-
-      gtk_cell_renderer_render (info->cell,
-                                widget->window,
-                                widget,
-                                /* FIXME ! */
-                                &area, &area, &event->area, state);
-      if (rtl)
-         area.x += info->real_width;
+	  if ((packing == GTK_PACK_START && !rtl) ||
+	      (packing == GTK_PACK_END && rtl))
+	    {
+	      area.x += area.width;
+	      area.x += cellview->priv->spacing;
+	    }
+	  else
+	    area.x -= cellview->priv->spacing;
+	}
     }
 
   return FALSE;
@@ -980,7 +997,7 @@ gtk_cell_view_get_displayed_row (GtkCellView *cell_view)
  * Since: 2.6
  * 
  * Deprecated: 3.0: Use gtk_cell_view_get_desired_width_of_row() and
- * gtk_cell_view_get_desired_height_of_row() instead.
+ * gtk_cell_view_get_desired_height_for_width_of_row() instead.
  */
 gboolean
 gtk_cell_view_get_size_of_row (GtkCellView    *cell_view,
@@ -992,8 +1009,9 @@ gtk_cell_view_get_size_of_row (GtkCellView    *cell_view,
   g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), FALSE);
   g_return_val_if_fail (path != NULL, FALSE);
 
+  /* Return the minimum height for the minimum width */
   gtk_cell_view_get_desired_width_of_row (cell_view, path, &req.width, NULL);
-  gtk_cell_view_get_desired_height_of_row (cell_view, path, &req.height, NULL);
+  gtk_cell_view_get_desired_height_for_width_of_row (cell_view, path, req.width, &req.height, NULL);
 
   if (requisition)
     *requisition = req;
@@ -1002,35 +1020,6 @@ gtk_cell_view_get_size_of_row (GtkCellView    *cell_view,
 }
 
 
-
-static void
-gtk_cell_view_get_desired_size_of_row (GtkCellView     *cell_view,
-				       GtkTreePath     *path,
-				       GtkOrientation   orientation,
-				       gint            *minimum_size,
-				       gint            *natural_size)
-{
-  GtkTreeRowReference *tmp;
-
-  tmp = cell_view->priv->displayed_row;
-  cell_view->priv->displayed_row =
-    gtk_tree_row_reference_new (cell_view->priv->model, path);
-
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    gtk_cell_view_get_width (GTK_SIZE_REQUEST (cell_view), minimum_size, natural_size);
-  else
-    gtk_cell_view_get_height (GTK_SIZE_REQUEST (cell_view), minimum_size, natural_size);
-
-  gtk_tree_row_reference_free (cell_view->priv->displayed_row);
-  cell_view->priv->displayed_row = tmp;
-
-  /* Restore active size */
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    gtk_cell_view_get_width (GTK_SIZE_REQUEST (cell_view), NULL, NULL);
-  else
-    gtk_cell_view_get_height (GTK_SIZE_REQUEST (cell_view), NULL, NULL);
-}
-
 /**
  * gtk_cell_view_get_desired_width_of_row:
  * @cell_view: a #GtkCellView
@@ -1049,37 +1038,65 @@ gtk_cell_view_get_desired_width_of_row (GtkCellView     *cell_view,
 					gint            *minimum_size,
 					gint            *natural_size)
 {
+  GtkTreeRowReference *tmp;
+
   g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
   g_return_if_fail (path != NULL);
   g_return_if_fail (minimum_size != NULL || natural_size != NULL);
 
-  gtk_cell_view_get_desired_size_of_row (cell_view, path, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
+  tmp = cell_view->priv->displayed_row;
+  cell_view->priv->displayed_row =
+    gtk_tree_row_reference_new (cell_view->priv->model, path);
+
+  gtk_cell_view_get_width (GTK_SIZE_REQUEST (cell_view), minimum_size, natural_size);
+
+  gtk_tree_row_reference_free (cell_view->priv->displayed_row);
+  cell_view->priv->displayed_row = tmp;
+
+  /* Restore active size (this will restore the cellrenderer info->width/requested_width's) */
+  gtk_cell_view_get_width (GTK_SIZE_REQUEST (cell_view), NULL, NULL);
 }
 
 
 /**
- * gtk_cell_view_get_desired_height_of_row:
+ * gtk_cell_view_get_desired_height_for_width_of_row:
  * @cell_view: a #GtkCellView
  * @path: a #GtkTreePath 
- * @minimum_size: location to store the minimum size 
- * @natural_size: location to store the natural size 
+ * @avail_size: available width
+ * @minimum_size: location to store the minimum height 
+ * @natural_size: location to store the natural height
  *
  * Sets @minimum_size and @natural_size to the height desired by @cell_view 
- * to display the model row pointed to by @path.
+ * if it were allocated a width of @avail_size to display the model row 
+ * pointed to by @path.
  * 
  * Since: 3.0
  */
 void
-gtk_cell_view_get_desired_height_of_row (GtkCellView     *cell_view,
-					 GtkTreePath     *path,
-					 gint            *minimum_size,
-					 gint            *natural_size)
+gtk_cell_view_get_desired_height_for_width_of_row (GtkCellView     *cell_view,
+						   GtkTreePath     *path,
+						   gint             avail_size,
+						   gint            *minimum_size,
+						   gint            *natural_size)
 {
+  GtkTreeRowReference *tmp;
+
   g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
   g_return_if_fail (path != NULL);
   g_return_if_fail (minimum_size != NULL || natural_size != NULL);
 
-  gtk_cell_view_get_desired_size_of_row (cell_view, path, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
+  tmp = cell_view->priv->displayed_row;
+  cell_view->priv->displayed_row =
+    gtk_tree_row_reference_new (cell_view->priv->model, path);
+
+  /* Then get the collective height_for_width based on the cached values */
+  gtk_cell_view_get_height_for_width (GTK_SIZE_REQUEST (cell_view), avail_size, minimum_size, natural_size);
+
+  gtk_tree_row_reference_free (cell_view->priv->displayed_row);
+  cell_view->priv->displayed_row = tmp;
+
+  /* Restore active size (this will restore the cellrenderer info->width/requested_width's) */
+  gtk_cell_view_get_width (GTK_SIZE_REQUEST (cell_view), NULL, NULL);
 }
 
 /**
@@ -1174,17 +1191,18 @@ gtk_cell_view_buildable_custom_tag_end (GtkBuildable *buildable,
 static void
 gtk_cell_view_size_request_init (GtkSizeRequestIface *iface)
 {
-  iface->get_width  = gtk_cell_view_get_width;
-  iface->get_height = gtk_cell_view_get_height;
+  iface->get_width            = gtk_cell_view_get_width;
+  iface->get_height           = gtk_cell_view_get_height;
+  iface->get_width_for_height = gtk_cell_view_get_width_for_height;
+  iface->get_height_for_width = gtk_cell_view_get_height_for_width;
 }
 
 static void
-gtk_cell_view_get_size (GtkSizeRequest *widget,
-			GtkOrientation  orientation,
-			gint           *minimum_size,
-			gint           *natural_size)
+gtk_cell_view_get_width  (GtkSizeRequest      *widget,
+			  gint                *minimum_size,
+			  gint                *natural_size)
 {
-  GList *i;
+  GList *list;
   gint cell_min, cell_nat;
   gboolean first_cell = TRUE;
   GtkCellView *cellview = GTK_CELL_VIEW (widget);
@@ -1195,37 +1213,28 @@ gtk_cell_view_get_size (GtkSizeRequest *widget,
   if (cellview->priv->displayed_row)
     gtk_cell_view_set_cell_data (cellview);
 
-  for (i = cellview->priv->cell_list; i; i = i->next)
+  for (list = cellview->priv->cell_list; list; list = list->next)
     {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
 
       if (gtk_cell_renderer_get_visible (info->cell))
         {
 	  
-	  if (!first_cell && orientation == GTK_ORIENTATION_HORIZONTAL)
+	  if (!first_cell)
 	    {
 	      minimum += cellview->priv->spacing;
 	      natural += cellview->priv->spacing;
 	    }
 
-	  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-	    {
-	      gtk_cell_size_request_get_width (GTK_CELL_SIZE_REQUEST (info->cell),
-					       GTK_WIDGET (cellview), &cell_min, &cell_nat);
-
-	      info->requested_width = cell_min;
-	      info->natural_width   = cell_nat;
+	  gtk_cell_size_request_get_width (GTK_CELL_SIZE_REQUEST (info->cell),
+					   GTK_WIDGET (cellview), &cell_min, &cell_nat);
+	  
+	  info->requested_width = cell_min;
+	  info->natural_width   = cell_nat;
+	  
+	  minimum += info->requested_width;
+	  natural += info->natural_width;
 
-	      minimum += info->requested_width;
-	      natural += info->natural_width;
-	    }
-	  else
-	    {
-	      gtk_cell_size_request_get_height (GTK_CELL_SIZE_REQUEST (info->cell),
-						GTK_WIDGET (cellview), &cell_min, &cell_nat);
-	      minimum = MAX (minimum, cell_min);
-	      natural = MAX (natural, cell_nat);
-	    }
 	  first_cell = FALSE;
         }
     }
@@ -1237,18 +1246,134 @@ gtk_cell_view_get_size (GtkSizeRequest *widget,
     *natural_size = natural;
 }
 
-static void
-gtk_cell_view_get_width  (GtkSizeRequest      *widget,
+static void       
+gtk_cell_view_get_height (GtkSizeRequest      *widget,
 			  gint                *minimum_size,
 			  gint                *natural_size)
 {
-  gtk_cell_view_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
+  gint minimum_width;
+
+  /* CellViews only need to respond to height-for-width mode (cellview is pretty much
+   * an implementation detail of GtkComboBox) */
+  gtk_cell_view_get_width (widget, &minimum_width, NULL);
+  gtk_cell_view_get_height_for_width (widget, minimum_width, minimum_size, natural_size);
 }
 
 static void       
-gtk_cell_view_get_height (GtkSizeRequest      *widget,
-			  gint                *minimum_size,
-			  gint                *natural_size)
+gtk_cell_view_get_width_for_height (GtkSizeRequest      *widget,
+				    gint                 for_size,
+				    gint                *minimum_size,
+				    gint                *natural_size)
+{
+  /* CellViews only need to respond to height-for-width mode (cellview is pretty much
+   * an implementation detail of GtkComboBox) */
+  gtk_cell_view_get_width (widget, minimum_size, natural_size);
+}
+
+static void       
+gtk_cell_view_get_height_for_width (GtkSizeRequest      *widget,
+				    gint                 for_size,
+				    gint                *minimum_size,
+				    gint                *natural_size)
 {
-  gtk_cell_view_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
+  GtkCellView      *cellview = GTK_CELL_VIEW (widget);
+  GList            *list;
+  GtkRequestedSize *sizes;
+  GArray           *array;
+  gint              minimum, natural, avail_size;
+  gboolean          first_cell = TRUE;
+  gint              n_expand_cells = 0;
+  gint              extra_per_cell, extra_extra, i;
+
+  minimum = natural = 0;
+  avail_size = for_size;
+
+  array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
+
+  if (cellview->priv->displayed_row)
+    gtk_cell_view_set_cell_data (cellview);
+
+  /* First allocate the right width to all cells */
+  for (list = cellview->priv->cell_list; list; list = list->next)
+    {
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
+
+      if (gtk_cell_renderer_get_visible (info->cell))
+        {
+	  GtkRequestedSize requested;
+
+	  gtk_cell_size_request_get_width (GTK_CELL_SIZE_REQUEST (info->cell),
+					   GTK_WIDGET (cellview), 
+					   &requested.minimum_size, 
+					   &requested.natural_size);
+
+	  requested.data = info;
+	  g_array_append_val (array, requested);
+
+	  avail_size -= requested.minimum_size;
+
+	  if (!first_cell)
+	    avail_size -= cellview->priv->spacing;
+
+	  first_cell = FALSE;
+
+	  if (info->expand)
+	    n_expand_cells++;
+        }
+    }
+
+  sizes      = (GtkRequestedSize *)array->data;
+  avail_size = gtk_distribute_natural_allocation (MAX (0, avail_size), array->len, sizes);
+
+  /* Deal with any expand space... */
+  if (n_expand_cells > 0)
+    {
+      extra_per_cell = avail_size / n_expand_cells;
+      extra_extra    = avail_size % n_expand_cells;
+    }
+  else
+    /* Everything just left-aligned if no cells expand */
+    extra_per_cell = extra_extra = 0;
+
+  /* Now get the height for the real width of each cell */
+  for (i = 0, list = cellview->priv->cell_list; list; list = list->next)
+    {
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
+      gint cell_minimum, cell_natural;
+
+      if (gtk_cell_renderer_get_visible (info->cell))
+        {
+	  gint cell_width = sizes[i].minimum_size;
+
+	  g_assert (sizes[i].data == info);
+
+	  if (info->expand)
+	    {
+	      cell_width += extra_per_cell;
+	      if (extra_extra)
+		{
+		  cell_width++;
+		  extra_extra--;
+		}
+	    }
+
+	  /* Get the height for the real width of this cell */
+	  gtk_cell_size_request_get_height_for_width (GTK_CELL_SIZE_REQUEST (info->cell),
+						      GTK_WIDGET (widget),
+						      cell_width, &cell_minimum, &cell_natural);
+
+	  minimum = MAX (minimum, cell_minimum);
+	  natural = MAX (natural, cell_natural);
+
+	  /* increment sizes[] index for visible cells */
+	  i++;
+        }
+    }
+
+  g_array_free (array, TRUE);
+
+  if (minimum_size)
+    *minimum_size = minimum;
+  if (natural_size)
+    *natural_size = natural;
 }
diff --git a/gtk/gtkcellview.h b/gtk/gtkcellview.h
index 84dc4e5..cb6b269 100644
--- a/gtk/gtkcellview.h
+++ b/gtk/gtkcellview.h
@@ -76,10 +76,11 @@ void              gtk_cell_view_get_desired_width_of_row(GtkCellView     *cell_v
                                                          GtkTreePath     *path,
                                                          gint            *minimum_size,
                                                          gint            *natural_size);
-void              gtk_cell_view_get_desired_height_of_row(GtkCellView     *cell_view,
-							  GtkTreePath     *path,
-							  gint            *minimum_size,
-							  gint            *natural_size);
+void              gtk_cell_view_get_desired_height_for_width_of_row(GtkCellView     *cell_view,
+								    GtkTreePath     *path,
+								    gint             avail_size,
+								    gint            *minimum_size,
+								    gint            *natural_size);
 
 void              gtk_cell_view_set_background_color    (GtkCellView     *cell_view,
                                                          const GdkColor  *color);
diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
index 1034265..7ebcfb9 100644
--- a/gtk/gtkcombobox.c
+++ b/gtk/gtkcombobox.c
@@ -108,8 +108,8 @@ struct _GtkComboBoxPrivate
   guint scroll_timer;
   guint resize_idle_id;
 
-  GtkRequisition minimum_size;
-  GtkRequisition natural_size;
+  gint  minimum_width;
+  gint  natural_width;
 
   GSList *cells;
 
@@ -474,6 +474,14 @@ static void     gtk_combo_box_get_width                      (GtkSizeRequest
 static void     gtk_combo_box_get_height                     (GtkSizeRequest      *widget,
 							      gint                *minimum_size,
 							      gint                *natural_size);
+static void     gtk_combo_box_get_width_for_height           (GtkSizeRequest        *widget,
+							      gint                   avail_size,
+							      gint                  *minimum_size,
+							      gint                  *natural_size);
+static void     gtk_combo_box_get_height_for_width           (GtkSizeRequest        *widget,
+							      gint                   avail_size,
+							      gint                  *minimum_size,
+							      gint                  *natural_size);
 
 
 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
@@ -947,8 +955,8 @@ gtk_combo_box_init (GtkComboBox *combo_box)
   _gtk_bin_set_child (GTK_BIN (combo_box), priv->cell_view);
   gtk_widget_show (priv->cell_view);
 
-  memset (&priv->minimum_size, 0x0, sizeof (GtkRequisition));
-  memset (&priv->natural_size, 0x0, sizeof (GtkRequisition));
+  priv->minimum_width = 0;
+  priv->natural_width = 0;
 
   priv->wrap_width = 0;
 
@@ -3591,14 +3599,14 @@ gtk_combo_box_menu_row_changed (GtkTreeModel *model,
 
   width = gtk_combo_box_calc_requested_width (combo_box, path);
 
-  if (width > priv->minimum_size.width)
+  if (width > priv->minimum_width)
     {
       if (priv->cell_view)
 	{
 	  gtk_widget_set_size_request (priv->cell_view, width, -1);
 	  gtk_widget_queue_resize (priv->cell_view);
 	}
-      priv->minimum_size.width = width;
+      priv->minimum_width = width;
     }
 }
 
@@ -4108,14 +4116,14 @@ gtk_combo_box_list_row_changed (GtkTreeModel *model,
 
   width = gtk_combo_box_calc_requested_width (combo_box, path);
 
-  if (width > priv->minimum_size.width)
+  if (width > priv->minimum_width)
     {
       if (priv->cell_view) 
 	{
 	  gtk_widget_set_size_request (priv->cell_view, width, -1);
 	  gtk_widget_queue_resize (priv->cell_view);
 	}
-      priv->minimum_size.width = width;
+      priv->minimum_width = width;
     }
 }
 
@@ -5923,8 +5931,10 @@ gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
 static void
 gtk_combo_box_size_request_init (GtkSizeRequestIface *iface)
 {
-  iface->get_width = gtk_combo_box_get_width;
-  iface->get_height = gtk_combo_box_get_height;
+  iface->get_width            = gtk_combo_box_get_width;
+  iface->get_height           = gtk_combo_box_get_height;
+  iface->get_height_for_width = gtk_combo_box_get_height_for_width;
+  iface->get_width_for_height = gtk_combo_box_get_width_for_height;
 }
 
 static void
@@ -5938,34 +5948,20 @@ gtk_combo_box_remeasure (GtkComboBox *combo_box)
       !gtk_tree_model_get_iter_first (priv->model, &iter))
     return;
 
-  memset (&priv->minimum_size, 0x0, sizeof (GtkRequisition));
-  memset (&priv->natural_size, 0x0, sizeof (GtkRequisition));
+  priv->minimum_width = priv->natural_width = 0;
 
   path = gtk_tree_path_new_from_indices (0, -1);
 
   do
     {
-      GtkRequisition req, nat_req;
+      gint row_min = 0, row_nat = 0;
 
       if (priv->cell_view)
-	{
-	  /* XXX FIXME: Currently still not doing height-for-width in cell renderers here */
-	  gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view), 
-						  path, &req.width, &nat_req.width);
-	  gtk_cell_view_get_desired_height_of_row (GTK_CELL_VIEW (priv->cell_view), 
-						   path, &req.height, &nat_req.height);
-	}
-      else
-        {
-	  memset (&req, 0x0, sizeof (GtkRequisition));
-	  memset (&nat_req, 0x0, sizeof (GtkRequisition));
-        }
-
-      priv->minimum_size.width  = MAX (priv->minimum_size.width,  req.width);
-      priv->minimum_size.height = MAX (priv->minimum_size.height, req.height);
+	gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view), 
+						path, &row_min, &row_nat);
 
-      priv->natural_size.width  = MAX (priv->natural_size.width,  nat_req.width);
-      priv->natural_size.height = MAX (priv->natural_size.height, nat_req.height);
+      priv->minimum_width = MAX (priv->minimum_width, row_min);
+      priv->natural_width = MAX (priv->natural_width, row_nat);
 
       gtk_tree_path_next (path);
     }
@@ -5975,35 +5971,80 @@ gtk_combo_box_remeasure (GtkComboBox *combo_box)
 }
 
 
-/* XXX TODO: Split this up into 2 orientations so as
- * to properly support height-for-width/width-for-height here
- *
- */
 static void
-gtk_combo_box_get_size (GtkSizeRequest *widget,
-			GtkRequisition *minimum_size,
-			GtkRequisition *natural_size)
+gtk_combo_box_measure_height_for_width (GtkComboBox *combo_box,
+					gint         avail_width, 
+					gint        *min_height, 
+					gint        *nat_height)
+{
+  GtkWidget             *child;
+  GtkComboBoxPrivate    *priv = combo_box->priv;
+  GtkTreeIter            iter;
+  GtkTreePath           *path;
+  gint                   child_min, child_nat;
+
+  child = gtk_bin_get_child (GTK_BIN (combo_box));
+
+  gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child), avail_width,
+					 &child_min, &child_nat);
+
+  if (!priv->model ||
+      !gtk_tree_model_get_iter_first (priv->model, &iter))
+    goto out;
+
+  path = gtk_tree_path_new_from_indices (0, -1);
+
+  do
+    {
+      gint row_min = 0, row_nat = 0;
+
+      if (priv->cell_view)
+	gtk_cell_view_get_desired_height_for_width_of_row (GTK_CELL_VIEW (priv->cell_view), 
+							   path, avail_width, 
+							   &row_min, &row_nat);
+
+      child_min = MAX (child_min, row_min);
+      child_nat = MAX (child_nat, row_nat);
+
+      gtk_tree_path_next (path);
+    }
+  while (gtk_tree_model_iter_next (priv->model, &iter));
+
+  gtk_tree_path_free (path);
+
+ out:
+
+  if (min_height)
+    *min_height = child_min;
+  if (nat_height)
+    *nat_height = child_nat;
+}
+
+
+static void     
+gtk_combo_box_get_width (GtkSizeRequest      *widget,
+			 gint                *minimum_size,
+			 gint                *natural_size)
 {
   GtkComboBox           *combo_box = GTK_COMBO_BOX (widget);
   GtkComboBoxPrivate    *priv = combo_box->priv;
   gint                   focus_width, focus_pad;
   gint                   font_size, arrow_size;
-  GtkRequisition         bin_req, bin_nat_req;
   PangoContext          *context;
   PangoFontMetrics      *metrics;
   PangoFontDescription  *font_desc;
   GtkWidget             *child;
+  gint                   minimum_width, natural_width;
+  gint                   child_min, child_nat;
 
   child = gtk_bin_get_child (GTK_BIN (widget));
  
   /* common */
-  gtk_size_request_get_size (GTK_SIZE_REQUEST (child), &bin_req, &bin_nat_req);
+  gtk_size_request_get_width (GTK_SIZE_REQUEST (child), &child_min, &child_nat);
   gtk_combo_box_remeasure (combo_box);
 
-  bin_req.width      = MAX (bin_req.width,      priv->minimum_size.width);
-  bin_req.height     = MAX (bin_req.height,     priv->minimum_size.height);
-  bin_nat_req.width  = MAX (bin_nat_req.width,  priv->natural_size.width);
-  bin_nat_req.height = MAX (bin_nat_req.height, priv->natural_size.height);
+  child_min  = MAX (child_min,  priv->minimum_width);
+  child_nat  = MAX (child_nat,  priv->natural_width);
 
   gtk_widget_style_get (GTK_WIDGET (widget),
 			"focus-line-width", &focus_width,
@@ -6029,109 +6070,74 @@ gtk_combo_box_get_size (GtkSizeRequest *widget,
 	  
       if (priv->cell_view)
         {
-          GtkRequisition button_req, sep_req, arrow_req;
-          gint border_width, xthickness, ythickness, xpad, ypad;
+          gint sep_width, arrow_width;
+          gint border_width, xthickness, xpad;
 
-          gtk_widget_size_request (priv->button, &button_req);
 	  border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
-          xthickness = priv->button->style->xthickness;
-          ythickness = priv->button->style->ythickness;
-
-	  xpad = 2*(border_width + xthickness + focus_width + focus_pad);
-	  ypad = 2*(border_width + ythickness + focus_width + focus_pad);
-
-          gtk_widget_size_request (priv->separator, &sep_req);
-          gtk_widget_size_request (priv->arrow, &arrow_req);
+          xthickness   = priv->button->style->xthickness;
 
-          minimum_size->width  = bin_req.width + sep_req.width + arrow_req.width;
-          minimum_size->height = MAX (sep_req.height, arrow_req.height);
-          minimum_size->height = MAX (minimum_size->height, bin_req.height);
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->separator), &sep_width, NULL);
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->arrow), &arrow_width, NULL);
 
-          natural_size->width  = bin_nat_req.width + sep_req.width + arrow_req.width;
-          natural_size->height = MAX (minimum_size->height, bin_nat_req.height);
+	  xpad = 2*(border_width + xthickness + focus_width + focus_pad);
 
-          minimum_size->width  += xpad;
-          minimum_size->height += ypad;
-          natural_size->width  += xpad;
-          natural_size->height += ypad;
+          minimum_width  = child_min + sep_width + arrow_width + xpad;
+          natural_width  = child_nat + sep_width + arrow_width + xpad;
         }
       else
         {
-          GtkRequisition but_req, but_nat_req;
-
-          gtk_size_request_get_size (GTK_SIZE_REQUEST (priv->button), 
-				     &but_req, &but_nat_req);
+          gint but_width, but_nat_width;
 
-          minimum_size->width  = bin_req.width + but_req.width;
-          minimum_size->height = MAX (bin_req.height, but_req.height);
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), 
+				      &but_width, &but_nat_width);
 
-          natural_size->width  = bin_nat_req.width + but_nat_req.width;
-          natural_size->height = MAX (bin_nat_req.height, but_nat_req.height);
+          minimum_width  = child_min + but_width;
+          natural_width  = child_nat + but_nat_width;
         }
     }
   else
     {
       /* list mode */
-      GtkRequisition button_req, button_nat_req, frame_req;
+      gint button_width, button_nat_width;
 
       /* sample + frame */
-      *minimum_size = bin_req;
-      *natural_size = bin_nat_req;
+      minimum_width = child_min;
+      natural_width = child_nat;
 
-      minimum_size->width += 2 * focus_width;
-      natural_size->width += 2 * focus_width;
+      minimum_width += 2 * focus_width;
+      natural_width += 2 * focus_width;
       
       if (priv->cell_view_frame)
         {
-	  gtk_widget_size_request (priv->cell_view_frame, &frame_req);
 	  if (priv->has_frame)
 	    {
 	      gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
-	      gint xpad = 2 * (border_width + GTK_WIDGET (priv->cell_view_frame)->style->xthickness);
-	      gint ypad = 2 * (border_width + GTK_WIDGET (priv->cell_view_frame)->style->ythickness);
+	      gint xpad         = 2 * (border_width + GTK_WIDGET (priv->cell_view_frame)->style->xthickness);
 
-	      minimum_size->width  += xpad;
-	      minimum_size->height += ypad;
-	      natural_size->width  += xpad;
-	      natural_size->height += ypad;
+	      minimum_width  += xpad;
+	      natural_width  += xpad;
 	    }
         }
 
       /* the button */
-      gtk_size_request_get_size (GTK_SIZE_REQUEST (priv->button), 
-				 &button_req, &button_nat_req);
+      gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), 
+				  &button_width, &button_nat_width);
 
-      minimum_size->width += button_req.width;
-      minimum_size->height = MAX (minimum_size->height, button_req.height);
-
-      natural_size->width += button_nat_req.width;
-      natural_size->height = MAX (natural_size->height, button_nat_req.height);
+      minimum_width += button_width;
+      natural_width += button_nat_width;
     }
 
   if (GTK_SHADOW_NONE != priv->shadow_type)
     {
-      minimum_size->width  += 2 * GTK_WIDGET (widget)->style->xthickness;
-      minimum_size->height += 2 * GTK_WIDGET (widget)->style->ythickness;
-
-      natural_size->width  += 2 * GTK_WIDGET (widget)->style->xthickness;
-      natural_size->height += 2 * GTK_WIDGET (widget)->style->ythickness;
+      minimum_width  += 2 * GTK_WIDGET (widget)->style->xthickness;
+      natural_width  += 2 * GTK_WIDGET (widget)->style->xthickness;
     }
-}
-
-static void     
-gtk_combo_box_get_width (GtkSizeRequest      *widget,
-			 gint                *minimum_size,
-			 gint                *natural_size)
-{
-  GtkRequisition minimum, natural;
-
-  gtk_combo_box_get_size (widget, &minimum, &natural);
 
   if (minimum_size)
-    *minimum_size = minimum.width;
+    *minimum_size = minimum_width;
 
   if (natural_size)
-    *natural_size = natural.width;
+    *natural_size = natural_width;
 }
 
 static void
@@ -6139,14 +6145,139 @@ gtk_combo_box_get_height (GtkSizeRequest      *widget,
 			  gint                *minimum_size,
 			  gint                *natural_size)
 { 
-  GtkRequisition minimum, natural;
+  gint min_width;
+
+  /* Combo box is height-for-width only 
+   * (so we always just reserve enough height for the minimum width) */
+  gtk_size_request_get_width (widget, &min_width, NULL);
+  gtk_size_request_get_height_for_width (widget, min_width, minimum_size, natural_size);
+}
+
+static void
+gtk_combo_box_get_width_for_height (GtkSizeRequest        *widget,
+				    gint                   avail_size,
+				    gint                  *minimum_size,
+				    gint                  *natural_size)
+{
+  /* Combo box is height-for-width only 
+   * (so we assume we always reserved enough height for the minimum width) */
+  gtk_size_request_get_width (widget, minimum_size, natural_size);
+}
+
+
+static void
+gtk_combo_box_get_height_for_width (GtkSizeRequest        *widget,
+				    gint                   avail_size,
+				    gint                  *minimum_size,
+				    gint                  *natural_size)
+{
+  GtkComboBox           *combo_box = GTK_COMBO_BOX (widget);
+  GtkComboBoxPrivate    *priv = combo_box->priv;
+  gint                   focus_width, focus_pad;
+  gint                   min_height, nat_height;
+  gint                   size;
+
+  gtk_widget_style_get (GTK_WIDGET (widget),
+			"focus-line-width", &focus_width,
+			"focus-padding", &focus_pad,
+			NULL);
+
+  size = avail_size;
 
-  gtk_combo_box_get_size (widget, &minimum, &natural);
+  if (GTK_SHADOW_NONE != priv->shadow_type)
+    size -= GTK_WIDGET (widget)->style->xthickness;
+
+  if (!priv->tree_view)
+    {
+      /* menu mode */
+      if (priv->cell_view)
+        {
+	  /* calculate x/y padding and separator/arrow size */
+          gint sep_width, arrow_width, sep_height, arrow_height;
+          gint border_width, xthickness, ythickness, xpad, ypad;
+
+	  border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
+          xthickness = priv->button->style->xthickness;
+          ythickness = priv->button->style->ythickness;
+
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->separator), &sep_width, NULL);
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->arrow), &arrow_width, NULL);
+          gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->separator), 
+						 sep_width, &sep_height, NULL);
+          gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->arrow), 
+						 arrow_width, &arrow_height, NULL);
+
+	  xpad = 2*(border_width + xthickness + focus_width + focus_pad);
+	  ypad = 2*(border_width + ythickness + focus_width + focus_pad);
+
+	  size -= sep_width + arrow_width + xpad;
+
+	  gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
+
+	  arrow_height = MAX (arrow_height, sep_height);
+	  min_height = MAX (min_height, arrow_height);
+	  nat_height = MAX (nat_height, arrow_height);
+
+	  min_height += ypad;
+	  nat_height += ypad;
+        }
+      else
+        {
+	  /* there is a custom child widget inside (no priv->cell_view) */
+          gint but_width, but_height;
+
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), &but_width, NULL);
+          gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->button), 
+						 but_width, &but_height, NULL);
+
+	  size -= but_width;
+
+	  gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
+	  
+	  min_height = MAX (min_height, but_height);
+	  nat_height = MAX (nat_height, but_height);
+        }
+    }
+  else
+    {
+      /* list mode */
+      gint but_width, but_height;
+      gint xpad = 0, ypad = 0;
+
+      gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), &but_width, NULL);
+      gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->button), 
+					     but_width, &but_height, NULL);
+      
+      if (priv->cell_view_frame && priv->has_frame)
+	{
+	  gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
+
+	  xpad = 2 * (border_width + GTK_WIDGET (priv->cell_view_frame)->style->xthickness);
+	  ypad = 2 * (border_width + GTK_WIDGET (priv->cell_view_frame)->style->ythickness);
+	}
+
+      size -= but_width;
+      size -= 2 * focus_width;
+      size -= xpad;
+
+      gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
+	  
+      min_height = MAX (min_height, but_height);
+      nat_height = MAX (nat_height, but_height);
+
+      min_height += ypad;
+      nat_height += ypad;
+    }
+
+  if (GTK_SHADOW_NONE != priv->shadow_type)
+    {
+      min_height += 2 * GTK_WIDGET (widget)->style->ythickness;
+      nat_height += 2 * GTK_WIDGET (widget)->style->ythickness;
+    }
 
   if (minimum_size)
-    *minimum_size = minimum.height;
+    *minimum_size = min_height;
 
   if (natural_size)
-    *natural_size = natural.height;
+    *natural_size = nat_height;
 }
-



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