From f470b3aa6484c6c56cc98d451c6f57708aaf4d53 Mon Sep 17 00:00:00 2001 From: Jim Evins Date: Mon, 10 May 2010 22:54:47 -0400 Subject: [PATCH] Refinements to text objects - Draw outline around text objects. - Use a more sophisticated test for determining if cursor is on a text object: test against glyphs instead of simple bounds test. The test will also indicate that cursor is on the object if it is on the selection outline. - Do word wrap instead of character wrap. --- src/label-text.c | 367 +++++++++++++++++++++++++++-------------------- 1 file changed, 214 insertions(+), 153 deletions(-) diff --git a/src/label-text.c b/src/label-text.c index 109ad600..7206bedd 100644 --- a/src/label-text.c +++ b/src/label-text.c @@ -40,14 +40,18 @@ #define FONT_SCALE (72.0/96.0) +#define HANDLE_OUTLINE_RGBA_ARGS 0.5, 0.5, 0.5, 0.75 +#define HANDLE_OUTLINE_WIDTH_PIXELS 2.0 + /*========================================================*/ /* Private types. */ /*========================================================*/ struct _glLabelTextPrivate { - GtkTextTagTable *tag_table; - GtkTextBuffer *buffer; + + GtkTextTagTable *tag_table; + GtkTextBuffer *buffer; gchar *font_family; gdouble font_size; @@ -132,6 +136,11 @@ static gdouble get_text_line_spacing (glLabelObject *object); static glColorNode* get_text_color (glLabelObject *object); +static void set_text_path (glLabelText *this, + cairo_t *cr, + gboolean screen_flag, + glMergeRecord *record); + static void draw_object (glLabelObject *object, cairo_t *cr, gboolean screen_flag, @@ -161,6 +170,9 @@ static gboolean object_at (glLabelObject *object, gdouble x_pixels, gdouble y_pixels); +static void draw_handles (glLabelObject *object, + cairo_t *cr); + /*****************************************************************************/ /* Object infrastructure. */ @@ -200,6 +212,7 @@ gl_label_text_class_init (glLabelTextClass *class) label_object_class->draw_object = draw_object; label_object_class->draw_shadow = draw_shadow; label_object_class->object_at = object_at; + label_object_class->draw_handles = draw_handles; object_class->finalize = gl_label_text_finalize; } @@ -941,135 +954,114 @@ gl_label_text_get_auto_shrink (glLabelText *ltext) /*****************************************************************************/ -/* Draw object method. */ +/* Automatically shrink text size to fit within horizontal width. */ /*****************************************************************************/ -static void -draw_object (glLabelObject *object, - cairo_t *cr, - gboolean screen_flag, - glMergeRecord *record) +static gdouble +auto_shrink_font_size (cairo_t *cr, + gchar *family, + gdouble size, + PangoWeight weight, + PangoStyle style, + gchar *text, + gdouble width) { - glColorNode *color_node; - guint color; + PangoLayout *layout; + PangoFontDescription *desc; + gint iw, ih; + gdouble layout_width; + gdouble new_size; - gl_debug (DEBUG_LABEL, "START"); + layout = pango_cairo_create_layout (cr); - color_node = gl_label_object_get_text_color (object); - color = gl_color_node_expand (color_node, record); - if (color_node->field_flag && screen_flag) - { - color = GL_COLOR_MERGE_DEFAULT; - } - gl_color_node_free (&color_node); - - draw_text_real (object, cr, screen_flag, record, color); + desc = pango_font_description_new (); + pango_font_description_set_family (desc, family); + pango_font_description_set_weight (desc, weight); + pango_font_description_set_style (desc, style); + pango_font_description_set_size (desc, size * PANGO_SCALE); - gl_debug (DEBUG_LABEL, "END"); -} + pango_layout_set_font_description (layout, desc); + pango_font_description_free (desc); + pango_layout_set_text (layout, text, -1); + pango_layout_set_width (layout, -1); + pango_layout_get_size (layout, &iw, &ih); + layout_width = (gdouble)iw / (gdouble)PANGO_SCALE; -/*****************************************************************************/ -/* Draw shadow method. */ -/*****************************************************************************/ -static void -draw_shadow (glLabelObject *object, - cairo_t *cr, - gboolean screen_flag, - glMergeRecord *record) -{ - glColorNode *color_node; - guint color; - glColorNode *shadow_color_node; - gdouble shadow_opacity; - guint shadow_color; + g_object_unref (layout); - gl_debug (DEBUG_LABEL, "START"); + g_print ("Object w = %g, layout w = %g\n", width, layout_width); - color_node = gl_label_object_get_text_color (object); - color = gl_color_node_expand (color_node, record); - if (color_node->field_flag && screen_flag) + if ( layout_width > width ) { - color = GL_COLOR_MERGE_DEFAULT; - } - gl_color_node_free (&color_node); - - shadow_color_node = gl_label_object_get_shadow_color (object); - if (shadow_color_node->field_flag) - { - shadow_color_node->color = GL_COLOR_SHADOW_MERGE_DEFAULT; - } - shadow_opacity = gl_label_object_get_shadow_opacity (object); - shadow_color = gl_color_shadow (shadow_color_node->color, shadow_opacity, color); - gl_color_node_free (&shadow_color_node); + /* Scale down. */ + new_size = size * (width-2*GL_LABEL_TEXT_MARGIN)/layout_width; - draw_text_real (object, cr, screen_flag, record, shadow_color); + /* Round down to nearest 1/2 point */ + new_size = (int)(new_size*2.0) / 2.0; - gl_debug (DEBUG_LABEL, "END"); + /* don't get ridiculously small. */ + if (new_size < 1.0) + { + new_size = 1.0; + } + } + else + { + new_size = size; + } + + return new_size; } /*****************************************************************************/ -/* Draw text. */ +/* Update pango layout. */ /*****************************************************************************/ static void -draw_text_real (glLabelObject *object, - cairo_t *cr, - gboolean screen_flag, - glMergeRecord *record, - guint color) +set_text_path (glLabelText *this, + cairo_t *cr, + gboolean screen_flag, + glMergeRecord *record) { - gdouble object_w, object_h; - gdouble raw_w, raw_h; - gchar *text; - GList *lines; - gchar *font_family; - gdouble font_size; - PangoWeight font_weight; - gboolean font_italic_flag; - gboolean auto_shrink; - gdouble text_line_spacing; - PangoAlignment alignment; - PangoStyle style; - PangoLayout *layout; + gdouble object_w, object_h; + gdouble raw_w, raw_h; + gchar *text; + GList *lines; + gdouble font_size; + gboolean auto_shrink; + PangoLayout *layout; + PangoStyle style; PangoFontDescription *desc; cairo_font_options_t *font_options; PangoContext *context; - gl_debug (DEBUG_LABEL, "START"); - - gl_label_object_get_size (object, &object_w, &object_h); - gl_label_object_get_raw_size (object, &raw_w, &raw_h); - lines = gl_label_text_get_lines (GL_LABEL_TEXT (object)); - font_family = gl_label_object_get_font_family (object); - font_size = gl_label_object_get_font_size (object) * FONT_SCALE; - font_weight = gl_label_object_get_font_weight (object); - font_italic_flag = gl_label_object_get_font_italic_flag (object); + gl_debug (DEBUG_LABEL, "START"); - alignment = gl_label_object_get_text_alignment (object); - text_line_spacing = - gl_label_object_get_text_line_spacing (object); - auto_shrink = gl_label_text_get_auto_shrink (GL_LABEL_TEXT (object)); + cairo_save (cr); - text = gl_text_node_lines_expand (lines, record); + gl_label_object_get_size (GL_LABEL_OBJECT (this), &object_w, &object_h); + gl_label_object_get_raw_size (GL_LABEL_OBJECT (this), &raw_w, &raw_h); - style = font_italic_flag ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL; + lines = gl_label_text_get_lines (this); + text = gl_text_node_lines_expand (lines, record); + style = this->priv->font_italic_flag ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL; + font_size = this->priv->font_size; + auto_shrink = gl_label_text_get_auto_shrink (this); if (!screen_flag && record && auto_shrink && (raw_w != 0.0)) { font_size = auto_shrink_font_size (cr, - font_family, - font_size, - font_weight, + this->priv->font_family, + this->priv->font_size * FONT_SCALE, + this->priv->font_weight, style, text, object_w); } - cairo_save (cr); - layout = pango_cairo_create_layout (cr); font_options = cairo_font_options_create (); @@ -1079,15 +1071,15 @@ draw_text_real (glLabelObject *object, cairo_font_options_destroy (font_options); desc = pango_font_description_new (); - pango_font_description_set_family (desc, font_family); - pango_font_description_set_weight (desc, font_weight); + pango_font_description_set_family (desc, this->priv->font_family); + pango_font_description_set_weight (desc, this->priv->font_weight); + pango_font_description_set_size (desc, font_size * FONT_SCALE * PANGO_SCALE); pango_font_description_set_style (desc, style); - pango_font_description_set_size (desc, font_size * PANGO_SCALE); pango_layout_set_font_description (layout, desc); pango_font_description_free (desc); pango_layout_set_text (layout, text, -1); - pango_layout_set_spacing (layout, font_size * (text_line_spacing-1) * PANGO_SCALE); + pango_layout_set_spacing (layout, font_size * (this->priv->line_spacing-1) * PANGO_SCALE); if (raw_w == 0.0) { pango_layout_set_width (layout, -1); @@ -1096,84 +1088,108 @@ draw_text_real (glLabelObject *object, { pango_layout_set_width (layout, object_w * PANGO_SCALE); } - pango_layout_set_wrap (layout, PANGO_WRAP_CHAR); - pango_layout_set_alignment (layout, alignment); + pango_layout_set_wrap (layout, PANGO_WRAP_WORD); + pango_layout_set_alignment (layout, this->priv->align); - cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (color)); cairo_move_to (cr, GL_LABEL_TEXT_MARGIN, 0); - pango_cairo_show_layout (cr, layout); - - cairo_restore (cr); + pango_cairo_layout_path (cr, layout); g_object_unref (layout); + gl_text_node_lines_free (&lines); + cairo_restore (cr); - gl_text_node_lines_free (&lines); - g_free (font_family); - - gl_debug (DEBUG_LABEL, "END"); + gl_debug (DEBUG_LABEL, "END"); } /*****************************************************************************/ -/* Automatically shrink text size to fit within horizontal width. */ +/* Draw object method. */ /*****************************************************************************/ -static gdouble -auto_shrink_font_size (cairo_t *cr, - gchar *family, - gdouble size, - PangoWeight weight, - PangoStyle style, - gchar *text, - gdouble width) +static void +draw_object (glLabelObject *object, + cairo_t *cr, + gboolean screen_flag, + glMergeRecord *record) { - PangoLayout *layout; - PangoFontDescription *desc; - gint iw, ih; - gdouble layout_width; - gdouble new_size; + glColorNode *color_node; + guint color; - layout = pango_cairo_create_layout (cr); + gl_debug (DEBUG_LABEL, "START"); - desc = pango_font_description_new (); - pango_font_description_set_family (desc, family); - pango_font_description_set_weight (desc, weight); - pango_font_description_set_style (desc, style); - pango_font_description_set_size (desc, size * PANGO_SCALE); - - pango_layout_set_font_description (layout, desc); - pango_font_description_free (desc); + color_node = gl_label_object_get_text_color (object); + color = gl_color_node_expand (color_node, record); + if (color_node->field_flag && screen_flag) + { + color = GL_COLOR_MERGE_DEFAULT; + } + gl_color_node_free (&color_node); - pango_layout_set_text (layout, text, -1); - pango_layout_set_width (layout, -1); - pango_layout_get_size (layout, &iw, &ih); - layout_width = (gdouble)iw / (gdouble)PANGO_SCALE; + draw_text_real (object, cr, screen_flag, record, color); - g_object_unref (layout); + gl_debug (DEBUG_LABEL, "END"); +} - g_print ("Object w = %g, layout w = %g\n", width, layout_width); - if ( layout_width > width ) - { - /* Scale down. */ - new_size = size * (width-2*GL_LABEL_TEXT_MARGIN)/layout_width; +/*****************************************************************************/ +/* Draw shadow method. */ +/*****************************************************************************/ +static void +draw_shadow (glLabelObject *object, + cairo_t *cr, + gboolean screen_flag, + glMergeRecord *record) +{ + glColorNode *color_node; + guint color; + glColorNode *shadow_color_node; + gdouble shadow_opacity; + guint shadow_color; - /* Round down to nearest 1/2 point */ - new_size = (int)(new_size*2.0) / 2.0; + gl_debug (DEBUG_LABEL, "START"); - /* don't get ridiculously small. */ - if (new_size < 1.0) - { - new_size = 1.0; - } + color_node = gl_label_object_get_text_color (object); + color = gl_color_node_expand (color_node, record); + if (color_node->field_flag && screen_flag) + { + color = GL_COLOR_MERGE_DEFAULT; } - else + gl_color_node_free (&color_node); + + shadow_color_node = gl_label_object_get_shadow_color (object); + if (shadow_color_node->field_flag) { - new_size = size; + shadow_color_node->color = GL_COLOR_SHADOW_MERGE_DEFAULT; } + shadow_opacity = gl_label_object_get_shadow_opacity (object); + shadow_color = gl_color_shadow (shadow_color_node->color, shadow_opacity, color); + gl_color_node_free (&shadow_color_node); - return new_size; + draw_text_real (object, cr, screen_flag, record, shadow_color); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Draw text. */ +/*****************************************************************************/ +static void +draw_text_real (glLabelObject *object, + cairo_t *cr, + gboolean screen_flag, + glMergeRecord *record, + guint color) +{ + gl_debug (DEBUG_LABEL, "START"); + + set_text_path (GL_LABEL_TEXT (object), cr, screen_flag, record); + + cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (color)); + cairo_fill (cr); + + gl_debug (DEBUG_LABEL, "END"); } @@ -1190,17 +1206,62 @@ object_at (glLabelObject *object, gl_label_object_get_size (object, &w, &h); - cairo_rectangle (cr, 0.0, 0.0, w, h); + if ( (x >= 0) && (x <= w) && (y >= 0) && (y <= h) ) + { + set_text_path (GL_LABEL_TEXT (object), cr, TRUE, NULL); + if (cairo_in_fill (cr, x, y)) + { + return TRUE; + } + } - if (cairo_in_fill (cr, x, y)) + if (gl_label_object_is_selected (object)) { - return TRUE; + cairo_rectangle (cr, 0, 0, w, h); + if (cairo_in_stroke (cr, x, y)) + { + return TRUE; + } } return FALSE; } +/*****************************************************************************/ +/* Draw text style handles. */ +/*****************************************************************************/ +static void +draw_handles (glLabelObject *object, + cairo_t *cr) +{ + gdouble w, h; + gdouble scale_x, scale_y; + gdouble dashes[2] = { 2, 2 }; + + gl_label_object_get_size (GL_LABEL_OBJECT(object), &w, &h); + + cairo_save (cr); + + cairo_rectangle (cr, 0, 0, w, h); + + scale_x = 1.0; + scale_y = 1.0; + cairo_device_to_user_distance (cr, &scale_x, &scale_y); + cairo_scale (cr, scale_x, scale_y); + + cairo_set_dash (cr, dashes, 2, 0); + cairo_set_line_width (cr, HANDLE_OUTLINE_WIDTH_PIXELS); + cairo_set_source_rgba (cr, HANDLE_OUTLINE_RGBA_ARGS); + cairo_stroke (cr); + + cairo_restore (cr); + + gl_label_object_draw_handles_box (object, cr); +} + + + /* * Local Variables: -- emacs -- 2.39.5