[gtk+/native-layout] Fixing height-for-width wrapping of GtkLabel



commit 35e2dfce08b00577590c0bdc0d67366689449cc1
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Fri Apr 9 21:47:25 2010 -0400

    Fixing height-for-width wrapping of GtkLabel
    
    Merged in fixes from the old branch in a patch prepared by Matthias Clasen,
    added some fixes of my own to make sure that label wrapping follows allocation
    and not requisition at show time (allocate time).

 gtk/gtklabel.c |  292 ++++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 202 insertions(+), 90 deletions(-)
---
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index c775a66..90904b4 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -3104,15 +3104,19 @@ gtk_label_ensure_layout (GtkLabel *label)
                                 widget->allocation.width * PANGO_SCALE);
       else if (label->wrap)
 	{
-	  GtkWidgetAuxInfo *aux_info;
+	  GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
 	  gint longest_paragraph;
 	  gint width, height;
 
-	  pango_layout_set_wrap (label->layout, label->wrap_mode);
-	  
-	  aux_info = _gtk_widget_get_aux_info (widget, FALSE);
-	  if (aux_info && aux_info->width > 0)
+ 	  if (aux_info && aux_info->width > 0)
 	    pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
+	  else if (widget->allocation.width > 1)
+ 	    {
+	      width = widget->allocation.width - label->misc.xpad * 2;
+
+	      pango_layout_set_wrap (label->layout, label->wrap_mode);
+	      pango_layout_set_width (label->layout, MAX (width, 1) * PANGO_SCALE);
+ 	    }
 	  else
 	    {
 	      GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (label));
@@ -3122,7 +3126,6 @@ gtk_label_ensure_layout (GtkLabel *label)
 	      pango_layout_get_extents (label->layout, NULL, &logical_rect);
 
 	      width = logical_rect.width;
-	      
 	      /* Try to guess a reasonable maximum width */
 	      longest_paragraph = width;
 
@@ -3203,6 +3206,8 @@ gtk_label_get_desired_size (GtkExtendedLayout *layout,
   GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (layout);
   GtkLabel *label = GTK_LABEL (layout);
   PangoRectangle required_rect;
+  GtkWidgetAuxInfo *aux_info;
+  PangoLayout *natural_layout; 
 
   /*  
    * If word wrapping is on, then the height requisition can depend
@@ -3222,89 +3227,99 @@ gtk_label_get_desired_size (GtkExtendedLayout *layout,
 
   gtk_label_ensure_layout (label);
 
-  if (minimum_size)
-    {
-      GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
-
-      pango_layout_get_extents (label->layout, NULL, &required_rect);
-      required_rect.x = required_rect.y = 0;
-
-      if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
-        {
-          /* backup the Pango layout, as get_label_char_width() scrambles it */
-
-          PangoLayout *backup = label->layout;
-          label->layout = pango_layout_copy (label->layout);
-
-          required_rect.width = get_label_char_width (label);
-
-          g_object_unref (label->layout);
-          label->layout = backup;
-        }
-
-      if (label->single_line_mode)
-        required_rect.height = get_single_line_height (GTK_WIDGET (label), label->layout);
-
-      if (label->have_transform)
-        {
-          PangoContext *context = pango_layout_get_context (label->layout);
-          const PangoMatrix *matrix = pango_context_get_matrix (context);
-          pango_matrix_transform_rectangle (matrix, &required_rect);
-        }
-
-      required_rect.width = PANGO_PIXELS_CEIL (required_rect.width);
-      required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
+  aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
 
-      if ((label->wrap || label->ellipsize ||
-           priv->width_chars > 0 || priv->max_width_chars > 0) &&
-          aux_info && aux_info->width > 0)
-        required_rect.width = aux_info->width;
-
-      minimum_size->width = required_rect.width + label->misc.xpad * 2;
-      minimum_size->height = required_rect.height + label->misc.ypad * 2;
+  pango_layout_get_extents (label->layout, NULL, &required_rect);
+  required_rect.x = required_rect.y = 0;
+  
+  if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
+    {
+      /* backup the Pango layout, as get_label_char_width() scrambles it */
+      
+      PangoLayout *backup = label->layout;
+      label->layout = pango_layout_copy (label->layout);
+      
+      required_rect.width = get_label_char_width (label);
+      
+      g_object_unref (label->layout);
+      label->layout = backup;
     }
-
-  if (natural_size)
+  
+  if (label->single_line_mode)
+        required_rect.height = get_single_line_height (GTK_WIDGET (label), label->layout);
+  
+  if (label->have_transform)
     {
-      PangoLayout *natural_layout = pango_layout_copy (label->layout);
+      PangoContext *context = pango_layout_get_context (label->layout);
+      const PangoMatrix *matrix = pango_context_get_matrix (context);
+      pango_matrix_transform_rectangle (matrix, &required_rect);
+    }
+  
+  required_rect.width = PANGO_PIXELS_CEIL (required_rect.width);
+  required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
+  
+  if ((label->wrap || label->ellipsize ||
+       priv->width_chars > 0 || priv->max_width_chars > 0) &&
+      aux_info && aux_info->width > 0)
+    required_rect.width = aux_info->width;
 
-      pango_layout_set_width (natural_layout, -1);
-      pango_layout_set_ellipsize (natural_layout, PANGO_ELLIPSIZE_NONE);
+  minimum_size->width = required_rect.width + label->misc.xpad * 2;
+  minimum_size->height = required_rect.height + label->misc.ypad * 2;
 
-      pango_layout_get_extents (natural_layout, NULL, &required_rect);
-      required_rect.x = required_rect.y = 0;
+  /* Natural size */
+  natural_layout = pango_layout_copy (label->layout);
+  pango_layout_set_width (natural_layout, -1);
+  pango_layout_set_ellipsize (natural_layout, PANGO_ELLIPSIZE_NONE);
 
-      if (label->single_line_mode)
-        required_rect.height = get_single_line_height (GTK_WIDGET (label), label->layout);
+  pango_layout_get_extents (natural_layout, NULL, &required_rect);
+  required_rect.x = required_rect.y = 0;
 
-      if (label->have_transform)
-        {
-          PangoContext *context = pango_layout_get_context (natural_layout);
-          const PangoMatrix *matrix = pango_context_get_matrix (context);
-          pango_matrix_transform_rectangle (matrix, &required_rect);
-        }
+  if (label->single_line_mode)
+    required_rect.height = get_single_line_height (GTK_WIDGET (label), label->layout);
 
-      required_rect.width = PANGO_PIXELS_CEIL (required_rect.width);
-      required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
+  if (label->have_transform)
+    {
+      PangoContext *context = pango_layout_get_context (natural_layout);
+      const PangoMatrix *matrix = pango_context_get_matrix (context);
+      pango_matrix_transform_rectangle (matrix, &required_rect);
+    }
 
-      natural_size->width = required_rect.width + label->misc.xpad * 2;
-      natural_size->height = required_rect.height + label->misc.ypad * 2;
+  required_rect.width = PANGO_PIXELS_CEIL (required_rect.width);
+  required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
 
-      g_object_unref (natural_layout);
-    }
+  natural_size->width = required_rect.width + label->misc.xpad * 2;
+  natural_size->height = required_rect.height + label->misc.ypad * 2;
+  
+  g_object_unref (natural_layout);
 }
 
 static void
