[clutter/clutter-1.6] clutter-text: Use the ink rect in the paint volume
- From: Neil Roberts <nroberts src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [clutter/clutter-1.6] clutter-text: Use the ink rect in the paint volume
- Date: Fri, 10 Jun 2011 14:30:31 +0000 (UTC)
commit 49448d0991a60e239ea80728694daeced8e0664d
Author: Neil Roberts <neil linux intel com>
Date: Mon Mar 7 18:53:07 2011 +0000
clutter-text: Use the ink rect in the paint volume
Previously ClutterText was just reporting the allocation as the paint
volume. The preferred size of a ClutterText is just the logical
rectangle of the layout. A pango layout can sometimes draw outside of
its logical rectangle for example with an italicised font with large
serifs. Additionally, ClutterText doesn't make any attempt to clip the
text if the actor gets allocated a size too small for the text so it
would also end up drawing outside of the paint volume in that case. To
fix this, the paint volume is now reported as the ink rect of the
Pango layout. The rectangle for the cursor and selection is also
unioned into that because it won't necessarily be within the ink
rectangle.
The function for drawing the selection rectangles has been split up
into a generic function that calculates the rectangles that need to be
drawn and a function that draws them. That way the get_paint_volume
virtual can share the code to calculate the rectangles.
http://bugzilla.clutter-project.org/show_bug.cgi?id=2599
(cherry picked from commit fe83dee71246ef6fdad07c4cd59d59b433023ebd)
clutter/clutter-text.c | 362 ++++++++++++++++++++++++++++++++++++------------
1 files changed, 271 insertions(+), 91 deletions(-)
---
diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c
index 6201874..4566cc3 100644
--- a/clutter/clutter-text.c
+++ b/clutter/clutter-text.c
@@ -59,6 +59,7 @@
#include "clutter-private.h" /* includes pango/cogl-pango.h */
#include "clutter-profile.h"
#include "clutter-units.h"
+#include "clutter-paint-volume-private.h"
/* cursor width in pixels */
#define DEFAULT_CURSOR_SIZE 2
@@ -176,6 +177,11 @@ struct _ClutterTextPrivate
ClutterColor cursor_color;
guint cursor_size;
+ /* Box representing the paint volume. The box is lazily calculated
+ and cached */
+ ClutterPaintVolume paint_volume;
+ gboolean paint_volume_valid;
+
guint preedit_cursor_pos;
gint preedit_n_chars;
@@ -242,6 +248,37 @@ static guint text_signals[LAST_SIGNAL] = { 0, };
static void clutter_text_font_changed_cb (ClutterText *text);
+static void
+clutter_text_dirty_paint_volume (ClutterText *text)
+{
+ ClutterTextPrivate *priv = text->priv;
+
+ if (priv->paint_volume_valid)
+ {
+ clutter_paint_volume_free (&priv->paint_volume);
+ priv->paint_volume_valid = FALSE;
+ }
+}
+
+static void
+clutter_text_queue_redraw (ClutterActor *self)
+{
+ /* This is a wrapper for clutter_actor_queue_redraw that also
+ dirties the cached paint volume. It would be nice if we could
+ just override the default implementation of the queue redraw
+ signal to do this instead but that doesn't work because the
+ signal isn't immediately emitted when queue_redraw is called.
+ Clutter will however immediately call get_paint_volume when
+ queue_redraw is called so we do need to dirty it immediately. */
+
+ clutter_text_dirty_paint_volume (CLUTTER_TEXT (self));
+
+ clutter_actor_queue_redraw (self);
+}
+
+#define clutter_actor_queue_redraw \
+ Please_use_clutter_text_queue_redraw_instead
+
#define offset_real(t,p) ((p) == -1 ? g_utf8_strlen ((t), -1) : (p))
static gint
@@ -271,7 +308,7 @@ clutter_text_clear_selection (ClutterText *self)
{
priv->selection_bound = priv->position;
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
}
}
@@ -451,6 +488,8 @@ clutter_text_dirty_cache (ClutterText *text)
g_object_unref (priv->cached_layouts[i].layout);
priv->cached_layouts[i].layout = NULL;
}
+
+ clutter_text_dirty_paint_volume (text);
}
/*
@@ -1352,12 +1391,113 @@ clutter_text_finalize (GObject *gobject)
if (priv->preedit_attrs)
pango_attr_list_unref (priv->preedit_attrs);
+ clutter_text_dirty_paint_volume (self);
+
g_free (priv->text);
g_free (priv->font_name);
G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject);
}
+typedef void (* ClutterTextSelectionFunc) (ClutterText *text,
+ const ClutterActorBox *box,
+ gpointer user_data);
+
+static void
+clutter_text_foreach_selection_rectangle (ClutterText *self,
+ ClutterTextSelectionFunc func,
+ gpointer user_data)
+{
+ ClutterTextPrivate *priv = self->priv;
+ PangoLayout *layout = clutter_text_get_layout (self);
+ gchar *utf8 = clutter_text_get_display_text (self);
+ gint lines;
+ gint start_index;
+ gint end_index;
+ gint line_no;
+
+ if (priv->position == 0)
+ start_index = 0;
+ else
+ start_index = offset_to_bytes (utf8, priv->position);
+
+ if (priv->selection_bound == 0)
+ end_index = 0;
+ else
+ end_index = offset_to_bytes (utf8, priv->selection_bound);
+
+ if (start_index > end_index)
+ {
+ gint temp = start_index;
+ start_index = end_index;
+ end_index = temp;
+ }
+
+ lines = pango_layout_get_line_count (layout);
+
+ for (line_no = 0; line_no < lines; line_no++)
+ {
+ PangoLayoutLine *line;
+ gint n_ranges;
+ gint *ranges;
+ gint i;
+ gint index_;
+ gint maxindex;
+ ClutterActorBox box;
+ gfloat y, height;
+
+ line = pango_layout_get_line_readonly (layout, line_no);
+ pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL);
+ if (maxindex < start_index)
+ continue;
+
+ pango_layout_line_get_x_ranges (line, start_index, end_index,
+ &ranges,
+ &n_ranges);
+ pango_layout_line_x_to_index (line, 0, &index_, NULL);
+
+ clutter_text_position_to_coords (self,
+ bytes_to_offset (utf8, index_),
+ NULL, &y, &height);
+
+ box.y1 = y;
+ box.y2 = y + height;
+
+ for (i = 0; i < n_ranges; i++)
+ {
+ gint range_x;
+ gint range_width;
+
+ range_x = ranges[i * 2] / PANGO_SCALE;
+
+ /* Account for any scrolling in single line mode */
+ if (priv->single_line_mode)
+ range_x += priv->text_x;
+
+
+ range_width = (ranges[i * 2 + 1] - ranges[i * 2])
+ / PANGO_SCALE;
+
+ box.x1 = range_x;
+ box.x2 = range_x + range_width;
+
+ func (self, &box, user_data);
+ }
+
+ g_free (ranges);
+ }
+
+ g_free (utf8);
+}
+
+static void
+clutter_text_paint_selection_rectangle_cb (ClutterText *text,
+ const ClutterActorBox *box,
+ gpointer user_data)
+{
+ cogl_rectangle (box->x1, box->y1, box->x2, box->y2);
+}
+
static void
cursor_paint (ClutterText *self)
{
@@ -1393,13 +1533,6 @@ cursor_paint (ClutterText *self)
}
else
{
- PangoLayout *layout = clutter_text_get_layout (self);
- gchar *utf8 = clutter_text_get_display_text (self);
- gint lines;
- gint start_index;
- gint end_index;
- gint line_no;
-
if (priv->selection_color_set)
color = &priv->selection_color;
else if (priv->cursor_color_set)
@@ -1414,74 +1547,8 @@ cursor_paint (ClutterText *self)
* color->alpha
/ 255);
- if (position == 0)
- start_index = 0;
- else
- start_index = offset_to_bytes (utf8, position);
-
- if (priv->selection_bound == 0)
- end_index = 0;
- else
- end_index = offset_to_bytes (utf8, priv->selection_bound);
-
- if (start_index > end_index)
- {
- gint temp = start_index;
- start_index = end_index;
- end_index = temp;
- }
-
- lines = pango_layout_get_line_count (layout);
-
- for (line_no = 0; line_no < lines; line_no++)
- {
- PangoLayoutLine *line;
- gint n_ranges;
- gint *ranges;
- gint i;
- gint index_;
- gint maxindex;
- gfloat y, height;
-
- line = pango_layout_get_line_readonly (layout, line_no);
- pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL);
- if (maxindex < start_index)
- continue;
-
- pango_layout_line_get_x_ranges (line, start_index, end_index,
- &ranges,
- &n_ranges);
- pango_layout_line_x_to_index (line, 0, &index_, NULL);
-
- clutter_text_position_to_coords (self,
- bytes_to_offset (utf8, index_),
- NULL, &y, &height);
-
- for (i = 0; i < n_ranges; i++)
- {
- gint range_x;
- gint range_width;
-
- range_x = ranges[i * 2] / PANGO_SCALE;
-
- /* Account for any scrolling in single line mode */
- if (priv->single_line_mode)
- range_x += priv->text_x;
-
-
- range_width = (ranges[i * 2 + 1] - ranges[i * 2])
- / PANGO_SCALE;
-
- cogl_rectangle (range_x,
- y,
- range_x + range_width,
- y + height);
- }
-
- g_free (ranges);
- }
-
- g_free (utf8);
+ clutter_text_foreach_selection_rectangle
+ (self, clutter_text_paint_selection_rectangle_cb, NULL);
}
}
}
@@ -1827,6 +1894,10 @@ clutter_text_paint (ClutterActor *self)
gint text_x = priv->text_x;
gboolean clip_set = FALSE;
+ /* Note that if anything in this paint method changes it needs to be
+ reflected in the get_paint_volume implementation which is tightly
+ tied to the workings of this function */
+
if (G_UNLIKELY (priv->font_desc == NULL || priv->text == NULL))
{
CLUTTER_NOTE (ACTOR, "desc: %p, text %p",
@@ -1922,13 +1993,122 @@ clutter_text_paint (ClutterActor *self)
}
+static void
+clutter_text_add_selection_to_paint_volume_cb (ClutterText *text,
+ const ClutterActorBox *box,
+ gpointer user_data)
+{
+ ClutterPaintVolume *total_volume = user_data;
+ ClutterPaintVolume rect_volume;
+ ClutterVertex vertex;
+
+ _clutter_paint_volume_init_static (CLUTTER_ACTOR (text), &rect_volume);
+
+ vertex.x = box->x1;
+ vertex.y = box->y1;
+ vertex.z = 0.0f;
+ clutter_paint_volume_set_origin (&rect_volume, &vertex);
+ clutter_paint_volume_set_width (&rect_volume, box->x2 - box->x1);
+ clutter_paint_volume_set_height (&rect_volume, box->y2 - box->y1);
+
+ clutter_paint_volume_union (total_volume, &rect_volume);
+
+ clutter_paint_volume_free (&rect_volume);
+}
+
+static void
+clutter_text_get_paint_volume_for_cursor (ClutterText *text,
+ ClutterPaintVolume *volume)
+{
+ ClutterTextPrivate *priv = text->priv;
+ ClutterVertex origin;
+
+ clutter_text_ensure_cursor_position (text);
+
+ if (priv->position == priv->selection_bound)
+ {
+ origin.x = priv->cursor_pos.x;
+ origin.y = priv->cursor_pos.y;
+ origin.z = 0;
+ clutter_paint_volume_set_origin (volume, &origin);
+ clutter_paint_volume_set_width (volume, priv->cursor_pos.width);
+ clutter_paint_volume_set_height (volume, priv->cursor_pos.height);
+ }
+ else
+ {
+ clutter_text_foreach_selection_rectangle
+ (text, clutter_text_add_selection_to_paint_volume_cb, volume);
+ }
+}
+
static gboolean
clutter_text_get_paint_volume (ClutterActor *self,
ClutterPaintVolume *volume)
{
- return _clutter_actor_set_default_paint_volume (self,
- CLUTTER_TYPE_TEXT,
- volume);
+ ClutterText *text = CLUTTER_TEXT (self);
+ ClutterTextPrivate *priv = text->priv;
+
+ /* ClutterText uses the logical layout as the natural size of the
+ actor. This means that it can sometimes paint outside of its
+ allocation for example with italic fonts with serifs. Therefore
+ we should use the ink rectangle of the layout instead */
+
+ if (!priv->paint_volume_valid)
+ {
+ PangoLayout *layout;
+ PangoRectangle ink_rect;
+ ClutterVertex origin;
+
+ /* If the text is single line editable then it gets clipped to
+ the allocation anyway so we can just use that */
+ if (priv->editable && priv->single_line_mode)
+ return _clutter_actor_set_default_paint_volume (self,
+ CLUTTER_TYPE_TEXT,
+ volume);
+
+ if (G_OBJECT_TYPE (self) != CLUTTER_TYPE_TEXT)
+ return FALSE;
+
+ if (!clutter_actor_has_allocation (self))
+ return FALSE;
+
+ _clutter_paint_volume_init_static (self, &priv->paint_volume);
+
+ layout = clutter_text_get_layout (text);
+ pango_layout_get_extents (layout, &ink_rect, NULL);
+
+ origin.x = ink_rect.x / (float) PANGO_SCALE;
+ origin.y = ink_rect.y / (float) PANGO_SCALE;
+ origin.z = 0;
+ clutter_paint_volume_set_origin (&priv->paint_volume, &origin);
+ clutter_paint_volume_set_width (&priv->paint_volume,
+ ink_rect.width / (float) PANGO_SCALE);
+ clutter_paint_volume_set_height (&priv->paint_volume,
+ ink_rect.height / (float) PANGO_SCALE);
+
+ /* If the cursor is visible then that will likely be drawn
+ outside of the ink rectangle so we should merge that in */
+ if (priv->editable && priv->cursor_visible && priv->has_focus)
+ {
+ ClutterPaintVolume cursor_paint_volume;
+
+ _clutter_paint_volume_init_static (self,
+ &cursor_paint_volume);
+
+ clutter_text_get_paint_volume_for_cursor (text, &cursor_paint_volume);
+
+ clutter_paint_volume_union (&priv->paint_volume,
+ &cursor_paint_volume);
+
+ clutter_paint_volume_free (&cursor_paint_volume);
+ }
+
+ priv->paint_volume_valid = TRUE;
+ }
+
+ _clutter_paint_volume_copy_static (&priv->paint_volume, volume);
+
+ return TRUE;
}
static void
@@ -2073,7 +2253,7 @@ clutter_text_key_focus_in (ClutterActor *actor)
priv->has_focus = TRUE;
- clutter_actor_queue_redraw (actor);
+ clutter_text_queue_redraw (actor);
}
static void
@@ -2083,7 +2263,7 @@ clutter_text_key_focus_out (ClutterActor *actor)
priv->has_focus = FALSE;
- clutter_actor_queue_redraw (actor);
+ clutter_text_queue_redraw (actor);
}
static gboolean
@@ -3323,7 +3503,7 @@ clutter_text_set_editable (ClutterText *self,
{
priv->editable = editable;
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EDITABLE]);
}
@@ -3373,7 +3553,7 @@ clutter_text_set_selectable (ClutterText *self,
{
priv->selectable = selectable;
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTABLE]);
}
@@ -3427,7 +3607,7 @@ clutter_text_set_activatable (ClutterText *self,
{
priv->activatable = activatable;
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIVATABLE]);
}
@@ -3518,7 +3698,7 @@ clutter_text_set_cursor_visible (ClutterText *self,
{
priv->cursor_visible = cursor_visible;
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_VISIBLE]);
}
@@ -3572,7 +3752,7 @@ clutter_text_set_cursor_color (ClutterText *self,
else
priv->cursor_color_set = FALSE;
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_COLOR]);
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_COLOR_SET]);
@@ -3714,7 +3894,7 @@ clutter_text_set_selection_bound (ClutterText *self,
else
priv->selection_bound = selection_bound;
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
}
@@ -3770,7 +3950,7 @@ clutter_text_set_selection_color (ClutterText *self,
else
priv->selection_color_set = FALSE;
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_COLOR]);
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_COLOR_SET]);
@@ -4115,7 +4295,7 @@ clutter_text_set_color (ClutterText *self,
priv->text_color = *color;
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_COLOR]);
}
@@ -4589,7 +4769,7 @@ clutter_text_set_cursor_position (ClutterText *self,
time the cursor is moved up or down */
priv->x_pos = -1;
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POSITION]);
}
@@ -4623,7 +4803,7 @@ clutter_text_set_cursor_size (ClutterText *self,
priv->cursor_size = size;
- clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ clutter_text_queue_redraw (CLUTTER_ACTOR (self));
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_SIZE]);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]