[gtk+] label: Don't recreate the layout just for measuring



commit db474e82c4631f6f6d9af98366e3f176c0c68a21
Author: Benjamin Otte <otte redhat com>
Date:   Tue Mar 29 02:49:19 2011 +0200

    label: Don't recreate the layout just for measuring
    
    Instead, create a custom one.

 gtk/gtklabel.c |  108 ++++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 85 insertions(+), 23 deletions(-)
---
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 0f62d4f..0ecad73 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -3232,12 +3232,79 @@ get_label_wrap_width (GtkLabel *label)
   return priv->wrap_width;
 }
 
+static PangoLayout *
+gtk_label_get_layout_with_guessed_wrap_width (GtkLabel *label)
+{
+  GdkScreen *screen;
+  PangoRectangle logical_rect;
+  gint wrap_width, width, height, longest_paragraph;
+  PangoLayout *layout;
+
+  if (!label->priv->wrap)
+    return gtk_label_get_measuring_layout (label, NULL, -1);
+
+  screen = gtk_widget_get_screen (GTK_WIDGET (label));
+
+  layout = gtk_label_get_measuring_layout (label, NULL, -1);
+  pango_layout_get_extents (layout, NULL, &logical_rect);
+
+  width = logical_rect.width;
+  /* Try to guess a reasonable maximum width */
+  longest_paragraph = width;
+
+  wrap_width = get_label_wrap_width (label);
+  width = MIN (width, wrap_width);
+  width = MIN (width,
+               PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
+
+  layout = gtk_label_get_measuring_layout (label, layout, width);
+
+  pango_layout_get_extents (layout, NULL, &logical_rect);
+  width = logical_rect.width;
+  height = logical_rect.height;
+
+  /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
+   * so we try short search for a narrower width that leaves us with the same height
+   */
+  if (longest_paragraph > 0)
+    {
+      gint nlines, perfect_width;
+
+      nlines = pango_layout_get_line_count (layout);
+      perfect_width = (longest_paragraph + nlines - 1) / nlines;
+      
+      if (perfect_width < width)
+        {
+          layout = gtk_label_get_measuring_layout (label, layout, perfect_width);
+          pango_layout_get_extents (layout, NULL, &logical_rect);
+
+          if (logical_rect.height <= height)
+            width = logical_rect.width;
+          else
+            {
+              gint mid_width = (perfect_width + width) / 2;
+              
+              if (mid_width > perfect_width)
+                {
+                  layout = gtk_label_get_measuring_layout (label, layout, mid_width);
+                  pango_layout_get_extents (layout, NULL, &logical_rect);
+
+                  if (logical_rect.height <= height)
+                    width = logical_rect.width;
+                }
+            }
+        }
+    }
+
+  return gtk_label_get_measuring_layout (label, layout, width);
+}
+
 static void
 gtk_label_ensure_layout (GtkLabel *label, gboolean guess_wrap_width)
 {
   GtkLabelPrivate *priv = label->priv;
-  GtkWidget *widget;
   PangoRectangle logical_rect;
+  GtkWidget *widget;
   gboolean rtl;
 
   widget = GTK_WIDGET (label);
@@ -3483,6 +3550,7 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
   PangoRectangle required_rect;
   PangoRectangle natural_rect;
   gint           xpad, ypad;
+  PangoLayout *layout;
 
   /* "width-chars" Hard-coded minimum width:
    *    - minimum size should be MAX (width-chars, strlen ("..."));
@@ -3496,34 +3564,36 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
 
   /* When calculating ->wrap sometimes we need to invent a size; Ideally we should be doing
    * that stuff here instead of inside gtk_label_ensure_layout() */
-  if (priv->wrap)
-    gtk_label_clear_layout (label);
-  gtk_label_ensure_layout (label, TRUE);
+  layout = gtk_label_get_layout_with_guessed_wrap_width (label);
 
   /* Start off with the pixel extents of the rendered layout */
-  pango_layout_get_extents (priv->layout, NULL, &required_rect);
+  pango_layout_get_extents (layout, NULL, &required_rect);
   required_rect.x = required_rect.y = 0;
 
   if (priv->single_line_mode || priv->wrap)
-    required_rect.height = get_single_line_height (GTK_WIDGET (label), priv->layout);
+    required_rect.height = get_single_line_height (GTK_WIDGET (label), layout);
 
   natural_rect = required_rect;
 
   /* Calculate text width itself based on GtkLabel property rules */
-  get_label_width (label, priv->layout, &required_rect.width, &natural_rect.width);
+  get_label_width (label, layout, &required_rect.width, &natural_rect.width);
 
   /* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
   if (priv->have_transform)
     {
-      PangoLayout       *layout  = pango_layout_copy (priv->layout);
-      PangoContext      *context = pango_layout_get_context (priv->layout);
-      const PangoMatrix *matrix  = pango_context_get_matrix (context);
+      PangoLayout *copy;
+      PangoContext *context;
+      const PangoMatrix *matrix;
 
-      pango_layout_set_width (layout, -1);
-      pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
+      copy = pango_layout_copy (priv->layout);
+      context = pango_layout_get_context (copy);
+      matrix = pango_context_get_matrix (context);
 
-      pango_layout_get_extents (layout, NULL, &natural_rect);
-      g_object_unref (layout);
+      pango_layout_set_width (copy, -1);
+      pango_layout_set_ellipsize (copy, PANGO_ELLIPSIZE_NONE);
+
+      pango_layout_get_extents (copy, NULL, &natural_rect);
+      g_object_unref (copy);
 
       pango_matrix_transform_rectangle (matrix, &required_rect);
       pango_matrix_transform_rectangle (matrix, &natural_rect);
@@ -3608,15 +3678,7 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
       *natural_size += ypad * 2;
     }
 
-  /* Restore real allocated size of layout; sometimes size-requests
-   * are randomly called without a following allocation; for this case
-   * we need to make sure we dont have a mucked up layout because we
-   * went and guessed the wrap-size.
-   */
-  if (priv->wrap)
-    gtk_label_clear_layout (label);
-  gtk_label_ensure_layout (label, FALSE);
-
+  g_object_unref (layout);
 }
 
 



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