rework this to be faster and deal more correctly with the ellipsis.

2002-12-09  Chris Toshok  <toshok@ximian.com>

	* e-clipped-label.[ch]: rework this to be faster and deal more
	correctly with the ellipsis.

svn path=/trunk/; revision=19076
This commit is contained in:
Chris Toshok
2002-12-09 23:30:30 +00:00
committed by Chris Toshok
parent d2971bf645
commit bdd9cae5e9
3 changed files with 104 additions and 69 deletions

View File

@ -1,3 +1,8 @@
2002-12-09 Chris Toshok <toshok@ximian.com>
* e-clipped-label.[ch]: rework this to be faster and deal more
correctly with the ellipsis.
2002-12-03 Not Zed <NotZed@Ximian.com> 2002-12-03 Not Zed <NotZed@Ximian.com>
* e-search-bar.c (impl_dispose): dispose can be run multiple times * e-search-bar.c (impl_dispose): dispose can be run multiple times

View File

@ -51,9 +51,10 @@ static void e_clipped_label_size_allocate (GtkWidget *widget,
GtkAllocation *allocation); GtkAllocation *allocation);
static gint e_clipped_label_expose (GtkWidget *widget, static gint e_clipped_label_expose (GtkWidget *widget,
GdkEventExpose *event); GdkEventExpose *event);
static void e_clipped_label_recalc_chars_displayed (EClippedLabel *label, PangoLayout *layout); static void e_clipped_label_recalc_chars_displayed (EClippedLabel *label);
static void e_clipped_label_finalize (GObject *object); static void e_clipped_label_finalize (GObject *object);
static void build_layout (EClippedLabel *label, const char *text);
static GtkMiscClass *parent_class; static GtkMiscClass *parent_class;
@ -133,28 +134,32 @@ GtkWidget *
e_clipped_label_new (const gchar *text) e_clipped_label_new (const gchar *text)
{ {
GtkWidget *label; GtkWidget *label;
EClippedLabel *clipped;
label = GTK_WIDGET (gtk_type_new (e_clipped_label_get_type ())); label = GTK_WIDGET (gtk_type_new (e_clipped_label_get_type ()));
clipped = E_CLIPPED_LABEL (label);
build_layout (clipped, e_clipped_label_ellipsis);
pango_layout_get_pixel_size (clipped->layout, &clipped->ellipsis_width, NULL);
if (text && *text) if (text && *text)
e_clipped_label_set_text (E_CLIPPED_LABEL (label), text); e_clipped_label_set_text (clipped, text);
return label; return label;
} }
static PangoLayout* static void
build_layout (GtkWidget *widget, char *text) build_layout (EClippedLabel *label, const char *text)
{ {
PangoLayout *layout; if (!label->layout) {
label->layout = gtk_widget_create_pango_layout (GTK_WIDGET (label), text);
layout = gtk_widget_create_pango_layout (widget, text);
#ifdef FROB_FONT_DESC #ifdef FROB_FONT_DESC
{ {
/* this makes the font used a little bigger than the /* this makes the font used a little bigger than the
default style for this widget, as well as makes it default style for this widget, as well as makes it
bold... */ bold... */
GtkStyle *style = gtk_widget_get_style (widget); GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (label));
PangoFontDescription *desc = pango_font_description_copy (style->font_desc); PangoFontDescription *desc = pango_font_description_copy (style->font_desc);
pango_font_description_set_size (desc, pango_font_description_set_size (desc,
pango_font_description_get_size (desc) * 1.2); pango_font_description_get_size (desc) * 1.2);
@ -164,8 +169,10 @@ build_layout (GtkWidget *widget, char *text)
pango_font_description_free (desc); pango_font_description_free (desc);
} }
#endif #endif
}
return layout; else {
pango_layout_set_text (label->layout, text, -1);
}
} }
static void static void
@ -174,21 +181,18 @@ e_clipped_label_size_request (GtkWidget *widget,
{ {
EClippedLabel *label; EClippedLabel *label;
GdkFont *font; GdkFont *font;
PangoLayout *layout;
int height; int height;
int width;
g_return_if_fail (E_IS_CLIPPED_LABEL (widget)); g_return_if_fail (E_IS_CLIPPED_LABEL (widget));
g_return_if_fail (requisition != NULL); g_return_if_fail (requisition != NULL);
label = E_CLIPPED_LABEL (widget); label = E_CLIPPED_LABEL (widget);
layout = build_layout (widget, label->label); pango_layout_get_pixel_size (label->layout, &width, &height);
pango_layout_get_pixel_size (layout, NULL, &height);
requisition->width = 0; requisition->width = 0;
requisition->height = height + 2 * GTK_MISC (widget)->ypad; requisition->height = height + 2 * GTK_MISC (widget)->ypad;
g_object_unref (layout);
} }
@ -215,7 +219,6 @@ e_clipped_label_expose (GtkWidget *widget,
GtkMisc *misc; GtkMisc *misc;
gint x, y; gint x, y;
gchar *tmp_str, tmp_ch; gchar *tmp_str, tmp_ch;
PangoLayout *layout;
PangoRectangle rect; PangoRectangle rect;
g_return_val_if_fail (E_IS_CLIPPED_LABEL (widget), FALSE); g_return_val_if_fail (E_IS_CLIPPED_LABEL (widget), FALSE);
@ -229,13 +232,9 @@ e_clipped_label_expose (GtkWidget *widget,
|| !label->label || (*label->label == '\0')) || !label->label || (*label->label == '\0'))
return TRUE; return TRUE;
layout = build_layout (widget, label->label);
/* Recalculate the number of characters displayed, if necessary. */ /* Recalculate the number of characters displayed, if necessary. */
if (label->chars_displayed == E_CLIPPED_LABEL_NEED_RECALC) if (label->chars_displayed == E_CLIPPED_LABEL_NEED_RECALC)
e_clipped_label_recalc_chars_displayed (label, layout); e_clipped_label_recalc_chars_displayed (label);
pango_layout_set_text (layout, label->label, strlen (label->label));
/* /*
* GC Clipping * GC Clipping
@ -245,12 +244,7 @@ e_clipped_label_expose (GtkWidget *widget,
gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state],
&event->area); &event->area);
pango_layout_get_pixel_extents (layout, &rect, NULL); pango_layout_get_pixel_extents (label->layout, &rect, NULL);
y = floor (widget->allocation.y + (gint)misc->ypad
+ (((gint)widget->allocation.height - 2 * (gint)misc->ypad
+ (gint)PANGO_ASCENT (rect) - PANGO_DESCENT(rect))
* misc->yalign) + 0.5);
if (label->chars_displayed == E_CLIPPED_LABEL_SHOW_ENTIRE_LABEL) { if (label->chars_displayed == E_CLIPPED_LABEL_SHOW_ENTIRE_LABEL) {
x = floor (widget->allocation.x + (gint)misc->xpad x = floor (widget->allocation.x + (gint)misc->xpad
@ -259,33 +253,38 @@ e_clipped_label_expose (GtkWidget *widget,
* misc->xalign) + 0.5); * misc->xalign) + 0.5);
gdk_draw_layout (widget->window, widget->style->fg_gc[widget->state], gdk_draw_layout (widget->window, widget->style->fg_gc[widget->state],
x, y, layout); x, label->label_y, label->layout);
} else { } else {
int layout_width; int layout_width;
x = widget->allocation.x + (gint)misc->xpad; x = widget->allocation.x + (gint)misc->xpad;
pango_layout_set_text (layout, label->label, label->chars_displayed); /* trim the layout to the number of characters we're displaying */
pango_layout_set_text (label->layout, label->label, label->chars_displayed);
/* draw it */
gdk_draw_layout (widget->window, widget->style->fg_gc[widget->state], gdk_draw_layout (widget->window, widget->style->fg_gc[widget->state],
x, y, layout); x, label->label_y, label->layout);
pango_layout_get_pixel_size (layout, &layout_width, NULL); pango_layout_get_pixel_size (label->layout, &layout_width, NULL);
/* offset the X position for the ellipsis */
x = widget->allocation.x + (gint)misc->xpad x = widget->allocation.x + (gint)misc->xpad
+ label->ellipsis_x; + label->ellipsis_x;
pango_layout_set_text (layout, e_clipped_label_ellipsis, strlen (e_clipped_label_ellipsis)); /* then draw the ellipsis */
pango_layout_set_text (label->layout, e_clipped_label_ellipsis, strlen (e_clipped_label_ellipsis));
gdk_draw_layout (widget->window, widget->style->fg_gc[widget->state], gdk_draw_layout (widget->window, widget->style->fg_gc[widget->state],
x, y, layout); x, label->label_y, label->layout);
/* then reset the layout to our original label text */
pango_layout_set_text (label->layout, label->label, -1);
} }
gdk_gc_set_clip_mask (widget->style->white_gc, NULL); gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL); gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
g_object_unref (layout);
return TRUE; return TRUE;
} }
@ -301,6 +300,8 @@ e_clipped_label_finalize (GObject *object)
g_free (label->label); g_free (label->label);
g_object_unref (label->layout);
(* G_OBJECT_CLASS (parent_class)->finalize) (object); (* G_OBJECT_CLASS (parent_class)->finalize) (object);
} }
@ -346,6 +347,8 @@ e_clipped_label_set_text (EClippedLabel *label,
if (text) if (text)
label->label = g_strdup (text); label->label = g_strdup (text);
build_layout (label, text);
/* Reset the number of characters displayed, so it is /* Reset the number of characters displayed, so it is
recalculated when needed. */ recalculated when needed. */
label->chars_displayed = E_CLIPPED_LABEL_NEED_RECALC; label->chars_displayed = E_CLIPPED_LABEL_NEED_RECALC;
@ -358,9 +361,15 @@ e_clipped_label_set_text (EClippedLabel *label,
static void static void
e_clipped_label_recalc_chars_displayed (EClippedLabel *label, PangoLayout *layout) e_clipped_label_recalc_chars_displayed (EClippedLabel *label)
{ {
gint max_width, width, ch, last_width, ellipsis_width; gint max_width, width, ch, last_width, ellipsis_width;
GSList *lines;
PangoLayoutLine *line;
int index;
PangoRectangle rect;
GtkWidget *widget = GTK_WIDGET (label);
GtkMisc *misc = GTK_MISC (label);
max_width = GTK_WIDGET (label)->allocation.width max_width = GTK_WIDGET (label)->allocation.width
- 2 * GTK_MISC (label)->xpad; - 2 * GTK_MISC (label)->xpad;
@ -371,40 +380,50 @@ e_clipped_label_recalc_chars_displayed (EClippedLabel *label, PangoLayout *layou
} }
/* See if the entire label fits in the allocated width. */ /* See if the entire label fits in the allocated width. */
pango_layout_get_pixel_size (layout, &label->label_width, NULL); pango_layout_set_text (label->layout, label->label, -1);
pango_layout_get_pixel_extents (label->layout, &rect, NULL);
label->label_y = floor (widget->allocation.y + (gint)misc->ypad
+ (((gint)widget->allocation.height - 2 * (gint)misc->ypad
+ (gint)PANGO_ASCENT (rect) - PANGO_DESCENT(rect))
* misc->yalign) + 0.5);
pango_layout_get_pixel_size (label->layout, &label->label_width, NULL);
if (label->label_width <= max_width) { if (label->label_width <= max_width) {
label->chars_displayed = E_CLIPPED_LABEL_SHOW_ENTIRE_LABEL; label->chars_displayed = E_CLIPPED_LABEL_SHOW_ENTIRE_LABEL;
return; return;
} }
/* Calculate the width of the ellipsis string. */
pango_layout_set_text (layout, e_clipped_label_ellipsis, strlen (e_clipped_label_ellipsis));
pango_layout_get_pixel_size (layout, &ellipsis_width, NULL);
max_width -= ellipsis_width;
if (max_width <= 0) { if (max_width <= 0) {
label->chars_displayed = 0; label->chars_displayed = 0;
label->ellipsis_x = 0; label->ellipsis_x = 0;
return; return;
} }
/* Step through the label, adding on the widths of the max_width -= label->ellipsis_width;
characters, until we can't fit any more in. */
width = last_width = 0;
for (ch = 0; ch < strlen (label->label); ch++) {
pango_layout_set_text (layout, label->label, ch);
pango_layout_get_pixel_size (layout, &width, NULL);
if (width > max_width) { lines = pango_layout_get_lines (label->layout);
label->chars_displayed = ch; line = lines->data;
label->ellipsis_x = last_width;
if (!pango_layout_line_x_to_index (line,
max_width * PANGO_SCALE,
&index,
NULL)) {
g_warning ("pango_layout_line_x_to_index returned false");
return; return;
} }
last_width = width; #if 0
} g_slist_foreach (lines, (GFunc)pango_layout_line_unref, NULL);
g_slist_free (lines);
#endif
g_warning ("Clipped label width not exceeded as expected"); label->chars_displayed = index;
label->chars_displayed = E_CLIPPED_LABEL_SHOW_ENTIRE_LABEL;
pango_layout_set_text (label->layout, label->label, label->chars_displayed);
pango_layout_get_pixel_size (label->layout, &width, NULL);
label->ellipsis_x = width;
} }

View File

@ -51,9 +51,17 @@ struct _EClippedLabel
gchar *label; gchar *label;
/* Our PangoLayout */
PangoLayout *layout;
/* This is the width of the entire label string, in pixels. */ /* This is the width of the entire label string, in pixels. */
gint label_width; gint label_width;
/* This is the label's y coord. we store it here so it won't
change as the label's baseline changes (for example if we
ellide the 'y' from 'Summary' the baseline drops) */
gint label_y;
/* This is the number of characters we can fit in, or /* This is the number of characters we can fit in, or
E_CLIPPED_LABEL_NEED_RECALC if it needs to be recalculated, or E_CLIPPED_LABEL_NEED_RECALC if it needs to be recalculated, or
E_CLIPPED_LABEL_SHOW_ENTIRE_LABEL to show the entire label. */ E_CLIPPED_LABEL_SHOW_ENTIRE_LABEL to show the entire label. */
@ -62,6 +70,9 @@ struct _EClippedLabel
/* This is the x position to display the ellipsis string, e.g. '...', /* This is the x position to display the ellipsis string, e.g. '...',
relative to the start of the label. */ relative to the start of the label. */
gint ellipsis_x; gint ellipsis_x;
/* This is the width of the ellipsis, in pixels */
gint ellipsis_width;
}; };
struct _EClippedLabelClass struct _EClippedLabelClass