-get_size_for_allocation (GtkLabel *label,
-                         gint      allocation,
-                         gint     *minimum_size,
-                         gint     *natural_size)
+get_size_for_allocation (GtkLabel        *label,
+			 GtkOrientation   orientation,
+                         gint             allocation,
+                         gint            *minimum_size,
+                         gint            *natural_size)
 {
   PangoLayout *layout;
+  GtkWidgetAuxInfo *aux_info = 
+    _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
+  gint aux_size;
+
+  if (label->wrap)
+    gtk_label_clear_layout (label);
 
   gtk_label_ensure_layout (label);
   layout = pango_layout_copy (label->layout);
-  pango_layout_set_width (layout, PANGO_SCALE * allocation);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    aux_size = aux_info->width;
+  else
+    aux_size = aux_info->height;
+
+  if (aux_size > 0)
+    pango_layout_set_width (layout, aux_size * PANGO_SCALE);
+  else
+    pango_layout_set_width (layout, allocation * PANGO_SCALE);
 
   if (minimum_size)
     pango_layout_get_pixel_size (layout, NULL, minimum_size);
@@ -3328,7 +3343,7 @@ gtk_label_get_width_for_height (GtkExtendedLayout *layout,
   gdouble angle = gtk_label_get_angle (label);
 
   if (90 == angle || 270 == angle)
-    get_size_for_allocation (label, height, minimum_width, natural_width);
+    get_size_for_allocation (label, GTK_ORIENTATION_VERTICAL, height, minimum_width, natural_width);
   else
     {
       GtkRequisition minimum_size, natural_size;
@@ -3354,7 +3369,7 @@ gtk_label_get_height_for_width (GtkExtendedLayout *layout,
   gdouble angle = gtk_label_get_angle (label);
 
   if (0 == angle || 180 == angle)
-    get_size_for_allocation (label, width, minimum_height, natural_height);
+    get_size_for_allocation (label, GTK_ORIENTATION_HORIZONTAL, width, minimum_height, natural_height);
   else
     {
       GtkRequisition minimum_size, natural_size;
@@ -3380,21 +3395,86 @@ gtk_label_size_allocate (GtkWidget     *widget,
 
   GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
 
-  if (label->ellipsize)
+  /* The layout may have been recently cleared in get_size_for_allocation(), but the 
+   * width at that point may not be the same as the allocated width
+   */
+  if (label->wrap)
+    gtk_label_clear_layout (label);
+
+  gtk_label_ensure_layout (label);
+
+  if (label->ellipsize || GTK_LABEL_GET_PRIVATE (label)->full_size)
     {
       if (label->layout)
 	{
-	  gint width;
-	  PangoRectangle logical;
+          PangoRectangle logical;
+          PangoRectangle bounds;
 
-	  width = (allocation->width - label->misc.xpad * 2) * PANGO_SCALE;
+          bounds.x = bounds.y = 0;
+          bounds.width = allocation->width - label->misc.xpad * 2;
+          bounds.height = allocation->height - label->misc.ypad * 2;
 
-	  pango_layout_set_width (label->layout, -1);
-	  pango_layout_get_extents (label->layout, NULL, &logical);
+          pango_layout_set_width (label->layout, -1);
+          pango_layout_get_pixel_extents (label->layout, NULL, &logical);
 
-	  if (logical.width > width)
-	    pango_layout_set_width (label->layout, width);
-	}
+          if (label->have_transform)
+            {
+              PangoContext *context = gtk_widget_get_pango_context (widget);
+              const PangoMatrix *matrix = pango_context_get_matrix (context);
+
+              const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
+              const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
+              if (fabs (dy) < 0.01)
+                {
+                  if (logical.width > bounds.width)
+                    pango_layout_set_width (label->layout, bounds.width * PANGO_SCALE);
+                }
+              else if (fabs (dx) < 0.01)
+                {
+                  if (logical.width > bounds.height)
+                    pango_layout_set_width (label->layout, bounds.height * PANGO_SCALE);
+                }
+              else
+                {
+                  gdouble x0, y0, x1, y1, length;
+                  gboolean vertical;
+                  gint cy;
+
+                  x0 = bounds.width / 2;
+                  y0 = dx ? x0 * dy / dx : dy * INFINITY;
+                  vertical = fabs (y0) > bounds.height / 2;
+
+                  if (vertical)
+                    {
+                      y0 = bounds.height/2;
+                      x0 = dy ? y0 * dx / dy : dx * INFINITY;
+                    }
+
+                  length = 2 * sqrt (x0 * x0 + y0 * y0);
+                  pango_layout_set_width (label->layout, rint (length * PANGO_SCALE));
+                  pango_layout_get_pixel_size (label->layout, NULL, &cy);
+
+                  x1 = +dy * cy/2;
+                  y1 = -dx * cy/2;
+
+                  if (vertical)
+                    {
+                      y0 = bounds.height/2 + y1 - y0;
+                      x0 = -y0 * dx/dy;
+                    }
+                  else
+                    {
+                      x0 = bounds.width/2 + x1 - x0;
+                      y0 = -x0 * dy/dx;
+                    }
+ 
+                  length = length - sqrt (x0 * x0 + y0 * y0) * 2;
+                  pango_layout_set_width (label->layout, rint (length * PANGO_SCALE));
+                }
+            }
+          else if (logical.width > bounds.width)
+            pango_layout_set_width (label->layout, bounds.width * PANGO_SCALE);
+        }
     }
 
   if (label->select_info && label->select_info->window)
@@ -3488,12 +3568,13 @@ get_layout_location (GtkLabel  *label,
                      gint      *yp)
 {
   GtkMisc *misc;
-  GtkWidget *widget; 
+  GtkWidget *widget;
   GtkLabelPrivate *priv;
   gfloat xalign;
   gint req_width, x, y;
+  gint req_height;
   PangoRectangle logical;
-  
+
   misc = GTK_MISC (label);
   widget = GTK_WIDGET (label);
   priv = GTK_LABEL_GET_PRIVATE (label);
@@ -3503,21 +3584,53 @@ get_layout_location (GtkLabel  *label,
   else
     xalign = 1.0 - misc->xalign;
 
-  pango_layout_get_pixel_extents (label->layout, NULL, &logical);
+  pango_layout_get_extents (label->layout, NULL, &logical);
 
-  if (label->ellipsize || priv->width_chars > 0)
+  if (label->have_transform)
+    {
+      PangoContext *context = gtk_widget_get_pango_context (widget);
+      const PangoMatrix *matrix = pango_context_get_matrix (context);
+      pango_matrix_transform_rectangle (matrix, &logical);
+    }
+
+  pango_extents_to_pixels (&logical, NULL);
+
+  if (label->ellipsize || priv->width_chars > 0 || priv->full_size)
     {
       int width;
 
       width = pango_layout_get_width (label->layout);
 
       req_width = logical.width;
+      req_height = logical.height;
+
       if (width != -1)
 	req_width = MIN(PANGO_PIXELS (width), req_width);
       req_width += 2 * misc->xpad;
+      req_height += 2 * misc->ypad;
+    }
+  else if (label->wrap)
+    {
+      GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
+
+      if (aux_info->width > 0)
+	req_width = aux_info->width;
+      else
+	req_width   = widget->allocation.width;
+
+      if (aux_info->height > 0)
+	req_height = aux_info->height;
+      else
+	req_height  = widget->allocation.height;
+
+      req_width  -= 2 * misc->xpad;
+      req_height -= 2 * misc->ypad;
     }
   else
-    req_width = widget->requisition.width;
+    {
+      req_width = widget->requisition.width;
+      req_height = widget->requisition.height;
+    }
 
   x = floor (widget->allocation.x + (gint)misc->xpad +
 	      xalign * (widget->allocation.width - req_width));
@@ -3526,7 +3639,6 @@ get_layout_location (GtkLabel  *label,
     x = MAX (x, widget->allocation.x + misc->xpad);
   else
     x = MIN (x, widget->allocation.x + widget->allocation.width - misc->xpad);
-  x -= logical.x;
 
   /* bgo#315462 - For single-line labels, *do* align the requisition with
    * respect to the allocation, even if we are under-allocated.  For multi-line
@@ -3543,10 +3655,10 @@ get_layout_location (GtkLabel  *label,
    */
   if (pango_layout_get_line_count (label->layout) == 1)
     y = floor (widget->allocation.y + (gint)misc->ypad 
-	       + (widget->allocation.height - widget->requisition.height) * misc->yalign);
+	       + (widget->allocation.height - req_height) * misc->yalign);
   else
     y = floor (widget->allocation.y + (gint)misc->ypad 
-	       + MAX (((widget->allocation.height - widget->requisition.height) * misc->yalign),
+	       + MAX (((widget->allocation.height - req_height) * misc->yalign),
 		      0));
 
   if (xp)



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