From 93ea1681bb21704a51370c0a2e7bc8f791a38d5a Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 27 Nov 2008 16:44:39 +0000 Subject: [PATCH 001/147] Use a GArray for the texture vertices in cogl_texture_polygon Previously it was a dynamic array that was manually reallocated. --- clutter/cogl/gl/cogl-context.c | 11 +++++++---- clutter/cogl/gl/cogl-context.h | 3 +-- clutter/cogl/gl/cogl-texture.c | 32 +++++++++----------------------- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/clutter/cogl/gl/cogl-context.c b/clutter/cogl/gl/cogl-context.c index a9240a102..006506ac9 100644 --- a/clutter/cogl/gl/cogl-context.c +++ b/clutter/cogl/gl/cogl-context.c @@ -57,9 +57,9 @@ cogl_create_context () _context->path_nodes_size = 0; _context->texture_handles = NULL; - _context->texture_vertices_size = 0; - _context->texture_vertices = NULL; - + _context->texture_vertices = g_array_new (FALSE, FALSE, + sizeof (CoglTextureGLVertex)); + _context->fbo_handles = NULL; _context->draw_buffer = COGL_WINDOW_BUFFER; @@ -140,7 +140,10 @@ cogl_destroy_context () g_array_free (_context->shader_handles, TRUE); if (_context->program_handles) g_array_free (_context->program_handles, TRUE); - + + if (_context->texture_vertices) + g_array_free (_context->texture_vertices, TRUE); + g_free (_context); } diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h index 5ab583862..f1fd2e786 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -64,8 +64,7 @@ typedef struct /* Textures */ GArray *texture_handles; - CoglTextureGLVertex *texture_vertices; - gulong texture_vertices_size; + GArray *texture_vertices; /* Framebuffer objects */ GArray *fbo_handles; diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index bf2eef304..01613c4bd 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -2252,23 +2252,9 @@ cogl_texture_polygon (CoglHandle handle, /* Make sure there is enough space in the global texture vertex array. This is used so we can render the polygon with a single call to OpenGL but still support any number of vertices */ - if (ctx->texture_vertices_size < n_vertices) - { - guint nsize = ctx->texture_vertices_size; - - if (nsize == 0) - nsize = 1; - do - nsize *= 2; - while (nsize < n_vertices); - - ctx->texture_vertices_size = nsize; + g_array_set_size (ctx->texture_vertices, n_vertices); + p = (CoglTextureGLVertex *) ctx->texture_vertices->data; - ctx->texture_vertices = g_realloc (ctx->texture_vertices, - nsize - * sizeof (CoglTextureGLVertex)); - } - /* Prepare GL state */ enable_flags = (COGL_ENABLE_TEXTURE_2D | COGL_ENABLE_VERTEX_ARRAY @@ -2282,14 +2268,12 @@ cogl_texture_polygon (CoglHandle handle, if (use_color) { enable_flags |= COGL_ENABLE_COLOR_ARRAY; - GE( glColorPointer (4, GL_UNSIGNED_BYTE, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].c) ); + GE( glColorPointer (4, GL_UNSIGNED_BYTE, + sizeof (CoglTextureGLVertex), p->c) ); } - GE( glVertexPointer (3, GL_FLOAT, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].v) ); - GE( glTexCoordPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].t) ); + GE( glVertexPointer (3, GL_FLOAT, sizeof (CoglTextureGLVertex), p->v ) ); + GE( glTexCoordPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), p->t ) ); cogl_enable (enable_flags); @@ -2320,9 +2304,11 @@ cogl_texture_polygon (CoglHandle handle, gl_handle = g_array_index (tex->slice_gl_handles, GLuint, tex_num++); + p = (CoglTextureGLVertex *) ctx->texture_vertices->data; + /* Convert the vertices into an array of GLfloats ready to pass to OpenGL */ - for (i = 0, p = ctx->texture_vertices; i < n_vertices; i++, p++) + for (i = 0; i < n_vertices; i++, p++) { #define CFX_F COGL_FIXED_TO_FLOAT From 10942e8e049a4bfdd3df5abd5036c63f5676df92 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 28 Nov 2008 12:33:19 +0000 Subject: [PATCH 002/147] Make cogl_texture_rectangle use the vertex array cogl_texture_quad_hw and _sw now just add vertices to the vertex array. The last texture used is stored and if a different texture is encountered then flushes the vertices. cogl_texture_rectangle always flushes the vertices after calling either of the functions. --- clutter/cogl/gl/cogl-context.h | 7 +- clutter/cogl/gl/cogl-texture.c | 201 ++++++++++++++++++--------------- 2 files changed, 119 insertions(+), 89 deletions(-) diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h index f1fd2e786..ffec47464 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -65,7 +65,12 @@ typedef struct /* Textures */ GArray *texture_handles; GArray *texture_vertices; - + /* The gl texture number that the above vertices apply to. This to + detect when a different slice is encountered so that the vertices + can be flushed */ + GLuint texture_current; + GLenum texture_target; + /* Framebuffer objects */ GArray *fbo_handles; CoglBufferTarget draw_buffer; diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index 01613c4bd..fe7208d81 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -1906,6 +1906,28 @@ cogl_texture_get_data (CoglHandle handle, return byte_size; } +static void +_cogl_texture_flush_vertices (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->texture_vertices > 0) + { + CoglTextureGLVertex *p + = (CoglTextureGLVertex *) ctx->texture_vertices->data; + + GE( glVertexPointer (2, GL_FLOAT, + sizeof (CoglTextureGLVertex), p->v ) ); + GE( glTexCoordPointer (2, GL_FLOAT, + sizeof (CoglTextureGLVertex), p->t ) ); + + GE( glBindTexture (ctx->texture_target, ctx->texture_current) ); + GE( glDrawArrays (GL_QUADS, 0, ctx->texture_vertices->len) ); + + g_array_set_size (ctx->texture_vertices, 0); + } +} + static void _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed x1, @@ -1917,40 +1939,23 @@ _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed tx2, CoglFixed ty2) { - CoglSpanIter iter_x , iter_y; - CoglFixed tw , th; - CoglFixed tqx , tqy; - CoglFixed first_tx , first_ty; - CoglFixed first_qx , first_qy; - CoglFixed slice_tx1 , slice_ty1; - CoglFixed slice_tx2 , slice_ty2; - CoglFixed slice_qx1 , slice_qy1; - CoglFixed slice_qx2 , slice_qy2; - GLfloat tex_coords[8]; - GLfloat quad_coords[8]; - GLuint gl_handle; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); - + CoglSpanIter iter_x , iter_y; + CoglFixed tw , th; + CoglFixed tqx , tqy; + CoglFixed first_tx , first_ty; + CoglFixed first_qx , first_qy; + CoglFixed slice_tx1 , slice_ty1; + CoglFixed slice_tx2 , slice_ty2; + CoglFixed slice_qx1 , slice_qy1; + CoglFixed slice_qx2 , slice_qy2; + GLuint gl_handle; + CoglTextureGLVertex *p; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); #if COGL_DEBUG printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); #endif - - - /* Prepare GL state */ - if (ctx->color_alpha < 255 - || tex->bitmap.format & COGL_A_BIT) - { - enable_flags |= COGL_ENABLE_BLEND; - } - - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; - - cogl_enable (enable_flags); /* If the texture coordinates are backwards then swap both the geometry and texture coordinates so that the texture will be @@ -1974,10 +1979,7 @@ _cogl_texture_quad_sw (CoglTexture *tex, ty1 = ty2; ty2 = temp; } - - GE( glTexCoordPointer (2, GL_FLOAT, 0, tex_coords) ); - GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords) ); - + /* Scale ratio from texture to quad widths */ tw = COGL_FIXED_FROM_INT (tex->bitmap.width); th = COGL_FIXED_FROM_INT (tex->bitmap.height); @@ -2069,23 +2071,35 @@ _cogl_texture_quad_sw (CoglTexture *tex, gl_handle = g_array_index (tex->slice_gl_handles, GLuint, iter_y.index * iter_x.array->len + iter_x.index); - - GE( glBindTexture (tex->gl_target, gl_handle) ); -#define CFX_F COGL_FIXED_TO_FLOAT - - /* Draw textured quad */ - tex_coords[0] = CFX_F(slice_tx1); tex_coords[1] = CFX_F(slice_ty2); - tex_coords[2] = CFX_F(slice_tx2); tex_coords[3] = CFX_F(slice_ty2); - tex_coords[4] = CFX_F(slice_tx1); tex_coords[5] = CFX_F(slice_ty1); - tex_coords[6] = CFX_F(slice_tx2); tex_coords[7] = CFX_F(slice_ty1); + /* If we're using a different texture from the one already queued + then flush the vertices */ + if (ctx->texture_vertices->len > 0 + && gl_handle != ctx->texture_current) + _cogl_texture_flush_vertices (); + ctx->texture_target = tex->gl_target; + ctx->texture_current = gl_handle; - quad_coords[0] = CFX_F(slice_qx1); quad_coords[1] = CFX_F(slice_qy2); - quad_coords[2] = CFX_F(slice_qx2); quad_coords[3] = CFX_F(slice_qy2); - quad_coords[4] = CFX_F(slice_qx1); quad_coords[5] = CFX_F(slice_qy1); - quad_coords[6] = CFX_F(slice_qx2); quad_coords[7] = CFX_F(slice_qy1); + /* Add the quad to the list of queued vertices */ + g_array_set_size (ctx->texture_vertices, + ctx->texture_vertices->len + 4); + p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, + ctx->texture_vertices->len - 4); - GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +#define CFX_F(x) COGL_FIXED_TO_FLOAT(x) + + p->v[0] = CFX_F (slice_qx1); p->v[1] = CFX_F (slice_qy2); + p->t[0] = CFX_F (slice_tx1); p->t[1] = CFX_F (slice_ty2); + p++; + p->v[0] = CFX_F (slice_qx2); p->v[1] = CFX_F (slice_qy2); + p->t[0] = CFX_F (slice_tx2); p->t[1] = CFX_F (slice_ty2); + p++; + p->v[0] = CFX_F (slice_qx2); p->v[1] = CFX_F (slice_qy1); + p->t[0] = CFX_F (slice_tx2); p->t[1] = CFX_F (slice_ty1); + p++; + p->v[0] = CFX_F (slice_qx1); p->v[1] = CFX_F (slice_qy1); + p->t[0] = CFX_F (slice_tx1); p->t[1] = CFX_F (slice_ty1); + p++; #undef CFX_F } @@ -2103,40 +2117,27 @@ _cogl_texture_quad_hw (CoglTexture *tex, CoglFixed tx2, CoglFixed ty2) { - GLfloat tex_coords[8]; - GLfloat quad_coords[8]; - GLuint gl_handle; - CoglTexSliceSpan *x_span; - CoglTexSliceSpan *y_span; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); + GLuint gl_handle; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + CoglTextureGLVertex *p; #if COGL_DEBUG printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); #endif - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - /* Prepare GL state */ - if (ctx->color_alpha < 255 - || tex->bitmap.format & COGL_A_BIT) - { - enable_flags |= COGL_ENABLE_BLEND; - } - - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; - cogl_enable (enable_flags); - - GE( glTexCoordPointer (2, GL_FLOAT, 0, tex_coords) ); - GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords) ); - + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* Pick and bind opengl texture object */ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); - GE( glBindTexture (tex->gl_target, gl_handle) ); - + + /* If we're using a different texture from the one already queued + then flush the vertices */ + if (ctx->texture_vertices->len > 0 && gl_handle != ctx->texture_current) + _cogl_texture_flush_vertices (); + ctx->texture_target = tex->gl_target; + ctx->texture_current = gl_handle; + /* Don't include the waste in the texture coordinates */ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); @@ -2147,20 +2148,25 @@ _cogl_texture_quad_hw (CoglTexture *tex, ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; + /* Add the quad to the list of queued vertices */ + g_array_set_size (ctx->texture_vertices, ctx->texture_vertices->len + 4); + p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, + ctx->texture_vertices->len - 4); + #define CFX_F(x) COGL_FIXED_TO_FLOAT(x) - - /* Draw textured quad */ - tex_coords[0] = CFX_F(tx1); tex_coords[1] = CFX_F(ty2); - tex_coords[2] = CFX_F(tx2); tex_coords[3] = CFX_F(ty2); - tex_coords[4] = CFX_F(tx1); tex_coords[5] = CFX_F(ty1); - tex_coords[6] = CFX_F(tx2); tex_coords[7] = CFX_F(ty1); - quad_coords[0] = CFX_F(x1); quad_coords[1] = CFX_F(y2); - quad_coords[2] = CFX_F(x2); quad_coords[3] = CFX_F(y2); - quad_coords[4] = CFX_F(x1); quad_coords[5] = CFX_F(y1); - quad_coords[6] = CFX_F(x2); quad_coords[7] = CFX_F(y1); - - GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); + p->v[0] = CFX_F (x1); p->v[1] = CFX_F (y2); + p->t[0] = CFX_F (tx1); p->t[1] = CFX_F (ty2); + p++; + p->v[0] = CFX_F (x2); p->v[1] = CFX_F (y2); + p->t[0] = CFX_F (tx2); p->t[1] = CFX_F (ty2); + p++; + p->v[0] = CFX_F (x2); p->v[1] = CFX_F (y1); + p->t[0] = CFX_F (tx2); p->t[1] = CFX_F (ty1); + p++; + p->v[0] = CFX_F (x1); p->v[1] = CFX_F (y1); + p->t[0] = CFX_F (tx1); p->t[1] = CFX_F (ty1); + p++; #undef CFX_F } @@ -2176,8 +2182,13 @@ cogl_texture_rectangle (CoglHandle handle, CoglFixed tx2, CoglFixed ty2) { - CoglTexture *tex; - + CoglTexture *tex; + gulong enable_flags = (COGL_ENABLE_TEXTURE_2D + | COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* Check if valid texture */ if (!cogl_is_texture (handle)) return; @@ -2194,6 +2205,18 @@ cogl_texture_rectangle (CoglHandle handle, if (tx1 == tx2 || ty1 == ty2) return; + /* Prepare GL state */ + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + enable_flags |= COGL_ENABLE_BLEND; + + if (ctx->enable_backface_culling) + enable_flags |= COGL_ENABLE_BACKFACE_CULLING; + + cogl_enable (enable_flags); + + g_array_set_size (ctx->texture_vertices, 0); + /* If there is only one GL texture and either the texture is NPOT (no waste) or all of the coordinates are in the range [0,1] then we can use hardware tiling */ @@ -2206,6 +2229,8 @@ cogl_texture_rectangle (CoglHandle handle, _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); else _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + + _cogl_texture_flush_vertices (); } void From b312cd2d5560de953cfc0438353f9f6d8e3cae9a Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 28 Nov 2008 12:44:21 +0000 Subject: [PATCH 003/147] Add cogl_texture_multiple_rectangles This takes an array of sets of 8 floats to describe the rectangles. It tries to send the geometry with a single glDrawArrays as far as possible. cogl_texture_rectangle is now just a wrapper around cogl_texture_multiple_rectangles. --- clutter/cogl/cogl-texture.h | 5 +++ clutter/cogl/gl/cogl-texture.c | 74 +++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/clutter/cogl/cogl-texture.h b/clutter/cogl/cogl-texture.h index 117a12b14..5df2a818d 100644 --- a/clutter/cogl/cogl-texture.h +++ b/clutter/cogl/cogl-texture.h @@ -385,6 +385,11 @@ void cogl_texture_polygon (CoglHandle handle, CoglTextureVertex *vertices, gboolean use_color); +void cogl_texture_multiple_rectangles + (CoglHandle handle, + const CoglFixed *verts, + guint n_rects); + G_END_DECLS #endif /* __COGL_TEXTURE_H__ */ diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index fe7208d81..cd241ff80 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -2172,15 +2172,9 @@ _cogl_texture_quad_hw (CoglTexture *tex, } void -cogl_texture_rectangle (CoglHandle handle, - CoglFixed x1, - CoglFixed y1, - CoglFixed x2, - CoglFixed y2, - CoglFixed tx1, - CoglFixed ty1, - CoglFixed tx2, - CoglFixed ty2) +cogl_texture_multiple_rectangles (CoglHandle handle, + const CoglFixed *verts, + guint n_rects) { CoglTexture *tex; gulong enable_flags = (COGL_ENABLE_TEXTURE_2D @@ -2202,9 +2196,6 @@ cogl_texture_rectangle (CoglHandle handle, if (tex->slice_gl_handles->len == 0) return; - if (tx1 == tx2 || ty1 == ty2) - return; - /* Prepare GL state */ if (ctx->color_alpha < 255 || tex->bitmap.format & COGL_A_BIT) @@ -2217,22 +2208,57 @@ cogl_texture_rectangle (CoglHandle handle, g_array_set_size (ctx->texture_vertices, 0); - /* If there is only one GL texture and either the texture is NPOT - (no waste) or all of the coordinates are in the range [0,1] then - we can use hardware tiling */ - if (tex->slice_gl_handles->len == 1 - && (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) - || (tx1 >= 0 && tx1 <= COGL_FIXED_1 - && tx2 >= 0 && tx2 <= COGL_FIXED_1 - && ty1 >= 0 && ty1 <= COGL_FIXED_1 - && ty2 >= 0 && ty2 <= COGL_FIXED_1))) - _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); - else - _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + while (n_rects-- > 0) + { + if (verts[4] != verts[6] && verts[5] != verts[7]) + { + /* If there is only one GL texture and either the texture is + NPOT (no waste) or all of the coordinates are in the + range [0,1] then we can use hardware tiling */ + if (tex->slice_gl_handles->len == 1 + && (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + || (verts[4] >= 0 && verts[4] <= COGL_FIXED_1 + && verts[6] >= 0 && verts[6] <= COGL_FIXED_1 + && verts[5] >= 0 && verts[5] <= COGL_FIXED_1 + && verts[7] >= 0 && verts[7] <= COGL_FIXED_1))) + _cogl_texture_quad_hw (tex, verts[0],verts[1], verts[2],verts[3], + verts[4],verts[5], verts[6],verts[7]); + else + _cogl_texture_quad_sw (tex, verts[0],verts[1], verts[2],verts[3], + verts[4],verts[5], verts[6],verts[7]); + } + + verts += 8; + } _cogl_texture_flush_vertices (); } +void +cogl_texture_rectangle (CoglHandle handle, + CoglFixed x1, + CoglFixed y1, + CoglFixed x2, + CoglFixed y2, + CoglFixed tx1, + CoglFixed ty1, + CoglFixed tx2, + CoglFixed ty2) +{ + CoglFixed verts[8]; + + verts[0] = x1; + verts[1] = y1; + verts[2] = x2; + verts[3] = y2; + verts[4] = tx1; + verts[5] = ty1; + verts[6] = tx2; + verts[7] = ty2; + + cogl_texture_multiple_rectangles (handle, verts, 1); +} + void cogl_texture_polygon (CoglHandle handle, guint n_vertices, From ab347481aeccd28b0986fbc991c96d72bbc3f072 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 28 Nov 2008 13:14:10 +0000 Subject: [PATCH 004/147] Use cogl_texture_multiple_rectangles in CoglPangoRenderer The glyphs are queued into an array of rectangles instead of being drawn directly. Whenever a different texture is used or the sequence is complete the array is flushed. This is based on a patch from Owen Taylor. --- clutter/pango/cogl-pango-render.c | 78 +++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/clutter/pango/cogl-pango-render.c b/clutter/pango/cogl-pango-render.c index bcf3b11fc..e69c412ac 100644 --- a/clutter/pango/cogl-pango-render.c +++ b/clutter/pango/cogl-pango-render.c @@ -51,6 +51,10 @@ struct _CoglPangoRenderer CoglPangoGlyphCache *mipmapped_glyph_cache; gboolean use_mipmapping; + + /* Array of rectangles to draw from the current texture */ + GArray *glyph_rectangles; + CoglHandle glyph_texture; }; struct _CoglPangoRendererClass @@ -58,6 +62,46 @@ struct _CoglPangoRendererClass PangoRendererClass class_instance; }; +static void +cogl_pango_renderer_glyphs_end (CoglPangoRenderer *priv) +{ + if (priv->glyph_rectangles->len > 0) + { + CoglFixed *rectangles = (CoglFixed *) priv->glyph_rectangles->data; + cogl_texture_multiple_rectangles (priv->glyph_texture, rectangles, + priv->glyph_rectangles->len / 8); + g_array_set_size (priv->glyph_rectangles, 0); + } +} + +static void +cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv, + CoglPangoGlyphCacheValue *cache_value, + CoglFixed x1, + CoglFixed y1) +{ + CoglFixed x2, y2; + CoglFixed *p; + + if (priv->glyph_rectangles->len > 0 + && priv->glyph_texture != cache_value->texture) + cogl_pango_renderer_glyphs_end (priv); + + priv->glyph_texture = cache_value->texture; + + x2 = x1 + CLUTTER_INT_TO_FIXED (cache_value->draw_width); + y2 = y1 + CLUTTER_INT_TO_FIXED (cache_value->draw_height); + + g_array_set_size (priv->glyph_rectangles, priv->glyph_rectangles->len + 8); + p = &g_array_index (priv->glyph_rectangles, CoglFixed, + priv->glyph_rectangles->len - 8); + + *(p++) = x1; *(p++) = y1; + *(p++) = x2; *(p++) = y2; + *(p++) = cache_value->tx1; *(p++) = cache_value->ty1; + *(p++) = cache_value->tx2; *(p++) = cache_value->ty2; +} + #define COGL_PANGO_UNIT_TO_FIXED(x) ((x) << (COGL_FIXED_Q - 10)) static void cogl_pango_renderer_finalize (GObject *object); @@ -89,6 +133,7 @@ cogl_pango_renderer_init (CoglPangoRenderer *priv) priv->glyph_cache = cogl_pango_glyph_cache_new (FALSE); priv->mipmapped_glyph_cache = cogl_pango_glyph_cache_new (TRUE); priv->use_mipmapping = FALSE; + priv->glyph_rectangles = g_array_new (FALSE, FALSE, sizeof (CoglFixed)); } static void @@ -111,6 +156,7 @@ cogl_pango_renderer_finalize (GObject *object) cogl_pango_glyph_cache_free (priv->mipmapped_glyph_cache); cogl_pango_glyph_cache_free (priv->glyph_cache); + g_array_free (priv->glyph_rectangles, TRUE); G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->finalize (object); } @@ -376,10 +422,10 @@ cogl_pango_renderer_draw_box (int x, int y, static void cogl_pango_renderer_get_device_units (PangoRenderer *renderer, - int xin, - int yin, - CoglFixed *xout, - CoglFixed *yout) + int xin, + int yin, + CoglFixed *xout, + CoglFixed *yout) { const PangoMatrix *matrix; @@ -454,6 +500,7 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, int xi, int yi) { + CoglPangoRenderer *priv = (CoglPangoRenderer *) renderer; CoglPangoGlyphCacheValue *cache_value; int i; @@ -474,6 +521,8 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, { PangoFontMetrics *metrics; + cogl_pango_renderer_glyphs_end (priv); + if (font == NULL || (metrics = pango_font_get_metrics (font, NULL)) == NULL) { @@ -503,10 +552,14 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, gi->glyph); if (cache_value == NULL) - cogl_pango_renderer_draw_box (COGL_FIXED_TO_INT (x), - COGL_FIXED_TO_INT (y), - PANGO_UNKNOWN_GLYPH_WIDTH, - PANGO_UNKNOWN_GLYPH_HEIGHT); + { + cogl_pango_renderer_glyphs_end (priv); + + cogl_pango_renderer_draw_box (COGL_FIXED_TO_INT (x), + COGL_FIXED_TO_INT (y), + PANGO_UNKNOWN_GLYPH_WIDTH, + PANGO_UNKNOWN_GLYPH_HEIGHT); + } else { CoglFixed width, height; @@ -517,15 +570,12 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, width = x + COGL_FIXED_FROM_INT (cache_value->draw_width); height = y + COGL_FIXED_FROM_INT (cache_value->draw_height); - /* Render the glyph from the texture */ - cogl_texture_rectangle (cache_value->texture, - x, y, - width, height, - cache_value->tx1, cache_value->ty1, - cache_value->tx2, cache_value->ty2); + cogl_pango_renderer_draw_glyph (priv, cache_value, x, y); } } xi += gi->geometry.width; } + + cogl_pango_renderer_glyphs_end (priv); } From 29505dd0ba651df08dcfb184f0b08efa5b4eddfe Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 28 Nov 2008 14:20:07 +0000 Subject: [PATCH 005/147] Use GL_TRIANGLES for the texture vertex array Most cards don't actually support GL_QUADS and they are deprecated in GL 3.0 so there is a chance it will perform faster with GL_TRIANGLES even though it has to submit two extra vertices. --- clutter/cogl/gl/cogl-texture.c | 36 +++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index cd241ff80..6031305e1 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -1922,7 +1922,7 @@ _cogl_texture_flush_vertices (void) sizeof (CoglTextureGLVertex), p->t ) ); GE( glBindTexture (ctx->texture_target, ctx->texture_current) ); - GE( glDrawArrays (GL_QUADS, 0, ctx->texture_vertices->len) ); + GE( glDrawArrays (GL_TRIANGLES, 0, ctx->texture_vertices->len) ); g_array_set_size (ctx->texture_vertices, 0); } @@ -2082,9 +2082,9 @@ _cogl_texture_quad_sw (CoglTexture *tex, /* Add the quad to the list of queued vertices */ g_array_set_size (ctx->texture_vertices, - ctx->texture_vertices->len + 4); + ctx->texture_vertices->len + 6); p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, - ctx->texture_vertices->len - 4); + ctx->texture_vertices->len - 6); #define CFX_F(x) COGL_FIXED_TO_FLOAT(x) @@ -2094,13 +2094,20 @@ _cogl_texture_quad_sw (CoglTexture *tex, p->v[0] = CFX_F (slice_qx2); p->v[1] = CFX_F (slice_qy2); p->t[0] = CFX_F (slice_tx2); p->t[1] = CFX_F (slice_ty2); p++; - p->v[0] = CFX_F (slice_qx2); p->v[1] = CFX_F (slice_qy1); - p->t[0] = CFX_F (slice_tx2); p->t[1] = CFX_F (slice_ty1); - p++; p->v[0] = CFX_F (slice_qx1); p->v[1] = CFX_F (slice_qy1); p->t[0] = CFX_F (slice_tx1); p->t[1] = CFX_F (slice_ty1); p++; + p->v[0] = CFX_F (slice_qx1); p->v[1] = CFX_F (slice_qy1); + p->t[0] = CFX_F (slice_tx1); p->t[1] = CFX_F (slice_ty1); + p++; + p->v[0] = CFX_F (slice_qx2); p->v[1] = CFX_F (slice_qy2); + p->t[0] = CFX_F (slice_tx2); p->t[1] = CFX_F (slice_ty2); + p++; + p->v[0] = CFX_F (slice_qx2); p->v[1] = CFX_F (slice_qy1); + p->t[0] = CFX_F (slice_tx2); p->t[1] = CFX_F (slice_ty1); + p++; + #undef CFX_F } } @@ -2149,9 +2156,9 @@ _cogl_texture_quad_hw (CoglTexture *tex, ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; /* Add the quad to the list of queued vertices */ - g_array_set_size (ctx->texture_vertices, ctx->texture_vertices->len + 4); + g_array_set_size (ctx->texture_vertices, ctx->texture_vertices->len + 6); p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, - ctx->texture_vertices->len - 4); + ctx->texture_vertices->len - 6); #define CFX_F(x) COGL_FIXED_TO_FLOAT(x) @@ -2161,13 +2168,20 @@ _cogl_texture_quad_hw (CoglTexture *tex, p->v[0] = CFX_F (x2); p->v[1] = CFX_F (y2); p->t[0] = CFX_F (tx2); p->t[1] = CFX_F (ty2); p++; - p->v[0] = CFX_F (x2); p->v[1] = CFX_F (y1); - p->t[0] = CFX_F (tx2); p->t[1] = CFX_F (ty1); - p++; p->v[0] = CFX_F (x1); p->v[1] = CFX_F (y1); p->t[0] = CFX_F (tx1); p->t[1] = CFX_F (ty1); p++; + p->v[0] = CFX_F (x1); p->v[1] = CFX_F (y1); + p->t[0] = CFX_F (tx1); p->t[1] = CFX_F (ty1); + p++; + p->v[0] = CFX_F (x2); p->v[1] = CFX_F (y2); + p->t[0] = CFX_F (tx2); p->t[1] = CFX_F (ty2); + p++; + p->v[0] = CFX_F (x2); p->v[1] = CFX_F (y1); + p->t[0] = CFX_F (tx2); p->t[1] = CFX_F (ty1); + p++; + #undef CFX_F } From e3f077d55f323d71efed19def7f6a740ff78c2ec Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 4 Dec 2008 17:50:03 +0000 Subject: [PATCH 006/147] Use the correct length in _cogl_texture_flush_vertices The check for whether there are any rectangles to flush was using the wrong value so it would always flush. Thanks to Johan Bilien for spotting. --- clutter/cogl/gl/cogl-texture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index 6031305e1..471bf4776 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -1911,7 +1911,7 @@ _cogl_texture_flush_vertices (void) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (ctx->texture_vertices > 0) + if (ctx->texture_vertices->len > 0) { CoglTextureGLVertex *p = (CoglTextureGLVertex *) ctx->texture_vertices->data; From bf72b8cdc3c43221e0813475957712f3ad9c8265 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 9 Dec 2008 13:02:28 +0000 Subject: [PATCH 007/147] Use glDrawRangeElements to share vertices When drawing a quad from cogl_texture_multiple_rectangles share two of the vertices in each triangle by using indices. --- clutter/cogl/gl/cogl-context.c | 4 + clutter/cogl/gl/cogl-context.h | 1 + clutter/cogl/gl/cogl-texture.c | 157 +++++++++++++++++---------------- 3 files changed, 88 insertions(+), 74 deletions(-) diff --git a/clutter/cogl/gl/cogl-context.c b/clutter/cogl/gl/cogl-context.c index 8a12e9572..dbb911024 100644 --- a/clutter/cogl/gl/cogl-context.c +++ b/clutter/cogl/gl/cogl-context.c @@ -58,6 +58,8 @@ cogl_create_context () _context->texture_handles = NULL; _context->texture_vertices = g_array_new (FALSE, FALSE, sizeof (CoglTextureGLVertex)); + _context->texture_indices = g_array_new (FALSE, FALSE, + sizeof (GLushort)); _context->fbo_handles = NULL; _context->draw_buffer = COGL_WINDOW_BUFFER; @@ -150,6 +152,8 @@ cogl_destroy_context () if (_context->texture_vertices) g_array_free (_context->texture_vertices, TRUE); + if (_context->texture_indices) + g_array_free (_context->texture_indices, TRUE); g_free (_context); } diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h index fd6e1b918..edabaca50 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -64,6 +64,7 @@ typedef struct /* Textures */ GArray *texture_handles; GArray *texture_vertices; + GArray *texture_indices; /* The gl texture number that the above vertices apply to. This to detect when a different slice is encountered so that the vertices can be flushed */ diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index 8f1dc6ded..4397da7f1 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -1927,12 +1927,64 @@ _cogl_texture_flush_vertices (void) sizeof (CoglTextureGLVertex), p->t ) ); GE( glBindTexture (ctx->texture_target, ctx->texture_current) ); - GE( glDrawArrays (GL_TRIANGLES, 0, ctx->texture_vertices->len) ); + GE( ctx->pf_glDrawRangeElements (GL_TRIANGLES, + 0, ctx->texture_vertices->len - 1, + ctx->texture_indices->len, + GL_UNSIGNED_SHORT, + ctx->texture_indices->data) ); g_array_set_size (ctx->texture_vertices, 0); + g_array_set_size (ctx->texture_indices, 0); } } +static void +_cogl_texture_add_quad_vertices (GLfloat x1, GLfloat y1, + GLfloat x2, GLfloat y2, + GLfloat tx1, GLfloat ty1, + GLfloat tx2, GLfloat ty2) +{ + CoglTextureGLVertex *p; + GLushort first_vert; + GLushort *q; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Add the four vertices of the quad to the list of queued + vertices */ + first_vert = ctx->texture_vertices->len; + g_array_set_size (ctx->texture_vertices, first_vert + 4); + p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, first_vert); + + p->v[0] = x1; p->v[1] = y1; + p->t[0] = tx1; p->t[1] = ty1; + p++; + p->v[0] = x1; p->v[1] = y2; + p->t[0] = tx1; p->t[1] = ty2; + p++; + p->v[0] = x2; p->v[1] = y2; + p->t[0] = tx2; p->t[1] = ty2; + p++; + p->v[0] = x2; p->v[1] = y1; + p->t[0] = tx2; p->t[1] = ty1; + p++; + + /* Add two triangles to the list of indices. That makes six new + indices but two of the vertices in the triangles are shared. */ + g_array_set_size (ctx->texture_indices, + ctx->texture_indices->len + 6); + q = &g_array_index (ctx->texture_indices, GLushort, + ctx->texture_indices->len - 6); + + *(q++) = first_vert + 0; + *(q++) = first_vert + 1; + *(q++) = first_vert + 3; + + *(q++) = first_vert + 1; + *(q++) = first_vert + 2; + *(q++) = first_vert + 3; +} + static void _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed x1, @@ -1944,17 +1996,16 @@ _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed tx2, CoglFixed ty2) { - CoglSpanIter iter_x , iter_y; - CoglFixed tw , th; - CoglFixed tqx , tqy; - CoglFixed first_tx , first_ty; - CoglFixed first_qx , first_qy; - CoglFixed slice_tx1 , slice_ty1; - CoglFixed slice_tx2 , slice_ty2; - CoglFixed slice_qx1 , slice_qy1; - CoglFixed slice_qx2 , slice_qy2; - GLuint gl_handle; - CoglTextureGLVertex *p; + CoglSpanIter iter_x , iter_y; + CoglFixed tw , th; + CoglFixed tqx , tqy; + CoglFixed first_tx , first_ty; + CoglFixed first_qx , first_qy; + CoglFixed slice_tx1 , slice_ty1; + CoglFixed slice_tx2 , slice_ty2; + CoglFixed slice_qx1 , slice_qy1; + CoglFixed slice_qx2 , slice_qy2; + GLuint gl_handle; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -2098,35 +2149,14 @@ _cogl_texture_quad_sw (CoglTexture *tex, ctx->texture_target = tex->gl_target; ctx->texture_current = gl_handle; - /* Add the quad to the list of queued vertices */ - g_array_set_size (ctx->texture_vertices, - ctx->texture_vertices->len + 6); - p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, - ctx->texture_vertices->len - 6); - -#define CFX_F(x) COGL_FIXED_TO_FLOAT(x) - - p->v[0] = CFX_F (slice_qx1); p->v[1] = CFX_F (slice_qy2); - p->t[0] = CFX_F (slice_tx1); p->t[1] = CFX_F (slice_ty2); - p++; - p->v[0] = CFX_F (slice_qx2); p->v[1] = CFX_F (slice_qy2); - p->t[0] = CFX_F (slice_tx2); p->t[1] = CFX_F (slice_ty2); - p++; - p->v[0] = CFX_F (slice_qx1); p->v[1] = CFX_F (slice_qy1); - p->t[0] = CFX_F (slice_tx1); p->t[1] = CFX_F (slice_ty1); - p++; - - p->v[0] = CFX_F (slice_qx1); p->v[1] = CFX_F (slice_qy1); - p->t[0] = CFX_F (slice_tx1); p->t[1] = CFX_F (slice_ty1); - p++; - p->v[0] = CFX_F (slice_qx2); p->v[1] = CFX_F (slice_qy2); - p->t[0] = CFX_F (slice_tx2); p->t[1] = CFX_F (slice_ty2); - p++; - p->v[0] = CFX_F (slice_qx2); p->v[1] = CFX_F (slice_qy1); - p->t[0] = CFX_F (slice_tx2); p->t[1] = CFX_F (slice_ty1); - p++; - -#undef CFX_F + _cogl_texture_add_quad_vertices (COGL_FIXED_TO_FLOAT (slice_qx1), + COGL_FIXED_TO_FLOAT (slice_qy1), + COGL_FIXED_TO_FLOAT (slice_qx2), + COGL_FIXED_TO_FLOAT (slice_qy2), + COGL_FIXED_TO_FLOAT (slice_tx1), + COGL_FIXED_TO_FLOAT (slice_ty1), + COGL_FIXED_TO_FLOAT (slice_tx2), + COGL_FIXED_TO_FLOAT (slice_ty2)); } } } @@ -2142,11 +2172,10 @@ _cogl_texture_quad_hw (CoglTexture *tex, CoglFixed tx2, CoglFixed ty2) { - GLuint gl_handle; - CoglTexSliceSpan *x_span; - CoglTexSliceSpan *y_span; - CoglTextureGLVertex *p; - GLenum wrap_mode; + GLuint gl_handle; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLenum wrap_mode; #if COGL_DEBUG printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); @@ -2190,11 +2219,6 @@ _cogl_texture_quad_hw (CoglTexture *tex, ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; - /* Add the quad to the list of queued vertices */ - g_array_set_size (ctx->texture_vertices, ctx->texture_vertices->len + 6); - p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, - ctx->texture_vertices->len - 6); - /* Denormalize texture coordinates for rectangle textures */ if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) { @@ -2204,29 +2228,14 @@ _cogl_texture_quad_hw (CoglTexture *tex, ty2 *= y_span->size; } -#define CFX_F(x) COGL_FIXED_TO_FLOAT(x) - - p->v[0] = CFX_F (x1); p->v[1] = CFX_F (y2); - p->t[0] = CFX_F (tx1); p->t[1] = CFX_F (ty2); - p++; - p->v[0] = CFX_F (x2); p->v[1] = CFX_F (y2); - p->t[0] = CFX_F (tx2); p->t[1] = CFX_F (ty2); - p++; - p->v[0] = CFX_F (x1); p->v[1] = CFX_F (y1); - p->t[0] = CFX_F (tx1); p->t[1] = CFX_F (ty1); - p++; - - p->v[0] = CFX_F (x1); p->v[1] = CFX_F (y1); - p->t[0] = CFX_F (tx1); p->t[1] = CFX_F (ty1); - p++; - p->v[0] = CFX_F (x2); p->v[1] = CFX_F (y2); - p->t[0] = CFX_F (tx2); p->t[1] = CFX_F (ty2); - p++; - p->v[0] = CFX_F (x2); p->v[1] = CFX_F (y1); - p->t[0] = CFX_F (tx2); p->t[1] = CFX_F (ty1); - p++; - -#undef CFX_F + _cogl_texture_add_quad_vertices (COGL_FIXED_TO_FLOAT (x1), + COGL_FIXED_TO_FLOAT (y1), + COGL_FIXED_TO_FLOAT (x2), + COGL_FIXED_TO_FLOAT (y2), + COGL_FIXED_TO_FLOAT (tx1), + COGL_FIXED_TO_FLOAT (ty1), + COGL_FIXED_TO_FLOAT (tx2), + COGL_FIXED_TO_FLOAT (ty2)); } void From 73974de482cff70c124aa49869b77099382591c3 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 9 Dec 2008 13:16:42 +0000 Subject: [PATCH 008/147] Add gtk-doc for cogl_texture_multiple_rectangles --- clutter/cogl/cogl-texture.h | 18 ++++++++++++++++++ doc/reference/cogl/cogl-sections.txt | 1 + 2 files changed, 19 insertions(+) diff --git a/clutter/cogl/cogl-texture.h b/clutter/cogl/cogl-texture.h index 5df2a818d..95e663084 100644 --- a/clutter/cogl/cogl-texture.h +++ b/clutter/cogl/cogl-texture.h @@ -385,6 +385,24 @@ void cogl_texture_polygon (CoglHandle handle, CoglTextureVertex *vertices, gboolean use_color); +/** + * cogl_texture_multiple_rectangles: + * @handle: a @CoglHandle. + * @verts: an array of vertices + * @n_rects: number of rectangles to draw + * + * Draws a series of rectangles in the same way that + * cogl_texture_rectangle() does. In some situations it can give a + * significant performance boost to use this function rather than + * calling cogl_texture_rectangle() separately for each rectangle. + * + * @verts should point to an array of #CoglFixeds with + * @n_rects * 8 elements. Each group of 8 values corresponds to the + * parameters x1, y1, x2, y2, tx1, ty1, tx2 and ty2 and have the same + * meaning as in cogl_texture_rectangle(). + * + * Since: 1.0 + */ void cogl_texture_multiple_rectangles (CoglHandle handle, const CoglFixed *verts, diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 03247ff3c..dcd7c933f 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -120,6 +120,7 @@ cogl_texture_set_region cogl_texture_ref cogl_texture_unref cogl_texture_rectangle +cogl_texture_multiple_rectangles cogl_texture_polygon From 8fc949bfafe0ca5de2cf19081f122896e29a3794 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 9 Dec 2008 15:01:10 +0000 Subject: [PATCH 009/147] Minor fix to indentation in gl/cogl-texture.c --- clutter/cogl/gl/cogl-texture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index 4397da7f1..ee1481296 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -2265,7 +2265,7 @@ cogl_texture_multiple_rectangles (CoglHandle handle, return; /* Prepare GL state */ - if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) enable_flags |= COGL_ENABLE_TEXTURE_RECT; else enable_flags |= COGL_ENABLE_TEXTURE_2D; From 0bedd891fb716e03b867e1f450205a2d503721cf Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 9 Dec 2008 15:10:33 +0000 Subject: [PATCH 010/147] Support cogl_texture_multiple_rectangles in GL ES backend --- clutter/cogl/gles/cogl-context.c | 14 +- clutter/cogl/gles/cogl-context.h | 11 +- clutter/cogl/gles/cogl-texture.c | 343 ++++++++++++++++++------------- 3 files changed, 212 insertions(+), 156 deletions(-) diff --git a/clutter/cogl/gles/cogl-context.c b/clutter/cogl/gles/cogl-context.c index c7c768dec..b09568de4 100644 --- a/clutter/cogl/gles/cogl-context.c +++ b/clutter/cogl/gles/cogl-context.c @@ -59,9 +59,11 @@ cogl_create_context () _context->last_path = 0; _context->texture_handles = NULL; - _context->texture_vertices_size = 0; - _context->texture_vertices = NULL; - + _context->texture_vertices = g_array_new (FALSE, FALSE, + sizeof (CoglTextureGLVertex)); + _context->texture_indices = g_array_new (FALSE, FALSE, + sizeof (GLushort)); + _context->fbo_handles = NULL; _context->program_handles = NULL; _context->shader_handles = NULL; @@ -104,8 +106,10 @@ cogl_destroy_context () #endif if (_context->texture_vertices) - g_free (_context->texture_vertices); - + g_array_free (_context->texture_vertices, TRUE); + if (_context->texture_indices) + g_array_free (_context->texture_indices, TRUE); + if (_context->texture_handles) g_array_free (_context->texture_handles, TRUE); if (_context->fbo_handles) diff --git a/clutter/cogl/gles/cogl-context.h b/clutter/cogl/gles/cogl-context.h index b62903efc..5d835712a 100644 --- a/clutter/cogl/gles/cogl-context.h +++ b/clutter/cogl/gles/cogl-context.h @@ -65,9 +65,14 @@ typedef struct /* Textures */ GArray *texture_handles; - CoglTextureGLVertex *texture_vertices; - gulong texture_vertices_size; - + GArray *texture_vertices; + GArray *texture_indices; + /* The gl texture number that the above vertices apply to. This to + detect when a different slice is encountered so that the vertices + can be flushed */ + GLuint texture_current; + GLenum texture_target; + /* Framebuffer objects */ GArray *fbo_handles; CoglBufferTarget draw_buffer; diff --git a/clutter/cogl/gles/cogl-texture.c b/clutter/cogl/gles/cogl-texture.c index 5a75eb389..9fa677687 100644 --- a/clutter/cogl/gles/cogl-texture.c +++ b/clutter/cogl/gles/cogl-texture.c @@ -2047,6 +2047,79 @@ cogl_texture_get_data (CoglHandle handle, return byte_size; } +static void +_cogl_texture_flush_vertices (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->texture_vertices->len > 0) + { + CoglTextureGLVertex *p + = (CoglTextureGLVertex *) ctx->texture_vertices->data; + + GE( glVertexPointer (2, GL_FLOAT, + sizeof (CoglTextureGLVertex), p->v ) ); + GE( glTexCoordPointer (2, GL_FLOAT, + sizeof (CoglTextureGLVertex), p->t ) ); + + GE( glBindTexture (ctx->texture_target, ctx->texture_current) ); + GE( glDrawElements (GL_TRIANGLES, + ctx->texture_indices->len, + GL_UNSIGNED_SHORT, + ctx->texture_indices->data) ); + + g_array_set_size (ctx->texture_vertices, 0); + g_array_set_size (ctx->texture_indices, 0); + } +} + +static void +_cogl_texture_add_quad_vertices (GLfloat x1, GLfloat y1, + GLfloat x2, GLfloat y2, + GLfloat tx1, GLfloat ty1, + GLfloat tx2, GLfloat ty2) +{ + CoglTextureGLVertex *p; + GLushort first_vert; + GLushort *q; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Add the four vertices of the quad to the list of queued + vertices */ + first_vert = ctx->texture_vertices->len; + g_array_set_size (ctx->texture_vertices, first_vert + 4); + p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, first_vert); + + p->v[0] = x1; p->v[1] = y1; + p->t[0] = tx1; p->t[1] = ty1; + p++; + p->v[0] = x1; p->v[1] = y2; + p->t[0] = tx1; p->t[1] = ty2; + p++; + p->v[0] = x2; p->v[1] = y2; + p->t[0] = tx2; p->t[1] = ty2; + p++; + p->v[0] = x2; p->v[1] = y1; + p->t[0] = tx2; p->t[1] = ty1; + p++; + + /* Add two triangles to the list of indices. That makes six new + indices but two of the vertices in the triangles are shared. */ + g_array_set_size (ctx->texture_indices, + ctx->texture_indices->len + 6); + q = &g_array_index (ctx->texture_indices, GLushort, + ctx->texture_indices->len - 6); + + *(q++) = first_vert + 0; + *(q++) = first_vert + 1; + *(q++) = first_vert + 3; + + *(q++) = first_vert + 1; + *(q++) = first_vert + 2; + *(q++) = first_vert + 3; +} + static void _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed x1, @@ -2067,31 +2140,13 @@ _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed slice_tx2 , slice_ty2; CoglFixed slice_qx1 , slice_qy1; CoglFixed slice_qx2 , slice_qy2; - GLfloat tex_coords[8]; - GLfloat quad_coords[8]; GLuint gl_handle; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); - + _COGL_GET_CONTEXT (ctx, NO_RETVAL); #if COGL_DEBUG printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); #endif - - - /* Prepare GL state */ - if (ctx->color_alpha < 255 - || tex->bitmap.format & COGL_A_BIT) - { - enable_flags |= COGL_ENABLE_BLEND; - } - - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; - - cogl_enable (enable_flags); /* If the texture coordinates are backwards then swap both the geometry and texture coordinates so that the texture will be @@ -2115,10 +2170,7 @@ _cogl_texture_quad_sw (CoglTexture *tex, ty1 = ty2; ty2 = temp; } - - GE( glTexCoordPointer (2, GL_FLOAT, 0, tex_coords) ); - GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords) ); - + /* Scale ratio from texture to quad widths */ tw = COGL_FIXED_FROM_INT (tex->bitmap.width); th = COGL_FIXED_FROM_INT (tex->bitmap.height); @@ -2158,17 +2210,16 @@ _cogl_texture_quad_sw (CoglTexture *tex, slice_qy2 = first_qy + COGL_FIXED_MUL (iter_y.intersect_end - first_ty, tqy); - + /* Localize slice texture coordinates */ slice_ty1 = iter_y.intersect_start - iter_y.pos; slice_ty2 = iter_y.intersect_end - iter_y.pos; - + /* Normalize texture coordinates to current slice (rectangle texture targets take denormalized) */ slice_ty1 /= iter_y.span->size; slice_ty2 /= iter_y.span->size; - - + /* Iterate until whole quad width covered */ for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans, first_tx, tx1, tx2) ; @@ -2188,12 +2239,12 @@ _cogl_texture_quad_sw (CoglTexture *tex, /* Localize slice texture coordinates */ slice_tx1 = iter_x.intersect_start - iter_x.pos; slice_tx2 = iter_x.intersect_end - iter_x.pos; - + /* Normalize texture coordinates to current slice (rectangle texture targets take denormalized) */ slice_tx1 /= iter_x.span->size; slice_tx2 /= iter_x.span->size; - + #if COGL_DEBUG printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index); printf("qx1: %f\n", COGL_FIXED_TO_FLOAT (slice_qx1)); @@ -2210,26 +2261,23 @@ _cogl_texture_quad_sw (CoglTexture *tex, gl_handle = g_array_index (tex->slice_gl_handles, GLuint, iter_y.index * iter_x.array->len + iter_x.index); - - GE( cogl_gles2_wrapper_bind_texture (tex->gl_target, gl_handle, - tex->gl_intformat) ); -#define CFX_F COGL_FIXED_TO_FLOAT - - /* Draw textured quad */ - tex_coords[0] = CFX_F(slice_tx1); tex_coords[1] = CFX_F(slice_ty2); - tex_coords[2] = CFX_F(slice_tx2); tex_coords[3] = CFX_F(slice_ty2); - tex_coords[4] = CFX_F(slice_tx1); tex_coords[5] = CFX_F(slice_ty1); - tex_coords[6] = CFX_F(slice_tx2); tex_coords[7] = CFX_F(slice_ty1); + /* If we're using a different texture from the one already queued + then flush the vertices */ + if (ctx->texture_vertices->len > 0 + && gl_handle != ctx->texture_current) + _cogl_texture_flush_vertices (); + ctx->texture_target = tex->gl_target; + ctx->texture_current = gl_handle; - quad_coords[0] = CFX_F(slice_qx1); quad_coords[1] = CFX_F(slice_qy2); - quad_coords[2] = CFX_F(slice_qx2); quad_coords[3] = CFX_F(slice_qy2); - quad_coords[4] = CFX_F(slice_qx1); quad_coords[5] = CFX_F(slice_qy1); - quad_coords[6] = CFX_F(slice_qx2); quad_coords[7] = CFX_F(slice_qy1); - - GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); - -#undef CFX_F + _cogl_texture_add_quad_vertices (COGL_FIXED_TO_FLOAT (slice_qx1), + COGL_FIXED_TO_FLOAT (slice_qy1), + COGL_FIXED_TO_FLOAT (slice_qx2), + COGL_FIXED_TO_FLOAT (slice_qy2), + COGL_FIXED_TO_FLOAT (slice_tx1), + COGL_FIXED_TO_FLOAT (slice_ty1), + COGL_FIXED_TO_FLOAT (slice_tx2), + COGL_FIXED_TO_FLOAT (slice_ty2)); } } } @@ -2245,41 +2293,27 @@ _cogl_texture_quad_hw (CoglTexture *tex, CoglFixed tx2, CoglFixed ty2) { - GLfloat tex_coords[8]; - GLfloat quad_coords[8]; GLuint gl_handle; CoglTexSliceSpan *x_span; CoglTexSliceSpan *y_span; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); #if COGL_DEBUG printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); #endif - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - /* Prepare GL state */ - if (ctx->color_alpha < 255 - || tex->bitmap.format & COGL_A_BIT) - { - enable_flags |= COGL_ENABLE_BLEND; - } - - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; - cogl_enable (enable_flags); - - GE( glTexCoordPointer (2, GL_FLOAT, 0, tex_coords) ); - GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords) ); - + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* Pick and bind opengl texture object */ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); - GE( cogl_gles2_wrapper_bind_texture (tex->gl_target, gl_handle, - tex->gl_intformat) ); - + + /* If we're using a different texture from the one already queued + then flush the vertices */ + if (ctx->texture_vertices->len > 0 + && gl_handle != ctx->texture_current) + _cogl_texture_flush_vertices (); + ctx->texture_target = tex->gl_target; + ctx->texture_current = gl_handle; + /* Don't include the waste in the texture coordinates */ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); @@ -2290,22 +2324,80 @@ _cogl_texture_quad_hw (CoglTexture *tex, ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; -#define CFX_F(x) COGL_FIXED_TO_FLOAT(x) + _cogl_texture_add_quad_vertices (COGL_FIXED_TO_FLOAT (x1), + COGL_FIXED_TO_FLOAT (y1), + COGL_FIXED_TO_FLOAT (x2), + COGL_FIXED_TO_FLOAT (y2), + COGL_FIXED_TO_FLOAT (tx1), + COGL_FIXED_TO_FLOAT (ty1), + COGL_FIXED_TO_FLOAT (tx2), + COGL_FIXED_TO_FLOAT (ty2)); +} + +void +cogl_texture_multiple_rectangles (CoglHandle handle, + const CoglFixed *verts, + guint n_rects) +{ + CoglTexture *tex; + gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY + | COGL_ENABLE_TEXTURE_2D); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; - /* Draw textured quad */ - tex_coords[0] = CFX_F(tx1); tex_coords[1] = CFX_F(ty2); - tex_coords[2] = CFX_F(tx2); tex_coords[3] = CFX_F(ty2); - tex_coords[4] = CFX_F(tx1); tex_coords[5] = CFX_F(ty1); - tex_coords[6] = CFX_F(tx2); tex_coords[7] = CFX_F(ty1); + cogl_clip_ensure (); - quad_coords[0] = CFX_F(x1); quad_coords[1] = CFX_F(y2); - quad_coords[2] = CFX_F(x2); quad_coords[3] = CFX_F(y2); - quad_coords[4] = CFX_F(x1); quad_coords[5] = CFX_F(y1); - quad_coords[6] = CFX_F(x2); quad_coords[7] = CFX_F(y1); + tex = _cogl_texture_pointer_from_handle (handle); + + /* Make sure we got stuff to draw */ + if (tex->slice_gl_handles == NULL) + return; + + if (tex->slice_gl_handles->len == 0) + return; - GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); + /* Prepare GL state */ + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + enable_flags |= COGL_ENABLE_BLEND; -#undef CFX_F + if (ctx->enable_backface_culling) + enable_flags |= COGL_ENABLE_BACKFACE_CULLING; + + cogl_enable (enable_flags); + + g_array_set_size (ctx->texture_vertices, 0); + + while (n_rects-- > 0) + { + if (verts[4] != verts[6] && verts[5] != verts[7]) + { + /* If there is only one GL texture and either the texture is + NPOT (no waste) or all of the coordinates are in the + range [0,1] then we can use hardware tiling */ + if (tex->slice_gl_handles->len == 1 + && ((cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && tex->gl_target == GL_TEXTURE_2D) + || (verts[4] >= 0 && verts[4] <= COGL_FIXED_1 + && verts[6] >= 0 && verts[6] <= COGL_FIXED_1 + && verts[5] >= 0 && verts[5] <= COGL_FIXED_1 + && verts[7] >= 0 && verts[7] <= COGL_FIXED_1))) + _cogl_texture_quad_hw (tex, verts[0],verts[1], verts[2],verts[3], + verts[4],verts[5], verts[6],verts[7]); + else + _cogl_texture_quad_sw (tex, verts[0],verts[1], verts[2],verts[3], + verts[4],verts[5], verts[6],verts[7]); + } + + verts += 8; + } + + _cogl_texture_flush_vertices (); } void @@ -2319,47 +2411,18 @@ cogl_texture_rectangle (CoglHandle handle, CoglFixed tx2, CoglFixed ty2) { - CoglTexture *tex; - - /* Check if valid texture */ - if (!cogl_is_texture (handle)) - return; + CoglFixed verts[8]; - cogl_clip_ensure (); - - tex = _cogl_texture_pointer_from_handle (handle); - - /* Make sure we got stuff to draw */ - if (tex->slice_gl_handles == NULL) - return; - - if (tex->slice_gl_handles->len == 0) - return; - - if (tx1 == tx2 || ty1 == ty2) - return; - - /* Pick tiling mode according to hw support */ - if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) - && tex->slice_gl_handles->len == 1) - { - _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); - } - else - { - if (tex->slice_gl_handles->len == 1 - && tx1 >= -COGL_FIXED_1 - && tx2 <= COGL_FIXED_1 - && ty1 >= -COGL_FIXED_1 - && ty2 <= COGL_FIXED_1) - { - _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); - } - else - { - _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); - } - } + verts[0] = x1; + verts[1] = y1; + verts[2] = x2; + verts[3] = y2; + verts[4] = tx1; + verts[5] = ty1; + verts[6] = tx2; + verts[7] = ty2; + + cogl_texture_multiple_rectangles (handle, verts, 1); } void @@ -2405,23 +2468,9 @@ cogl_texture_polygon (CoglHandle handle, /* Make sure there is enough space in the global texture vertex array. This is used so we can render the polygon with a single call to OpenGL but still support any number of vertices */ - if (ctx->texture_vertices_size < n_vertices) - { - guint nsize = ctx->texture_vertices_size; - - if (nsize == 0) - nsize = 1; - do - nsize *= 2; - while (nsize < n_vertices); - - ctx->texture_vertices_size = nsize; + g_array_set_size (ctx->texture_vertices, n_vertices); + p = (CoglTextureGLVertex *) ctx->texture_vertices->data; - ctx->texture_vertices = g_realloc (ctx->texture_vertices, - nsize - * sizeof (CoglTextureGLVertex)); - } - /* Prepare GL state */ enable_flags = (COGL_ENABLE_TEXTURE_2D | COGL_ENABLE_VERTEX_ARRAY @@ -2447,14 +2496,12 @@ cogl_texture_polygon (CoglHandle handle, if (use_color) { enable_flags |= COGL_ENABLE_COLOR_ARRAY; - GE( glColorPointer (4, GL_UNSIGNED_BYTE, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].c) ); - } + GE( glColorPointer (4, GL_UNSIGNED_BYTE, + sizeof (CoglTextureGLVertex), p->c) ); + } - GE( glVertexPointer (3, GL_FLOAT, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].v) ); - GE( glTexCoordPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].t) ); + GE( glVertexPointer (3, GL_FLOAT, sizeof (CoglTextureGLVertex), p->v ) ); + GE( glTexCoordPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), p->t ) ); cogl_enable (enable_flags); @@ -2464,7 +2511,7 @@ cogl_texture_polygon (CoglHandle handle, /* Convert the vertices into an array of GLfloats ready to pass to OpenGL */ - for (i = 0, p = ctx->texture_vertices; i < n_vertices; i++, p++) + for (i = 0; i < n_vertices; i++, p++) { #define CFX_F COGL_FIXED_TO_FLOAT From 9537f8e4ae69af43848f679d6568e94eb800b266 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 9 Dec 2008 18:03:29 +0000 Subject: [PATCH 011/147] Use a single index array There's no point in clearing the index array because it is always the same sequence of indices regardless of the vertices. Instead it is just added to when there are more vertices than ever before. --- clutter/cogl/gl/cogl-texture.c | 51 +++++++++++++++++++++----------- clutter/cogl/gles/cogl-texture.c | 51 +++++++++++++++++++++----------- 2 files changed, 66 insertions(+), 36 deletions(-) diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index ee1481296..71a53f06b 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -1918,9 +1918,41 @@ _cogl_texture_flush_vertices (void) if (ctx->texture_vertices->len > 0) { + int needed_indices; CoglTextureGLVertex *p = (CoglTextureGLVertex *) ctx->texture_vertices->data; + /* The indices are always the same sequence regardless of the + vertices so we only need to change it if there are more + vertices than ever before */ + needed_indices = ctx->texture_vertices->len / 4 * 6; + if (needed_indices > ctx->texture_indices->len) + { + int old_len = ctx->texture_indices->len; + int vert_num = old_len / 6 * 4; + int i; + GLushort *q; + + /* Add two triangles for each quad to the list of + indices. That makes six new indices but two of the + vertices in the triangles are shared. */ + g_array_set_size (ctx->texture_indices, needed_indices); + q = &g_array_index (ctx->texture_indices, GLushort, old_len); + + for (i = old_len; + i < ctx->texture_indices->len; + i += 6, vert_num += 4) + { + *(q++) = vert_num + 0; + *(q++) = vert_num + 1; + *(q++) = vert_num + 3; + + *(q++) = vert_num + 1; + *(q++) = vert_num + 2; + *(q++) = vert_num + 3; + } + } + GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), p->v ) ); GE( glTexCoordPointer (2, GL_FLOAT, @@ -1929,12 +1961,11 @@ _cogl_texture_flush_vertices (void) GE( glBindTexture (ctx->texture_target, ctx->texture_current) ); GE( ctx->pf_glDrawRangeElements (GL_TRIANGLES, 0, ctx->texture_vertices->len - 1, - ctx->texture_indices->len, + needed_indices, GL_UNSIGNED_SHORT, ctx->texture_indices->data) ); g_array_set_size (ctx->texture_vertices, 0); - g_array_set_size (ctx->texture_indices, 0); } } @@ -1946,7 +1977,6 @@ _cogl_texture_add_quad_vertices (GLfloat x1, GLfloat y1, { CoglTextureGLVertex *p; GLushort first_vert; - GLushort *q; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -1968,21 +1998,6 @@ _cogl_texture_add_quad_vertices (GLfloat x1, GLfloat y1, p->v[0] = x2; p->v[1] = y1; p->t[0] = tx2; p->t[1] = ty1; p++; - - /* Add two triangles to the list of indices. That makes six new - indices but two of the vertices in the triangles are shared. */ - g_array_set_size (ctx->texture_indices, - ctx->texture_indices->len + 6); - q = &g_array_index (ctx->texture_indices, GLushort, - ctx->texture_indices->len - 6); - - *(q++) = first_vert + 0; - *(q++) = first_vert + 1; - *(q++) = first_vert + 3; - - *(q++) = first_vert + 1; - *(q++) = first_vert + 2; - *(q++) = first_vert + 3; } static void diff --git a/clutter/cogl/gles/cogl-texture.c b/clutter/cogl/gles/cogl-texture.c index 9fa677687..915e82b23 100644 --- a/clutter/cogl/gles/cogl-texture.c +++ b/clutter/cogl/gles/cogl-texture.c @@ -2054,9 +2054,41 @@ _cogl_texture_flush_vertices (void) if (ctx->texture_vertices->len > 0) { + int needed_indices; CoglTextureGLVertex *p = (CoglTextureGLVertex *) ctx->texture_vertices->data; + /* The indices are always the same sequence regardless of the + vertices so we only need to change it if there are more + vertices than ever before */ + needed_indices = ctx->texture_vertices->len / 4 * 6; + if (needed_indices > ctx->texture_indices->len) + { + int old_len = ctx->texture_indices->len; + int vert_num = old_len / 6 * 4; + int i; + GLushort *q; + + /* Add two triangles for each quad to the list of + indices. That makes six new indices but two of the + vertices in the triangles are shared. */ + g_array_set_size (ctx->texture_indices, needed_indices); + q = &g_array_index (ctx->texture_indices, GLushort, old_len); + + for (i = old_len; + i < ctx->texture_indices->len; + i += 6, vert_num += 4) + { + *(q++) = vert_num + 0; + *(q++) = vert_num + 1; + *(q++) = vert_num + 3; + + *(q++) = vert_num + 1; + *(q++) = vert_num + 2; + *(q++) = vert_num + 3; + } + } + GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), p->v ) ); GE( glTexCoordPointer (2, GL_FLOAT, @@ -2064,12 +2096,11 @@ _cogl_texture_flush_vertices (void) GE( glBindTexture (ctx->texture_target, ctx->texture_current) ); GE( glDrawElements (GL_TRIANGLES, - ctx->texture_indices->len, + needed_indices, GL_UNSIGNED_SHORT, ctx->texture_indices->data) ); g_array_set_size (ctx->texture_vertices, 0); - g_array_set_size (ctx->texture_indices, 0); } } @@ -2081,7 +2112,6 @@ _cogl_texture_add_quad_vertices (GLfloat x1, GLfloat y1, { CoglTextureGLVertex *p; GLushort first_vert; - GLushort *q; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -2103,21 +2133,6 @@ _cogl_texture_add_quad_vertices (GLfloat x1, GLfloat y1, p->v[0] = x2; p->v[1] = y1; p->t[0] = tx2; p->t[1] = ty1; p++; - - /* Add two triangles to the list of indices. That makes six new - indices but two of the vertices in the triangles are shared. */ - g_array_set_size (ctx->texture_indices, - ctx->texture_indices->len + 6); - q = &g_array_index (ctx->texture_indices, GLushort, - ctx->texture_indices->len - 6); - - *(q++) = first_vert + 0; - *(q++) = first_vert + 1; - *(q++) = first_vert + 3; - - *(q++) = first_vert + 1; - *(q++) = first_vert + 2; - *(q++) = first_vert + 3; } static void From a98720ae19f4892676c043bb6e4f2258eb201827 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:12:48 +0000 Subject: [PATCH 012/147] Initial import of the Text actor from Tidy The TidyText actor is meant as a replacement for both ClutterLabel and ClutterText. Any text-displaying and editing actor should derive from ClutterText and implement the various visual cues to differentiate the editable from the non-editable state. Those visual cues usually belong to a high-level toolkit, especially if themeing is involved. --- clutter/Makefile.am | 2 + clutter/clutter-text.c | 1561 +++++++++++++++++++++++++++++++++ clutter/clutter-text.h | 136 +++ clutter/clutter.h | 1 + tests/interactive/Makefile.am | 3 +- tests/interactive/test-text.c | 176 ++++ 6 files changed, 1878 insertions(+), 1 deletion(-) create mode 100644 clutter/clutter-text.c create mode 100644 clutter/clutter-text.h create mode 100644 tests/interactive/test-text.c diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 97c9907ad..f51523200 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -86,6 +86,7 @@ source_h = \ $(srcdir)/clutter-stage.h \ $(srcdir)/clutter-stage-manager.h \ $(srcdir)/clutter-texture.h \ + $(srcdir)/clutter-text.h \ $(srcdir)/clutter-timeline.h \ $(srcdir)/clutter-timeout-pool.h \ $(srcdir)/clutter-types.h \ @@ -180,6 +181,7 @@ source_c = \ clutter-stage-manager.c \ clutter-stage-window.c \ clutter-texture.c \ + clutter-text.c \ clutter-timeline.c \ clutter-timeout-pool.c \ clutter-units.c \ diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c new file mode 100644 index 000000000..6733fe1bd --- /dev/null +++ b/clutter/clutter-text.c @@ -0,0 +1,1561 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2006-2008 OpenedHand + * + * Authored By Øyvind Kolås + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* TODO: undo/redo hooks? + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "clutter-text.h" +#include + +static gboolean clutter_text_key_press (ClutterActor *actor, + ClutterKeyEvent *kev); +static gboolean clutter_text_position_to_coords (ClutterText *ttext, + gint position, + gint *x, + gint *y, + gint *cursor_height); +static gint clutter_text_coords_to_position (ClutterText *text, + gint x, + gint y); +static void clutter_text_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void clutter_text_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void clutter_text_finalize (GObject *gobject); + +static void init_commands (ClutterText *ttext); +static void init_mappings (ClutterText *ttext); + +void +clutter_text_delete_text (ClutterText *ttext, + gssize start_pos, + gssize end_pos); + +static gboolean +clutter_text_truncate_selection (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event); + +G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_LABEL); + +struct _ClutterTextPrivate +{ + gboolean editable; + gboolean cursor_visible; + gboolean activatable; + gboolean selectable; + + gint position; /* current cursor position */ + gint selection_bound; + /* current 'other end of selection' position */ + gboolean in_select_drag; + gint x_pos; /* the x position in the pangolayout, used to + * avoid drifting when repeatedly moving up|down + */ + gboolean cursor_color_set; + ClutterColor cursor_color; + ClutterGeometry cursor_pos; /* Where to draw the cursor */ + + GList *mappings; + GList *commands; /* each instance has it's own set of commands + so that actor specific actions can be added + to single actor classes + */ +}; + +#define CLUTTER_TEXT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + CLUTTER_TYPE_TEXT, \ + ClutterTextPrivate)) + +enum +{ + PROP_0, + PROP_POSITION, + PROP_SELECTION_BOUND, + PROP_CURSOR_VISIBLE, + PROP_CURSOR_COLOR, + PROP_CURSOR_COLOR_SET, + PROP_EDITABLE, + PROP_SELECTABLE, + PROP_ACTIVATABLE +}; + +enum +{ + TEXT_CHANGED, + CURSOR_EVENT, + ACTIVATE, + LAST_SIGNAL +}; + +static guint label_signals[LAST_SIGNAL] = { 0, }; + +#define offset_real(text, pos) \ + (pos==-1?g_utf8_strlen(text, -1):pos) \ + +#define offset_to_bytes(text,pos)\ + (pos==-1?strlen(text):((gint)(g_utf8_offset_to_pointer (text, pos) - text))) + +#define bytes_to_offset(text, pos) \ + (g_utf8_pointer_to_offset (text, text + pos)) + + +typedef struct TextCommand { + const gchar *name; + gboolean (*func) (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event); +} TextCommand; + +typedef struct ClutterTextMapping { + ClutterModifierType state; + guint keyval; + const gchar *action; +} ClutterTextMapping; + + +void +clutter_text_mappings_clear (ClutterText *ttext) +{ + ClutterTextPrivate *priv = ttext->priv; + GList *iter; + for (iter = priv->mappings; iter; iter=iter->next) + { + g_free (iter->data); + } + g_list_free (priv->mappings); + priv->mappings = NULL; +} + +void clutter_text_add_mapping (ClutterText *ttext, + guint keyval, + ClutterModifierType state, + const gchar *commandline) +{ + ClutterTextMapping *tmapping = g_new (ClutterTextMapping, 1); + ClutterTextPrivate *priv = ttext->priv; + tmapping->keyval = keyval; + tmapping->state = state; + tmapping->action = commandline; + priv->mappings = g_list_append (priv->mappings, tmapping); +} + +void clutter_text_add_action (ClutterText *ttext, + const gchar *name, + gboolean (*func) (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event)) +{ + TextCommand *tcommand = g_new (TextCommand, 1); + ClutterTextPrivate *priv = ttext->priv; + tcommand->name = name; + tcommand->func = func; + priv->commands = g_list_append (priv->commands, tcommand); +} + +static void init_mappings (ClutterText *ttext) +{ + ClutterTextPrivate *priv = ttext->priv; + if (priv->mappings) + return; + clutter_text_add_mapping (ttext, CLUTTER_Left, 0, "move-left"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Left, 0, "move-left"); + clutter_text_add_mapping (ttext, CLUTTER_Right, 0, "move-right"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Right, 0,"move-right"); + clutter_text_add_mapping (ttext, CLUTTER_Up, 0, "move-up"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Up, 0, "move-up"); + clutter_text_add_mapping (ttext, CLUTTER_Down, 0, "move-down"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Down, 0, "move-down"); + clutter_text_add_mapping (ttext, CLUTTER_Begin, 0, "move-start-line"); + clutter_text_add_mapping (ttext, CLUTTER_Home, 0, "move-start-line"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Home, 0, "move-start-line"); + clutter_text_add_mapping (ttext, CLUTTER_End, 0, "move-end-line"); + clutter_text_add_mapping (ttext, CLUTTER_KP_End, 0, "move-end-line"); + clutter_text_add_mapping (ttext, CLUTTER_BackSpace,0,"delete-previous"); + clutter_text_add_mapping (ttext, CLUTTER_Delete, 0, "delete-next"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Delete,0,"delete-next"); + clutter_text_add_mapping (ttext, CLUTTER_Return, 0, "activate"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Enter, 0,"activate"); + clutter_text_add_mapping (ttext, CLUTTER_ISO_Enter,0,"activate"); +} + + +static gint +clutter_text_coords_to_position (ClutterText *text, + gint x, + gint y) +{ + gint index_; + gint px, py; + gint trailing; + + px = x * PANGO_SCALE; + py = y * PANGO_SCALE; + + pango_layout_xy_to_index (clutter_label_get_layout (CLUTTER_LABEL (text)), + px, py, &index_, &trailing); + + return index_ + trailing; +} + +static gboolean +clutter_text_position_to_coords (ClutterText *ttext, + gint position, + gint *x, + gint *y, + gint *cursor_height) +{ + ClutterTextPrivate *priv; + gint index_; + PangoRectangle rect; + const gchar *text; + + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + priv = ttext->priv; + + if (position == -1) + { + index_ = strlen (text); + } + else + { + index_ = offset_to_bytes (text, position); + } + + if (index_ > strlen (text)) + index_ = strlen (text); + + pango_layout_get_cursor_pos ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + index_, &rect, NULL); + + if (x) + *x = rect.x / PANGO_SCALE; + if (y) + *y = (rect.y + rect.height) / PANGO_SCALE; + if (cursor_height) + *cursor_height = rect.height / PANGO_SCALE; + + return TRUE; /* FIXME: should return false if coords were outside text */ +} + +static void +clutter_text_ensure_cursor_position (ClutterText *ttext) +{ + gint x,y,cursor_height; + + ClutterTextPrivate *priv; + priv = ttext->priv; + + clutter_text_position_to_coords (ttext, priv->position, &x, &y, &cursor_height); + + priv->cursor_pos.x = x; + priv->cursor_pos.y = y - cursor_height; + priv->cursor_pos.width = 2; + priv->cursor_pos.height = cursor_height; + + g_signal_emit (ttext, label_signals[CURSOR_EVENT], 0, &priv->cursor_pos); +} + +gint +clutter_text_get_cursor_position (ClutterText *ttext) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (ttext), -1); + return ttext->priv->position; +} + +void +clutter_text_set_cursor_position (ClutterText *ttext, + gint position) +{ + ClutterLabel *label = CLUTTER_LABEL (ttext); + const gchar *text; + ClutterTextPrivate *priv; + gint len; + + g_return_if_fail (CLUTTER_IS_TEXT (ttext)); + + priv = ttext->priv; + + text = clutter_label_get_text (label); + if (text == NULL) + return; + + len = g_utf8_strlen (text, -1); + + if (position < 0 || position >= len) + priv->position = -1; + else + priv->position = position; + + if (CLUTTER_ACTOR_IS_VISIBLE (ttext)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (ttext)); +} + +static gboolean +clutter_text_truncate_selection (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (ttext)); + ClutterTextPrivate *priv; + gint start_index; + gint end_index; + + priv = ttext->priv; + + g_object_ref (ttext); + + start_index = offset_real (utf8, priv->position); + end_index = offset_real (utf8, priv->selection_bound); + + if (end_index == start_index) + return FALSE; + + if (end_index < start_index) + { + gint temp = start_index; + start_index = end_index; + end_index = temp; + } + + clutter_text_delete_text (ttext, start_index, end_index); + priv->position = start_index; + priv->selection_bound = start_index; + return TRUE; +} + + +void +clutter_text_insert_unichar (ClutterText *ttext, + gunichar wc) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + const gchar *old_text; + glong pos; + + g_return_if_fail (CLUTTER_IS_TEXT (ttext)); + g_return_if_fail (g_unichar_validate (wc)); + + if (wc == 0) + return; + + clutter_text_truncate_selection (ttext, NULL, 0); + + priv = ttext->priv; + + g_object_ref (ttext); + + old_text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + + new = g_string_new (old_text); + pos = offset_to_bytes (old_text, priv->position); + new = g_string_insert_unichar (new, pos, wc); + + clutter_label_set_text (CLUTTER_LABEL (ttext), new->str); + + if (priv->position >= 0) + { + clutter_text_set_cursor_position (ttext, priv->position + 1); + clutter_text_set_selection_bound (ttext, priv->position); + } + + g_string_free (new, TRUE); + + g_object_unref (ttext); + + g_signal_emit (G_OBJECT (ttext), label_signals[TEXT_CHANGED], 0); +} + +void +clutter_text_delete_text (ClutterText *ttext, + gssize start_pos, + gssize end_pos) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + gint start_bytes; + gint end_bytes; + const gchar *text; + + g_return_if_fail (CLUTTER_IS_TEXT (ttext)); + + priv = ttext->priv; + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + if (end_pos == -1) + { + start_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1) - 1); + end_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1)); + } + else + { + start_bytes = offset_to_bytes (text, start_pos); + end_bytes = offset_to_bytes (text, end_pos); + } + + new = g_string_new (text); + + new = g_string_erase (new, start_bytes, end_bytes - start_bytes); + + clutter_label_set_text (CLUTTER_LABEL (ttext), new->str); + + g_string_free (new, TRUE); + g_signal_emit (G_OBJECT (ttext), label_signals[TEXT_CHANGED], 0); +} + +static void +clutter_text_finalize (GObject *gobject) +{ + ClutterTextPrivate *priv; + ClutterText *ttext; + GList *iter; + + ttext = CLUTTER_TEXT (gobject); + priv = ttext->priv; + + clutter_text_mappings_clear (ttext); + + for (iter = priv->commands; iter; iter=iter->next) + g_free (iter->data); + g_list_free (priv->commands); + priv->commands = NULL; +} + +static void +clutter_text_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterTextPrivate *priv; + ClutterText *ttext; + + ttext = CLUTTER_TEXT (gobject); + priv = ttext->priv; + + switch (prop_id) + { + case PROP_POSITION: + clutter_text_set_cursor_position (ttext, g_value_get_int (value)); + break; + case PROP_SELECTION_BOUND: + clutter_text_set_selection_bound (ttext, g_value_get_int (value)); + break; + case PROP_CURSOR_VISIBLE: + clutter_text_set_cursor_visible (ttext, g_value_get_boolean (value)); + break; + case PROP_CURSOR_COLOR: + clutter_text_set_cursor_color (ttext, g_value_get_boxed (value)); + break; + case PROP_EDITABLE: + clutter_text_set_editable (ttext, g_value_get_boolean (value)); + break; + case PROP_ACTIVATABLE: + clutter_text_set_activatable (ttext, g_value_get_boolean (value)); + break; + case PROP_SELECTABLE: + clutter_text_set_selectable (ttext, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_text_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterTextPrivate *priv; + + priv = CLUTTER_TEXT (gobject)->priv; + + switch (prop_id) + { + case PROP_CURSOR_VISIBLE: + g_value_set_boolean (value, priv->cursor_visible); + break; + case PROP_CURSOR_COLOR: + { + ClutterColor color; + clutter_text_get_cursor_color (CLUTTER_TEXT (gobject), &color); + g_value_set_boxed (value, &color); + } + break; + case PROP_POSITION: + g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); + break; + case PROP_SELECTION_BOUND: + g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->selection_bound)); + break; + case PROP_EDITABLE: + g_value_set_boolean (value, priv->editable); + break; + case PROP_SELECTABLE: + g_value_set_boolean (value, priv->selectable); + break; + case PROP_ACTIVATABLE: + g_value_set_boolean (value, priv->activatable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +cursor_paint (ClutterActor *actor, + gpointer user_data) +{ + ClutterText *ttext = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = ttext->priv; + + if (priv->editable && + priv->cursor_visible) + { + if (priv->cursor_color_set) + { + cogl_set_source_color4ub (priv->cursor_color.red, + priv->cursor_color.green, + priv->cursor_color.blue, + priv->cursor_color.alpha); + } + else + { + ClutterColor color; + clutter_label_get_color (CLUTTER_LABEL (actor), &color); + cogl_set_source_color4ub (color.red, + color.green, + color.blue, + color.alpha); + } + + clutter_text_ensure_cursor_position (ttext); + + if (priv->position == 0) + priv->cursor_pos.x -= 2; + + if (priv->position == priv->selection_bound) + { + cogl_rectangle (priv->cursor_pos.x, + priv->cursor_pos.y, + priv->cursor_pos.width, + priv->cursor_pos.height); + } + else + { + gint lines; + gint start_index; + gint end_index; + const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + start_index = offset_to_bytes (utf8, priv->position); + 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; + } + + PangoLayout *layout = clutter_label_get_layout (CLUTTER_LABEL (ttext)); + lines = pango_layout_get_line_count (layout); + gint line_no; + for (line_no = 0; line_no < lines; line_no++) + { + PangoLayoutLine *line; + gint n_ranges; + gint *ranges; + gint i; + gint y; + gint height; + gint index; + gint maxindex; + + 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 (ttext, bytes_to_offset (utf8, index), NULL, &y, &height); + + for (i=0;ipriv; + ClutterUnit x, y; + gint index_; + const gchar *text; + + text = clutter_label_get_text (CLUTTER_LABEL (actor)); + + x = CLUTTER_UNITS_FROM_INT (bev->x); + y = CLUTTER_UNITS_FROM_INT (bev->y); + + clutter_actor_transform_stage_point (actor, x, y, &x, &y); + + index_ = clutter_text_coords_to_position (ttext, CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); + + clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); + clutter_text_set_selection_bound (ttext, bytes_to_offset (text, index_) + ); + + /* we'll steal keyfocus if we do not have it */ + { + ClutterActor *stage; + for (stage = actor; + clutter_actor_get_parent (stage); + stage = clutter_actor_get_parent (stage)); + if (stage && CLUTTER_IS_STAGE (stage)) + clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); + } + + priv->in_select_drag = TRUE; + clutter_grab_pointer (actor); + + return TRUE; +} + + +static gboolean +clutter_text_motion (ClutterActor *actor, + ClutterMotionEvent *mev) +{ + ClutterText *ttext = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = ttext->priv; + ClutterUnit x, y; + gint index_; + const gchar *text; + + if (!priv->in_select_drag) + { + return FALSE; + } + + text = clutter_label_get_text (CLUTTER_LABEL (actor)); + + x = CLUTTER_UNITS_FROM_INT (mev->x); + y = CLUTTER_UNITS_FROM_INT (mev->y); + + clutter_actor_transform_stage_point (actor, x, y, &x, &y); + + index_ = clutter_text_coords_to_position (ttext, CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); + + if (priv->selectable) + { + clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); + } + else + { + clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); + clutter_text_set_selection_bound (ttext, bytes_to_offset (text, index_)); + } + + return TRUE; +} + +static gboolean +clutter_text_release (ClutterActor *actor, + ClutterButtonEvent *bev) +{ + ClutterText *ttext = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = ttext->priv; + if (priv->in_select_drag) + { + clutter_ungrab_pointer (); + priv->in_select_drag = FALSE; + return TRUE; + } + return FALSE; +} + +static void +clutter_text_constructed (GObject *object) +{ + g_signal_connect (CLUTTER_ACTOR (object), + "paint", G_CALLBACK (cursor_paint), + NULL); + + if (G_OBJECT_CLASS (clutter_text_parent_class)->constructed != NULL) + G_OBJECT_CLASS (clutter_text_parent_class)->constructed (object); +} + +static void +clutter_text_class_init (ClutterTextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->set_property = clutter_text_set_property; + gobject_class->get_property = clutter_text_get_property; + gobject_class->constructed = clutter_text_constructed; + gobject_class->finalize = clutter_text_finalize; + + actor_class->key_press_event = clutter_text_key_press; + actor_class->button_press_event = clutter_text_press; + actor_class->button_release_event = clutter_text_release; + actor_class->motion_event = clutter_text_motion; + + /** + * ClutterText:editable: + * + * Whether key events delivered to the actor causes editing. + */ + g_object_class_install_property + (gobject_class, PROP_EDITABLE, + g_param_spec_boolean ("editable", + "Editable", + "Whether the text is editable", + TRUE, + G_PARAM_READWRITE)); + + + /** + * ClutterText:selectable: + * + * Whether it is possible to select text. + */ + g_object_class_install_property + (gobject_class, PROP_SELECTABLE, + g_param_spec_boolean ("selectable", + "Editable", + "Whether the text is selectable", + TRUE, + G_PARAM_READWRITE)); + + /** + * ClutterText:activatable: + * + * Toggles whether return invokes the activate signal or not. + */ + g_object_class_install_property + (gobject_class, PROP_ACTIVATABLE, + g_param_spec_boolean ("activatable", + "Editable", + "Whether return causes the activate signal to be fired", + TRUE, + G_PARAM_READWRITE)); + + /** + * ClutterText:cursor-visible: + * + * Whether the input cursor is visible or not, it will only be visible + * if both cursor-visible is set and editable is set at the same time, + * the value defaults to TRUE. + */ + g_object_class_install_property + (gobject_class, PROP_CURSOR_VISIBLE, + g_param_spec_boolean ("cursor-visible", + "Cursor Visible", + "Whether the input cursor is visible", + TRUE, + G_PARAM_READWRITE)); + + + g_object_class_install_property + (gobject_class, PROP_CURSOR_COLOR, + g_param_spec_boxed ("cursor-color", + "Cursor Colour", + "Cursor Colour", + CLUTTER_TYPE_COLOR, + G_PARAM_READWRITE)); + + /** + * ClutterText:position: + * + * The current input cursor position. -1 is taken to be the end of the text + */ + g_object_class_install_property + (gobject_class, PROP_POSITION, + g_param_spec_int ("position", + "Position", + "The cursor position", + -1, G_MAXINT, + -1, + G_PARAM_READWRITE)); + + + /** + * ClutterText:selection-bound: + * + * The current input cursor position. -1 is taken to be the end of the text + */ + g_object_class_install_property + (gobject_class, PROP_SELECTION_BOUND, + g_param_spec_int ("selection-bound", + "Selection-bound", + "The cursor position of the other end of the selection.", + -1, G_MAXINT, + -1, + G_PARAM_READWRITE)); + + /** + * ClutterText::text-changed: + * @actor: the actor which received the event + * + * The ::text-changed signal is emitted after @entry's text changes + */ + label_signals[TEXT_CHANGED] = + g_signal_new ("text-changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterEntryClass, text_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + + label_signals[CURSOR_EVENT] = + g_signal_new ("cursor-event", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterEntryClass, cursor_event), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE); + + + /** + * ClutterText::activate + * @actor: the actor which received the event + * + * The ::activate signal is emitted each time the entry is 'activated' + * by the user, normally by pressing the 'Enter' key. + * + * Since: 0.4 + */ + label_signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterEntryClass, activate), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + + g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); +} + +static void +clutter_text_init (ClutterText *self) +{ + ClutterTextPrivate *priv; + + self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); + priv->x_pos = -1; + priv->cursor_visible = TRUE; + priv->editable = FALSE; + + priv->cursor_color_set = FALSE; + init_commands (self); /* FIXME: free */ + init_mappings (self); /* FIXME: free */ +} + +ClutterActor * +clutter_text_new_full (const gchar *font_name, + const gchar *text, + const ClutterColor *color) +{ + return g_object_new (CLUTTER_TYPE_TEXT, + "font-name", font_name, + "text", text, + "color", color, + NULL); +} + +ClutterActor * +clutter_text_new_with_text (const gchar *font_name, + const gchar *text) +{ + return g_object_new (CLUTTER_TYPE_TEXT, + "font-name", font_name, + "text", text, + NULL); +} + + +void +clutter_text_set_editable (ClutterText *label, + gboolean editable) +{ + ClutterTextPrivate *priv; + + priv = label->priv; + priv->editable = editable; + clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); +} + +gboolean +clutter_text_get_editable (ClutterText *label) +{ + ClutterTextPrivate *priv; + priv = label->priv; + return priv->editable; +} + + +void +clutter_text_set_selectable (ClutterText *label, + gboolean selectable) +{ + ClutterTextPrivate *priv; + + priv = label->priv; + priv->selectable = selectable; + clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); +} + +gboolean +clutter_text_get_selectable (ClutterText *label) +{ + ClutterTextPrivate *priv; + priv = label->priv; + return priv->selectable; +} + + +void +clutter_text_set_activatable (ClutterText *label, + gboolean activatable) +{ + ClutterTextPrivate *priv; + + priv = label->priv; + priv->activatable = activatable; + clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); +} + +gboolean +clutter_text_get_activatable (ClutterText *label) +{ + ClutterTextPrivate *priv; + priv = label->priv; + return priv->activatable; +} + + +void +clutter_text_set_cursor_visible (ClutterText *label, + gboolean cursor_visible) +{ + ClutterTextPrivate *priv; + + priv = label->priv; + priv->cursor_visible = cursor_visible; + clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); +} + +gboolean +clutter_text_get_cursor_visible (ClutterText *label) +{ + ClutterTextPrivate *priv; + priv = label->priv; + return priv->cursor_visible; +} + +void +clutter_text_set_cursor_color (ClutterText *text, + const ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_LABEL (text)); + g_return_if_fail (color != NULL); + + priv = text->priv; + + g_object_ref (text); + + if (color) + { + priv->cursor_color = *color; + priv->cursor_color_set = TRUE; + } + else + { + priv->cursor_color_set = FALSE; + } +} + + +void +clutter_text_get_cursor_color (ClutterText *text, + ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_LABEL (text)); + g_return_if_fail (color != NULL); + + priv = text->priv; + + *color = priv->cursor_color; +} + + +gint +clutter_text_get_selection_bound (ClutterText *text) +{ + ClutterTextPrivate *priv; + + priv = text->priv; + + return priv->selection_bound; +} + +gchar * +clutter_text_get_selection (ClutterText *text) +{ + ClutterTextPrivate *priv; + + const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (text)); + gchar *str; + gint len; + gint start_index; + gint end_index; + gint start_offset; + gint end_offset; + + priv = text->priv; + + start_index = priv->position; + end_index = priv->selection_bound; + + if (end_index == start_index) + return g_strdup (""); + if (end_index < start_index) + { + gint temp = start_index; + start_index = end_index; + end_index = temp; + } + + start_offset = offset_to_bytes (utf8, start_index); + end_offset = offset_to_bytes (utf8, end_index); + len = end_offset - start_offset; + + str = g_malloc (len + 1); + g_utf8_strncpy (str, utf8 + start_offset, end_index-start_index); + return str; +} + + + +void +clutter_text_set_selection_bound (ClutterText *text, + gint selection_bound) +{ + ClutterTextPrivate *priv; + + priv = text->priv; + priv->selection_bound = selection_bound; + + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); +} + + +/****************************************************************/ +/* The following are the commands available for keybinding when */ +/* using the entry, these can also be invoked programmatically */ +/* through clutter_text_action() */ +/****************************************************************/ + +static gboolean +clutter_text_action_activate (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + g_signal_emit (G_OBJECT (ttext), label_signals[ACTIVATE], 0); + return TRUE; +} + +static void +clutter_text_clear_selection (ClutterText *ttext) +{ + ClutterTextPrivate *priv = ttext->priv; + priv->selection_bound = priv->position; +} + +static gboolean +clutter_text_action_move_left (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint pos = priv->position; + gint len; + len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + + if (pos != 0 && len !=0) + { + if (pos == -1) + { + clutter_text_set_cursor_position (ttext, len - 1); + } + else + { + clutter_text_set_cursor_position (ttext, pos - 1); + } + } + + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + + return TRUE; +} + + +static gboolean +clutter_text_action_move_right (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint pos; + + gint len; + len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + + pos = priv->position; + if (pos != -1 && len !=0) + { + if (pos != len) + { + clutter_text_set_cursor_position (ttext, pos + 1); + } + } + + if (!(priv->selectable && + event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + { + clutter_text_clear_selection (ttext); + } + + return TRUE; +} + +static gboolean +clutter_text_action_move_up (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint line_no; + gint index_; + gint x; + const gchar *text; + PangoLayoutLine *layout_line; + + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + pango_layout_index_to_line_x ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + offset_to_bytes (text, priv->position), + 0, + &line_no, + &x); + + if (priv->x_pos != -1) + x = priv->x_pos; + else + priv->x_pos = x; + + line_no -= 1; + if (line_no < 0) + return FALSE; + + layout_line = pango_layout_get_line_readonly ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + line_no); + + if (!layout_line) + return TRUE; + + pango_layout_line_x_to_index (layout_line, x, &index_, NULL); + + { + gint pos = bytes_to_offset (text, index_); + clutter_text_set_cursor_position (ttext, pos); + } + + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + + return TRUE; +} + +static gboolean +clutter_text_action_move_down (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint line_no; + gint index_; + gint x; + const gchar *text; + PangoLayoutLine *layout_line; + + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + pango_layout_index_to_line_x ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + offset_to_bytes (text, priv->position), + 0, + &line_no, + &x); + + if (priv->x_pos != -1) + x = priv->x_pos; + else + priv->x_pos = x; + + layout_line = pango_layout_get_line_readonly ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + line_no + 1); + + if (!layout_line) + { + return FALSE; + } + + pango_layout_line_x_to_index (layout_line, x, &index_, NULL); + + { + gint pos = bytes_to_offset (text, index_); + clutter_text_set_cursor_position (ttext, pos); + } + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + return TRUE; +} + +static gboolean +clutter_text_action_move_start (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + + clutter_text_set_cursor_position (ttext, 0); + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + return TRUE; +} + +static gboolean +clutter_text_action_move_end (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + + clutter_text_set_cursor_position (ttext, -1); + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + + return TRUE; +} + +static gboolean +clutter_text_action_move_start_line (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint line_no; + gint index_; + const gchar *text; + PangoLayoutLine *layout_line; + gint position; + + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + + pango_layout_index_to_line_x ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + offset_to_bytes (text, priv->position), + 0, + &line_no, + NULL); + + layout_line = pango_layout_get_line_readonly ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + line_no); + + pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); + + position = bytes_to_offset (text, index_); + clutter_text_set_cursor_position (ttext, position); + + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + + return TRUE; +} + +static gboolean +clutter_text_action_move_end_line (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint line_no; + gint index_; + gint trailing; + const gchar *text; + PangoLayoutLine *layout_line; + gint position; + + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + index_ = offset_to_bytes (text, priv->position); + + pango_layout_index_to_line_x ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + index_, + 0, + &line_no, + NULL); + + layout_line = pango_layout_get_line_readonly ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + line_no); + + pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); + index_ += trailing; + + position = bytes_to_offset (text, index_); + + clutter_text_set_cursor_position (ttext, position); + + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + + return TRUE; +} + +static gboolean +clutter_text_action_delete_next (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (ttext, NULL, 0)) + return TRUE; + priv = ttext->priv; + pos = priv->position; + len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + + if (len && pos != -1 && pos < len) + clutter_text_delete_text (ttext, pos, pos+1);; + return TRUE; +} + +static gboolean +clutter_text_action_delete_previous (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (ttext, NULL, 0)) + return TRUE; + priv = ttext->priv; + pos = priv->position; + len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + + if (pos != 0 && len !=0) + { + if (pos == -1) + { + clutter_text_set_cursor_position (ttext, len - 1); + clutter_text_set_selection_bound (ttext, len - 1); + } + else + { + clutter_text_set_cursor_position (ttext, pos - 1); + clutter_text_set_selection_bound (ttext, pos - 1); + } + clutter_text_delete_text (ttext, pos-1, pos);; + } + return TRUE; +} + + +static void init_commands (ClutterText *ttext) +{ + ClutterTextPrivate *priv = ttext->priv; + if (priv->commands) + return; + clutter_text_add_action (ttext, "move-left", clutter_text_action_move_left); + clutter_text_add_action (ttext, "move-right", clutter_text_action_move_right); + clutter_text_add_action (ttext, "move-up", clutter_text_action_move_up); + clutter_text_add_action (ttext, "move-down", clutter_text_action_move_down); + clutter_text_add_action (ttext, "move-start", clutter_text_action_move_start); + clutter_text_add_action (ttext, "move-end", clutter_text_action_move_end); + clutter_text_add_action (ttext, "move-start-line", clutter_text_action_move_start_line); + clutter_text_add_action (ttext, "move-end-line", clutter_text_action_move_end_line); + clutter_text_add_action (ttext, "delete-previous", clutter_text_action_delete_previous); + clutter_text_add_action (ttext, "delete-next", clutter_text_action_delete_next); + clutter_text_add_action (ttext, "activate", clutter_text_action_activate); + clutter_text_add_action (ttext, "truncate-selection", clutter_text_truncate_selection); +} + +gboolean +clutter_text_action (ClutterText *ttext, + const gchar *command, + ClutterEvent *event) +{ + gchar command2[64]; + gint i; + + GList *iter; + ClutterTextPrivate *priv = ttext->priv; + + for (i=0; command[i] && + command[i]!=' '&& + i<62; i++) + { + command2[i]=command[i]; + } + command2[i]='\0'; + + if (!g_str_equal (command2, "move-up") && + !g_str_equal (command2, "move-down")) + priv->x_pos = -1; + + for (iter=priv->commands;iter;iter=iter->next) + { + TextCommand *tcommand = iter->data; + if (g_str_equal (command2, tcommand->name)) + return tcommand->func (ttext, command, event); + } + + g_warning ("unhandled text command %s", command); + return FALSE; +} + +static gboolean +clutter_text_key_press (ClutterActor *actor, + ClutterKeyEvent *kev) +{ + ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; + gint keyval = clutter_key_event_symbol (kev); + GList *iter; + + if (!priv->editable) + return FALSE; + + for (iter=priv->mappings;iter;iter=iter->next) + { + ClutterTextMapping *mapping = iter->data; + + if ( + (mapping->keyval == keyval) && + ( + (mapping->state == 0) || + (mapping->state && (kev->modifier_state & mapping->state)) + ) + ) + { + if (!g_str_equal (mapping->action, "activate") || + priv->activatable) + return clutter_text_action (CLUTTER_TEXT (actor), mapping->action, (ClutterEvent*)kev); + } + } + + { + gunichar key_unichar = clutter_key_event_unicode (kev); + + if (key_unichar == '\r') /* return is reported as CR we want LF */ + key_unichar = '\n'; + if (g_unichar_validate (key_unichar)) + { + clutter_text_insert_unichar (CLUTTER_TEXT (actor), key_unichar); + return TRUE; + } + } + return FALSE; +} + diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h new file mode 100644 index 000000000..908275cc2 --- /dev/null +++ b/clutter/clutter-text.h @@ -0,0 +1,136 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2006-2008 OpenedHand + * + * Authored By Øyvind Kolås + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CLUTTER_TEXT_H__ +#define __CLUTTER_TEXT_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_TEXT (clutter_text_get_type ()) + +#define CLUTTER_TEXT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + CLUTTER_TYPE_TEXT, ClutterText)) + +#define CLUTTER_TEXT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + CLUTTER_TYPE_TEXT, ClutterTextClass)) + +#define CLUTTER_IS_TEXT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + CLUTTER_TYPE_TEXT)) + +#define CLUTTER_IS_TEXT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + CLUTTER_TYPE_TEXT)) + +#define CLUTTER_TEXT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + CLUTTER_TYPE_TEXT, ClutterTextClass)) + +typedef struct _ClutterText ClutterText; +typedef struct _ClutterTextPrivate ClutterTextPrivate; +typedef struct _ClutterTextClass ClutterTextClass; + +struct _ClutterText +{ + ClutterLabel parent_instance; + + /*< private >*/ + ClutterTextPrivate *priv; +}; + +struct _ClutterTextClass +{ + ClutterLabelClass parent_class; + + void (* text_changed) (ClutterText *text); + void (* activate) (ClutterText *text); + void (* cursor_event) (ClutterText *text, + ClutterGeometry *geometry); +}; + +GType clutter_text_get_type (void) G_GNUC_CONST; + +ClutterActor *clutter_text_new_full (const gchar *font_name, + const gchar *text, + const ClutterColor *color); +ClutterActor *clutter_text_new_with_text (const gchar *font_name, + const gchar *text); + +void clutter_text_set_editable (ClutterText *label, + gboolean editable); +gboolean clutter_text_get_editable (ClutterText *label); +void clutter_text_set_activatable (ClutterText *label, + gboolean activatable); +gboolean clutter_text_get_activatable (ClutterText *label); + +gint clutter_text_get_cursor_position (ClutterText *label); +void clutter_text_set_cursor_position (ClutterText *label, + gint position); +void clutter_text_set_cursor_visible (ClutterText *label, + gboolean cursor_visible); +gboolean clutter_text_get_cursor_visible (ClutterText *label); +void clutter_text_set_cursor_color (ClutterText *text, + const ClutterColor *color); +void clutter_text_get_cursor_color (ClutterText *text, + ClutterColor *color); +void clutter_text_set_selectable (ClutterText *label, + gboolean selectable); +gboolean clutter_text_get_selectable (ClutterText *label); +void clutter_text_set_selection_bound (ClutterText *text, + gint selection_bound); +gint clutter_text_get_selection_bound (ClutterText *text); +gchar * clutter_text_get_selection (ClutterText *text); +void clutter_text_insert_unichar (ClutterText *ttext, + gunichar wc); + + +/* add a custom action that can be used in keybindings */ +void clutter_text_add_action (ClutterText *ttext, + const gchar *name, + gboolean (*func) (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event)); + +/* invoke an action registered by you or one of the tidy text default actions */ +gboolean clutter_text_action (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event); + +void clutter_text_mappings_clear (ClutterText *ttext); + +/* Add a keybinding to handle for the default keypress vfunc handler */ +void clutter_text_add_mapping (ClutterText *ttext, + guint keyval, + ClutterModifierType state, + const gchar *commandline); + +G_END_DECLS + +#endif /* __CLUTTER_TEXT_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 56c1e1789..664c88970 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -68,6 +68,7 @@ #include "clutter-stage.h" #include "clutter-stage-manager.h" #include "clutter-texture.h" +#include "clutter-text.h" #include "clutter-timeline.h" #include "clutter-timeout-pool.h" #include "clutter-types.h" diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index dcc4e673a..3a536162d 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -38,7 +38,8 @@ UNIT_TESTS = \ test-layout.c \ test-animation.c \ test-easing.c \ - test-binding-pool.c + test-binding-pool.c \ + test-text.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-text.c b/tests/interactive/test-text.c new file mode 100644 index 000000000..6a0393f44 --- /dev/null +++ b/tests/interactive/test-text.c @@ -0,0 +1,176 @@ +/* Try this text editor, it has issues but does work, + * try ctrl+A, ctrl+C, ctrl+V, ctrl+X as well as selecting text with + * mouse and keyboard, /Øyvind K + */ + +#include +#include + +#define FONT "Mono Bold 22px" + +static gchar *clipboard = NULL; + +static gchar *runes = +"ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ\n" +"ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ\n" +"ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬\n"; + + +static gboolean +select_all (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + gint len; + len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + + clutter_text_set_cursor_position (ttext, 0); + clutter_text_set_selection_bound (ttext, len); + + return TRUE; +} + +static gboolean +copy (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + if (clipboard) + g_free (clipboard); + clipboard = clutter_text_get_selection (ttext); + return TRUE; +} + +static gboolean +paste (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + const gchar *p; + if (!clipboard) + return TRUE; + + for (p=clipboard; *p!='\0'; p=g_utf8_next_char (p)) + { + clutter_text_insert_unichar (ttext, g_utf8_get_char_validated (p, 3)); + } + return TRUE; +} + +static gboolean +cut (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + clutter_text_action (ttext, "copy", NULL); + clutter_text_action (ttext, "truncate-selection", NULL); + return TRUE; +} + +static gboolean +pageup (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + gint i; + for (i=0;i<10;i++) + clutter_text_action (ttext, "move-up", event); + return TRUE; +} + +static gboolean +pagedown (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + gint i; + for (i=0;i<10;i++) + clutter_text_action (ttext, "move-down", event); + return TRUE; +} + +static void cursor_event (ClutterText *text, + ClutterGeometry *geometry) +{ + gint y; + + y = clutter_actor_get_y (CLUTTER_ACTOR (text)); + + if (y + geometry->y < 50) + { + clutter_actor_set_y (CLUTTER_ACTOR (text), y + 100); + } + else if (y + geometry->y > 720) + { + clutter_actor_set_y (CLUTTER_ACTOR (text), y - 100); + } + +} + +G_MODULE_EXPORT gint +test_text_main (gint argc, + gchar **argv) +{ + ClutterActor *stage; + ClutterActor *text; + ClutterColor text_color = {0x33, 0xff, 0x33, 0xff}; + ClutterColor cursor_color = {0xff, 0x33, 0x33, 0xff}; + ClutterColor background_color = {0x00, 0x00, 0x00, 0xff}; + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &background_color); + + text = clutter_text_new_full (FONT, "·", &text_color); + + clutter_container_add (CLUTTER_CONTAINER (stage), text, NULL); + clutter_actor_set_position (text, 40, 30); + clutter_actor_set_width (text, 1024); + clutter_label_set_line_wrap (CLUTTER_LABEL (text), TRUE); + + clutter_actor_set_reactive (text, TRUE); + clutter_stage_set_key_focus (CLUTTER_STAGE (stage), text); + + clutter_text_set_editable (CLUTTER_TEXT (text), TRUE); + clutter_text_set_selectable (CLUTTER_TEXT (text), TRUE); + clutter_text_set_cursor_color (CLUTTER_TEXT (text), &cursor_color); + + clutter_text_add_action (CLUTTER_TEXT (text), "select-all", select_all); + clutter_text_add_action (CLUTTER_TEXT (text), "copy", copy); + clutter_text_add_action (CLUTTER_TEXT (text), "paste", paste); + clutter_text_add_action (CLUTTER_TEXT (text), "cut", cut); + clutter_text_add_action (CLUTTER_TEXT (text), "pageup", pageup); + clutter_text_add_action (CLUTTER_TEXT (text), "pagedown", pagedown); + + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_a, CLUTTER_CONTROL_MASK, "select-all"); + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_c, CLUTTER_CONTROL_MASK, "copy"); + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_v, CLUTTER_CONTROL_MASK, "paste"); + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_x, CLUTTER_CONTROL_MASK, "cut"); + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_Page_Up, 0, "pageup"); + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_Page_Down, 0, "pagedown"); + + if (argv[1]) + { + gchar *utf8; + g_file_get_contents (argv[1], &utf8, NULL, NULL); + clutter_label_set_text (CLUTTER_LABEL (text), utf8); + } + else + { + clutter_label_set_text (CLUTTER_LABEL (text), runes); + } + + g_signal_connect (text, "cursor-event", G_CALLBACK (cursor_event), NULL); + + clutter_actor_set_size (stage, 1024, 768); + clutter_actor_show (stage); + + clutter_main (); + return 0; +} From f9bf187e5d0268886a96a40911debd21b666e5e0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:15:11 +0000 Subject: [PATCH 013/147] Use internal headers Since ClutterText is part of Clutter, it can use the internal and private headers instead of the catch-all clutter.h. --- clutter/clutter-text.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 6733fe1bd..da0612934 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -33,7 +33,22 @@ #include #include "clutter-text.h" -#include + +#include "clutter-main.h" +#include "clutter-enum-types.h" +#include "clutter-private.h" +#include "clutter-debug.h" +#include "clutter-units.h" + +#include "cogl-pango.h" + +#define DEFAULT_FONT_NAME "Sans 10" + +/* Probably move into main */ +static PangoContext *_context = NULL; + +static const ClutterColor default_cursor_color = { 0, 0, 0, 255 }; +static const ClutterColor default_text_color = { 0, 0, 0, 255 }; static gboolean clutter_text_key_press (ClutterActor *actor, ClutterKeyEvent *kev); From a91bb5450f029200635d9fdaf470c27046ebdeae Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:15:56 +0000 Subject: [PATCH 014/147] Group all the integer private fields Integer fields using the ": " notation should be packed together so that the compiler can optimize the structure size correctly. --- clutter/clutter-text.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index da0612934..24e2e82ff 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -34,6 +34,7 @@ #include "clutter-text.h" +#include "clutter-keysyms.h" #include "clutter-main.h" #include "clutter-enum-types.h" #include "clutter-private.h" @@ -87,19 +88,19 @@ G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_LABEL); struct _ClutterTextPrivate { - gboolean editable; - gboolean cursor_visible; - gboolean activatable; - gboolean selectable; + guint editable : 1; + guint cursor_visible : 1; + guint activatable : 1; + guint selectable : 1; + guint in_select_drag : 1; + guint cursor_color_set : 1; gint position; /* current cursor position */ gint selection_bound; /* current 'other end of selection' position */ - gboolean in_select_drag; gint x_pos; /* the x position in the pangolayout, used to * avoid drifting when repeatedly moving up|down */ - gboolean cursor_color_set; ClutterColor cursor_color; ClutterGeometry cursor_pos; /* Where to draw the cursor */ @@ -879,7 +880,7 @@ clutter_text_class_init (ClutterTextClass *klass) g_signal_new ("text-changed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, text_changed), + G_STRUCT_OFFSET (ClutterTextClass, text_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -889,7 +890,7 @@ clutter_text_class_init (ClutterTextClass *klass) g_signal_new ("cursor-event", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, cursor_event), + G_STRUCT_OFFSET (ClutterTextClass, cursor_event), NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, @@ -909,7 +910,7 @@ clutter_text_class_init (ClutterTextClass *klass) g_signal_new ("activate", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, activate), + G_STRUCT_OFFSET (ClutterTextClass, activate), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); From 4d62da80adf7347d1be25cdc42255438e5d8fb64 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:17:26 +0000 Subject: [PATCH 015/147] Coalesce ClutterLabel API usage ClutterText replaces ClutterLabel, so it should expose the same kind of API - ideally with the minimal amount of changes, so that the porting is trivial. --- clutter/clutter-text.c | 693 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 608 insertions(+), 85 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 24e2e82ff..34ee928e0 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -45,6 +45,18 @@ #define DEFAULT_FONT_NAME "Sans 10" +/* We need at least three cached layouts to run the allocation without + regenerating a new layout. First the layout will be generated at + full width to get the preferred width, then it will be generated at + the preferred width to get the preferred height and then it might + be regenerated at a different width to get the height for the + actual allocated width */ +#define N_CACHED_LAYOUTS 3 + +#define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) + +typedef struct _LayoutCache LayoutCache; + /* Probably move into main */ static PangoContext *_context = NULL; @@ -71,6 +83,14 @@ static void clutter_text_get_property (GObject *gobject, GParamSpec *pspec); static void clutter_text_finalize (GObject *gobject); +G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *text); +void clutter_text_set_text (ClutterText *text, const gchar *str); +PangoLayout *clutter_text_get_layout (ClutterText *text); +void clutter_text_set_color (ClutterText *text, const ClutterColor *color); +void clutter_text_get_color (ClutterText *text, ClutterColor *color); +void clutter_text_set_font_name (ClutterText *text, const gchar *font_name); +G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *text); + static void init_commands (ClutterText *ttext); static void init_mappings (ClutterText *ttext); @@ -84,10 +104,45 @@ clutter_text_truncate_selection (ClutterText *ttext, const gchar *commandline, ClutterEvent *event); -G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_LABEL); +G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_ACTOR); + +struct _LayoutCache +{ + /* Cached layout. Pango internally caches the computed extents when + they are requested so there is no need to cache that as well */ + PangoLayout *layout; + + /* The width that used to generate this layout */ + ClutterUnit width; + + /* A number representing the age of this cache (so that when a new + layout is needed the last used cache is replaced) */ + guint age; +}; struct _ClutterTextPrivate { + PangoFontDescription *font_desc; + + gchar *text; + gchar *font_name; + + ClutterColor text_color; + + LayoutCache cached_layouts[N_CACHED_LAYOUTS]; + guint cache_age; + + PangoAttrList *attrs; + PangoAttrList *effective_attrs; + + guint alignment : 2; + guint wrap : 1; + guint use_underline : 1; + guint use_markup : 1; + guint ellipsize : 3; + guint single_line_mode : 1; + guint wrap_mode : 3; + guint justify : 1; guint editable : 1; guint cursor_visible : 1; guint activatable : 1; @@ -111,14 +166,20 @@ struct _ClutterTextPrivate */ }; -#define CLUTTER_TEXT_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ - CLUTTER_TYPE_TEXT, \ - ClutterTextPrivate)) - enum { PROP_0, + + PROP_FONT_NAME, + PROP_TEXT, + PROP_COLOR, + PROP_ATTRIBUTES, + PROP_USE_MARKUP, + PROP_ALIGNMENT, + PROP_WRAP, + PROP_WRAP_MODE, + PROP_JUSTIFY, + PROP_ELLIPSIZE, PROP_POSITION, PROP_SELECTION_BOUND, PROP_CURSOR_VISIBLE, @@ -134,10 +195,11 @@ enum TEXT_CHANGED, CURSOR_EVENT, ACTIVATE, + LAST_SIGNAL }; -static guint label_signals[LAST_SIGNAL] = { 0, }; +static guint text_signals[LAST_SIGNAL] = { 0, }; #define offset_real(text, pos) \ (pos==-1?g_utf8_strlen(text, -1):pos) \ @@ -228,11 +290,164 @@ static void init_mappings (ClutterText *ttext) clutter_text_add_mapping (ttext, CLUTTER_ISO_Enter,0,"activate"); } +static PangoLayout * +clutter_text_create_layout_no_cache (ClutterText *text, + ClutterUnit allocation_width) +{ + ClutterTextPrivate *priv = text->priv; + PangoLayout *layout; + + if (G_UNLIKELY (_context == NULL)) + _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); + + layout = pango_layout_new (_context); + + pango_layout_set_font_description (layout, priv->font_desc); + + if (priv->effective_attrs) + pango_layout_set_attributes (layout, priv->effective_attrs); + + pango_layout_set_alignment (layout, priv->alignment); + pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode); + pango_layout_set_justify (layout, priv->justify); + + if (priv->text) + { + if (!priv->use_markup) + pango_layout_set_text (layout, priv->text, -1); + else + pango_layout_set_markup (layout, priv->text, -1); + } + + if (allocation_width > 0 && + (priv->ellipsize != PANGO_ELLIPSIZE_NONE || priv->wrap)) + { + int layout_width, layout_height; + + pango_layout_get_size (layout, &layout_width, &layout_height); + + /* No need to set ellipsize or wrap if we already have enough + * space, since we don't want to make the layout wider than it + * would be otherwise. + */ + + if (CLUTTER_UNITS_FROM_PANGO_UNIT (layout_width) > allocation_width) + { + if (priv->ellipsize != PANGO_ELLIPSIZE_NONE) + { + gint width; + + width = allocation_width > 0 + ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) + : -1; + + pango_layout_set_ellipsize (layout, priv->ellipsize); + pango_layout_set_width (layout, width); + } + else if (priv->wrap) + { + gint width; + + width = allocation_width > 0 + ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) + : -1; + + pango_layout_set_wrap (layout, priv->wrap_mode); + pango_layout_set_width (layout, width); + } + } + } + + return layout; +} + +static void +clutter_text_dirty_cache (ClutterText *text) +{ + ClutterTextPrivate *priv = text->priv; + int i; + + /* Delete the cached layouts so they will be recreated the next time + they are needed */ + for (i = 0; i < N_CACHED_LAYOUTS; i++) + if (priv->cached_layouts[i].layout) + { + g_object_unref (priv->cached_layouts[i].layout); + priv->cached_layouts[i].layout = NULL; + } +} + +/* + * clutter_text_create_layout: + * @text: a #ClutterText + * @allocation_width: the allocation width + * + * Like clutter_text_create_layout_no_cache(), but will also ensure + * the glyphs cache. If a previously cached layout generated using the + * same width is available then that will be used instead of + * generating a new one. + */ +static PangoLayout * +clutter_text_create_layout (ClutterText *text, + ClutterUnit allocation_width) +{ + ClutterTextPrivate *priv = text->priv; + LayoutCache *oldest_cache = priv->cached_layouts; + gboolean found_free_cache = FALSE; + int i; + + /* Search for a cached layout with the same width and keep track of + the oldest one */ + for (i = 0; i < N_CACHED_LAYOUTS; i++) + { + if (priv->cached_layouts[i].layout == NULL) + { + /* Always prefer free cache spaces */ + found_free_cache = TRUE; + oldest_cache = priv->cached_layouts + i; + } + /* If this cached layout is using the same width then we can + just return that directly */ + else if (priv->cached_layouts[i].width == allocation_width) + { + CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache hit for width %i", + text, + CLUTTER_UNITS_TO_DEVICE (allocation_width)); + + return priv->cached_layouts[i].layout; + } + else if (!found_free_cache && + (priv->cached_layouts[i].age < oldest_cache->age)) + { + oldest_cache = priv->cached_layouts + i; + } + } + + CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache miss for width %i", + text, + CLUTTER_UNITS_TO_DEVICE (allocation_width)); + + /* If we make it here then we didn't have a cached version so we + need to recreate the layout */ + if (oldest_cache->layout) + g_object_unref (oldest_cache->layout); + + oldest_cache->layout = + clutter_text_create_layout_no_cache (text, allocation_width); + + cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout); + + /* Mark the 'time' this cache was created and advance the time */ + oldest_cache->age = priv->cache_age++; + oldest_cache->width = allocation_width; + + return oldest_cache->layout; +} static gint clutter_text_coords_to_position (ClutterText *text, - gint x, - gint y) + gint x, + gint y) { gint index_; gint px, py; @@ -241,7 +456,7 @@ clutter_text_coords_to_position (ClutterText *text, px = x * PANGO_SCALE; py = y * PANGO_SCALE; - pango_layout_xy_to_index (clutter_label_get_layout (CLUTTER_LABEL (text)), + pango_layout_xy_to_index (clutter_text_get_layout (text), px, py, &index_, &trailing); return index_ + trailing; @@ -259,7 +474,7 @@ clutter_text_position_to_coords (ClutterText *ttext, PangoRectangle rect; const gchar *text; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); priv = ttext->priv; @@ -276,7 +491,7 @@ clutter_text_position_to_coords (ClutterText *ttext, index_ = strlen (text); pango_layout_get_cursor_pos ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), index_, &rect, NULL); if (x) @@ -304,7 +519,7 @@ clutter_text_ensure_cursor_position (ClutterText *ttext) priv->cursor_pos.width = 2; priv->cursor_pos.height = cursor_height; - g_signal_emit (ttext, label_signals[CURSOR_EVENT], 0, &priv->cursor_pos); + g_signal_emit (ttext, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } gint @@ -318,7 +533,6 @@ void clutter_text_set_cursor_position (ClutterText *ttext, gint position) { - ClutterLabel *label = CLUTTER_LABEL (ttext); const gchar *text; ClutterTextPrivate *priv; gint len; @@ -327,7 +541,7 @@ clutter_text_set_cursor_position (ClutterText *ttext, priv = ttext->priv; - text = clutter_label_get_text (label); + text = clutter_text_get_text (ttext); if (text == NULL) return; @@ -347,7 +561,7 @@ clutter_text_truncate_selection (ClutterText *ttext, const gchar *commandline, ClutterEvent *event) { - const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (ttext)); + const gchar *utf8 = clutter_text_get_text (ttext); ClutterTextPrivate *priv; gint start_index; gint end_index; @@ -397,14 +611,14 @@ clutter_text_insert_unichar (ClutterText *ttext, g_object_ref (ttext); - old_text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + old_text = clutter_text_get_text (ttext); new = g_string_new (old_text); pos = offset_to_bytes (old_text, priv->position); new = g_string_insert_unichar (new, pos, wc); - clutter_label_set_text (CLUTTER_LABEL (ttext), new->str); + clutter_text_set_text (ttext, new->str); if (priv->position >= 0) { @@ -416,7 +630,7 @@ clutter_text_insert_unichar (ClutterText *ttext, g_object_unref (ttext); - g_signal_emit (G_OBJECT (ttext), label_signals[TEXT_CHANGED], 0); + g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); } void @@ -433,7 +647,7 @@ clutter_text_delete_text (ClutterText *ttext, g_return_if_fail (CLUTTER_IS_TEXT (ttext)); priv = ttext->priv; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); if (end_pos == -1) { @@ -450,10 +664,10 @@ clutter_text_delete_text (ClutterText *ttext, new = g_string_erase (new, start_bytes, end_bytes - start_bytes); - clutter_label_set_text (CLUTTER_LABEL (ttext), new->str); + clutter_text_set_text (ttext, new->str); g_string_free (new, TRUE); - g_signal_emit (G_OBJECT (ttext), label_signals[TEXT_CHANGED], 0); + g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); } static void @@ -488,6 +702,15 @@ clutter_text_set_property (GObject *gobject, switch (prop_id) { + case PROP_TEXT: + clutter_text_set_text (ttext, g_value_get_string (value)); + break; + case PROP_COLOR: + clutter_text_set_color (ttext, clutter_value_get_color (value)); + break; + case PROP_FONT_NAME: + clutter_text_set_font_name (ttext, g_value_get_string (value)); + break; case PROP_POSITION: clutter_text_set_cursor_position (ttext, g_value_get_int (value)); break; @@ -528,15 +751,20 @@ clutter_text_get_property (GObject *gobject, switch (prop_id) { + case PROP_TEXT: + g_value_set_string (value, priv->text); + break; + case PROP_FONT_NAME: + g_value_set_string (value, priv->font_name); + break; + case PROP_COLOR: + clutter_value_set_color (value, &priv->text_color); + break; case PROP_CURSOR_VISIBLE: g_value_set_boolean (value, priv->cursor_visible); break; case PROP_CURSOR_COLOR: - { - ClutterColor color; - clutter_text_get_cursor_color (CLUTTER_TEXT (gobject), &color); - g_value_set_boxed (value, &color); - } + clutter_value_set_color (value, &priv->cursor_color); break; case PROP_POSITION: g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); @@ -560,10 +788,8 @@ clutter_text_get_property (GObject *gobject, } static void -cursor_paint (ClutterActor *actor, - gpointer user_data) +cursor_paint (ClutterText *ttext) { - ClutterText *ttext = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = ttext->priv; if (priv->editable && @@ -579,7 +805,7 @@ cursor_paint (ClutterActor *actor, else { ClutterColor color; - clutter_label_get_color (CLUTTER_LABEL (actor), &color); + clutter_text_get_color (ttext, &color); cogl_set_source_color4ub (color.red, color.green, color.blue, @@ -603,7 +829,7 @@ cursor_paint (ClutterActor *actor, gint lines; gint start_index; gint end_index; - const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (ttext)); + const gchar *utf8 = clutter_text_get_text (ttext); start_index = offset_to_bytes (utf8, priv->position); end_index = offset_to_bytes (utf8, priv->selection_bound); @@ -615,7 +841,7 @@ cursor_paint (ClutterActor *actor, end_index = temp; } - PangoLayout *layout = clutter_label_get_layout (CLUTTER_LABEL (ttext)); + PangoLayout *layout = clutter_text_get_layout (ttext); lines = pango_layout_get_line_count (layout); gint line_no; for (line_no = 0; line_no < lines; line_no++) @@ -666,7 +892,7 @@ clutter_text_press (ClutterActor *actor, gint index_; const gchar *text; - text = clutter_label_get_text (CLUTTER_LABEL (actor)); + text = clutter_text_get_text (ttext); x = CLUTTER_UNITS_FROM_INT (bev->x); y = CLUTTER_UNITS_FROM_INT (bev->y); @@ -712,7 +938,7 @@ clutter_text_motion (ClutterActor *actor, return FALSE; } - text = clutter_label_get_text (CLUTTER_LABEL (actor)); + text = clutter_text_get_text (ttext); x = CLUTTER_UNITS_FROM_INT (mev->x); y = CLUTTER_UNITS_FROM_INT (mev->y); @@ -750,13 +976,124 @@ clutter_text_release (ClutterActor *actor, return FALSE; } +static void +clutter_text_paint (ClutterActor *self) +{ + ClutterText *text = CLUTTER_TEXT (self); + ClutterTextPrivate *priv = text->priv; + PangoLayout *layout; + ClutterActorBox alloc = { 0, }; + CoglColor color = { 0, }; + + if (priv->font_desc == NULL || priv->text == NULL) + { + CLUTTER_NOTE (ACTOR, "desc: %p, text %p", + priv->font_desc ? priv->font_desc : 0x0, + priv->text ? priv->text : 0x0); + return; + } + + cursor_paint (text); + + CLUTTER_NOTE (PAINT, "painting text (text:`%s')", priv->text); + + clutter_actor_get_allocation_box (self, &alloc); + layout = clutter_text_create_layout (text, alloc.x2 - alloc.x1); + + cogl_color_set_from_4ub (&color, + priv->text_color.red, + priv->text_color.green, + priv->text_color.blue, + clutter_actor_get_paint_opacity (self)); + cogl_pango_render_layout (layout, 0, 0, &color, 0); +} + +static void +clutter_text_get_preferred_width (ClutterActor *self, + ClutterUnit for_height, + ClutterUnit *min_width_p, + ClutterUnit *natural_width_p) +{ + ClutterText *text = CLUTTER_TEXT (self); + ClutterTextPrivate *priv = text->priv; + PangoRectangle logical_rect = { 0, }; + PangoLayout *layout; + ClutterUnit layout_width; + + layout = clutter_text_create_layout (text, -1); + + pango_layout_get_extents (layout, NULL, &logical_rect); + + layout_width = logical_rect.width > 0 + ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.width) + : 1; + + if (min_width_p) + { + if (priv->wrap || priv->ellipsize) + *min_width_p = 1; + else + *min_width_p = layout_width; + } + + if (natural_width_p) + *natural_width_p = layout_width; +} + +static void +clutter_text_get_preferred_height (ClutterActor *self, + ClutterUnit for_width, + ClutterUnit *min_height_p, + ClutterUnit *natural_height_p) +{ + ClutterText *text = CLUTTER_TEXT (self); + + if (for_width == 0) + { + if (min_height_p) + *min_height_p = 0; + + if (natural_height_p) + *natural_height_p = 0; + } + else + { + PangoLayout *layout; + PangoRectangle logical_rect = { 0, }; + ClutterUnit height; + + layout = clutter_text_create_layout (text, for_width); + + pango_layout_get_extents (layout, NULL, &logical_rect); + height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.height); + + if (min_height_p) + *min_height_p = height; + + if (natural_height_p) + *natural_height_p = height; + } +} + +static void +clutter_text_allocate (ClutterActor *self, + const ClutterActorBox *box, + gboolean origin_changed) +{ + ClutterText *text = CLUTTER_TEXT (self); + ClutterActorClass *parent_class; + + /* Ensure that there is a cached layout with the right width so that + we don't need to create the text during the paint run */ + clutter_text_create_layout (text, box->x2 - box->x1); + + parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class); + parent_class->allocate (self, box, origin_changed); +} + static void clutter_text_constructed (GObject *object) { - g_signal_connect (CLUTTER_ACTOR (object), - "paint", G_CALLBACK (cursor_paint), - NULL); - if (G_OBJECT_CLASS (clutter_text_parent_class)->constructed != NULL) G_OBJECT_CLASS (clutter_text_parent_class)->constructed (object); } @@ -766,17 +1103,51 @@ clutter_text_class_init (ClutterTextClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + GParamSpec *pspec; gobject_class->set_property = clutter_text_set_property; gobject_class->get_property = clutter_text_get_property; gobject_class->constructed = clutter_text_constructed; gobject_class->finalize = clutter_text_finalize; + actor_class->paint = clutter_text_paint; + actor_class->get_preferred_width = clutter_text_get_preferred_width; + actor_class->get_preferred_height = clutter_text_get_preferred_height; + actor_class->allocate = clutter_text_allocate; actor_class->key_press_event = clutter_text_key_press; actor_class->button_press_event = clutter_text_press; actor_class->button_release_event = clutter_text_release; actor_class->motion_event = clutter_text_motion; + /** + * ClutterText:font-name: + * + * The font to be used by the #ClutterText, as a string + * that can be parsed by pango_font_description_from_string(). + * + * Since: 0.2 + */ + pspec = g_param_spec_string ("font-name", + "Font Name", + "The font to be used by the text", + NULL, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec); + + pspec = g_param_spec_string ("text", + "Text", + "The text to render", + NULL, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_TEXT, pspec); + + pspec = clutter_param_spec_color ("color", + "Font Color", + "Color of the font used by the text", + &default_text_color, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_COLOR, pspec); + /** * ClutterText:editable: * @@ -876,7 +1247,7 @@ clutter_text_class_init (ClutterTextClass *klass) * * The ::text-changed signal is emitted after @entry's text changes */ - label_signals[TEXT_CHANGED] = + text_signals[TEXT_CHANGED] = g_signal_new ("text-changed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, @@ -886,7 +1257,7 @@ clutter_text_class_init (ClutterTextClass *klass) G_TYPE_NONE, 0); - label_signals[CURSOR_EVENT] = + text_signals[CURSOR_EVENT] = g_signal_new ("cursor-event", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, @@ -906,7 +1277,7 @@ clutter_text_class_init (ClutterTextClass *klass) * * Since: 0.4 */ - label_signals[ACTIVATE] = + text_signals[ACTIVATE] = g_signal_new ("activate", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, @@ -925,19 +1296,21 @@ clutter_text_init (ClutterText *self) ClutterTextPrivate *priv; self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); + priv->x_pos = -1; priv->cursor_visible = TRUE; priv->editable = FALSE; priv->cursor_color_set = FALSE; + init_commands (self); /* FIXME: free */ init_mappings (self); /* FIXME: free */ } ClutterActor * clutter_text_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color) + const gchar *text, + const ClutterColor *color) { return g_object_new (CLUTTER_TYPE_TEXT, "font-name", font_name, @@ -958,81 +1331,81 @@ clutter_text_new_with_text (const gchar *font_name, void -clutter_text_set_editable (ClutterText *label, - gboolean editable) +clutter_text_set_editable (ClutterText *text, + gboolean editable) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; priv->editable = editable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); } gboolean -clutter_text_get_editable (ClutterText *label) +clutter_text_get_editable (ClutterText *text) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; return priv->editable; } void -clutter_text_set_selectable (ClutterText *label, +clutter_text_set_selectable (ClutterText *text, gboolean selectable) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; priv->selectable = selectable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); } gboolean -clutter_text_get_selectable (ClutterText *label) +clutter_text_get_selectable (ClutterText *text) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; return priv->selectable; } void -clutter_text_set_activatable (ClutterText *label, +clutter_text_set_activatable (ClutterText *text, gboolean activatable) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; priv->activatable = activatable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); } gboolean -clutter_text_get_activatable (ClutterText *label) +clutter_text_get_activatable (ClutterText *text) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; return priv->activatable; } void -clutter_text_set_cursor_visible (ClutterText *label, +clutter_text_set_cursor_visible (ClutterText *text, gboolean cursor_visible) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; priv->cursor_visible = cursor_visible; - clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); } gboolean -clutter_text_get_cursor_visible (ClutterText *label) +clutter_text_get_cursor_visible (ClutterText *text) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; return priv->cursor_visible; } @@ -1042,7 +1415,7 @@ clutter_text_set_cursor_color (ClutterText *text, { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_LABEL (text)); + g_return_if_fail (CLUTTER_IS_TEXT (text)); g_return_if_fail (color != NULL); priv = text->priv; @@ -1067,7 +1440,7 @@ clutter_text_get_cursor_color (ClutterText *text, { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_LABEL (text)); + g_return_if_fail (CLUTTER_IS_TEXT (text)); g_return_if_fail (color != NULL); priv = text->priv; @@ -1091,7 +1464,7 @@ clutter_text_get_selection (ClutterText *text) { ClutterTextPrivate *priv; - const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (text)); + const gchar *utf8 = clutter_text_get_text (text); gchar *str; gint len; gint start_index; @@ -1148,7 +1521,7 @@ clutter_text_action_activate (ClutterText *ttext, const gchar *commandline, ClutterEvent *event) { - g_signal_emit (G_OBJECT (ttext), label_signals[ACTIVATE], 0); + g_signal_emit (G_OBJECT (ttext), text_signals[ACTIVATE], 0); return TRUE; } @@ -1167,7 +1540,7 @@ clutter_text_action_move_left (ClutterText *ttext, ClutterTextPrivate *priv = ttext->priv; gint pos = priv->position; gint len; - len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + len = g_utf8_strlen (clutter_text_get_text (ttext), -1); if (pos != 0 && len !=0) { @@ -1198,7 +1571,7 @@ clutter_text_action_move_right (ClutterText *ttext, gint pos; gint len; - len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + len = g_utf8_strlen (clutter_text_get_text (ttext), -1); pos = priv->position; if (pos != -1 && len !=0) @@ -1231,10 +1604,10 @@ clutter_text_action_move_up (ClutterText *ttext, const gchar *text; PangoLayoutLine *layout_line; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); pango_layout_index_to_line_x ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), offset_to_bytes (text, priv->position), 0, &line_no, @@ -1250,7 +1623,7 @@ clutter_text_action_move_up (ClutterText *ttext, return FALSE; layout_line = pango_layout_get_line_readonly ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), line_no); if (!layout_line) @@ -1282,10 +1655,10 @@ clutter_text_action_move_down (ClutterText *ttext, const gchar *text; PangoLayoutLine *layout_line; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); pango_layout_index_to_line_x ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), offset_to_bytes (text, priv->position), 0, &line_no, @@ -1297,7 +1670,7 @@ clutter_text_action_move_down (ClutterText *ttext, priv->x_pos = x; layout_line = pango_layout_get_line_readonly ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), line_no + 1); if (!layout_line) @@ -1358,18 +1731,18 @@ clutter_text_action_move_start_line (ClutterText *ttext, PangoLayoutLine *layout_line; gint position; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); pango_layout_index_to_line_x ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), offset_to_bytes (text, priv->position), 0, &line_no, NULL); layout_line = pango_layout_get_line_readonly ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), line_no); pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); @@ -1397,19 +1770,19 @@ clutter_text_action_move_end_line (ClutterText *ttext, PangoLayoutLine *layout_line; gint position; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); index_ = offset_to_bytes (text, priv->position); pango_layout_index_to_line_x ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), index_, 0, &line_no, NULL); layout_line = pango_layout_get_line_readonly ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), line_no); pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); @@ -1439,7 +1812,7 @@ clutter_text_action_delete_next (ClutterText *ttext, return TRUE; priv = ttext->priv; pos = priv->position; - len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + len = g_utf8_strlen (clutter_text_get_text (ttext), -1); if (len && pos != -1 && pos < len) clutter_text_delete_text (ttext, pos, pos+1);; @@ -1459,7 +1832,7 @@ clutter_text_action_delete_previous (ClutterText *ttext, return TRUE; priv = ttext->priv; pos = priv->position; - len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + len = g_utf8_strlen (clutter_text_get_text (ttext), -1); if (pos != 0 && len !=0) { @@ -1575,3 +1948,153 @@ clutter_text_key_press (ClutterActor *actor, return FALSE; } +G_CONST_RETURN gchar * +clutter_text_get_font_name (ClutterText *text) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + + return text->priv->font_name; +} + +void +clutter_text_set_font_name (ClutterText *text, + const gchar *font_name) +{ + ClutterTextPrivate *priv; + PangoFontDescription *desc; + + g_return_if_fail (CLUTTER_IS_TEXT (text)); + + if (!font_name || font_name[0] == '\0') + font_name = DEFAULT_FONT_NAME; + + priv = text->priv; + + if (priv->font_name && strcmp (priv->font_name, font_name) == 0) + return; + + desc = pango_font_description_from_string (font_name); + if (!desc) + { + g_warning ("Attempting to create a PangoFontDescription for " + "font name `%s', but failed.", + font_name); + return; + } + + g_free (priv->font_name); + priv->font_name = g_strdup (font_name); + + if (priv->font_desc) + pango_font_description_free (priv->font_desc); + + priv->font_desc = desc; + + clutter_text_dirty_cache (text); + + if (priv->text && priv->text[0] != '\0') + clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + + g_object_notify (G_OBJECT (text), "font-name"); +} + +G_CONST_RETURN gchar * +clutter_text_get_text (ClutterText *text) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + + return text->priv->text; +} + +void +clutter_text_set_text (ClutterText *text, + const gchar *str) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (text)); + + priv = text->priv; + + g_free (priv->text); + priv->text = g_strdup (str); + + clutter_text_dirty_cache (text); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + + g_object_notify (G_OBJECT (text), "text"); +} + +PangoLayout * +clutter_text_get_layout (ClutterText *text) +{ + ClutterUnit width; + + g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + + width = clutter_actor_get_widthu (CLUTTER_ACTOR (text)); + + return clutter_text_create_layout (text, width); +} + +void +clutter_text_set_color (ClutterText *text, + const ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (color != NULL); + + priv = text->priv; + + priv->text_color = *color; + + if (CLUTTER_ACTOR_IS_VISIBLE (text)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + + g_object_notify (G_OBJECT (text), "color"); +} + +void +clutter_text_get_color (ClutterText *text, + ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (color != NULL); + + priv = text->priv; + + *color = priv->text_color; +} + +gboolean +clutter_text_get_line_wrap (ClutterText *text) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (text), FALSE); + + return text->priv->wrap; +} + +void +clutter_text_set_line_wrap (ClutterText *text, + gboolean line_wrap) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (text)); + + priv = text->priv; + + if (priv->wrap != line_wrap) + { + priv->wrap = line_wrap; + + clutter_text_dirty_cache (text); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + } +} From 5ac16652df4fc165c6237464e6df250687bf023f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:18:05 +0000 Subject: [PATCH 016/147] Update the ClutterText interactive test Use ClutterText's own API instead of relying on the Label's API. ClutterText is not meant to be a subclass of ClutterLabel. --- tests/interactive/test-text.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/interactive/test-text.c b/tests/interactive/test-text.c index 6a0393f44..ce262dcfc 100644 --- a/tests/interactive/test-text.c +++ b/tests/interactive/test-text.c @@ -22,7 +22,7 @@ select_all (ClutterText *ttext, ClutterEvent *event) { gint len; - len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + len = g_utf8_strlen (clutter_text_get_text (ttext), -1); clutter_text_set_cursor_position (ttext, 0); clutter_text_set_selection_bound (ttext, len); @@ -126,7 +126,7 @@ test_text_main (gint argc, clutter_container_add (CLUTTER_CONTAINER (stage), text, NULL); clutter_actor_set_position (text, 40, 30); clutter_actor_set_width (text, 1024); - clutter_label_set_line_wrap (CLUTTER_LABEL (text), TRUE); + clutter_text_set_line_wrap (CLUTTER_TEXT (text), TRUE); clutter_actor_set_reactive (text, TRUE); clutter_stage_set_key_focus (CLUTTER_STAGE (stage), text); @@ -159,11 +159,11 @@ test_text_main (gint argc, { gchar *utf8; g_file_get_contents (argv[1], &utf8, NULL, NULL); - clutter_label_set_text (CLUTTER_LABEL (text), utf8); + clutter_text_set_text (CLUTTER_TEXT (text), utf8); } else { - clutter_label_set_text (CLUTTER_LABEL (text), runes); + clutter_text_set_text (CLUTTER_TEXT (text), runes); } g_signal_connect (text, "cursor-event", G_CALLBACK (cursor_event), NULL); From 437f96982e5754f25630570818d4a5173bd9a8ce Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:20:14 +0000 Subject: [PATCH 017/147] Expose the Text accessors as public API Fix up the header to expose ClutterText accessors for the main properties, matching ClutterLabel. --- clutter/clutter-text.c | 8 ------- clutter/clutter-text.h | 47 +++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 34ee928e0..87a5b855d 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -83,14 +83,6 @@ static void clutter_text_get_property (GObject *gobject, GParamSpec *pspec); static void clutter_text_finalize (GObject *gobject); -G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *text); -void clutter_text_set_text (ClutterText *text, const gchar *str); -PangoLayout *clutter_text_get_layout (ClutterText *text); -void clutter_text_set_color (ClutterText *text, const ClutterColor *color); -void clutter_text_get_color (ClutterText *text, ClutterColor *color); -void clutter_text_set_font_name (ClutterText *text, const gchar *font_name); -G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *text); - static void init_commands (ClutterText *ttext); static void init_mappings (ClutterText *ttext); diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 908275cc2..cd901bffa 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -18,11 +18,13 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + #ifndef __CLUTTER_TEXT_H__ #define __CLUTTER_TEXT_H__ @@ -31,27 +33,12 @@ G_BEGIN_DECLS -#define CLUTTER_TYPE_TEXT (clutter_text_get_type ()) - -#define CLUTTER_TEXT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_TEXT, ClutterText)) - -#define CLUTTER_TEXT_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_TEXT, ClutterTextClass)) - -#define CLUTTER_IS_TEXT(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_TEXT)) - -#define CLUTTER_IS_TEXT_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_TEXT)) - -#define CLUTTER_TEXT_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_TEXT, ClutterTextClass)) +#define CLUTTER_TYPE_TEXT (clutter_text_get_type ()) +#define CLUTTER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TEXT, ClutterText)) +#define CLUTTER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_TEXT, ClutterTextClass)) +#define CLUTTER_IS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TEXT)) +#define CLUTTER_IS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TEXT)) +#define CLUTTER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TEXT, ClutterTextClass)) typedef struct _ClutterText ClutterText; typedef struct _ClutterTextPrivate ClutterTextPrivate; @@ -83,6 +70,18 @@ ClutterActor *clutter_text_new_full (const gchar *font_name, ClutterActor *clutter_text_new_with_text (const gchar *font_name, const gchar *text); +G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *text); +void clutter_text_set_text (ClutterText *text, + const gchar *str); +PangoLayout * clutter_text_get_layout (ClutterText *text); +void clutter_text_set_color (ClutterText *text, + const ClutterColor *color); +void clutter_text_get_color (ClutterText *text, + ClutterColor *color); +void clutter_text_set_font_name (ClutterText *text, + const gchar *font_name); +G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *text); + void clutter_text_set_editable (ClutterText *label, gboolean editable); gboolean clutter_text_get_editable (ClutterText *label); From 40fb75052ba11b80b41590f17ab3b194df11ae3f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:21:39 +0000 Subject: [PATCH 018/147] Merge Label and Entry API into ClutterText ClutterText should expose both ClutterLabel and ClutterEntry functionality using an API that matches the one provided by those two classes. --- clutter/clutter-text.h | 142 +++++++++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 47 deletions(-) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index cd901bffa..054c941c3 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -46,69 +46,117 @@ typedef struct _ClutterTextClass ClutterTextClass; struct _ClutterText { - ClutterLabel parent_instance; - /*< private >*/ + ClutterActor parent_instance; + ClutterTextPrivate *priv; }; struct _ClutterTextClass { - ClutterLabelClass parent_class; + ClutterActorClass parent_class; - void (* text_changed) (ClutterText *text); - void (* activate) (ClutterText *text); - void (* cursor_event) (ClutterText *text, + void (* text_changed) (ClutterText *self); + void (* activate) (ClutterText *self); + void (* cursor_event) (ClutterText *self, ClutterGeometry *geometry); + + void (* _clutter_reserved1) (void); + void (* _clutter_reserved2) (void); + void (* _clutter_reserved3) (void); + void (* _clutter_reserved4) (void); + void (* _clutter_reserved5) (void); + void (* _clutter_reserved6) (void); + void (* _clutter_reserved7) (void); + void (* _clutter_reserved8) (void); }; GType clutter_text_get_type (void) G_GNUC_CONST; -ClutterActor *clutter_text_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color); -ClutterActor *clutter_text_new_with_text (const gchar *font_name, - const gchar *text); +ClutterActor * clutter_text_new_full (const gchar *font_name, + const gchar *text, + const ClutterColor *color); +ClutterActor * clutter_text_new_with_text (const gchar *font_name, + const gchar *text); -G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *text); -void clutter_text_set_text (ClutterText *text, - const gchar *str); -PangoLayout * clutter_text_get_layout (ClutterText *text); -void clutter_text_set_color (ClutterText *text, - const ClutterColor *color); -void clutter_text_get_color (ClutterText *text, - ClutterColor *color); -void clutter_text_set_font_name (ClutterText *text, - const gchar *font_name); -G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *text); +G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *self); +void clutter_text_set_text (ClutterText *self, + const gchar *text); +PangoLayout * clutter_text_get_layout (ClutterText *self); +void clutter_text_set_color (ClutterText *self, + const ClutterColor *color); +void clutter_text_get_color (ClutterText *self, + ClutterColor *color); +void clutter_text_set_font_name (ClutterText *self, + const gchar *font_name); +G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *self); -void clutter_text_set_editable (ClutterText *label, - gboolean editable); -gboolean clutter_text_get_editable (ClutterText *label); -void clutter_text_set_activatable (ClutterText *label, - gboolean activatable); -gboolean clutter_text_get_activatable (ClutterText *label); +void clutter_text_set_ellipsize (ClutterText *self, + PangoEllipsizeMode mode); +PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self); +void clutter_text_set_line_wrap (ClutterText *self, + gboolean wrap); +gboolean clutter_text_get_line_wrap (ClutterText *self); +void clutter_text_set_line_wrap_mode (ClutterText *self, + PangoWrapMode wrap_mode); +PangoWrapMode clutter_text_get_line_wrap_mode (ClutterText *self); +PangoLayout * clutter_text_get_layout (ClutterText *self); +void clutter_text_set_attributes (ClutterText *self, + PangoAttrList *attrs); +PangoAttrList * clutter_text_get_attributes (ClutterText *self); +void clutter_text_set_use_markup (ClutterText *self, + gboolean setting); +gboolean clutter_text_get_use_markup (ClutterText *self); +void clutter_text_set_alignment (ClutterText *self, + PangoAlignment alignment); +PangoAlignment clutter_text_get_alignment (ClutterText *self); +void clutter_text_set_justify (ClutterText *self, + gboolean justify); +gboolean clutter_text_get_justify (ClutterText *self); -gint clutter_text_get_cursor_position (ClutterText *label); -void clutter_text_set_cursor_position (ClutterText *label, - gint position); -void clutter_text_set_cursor_visible (ClutterText *label, - gboolean cursor_visible); -gboolean clutter_text_get_cursor_visible (ClutterText *label); -void clutter_text_set_cursor_color (ClutterText *text, - const ClutterColor *color); -void clutter_text_get_cursor_color (ClutterText *text, - ClutterColor *color); -void clutter_text_set_selectable (ClutterText *label, - gboolean selectable); -gboolean clutter_text_get_selectable (ClutterText *label); -void clutter_text_set_selection_bound (ClutterText *text, - gint selection_bound); -gint clutter_text_get_selection_bound (ClutterText *text); -gchar * clutter_text_get_selection (ClutterText *text); -void clutter_text_insert_unichar (ClutterText *ttext, - gunichar wc); +void clutter_text_insert_unichar (ClutterText *self, + gunichar wc); +void clutter_text_delete_chars (ClutterText *self, + guint len); +void clutter_text_insert_text (ClutterText *self, + const gchar *text, + gssize position); +void clutter_text_delete_text (ClutterText *self, + gssize start_pos, + gssize end_pos); +void clutter_text_set_editable (ClutterText *self, + gboolean editable); +gboolean clutter_text_get_editable (ClutterText *self); +void clutter_text_set_activatable (ClutterText *self, + gboolean activatable); +gboolean clutter_text_get_activatable (ClutterText *self); +gint clutter_text_get_cursor_position (ClutterText *self); +void clutter_text_set_cursor_position (ClutterText *self, + gint position); +void clutter_text_set_cursor_visible (ClutterText *self, + gboolean cursor_visible); +gboolean clutter_text_get_cursor_visible (ClutterText *self); +void clutter_text_set_cursor_color (ClutterText *self, + const ClutterColor *color); +void clutter_text_get_cursor_color (ClutterText *self, + ClutterColor *color); +void clutter_text_set_selectable (ClutterText *self, + gboolean selectable); +gboolean clutter_text_get_selectable (ClutterText *self); +void clutter_text_set_selection_bound (ClutterText *self, + gint selection_bound); +gint clutter_text_get_selection_bound (ClutterText *self); +gchar * clutter_text_get_selection (ClutterText *self); +void clutter_text_set_visibility (ClutterText *self, + gboolean visible); +gboolean clutter_text_get_visibility (ClutterText *self); +void clutter_text_set_invisible_char (ClutterText *self, + gunichar wc); +gunichar clutter_text_get_invisible_char (ClutterText *self); +void clutter_text_set_max_length (ClutterText *self, + gint max); +gint clutter_text_get_max_length (ClutterText *self); /* add a custom action that can be used in keybindings */ void clutter_text_add_action (ClutterText *ttext, From 430d1cf347beeaa619651f2a227d00e053b6b2da Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:22:46 +0000 Subject: [PATCH 019/147] Add Actor::grab_key_focus() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug 1003 - Add clutter_actor_take_key_focus() The grab_key_focus() method is just a simple wrapper around clutter_stage_take_key_focus() that removes the need to get the ClutterStage of an actor in order to set the key focus. Based on a patch by Xan López. Signed-off-by: Emmanuele Bassi Signed-off-by: Øyvind Kolås --- clutter/clutter-actor.c | 25 +++++++++++++++++++++++++ clutter/clutter-actor.h | 2 ++ 2 files changed, 27 insertions(+) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 424144436..6a30c1c11 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -7564,3 +7564,28 @@ clutter_actor_allocate_preferred_size (ClutterActor *self, clutter_actor_allocate (self, &actor_box, absolute_origin_changed); } + +/** + * clutter_actor_grab_key_focus: + * @self: a #ClutterActor + * + * Sets the key focus of the #ClutterStage including @self + * to this #ClutterActor. + * + * Since: 1.0 + */ +void +clutter_actor_grab_key_focus (ClutterActor *self) +{ + ClutterActor *parent; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + parent = clutter_actor_get_parent (self); + if (!parent) + return; + + parent = clutter_actor_get_stage (self); + if (parent && CLUTTER_IS_STAGE (parent)) + clutter_stage_set_key_focus (CLUTTER_STAGE (parent), self); +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index e964a9ef0..1874e283b 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -561,6 +561,8 @@ void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, gboolean clutter_actor_get_paint_visibility (ClutterActor *self); +void clutter_actor_grab_key_focus (ClutterActor *self); + G_END_DECLS #endif /* _HAVE_CLUTTER_ACTOR_H */ From c5f51f7027dbbac373587621a52fc6f95f529e69 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:31:48 +0000 Subject: [PATCH 020/147] Add Text::get_chars() method declaration Add the declaration for a clutter_text_get_chars() function that returns a portion of the contents of a Text actor. --- clutter/clutter-text.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 054c941c3..65c919796 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -124,6 +124,9 @@ void clutter_text_insert_text (ClutterText *self void clutter_text_delete_text (ClutterText *self, gssize start_pos, gssize end_pos); +gchar * clutter_text_get_chars (ClutterText *self, + gssize start_pos, + gssize end_pos); void clutter_text_set_editable (ClutterText *self, gboolean editable); gboolean clutter_text_get_editable (ClutterText *self); From fc168a34823567caead8e7cbfcf91b3f47b229e8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:33:10 +0000 Subject: [PATCH 021/147] Merge the ClutterLabel properties into ClutterText ClutterText should have the same properties as ClutterLabel. While at it, we can finally fix the disconnect between the wrap and wrap-mode properties and its accessors, that we perpetuated from GtkLabel. The ClutterText:line-wrap property and ClutterText:line-wrap-mode are mapped to the set_line_wrap(), get_line_wrap() and set_line_wrap_mode(), get_line_wrap_mode() accessor functions respectively. This should simplify bindings the Vala ones that map a property into a method. --- clutter/clutter-text.c | 1286 +++++++++++++++++++++++++++------------- 1 file changed, 873 insertions(+), 413 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 87a5b855d..4f2e3d7d6 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -46,11 +46,12 @@ #define DEFAULT_FONT_NAME "Sans 10" /* We need at least three cached layouts to run the allocation without - regenerating a new layout. First the layout will be generated at - full width to get the preferred width, then it will be generated at - the preferred width to get the preferred height and then it might - be regenerated at a different width to get the height for the - actual allocated width */ + * regenerating a new layout. First the layout will be generated at + * full width to get the preferred width, then it will be generated at + * the preferred width to get the preferred height and then it might + * be regenerated at a different width to get the height for the + * actual allocated width + */ #define N_CACHED_LAYOUTS 3 #define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) @@ -63,52 +64,22 @@ static PangoContext *_context = NULL; static const ClutterColor default_cursor_color = { 0, 0, 0, 255 }; static const ClutterColor default_text_color = { 0, 0, 0, 255 }; -static gboolean clutter_text_key_press (ClutterActor *actor, - ClutterKeyEvent *kev); -static gboolean clutter_text_position_to_coords (ClutterText *ttext, - gint position, - gint *x, - gint *y, - gint *cursor_height); -static gint clutter_text_coords_to_position (ClutterText *text, - gint x, - gint y); -static void clutter_text_set_property (GObject *gobject, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void clutter_text_get_property (GObject *gobject, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void clutter_text_finalize (GObject *gobject); - -static void init_commands (ClutterText *ttext); -static void init_mappings (ClutterText *ttext); - -void -clutter_text_delete_text (ClutterText *ttext, - gssize start_pos, - gssize end_pos); - -static gboolean -clutter_text_truncate_selection (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event); - G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_ACTOR); struct _LayoutCache { - /* Cached layout. Pango internally caches the computed extents when - they are requested so there is no need to cache that as well */ + /* Cached layout. Pango internally caches the computed extents + * when they are requested so there is no need to cache that as + * well + */ PangoLayout *layout; /* The width that used to generate this layout */ ClutterUnit width; - /* A number representing the age of this cache (so that when a new - layout is needed the last used cache is replaced) */ + /* A number representing the age of this cache (so that when a + * new layout is needed the last used cache is replaced) + */ guint age; }; @@ -124,8 +95,8 @@ struct _ClutterTextPrivate LayoutCache cached_layouts[N_CACHED_LAYOUTS]; guint cache_age; - PangoAttrList *attrs; - PangoAttrList *effective_attrs; + PangoAttrList *attrs; + PangoAttrList *effective_attrs; guint alignment : 2; guint wrap : 1; @@ -142,20 +113,28 @@ struct _ClutterTextPrivate guint in_select_drag : 1; guint cursor_color_set : 1; - gint position; /* current cursor position */ - gint selection_bound; - /* current 'other end of selection' position */ - gint x_pos; /* the x position in the pangolayout, used to - * avoid drifting when repeatedly moving up|down - */ - ClutterColor cursor_color; - ClutterGeometry cursor_pos; /* Where to draw the cursor */ + /* current cursor position */ + gint position; - GList *mappings; - GList *commands; /* each instance has it's own set of commands - so that actor specific actions can be added - to single actor classes - */ + /* current 'other end of selection' position */ + gint selection_bound; + + /* the x position in the pangolayout, used to + * avoid drifting when repeatedly moving up|down + */ + gint x_pos; + + /* Where to draw the cursor */ + ClutterGeometry cursor_pos; + ClutterColor cursor_color; + + ClutterColor selection_color; + + GList *mappings; + GList *commands; /* each instance has it's own set of commands + so that actor specific actions can be added + to single actor classes + */ }; enum @@ -165,11 +144,11 @@ enum PROP_FONT_NAME, PROP_TEXT, PROP_COLOR, - PROP_ATTRIBUTES, PROP_USE_MARKUP, + PROP_ATTRIBUTES, PROP_ALIGNMENT, - PROP_WRAP, - PROP_WRAP_MODE, + PROP_LINE_WRAP, + PROP_LINE_WRAP_MODE, PROP_JUSTIFY, PROP_ELLIPSIZE, PROP_POSITION, @@ -193,14 +172,15 @@ enum static guint text_signals[LAST_SIGNAL] = { 0, }; -#define offset_real(text, pos) \ - (pos==-1?g_utf8_strlen(text, -1):pos) \ +#define offset_real(text, pos) \ + (pos == -1 ? g_utf8_strlen (text, -1) : pos) -#define offset_to_bytes(text,pos)\ - (pos==-1?strlen(text):((gint)(g_utf8_offset_to_pointer (text, pos) - text))) +#define offset_to_bytes(text,pos) \ + (pos == -1 ? strlen (text) \ + : ((gint) (g_utf8_offset_to_pointer (text, pos) - text))) -#define bytes_to_offset(text, pos) \ - (g_utf8_pointer_to_offset (text, text + pos)) +#define bytes_to_offset(text, pos) \ + (g_utf8_pointer_to_offset (text, text + pos)) typedef struct TextCommand { @@ -216,51 +196,26 @@ typedef struct ClutterTextMapping { const gchar *action; } ClutterTextMapping; - void -clutter_text_mappings_clear (ClutterText *ttext) +clutter_text_mappings_clear (ClutterText *self) { - ClutterTextPrivate *priv = ttext->priv; - GList *iter; - for (iter = priv->mappings; iter; iter=iter->next) - { - g_free (iter->data); - } + ClutterTextPrivate *priv = self->priv; + + g_list_foreach (priv->mappings, (GFunc) g_free, NULL); g_list_free (priv->mappings); priv->mappings = NULL; } -void clutter_text_add_mapping (ClutterText *ttext, - guint keyval, - ClutterModifierType state, - const gchar *commandline) -{ - ClutterTextMapping *tmapping = g_new (ClutterTextMapping, 1); - ClutterTextPrivate *priv = ttext->priv; - tmapping->keyval = keyval; - tmapping->state = state; - tmapping->action = commandline; - priv->mappings = g_list_append (priv->mappings, tmapping); -} +static void init_commands (ClutterText *ttext); -void clutter_text_add_action (ClutterText *ttext, - const gchar *name, - gboolean (*func) (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event)) +static void +init_mappings (ClutterText *ttext) { - TextCommand *tcommand = g_new (TextCommand, 1); ClutterTextPrivate *priv = ttext->priv; - tcommand->name = name; - tcommand->func = func; - priv->commands = g_list_append (priv->commands, tcommand); -} -static void init_mappings (ClutterText *ttext) -{ - ClutterTextPrivate *priv = ttext->priv; if (priv->mappings) return; + clutter_text_add_mapping (ttext, CLUTTER_Left, 0, "move-left"); clutter_text_add_mapping (ttext, CLUTTER_KP_Left, 0, "move-left"); clutter_text_add_mapping (ttext, CLUTTER_Right, 0, "move-right"); @@ -274,12 +229,12 @@ static void init_mappings (ClutterText *ttext) clutter_text_add_mapping (ttext, CLUTTER_KP_Home, 0, "move-start-line"); clutter_text_add_mapping (ttext, CLUTTER_End, 0, "move-end-line"); clutter_text_add_mapping (ttext, CLUTTER_KP_End, 0, "move-end-line"); - clutter_text_add_mapping (ttext, CLUTTER_BackSpace,0,"delete-previous"); + clutter_text_add_mapping (ttext, CLUTTER_BackSpace, 0 , "delete-previous"); clutter_text_add_mapping (ttext, CLUTTER_Delete, 0, "delete-next"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Delete,0,"delete-next"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Delete,0, "delete-next"); clutter_text_add_mapping (ttext, CLUTTER_Return, 0, "activate"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Enter, 0,"activate"); - clutter_text_add_mapping (ttext, CLUTTER_ISO_Enter,0,"activate"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Enter, 0, "activate"); + clutter_text_add_mapping (ttext, CLUTTER_ISO_Enter, 0, "activate"); } static PangoLayout * @@ -514,56 +469,15 @@ clutter_text_ensure_cursor_position (ClutterText *ttext) g_signal_emit (ttext, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } -gint -clutter_text_get_cursor_position (ClutterText *ttext) +static inline gboolean +clutter_text_truncate_selection_internal (ClutterText *self) { - g_return_val_if_fail (CLUTTER_IS_TEXT (ttext), -1); - return ttext->priv->position; -} + ClutterTextPrivate *priv = self->priv; + gint start_index; + gint end_index; -void -clutter_text_set_cursor_position (ClutterText *ttext, - gint position) -{ - const gchar *text; - ClutterTextPrivate *priv; - gint len; - - g_return_if_fail (CLUTTER_IS_TEXT (ttext)); - - priv = ttext->priv; - - text = clutter_text_get_text (ttext); - if (text == NULL) - return; - - len = g_utf8_strlen (text, -1); - - if (position < 0 || position >= len) - priv->position = -1; - else - priv->position = position; - - if (CLUTTER_ACTOR_IS_VISIBLE (ttext)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (ttext)); -} - -static gboolean -clutter_text_truncate_selection (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - const gchar *utf8 = clutter_text_get_text (ttext); - ClutterTextPrivate *priv; - gint start_index; - gint end_index; - - priv = ttext->priv; - - g_object_ref (ttext); - - start_index = offset_real (utf8, priv->position); - end_index = offset_real (utf8, priv->selection_bound); + start_index = offset_real (priv->text, priv->position); + end_index = offset_real (priv->text, priv->selection_bound); if (end_index == start_index) return FALSE; @@ -575,154 +489,98 @@ clutter_text_truncate_selection (ClutterText *ttext, end_index = temp; } - clutter_text_delete_text (ttext, start_index, end_index); + clutter_text_delete_text (self, start_index, end_index); + priv->position = start_index; priv->selection_bound = start_index; + return TRUE; } - -void -clutter_text_insert_unichar (ClutterText *ttext, - gunichar wc) +static gboolean +clutter_text_truncate_selection (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { - ClutterTextPrivate *priv; - GString *new = NULL; - const gchar *old_text; - glong pos; - - g_return_if_fail (CLUTTER_IS_TEXT (ttext)); - g_return_if_fail (g_unichar_validate (wc)); - - if (wc == 0) - return; - - clutter_text_truncate_selection (ttext, NULL, 0); - - priv = ttext->priv; - - g_object_ref (ttext); - - old_text = clutter_text_get_text (ttext); - - - new = g_string_new (old_text); - pos = offset_to_bytes (old_text, priv->position); - new = g_string_insert_unichar (new, pos, wc); - - clutter_text_set_text (ttext, new->str); - - if (priv->position >= 0) - { - clutter_text_set_cursor_position (ttext, priv->position + 1); - clutter_text_set_selection_bound (ttext, priv->position); - } - - g_string_free (new, TRUE); - - g_object_unref (ttext); - - g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); -} - -void -clutter_text_delete_text (ClutterText *ttext, - gssize start_pos, - gssize end_pos) -{ - ClutterTextPrivate *priv; - GString *new = NULL; - gint start_bytes; - gint end_bytes; - const gchar *text; - - g_return_if_fail (CLUTTER_IS_TEXT (ttext)); - - priv = ttext->priv; - text = clutter_text_get_text (ttext); - - if (end_pos == -1) - { - start_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1) - 1); - end_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1)); - } - else - { - start_bytes = offset_to_bytes (text, start_pos); - end_bytes = offset_to_bytes (text, end_pos); - } - - new = g_string_new (text); - - new = g_string_erase (new, start_bytes, end_bytes - start_bytes); - - clutter_text_set_text (ttext, new->str); - - g_string_free (new, TRUE); - g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); -} - -static void -clutter_text_finalize (GObject *gobject) -{ - ClutterTextPrivate *priv; - ClutterText *ttext; - GList *iter; - - ttext = CLUTTER_TEXT (gobject); - priv = ttext->priv; - - clutter_text_mappings_clear (ttext); - - for (iter = priv->commands; iter; iter=iter->next) - g_free (iter->data); - g_list_free (priv->commands); - priv->commands = NULL; + return clutter_text_truncate_selection_internal (ttext); } static void clutter_text_set_property (GObject *gobject, - guint prop_id, - const GValue *value, - GParamSpec *pspec) + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - ClutterTextPrivate *priv; - ClutterText *ttext; - - ttext = CLUTTER_TEXT (gobject); - priv = ttext->priv; + ClutterText *self = CLUTTER_TEXT (gobject); switch (prop_id) { case PROP_TEXT: - clutter_text_set_text (ttext, g_value_get_string (value)); + clutter_text_set_text (self, g_value_get_string (value)); break; + case PROP_COLOR: - clutter_text_set_color (ttext, clutter_value_get_color (value)); + clutter_text_set_color (self, clutter_value_get_color (value)); break; + case PROP_FONT_NAME: - clutter_text_set_font_name (ttext, g_value_get_string (value)); + clutter_text_set_font_name (self, g_value_get_string (value)); break; + + case PROP_USE_MARKUP: + clutter_text_set_use_markup (self, g_value_get_boolean (value)); + break; + + case PROP_ATTRIBUTES: + clutter_text_set_attributes (self, g_value_get_boxed (value)); + break; + + case PROP_ALIGNMENT: + clutter_text_set_alignment (self, g_value_get_enum (value)); + break; + + case PROP_LINE_WRAP: + clutter_text_set_line_wrap (self, g_value_get_boolean (value)); + break; + + case PROP_LINE_WRAP_MODE: + clutter_text_set_line_wrap_mode (self, g_value_get_enum (value)); + break; + + case PROP_JUSTIFY: + clutter_text_set_justify (self, g_value_get_boolean (value)); + break; + + case PROP_ELLIPSIZE: + clutter_text_set_ellipsize (self, g_value_get_enum (value)); + break; + case PROP_POSITION: - clutter_text_set_cursor_position (ttext, g_value_get_int (value)); + clutter_text_set_cursor_position (self, g_value_get_int (value)); break; + case PROP_SELECTION_BOUND: - clutter_text_set_selection_bound (ttext, g_value_get_int (value)); + clutter_text_set_selection_bound (self, g_value_get_int (value)); break; + case PROP_CURSOR_VISIBLE: - clutter_text_set_cursor_visible (ttext, g_value_get_boolean (value)); + clutter_text_set_cursor_visible (self, g_value_get_boolean (value)); break; + case PROP_CURSOR_COLOR: - clutter_text_set_cursor_color (ttext, g_value_get_boxed (value)); + clutter_text_set_cursor_color (self, g_value_get_boxed (value)); break; + case PROP_EDITABLE: - clutter_text_set_editable (ttext, g_value_get_boolean (value)); + clutter_text_set_editable (self, g_value_get_boolean (value)); break; + case PROP_ACTIVATABLE: - clutter_text_set_activatable (ttext, g_value_get_boolean (value)); + clutter_text_set_activatable (self, g_value_get_boolean (value)); break; + case PROP_SELECTABLE: - clutter_text_set_selectable (ttext, g_value_get_boolean (value)); + clutter_text_set_selectable (self, g_value_get_boolean (value)); break; default: @@ -737,48 +595,87 @@ clutter_text_get_property (GObject *gobject, GValue *value, GParamSpec *pspec) { - ClutterTextPrivate *priv; - - priv = CLUTTER_TEXT (gobject)->priv; + ClutterTextPrivate *priv = CLUTTER_TEXT (gobject)->priv; switch (prop_id) { case PROP_TEXT: g_value_set_string (value, priv->text); break; + case PROP_FONT_NAME: g_value_set_string (value, priv->font_name); break; + case PROP_COLOR: clutter_value_set_color (value, &priv->text_color); break; + case PROP_CURSOR_VISIBLE: g_value_set_boolean (value, priv->cursor_visible); break; + case PROP_CURSOR_COLOR: clutter_value_set_color (value, &priv->cursor_color); break; + case PROP_POSITION: g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); break; + case PROP_SELECTION_BOUND: g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->selection_bound)); break; + case PROP_EDITABLE: g_value_set_boolean (value, priv->editable); break; + case PROP_SELECTABLE: g_value_set_boolean (value, priv->selectable); break; + case PROP_ACTIVATABLE: g_value_set_boolean (value, priv->activatable); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } +static void +clutter_text_dispose (GObject *gobject) +{ + ClutterText *self = CLUTTER_TEXT (gobject); + + /* get rid of the entire cache */ + clutter_text_dirty_cache (self); + + G_OBJECT_CLASS (clutter_text_parent_class)->dispose (gobject); +} + +static void +clutter_text_finalize (GObject *gobject) +{ + ClutterText *self = CLUTTER_TEXT (gobject); + ClutterTextPrivate *priv = self->priv; + + if (priv->font_desc) + pango_font_description_free (priv->font_desc); + + g_free (priv->text); + g_free (priv->font_name); + + clutter_text_mappings_clear (self); + + g_list_foreach (priv->commands, (GFunc) g_free, NULL); + g_list_free (priv->commands); + + G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject); +} + static void cursor_paint (ClutterText *ttext) { @@ -875,8 +772,8 @@ cursor_paint (ClutterText *ttext) static gboolean -clutter_text_press (ClutterActor *actor, - ClutterButtonEvent *bev) +clutter_text_button_press (ClutterActor *actor, + ClutterButtonEvent *bev) { ClutterText *ttext = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = ttext->priv; @@ -899,14 +796,7 @@ clutter_text_press (ClutterActor *actor, ); /* we'll steal keyfocus if we do not have it */ - { - ClutterActor *stage; - for (stage = actor; - clutter_actor_get_parent (stage); - stage = clutter_actor_get_parent (stage)); - if (stage && CLUTTER_IS_STAGE (stage)) - clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); - } + clutter_actor_grab_key_focus (actor); priv->in_select_drag = TRUE; clutter_grab_pointer (actor); @@ -917,7 +807,7 @@ clutter_text_press (ClutterActor *actor, static gboolean clutter_text_motion (ClutterActor *actor, - ClutterMotionEvent *mev) + ClutterMotionEvent *mev) { ClutterText *ttext = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = ttext->priv; @@ -926,9 +816,7 @@ clutter_text_motion (ClutterActor *actor, const gchar *text; if (!priv->in_select_drag) - { - return FALSE; - } + return FALSE; text = clutter_text_get_text (ttext); @@ -941,9 +829,7 @@ clutter_text_motion (ClutterActor *actor, CLUTTER_UNITS_TO_INT (y)); if (priv->selectable) - { - clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); - } + clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); else { clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); @@ -954,8 +840,8 @@ clutter_text_motion (ClutterActor *actor, } static gboolean -clutter_text_release (ClutterActor *actor, - ClutterButtonEvent *bev) +clutter_text_button_release (ClutterActor *actor, + ClutterButtonEvent *bev) { ClutterText *ttext = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = ttext->priv; @@ -968,6 +854,54 @@ clutter_text_release (ClutterActor *actor, return FALSE; } +static gboolean +clutter_text_key_press (ClutterActor *actor, + ClutterKeyEvent *kev) +{ + ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; + gint keyval = clutter_key_event_symbol (kev); + GList *iter; + + if (!priv->editable) + return FALSE; + + for (iter = priv->mappings; iter != NULL; iter = iter->next) + { + ClutterTextMapping *mapping = iter->data; + + if ( + (mapping->keyval == keyval) && + ( + (mapping->state == 0) || + (mapping->state && (kev->modifier_state & mapping->state)) + ) + ) + { + if (!g_str_equal (mapping->action, "activate") || + priv->activatable) + return clutter_text_action (CLUTTER_TEXT (actor), + mapping->action, + (ClutterEvent *) kev); + } + } + + { + gunichar key_unichar = clutter_key_event_unicode (kev); + + if (key_unichar == '\r') /* return is reported as CR we want LF */ + key_unichar = '\n'; + + if (g_unichar_validate (key_unichar)) + { + clutter_text_insert_unichar (CLUTTER_TEXT (actor), key_unichar); + return TRUE; + } + } + + return FALSE; +} + + static void clutter_text_paint (ClutterActor *self) { @@ -1002,9 +936,9 @@ clutter_text_paint (ClutterActor *self) static void clutter_text_get_preferred_width (ClutterActor *self, - ClutterUnit for_height, - ClutterUnit *min_width_p, - ClutterUnit *natural_width_p) + ClutterUnit for_height, + ClutterUnit *min_width_p, + ClutterUnit *natural_width_p) { ClutterText *text = CLUTTER_TEXT (self); ClutterTextPrivate *priv = text->priv; @@ -1034,9 +968,9 @@ clutter_text_get_preferred_width (ClutterActor *self, static void clutter_text_get_preferred_height (ClutterActor *self, - ClutterUnit for_width, - ClutterUnit *min_height_p, - ClutterUnit *natural_height_p) + ClutterUnit for_width, + ClutterUnit *min_height_p, + ClutterUnit *natural_height_p) { ClutterText *text = CLUTTER_TEXT (self); @@ -1069,8 +1003,8 @@ clutter_text_get_preferred_height (ClutterActor *self, static void clutter_text_allocate (ClutterActor *self, - const ClutterActorBox *box, - gboolean origin_changed) + const ClutterActorBox *box, + gboolean origin_changed) { ClutterText *text = CLUTTER_TEXT (self); ClutterActorClass *parent_class; @@ -1083,13 +1017,6 @@ clutter_text_allocate (ClutterActor *self, parent_class->allocate (self, box, origin_changed); } -static void -clutter_text_constructed (GObject *object) -{ - if (G_OBJECT_CLASS (clutter_text_parent_class)->constructed != NULL) - G_OBJECT_CLASS (clutter_text_parent_class)->constructed (object); -} - static void clutter_text_class_init (ClutterTextClass *klass) { @@ -1099,7 +1026,7 @@ clutter_text_class_init (ClutterTextClass *klass) gobject_class->set_property = clutter_text_set_property; gobject_class->get_property = clutter_text_get_property; - gobject_class->constructed = clutter_text_constructed; + gobject_class->dispose = clutter_text_dispose; gobject_class->finalize = clutter_text_finalize; actor_class->paint = clutter_text_paint; @@ -1107,8 +1034,8 @@ clutter_text_class_init (ClutterTextClass *klass) actor_class->get_preferred_height = clutter_text_get_preferred_height; actor_class->allocate = clutter_text_allocate; actor_class->key_press_event = clutter_text_key_press; - actor_class->button_press_event = clutter_text_press; - actor_class->button_release_event = clutter_text_release; + actor_class->button_press_event = clutter_text_button_press; + actor_class->button_release_event = clutter_text_button_release; actor_class->motion_event = clutter_text_motion; /** @@ -1145,40 +1072,37 @@ clutter_text_class_init (ClutterTextClass *klass) * * Whether key events delivered to the actor causes editing. */ - g_object_class_install_property - (gobject_class, PROP_EDITABLE, - g_param_spec_boolean ("editable", - "Editable", - "Whether the text is editable", - TRUE, - G_PARAM_READWRITE)); - + pspec = g_param_spec_boolean ("editable", + "Editable", + "Whether the text is editable", + TRUE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_EDITABLE, pspec); /** * ClutterText:selectable: * * Whether it is possible to select text. */ - g_object_class_install_property - (gobject_class, PROP_SELECTABLE, - g_param_spec_boolean ("selectable", - "Editable", - "Whether the text is selectable", - TRUE, - G_PARAM_READWRITE)); + pspec = g_param_spec_boolean ("selectable", + "Selectable", + "Whether the text is selectable", + TRUE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_SELECTABLE, pspec); /** * ClutterText:activatable: * * Toggles whether return invokes the activate signal or not. */ - g_object_class_install_property - (gobject_class, PROP_ACTIVATABLE, - g_param_spec_boolean ("activatable", - "Editable", - "Whether return causes the activate signal to be fired", - TRUE, - G_PARAM_READWRITE)); + pspec = g_param_spec_boolean ("activatable", + "Activatable", + "Whether pressing return causes the " + "activate signal to be emitted", + TRUE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ACTIVATABLE, pspec); /** * ClutterText:cursor-visible: @@ -1209,29 +1133,123 @@ clutter_text_class_init (ClutterTextClass *klass) * * The current input cursor position. -1 is taken to be the end of the text */ - g_object_class_install_property - (gobject_class, PROP_POSITION, - g_param_spec_int ("position", + pspec = g_param_spec_int ("position", "Position", "The cursor position", -1, G_MAXINT, -1, - G_PARAM_READWRITE)); - + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_POSITION, pspec); /** * ClutterText:selection-bound: * * The current input cursor position. -1 is taken to be the end of the text */ - g_object_class_install_property - (gobject_class, PROP_SELECTION_BOUND, - g_param_spec_int ("selection-bound", + pspec = g_param_spec_int ("selection-bound", "Selection-bound", "The cursor position of the other end of the selection.", -1, G_MAXINT, -1, - G_PARAM_READWRITE)); + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec); + + pspec = g_param_spec_boxed ("attributes", + "Attributes", + "A list of style attributes to apply to " + "the contents of the actor", + PANGO_TYPE_ATTR_LIST, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec); + + /** + * ClutterText:use-markup: + * + * Whether the text includes Pango markup. See pango_layout_set_markup() + * in the Pango documentation. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("use-markup", + "Use markup", + "Whether or not the text " + "includes Pango markup", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec); + + /** + * ClutterText:line-wrap: + * + * Whether to wrap the lines of #ClutterText:text if the contents + * exceed the available allocation. The wrapping strategy is + * controlled by the #ClutterText:line-wrap-mode property. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("line-wrap", + "Line wrap", + "If set, wrap the lines if the text " + "becomes too wide", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_LINE_WRAP, pspec); + + /** + * ClutterText:line-wrap-mode: + * + * If #ClutterText:line-wrap is set to %TRUE, this property will + * control how the text is wrapped. + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("line-wrap-mode", + "Line wrap mode", + "Control how line-wrapping is done", + PANGO_TYPE_WRAP_MODE, + PANGO_WRAP_WORD, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_LINE_WRAP_MODE, pspec); + + pspec = g_param_spec_enum ("ellipsize", + "Ellipsize", + "The preferred place to ellipsize the string", + PANGO_TYPE_ELLIPSIZE_MODE, + PANGO_ELLIPSIZE_NONE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec); + + /** + * ClutterText:alignment: + * + * The preferred alignment for the text. This property controls + * the alignment of multi-line paragraphs. + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("alignment", + "Alignment", + "The preferred alignment for the string, " + "for multi-line text", + PANGO_TYPE_ALIGNMENT, + PANGO_ALIGN_LEFT, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ALIGNMENT, pspec); + + /** + * ClutterText:justify: + * + * Whether the contents of the #ClutterText should be justified + * on both margins. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("justify", + "Justify", + "Whether the text should be justified", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); /** * ClutterText::text-changed: @@ -1248,7 +1266,6 @@ clutter_text_class_init (ClutterTextClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - text_signals[CURSOR_EVENT] = g_signal_new ("cursor-event", G_TYPE_FROM_CLASS (gobject_class), @@ -1259,15 +1276,14 @@ clutter_text_class_init (ClutterTextClass *klass) G_TYPE_NONE, 1, CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE); - - /** + /** * ClutterText::activate * @actor: the actor which received the event * * The ::activate signal is emitted each time the entry is 'activated' * by the user, normally by pressing the 'Enter' key. * - * Since: 0.4 + * Since: 1.0 */ text_signals[ACTIVATE] = g_signal_new ("activate", @@ -1278,7 +1294,6 @@ clutter_text_class_init (ClutterTextClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); } @@ -1440,17 +1455,6 @@ clutter_text_get_cursor_color (ClutterText *text, *color = priv->cursor_color; } - -gint -clutter_text_get_selection_bound (ClutterText *text) -{ - ClutterTextPrivate *priv; - - priv = text->priv; - - return priv->selection_bound; -} - gchar * clutter_text_get_selection (ClutterText *text) { @@ -1487,20 +1491,34 @@ clutter_text_get_selection (ClutterText *text) return str; } - - void -clutter_text_set_selection_bound (ClutterText *text, - gint selection_bound) +clutter_text_set_selection_bound (ClutterText *self, + gint selection_bound) { ClutterTextPrivate *priv; - priv = text->priv; - priv->selection_bound = selection_bound; + g_return_if_fail (CLUTTER_IS_TEXT (self)); - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + priv = self->priv; + + if (priv->selection_bound != selection_bound) + { + priv->selection_bound = selection_bound; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "selection-bound"); + } } +gint +clutter_text_get_selection_bound (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1); + + return self->priv->selection_bound; +} /****************************************************************/ /* The following are the commands available for keybinding when */ @@ -1843,7 +1861,6 @@ clutter_text_action_delete_previous (ClutterText *ttext, return TRUE; } - static void init_commands (ClutterText *ttext) { ClutterTextPrivate *priv = ttext->priv; @@ -1897,49 +1914,6 @@ clutter_text_action (ClutterText *ttext, return FALSE; } -static gboolean -clutter_text_key_press (ClutterActor *actor, - ClutterKeyEvent *kev) -{ - ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; - gint keyval = clutter_key_event_symbol (kev); - GList *iter; - - if (!priv->editable) - return FALSE; - - for (iter=priv->mappings;iter;iter=iter->next) - { - ClutterTextMapping *mapping = iter->data; - - if ( - (mapping->keyval == keyval) && - ( - (mapping->state == 0) || - (mapping->state && (kev->modifier_state & mapping->state)) - ) - ) - { - if (!g_str_equal (mapping->action, "activate") || - priv->activatable) - return clutter_text_action (CLUTTER_TEXT (actor), mapping->action, (ClutterEvent*)kev); - } - } - - { - gunichar key_unichar = clutter_key_event_unicode (kev); - - if (key_unichar == '\r') /* return is reported as CR we want LF */ - key_unichar = '\n'; - if (g_unichar_validate (key_unichar)) - { - clutter_text_insert_unichar (CLUTTER_TEXT (actor), key_unichar); - return TRUE; - } - } - return FALSE; -} - G_CONST_RETURN gchar * clutter_text_get_font_name (ClutterText *text) { @@ -2063,30 +2037,516 @@ clutter_text_get_color (ClutterText *text, *color = priv->text_color; } -gboolean -clutter_text_get_line_wrap (ClutterText *text) +/** + * clutter_text_set_ellipsize: + * @self: a #ClutterText + * @mode: a #PangoEllipsizeMode + * + * Sets the mode used to ellipsize (add an ellipsis: "...") to the + * text if there is not enough space to render the entire contents + * of a #ClutterText actor + * + * Since: 1.0 + */ +void +clutter_text_set_ellipsize (ClutterText *self, + PangoEllipsizeMode mode) { - g_return_val_if_fail (CLUTTER_IS_TEXT (text), FALSE); + ClutterTextPrivate *priv; - return text->priv->wrap; + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && + mode <= PANGO_ELLIPSIZE_END); + + priv = self->priv; + + if ((PangoEllipsizeMode) priv->ellipsize != mode) + { + priv->ellipsize = mode; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "ellipsize"); + } +} + +/** + * clutter_text_get_ellipsize: + * @self: a #ClutterText + * + * Returns the ellipsizing position of a #ClutterText actor, as + * set by clutter_text_set_ellipsize(). + * + * Return value: #PangoEllipsizeMode + * + * Since: 1.0 + */ +PangoEllipsizeMode +clutter_text_get_ellipsize (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ELLIPSIZE_NONE); + + return self->priv->ellipsize; +} + +gboolean +clutter_text_get_line_wrap (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->wrap; } void -clutter_text_set_line_wrap (ClutterText *text, +clutter_text_set_line_wrap (ClutterText *self, gboolean line_wrap) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); - priv = text->priv; + priv = self->priv; if (priv->wrap != line_wrap) { priv->wrap = line_wrap; - clutter_text_dirty_cache (text); + clutter_text_dirty_cache (self); - clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "line-wrap"); } } + +/** + * clutter_text_set_line_wrap_mode: + * @self: a #ClutterText + * @wrap_mode: the line wrapping mode + * + * If line wrapping is enabled (see clutter_text_set_line_wrap()) this + * function controls how the line wrapping is performed. The default is + * %PANGO_WRAP_WORD which means wrap on word boundaries. + * + * Since: 1.0 + */ +void +clutter_text_set_line_wrap_mode (ClutterText *self, + PangoWrapMode wrap_mode) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->wrap_mode != wrap_mode) + { + priv->wrap_mode = wrap_mode; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "line-wrap-mode"); + } +} + +/** + * clutter_text_get_line_wrap_mode: + * @self: a #ClutterText + * + * Retrieves the line wrap mode used by the #ClutterText actor. + * + * See clutter_text_set_line_wrap_mode (). + * + * Return value: the wrap mode used by the #ClutterText + * + * Since: 1.0 + */ +PangoWrapMode +clutter_text_get_line_wrap_mode (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_WRAP_WORD); + + return self->priv->wrap_mode; +} + +/** + * clutter_text_set_attributes: + * @self: a #ClutterText + * @attrs: a #PangoAttrList or %NULL to unset the attributes + * + * Sets the attributes list that are going to be applied to the + * #ClutterText contents. The attributes set with this function + * will be ignored if the #ClutterText:use_markup property is + * set to %TRUE. + * + * The #ClutterText actor will take a reference on the #PangoAttrList + * passed to this function. + * + * Since: 1.0 + */ +void +clutter_text_set_attributes (ClutterText *self, + PangoAttrList *attrs) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (attrs) + pango_attr_list_ref (attrs); + + if (priv->attrs) + pango_attr_list_unref (priv->attrs); + + if (!priv->use_markup) + { + if (attrs) + pango_attr_list_ref (attrs); + + if (priv->effective_attrs) + pango_attr_list_unref (priv->effective_attrs); + + priv->effective_attrs = attrs; + } + + priv->attrs = attrs; + + clutter_text_dirty_cache (self); + + g_object_notify (G_OBJECT (self), "attributes"); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + +/** + * clutter_text_get_attributes: + * @self: a #ClutterText + * + * Gets the attribute list that was set on the #ClutterText actor + * clutter_text_set_attributes(), if any. + * + * Return value: the attribute list, or %NULL if none was set. The + * returned value is owned by the #ClutterText and should not be + * unreferenced. + * + * Since: 1.0 + */ +PangoAttrList * +clutter_text_get_attributes (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + return self->priv->attrs; +} + +/** + * clutter_text_set_alignment: + * @self: a #ClutterText + * @alignment: A #PangoAlignment + * + * Sets text alignment of the #ClutterText actor. + * + * The alignment will only be used when the contents of the + * #ClutterText actor are enough to wrap, and the #ClutterText:line-wrap + * property is set to %TRUE. + * + * Since: 1.0 + */ +void +clutter_text_set_alignment (ClutterText *self, + PangoAlignment alignment) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->alignment != alignment) + { + priv->alignment = alignment; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "alignment"); + } +} + +/** + * clutter_text_get_alignment: + * @self: a #ClutterText + * + * Retrieves the alignment of @self. + * + * Return value: a #PangoAlignment + * + * Since 1.0 + */ +PangoAlignment +clutter_text_get_alignment (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ALIGN_LEFT); + + return self->priv->alignment; +} + +/** + * clutter_text_set_use_markup: + * @self: a #ClutterText + * @setting: %TRUE if the text should be parsed for markup. + * + * Sets whether the contents of the #ClutterText actor contains markup + * in Pango's text markup language. + * + * Since: 1.0 + */ +void +clutter_text_set_use_markup (ClutterText *self, + gboolean setting) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->use_markup != setting) + { + priv->use_markup = setting; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "use-markup"); + } +} + +/** + * clutter_text_get_use_markup: + * @self: a #ClutterText + * + * Retrieves whether the contents of the #ClutterText actor should be + * parsed for the Pango text markup. + * + * Return value: %TRUE if the contents will be parsed for markup + * + * Since: 1.0 + */ +gboolean +clutter_text_get_use_markup (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->use_markup; +} + +/** + * clutter_text_set_justify: + * @self: a #ClutterText + * @justify: whether the text should be justified + * + * Sets whether the text of the #ClutterText actor should be justified + * on both margins. This setting is ignored if Clutter is compiled + * against Pango < 1.18. + * + * Since: 0.6 + */ +void +clutter_text_set_justify (ClutterText *self, + gboolean justify) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->justify != justify) + { + priv->justify = justify; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "justify"); + } +} + +/** + * clutter_text_get_justify: + * @self: a #ClutterText + * + * Retrieves whether the #ClutterText actor should justify its contents + * on both margins. + * + * Return value: %TRUE if the text should be justified + * + * Since: 0.6 + */ +gboolean +clutter_text_get_justify (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->justify; +} + +/** + * clutter_text_get_cursor_position: + * @self: a #ClutterText + * + * Retrieves the cursor position. + * + * Return value: the cursor position, in characters + * + * Since: 1.0 + */ +gint +clutter_text_get_cursor_position (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1); + + return self->priv->position; +} + +void +clutter_text_set_cursor_position (ClutterText *self, + gint position) +{ + ClutterTextPrivate *priv; + gint len; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->text == NULL) + return; + + len = g_utf8_strlen (priv->text, -1); + + if (position < 0 || position >= len) + priv->position = -1; + else + priv->position = position; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); +} + +void +clutter_text_insert_unichar (ClutterText *self, + gunichar wc) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + glong pos; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (g_unichar_validate (wc)); + + if (wc == 0) + return; + + clutter_text_truncate_selection (self, NULL, 0); + + priv = self->priv; + + g_object_ref (self); + + new = g_string_new (priv->text); + pos = offset_to_bytes (priv->text, priv->position); + new = g_string_insert_unichar (new, pos, wc); + + clutter_text_set_text (self, new->str); + + if (priv->position >= 0) + { + clutter_text_set_cursor_position (self, priv->position + 1); + clutter_text_set_selection_bound (self, priv->position); + } + + g_string_free (new, TRUE); + + g_object_unref (self); + + g_signal_emit (G_OBJECT (self), text_signals[TEXT_CHANGED], 0); +} + +void +clutter_text_delete_text (ClutterText *ttext, + gssize start_pos, + gssize end_pos) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + gint start_bytes; + gint end_bytes; + const gchar *text; + + g_return_if_fail (CLUTTER_IS_TEXT (ttext)); + + priv = ttext->priv; + text = clutter_text_get_text (ttext); + + if (end_pos == -1) + { + start_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1) - 1); + end_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1)); + } + else + { + start_bytes = offset_to_bytes (text, start_pos); + end_bytes = offset_to_bytes (text, end_pos); + } + + new = g_string_new (text); + + new = g_string_erase (new, start_bytes, end_bytes - start_bytes); + + clutter_text_set_text (ttext, new->str); + + g_string_free (new, TRUE); + g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); +} + + +void +clutter_text_add_mapping (ClutterText *ttext, + guint keyval, + ClutterModifierType state, + const gchar *commandline) +{ + ClutterTextMapping *tmapping = g_new (ClutterTextMapping, 1); + ClutterTextPrivate *priv = ttext->priv; + tmapping->keyval = keyval; + tmapping->state = state; + tmapping->action = commandline; + priv->mappings = g_list_append (priv->mappings, tmapping); +} + +void +clutter_text_add_action (ClutterText *ttext, + const gchar *name, + gboolean (*func) (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event)) +{ + TextCommand *tcommand = g_new (TextCommand, 1); + ClutterTextPrivate *priv = ttext->priv; + tcommand->name = name; + tcommand->func = func; + priv->commands = g_list_append (priv->commands, tcommand); +} + From 97cf7e68199f9ac901777f7c68b5475373d5a4eb Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:35:52 +0000 Subject: [PATCH 022/147] Correctly initialize the initial state of ClutterText Some of the values in ClutterText have an initial state that does not match a zero-ed out ClutterTextPrivate structure. --- clutter/clutter-text.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4f2e3d7d6..de3d7c956 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -122,7 +122,7 @@ struct _ClutterTextPrivate /* the x position in the pangolayout, used to * avoid drifting when repeatedly moving up|down */ - gint x_pos; + gint x_pos; /* Where to draw the cursor */ ClutterGeometry cursor_pos; @@ -244,9 +244,6 @@ clutter_text_create_layout_no_cache (ClutterText *text, ClutterTextPrivate *priv = text->priv; PangoLayout *layout; - if (G_UNLIKELY (_context == NULL)) - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - layout = pango_layout_new (_context); pango_layout_set_font_description (layout, priv->font_desc); @@ -1024,6 +1021,8 @@ clutter_text_class_init (ClutterTextClass *klass) ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; + _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); + gobject_class->set_property = clutter_text_set_property; gobject_class->get_property = clutter_text_get_property; gobject_class->dispose = clutter_text_dispose; @@ -1301,9 +1300,29 @@ static void clutter_text_init (ClutterText *self) { ClutterTextPrivate *priv; + int i; self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); + priv->alignment = PANGO_ALIGN_LEFT; + priv->wrap = FALSE; + priv->wrap_mode = PANGO_WRAP_WORD; + priv->ellipsize = PANGO_ELLIPSIZE_NONE; + priv->use_underline = FALSE; + priv->use_markup = FALSE; + priv->justify = FALSE; + + for (i = 0; i < N_CACHED_LAYOUTS; i++) + priv->cached_layouts[i].layout = NULL; + + priv->text = NULL; + + priv->text_color = default_text_color; + priv->cursor_color = default_cursor_color; + + priv->font_name = g_strdup (DEFAULT_FONT_NAME); + priv->font_desc = pango_font_description_from_string (priv->font_name); + priv->x_pos = -1; priv->cursor_visible = TRUE; priv->editable = FALSE; From 64af7bafe387571d7087dd4c99b4f459c1ca41c0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:37:46 +0000 Subject: [PATCH 023/147] Move test-threads to ClutterText The test-threads interactive test is a good candidate for the switch from ClutterLabel to ClutterText to verify that the behaviour of the two classes is the same. --- tests/interactive/test-threads.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/interactive/test-threads.c b/tests/interactive/test-threads.c index 86eb6cfe3..5f946b653 100644 --- a/tests/interactive/test-threads.c +++ b/tests/interactive/test-threads.c @@ -42,7 +42,7 @@ test_thread_done_idle (gpointer user_data) g_print ("Thread completed\n"); - clutter_label_set_text (CLUTTER_LABEL (data->label), "Completed"); + clutter_text_set_text (CLUTTER_TEXT (data->label), "Completed"); clutter_timeline_stop (data->timeline); test_thread_data_free (data); @@ -67,7 +67,7 @@ update_label_idle (gpointer data) text = g_strdup_printf ("Count to %d", update->count); - clutter_label_set_text (CLUTTER_LABEL (update->thread_data->label), text); + clutter_text_set_text (CLUTTER_TEXT (update->thread_data->label), text); clutter_actor_set_width (update->thread_data->label, -1); if (update->count == 0) @@ -151,7 +151,7 @@ on_key_press_event (ClutterStage *stage, switch (clutter_key_event_symbol (event)) { case CLUTTER_s: - clutter_label_set_text (CLUTTER_LABEL (help_label), "Press 'q' to quit"); + clutter_text_set_text (CLUTTER_TEXT (help_label), "Press 'q' to quit"); clutter_timeline_start (timeline); @@ -191,10 +191,10 @@ test_threads_main (int argc, char *argv[]) clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); clutter_actor_set_size (stage, 600, 300); - count_label = clutter_label_new_with_text ("Mono 12", "Counter"); + count_label = clutter_text_new_with_text ("Mono 12", "Counter"); clutter_actor_set_position (count_label, 350, 50); - help_label = clutter_label_new_with_text ("Mono 12", "Press 's' to start"); + help_label = clutter_text_new_with_text ("Mono 12", "Press 's' to start"); clutter_actor_set_position (help_label, 50, 50); rect = clutter_rectangle_new_with_color (&rect_color); From d5df1bebcf28cb035ea9529fbc1352c8300f7e25 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:40:55 +0000 Subject: [PATCH 024/147] Add text-visibility accessors for ClutterText A ClutterText can be put in "password mode" by setting the text as "invisible": every character inside the Text actor's contents will be replaced when building the Pango layout with a specific Unicode character. The Unicode character is set to '*' by default, but the user can be changed using the provided API. --- clutter/clutter-text.c | 652 ++++++++++++++++++++++++++++++----------- clutter/clutter-text.h | 21 +- 2 files changed, 494 insertions(+), 179 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index de3d7c956..ffee909d9 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -3,9 +3,10 @@ * * An OpenGL based 'interactive canvas' library. * - * Copyright (C) 2006-2008 OpenedHand + * Copyright (C) 2008 Intel Corporation. * - * Authored By Øyvind Kolås + * Authored By: Øyvind Kolås + * Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,9 +19,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* TODO: undo/redo hooks? @@ -112,6 +111,7 @@ struct _ClutterTextPrivate guint selectable : 1; guint in_select_drag : 1; guint cursor_color_set : 1; + guint text_visible : 1; /* current cursor position */ gint position; @@ -124,17 +124,25 @@ struct _ClutterTextPrivate */ gint x_pos; + /* the length of the text, in bytes */ + gint n_bytes; + + /* the length of the text, in characters */ + gint n_chars; + /* Where to draw the cursor */ ClutterGeometry cursor_pos; ClutterColor cursor_color; - ClutterColor selection_color; - GList *mappings; GList *commands; /* each instance has it's own set of commands so that actor specific actions can be added to single actor classes */ + + gint max_length; + + gunichar priv_char; }; enum @@ -158,7 +166,9 @@ enum PROP_CURSOR_COLOR_SET, PROP_EDITABLE, PROP_SELECTABLE, - PROP_ACTIVATABLE + PROP_ACTIVATABLE, + PROP_TEXT_VISIBLE, + PROP_INVISIBLE_CHAR }; enum @@ -258,7 +268,36 @@ clutter_text_create_layout_no_cache (ClutterText *text, if (priv->text) { if (!priv->use_markup) - pango_layout_set_text (layout, priv->text, -1); + { + if (priv->text_visible) + pango_layout_set_text (layout, priv->text, priv->n_bytes); + else + { + GString *str = g_string_sized_new (priv->n_bytes); + gunichar invisible_char; + gchar buf[7]; + gint char_len, i; + + if (priv->priv_char != 0) + invisible_char = priv->priv_char; + else + invisible_char = ' '; + + /* we need to convert the string built of invisible + * characters into UTF-8 for it to be fed to the Pango + * layout + */ + memset (buf, 0, sizeof (buf)); + char_len = g_unichar_to_utf8 (invisible_char, buf); + + for (i = 0; i < priv->n_chars; i++) + g_string_append_len (str, buf, char_len); + + pango_layout_set_text (layout, str->str, str->len); + + g_string_free (str, TRUE); + } + } else pango_layout_set_markup (layout, priv->text, -1); } @@ -423,20 +462,16 @@ clutter_text_position_to_coords (ClutterText *ttext, priv = ttext->priv; if (position == -1) - { - index_ = strlen (text); - } + index_ = strlen (text); else - { - index_ = offset_to_bytes (text, position); - } + index_ = offset_to_bytes (text, position); if (index_ > strlen (text)) index_ = strlen (text); - pango_layout_get_cursor_pos ( - clutter_text_get_layout (ttext), - index_, &rect, NULL); + pango_layout_get_cursor_pos (clutter_text_get_layout (ttext), + index_, + &rect, NULL); if (x) *x = rect.x / PANGO_SCALE; @@ -580,17 +615,24 @@ clutter_text_set_property (GObject *gobject, clutter_text_set_selectable (self, g_value_get_boolean (value)); break; + case PROP_TEXT_VISIBLE: + clutter_text_set_text_visible (self, g_value_get_boolean (value)); + break; + + case PROP_INVISIBLE_CHAR: + clutter_text_set_invisible_char (self, g_value_get_uint (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - break; } } static void clutter_text_get_property (GObject *gobject, - guint prop_id, - GValue *value, - GParamSpec *pspec) + guint prop_id, + GValue *value, + GParamSpec *pspec) { ClutterTextPrivate *priv = CLUTTER_TEXT (gobject)->priv; @@ -636,9 +678,16 @@ clutter_text_get_property (GObject *gobject, g_value_set_boolean (value, priv->activatable); break; + case PROP_TEXT_VISIBLE: + g_value_set_boolean (value, priv->text_visible); + break; + + case PROP_INVISIBLE_CHAR: + g_value_set_uint (value, priv->priv_char); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - break; } } @@ -674,12 +723,11 @@ clutter_text_finalize (GObject *gobject) } static void -cursor_paint (ClutterText *ttext) +cursor_paint (ClutterText *self) { - ClutterTextPrivate *priv = ttext->priv; + ClutterTextPrivate *priv = self->priv; - if (priv->editable && - priv->cursor_visible) + if (priv->editable && priv->cursor_visible) { if (priv->cursor_color_set) { @@ -690,15 +738,13 @@ cursor_paint (ClutterText *ttext) } else { - ClutterColor color; - clutter_text_get_color (ttext, &color); - cogl_set_source_color4ub (color.red, - color.green, - color.blue, - color.alpha); + cogl_set_source_color4ub (priv->text_color.red, + priv->text_color.green, + priv->text_color.blue, + priv->text_color.alpha); } - clutter_text_ensure_cursor_position (ttext); + clutter_text_ensure_cursor_position (self); if (priv->position == 0) priv->cursor_pos.x -= 2; @@ -712,10 +758,12 @@ cursor_paint (ClutterText *ttext) } else { + PangoLayout *layout = clutter_text_get_layout (self); + const gchar *utf8 = priv->text; gint lines; gint start_index; gint end_index; - const gchar *utf8 = clutter_text_get_text (ttext); + gint line_no; start_index = offset_to_bytes (utf8, priv->position); end_index = offset_to_bytes (utf8, priv->selection_bound); @@ -727,9 +775,8 @@ cursor_paint (ClutterText *ttext) end_index = temp; } - PangoLayout *layout = clutter_text_get_layout (ttext); lines = pango_layout_get_line_count (layout); - gint line_no; + for (line_no = 0; line_no < lines; line_no++) { PangoLayoutLine *line; @@ -746,23 +793,26 @@ cursor_paint (ClutterText *ttext) if (maxindex < start_index) continue; - pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges); + 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 (ttext, bytes_to_offset (utf8, index), NULL, &y, &height); + clutter_text_position_to_coords (self, + bytes_to_offset (utf8, index), + NULL, &y, &height); - for (i=0;ipriv; - ClutterUnit x, y; - gint index_; - const gchar *text; - - text = clutter_text_get_text (ttext); + ClutterText *self = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = self->priv; + ClutterUnit x, y; + gint index_; x = CLUTTER_UNITS_FROM_INT (bev->x); y = CLUTTER_UNITS_FROM_INT (bev->y); clutter_actor_transform_stage_point (actor, x, y, &x, &y); - index_ = clutter_text_coords_to_position (ttext, CLUTTER_UNITS_TO_INT (x), - CLUTTER_UNITS_TO_INT (y)); + index_ = clutter_text_coords_to_position (self, + CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); - clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); - clutter_text_set_selection_bound (ttext, bytes_to_offset (text, index_) - ); + clutter_text_set_cursor_position (self, bytes_to_offset (priv->text, index_)); + clutter_text_set_selection_bound (self, bytes_to_offset (priv->text, index_)); + + /* grab the pointer */ + priv->in_select_drag = TRUE; + clutter_grab_pointer (actor); /* we'll steal keyfocus if we do not have it */ clutter_actor_grab_key_focus (actor); - priv->in_select_drag = TRUE; - clutter_grab_pointer (actor); - return TRUE; } @@ -842,12 +890,15 @@ clutter_text_button_release (ClutterActor *actor, { ClutterText *ttext = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = ttext->priv; + if (priv->in_select_drag) { clutter_ungrab_pointer (); priv->in_select_drag = FALSE; + return TRUE; } + return FALSE; } @@ -898,7 +949,6 @@ clutter_text_key_press (ClutterActor *actor, return FALSE; } - static void clutter_text_paint (ClutterActor *self) { @@ -1006,8 +1056,9 @@ clutter_text_allocate (ClutterActor *self, ClutterText *text = CLUTTER_TEXT (self); ClutterActorClass *parent_class; - /* Ensure that there is a cached layout with the right width so that - we don't need to create the text during the paint run */ + /* Ensure that there is a cached layout with the right width so + * that we don't need to create the text during the paint run + */ clutter_text_create_layout (text, box->x2 - box->x1); parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class); @@ -1023,6 +1074,8 @@ clutter_text_class_init (ClutterTextClass *klass) _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); + g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); + gobject_class->set_property = clutter_text_set_property; gobject_class->get_property = clutter_text_get_property; gobject_class->dispose = clutter_text_dispose; @@ -1043,7 +1096,7 @@ clutter_text_class_init (ClutterTextClass *klass) * The font to be used by the #ClutterText, as a string * that can be parsed by pango_font_description_from_string(). * - * Since: 0.2 + * Since: 1.0 */ pspec = g_param_spec_string ("font-name", "Font Name", @@ -1110,22 +1163,19 @@ clutter_text_class_init (ClutterTextClass *klass) * if both cursor-visible is set and editable is set at the same time, * the value defaults to TRUE. */ - g_object_class_install_property - (gobject_class, PROP_CURSOR_VISIBLE, - g_param_spec_boolean ("cursor-visible", - "Cursor Visible", - "Whether the input cursor is visible", - TRUE, - G_PARAM_READWRITE)); + pspec = g_param_spec_boolean ("cursor-visible", + "Cursor Visible", + "Whether the input cursor is visible", + TRUE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec); - - g_object_class_install_property - (gobject_class, PROP_CURSOR_COLOR, - g_param_spec_boxed ("cursor-color", - "Cursor Colour", - "Cursor Colour", - CLUTTER_TYPE_COLOR, - G_PARAM_READWRITE)); + pspec = clutter_param_spec_color ("cursor-color", + "Cursor Colour", + "Cursor Colour", + &default_cursor_color, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec); /** * ClutterText:position: @@ -1133,11 +1183,11 @@ clutter_text_class_init (ClutterTextClass *klass) * The current input cursor position. -1 is taken to be the end of the text */ pspec = g_param_spec_int ("position", - "Position", - "The cursor position", - -1, G_MAXINT, - -1, - G_PARAM_READWRITE); + "Position", + "The cursor position", + -1, G_MAXINT, + -1, + CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_POSITION, pspec); /** @@ -1146,11 +1196,12 @@ clutter_text_class_init (ClutterTextClass *klass) * The current input cursor position. -1 is taken to be the end of the text */ pspec = g_param_spec_int ("selection-bound", - "Selection-bound", - "The cursor position of the other end of the selection.", - -1, G_MAXINT, - -1, - G_PARAM_READWRITE); + "Selection-bound", + "The cursor position of the other end " + "of the selection", + -1, G_MAXINT, + -1, + CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec); pspec = g_param_spec_boxed ("attributes", @@ -1250,11 +1301,30 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); - /** + pspec = g_param_spec_boolean ("text-visible", + "Text Visible", + "Whether the text should be visible " + "or subsituted with an invisible " + "Unicode character", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_TEXT_VISIBLE, pspec); + + pspec = g_param_spec_unichar ("invisible-char", + "Invisible Character", + "The Unicode character used when the " + "text is set as not visible", + '*', + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_INVISIBLE_CHAR, pspec); + + /** * ClutterText::text-changed: * @actor: the actor which received the event * - * The ::text-changed signal is emitted after @entry's text changes + * The ::text-changed signal is emitted after @actor's text changes + * + * Since: 1.0 */ text_signals[TEXT_CHANGED] = g_signal_new ("text-changed", @@ -1279,7 +1349,7 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText::activate * @actor: the actor which received the event * - * The ::activate signal is emitted each time the entry is 'activated' + * The ::activate signal is emitted each time the actor is 'activated' * by the user, normally by pressing the 'Enter' key. * * Since: 1.0 @@ -1292,8 +1362,6 @@ clutter_text_class_init (ClutterTextClass *klass) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); } static void @@ -1326,9 +1394,13 @@ clutter_text_init (ClutterText *self) priv->x_pos = -1; priv->cursor_visible = TRUE; priv->editable = FALSE; + priv->selectable = TRUE; priv->cursor_color_set = FALSE; + priv->text_visible = TRUE; + priv->priv_char = '*'; + init_commands (self); /* FIXME: free */ init_mappings (self); /* FIXME: free */ } @@ -1347,7 +1419,7 @@ clutter_text_new_full (const gchar *font_name, ClutterActor * clutter_text_new_with_text (const gchar *font_name, - const gchar *text) + const gchar *text) { return g_object_new (CLUTTER_TYPE_TEXT, "font-name", font_name, @@ -1355,98 +1427,133 @@ clutter_text_new_with_text (const gchar *font_name, NULL); } - void -clutter_text_set_editable (ClutterText *text, +clutter_text_set_editable (ClutterText *self, gboolean editable) { ClutterTextPrivate *priv; - priv = text->priv; - priv->editable = editable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->editable != editable) + { + priv->editable = editable; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "editable"); + } } gboolean -clutter_text_get_editable (ClutterText *text) +clutter_text_get_editable (ClutterText *self) { - ClutterTextPrivate *priv; - priv = text->priv; - return priv->editable; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->editable; } void -clutter_text_set_selectable (ClutterText *text, - gboolean selectable) +clutter_text_set_selectable (ClutterText *self, + gboolean selectable) { ClutterTextPrivate *priv; - priv = text->priv; - priv->selectable = selectable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->selectable != selectable) + { + priv->selectable = selectable; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "selectable"); + } } gboolean -clutter_text_get_selectable (ClutterText *text) +clutter_text_get_selectable (ClutterText *self) { - ClutterTextPrivate *priv; - priv = text->priv; - return priv->selectable; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->selectable; } void -clutter_text_set_activatable (ClutterText *text, - gboolean activatable) +clutter_text_set_activatable (ClutterText *self, + gboolean activatable) { ClutterTextPrivate *priv; - priv = text->priv; - priv->activatable = activatable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->activatable != activatable) + { + priv->activatable = activatable; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "activatable"); + } } gboolean -clutter_text_get_activatable (ClutterText *text) +clutter_text_get_activatable (ClutterText *self) { - ClutterTextPrivate *priv; - priv = text->priv; - return priv->activatable; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->activatable; } - void -clutter_text_set_cursor_visible (ClutterText *text, - gboolean cursor_visible) +clutter_text_set_cursor_visible (ClutterText *self, + gboolean cursor_visible) { ClutterTextPrivate *priv; - priv = text->priv; - priv->cursor_visible = cursor_visible; - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->cursor_visible != cursor_visible) + { + priv->cursor_visible = cursor_visible; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "cursor-visible"); + } } gboolean -clutter_text_get_cursor_visible (ClutterText *text) +clutter_text_get_cursor_visible (ClutterText *self) { - ClutterTextPrivate *priv; - priv = text->priv; - return priv->cursor_visible; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->cursor_visible; } void -clutter_text_set_cursor_color (ClutterText *text, - const ClutterColor *color) +clutter_text_set_cursor_color (ClutterText *self, + const ClutterColor *color) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); - g_return_if_fail (color != NULL); + g_return_if_fail (CLUTTER_IS_TEXT (self)); - priv = text->priv; - - g_object_ref (text); + priv = self->priv; if (color) { @@ -1454,22 +1561,26 @@ clutter_text_set_cursor_color (ClutterText *text, priv->cursor_color_set = TRUE; } else - { - priv->cursor_color_set = FALSE; - } + priv->cursor_color_set = FALSE; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "cursor-color"); + g_object_notify (G_OBJECT (self), "cursor-color-set"); } void -clutter_text_get_cursor_color (ClutterText *text, - ClutterColor *color) +clutter_text_get_cursor_color (ClutterText *self, + ClutterColor *color) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); - priv = text->priv; + priv = self->priv; *color = priv->cursor_color; } @@ -1478,7 +1589,6 @@ gchar * clutter_text_get_selection (ClutterText *text) { ClutterTextPrivate *priv; - const gchar *utf8 = clutter_text_get_text (text); gchar *str; gint len; @@ -1635,12 +1745,10 @@ clutter_text_action_move_up (ClutterText *ttext, text = clutter_text_get_text (ttext); - pango_layout_index_to_line_x ( - clutter_text_get_layout (ttext), - offset_to_bytes (text, priv->position), - 0, - &line_no, - &x); + pango_layout_index_to_line_x (clutter_text_get_layout (ttext), + offset_to_bytes (text, priv->position), + 0, + &line_no, &x); if (priv->x_pos != -1) x = priv->x_pos; @@ -1709,10 +1817,12 @@ clutter_text_action_move_down (ClutterText *ttext, pango_layout_line_x_to_index (layout_line, x, &index_, NULL); - { - gint pos = bytes_to_offset (text, index_); - clutter_text_set_cursor_position (ttext, pos); - } + { + gint pos = bytes_to_offset (text, index_); + + clutter_text_set_cursor_position (ttext, pos); + } + if (!(priv->selectable && event && (event->key.modifier_state & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (ttext); @@ -1992,21 +2102,54 @@ clutter_text_get_text (ClutterText *text) } void -clutter_text_set_text (ClutterText *text, - const gchar *str) +clutter_text_set_text (ClutterText *self, + const gchar *text) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); - priv = text->priv; + priv = self->priv; - g_free (priv->text); - priv->text = g_strdup (str); + if (priv->max_length > 0) + { + gint len = g_utf8_strlen (text, -1); - clutter_text_dirty_cache (text); + if (len < priv->max_length) + { + g_free (priv->text); - clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + priv->text = g_strdup (text); + priv->n_bytes = priv->text ? strlen (priv->text) : 0; + priv->n_chars = len; + } + else + { + gchar *n = g_malloc0 (priv->max_length + 1); + + g_free (priv->text); + + g_utf8_strncpy (n, text, priv->max_length); + + priv->text = n; + priv->n_bytes = strlen (n); + priv->n_chars = priv->max_length; + } + } + else + { + g_free (priv->text); + + priv->text = g_strdup (text); + priv->n_bytes = priv->text ? strlen (priv->text) : 0; + priv->n_chars = priv->text ? g_utf8_strlen (priv->text, -1) : 0; + } + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_signal_emit (self, text_signals[TEXT_CHANGED], 0); g_object_notify (G_OBJECT (text), "text"); } @@ -2464,6 +2607,177 @@ clutter_text_set_cursor_position (ClutterText *self, clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } +/** + * clutter_text_set_text_visible: + * @self: a #ClutterText + * @visible: %TRUE if the contents of the actor are displayed as plain text. + * + * Sets whether the contents of the text actor are visible or not. When + * visibility is set to %FALSE, characters are displayed as the invisible + * char, and will also appear that way when the text in the text actor is + * copied elsewhere. + * + * The default invisible char is the asterisk '*', but it can be changed with + * clutter_text_set_invisible_char(). + * + * Since: 1.0 + */ +void +clutter_text_set_text_visible (ClutterText *self, + gboolean visible) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->text_visible != visible) + { + priv->text_visible = visible; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "text-visible"); + } +} + +/** + * clutter_text_get_text_visible: + * @self: a #ClutterText + * + * Retrieves the actor's text visibility. + * + * Return value: %TRUE if the contents of the actor are displayed as plaintext + * + * Since: 1.0 + */ +gboolean +clutter_text_get_text_visible (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->text_visible; +} + +/** + * clutter_text_set_invisible_char: + * @self: a #ClutterText + * @wc: a Unicode character + * + * Sets the character to use in place of the actual text when + * clutter_text_set_text_visible() has been called to set text visibility + * to %FALSE. i.e. this is the character used in "password mode" to show the + * user how many characters have been typed. The default invisible char is an + * asterisk ('*'). If you set the invisible char to 0, then the user will get + * no feedback at all: there will be no text on the screen as they type. + * + * Since: 1.0 + */ +void +clutter_text_set_invisible_char (ClutterText *self, + gunichar wc) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + priv->priv_char = wc; + + if (priv->text_visible) + { + clutter_text_dirty_cache (self); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + } + + g_object_notify (G_OBJECT (self), "invisible-char"); +} + +/** + * clutter_text_get_invisible_char: + * @self: a #ClutterText + * + * Returns the character to use in place of the actual text when + * the #ClutterText:text-visibility property is set to %FALSE. + * + * Return value: a Unicode character + * + * Since: 1.0 + */ +gunichar +clutter_text_get_invisible_char (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), '*'); + + return self->priv->priv_char; +} + +#if 0 +/** + * clutter_text_set_max_length: + * @self: a #ClutterText + * @max: the maximum number of characters allowed in the text actor; 0 + * to disable or -1 to set the length of the current string + * + * Sets the maximum allowed length of the contents of the actor. If the + * current contents are longer than the given length, then they will be + * truncated to fit. + * + * Since: 0.4 + */ +void +clutter_text_set_max_length (ClutterText *self, + gint max) +{ + ClutterTextPrivate *priv; + gchar *new = NULL; + + g_return_if_fail (CLUTTER_IS_TEXT (entry)); + + priv = self->priv; + + if (priv->max_length != max) + { + g_object_ref (entry); + + if (max < 0) + max = g_utf8_strlen (priv->text, -1); + + priv->max_length = max; + + new = g_strdup (priv->text); + clutter_text_set_text (entry, new); + g_free (new); + + g_object_notify (G_OBJECT (entry), "max-length"); + g_object_unref (entry); + } +} + +/** + * clutter_text_get_max_length: + * @entry: a #ClutterText + * + * Gets the maximum length of text that can be set into @entry. + * See clutter_text_set_max_length(). + * + * Return value: the maximum number of characters. + * + * Since: 0.4 + */ +gint +clutter_text_get_max_length (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (entry), -1); + + return self->priv->max_length; +} +#endif + void clutter_text_insert_unichar (ClutterText *self, gunichar wc) @@ -2499,8 +2813,6 @@ clutter_text_insert_unichar (ClutterText *self, g_string_free (new, TRUE); g_object_unref (self); - - g_signal_emit (G_OBJECT (self), text_signals[TEXT_CHANGED], 0); } void @@ -2531,13 +2843,11 @@ clutter_text_delete_text (ClutterText *ttext, } new = g_string_new (text); - new = g_string_erase (new, start_bytes, end_bytes - start_bytes); clutter_text_set_text (ttext, new->str); g_string_free (new, TRUE); - g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); } diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 65c919796..9bc2741e8 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -3,9 +3,10 @@ * * An OpenGL based 'interactive canvas' library. * - * Copyright (C) 2006-2008 OpenedHand + * Copyright (C) 2008 Intel Corporation. * - * Authored By Øyvind Kolås + * Authored By: Øyvind Kolås + * Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,8 +29,8 @@ #ifndef __CLUTTER_TEXT_H__ #define __CLUTTER_TEXT_H__ -#include -#include +#include +#include G_BEGIN_DECLS @@ -54,13 +55,17 @@ struct _ClutterText struct _ClutterTextClass { + /*< private >*/ ClutterActorClass parent_class; + /*< public >*/ void (* text_changed) (ClutterText *self); void (* activate) (ClutterText *self); void (* cursor_event) (ClutterText *self, ClutterGeometry *geometry); + /*< private >*/ + /* padding for future expansion */ void (* _clutter_reserved1) (void); void (* _clutter_reserved2) (void); void (* _clutter_reserved3) (void); @@ -151,14 +156,14 @@ void clutter_text_set_selection_bound (ClutterText *self gint selection_bound); gint clutter_text_get_selection_bound (ClutterText *self); gchar * clutter_text_get_selection (ClutterText *self); -void clutter_text_set_visibility (ClutterText *self, +void clutter_text_set_text_visible (ClutterText *self, gboolean visible); -gboolean clutter_text_get_visibility (ClutterText *self); +gboolean clutter_text_get_text_visible (ClutterText *self); void clutter_text_set_invisible_char (ClutterText *self, - gunichar wc); + gunichar wc); gunichar clutter_text_get_invisible_char (ClutterText *self); void clutter_text_set_max_length (ClutterText *self, - gint max); + gint max); gint clutter_text_get_max_length (ClutterText *self); /* add a custom action that can be used in keybindings */ From 7af992974e08707d583cdb94d7353e4e64ee5633 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:42:33 +0000 Subject: [PATCH 025/147] Initialize the cursor position By default, the cursor position is initialized to -1, meaning "place the cursor always at the end of the text". --- clutter/clutter-text.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index ffee909d9..2fe488bc3 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1391,6 +1391,8 @@ clutter_text_init (ClutterText *self) priv->font_name = g_strdup (DEFAULT_FONT_NAME); priv->font_desc = pango_font_description_from_string (priv->font_name); + priv->position = -1; + priv->x_pos = -1; priv->cursor_visible = TRUE; priv->editable = FALSE; From 74257dfa273a6f283e01bdc91d5a74dea0bf38e0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:43:37 +0000 Subject: [PATCH 026/147] Add missing basic constructor ClutterText should have an empty constructor, mostly for bindings to use, that just proxies call to g_object_new() without setting any property. --- clutter/clutter-text.c | 6 ++++++ clutter/clutter-text.h | 1 + 2 files changed, 7 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 2fe488bc3..83ab13513 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1407,6 +1407,12 @@ clutter_text_init (ClutterText *self) init_mappings (self); /* FIXME: free */ } +ClutterActor * +clutter_text_new (void) +{ + return g_object_new (CLUTTER_TYPE_TEXT, NULL); +} + ClutterActor * clutter_text_new_full (const gchar *font_name, const gchar *text, diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 9bc2741e8..491e3a3ff 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -78,6 +78,7 @@ struct _ClutterTextClass GType clutter_text_get_type (void) G_GNUC_CONST; +ClutterActor * clutter_text_new (void); ClutterActor * clutter_text_new_full (const gchar *font_name, const gchar *text, const ClutterColor *color); From 9169dff794889a9533f1cf778d452fdfdcc4edae Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:43:56 +0000 Subject: [PATCH 027/147] Add text editing methods Port the text editing methods from ClutterEntry, so that ClutterText exposes the same API. --- clutter/clutter-text.c | 100 +++++++++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 83ab13513..1fdb61ce4 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2824,7 +2824,28 @@ clutter_text_insert_unichar (ClutterText *self, } void -clutter_text_delete_text (ClutterText *ttext, +clutter_text_insert_text (ClutterText *self, + const gchar *text, + gssize position) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (text != NULL); + + priv = self->priv; + + new = g_string_new (priv->text); + new = g_string_insert (new, position, text); + + clutter_text_set_text (self, new->str); + + g_string_free (new, TRUE); +} + +void +clutter_text_delete_text (ClutterText *self, gssize start_pos, gssize end_pos) { @@ -2832,38 +2853,89 @@ clutter_text_delete_text (ClutterText *ttext, GString *new = NULL; gint start_bytes; gint end_bytes; - const gchar *text; - g_return_if_fail (CLUTTER_IS_TEXT (ttext)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); - priv = ttext->priv; - text = clutter_text_get_text (ttext); + priv = self->priv; if (end_pos == -1) { - start_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1) - 1); - end_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1)); + start_bytes = offset_to_bytes (priv->text, + g_utf8_strlen (priv->text, -1) - 1); + end_bytes = offset_to_bytes (priv->text, + g_utf8_strlen (priv->text, -1)); } else { - start_bytes = offset_to_bytes (text, start_pos); - end_bytes = offset_to_bytes (text, end_pos); + start_bytes = offset_to_bytes (priv->text, start_pos); + end_bytes = offset_to_bytes (priv->text, end_pos); } - new = g_string_new (text); + new = g_string_new (priv->text); new = g_string_erase (new, start_bytes, end_bytes - start_bytes); - clutter_text_set_text (ttext, new->str); + clutter_text_set_text (self, new->str); g_string_free (new, TRUE); } +void +clutter_text_delete_chars (ClutterText *self, + guint n_chars) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + gint len; + gint pos; + gint num_pos; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (!priv->text) + return; + + len = g_utf8_strlen (priv->text, -1); + new = g_string_new (priv->text); + + if (priv->position == -1) + { + num_pos = offset_to_bytes (priv->text, len - n_chars); + new = g_string_erase (new, num_pos, -1); + } + else + { + pos = offset_to_bytes (priv->text, priv->position - n_chars); + num_pos = offset_to_bytes (priv->text, priv->position); + new = g_string_erase (new, pos, num_pos - pos); + } + + clutter_text_set_text (self, new->str); + + if (priv->position > 0) + clutter_text_set_cursor_position (self, priv->position - n_chars); + + g_string_free (new, TRUE); + + g_object_notify (G_OBJECT (self), "text"); +} + +gchar * +clutter_text_get_chars (ClutterText *self, + gssize start_pos, + gssize end_pos) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + return NULL; +} void clutter_text_add_mapping (ClutterText *ttext, - guint keyval, - ClutterModifierType state, - const gchar *commandline) + guint keyval, + ClutterModifierType state, + const gchar *commandline) { ClutterTextMapping *tmapping = g_new (ClutterTextMapping, 1); ClutterTextPrivate *priv = ttext->priv; From 94789e3cc3c84acbb63cc3e83a8fb949dd5186f5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:45:37 +0000 Subject: [PATCH 028/147] Add a separate test unit for ClutterText Instead of changing the unit for ClutterEntry, we add a new test unit specifically for ClutterText so that we can later tweak it specifically for the behaviour changes needed to make ClutterText work better. --- tests/conform/Makefile.am | 3 +- tests/conform/test-clutter-entry.c | 2 +- tests/conform/test-clutter-text.c | 370 +++++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 17 ++ 4 files changed, 390 insertions(+), 2 deletions(-) create mode 100644 tests/conform/test-clutter-text.c diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 985d74125..774156cfc 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -22,7 +22,8 @@ test_conformance_SOURCES = \ test-actor-invariants.c \ test-paint-opacity.c \ test-backface-culling.c \ - test-binding-pool.c + test-binding-pool.c \ + test-clutter-text.c # For convenience, this provides a way to easily run individual unit tests: .PHONY: wrappers diff --git a/tests/conform/test-clutter-entry.c b/tests/conform/test-clutter-entry.c index 6562836b1..a12d46b75 100644 --- a/tests/conform/test-clutter-entry.c +++ b/tests/conform/test-clutter-entry.c @@ -10,7 +10,7 @@ typedef struct { gint nbytes; } TestData; -const TestData +static const TestData test_data[] = { { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c new file mode 100644 index 000000000..5080423d0 --- /dev/null +++ b/tests/conform/test-clutter-text.c @@ -0,0 +1,370 @@ +#include +#include +#include + +#include "test-conform-common.h" + +typedef struct { + gunichar unichar; + const char bytes[6]; + gint nbytes; +} TestData; + +static const TestData +test_data[] = { + { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ + { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ +}; + +void +test_text_utf8_validation (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + gunichar unichar; + char bytes[6]; + int nbytes; + + g_assert (g_unichar_validate (t->unichar)); + + nbytes = g_unichar_to_utf8 (t->unichar, bytes); + bytes[nbytes] = '\0'; + g_assert (nbytes == t->nbytes); + g_assert (memcmp (t->bytes, bytes, nbytes) == 0); + + unichar = g_utf8_get_char_validated (bytes, nbytes); + g_assert (unichar == t->unichar); + } +} + +static int +get_nbytes (ClutterText *text) +{ + const char *s = clutter_text_get_text (text); + return strlen (s); +} + +static int +get_nchars (ClutterText *text) +{ + const char *s = clutter_text_get_text (text); + g_assert (g_utf8_validate (s, -1, NULL)); + return g_utf8_strlen (s, -1); +} + +#define DONT_MOVE_CURSOR (-2) + +static void +insert_unichar (ClutterText *text, gunichar unichar, int position) +{ + if (position > DONT_MOVE_CURSOR) + { + clutter_text_set_cursor_position (text, position); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, position); + } + + clutter_text_insert_unichar (text, unichar); +} + +void +test_text_empty (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + g_assert (clutter_text_get_text (text) == NULL); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_set_empty (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + /* annoyingly slightly different from initially empty */ + clutter_text_set_text (text, ""); + g_assert_cmpint (get_nchars (text), ==, 0); + g_assert_cmpint (get_nbytes (text), ==, 0); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_set_text (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + clutter_text_set_text (text, "abcdef"); + g_assert_cmpint (get_nchars (text), ==, 6); + g_assert_cmpint (get_nbytes (text), ==, 6); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_text_set_cursor_position (text, 5); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 5); + + clutter_text_set_text (text, ""); + /* FIXME: cursor position should be -1? + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + */ + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_append_some (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + int j; + + for (j = 1; j <= 4; j++) + { + insert_unichar (text, t->unichar, DONT_MOVE_CURSOR); + g_assert_cmpint (get_nchars (text), ==, j); + g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + } + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_prepend_some (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + int j; + + clutter_text_insert_unichar (text, t->unichar); + g_assert_cmpint (get_nchars (text), ==, 1); + g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + for (j = 2; j <= 4; j++) + { + insert_unichar (text, t->unichar, 0); + g_assert_cmpint (get_nchars (text), ==, j); + g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + } + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_insert (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + + clutter_text_insert_unichar (text, t->unichar); + clutter_text_insert_unichar (text, t->unichar); + + insert_unichar (text, t->unichar, 1); + g_assert_cmpint (get_nchars (text), ==, 3); + g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 2); + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_delete_chars (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + int j; + + for (j = 0; j < 4; j++) + clutter_text_insert_unichar (text, t->unichar); + + clutter_text_set_cursor_position (text, 2); + clutter_text_delete_chars (text, 1); + g_assert_cmpint (get_nchars (text), ==, 3); + g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + + clutter_text_set_cursor_position (text, 2); + clutter_text_delete_chars (text, 1); + g_assert_cmpint (get_nchars (text), ==, 2); + g_assert_cmpint (get_nbytes (text), ==, 2 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_delete_text (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + int j; + + for (j = 0; j < 4; j++) + clutter_text_insert_unichar (text, t->unichar); + + clutter_text_set_cursor_position (text, 3); + clutter_text_delete_text (text, 2, 4); + + g_assert_cmpint (get_nchars (text), ==, 2); + g_assert_cmpint (get_nbytes (text), ==, 2 * t->nbytes); + + /* FIXME: cursor position should be -1? + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + */ + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +static void +init_event (ClutterKeyEvent *event) +{ + event->type = CLUTTER_KEY_PRESS; + event->time = 0; /* not needed */ + event->flags = CLUTTER_EVENT_FLAG_SYNTHETIC; + event->stage = NULL; /* not needed */ + event->source = NULL; /* not needed */ + event->modifier_state = 0; + event->hardware_keycode = 0; /* not needed */ +} + +static void +send_keyval (ClutterText *text, int keyval) +{ + ClutterKeyEvent event; + + init_event (&event); + event.keyval = keyval; + event.unicode_value = 0; /* should be ignored for cursor keys etc. */ + + clutter_actor_event (CLUTTER_ACTOR (text), (ClutterEvent *) &event, FALSE); +} + +static inline void +send_unichar (ClutterText *text, gunichar unichar) +{ + ClutterKeyEvent event; + + init_event (&event); + event.keyval = 0; /* should be ignored for printable characters */ + event.unicode_value = unichar; + + clutter_actor_event (CLUTTER_ACTOR (text), (ClutterEvent *) &event, FALSE); +} + +void +test_text_cursor (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + int j; + + for (j = 0; j < 4; ++j) + clutter_text_insert_unichar (text, t->unichar); + + clutter_text_set_cursor_position (text, 2); + + /* test cursor moves and is clamped */ + send_keyval (text, CLUTTER_Left); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + + send_keyval (text, CLUTTER_Left); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 0); + + send_keyval (text, CLUTTER_Left); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 0); + + /* delete text containing the cursor */ + clutter_text_set_cursor_position (text, 3); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 3); + + clutter_text_delete_text (text, 2, 4); + send_keyval (text, CLUTTER_Left); + + /* FIXME: cursor position should be -1? + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + */ + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_event (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + + send_unichar (text, t->unichar); + + g_assert_cmpint (get_nchars (text), ==, 1); + g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index ff9535baf..9fb08c58d 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -70,6 +70,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/label", test_label_cache); + /* ClutterEntry */ TEST_CONFORM_SIMPLE ("/entry", test_entry_utf8_validation); TEST_CONFORM_SIMPLE ("/entry", test_entry_empty); TEST_CONFORM_SIMPLE ("/entry", test_entry_set_empty); @@ -85,6 +86,22 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/entry", test_entry_cursor); TEST_CONFORM_SIMPLE ("/entry", test_entry_event); + /* ClutterText */ + TEST_CONFORM_SIMPLE ("/text", test_text_utf8_validation); + TEST_CONFORM_SIMPLE ("/text", test_text_empty); + TEST_CONFORM_SIMPLE ("/text", test_text_set_empty); + TEST_CONFORM_SIMPLE ("/text", test_text_set_text); + + TEST_CONFORM_SIMPLE ("/text", test_text_append_some); + TEST_CONFORM_SIMPLE ("/text", test_text_prepend_some); + TEST_CONFORM_SIMPLE ("/text", test_text_insert); + + TEST_CONFORM_SIMPLE ("/text", test_text_delete_chars); + TEST_CONFORM_SIMPLE ("/text", test_text_delete_text); + + TEST_CONFORM_SIMPLE ("/text", test_text_cursor); + TEST_CONFORM_SIMPLE ("/text", test_text_event); + TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_size); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_color); From 7fa93ebe9ed7be18a6dba6174020434759db691b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:47:06 +0000 Subject: [PATCH 029/147] Replace offset_to_bytes() implementation We should re-use the offset_to_bytes() implementation from ClutterEntry as it guaranteed some behaviour and sanity checks that we want to keep inside ClutterText. --- clutter/clutter-text.c | 46 +++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 1fdb61ce4..1c6103568 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -182,15 +182,47 @@ enum static guint text_signals[LAST_SIGNAL] = { 0, }; -#define offset_real(text, pos) \ - (pos == -1 ? g_utf8_strlen (text, -1) : pos) +#define offset_real(t,p) \ + ((p) == -1 ? g_utf8_strlen ((t), -1) : (p)) -#define offset_to_bytes(text,pos) \ - (pos == -1 ? strlen (text) \ - : ((gint) (g_utf8_offset_to_pointer (text, pos) - text))) +static gint +offset_to_bytes (const gchar *text, + gint pos) +{ + gchar *c = NULL; + gint i, j, len; -#define bytes_to_offset(text, pos) \ - (g_utf8_pointer_to_offset (text, text + pos)) + if (pos < 0) + return strlen (text); + +#if 0 + if (pos < 1) + return pos; +#endif + + c = g_utf8_next_char (text); + j = 1; + len = strlen (text); + + for (i = 0; i < len; i++) + { + if (&text[i] == c) + { + if (j == pos) + break; + else + { + c = g_utf8_next_char (c); + j++; + } + } + } + + return i; +} + +#define bytes_to_offset(t,p) \ + (g_utf8_pointer_to_offset ((t), (t) + (p))) typedef struct TextCommand { From bdb0cc462d547c0e553c02f1692f35e60034d338 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:48:17 +0000 Subject: [PATCH 030/147] General whitespace fixes in ClutterText Let's keep whitespace fixes to their own commit to avoid polluting git-blame. --- clutter/clutter-text.c | 105 ++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 43 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 1c6103568..43ab3a2d1 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -478,68 +478,80 @@ clutter_text_coords_to_position (ClutterText *text, } static gboolean -clutter_text_position_to_coords (ClutterText *ttext, - gint position, - gint *x, - gint *y, - gint *cursor_height) +clutter_text_position_to_coords (ClutterText *self, + gint position, + gint *x, + gint *y, + gint *cursor_height) { - ClutterTextPrivate *priv; - gint index_; - PangoRectangle rect; - const gchar *text; + ClutterTextPrivate *priv = self->priv; + PangoRectangle rect; + gint priv_char_bytes; + gint index_; - text = clutter_text_get_text (ttext); - - priv = ttext->priv; + if (!priv->text_visible && priv->priv_char) + priv_char_bytes = g_unichar_to_utf8 (priv->priv_char, NULL); + else + priv_char_bytes = 1; if (position == -1) - index_ = strlen (text); + { + if (priv->text_visible) + index_ = strlen (priv->text); + else + index_ = priv->n_chars * priv_char_bytes; + } else - index_ = offset_to_bytes (text, position); + { + if (priv->text_visible) + index_ = offset_to_bytes (priv->text, position); + else + index_ = priv->position * priv_char_bytes; + } - if (index_ > strlen (text)) - index_ = strlen (text); - - pango_layout_get_cursor_pos (clutter_text_get_layout (ttext), - index_, + pango_layout_get_cursor_pos (clutter_text_get_layout (self), index_, &rect, NULL); if (x) *x = rect.x / PANGO_SCALE; + if (y) *y = (rect.y + rect.height) / PANGO_SCALE; + if (cursor_height) *cursor_height = rect.height / PANGO_SCALE; return TRUE; /* FIXME: should return false if coords were outside text */ } -static void -clutter_text_ensure_cursor_position (ClutterText *ttext) +static inline void +clutter_text_ensure_cursor_position (ClutterText *self) { - gint x,y,cursor_height; - - ClutterTextPrivate *priv; - priv = ttext->priv; + ClutterTextPrivate *priv = self->priv; + gint x, y, cursor_height; - clutter_text_position_to_coords (ttext, priv->position, &x, &y, &cursor_height); + clutter_text_position_to_coords (self, priv->position, + &x, &y, + &cursor_height); priv->cursor_pos.x = x; priv->cursor_pos.y = y - cursor_height; - priv->cursor_pos.width = 2; + priv->cursor_pos.width = 2; priv->cursor_pos.height = cursor_height; - g_signal_emit (ttext, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); + g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } -static inline gboolean +static gboolean clutter_text_truncate_selection_internal (ClutterText *self) { ClutterTextPrivate *priv = self->priv; gint start_index; gint end_index; + if (!priv->text) + return TRUE; + start_index = offset_real (priv->text, priv->position); end_index = offset_real (priv->text, priv->selection_bound); @@ -1424,6 +1436,7 @@ clutter_text_init (ClutterText *self) priv->font_desc = pango_font_description_from_string (priv->font_name); priv->position = -1; + priv->selection_bound = -1; priv->x_pos = -1; priv->cursor_visible = TRUE; @@ -1435,6 +1448,8 @@ clutter_text_init (ClutterText *self) priv->text_visible = TRUE; priv->priv_char = '*'; + priv->max_length = 0; + init_commands (self); /* FIXME: free */ init_mappings (self); /* FIXME: free */ } @@ -1696,9 +1711,9 @@ clutter_text_get_selection_bound (ClutterText *self) /****************************************************************/ static gboolean -clutter_text_action_activate (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_activate (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { g_signal_emit (G_OBJECT (ttext), text_signals[ACTIVATE], 0); return TRUE; @@ -1708,13 +1723,14 @@ static void clutter_text_clear_selection (ClutterText *ttext) { ClutterTextPrivate *priv = ttext->priv; + priv->selection_bound = priv->position; } static gboolean clutter_text_action_move_left (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint pos = priv->position; @@ -1772,9 +1788,9 @@ clutter_text_action_move_right (ClutterText *ttext, } static gboolean -clutter_text_action_move_up (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_up (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint line_no; @@ -1799,9 +1815,8 @@ clutter_text_action_move_up (ClutterText *ttext, if (line_no < 0) return FALSE; - layout_line = pango_layout_get_line_readonly ( - clutter_text_get_layout (ttext), - line_no); + layout_line = + pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); if (!layout_line) return TRUE; @@ -1987,14 +2002,16 @@ clutter_text_action_delete_next (ClutterText *ttext, gint pos; gint len; - if (clutter_text_truncate_selection (ttext, NULL, 0)) + if (clutter_text_truncate_selection_internal (ttext)) return TRUE; + priv = ttext->priv; pos = priv->position; len = g_utf8_strlen (clutter_text_get_text (ttext), -1); if (len && pos != -1 && pos < len) - clutter_text_delete_text (ttext, pos, pos+1);; + clutter_text_delete_text (ttext, pos, pos+1); + return TRUE; } @@ -2007,8 +2024,9 @@ clutter_text_action_delete_previous (ClutterText *ttext, gint pos; gint len; - if (clutter_text_truncate_selection (ttext, NULL, 0)) + if (clutter_text_truncate_selection_internal (ttext)) return TRUE; + priv = ttext->priv; pos = priv->position; len = g_utf8_strlen (clutter_text_get_text (ttext), -1); @@ -2148,6 +2166,7 @@ clutter_text_set_text (ClutterText *self, ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (text != NULL); priv = self->priv; From c72722df8ce3d3c91a26b8fc41cb7af9c6b6d864 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:49:14 +0000 Subject: [PATCH 031/147] Add :cursor-color-set property declaration The :cursor-color-set property is a read-only property that reflects whether the ClutterText actor is going to use the color set inside the :cursor-color property when painting the cursor. --- clutter/clutter-text.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 43ab3a2d1..8395266eb 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -702,6 +702,10 @@ clutter_text_get_property (GObject *gobject, clutter_value_set_color (value, &priv->cursor_color); break; + case PROP_CURSOR_COLOR_SET: + g_value_set_boolean (value, priv->cursor_color_set); + break; + case PROP_POSITION: g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); break; @@ -1215,12 +1219,19 @@ clutter_text_class_init (ClutterTextClass *klass) g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec); pspec = clutter_param_spec_color ("cursor-color", - "Cursor Colour", - "Cursor Colour", + "Cursor Color", + "Cursor Color", &default_cursor_color, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec); + pspec = g_param_spec_boolean ("cursor-color-set", + "Cursor Color Set", + "Whether the cursor color has been set", + FALSE, + CLUTTER_PARAM_READABLE); + g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR_SET, pspec); + /** * ClutterText:position: * From 4cc57bdc4178571c7c296b09d09b8da93c828ead Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:49:45 +0000 Subject: [PATCH 032/147] Improve the safety checks inside the text setters We should check that the contents of the Text actor are not NULL when computing the offsets in bytes. --- clutter/clutter-text.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 8395266eb..b12ca9697 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2190,7 +2190,7 @@ clutter_text_set_text (ClutterText *self, g_free (priv->text); priv->text = g_strdup (text); - priv->n_bytes = priv->text ? strlen (priv->text) : 0; + priv->n_bytes = strlen (text); priv->n_chars = len; } else @@ -2211,8 +2211,8 @@ clutter_text_set_text (ClutterText *self, g_free (priv->text); priv->text = g_strdup (text); - priv->n_bytes = priv->text ? strlen (priv->text) : 0; - priv->n_chars = priv->text ? g_utf8_strlen (priv->text, -1) : 0; + priv->n_bytes = strlen (text); + priv->n_chars = g_utf8_strlen (text, -1); } clutter_text_dirty_cache (self); @@ -2221,7 +2221,7 @@ clutter_text_set_text (ClutterText *self, g_signal_emit (self, text_signals[TEXT_CHANGED], 0); - g_object_notify (G_OBJECT (text), "text"); + g_object_notify (G_OBJECT (self), "text"); } PangoLayout * @@ -2663,9 +2663,6 @@ clutter_text_set_cursor_position (ClutterText *self, priv = self->priv; - if (priv->text == NULL) - return; - len = g_utf8_strlen (priv->text, -1); if (position < 0 || position >= len) @@ -2862,14 +2859,15 @@ clutter_text_insert_unichar (ClutterText *self, if (wc == 0) return; - clutter_text_truncate_selection (self, NULL, 0); - priv = self->priv; - g_object_ref (self); - new = g_string_new (priv->text); - pos = offset_to_bytes (priv->text, priv->position); + + if (priv->text) + pos = offset_to_bytes (priv->text, priv->position); + else + pos = 0; + new = g_string_insert_unichar (new, pos, wc); clutter_text_set_text (self, new->str); @@ -2881,8 +2879,6 @@ clutter_text_insert_unichar (ClutterText *self, } g_string_free (new, TRUE); - - g_object_unref (self); } void @@ -2920,6 +2916,9 @@ clutter_text_delete_text (ClutterText *self, priv = self->priv; + if (!priv->text) + return; + if (end_pos == -1) { start_bytes = offset_to_bytes (priv->text, From 1ff02f570e0dd214ffc4a29d82b8876b362d74f9 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:50:02 +0000 Subject: [PATCH 033/147] Use the stored contents length in ::delete_chars() Instead of recomputing it, we can reuse the contents length we compute and store inside clutter_text_set_text(). --- clutter/clutter-text.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index b12ca9697..d5213a46f 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2962,7 +2962,7 @@ clutter_text_delete_chars (ClutterText *self, if (priv->position == -1) { - num_pos = offset_to_bytes (priv->text, len - n_chars); + num_pos = offset_to_bytes (priv->text, priv->n_chars - n_chars); new = g_string_erase (new, num_pos, -1); } else From 191d1aebff0dce424f67a4ac9065570d35f519b7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:51:28 +0000 Subject: [PATCH 034/147] Update the ClutterText test unit Whenever we are sending specially crafted KeyEvents to a ClutterText we also need to set it editable, since the event handling code depends on the editability setting. --- tests/conform/test-clutter-text.c | 44 ++++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c index 5080423d0..993478c4d 100644 --- a/tests/conform/test-clutter-text.c +++ b/tests/conform/test-clutter-text.c @@ -11,7 +11,7 @@ typedef struct { } TestData; static const TestData -test_data[] = { +test_text_data[] = { { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ }; @@ -22,9 +22,9 @@ test_text_utf8_validation (TestConformSimpleFixture *fixture, { int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; gunichar unichar; char bytes[6]; int nbytes; @@ -111,8 +111,8 @@ test_text_set_text (TestConformSimpleFixture *fixture, clutter_text_set_cursor_position (text, 5); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 5); - clutter_text_set_text (text, ""); /* FIXME: cursor position should be -1? + clutter_text_set_text (text, ""); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); */ @@ -126,14 +126,15 @@ test_text_append_some (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; int j; for (j = 1; j <= 4; j++) { insert_unichar (text, t->unichar, DONT_MOVE_CURSOR); + g_assert_cmpint (get_nchars (text), ==, j); g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); @@ -152,9 +153,9 @@ test_text_prepend_some (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; int j; clutter_text_insert_unichar (text, t->unichar); @@ -183,14 +184,15 @@ test_text_insert (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; clutter_text_insert_unichar (text, t->unichar); clutter_text_insert_unichar (text, t->unichar); insert_unichar (text, t->unichar, 1); + g_assert_cmpint (get_nchars (text), ==, 3); g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 2); @@ -208,9 +210,9 @@ test_text_delete_chars (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; int j; for (j = 0; j < 4; j++) @@ -241,9 +243,9 @@ test_text_delete_text (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; int j; for (j = 0; j < 4; j++) @@ -289,7 +291,7 @@ send_keyval (ClutterText *text, int keyval) clutter_actor_event (CLUTTER_ACTOR (text), (ClutterEvent *) &event, FALSE); } -static inline void +static void send_unichar (ClutterText *text, gunichar unichar) { ClutterKeyEvent event; @@ -308,9 +310,11 @@ test_text_cursor (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + clutter_text_set_editable (text, TRUE); + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; int j; for (j = 0; j < 4; ++j) @@ -352,9 +356,11 @@ test_text_event (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + clutter_text_set_editable (text, TRUE); + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; send_unichar (text, t->unichar); From d10f7127a2c07cf80c10c35eeacaa4b20269f740 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:54:07 +0000 Subject: [PATCH 035/147] Implement Text:max-length The :max-length property establishes the maximum available length for the Text actor's contents, in characters. --- clutter/clutter-text.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index d5213a46f..20489da75 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -168,7 +168,8 @@ enum PROP_SELECTABLE, PROP_ACTIVATABLE, PROP_TEXT_VISIBLE, - PROP_INVISIBLE_CHAR + PROP_INVISIBLE_CHAR, + PROP_MAX_LENGTH }; enum @@ -667,6 +668,10 @@ clutter_text_set_property (GObject *gobject, clutter_text_set_invisible_char (self, g_value_get_uint (value)); break; + case PROP_MAX_LENGTH: + clutter_text_set_max_length (self, g_value_get_int (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -734,6 +739,10 @@ clutter_text_get_property (GObject *gobject, g_value_set_uint (value, priv->priv_char); break; + case PROP_MAX_LENGTH: + g_value_set_int (value, priv->max_length); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -1373,6 +1382,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_INVISIBLE_CHAR, pspec); + pspec = g_param_spec_int ("max-length", + "Max Length", + "Maximum length of the text inside the actor", + -1, G_MAXINT, 0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, pspec); + /** * ClutterText::text-changed: * @actor: the actor which received the event @@ -2783,7 +2799,6 @@ clutter_text_get_invisible_char (ClutterText *self) return self->priv->priv_char; } -#if 0 /** * clutter_text_set_max_length: * @self: a #ClutterText @@ -2794,56 +2809,53 @@ clutter_text_get_invisible_char (ClutterText *self) * current contents are longer than the given length, then they will be * truncated to fit. * - * Since: 0.4 + * Since: 1.0 */ void clutter_text_set_max_length (ClutterText *self, - gint max) + gint max) { ClutterTextPrivate *priv; gchar *new = NULL; - g_return_if_fail (CLUTTER_IS_TEXT (entry)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->max_length != max) { - g_object_ref (entry); - if (max < 0) max = g_utf8_strlen (priv->text, -1); priv->max_length = max; new = g_strdup (priv->text); - clutter_text_set_text (entry, new); + clutter_text_set_text (self, new); g_free (new); - g_object_notify (G_OBJECT (entry), "max-length"); - g_object_unref (entry); + g_object_notify (G_OBJECT (self), "max-length"); } } /** * clutter_text_get_max_length: - * @entry: a #ClutterText + * @self: a #ClutterText + * + * Gets the maximum length of text that can be set into a text actor. * - * Gets the maximum length of text that can be set into @entry. * See clutter_text_set_max_length(). * * Return value: the maximum number of characters. * - * Since: 0.4 + * Since: 1.0 */ gint clutter_text_get_max_length (ClutterText *self) { - g_return_val_if_fail (CLUTTER_IS_TEXT (entry), -1); + g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); return self->priv->max_length; } -#endif void clutter_text_insert_unichar (ClutterText *self, From d84a88ac3b2a7dbc528c7f00a2dd0fc877d37783 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:54:32 +0000 Subject: [PATCH 036/147] Coding style consistency Channel mitch's spirit. This is also how I get to have the highest commit count on Clutter's repository. --- clutter/clutter-text.c | 126 ++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 20489da75..93809bf5c 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1755,7 +1755,7 @@ clutter_text_clear_selection (ClutterText *ttext) } static gboolean -clutter_text_action_move_left (ClutterText *ttext, +clutter_text_action_move_left (ClutterText *ttext, const gchar *commandline, ClutterEvent *event) { @@ -1785,9 +1785,9 @@ clutter_text_action_move_left (ClutterText *ttext, static gboolean -clutter_text_action_move_right (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_right (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint pos; @@ -1863,9 +1863,9 @@ clutter_text_action_move_up (ClutterText *ttext, } static gboolean -clutter_text_action_move_down (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_down (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint line_no; @@ -1888,14 +1888,12 @@ clutter_text_action_move_down (ClutterText *ttext, else priv->x_pos = x; - layout_line = pango_layout_get_line_readonly ( - clutter_text_get_layout (ttext), - line_no + 1); + layout_line = + pango_layout_get_line_readonly (clutter_text_get_layout (ttext), + line_no + 1); if (!layout_line) - { - return FALSE; - } + return FALSE; pango_layout_line_x_to_index (layout_line, x, &index_, NULL); @@ -1912,23 +1910,25 @@ clutter_text_action_move_down (ClutterText *ttext, } static gboolean -clutter_text_action_move_start (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_start (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; clutter_text_set_cursor_position (ttext, 0); + if (!(priv->selectable && event && (event->key.modifier_state & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (ttext); + return TRUE; } static gboolean -clutter_text_action_move_end (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_end (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; @@ -1941,9 +1941,9 @@ clutter_text_action_move_end (ClutterText *ttext, } static gboolean -clutter_text_action_move_start_line (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_start_line (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint line_no; @@ -1962,9 +1962,8 @@ clutter_text_action_move_start_line (ClutterText *ttext, &line_no, NULL); - layout_line = pango_layout_get_line_readonly ( - clutter_text_get_layout (ttext), - line_no); + layout_line = + pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); @@ -1979,9 +1978,9 @@ clutter_text_action_move_start_line (ClutterText *ttext, } static gboolean -clutter_text_action_move_end_line (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_end_line (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint line_no; @@ -2002,9 +2001,8 @@ clutter_text_action_move_end_line (ClutterText *ttext, &line_no, NULL); - layout_line = pango_layout_get_line_readonly ( - clutter_text_get_layout (ttext), - line_no); + layout_line = + pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); index_ += trailing; @@ -2021,9 +2019,9 @@ clutter_text_action_move_end_line (ClutterText *ttext, } static gboolean -clutter_text_action_delete_next (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_delete_next (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv; gint pos; @@ -2043,9 +2041,9 @@ clutter_text_action_delete_next (ClutterText *ttext, } static gboolean -clutter_text_action_delete_previous (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_delete_previous (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv; gint pos; @@ -2075,11 +2073,13 @@ clutter_text_action_delete_previous (ClutterText *ttext, return TRUE; } -static void init_commands (ClutterText *ttext) +static void +init_commands (ClutterText *ttext) { ClutterTextPrivate *priv = ttext->priv; if (priv->commands) return; + clutter_text_add_action (ttext, "move-left", clutter_text_action_move_left); clutter_text_add_action (ttext, "move-right", clutter_text_action_move_right); clutter_text_add_action (ttext, "move-up", clutter_text_action_move_up); @@ -2095,9 +2095,9 @@ static void init_commands (ClutterText *ttext) } gboolean -clutter_text_action (ClutterText *ttext, - const gchar *command, - ClutterEvent *event) +clutter_text_action (ClutterText *ttext, + const gchar *command, + ClutterEvent *event) { gchar command2[64]; gint i; @@ -2137,18 +2137,18 @@ clutter_text_get_font_name (ClutterText *text) } void -clutter_text_set_font_name (ClutterText *text, +clutter_text_set_font_name (ClutterText *self, const gchar *font_name) { ClutterTextPrivate *priv; PangoFontDescription *desc; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); if (!font_name || font_name[0] == '\0') font_name = DEFAULT_FONT_NAME; - priv = text->priv; + priv = self->priv; if (priv->font_name && strcmp (priv->font_name, font_name) == 0) return; @@ -2170,20 +2170,20 @@ clutter_text_set_font_name (ClutterText *text, priv->font_desc = desc; - clutter_text_dirty_cache (text); + clutter_text_dirty_cache (self); if (priv->text && priv->text[0] != '\0') - clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); - g_object_notify (G_OBJECT (text), "font-name"); + g_object_notify (G_OBJECT (self), "font-name"); } G_CONST_RETURN gchar * -clutter_text_get_text (ClutterText *text) +clutter_text_get_text (ClutterText *self) { - g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); - return text->priv->text; + return self->priv->text; } void @@ -2241,46 +2241,46 @@ clutter_text_set_text (ClutterText *self, } PangoLayout * -clutter_text_get_layout (ClutterText *text) +clutter_text_get_layout (ClutterText *self) { ClutterUnit width; - g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); - width = clutter_actor_get_widthu (CLUTTER_ACTOR (text)); + width = clutter_actor_get_widthu (CLUTTER_ACTOR (self)); - return clutter_text_create_layout (text, width); + return clutter_text_create_layout (self, width); } void -clutter_text_set_color (ClutterText *text, +clutter_text_set_color (ClutterText *self, const ClutterColor *color) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); - priv = text->priv; + priv = self->priv; priv->text_color = *color; - if (CLUTTER_ACTOR_IS_VISIBLE (text)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); - g_object_notify (G_OBJECT (text), "color"); + g_object_notify (G_OBJECT (self), "color"); } void -clutter_text_get_color (ClutterText *text, +clutter_text_get_color (ClutterText *self, ClutterColor *color) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); - priv = text->priv; + priv = self->priv; *color = priv->text_color; } From 4a1f4d6f9aa3c89750e9eac5f4d95f799d2d2291 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:55:39 +0000 Subject: [PATCH 037/147] Move internal data structures on top Try to keep the declarations section of the source files clean and possibly consistent. --- clutter/clutter-text.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 93809bf5c..601f351a7 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -56,6 +56,8 @@ #define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) typedef struct _LayoutCache LayoutCache; +typedef struct _TextCommand TextCommand; +typedef struct _ClutterTextMapping ClutterTextMapping; /* Probably move into main */ static PangoContext *_context = NULL; @@ -82,6 +84,21 @@ struct _LayoutCache guint age; }; +struct _TextCommand +{ + const gchar *name; + gboolean (*func) (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event); +}; + +struct _ClutterTextMapping +{ + ClutterModifierType state; + guint keyval; + const gchar *action; +}; + struct _ClutterTextPrivate { PangoFontDescription *font_desc; @@ -226,19 +243,6 @@ offset_to_bytes (const gchar *text, (g_utf8_pointer_to_offset ((t), (t) + (p))) -typedef struct TextCommand { - const gchar *name; - gboolean (*func) (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event); -} TextCommand; - -typedef struct ClutterTextMapping { - ClutterModifierType state; - guint keyval; - const gchar *action; -} ClutterTextMapping; - void clutter_text_mappings_clear (ClutterText *self) { From e93a7e243c3c43be59fbbfacaae9a55c5dd923a0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:56:44 +0000 Subject: [PATCH 038/147] Add comments in the test suite Comment why we need to enable the editability of the Text actor inside the test suite. This should clarify commit ea508ea528d61ae478d8bc4c88f54a89304f18e8 --- tests/conform/test-clutter-text.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c index 993478c4d..e6662182a 100644 --- a/tests/conform/test-clutter-text.c +++ b/tests/conform/test-clutter-text.c @@ -159,6 +159,7 @@ test_text_prepend_some (TestConformSimpleFixture *fixture, int j; clutter_text_insert_unichar (text, t->unichar); + g_assert_cmpint (get_nchars (text), ==, 1); g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); @@ -166,6 +167,7 @@ test_text_prepend_some (TestConformSimpleFixture *fixture, for (j = 2; j <= 4; j++) { insert_unichar (text, t->unichar, 0); + g_assert_cmpint (get_nchars (text), ==, j); g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); @@ -310,6 +312,7 @@ test_text_cursor (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; + /* only editable entries listen to events */ clutter_text_set_editable (text, TRUE); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) @@ -356,6 +359,7 @@ test_text_event (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; + /* only editable entries listen to events */ clutter_text_set_editable (text, TRUE); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) From 2fedd3263c4291aa2860e26ebface29913f909e7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:57:46 +0000 Subject: [PATCH 039/147] Do not namespace internal data structures Since the internal data structures are not exported (duh!), we can eschew the namespacing and save us some characters. --- clutter/clutter-text.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 601f351a7..db9cf469a 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -57,7 +57,7 @@ typedef struct _LayoutCache LayoutCache; typedef struct _TextCommand TextCommand; -typedef struct _ClutterTextMapping ClutterTextMapping; +typedef struct _TextMapping TextMapping; /* Probably move into main */ static PangoContext *_context = NULL; @@ -92,7 +92,7 @@ struct _TextCommand ClutterEvent *event); }; -struct _ClutterTextMapping +struct _TextMapping { ClutterModifierType state; guint keyval; @@ -976,7 +976,7 @@ clutter_text_key_press (ClutterActor *actor, for (iter = priv->mappings; iter != NULL; iter = iter->next) { - ClutterTextMapping *mapping = iter->data; + TextMapping *mapping = iter->data; if ( (mapping->keyval == keyval) && @@ -3014,7 +3014,7 @@ clutter_text_add_mapping (ClutterText *ttext, ClutterModifierType state, const gchar *commandline) { - ClutterTextMapping *tmapping = g_new (ClutterTextMapping, 1); + TextMapping *tmapping = g_new (TextMapping, 1); ClutterTextPrivate *priv = ttext->priv; tmapping->keyval = keyval; tmapping->state = state; From b1c366a143eb1db68b46cb235e43761dfbb93745 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:59:50 +0000 Subject: [PATCH 040/147] Add Text::get_chars() implementation and tests The clutter_text_get_chars() function returns a section of the contents of the Text actor, delimited by a start and an end position. This commit adds the implementation for that function and a test unit that guarantees the offset-to-bytes computations are correct. --- clutter/clutter-text.c | 35 ++++++++++++++++++++++++++++++- tests/conform/test-clutter-text.c | 31 +++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 7 +------ 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index db9cf469a..678defbca 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2998,14 +2998,47 @@ clutter_text_delete_chars (ClutterText *self, g_object_notify (G_OBJECT (self), "text"); } +/** + * clutter_text_get_chars: + * @self: a #ClutterTex + * @start_pos: start of text, in characters + * @end_pos: end of text, in characters + * + * Retrieves the contents of the #ClutterText actor between + * @start_pos and @end_pos. + * + * The positions are specified in characters, not in bytes. + * + * Return value: a newly allocated string with the contents of + * the text actor between the specified positions. Use g_free() + * to free the resources when done + * + * Since: 1.0 + */ gchar * clutter_text_get_chars (ClutterText *self, gssize start_pos, gssize end_pos) { + ClutterTextPrivate *priv; + gint start_index, end_index; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); - return NULL; + priv = self->priv; + + if (end_pos < 0) + end_pos = priv->n_chars; + + start_pos = MIN (priv->n_chars, start_pos); + end_pos = MIN (priv->n_chars, end_pos); + + start_index = g_utf8_offset_to_pointer (priv->text, start_pos) + - priv->text; + end_index = g_utf8_offset_to_pointer (priv->text, end_pos) + - priv->text; + + return g_strndup (priv->text + start_index, end_index - start_index); } void diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c index e6662182a..b47002a40 100644 --- a/tests/conform/test-clutter-text.c +++ b/tests/conform/test-clutter-text.c @@ -238,6 +238,37 @@ test_text_delete_chars (TestConformSimpleFixture *fixture, clutter_actor_destroy (CLUTTER_ACTOR (text)); } +void +test_text_get_chars (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + gchar *chars; + + clutter_text_set_text (text, "00abcdef11"); + g_assert_cmpint (get_nchars (text), ==, 10); + g_assert_cmpint (get_nbytes (text), ==, 10); + g_assert_cmpstr (clutter_text_get_text (text), ==, "00abcdef11"); + + chars = clutter_text_get_chars (text, 2, -1); + g_assert_cmpstr (chars, ==, "abcdef11"); + g_free (chars); + + chars = clutter_text_get_chars (text, 0, 8); + g_assert_cmpstr (chars, ==, "00abcdef"); + g_free (chars); + + chars = clutter_text_get_chars (text, 2, 8); + g_assert_cmpstr (chars, ==, "abcdef"); + g_free (chars); + + chars = clutter_text_get_chars (text, 8, 12); + g_assert_cmpstr (chars, ==, "11"); + g_free (chars); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + void test_text_delete_text (TestConformSimpleFixture *fixture, gconstpointer data) diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 9fb08c58d..d12c4b83a 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -75,14 +75,11 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/entry", test_entry_empty); TEST_CONFORM_SIMPLE ("/entry", test_entry_set_empty); TEST_CONFORM_SIMPLE ("/entry", test_entry_set_text); - TEST_CONFORM_SIMPLE ("/entry", test_entry_append_some); TEST_CONFORM_SIMPLE ("/entry", test_entry_prepend_some); TEST_CONFORM_SIMPLE ("/entry", test_entry_insert); - TEST_CONFORM_SIMPLE ("/entry", test_entry_delete_chars); TEST_CONFORM_SIMPLE ("/entry", test_entry_delete_text); - TEST_CONFORM_SIMPLE ("/entry", test_entry_cursor); TEST_CONFORM_SIMPLE ("/entry", test_entry_event); @@ -91,16 +88,14 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/text", test_text_empty); TEST_CONFORM_SIMPLE ("/text", test_text_set_empty); TEST_CONFORM_SIMPLE ("/text", test_text_set_text); - TEST_CONFORM_SIMPLE ("/text", test_text_append_some); TEST_CONFORM_SIMPLE ("/text", test_text_prepend_some); TEST_CONFORM_SIMPLE ("/text", test_text_insert); - TEST_CONFORM_SIMPLE ("/text", test_text_delete_chars); TEST_CONFORM_SIMPLE ("/text", test_text_delete_text); - TEST_CONFORM_SIMPLE ("/text", test_text_cursor); TEST_CONFORM_SIMPLE ("/text", test_text_event); + TEST_CONFORM_SIMPLE ("/text", test_text_get_chars); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_size); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_color); From eae98800d2fd45cacbab9f82fb9345e7f23ea517 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:03:17 +0000 Subject: [PATCH 041/147] Use ClutterBindPool inside ClutterText ClutterText should use the newly added ClutterBindingPool API to handle key events, instead of its homegrown code. This commit removes the action/mapping code and defers the entire key binding matching to a ClutterBindingPool created inside the Text class initialization function. --- clutter/clutter-text.c | 971 +++++++++++++++++++---------------------- clutter/clutter-text.h | 20 - 2 files changed, 443 insertions(+), 548 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 678defbca..899c9a9cf 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -33,11 +33,12 @@ #include "clutter-text.h" +#include "clutter-binding-pool.h" +#include "clutter-debug.h" +#include "clutter-enum-types.h" #include "clutter-keysyms.h" #include "clutter-main.h" -#include "clutter-enum-types.h" #include "clutter-private.h" -#include "clutter-debug.h" #include "clutter-units.h" #include "cogl-pango.h" @@ -84,21 +85,6 @@ struct _LayoutCache guint age; }; -struct _TextCommand -{ - const gchar *name; - gboolean (*func) (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event); -}; - -struct _TextMapping -{ - ClutterModifierType state; - guint keyval; - const gchar *action; -}; - struct _ClutterTextPrivate { PangoFontDescription *font_desc; @@ -151,12 +137,6 @@ struct _ClutterTextPrivate ClutterGeometry cursor_pos; ClutterColor cursor_color; - GList *mappings; - GList *commands; /* each instance has it's own set of commands - so that actor specific actions can be added - to single actor classes - */ - gint max_length; gunichar priv_char; @@ -243,45 +223,12 @@ offset_to_bytes (const gchar *text, (g_utf8_pointer_to_offset ((t), (t) + (p))) -void -clutter_text_mappings_clear (ClutterText *self) +static inline void +clutter_text_clear_selection (ClutterText *self) { ClutterTextPrivate *priv = self->priv; - g_list_foreach (priv->mappings, (GFunc) g_free, NULL); - g_list_free (priv->mappings); - priv->mappings = NULL; -} - -static void init_commands (ClutterText *ttext); - -static void -init_mappings (ClutterText *ttext) -{ - ClutterTextPrivate *priv = ttext->priv; - - if (priv->mappings) - return; - - clutter_text_add_mapping (ttext, CLUTTER_Left, 0, "move-left"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Left, 0, "move-left"); - clutter_text_add_mapping (ttext, CLUTTER_Right, 0, "move-right"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Right, 0,"move-right"); - clutter_text_add_mapping (ttext, CLUTTER_Up, 0, "move-up"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Up, 0, "move-up"); - clutter_text_add_mapping (ttext, CLUTTER_Down, 0, "move-down"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Down, 0, "move-down"); - clutter_text_add_mapping (ttext, CLUTTER_Begin, 0, "move-start-line"); - clutter_text_add_mapping (ttext, CLUTTER_Home, 0, "move-start-line"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Home, 0, "move-start-line"); - clutter_text_add_mapping (ttext, CLUTTER_End, 0, "move-end-line"); - clutter_text_add_mapping (ttext, CLUTTER_KP_End, 0, "move-end-line"); - clutter_text_add_mapping (ttext, CLUTTER_BackSpace, 0 , "delete-previous"); - clutter_text_add_mapping (ttext, CLUTTER_Delete, 0, "delete-next"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Delete,0, "delete-next"); - clutter_text_add_mapping (ttext, CLUTTER_Return, 0, "activate"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Enter, 0, "activate"); - clutter_text_add_mapping (ttext, CLUTTER_ISO_Enter, 0, "activate"); + priv->selection_bound = priv->position; } static PangoLayout * @@ -548,7 +495,7 @@ clutter_text_ensure_cursor_position (ClutterText *self) } static gboolean -clutter_text_truncate_selection_internal (ClutterText *self) +clutter_text_truncate_selection (ClutterText *self) { ClutterTextPrivate *priv = self->priv; gint start_index; @@ -578,14 +525,6 @@ clutter_text_truncate_selection_internal (ClutterText *self) return TRUE; } -static gboolean -clutter_text_truncate_selection (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - return clutter_text_truncate_selection_internal (ttext); -} - static void clutter_text_set_property (GObject *gobject, guint prop_id, @@ -775,11 +714,6 @@ clutter_text_finalize (GObject *gobject) g_free (priv->text); g_free (priv->font_name); - clutter_text_mappings_clear (self); - - g_list_foreach (priv->commands, (GFunc) g_free, NULL); - g_list_free (priv->commands); - G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject); } @@ -877,8 +811,6 @@ cursor_paint (ClutterText *self) } } - - static gboolean clutter_text_button_press (ClutterActor *actor, ClutterButtonEvent *bev) @@ -910,7 +842,6 @@ clutter_text_button_press (ClutterActor *actor, return TRUE; } - static gboolean clutter_text_motion (ClutterActor *actor, ClutterMotionEvent *mev) @@ -965,37 +896,29 @@ clutter_text_button_release (ClutterActor *actor, static gboolean clutter_text_key_press (ClutterActor *actor, - ClutterKeyEvent *kev) + ClutterKeyEvent *event) { - ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; - gint keyval = clutter_key_event_symbol (kev); - GList *iter; + ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; + ClutterBindingPool *pool; + gboolean res; + gint keyval; if (!priv->editable) return FALSE; - for (iter = priv->mappings; iter != NULL; iter = iter->next) - { - TextMapping *mapping = iter->data; + keyval = clutter_key_event_symbol (event); - if ( - (mapping->keyval == keyval) && - ( - (mapping->state == 0) || - (mapping->state && (kev->modifier_state & mapping->state)) - ) - ) - { - if (!g_str_equal (mapping->action, "activate") || - priv->activatable) - return clutter_text_action (CLUTTER_TEXT (actor), - mapping->action, - (ClutterEvent *) kev); - } - } + pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor)); + g_assert (pool != NULL); + res = clutter_binding_pool_activate (pool, keyval, + event->modifier_state, + G_OBJECT (actor)); + if (res) + return TRUE; + else { - gunichar key_unichar = clutter_key_event_unicode (kev); + gunichar key_unichar = clutter_key_event_unicode (event); if (key_unichar == '\r') /* return is reported as CR we want LF */ key_unichar = '\n'; @@ -1003,6 +926,7 @@ clutter_text_key_press (ClutterActor *actor, if (g_unichar_validate (key_unichar)) { clutter_text_insert_unichar (CLUTTER_TEXT (actor), key_unichar); + return TRUE; } } @@ -1126,11 +1050,340 @@ clutter_text_allocate (ClutterActor *self, parent_class->allocate (self, box, origin_changed); } +static gboolean +clutter_text_real_move_left (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos = priv->position; + gint len; + + len = g_utf8_strlen (priv->text, -1); + + if (pos != 0 && len !=0) + { + if (pos == -1) + clutter_text_set_cursor_position (self, len - 1); + else + clutter_text_set_cursor_position (self, pos - 1); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_move_right (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos = priv->position; + gint len; + + len = g_utf8_strlen (priv->text, -1); + + if (pos != -1 && len !=0) + { + if (pos != len) + clutter_text_set_cursor_position (self, pos + 1); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_move_up (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint x; + + layout = clutter_text_get_layout (self); + + pango_layout_index_to_line_x (layout, + offset_to_bytes (priv->text, priv->position), + 0, + &line_no, &x); + + if (priv->x_pos != -1) + x = priv->x_pos; + else + priv->x_pos = x; + + line_no -= 1; + if (line_no < 0) + return FALSE; + + layout_line = pango_layout_get_line_readonly (layout, line_no); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, x, &index_, NULL); + + { + gint pos = bytes_to_offset (priv->text, index_); + + clutter_text_set_cursor_position (self, pos); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_move_down (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint x; + + layout = clutter_text_get_layout (self); + + pango_layout_index_to_line_x (layout, + offset_to_bytes (priv->text, priv->position), + 0, + &line_no, &x); + + if (priv->x_pos != -1) + x = priv->x_pos; + else + priv->x_pos = x; + + layout_line = pango_layout_get_line_readonly (layout, line_no + 1); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, x, &index_, NULL); + + { + gint pos = bytes_to_offset (priv->text, index_); + + clutter_text_set_cursor_position (self, pos); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_line_start (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint position; + + layout = clutter_text_get_layout (self); + + pango_layout_index_to_line_x (layout, + offset_to_bytes (priv->text, priv->position), + 0, + &line_no, NULL); + + layout_line = pango_layout_get_line_readonly (layout, line_no); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); + + position = bytes_to_offset (priv->text, index_); + clutter_text_set_cursor_position (self, position); + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_line_end (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint trailing; + gint position; + + layout = clutter_text_get_layout (self); + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, + 0, + &line_no, NULL); + + layout_line = pango_layout_get_line_readonly (layout, line_no); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); + index_ += trailing; + + position = bytes_to_offset (priv->text, index_); + + clutter_text_set_cursor_position (self, position); + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_del_next (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (self)) + return FALSE; + + pos = priv->position; + len = g_utf8_strlen (priv->text, -1); + + if (len && pos != -1 && pos < len) + { + clutter_text_delete_text (self, pos, pos + 1); + + return TRUE; + } + + return FALSE; +} + +static gboolean +clutter_text_real_del_prev (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (self)) + return FALSE; + + pos = priv->position; + len = g_utf8_strlen (priv->text, -1); + + if (pos != 0 && len != 0) + { + if (pos == -1) + { + clutter_text_set_cursor_position (self, len - 1); + clutter_text_set_selection_bound (self, len - 1); + } + else + { + clutter_text_set_cursor_position (self, pos - 1); + clutter_text_set_selection_bound (self, pos - 1); + } + + clutter_text_delete_text (self, pos - 1, pos); + + return TRUE; + } + + return FALSE; +} + +static gboolean +clutter_text_real_activate (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + + if (priv->activatable) + { + g_signal_emit (self, text_signals[ACTIVATE], 0); + + return TRUE; + } + + return FALSE; +} + +static gboolean +clutter_text_real_page_up (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + return FALSE; +} + +static gboolean +clutter_text_real_page_down (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + return FALSE; +} + +static void +clutter_text_add_move_binding (ClutterBindingPool *pool, + const gchar *action, + guint key_val, + GCallback callback) +{ + clutter_binding_pool_install_action (pool, action, + key_val, 0, + callback, + NULL, NULL); + clutter_binding_pool_install_action (pool, action, + key_val, CLUTTER_SHIFT_MASK, + callback, + NULL, NULL); +} + static void clutter_text_class_init (ClutterTextClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + ClutterBindingPool *binding_pool; GParamSpec *pspec; _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); @@ -1437,6 +1690,97 @@ clutter_text_class_init (ClutterTextClass *klass) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + binding_pool = clutter_binding_pool_get_for_class (klass); + + clutter_text_add_move_binding (binding_pool, "move-left", + CLUTTER_Left, + G_CALLBACK (clutter_text_real_move_left)); + clutter_text_add_move_binding (binding_pool, "move-left", + CLUTTER_KP_Left, + G_CALLBACK (clutter_text_real_move_left)); + clutter_text_add_move_binding (binding_pool, "move-right", + CLUTTER_Right, + G_CALLBACK (clutter_text_real_move_right)); + clutter_text_add_move_binding (binding_pool, "move-right", + CLUTTER_KP_Right, + G_CALLBACK (clutter_text_real_move_right)); + clutter_text_add_move_binding (binding_pool, "move-up", + CLUTTER_Up, + G_CALLBACK (clutter_text_real_move_up)); + clutter_text_add_move_binding (binding_pool, "move-up", + CLUTTER_KP_Up, + G_CALLBACK (clutter_text_real_move_up)); + clutter_text_add_move_binding (binding_pool, "move-down", + CLUTTER_Down, + G_CALLBACK (clutter_text_real_move_down)); + clutter_text_add_move_binding (binding_pool, "move-down", + CLUTTER_KP_Down, + G_CALLBACK (clutter_text_real_move_down)); + + clutter_binding_pool_install_action (binding_pool, "line-start", + CLUTTER_Home, 0, + G_CALLBACK (clutter_text_real_line_start), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "line-start", + CLUTTER_KP_Home, 0, + G_CALLBACK (clutter_text_real_line_start), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "line-start", + CLUTTER_Begin, 0, + G_CALLBACK (clutter_text_real_line_start), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "line-end", + CLUTTER_End, 0, + G_CALLBACK (clutter_text_real_line_end), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "line-end", + CLUTTER_KP_End, 0, + G_CALLBACK (clutter_text_real_line_end), + NULL, NULL); + + clutter_binding_pool_install_action (binding_pool, "page-up", + CLUTTER_Page_Up, 0, + G_CALLBACK (clutter_text_real_page_up), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "page-up", + CLUTTER_KP_Page_Up, 0, + G_CALLBACK (clutter_text_real_page_up), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "page-down", + CLUTTER_Page_Down, 0, + G_CALLBACK (clutter_text_real_page_down), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "page-up", + CLUTTER_KP_Page_Down, 0, + G_CALLBACK (clutter_text_real_page_down), + NULL, NULL); + + clutter_binding_pool_install_action (binding_pool, "delete-next", + CLUTTER_Delete, 0, + G_CALLBACK (clutter_text_real_del_next), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "delete-next", + CLUTTER_KP_Delete, 0, + G_CALLBACK (clutter_text_real_del_next), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "delete-prev", + CLUTTER_BackSpace, 0, + G_CALLBACK (clutter_text_real_del_prev), + NULL, NULL); + + clutter_binding_pool_install_action (binding_pool, "activate", + CLUTTER_Return, 0, + G_CALLBACK (clutter_text_real_activate), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "activate", + CLUTTER_KP_Enter, 0, + G_CALLBACK (clutter_text_real_activate), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "activate", + CLUTTER_ISO_Enter, 0, + G_CALLBACK (clutter_text_real_activate), + NULL, NULL); } static void @@ -1480,9 +1824,6 @@ clutter_text_init (ClutterText *self) priv->priv_char = '*'; priv->max_length = 0; - - init_commands (self); /* FIXME: free */ - init_mappings (self); /* FIXME: free */ } ClutterActor * @@ -1735,403 +2076,6 @@ clutter_text_get_selection_bound (ClutterText *self) return self->priv->selection_bound; } -/****************************************************************/ -/* The following are the commands available for keybinding when */ -/* using the entry, these can also be invoked programmatically */ -/* through clutter_text_action() */ -/****************************************************************/ - -static gboolean -clutter_text_action_activate (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - g_signal_emit (G_OBJECT (ttext), text_signals[ACTIVATE], 0); - return TRUE; -} - -static void -clutter_text_clear_selection (ClutterText *ttext) -{ - ClutterTextPrivate *priv = ttext->priv; - - priv->selection_bound = priv->position; -} - -static gboolean -clutter_text_action_move_left (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint pos = priv->position; - gint len; - len = g_utf8_strlen (clutter_text_get_text (ttext), -1); - - if (pos != 0 && len !=0) - { - if (pos == -1) - { - clutter_text_set_cursor_position (ttext, len - 1); - } - else - { - clutter_text_set_cursor_position (ttext, pos - 1); - } - } - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - - -static gboolean -clutter_text_action_move_right (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint pos; - - gint len; - len = g_utf8_strlen (clutter_text_get_text (ttext), -1); - - pos = priv->position; - if (pos != -1 && len !=0) - { - if (pos != len) - { - clutter_text_set_cursor_position (ttext, pos + 1); - } - } - - if (!(priv->selectable && - event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - { - clutter_text_clear_selection (ttext); - } - - return TRUE; -} - -static gboolean -clutter_text_action_move_up (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint line_no; - gint index_; - gint x; - const gchar *text; - PangoLayoutLine *layout_line; - - text = clutter_text_get_text (ttext); - - pango_layout_index_to_line_x (clutter_text_get_layout (ttext), - offset_to_bytes (text, priv->position), - 0, - &line_no, &x); - - if (priv->x_pos != -1) - x = priv->x_pos; - else - priv->x_pos = x; - - line_no -= 1; - if (line_no < 0) - return FALSE; - - layout_line = - pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); - - if (!layout_line) - return TRUE; - - pango_layout_line_x_to_index (layout_line, x, &index_, NULL); - - { - gint pos = bytes_to_offset (text, index_); - clutter_text_set_cursor_position (ttext, pos); - } - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - -static gboolean -clutter_text_action_move_down (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint line_no; - gint index_; - gint x; - const gchar *text; - PangoLayoutLine *layout_line; - - text = clutter_text_get_text (ttext); - - pango_layout_index_to_line_x ( - clutter_text_get_layout (ttext), - offset_to_bytes (text, priv->position), - 0, - &line_no, - &x); - - if (priv->x_pos != -1) - x = priv->x_pos; - else - priv->x_pos = x; - - layout_line = - pango_layout_get_line_readonly (clutter_text_get_layout (ttext), - line_no + 1); - - if (!layout_line) - return FALSE; - - pango_layout_line_x_to_index (layout_line, x, &index_, NULL); - - { - gint pos = bytes_to_offset (text, index_); - - clutter_text_set_cursor_position (ttext, pos); - } - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - return TRUE; -} - -static gboolean -clutter_text_action_move_start (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - - clutter_text_set_cursor_position (ttext, 0); - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - -static gboolean -clutter_text_action_move_end (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - - clutter_text_set_cursor_position (ttext, -1); - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - -static gboolean -clutter_text_action_move_start_line (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint line_no; - gint index_; - const gchar *text; - PangoLayoutLine *layout_line; - gint position; - - text = clutter_text_get_text (ttext); - - - pango_layout_index_to_line_x ( - clutter_text_get_layout (ttext), - offset_to_bytes (text, priv->position), - 0, - &line_no, - NULL); - - layout_line = - pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); - - pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); - - position = bytes_to_offset (text, index_); - clutter_text_set_cursor_position (ttext, position); - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - -static gboolean -clutter_text_action_move_end_line (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint line_no; - gint index_; - gint trailing; - const gchar *text; - PangoLayoutLine *layout_line; - gint position; - - text = clutter_text_get_text (ttext); - - index_ = offset_to_bytes (text, priv->position); - - pango_layout_index_to_line_x ( - clutter_text_get_layout (ttext), - index_, - 0, - &line_no, - NULL); - - layout_line = - pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); - - pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); - index_ += trailing; - - position = bytes_to_offset (text, index_); - - clutter_text_set_cursor_position (ttext, position); - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - -static gboolean -clutter_text_action_delete_next (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv; - gint pos; - gint len; - - if (clutter_text_truncate_selection_internal (ttext)) - return TRUE; - - priv = ttext->priv; - pos = priv->position; - len = g_utf8_strlen (clutter_text_get_text (ttext), -1); - - if (len && pos != -1 && pos < len) - clutter_text_delete_text (ttext, pos, pos+1); - - return TRUE; -} - -static gboolean -clutter_text_action_delete_previous (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv; - gint pos; - gint len; - - if (clutter_text_truncate_selection_internal (ttext)) - return TRUE; - - priv = ttext->priv; - pos = priv->position; - len = g_utf8_strlen (clutter_text_get_text (ttext), -1); - - if (pos != 0 && len !=0) - { - if (pos == -1) - { - clutter_text_set_cursor_position (ttext, len - 1); - clutter_text_set_selection_bound (ttext, len - 1); - } - else - { - clutter_text_set_cursor_position (ttext, pos - 1); - clutter_text_set_selection_bound (ttext, pos - 1); - } - clutter_text_delete_text (ttext, pos-1, pos);; - } - return TRUE; -} - -static void -init_commands (ClutterText *ttext) -{ - ClutterTextPrivate *priv = ttext->priv; - if (priv->commands) - return; - - clutter_text_add_action (ttext, "move-left", clutter_text_action_move_left); - clutter_text_add_action (ttext, "move-right", clutter_text_action_move_right); - clutter_text_add_action (ttext, "move-up", clutter_text_action_move_up); - clutter_text_add_action (ttext, "move-down", clutter_text_action_move_down); - clutter_text_add_action (ttext, "move-start", clutter_text_action_move_start); - clutter_text_add_action (ttext, "move-end", clutter_text_action_move_end); - clutter_text_add_action (ttext, "move-start-line", clutter_text_action_move_start_line); - clutter_text_add_action (ttext, "move-end-line", clutter_text_action_move_end_line); - clutter_text_add_action (ttext, "delete-previous", clutter_text_action_delete_previous); - clutter_text_add_action (ttext, "delete-next", clutter_text_action_delete_next); - clutter_text_add_action (ttext, "activate", clutter_text_action_activate); - clutter_text_add_action (ttext, "truncate-selection", clutter_text_truncate_selection); -} - -gboolean -clutter_text_action (ClutterText *ttext, - const gchar *command, - ClutterEvent *event) -{ - gchar command2[64]; - gint i; - - GList *iter; - ClutterTextPrivate *priv = ttext->priv; - - for (i=0; command[i] && - command[i]!=' '&& - i<62; i++) - { - command2[i]=command[i]; - } - command2[i]='\0'; - - if (!g_str_equal (command2, "move-up") && - !g_str_equal (command2, "move-down")) - priv->x_pos = -1; - - for (iter=priv->commands;iter;iter=iter->next) - { - TextCommand *tcommand = iter->data; - if (g_str_equal (command2, tcommand->name)) - return tcommand->func (ttext, command, event); - } - - g_warning ("unhandled text command %s", command); - return FALSE; -} - G_CONST_RETURN gchar * clutter_text_get_font_name (ClutterText *text) { @@ -3040,32 +2984,3 @@ clutter_text_get_chars (ClutterText *self, return g_strndup (priv->text + start_index, end_index - start_index); } - -void -clutter_text_add_mapping (ClutterText *ttext, - guint keyval, - ClutterModifierType state, - const gchar *commandline) -{ - TextMapping *tmapping = g_new (TextMapping, 1); - ClutterTextPrivate *priv = ttext->priv; - tmapping->keyval = keyval; - tmapping->state = state; - tmapping->action = commandline; - priv->mappings = g_list_append (priv->mappings, tmapping); -} - -void -clutter_text_add_action (ClutterText *ttext, - const gchar *name, - gboolean (*func) (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event)) -{ - TextCommand *tcommand = g_new (TextCommand, 1); - ClutterTextPrivate *priv = ttext->priv; - tcommand->name = name; - tcommand->func = func; - priv->commands = g_list_append (priv->commands, tcommand); -} - diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 491e3a3ff..87f103486 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -167,26 +167,6 @@ void clutter_text_set_max_length (ClutterText *self gint max); gint clutter_text_get_max_length (ClutterText *self); -/* add a custom action that can be used in keybindings */ -void clutter_text_add_action (ClutterText *ttext, - const gchar *name, - gboolean (*func) (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event)); - -/* invoke an action registered by you or one of the tidy text default actions */ -gboolean clutter_text_action (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event); - -void clutter_text_mappings_clear (ClutterText *ttext); - -/* Add a keybinding to handle for the default keypress vfunc handler */ -void clutter_text_add_mapping (ClutterText *ttext, - guint keyval, - ClutterModifierType state, - const gchar *commandline); - G_END_DECLS #endif /* __CLUTTER_TEXT_H__ */ From ec4c15f8e71faee23b9b92cd49cb2a011750775b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:07:20 +0000 Subject: [PATCH 042/147] Comment out the mapping API The test-text interactive test for ClutterText should not use the mapping API, since ClutterText does not have it anymore. --- tests/interactive/test-text.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/interactive/test-text.c b/tests/interactive/test-text.c index ce262dcfc..30231b3b5 100644 --- a/tests/interactive/test-text.c +++ b/tests/interactive/test-text.c @@ -135,6 +135,7 @@ test_text_main (gint argc, clutter_text_set_selectable (CLUTTER_TEXT (text), TRUE); clutter_text_set_cursor_color (CLUTTER_TEXT (text), &cursor_color); +#if 0 clutter_text_add_action (CLUTTER_TEXT (text), "select-all", select_all); clutter_text_add_action (CLUTTER_TEXT (text), "copy", copy); clutter_text_add_action (CLUTTER_TEXT (text), "paste", paste); @@ -154,6 +155,7 @@ test_text_main (gint argc, CLUTTER_Page_Up, 0, "pageup"); clutter_text_add_mapping (CLUTTER_TEXT (text), CLUTTER_Page_Down, 0, "pagedown"); +#endif if (argv[1]) { From 35172a7615afa88eea20eee83634e8a07d701d5c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:08:20 +0000 Subject: [PATCH 043/147] Change the binding propagation when truncating a selection If a selection has been truncated inside a key binding handler, we should just return and let the usual key event handler continue. This fixes the case where we deleted a selection using the Delete or the Backspace keys. --- clutter/clutter-text.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 899c9a9cf..baec33d76 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1274,7 +1274,7 @@ clutter_text_real_del_next (ClutterText *self, gint len; if (clutter_text_truncate_selection (self)) - return FALSE; + return TRUE; pos = priv->position; len = g_utf8_strlen (priv->text, -1); @@ -1300,7 +1300,7 @@ clutter_text_real_del_prev (ClutterText *self, gint len; if (clutter_text_truncate_selection (self)) - return FALSE; + return TRUE; pos = priv->position; len = g_utf8_strlen (priv->text, -1); From f13e00b41189fafb2eebd3df214e19db9fb107b7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:10:46 +0000 Subject: [PATCH 044/147] Truncate selections on text insertion When inserting text on a key press event we should also truncate the selection. We should not truncate the selection when inserting any Unicode character, since changing the selection also changes the cursor position - and one of the invariants we inherited from ClutterEntry is that inserting characters programmatically does not change the cursor position. --- clutter/clutter-text.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index baec33d76..284c33d5d 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -898,7 +898,8 @@ static gboolean clutter_text_key_press (ClutterActor *actor, ClutterKeyEvent *event) { - ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; + ClutterText *self = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = self->priv; ClutterBindingPool *pool; gboolean res; gint keyval; @@ -925,7 +926,8 @@ clutter_text_key_press (ClutterActor *actor, if (g_unichar_validate (key_unichar)) { - clutter_text_insert_unichar (CLUTTER_TEXT (actor), key_unichar); + clutter_text_truncate_selection (self); + clutter_text_insert_unichar (self, key_unichar); return TRUE; } From 06b00f9dfcaa35acf01723fce0e5e224acdadba7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:13:26 +0000 Subject: [PATCH 045/147] Remove units for Entry and Label ClutterText should supercede all unit tests for ClutterLabel and ClutterEntry. --- tests/conform/Makefile.am | 5 +- tests/conform/test-clutter-entry.c | 370 ------------------ tests/conform/test-conform-main.c | 16 +- .../{test-label-cache.c => test-text-cache.c} | 42 +- 4 files changed, 24 insertions(+), 409 deletions(-) delete mode 100644 tests/conform/test-clutter-entry.c rename tests/conform/{test-label-cache.c => test-text-cache.c} (84%) diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 774156cfc..0586daf4c 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -15,15 +15,14 @@ test_conformance_SOURCES = \ test-mesh-mutability.c \ test-path.c \ test-pick.c \ - test-label-cache.c \ - test-clutter-entry.c \ test-clutter-rectangle.c \ test-clutter-fixed.c \ test-actor-invariants.c \ test-paint-opacity.c \ test-backface-culling.c \ test-binding-pool.c \ - test-clutter-text.c + test-clutter-text.c \ + test-text-cache.c # For convenience, this provides a way to easily run individual unit tests: .PHONY: wrappers diff --git a/tests/conform/test-clutter-entry.c b/tests/conform/test-clutter-entry.c deleted file mode 100644 index a12d46b75..000000000 --- a/tests/conform/test-clutter-entry.c +++ /dev/null @@ -1,370 +0,0 @@ -#include -#include -#include - -#include "test-conform-common.h" - -typedef struct { - gunichar unichar; - const char bytes[6]; - gint nbytes; -} TestData; - -static const TestData -test_data[] = { - { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ - { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ -}; - -void -test_entry_utf8_validation (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - gunichar unichar; - char bytes[6]; - int nbytes; - - g_assert (g_unichar_validate (t->unichar)); - - nbytes = g_unichar_to_utf8 (t->unichar, bytes); - bytes[nbytes] = '\0'; - g_assert (nbytes == t->nbytes); - g_assert (memcmp (t->bytes, bytes, nbytes) == 0); - - unichar = g_utf8_get_char_validated (bytes, nbytes); - g_assert (unichar == t->unichar); - } -} - -static int -get_nbytes (ClutterEntry *entry) -{ - const char *s = clutter_entry_get_text (entry); - return strlen (s); -} - -static int -get_nchars (ClutterEntry *entry) -{ - const char *s = clutter_entry_get_text (entry); - g_assert (g_utf8_validate (s, -1, NULL)); - return g_utf8_strlen (s, -1); -} - -#define DONT_MOVE_CURSOR (-2) - -static void -insert_unichar (ClutterEntry *entry, gunichar unichar, int position) -{ - if (position > DONT_MOVE_CURSOR) - { - clutter_entry_set_cursor_position (entry, position); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, position); - } - - clutter_entry_insert_unichar (entry, unichar); -} - -void -test_entry_empty (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - - g_assert (clutter_entry_get_text (entry) == NULL); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_set_empty (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - - /* annoyingly slightly different from initially empty */ - clutter_entry_set_text (entry, ""); - g_assert_cmpint (get_nchars (entry), ==, 0); - g_assert_cmpint (get_nbytes (entry), ==, 0); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_set_text (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - - clutter_entry_set_text (entry, "abcdef"); - g_assert_cmpint (get_nchars (entry), ==, 6); - g_assert_cmpint (get_nbytes (entry), ==, 6); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_entry_set_cursor_position (entry, 5); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 5); - - clutter_entry_set_text (entry, ""); - /* FIXME: cursor position should be -1? - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - */ - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_append_some (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 1; j <= 4; j++) - { - insert_unichar (entry, t->unichar, DONT_MOVE_CURSOR); - g_assert_cmpint (get_nchars (entry), ==, j); - g_assert_cmpint (get_nbytes (entry), ==, j * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - } - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_prepend_some (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - clutter_entry_insert_unichar (entry, t->unichar); - g_assert_cmpint (get_nchars (entry), ==, 1); - g_assert_cmpint (get_nbytes (entry), ==, 1 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - for (j = 2; j <= 4; j++) - { - insert_unichar (entry, t->unichar, 0); - g_assert_cmpint (get_nchars (entry), ==, j); - g_assert_cmpint (get_nbytes (entry), ==, j * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - } - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_insert (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - - clutter_entry_insert_unichar (entry, t->unichar); - clutter_entry_insert_unichar (entry, t->unichar); - - insert_unichar (entry, t->unichar, 1); - g_assert_cmpint (get_nchars (entry), ==, 3); - g_assert_cmpint (get_nbytes (entry), ==, 3 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 2); - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_delete_chars (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 0; j < 4; j++) - clutter_entry_insert_unichar (entry, t->unichar); - - clutter_entry_set_cursor_position (entry, 2); - clutter_entry_delete_chars (entry, 1); - g_assert_cmpint (get_nchars (entry), ==, 3); - g_assert_cmpint (get_nbytes (entry), ==, 3 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - - clutter_entry_set_cursor_position (entry, 2); - clutter_entry_delete_chars (entry, 1); - g_assert_cmpint (get_nchars (entry), ==, 2); - g_assert_cmpint (get_nbytes (entry), ==, 2 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_delete_text (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 0; j < 4; j++) - clutter_entry_insert_unichar (entry, t->unichar); - - clutter_entry_set_cursor_position (entry, 3); - clutter_entry_delete_text (entry, 2, 4); - - g_assert_cmpint (get_nchars (entry), ==, 2); - g_assert_cmpint (get_nbytes (entry), ==, 2 * t->nbytes); - - /* FIXME: cursor position should be -1? - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - */ - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -static void -init_event (ClutterKeyEvent *event) -{ - event->type = CLUTTER_KEY_PRESS; - event->time = 0; /* not needed */ - event->flags = CLUTTER_EVENT_FLAG_SYNTHETIC; - event->stage = NULL; /* not needed */ - event->source = NULL; /* not needed */ - event->modifier_state = 0; - event->hardware_keycode = 0; /* not needed */ -} - -static void -send_keyval (ClutterEntry *entry, int keyval) -{ - ClutterKeyEvent event; - - init_event (&event); - event.keyval = keyval; - event.unicode_value = 0; /* should be ignored for cursor keys etc. */ - - clutter_entry_handle_key_event (entry, &event); -} - -static inline void -send_unichar (ClutterEntry *entry, gunichar unichar) -{ - ClutterKeyEvent event; - - init_event (&event); - event.keyval = 0; /* should be ignored for printable characters */ - event.unicode_value = unichar; - - clutter_entry_handle_key_event (entry, &event); -} - -void -test_entry_cursor (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 0; j < 4; ++j) - clutter_entry_insert_unichar (entry, t->unichar); - - clutter_entry_set_cursor_position (entry, 2); - - /* test cursor moves and is clamped */ - send_keyval (entry, CLUTTER_Left); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - - send_keyval (entry, CLUTTER_Left); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 0); - - send_keyval (entry, CLUTTER_Left); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 0); - - /* delete text containing the cursor */ - clutter_entry_set_cursor_position (entry, 3); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 3); - - clutter_entry_delete_text (entry, 2, 4); - send_keyval (entry, CLUTTER_Left); - - /* FIXME: cursor position should be -1? - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - */ - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_event (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - - send_unichar (entry, t->unichar); - - g_assert_cmpint (get_nchars (entry), ==, 1); - g_assert_cmpint (get_nbytes (entry), ==, 1 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index d12c4b83a..abf6280b2 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -68,21 +68,6 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/picking", test_pick); - TEST_CONFORM_SIMPLE ("/label", test_label_cache); - - /* ClutterEntry */ - TEST_CONFORM_SIMPLE ("/entry", test_entry_utf8_validation); - TEST_CONFORM_SIMPLE ("/entry", test_entry_empty); - TEST_CONFORM_SIMPLE ("/entry", test_entry_set_empty); - TEST_CONFORM_SIMPLE ("/entry", test_entry_set_text); - TEST_CONFORM_SIMPLE ("/entry", test_entry_append_some); - TEST_CONFORM_SIMPLE ("/entry", test_entry_prepend_some); - TEST_CONFORM_SIMPLE ("/entry", test_entry_insert); - TEST_CONFORM_SIMPLE ("/entry", test_entry_delete_chars); - TEST_CONFORM_SIMPLE ("/entry", test_entry_delete_text); - TEST_CONFORM_SIMPLE ("/entry", test_entry_cursor); - TEST_CONFORM_SIMPLE ("/entry", test_entry_event); - /* ClutterText */ TEST_CONFORM_SIMPLE ("/text", test_text_utf8_validation); TEST_CONFORM_SIMPLE ("/text", test_text_empty); @@ -96,6 +81,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/text", test_text_cursor); TEST_CONFORM_SIMPLE ("/text", test_text_event); TEST_CONFORM_SIMPLE ("/text", test_text_get_chars); + TEST_CONFORM_SIMPLE ("/text", test_text_cache); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_size); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_color); diff --git a/tests/conform/test-label-cache.c b/tests/conform/test-text-cache.c similarity index 84% rename from tests/conform/test-label-cache.c rename to tests/conform/test-text-cache.c index e9298f8fd..26687fc00 100644 --- a/tests/conform/test-label-cache.c +++ b/tests/conform/test-text-cache.c @@ -34,7 +34,7 @@ on_paint (ClutterActor *label, CallbackData *data) /* Check whether the layout used for this paint is different from the layout used for the last paint */ - new_layout = clutter_label_get_layout (CLUTTER_LABEL (data->label)); + new_layout = clutter_text_get_layout (CLUTTER_TEXT (data->label)); data->layout_changed = data->old_layout != new_layout; if (data->old_layout) @@ -119,12 +119,12 @@ do_tests (CallbackData *data) PangoAttribute *attr; /* TEST 1: change the text */ - clutter_label_set_text (CLUTTER_LABEL (data->label), "Counter 0"); + clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 0"); pango_layout_set_text (data->test_layout, "Counter 0", -1); check_result (data, "Change text", TRUE); /* TEST 2: change a single character */ - clutter_label_set_text (CLUTTER_LABEL (data->label), "Counter 1"); + clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 1"); pango_layout_set_text (data->test_layout, "Counter 1", -1); check_result (data, "Change a single character", TRUE); @@ -133,14 +133,14 @@ do_tests (CallbackData *data) check_result (data, "Move the label", FALSE); /* TEST 4: change the font */ - clutter_label_set_font_name (CLUTTER_LABEL (data->label), "Serif 15"); + clutter_text_set_font_name (CLUTTER_TEXT (data->label), "Serif 15"); fd = pango_font_description_from_string ("Serif 15"); pango_layout_set_font_description (data->test_layout, fd); pango_font_description_free (fd); check_result (data, "Change the font", TRUE); /* TEST 5: change the color */ - clutter_label_set_color (CLUTTER_LABEL (data->label), &red); + clutter_text_set_color (CLUTTER_TEXT (data->label), &red); check_result (data, "Change the color", FALSE); /* TEST 6: change the attributes */ @@ -150,21 +150,21 @@ do_tests (CallbackData *data) attr_list = pango_attr_list_new (); pango_attr_list_insert (attr_list, attr); attr_list_copy = pango_attr_list_copy (attr_list); - clutter_label_set_attributes (CLUTTER_LABEL (data->label), attr_list); + clutter_text_set_attributes (CLUTTER_TEXT (data->label), attr_list); pango_layout_set_attributes (data->test_layout, attr_list_copy); pango_attr_list_unref (attr_list_copy); pango_attr_list_unref (attr_list); check_result (data, "Change the attributes", TRUE); /* TEST 7: change the text again */ - clutter_label_set_attributes (CLUTTER_LABEL (data->label), NULL); - clutter_label_set_text (CLUTTER_LABEL (data->label), long_text); + clutter_text_set_attributes (CLUTTER_TEXT (data->label), NULL); + clutter_text_set_text (CLUTTER_TEXT (data->label), long_text); pango_layout_set_attributes (data->test_layout, NULL); pango_layout_set_text (data->test_layout, long_text, -1); check_result (data, "Change the text again", TRUE); /* TEST 8: enable markup */ - clutter_label_set_use_markup (CLUTTER_LABEL (data->label), TRUE); + clutter_text_set_use_markup (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_markup (data->test_layout, long_text, -1); check_result (data, "Enable markup", TRUE); @@ -178,28 +178,28 @@ do_tests (CallbackData *data) force_redraw (data); /* TEST 9: enable ellipsize */ - clutter_label_set_ellipsize (CLUTTER_LABEL (data->label), + clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_END); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_END); check_result (data, "Enable ellipsize", TRUE); - clutter_label_set_ellipsize (CLUTTER_LABEL (data->label), + clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_NONE); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_NONE); force_redraw (data); /* TEST 10: enable line wrap */ - clutter_label_set_line_wrap (CLUTTER_LABEL (data->label), TRUE); + clutter_text_set_line_wrap (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_WORD); check_result (data, "Enable line wrap", TRUE); /* TEST 11: change wrap mode */ - clutter_label_set_line_wrap_mode (CLUTTER_LABEL (data->label), + clutter_text_set_line_wrap_mode (CLUTTER_TEXT (data->label), PANGO_WRAP_CHAR); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_CHAR); check_result (data, "Change wrap mode", TRUE); /* TEST 12: enable justify */ - clutter_label_set_justify (CLUTTER_LABEL (data->label), TRUE); + clutter_text_set_justify (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_justify (data->test_layout, TRUE); /* Pango appears to have a bug which means that you can't change the justification after setting the text but this fixes it. @@ -208,7 +208,7 @@ do_tests (CallbackData *data) check_result (data, "Enable justify", TRUE); /* TEST 13: change alignment */ - clutter_label_set_alignment (CLUTTER_LABEL (data->label), PANGO_ALIGN_RIGHT); + clutter_text_set_alignment (CLUTTER_TEXT (data->label), PANGO_ALIGN_RIGHT); pango_layout_set_alignment (data->test_layout, PANGO_ALIGN_RIGHT); check_result (data, "Change alignment", TRUE); @@ -218,7 +218,7 @@ do_tests (CallbackData *data) } static PangoLayout * -make_layout_like_label (ClutterLabel *label) +make_layout_like_label (ClutterText *label) { PangoLayout *label_layout, *new_layout; PangoContext *context; @@ -226,7 +226,7 @@ make_layout_like_label (ClutterLabel *label) /* Make another layout using the same context as the layout from the label */ - label_layout = clutter_label_get_layout (label); + label_layout = clutter_text_get_layout (label); context = pango_layout_get_context (label_layout); new_layout = pango_layout_new (context); fd = pango_font_description_from_string (TEST_FONT); @@ -237,8 +237,8 @@ make_layout_like_label (ClutterLabel *label) } void -test_label_cache (TestConformSimpleFixture *fixture, - gconstpointer _data) +test_text_cache (TestConformSimpleFixture *fixture, + gconstpointer _data) { CallbackData data; @@ -246,9 +246,9 @@ test_label_cache (TestConformSimpleFixture *fixture, data.stage = clutter_stage_get_default (); - data.label = clutter_label_new_with_text (TEST_FONT, ""); + data.label = clutter_text_new_with_text (TEST_FONT, ""); - data.test_layout = make_layout_like_label (CLUTTER_LABEL (data.label)); + data.test_layout = make_layout_like_label (CLUTTER_TEXT (data.label)); g_signal_connect (data.label, "paint", G_CALLBACK (on_paint), &data); From 7a93bcb64eef54c1fae50129111a2d810ad4f622 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:14:53 +0000 Subject: [PATCH 046/147] Remove unused types Since ClutterText now uses ClutterBindingPool there is no more need for the internal key mapping types. --- clutter/clutter-text.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 284c33d5d..dcd6ec231 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -57,8 +57,6 @@ #define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) typedef struct _LayoutCache LayoutCache; -typedef struct _TextCommand TextCommand; -typedef struct _TextMapping TextMapping; /* Probably move into main */ static PangoContext *_context = NULL; From cfb60228313d16c3a373e8d39b6c05e310ab4ef4 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:15:49 +0000 Subject: [PATCH 047/147] Code style fixes Fix the arguments alignment whenever not consistent with the coding style rules. --- clutter/clutter-text.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index dcd6ec231..db5432896 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1327,10 +1327,10 @@ clutter_text_real_del_prev (ClutterText *self, } static gboolean -clutter_text_real_activate (ClutterText *self, - const gchar *action, - guint keyval, - ClutterModifierType modifiers) +clutter_text_real_activate (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; @@ -1345,19 +1345,19 @@ clutter_text_real_activate (ClutterText *self, } static gboolean -clutter_text_real_page_up (ClutterText *self, - const gchar *action, - guint keyval, - ClutterModifierType modifiers) +clutter_text_real_page_up (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) { return FALSE; } static gboolean -clutter_text_real_page_down (ClutterText *self, - const gchar *action, - guint keyval, - ClutterModifierType modifiers) +clutter_text_real_page_down (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) { return FALSE; } From e9c369f86d0eea327458cc4bd7b9bc750960a09f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:17:11 +0000 Subject: [PATCH 048/147] Add documentation annotations ClutterText is heavily under-documented. This commit ports most of the API documentation from ClutterLabel and ClutterEntry to ClutterText. --- clutter/clutter-text.c | 192 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index db5432896..886256844 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1648,7 +1648,7 @@ clutter_text_class_init (ClutterTextClass *klass) /** * ClutterText::text-changed: - * @actor: the actor which received the event + * @self: the #ClutterText that emitted the signal * * The ::text-changed signal is emitted after @actor's text changes * @@ -1663,6 +1663,15 @@ clutter_text_class_init (ClutterTextClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * ClutterText::cursor-event: + * @self: the #ClutterText that emitted the signal + * @geometry: the coordinates of the cursor + * + * FIXME + * + * Since: 1.0 + */ text_signals[CURSOR_EVENT] = g_signal_new ("cursor-event", G_TYPE_FROM_CLASS (gobject_class), @@ -1675,7 +1684,7 @@ clutter_text_class_init (ClutterTextClass *klass) /** * ClutterText::activate - * @actor: the actor which received the event + * @self: the #ClutterText that emitted the signal * * The ::activate signal is emitted each time the actor is 'activated' * by the user, normally by pressing the 'Enter' key. @@ -2076,6 +2085,18 @@ clutter_text_get_selection_bound (ClutterText *self) return self->priv->selection_bound; } +/** + * clutter_text_get_font_name: + * @self: a #ClutterText + * + * Retrieves the font name as set by clutter_text_set_font_name(). + * + * Return value: a string containing the font name. The returned + * string is owned by the #ClutterText actor and should not be + * modified or freed + * + * Since: 1.0 + */ G_CONST_RETURN gchar * clutter_text_get_font_name (ClutterText *text) { @@ -2084,6 +2105,23 @@ clutter_text_get_font_name (ClutterText *text) return text->priv->font_name; } +/** + * clutter_text_set_font_name: + * @self: a #ClutterText + * @font_name: a font name + * + * Sets the font used by a #ClutterText. The @font_name string + * must be something that can be parsed by the + * pango_font_description_from_string() function, like: + * + * |[ + * clutter_text_set_font_name (text, "Sans 10pt"); + * clutter_text_set_font_name (text, "Serif 16px"); + * clutter_text_set_font_name (text, "Helvetica 10"); + * ]| + * + * Since: 1.0 + */ void clutter_text_set_font_name (ClutterText *self, const gchar *font_name) @@ -2126,6 +2164,28 @@ clutter_text_set_font_name (ClutterText *self, g_object_notify (G_OBJECT (self), "font-name"); } +/** + * clutter_text_get_text: + * @self: a #ClutterText + * + * Retrieves a pointer to the current contents of a #ClutterText + * actor. + * + * If you need a copy of the contents for manipulating, either + * use g_strdup() on the returned string, or use: + * + * |[ + * copy = clutter_text_get_chars (text, 0, -1); + * ]| + * + * Which will return a newly allocated string. + * + * Return value: the contents of the actor. The returned string + * is owned by the #ClutterText actor and should never be + * modified or freed + * + * Since: 1.0 + */ G_CONST_RETURN gchar * clutter_text_get_text (ClutterText *self) { @@ -2134,6 +2194,15 @@ clutter_text_get_text (ClutterText *self) return self->priv->text; } +/** + * clutter_text_set_text: + * @self: a #ClutterText + * @text: the text to set + * + * Sets the contents of a #ClutterText actor. + * + * Since: 1.0 + */ void clutter_text_set_text (ClutterText *self, const gchar *text) @@ -2188,6 +2257,17 @@ clutter_text_set_text (ClutterText *self, g_object_notify (G_OBJECT (self), "text"); } +/** + * clutter_text_get_layout: + * @self: a #ClutterText + * + * Retrieves the current #PangoLayout used by a #ClutterText actor. + * + * Return value: a #PangoLayout. The returned object is owned by + * the #ClutterText actor and should not be modified or freed + * + * Since: 1.0 + */ PangoLayout * clutter_text_get_layout (ClutterText *self) { @@ -2200,6 +2280,20 @@ clutter_text_get_layout (ClutterText *self) return clutter_text_create_layout (self, width); } +/** + * clutter_text_set_color: + * @self: a #ClutterText + * @color: a #ClutterColor + * + * Sets the color of the contents of a #ClutterText actor. + * + * The overall opacity of the #ClutterText actor will be the + * result of the alpha value of @color and the composited + * opacity of the actor itself on the scenegraph, as returned + * by clutter_actor_get_paint_opacity(). + * + * Since: 1.0 + */ void clutter_text_set_color (ClutterText *self, const ClutterColor *color) @@ -2219,6 +2313,15 @@ clutter_text_set_color (ClutterText *self, g_object_notify (G_OBJECT (self), "color"); } +/** + * clutter_text_get_color: + * @self: a #ClutterText + * @color: return location for a #ClutterColor + * + * Retrieves the text color as set by clutter_text_get_color(). + * + * Since: 1.0 + */ void clutter_text_get_color (ClutterText *self, ClutterColor *color) @@ -2287,6 +2390,17 @@ clutter_text_get_ellipsize (ClutterText *self) return self->priv->ellipsize; } +/** + * clutter_text_get_line_wrap: + * @self: a #ClutterText + * + * Retrieves the value set using clutter_text_set_line_wrap(). + * + * Return value: %TRUE if the #ClutterText actor should wrap + * its contents + * + * Since: 1.0 + */ gboolean clutter_text_get_line_wrap (ClutterText *self) { @@ -2295,6 +2409,16 @@ clutter_text_get_line_wrap (ClutterText *self) return self->priv->wrap; } +/** + * clutter_text_set_line_wrap: + * @self: a #ClutterText + * @line_wrap: whether the contents should wrap + * + * Sets whether the contents of a #ClutterText actor should wrap, + * if they don't fit the size assigned to the actor. + * + * Since: 1.0 + */ void clutter_text_set_line_wrap (ClutterText *self, gboolean line_wrap) @@ -2555,7 +2679,7 @@ clutter_text_get_use_markup (ClutterText *self) * on both margins. This setting is ignored if Clutter is compiled * against Pango < 1.18. * - * Since: 0.6 + * Since: 1.0 */ void clutter_text_set_justify (ClutterText *self, @@ -2616,6 +2740,17 @@ clutter_text_get_cursor_position (ClutterText *self) return self->priv->position; } +/** + * clutter_text_set_cursor_position: + * @self: a #ClutterText + * @position: the new cursor position, in characters + * + * Sets the cursor of a #ClutterText actor at @position. + * + * The position is expressed in characters, not in bytes. + * + * Since: 1.0 + */ void clutter_text_set_cursor_position (ClutterText *self, gint position) @@ -2805,6 +2940,16 @@ clutter_text_get_max_length (ClutterText *self) return self->priv->max_length; } +/** + * clutter_text_insert_unichar: + * @self: a #ClutterText + * @wc: a Unicode character + * + * Inserts @wc at the current cursor position of a + * #ClutterText actor. + * + * Since: 1.0 + */ void clutter_text_insert_unichar (ClutterText *self, gunichar wc) @@ -2841,6 +2986,21 @@ clutter_text_insert_unichar (ClutterText *self, g_string_free (new, TRUE); } +/** + * clutter_text_insert_text: + * @self: a #ClutterText + * @text: the text to be inserted + * @position: the position of the insertion, or -1 + * + * Inserts @text into a #ClutterActor at the given position. + * + * If @position is a negative number, the text will be appended + * at the end of the current contents of the #ClutterText. + * + * The position is expressed in characters, not in bytes. + * + * Since: 1.0 + */ void clutter_text_insert_text (ClutterText *self, const gchar *text, @@ -2862,6 +3022,20 @@ clutter_text_insert_text (ClutterText *self, g_string_free (new, TRUE); } +/** + * clutter_text_delete_text: + * @self: a #ClutterText + * @start_pos: starting position + * @end_pos: ending position + * + * Deletes the text inside a #ClutterText actor between @start_pos + * and @end_pos. + * + * The starting and ending positions are expressed in characters, + * not in bytes. + * + * Since: 1.0 + */ void clutter_text_delete_text (ClutterText *self, gssize start_pos, @@ -2900,6 +3074,16 @@ clutter_text_delete_text (ClutterText *self, g_string_free (new, TRUE); } +/** + * clutter_text_delete_chars: + * @self: a #ClutterText + * @n_chars: the number of characters to delete + * + * Deletes @n_chars inside a #ClutterText actor, starting from the + * current cursor position. + * + * Since: 1.0 + */ void clutter_text_delete_chars (ClutterText *self, guint n_chars) @@ -2944,7 +3128,7 @@ clutter_text_delete_chars (ClutterText *self, /** * clutter_text_get_chars: - * @self: a #ClutterTex + * @self: a #ClutterText * @start_pos: start of text, in characters * @end_pos: end of text, in characters * From 2ca7cb46f95ec49e1016ddb880f2fd9ff6cc8c25 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:18:26 +0000 Subject: [PATCH 049/147] Use the paint opacity when painting a ClutterText ClutterText should use the paint opacity for both text and cursor. ClutterLabel had the wrong behaviour, as it set the actor's opacity using the text color's alpha channel, and ClutterEntry completely disregarded the actor's opacity when painting the cursor. This commit harmonizes the ClutterText behaviour to always use a composition of the actor's paint opacity and the text and cursor alpha channel values, thus behaving more consistently with the rest of Clutter. --- clutter/clutter-text.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 886256844..8027dfecc 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -719,22 +719,32 @@ static void cursor_paint (ClutterText *self) { ClutterTextPrivate *priv = self->priv; + ClutterActor *actor = CLUTTER_ACTOR (self); + guint8 real_opacity; if (priv->editable && priv->cursor_visible) { if (priv->cursor_color_set) { + real_opacity = clutter_actor_get_paint_opacity (actor) + * priv->cursor_color.alpha + / 255; + cogl_set_source_color4ub (priv->cursor_color.red, priv->cursor_color.green, priv->cursor_color.blue, - priv->cursor_color.alpha); + real_opacity); } else { + real_opacity = clutter_actor_get_paint_opacity (actor) + * priv->text_color.alpha + / 255; + cogl_set_source_color4ub (priv->text_color.red, priv->text_color.green, priv->text_color.blue, - priv->text_color.alpha); + real_opacity); } clutter_text_ensure_cursor_position (self); @@ -942,6 +952,7 @@ clutter_text_paint (ClutterActor *self) PangoLayout *layout; ClutterActorBox alloc = { 0, }; CoglColor color = { 0, }; + guint8 real_opacity; if (priv->font_desc == NULL || priv->text == NULL) { @@ -958,11 +969,15 @@ clutter_text_paint (ClutterActor *self) clutter_actor_get_allocation_box (self, &alloc); layout = clutter_text_create_layout (text, alloc.x2 - alloc.x1); + real_opacity = clutter_actor_get_paint_opacity (self) + * priv->text_color.alpha + / 255; + cogl_color_set_from_4ub (&color, priv->text_color.red, priv->text_color.green, priv->text_color.blue, - clutter_actor_get_paint_opacity (self)); + real_opacity); cogl_pango_render_layout (layout, 0, 0, &color, 0); } From 77b3b9d5f494bb193611004456dd8f025e379c18 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:21:57 +0000 Subject: [PATCH 050/147] Do not use contractions when not needed I understand we are not Pascal developers, and we don't have to use cute and cuddly names like "i_am_an_integer_counter", but a ClutterButtonEvent should be stored inside an "event" variable. Using "bev" instead? Mmmh, not so much. --- clutter/clutter-text.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 8027dfecc..4a40a8bf0 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -821,15 +821,15 @@ cursor_paint (ClutterText *self) static gboolean clutter_text_button_press (ClutterActor *actor, - ClutterButtonEvent *bev) + ClutterButtonEvent *event) { ClutterText *self = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = self->priv; ClutterUnit x, y; gint index_; - x = CLUTTER_UNITS_FROM_INT (bev->x); - y = CLUTTER_UNITS_FROM_INT (bev->y); + x = CLUTTER_UNITS_FROM_INT (event->x); + y = CLUTTER_UNITS_FROM_INT (event->y); clutter_actor_transform_stage_point (actor, x, y, &x, &y); From 786bc4d4d5741b799d7da2a86cd584600a3a2993 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:24:45 +0000 Subject: [PATCH 051/147] Allow only synthetic events with keyval 0 We allow KeyEvents with a key symbol of '0' to fall through only if they are marked as synthetic. Otherwise we discard them without mercy. Synthetic events are useful to test ClutterText behaviour; in fact, we do use them inside the test suite exactly for that reason. --- clutter/clutter-text.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4a40a8bf0..4b657d1da 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -920,20 +920,36 @@ clutter_text_key_press (ClutterActor *actor, pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor)); g_assert (pool != NULL); - res = clutter_binding_pool_activate (pool, keyval, - event->modifier_state, - G_OBJECT (actor)); + /* we allow passing synthetic events that only contain + * the Unicode value and not the key symbol + */ + if (keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC)) + res = FALSE; + else + res = clutter_binding_pool_activate (pool, keyval, + event->modifier_state, + G_OBJECT (actor)); + + /* if the key binding has handled the event we bail out + * as fast as we can; otherwise, we try to insert the + * Unicode character inside the key event into the text + * actor + */ if (res) return TRUE; else { gunichar key_unichar = clutter_key_event_unicode (event); - if (key_unichar == '\r') /* return is reported as CR we want LF */ + /* return is reported as CR, but we want LF */ + if (key_unichar == '\r') key_unichar = '\n'; if (g_unichar_validate (key_unichar)) { + /* truncate the eventual selection so that the + * Unicode character can replace it + */ clutter_text_truncate_selection (self); clutter_text_insert_unichar (self, key_unichar); From edd6b65743132648172b2bfa99eb69be82fd1971 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:26:47 +0000 Subject: [PATCH 052/147] Update the paint opacity unit test ClutterText behaviour with regards to the paint opacity has been changed by commit 07e19fff5ffa9617413fa6c3715914513fec3793. We need to update the test suite for the paint opacity to reflect that change as well. --- tests/conform/test-paint-opacity.c | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/conform/test-paint-opacity.c b/tests/conform/test-paint-opacity.c index 77618a98e..3dd48e912 100644 --- a/tests/conform/test-paint-opacity.c +++ b/tests/conform/test-paint-opacity.c @@ -14,12 +14,12 @@ test_label_opacity (TestConformSimpleFixture *fixture, stage = clutter_stage_get_default (); - label = clutter_label_new_with_text ("Sans 18px", "Label, 50% opacity"); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); + label = clutter_text_new_with_text ("Sans 18px", "Label, 50% opacity"); + clutter_text_set_color (CLUTTER_TEXT (label), &label_color); if (g_test_verbose ()) g_print ("label 50%%.get_color()/1\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); clutter_container_add (CLUTTER_CONTAINER (stage), label, NULL); @@ -27,12 +27,16 @@ test_label_opacity (TestConformSimpleFixture *fixture, if (g_test_verbose ()) g_print ("label 50%%.get_color()/2\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); if (g_test_verbose ()) - g_print ("label 50%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (label)); + g_print ("label 50%%.get_paint_opacity()/1\n"); + g_assert (clutter_actor_get_paint_opacity (label) == 255); + + if (g_test_verbose ()) + g_print ("label 50%%.get_paint_opacity()/2\n"); + clutter_actor_set_opacity (label, 128); g_assert (clutter_actor_get_paint_opacity (label) == 128); clutter_actor_destroy (label); @@ -66,8 +70,7 @@ test_rectangle_opacity (TestConformSimpleFixture *fixture, g_assert (color_check.alpha == rect_color.alpha); if (g_test_verbose ()) - g_print ("rect 100%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (rect)); + g_print ("rect 100%%.get_paint_opacity()\n"); g_assert (clutter_actor_get_paint_opacity (rect) == 255); clutter_actor_destroy (rect); @@ -91,25 +94,24 @@ test_paint_opacity (TestConformSimpleFixture *fixture, clutter_actor_set_position (group1, 10, 30); clutter_actor_show (group1); - label = clutter_label_new_with_text ("Sans 18px", "Label+Group, 25% opacity"); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); + label = clutter_text_new_with_text ("Sans 18px", "Label+Group, 25% opacity"); + clutter_text_set_color (CLUTTER_TEXT (label), &label_color); if (g_test_verbose ()) g_print ("label 50%% + group 50%%.get_color()/1\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); clutter_container_add (CLUTTER_CONTAINER (group1), label, NULL); if (g_test_verbose ()) g_print ("label 50%% + group 50%%.get_color()/2\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); if (g_test_verbose ()) - g_print ("label 50%% + group 50%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (label)); - g_assert (clutter_actor_get_paint_opacity (label) == 64); + g_print ("label 50%% + group 50%%.get_paint_opacity() = 128\n"); + g_assert (clutter_actor_get_paint_opacity (label) == 128); clutter_actor_destroy (label); @@ -133,9 +135,7 @@ test_paint_opacity (TestConformSimpleFixture *fixture, g_assert (color_check.alpha == rect_color.alpha); if (g_test_verbose ()) - g_print ("rect 100%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (rect)); - + g_print ("rect 100%%.get_paint_opacity()\n"); g_assert (clutter_actor_get_paint_opacity (rect) == 128); clutter_actor_destroy (rect); From 45de6df615c5573c38d635fd95c1bb06066e2c82 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 13:39:35 +0000 Subject: [PATCH 053/147] Update ignore file Add the conformance test units for ClutterText. --- .gitignore | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitignore b/.gitignore index dba281eaa..11ca7ca69 100644 --- a/.gitignore +++ b/.gitignore @@ -152,6 +152,19 @@ stamp-h1 /tests/conform/test_rectangle_opacity /tests/conform/test_backface_culling /tests/conform/test_binding_pool +/tests/conform/test_text_append_some +/tests/conform/test_text_cache +/tests/conform/test_text_cursor +/tests/conform/test_text_delete_chars +/tests/conform/test_text_delete_text +/tests/conform/test_text_empty +/tests/conform/test_text_event +/tests/conform/test_text_get_chars +/tests/conform/test_text_insert +/tests/conform/test_text_prepend_some +/tests/conform/test_text_set_empty +/tests/conform/test_text_set_text +/tests/conform/test_text_utf8_validation /tests/micro-bench/test-text /tests/tools/disable-npots.sh /clutter/x11/clutter-x11-enum-types.[ch] From 24a616368845de3b2e1ee4c335f3051411dfecd6 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 13:45:22 +0000 Subject: [PATCH 054/147] Do not compile the opacity and entry interactive tests The test-opacity interactive test is superceded by the equivalent units inside the conformance test suite. The test-entry interactive test is superceded by the test-text one. --- tests/interactive/Makefile.am | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 3a536162d..c07f48fa4 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -6,7 +6,6 @@ UNIT_TESTS = \ test-scale.c \ test-actors.c \ test-behave.c \ - test-entry.c \ test-project.c \ test-perspective.c \ test-rotate.c \ @@ -21,7 +20,6 @@ UNIT_TESTS = \ test-shader.c \ test-unproject.c \ test-viewport test-fbo.c \ - test-opacity.c \ test-multistage.c \ test-cogl-primitives.c \ test-cogl-tex-tile.c \ From 5c14044e5216274cd806122e9ac2550ebb39d9bf Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 13:48:01 +0000 Subject: [PATCH 055/147] Update the interactive tests to ClutterText Instead of using ClutterLabel, use ClutterText to display some text where needed. --- tests/interactive/test-clip.c | 4 +-- tests/interactive/test-cogl-tex-polygon.c | 14 ++++----- tests/interactive/test-depth.c | 10 +++---- tests/interactive/test-easing.c | 8 +++--- tests/interactive/test-fbo.c | 4 +-- tests/interactive/test-layout.c | 4 +-- tests/interactive/test-multistage.c | 8 +++--- tests/interactive/test-project.c | 4 +-- tests/interactive/test-random-text.c | 2 +- tests/interactive/test-rotate.c | 4 +-- tests/interactive/test-shader.c | 2 +- tests/interactive/test-stage-read-pixels.c | 6 ++-- tests/interactive/test-unproject.c | 33 +++++++++++----------- 13 files changed, 51 insertions(+), 52 deletions(-) diff --git a/tests/interactive/test-clip.c b/tests/interactive/test-clip.c index 3b52fdb81..1d3d05a33 100644 --- a/tests/interactive/test-clip.c +++ b/tests/interactive/test-clip.c @@ -305,8 +305,8 @@ test_clip_main (int argc, char **argv) data.hand = cogl_texture_new_from_file ("redhand.png", 64, FALSE, COGL_PIXEL_FORMAT_ANY, NULL); - label = clutter_label_new_with_text ("Sans 12px", instructions); - clutter_label_set_line_wrap (CLUTTER_LABEL (label), TRUE); + label = clutter_text_new_with_text ("Sans 12px", instructions); + clutter_text_set_line_wrap (CLUTTER_TEXT (label), TRUE); clutter_actor_set_width (label, clutter_actor_get_width (data.stage) - 310); clutter_actor_set_y (label, clutter_actor_get_height (data.stage) diff --git a/tests/interactive/test-cogl-tex-polygon.c b/tests/interactive/test-cogl-tex-polygon.c index 5e237eda7..1e9d38b18 100644 --- a/tests/interactive/test-cogl-tex-polygon.c +++ b/tests/interactive/test-cogl-tex-polygon.c @@ -297,16 +297,16 @@ frame_cb (ClutterTimeline *timeline, } static void -update_toggle_text (ClutterLabel *button, gboolean val) +update_toggle_text (ClutterText *button, gboolean val) { - clutter_label_set_text (button, val ? "Enabled" : "Disabled"); + clutter_text_set_text (button, val ? "Enabled" : "Disabled"); } static gboolean on_toggle_click (ClutterActor *button, ClutterEvent *event, gboolean *toggle_val) { - update_toggle_text (CLUTTER_LABEL (button), *toggle_val = !*toggle_val); + update_toggle_text (CLUTTER_TEXT (button), *toggle_val = !*toggle_val); return TRUE; } @@ -315,12 +315,12 @@ static ClutterActor * make_toggle (const char *label_text, gboolean *toggle_val) { ClutterActor *group = clutter_group_new (); - ClutterActor *label = clutter_label_new_with_text ("Sans 14", label_text); - ClutterActor *button = clutter_label_new_with_text ("Sans 14", ""); + ClutterActor *label = clutter_text_new_with_text ("Sans 14", label_text); + ClutterActor *button = clutter_text_new_with_text ("Sans 14", ""); clutter_actor_set_reactive (button, TRUE); - update_toggle_text (CLUTTER_LABEL (button), *toggle_val); + update_toggle_text (CLUTTER_TEXT (button), *toggle_val); clutter_actor_set_position (button, clutter_actor_get_width (label) + 10, 0); clutter_container_add (CLUTTER_CONTAINER (group), label, button, NULL); @@ -373,7 +373,7 @@ test_cogl_tex_polygon_main (int argc, char *argv[]) clutter_actor_set_position (filtering_toggle, 0, clutter_actor_get_y (slicing_toggle) - clutter_actor_get_height (filtering_toggle)); - note = clutter_label_new_with_text ("Sans 10", "<- Click to change"); + note = clutter_text_new_with_text ("Sans 10", "<- Click to change"); clutter_actor_set_position (note, clutter_actor_get_width (filtering_toggle) + 10, (clutter_actor_get_height (stage) diff --git a/tests/interactive/test-depth.c b/tests/interactive/test-depth.c index a07eff234..02e5e8bce 100644 --- a/tests/interactive/test-depth.c +++ b/tests/interactive/test-depth.c @@ -83,10 +83,10 @@ janus_group (const gchar *front_text, group = clutter_group_new (); rectangle = clutter_rectangle_new_with_color (&slide_color); - front = clutter_label_new_with_text ("Sans 50px", front_text); - back = clutter_label_new_with_text ("Sans 50px", back_text); - clutter_label_set_color (CLUTTER_LABEL (front), &red); - clutter_label_set_color (CLUTTER_LABEL (back), &green); + front = clutter_text_new_with_text ("Sans 50px", front_text); + back = clutter_text_new_with_text ("Sans 50px", back_text); + clutter_text_set_color (CLUTTER_TEXT (front), &red); + clutter_text_set_color (CLUTTER_TEXT (back), &green); clutter_actor_get_size (front, &width, &height); clutter_actor_get_size (back, &width2, &height2); @@ -134,7 +134,7 @@ test_depth_main (int argc, char *argv[]) clutter_stage_add (stage, group); clutter_actor_show (group); - label = clutter_label_new_with_text ("Mono 26", "Clutter"); + label = clutter_text_new_with_text ("Mono 26", "Clutter"); clutter_actor_set_position (label, 120, 200); clutter_actor_show (label); diff --git a/tests/interactive/test-easing.c b/tests/interactive/test-easing.c index 555aa3c3a..22048ad4e 100644 --- a/tests/interactive/test-easing.c +++ b/tests/interactive/test-easing.c @@ -41,7 +41,7 @@ on_button_press (ClutterActor *actor, current_mode + 1, n_easing_modes); - clutter_label_set_text (CLUTTER_LABEL (easing_mode_label), text); + clutter_text_set_text (CLUTTER_TEXT (easing_mode_label), text); g_free (text); clutter_actor_get_size (main_stage, &stage_width, &stage_height); @@ -97,10 +97,10 @@ test_easing_main (int argc, char *argv[]) current_mode + 1, n_easing_modes); - label = clutter_label_new (); + label = clutter_text_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), label); - clutter_label_set_font_name (CLUTTER_LABEL (label), "Sans 18px"); - clutter_label_set_text (CLUTTER_LABEL (label), text); + clutter_text_set_font_name (CLUTTER_TEXT (label), "Sans 18px"); + clutter_text_set_text (CLUTTER_TEXT (label), text); clutter_actor_get_size (label, &label_width, &label_height); clutter_actor_set_position (label, stage_width - label_width - 10, diff --git a/tests/interactive/test-fbo.c b/tests/interactive/test-fbo.c index bdf99f1cb..f30cbf049 100644 --- a/tests/interactive/test-fbo.c +++ b/tests/interactive/test-fbo.c @@ -43,9 +43,9 @@ make_source(void) clutter_group_add (source, actor); - actor = clutter_label_new_with_text ("Sans Bold 50px", "Clutter"); + actor = clutter_text_new_with_text ("Sans Bold 50px", "Clutter"); - clutter_label_set_color (CLUTTER_LABEL (actor), &yellow); + clutter_text_set_color (CLUTTER_TEXT (actor), &yellow); clutter_actor_set_y (actor, clutter_actor_get_height(source) + 5); clutter_group_add (source, actor); diff --git a/tests/interactive/test-layout.c b/tests/interactive/test-layout.c index 9fe17222e..ea27f8e11 100644 --- a/tests/interactive/test-layout.c +++ b/tests/interactive/test-layout.c @@ -787,7 +787,7 @@ test_layout_main (int argc, char *argv[]) clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); - instructions = clutter_label_new_with_text ("Sans 14", + instructions = clutter_text_new_with_text ("Sans 14", "Instructions:\n" "a - add a new item\n" "d - remove last item\n" @@ -799,7 +799,7 @@ test_layout_main (int argc, char *argv[]) "s - use transformed box\n" "q - quit"); - clutter_label_set_use_markup (CLUTTER_LABEL (instructions), TRUE); + clutter_text_set_use_markup (CLUTTER_TEXT (instructions), TRUE); clutter_actor_set_position (instructions, 450, 10); clutter_container_add_actor (CLUTTER_CONTAINER (stage), instructions); diff --git a/tests/interactive/test-multistage.c b/tests/interactive/test-multistage.c index 19b9c750f..1021f8b90 100644 --- a/tests/interactive/test-multistage.c +++ b/tests/interactive/test-multistage.c @@ -50,10 +50,10 @@ on_button_press (ClutterActor *actor, clutter_container_add_actor (CLUTTER_CONTAINER (new_stage), tex); stage_label = g_strdup_printf ("Stage: %d", ++n_stages); - label = clutter_label_new_with_text ("Mono 12", stage_label); + label = clutter_text_new_with_text ("Mono 12", stage_label); - clutter_label_set_color (CLUTTER_LABEL (label), &white); - clutter_label_set_use_markup (CLUTTER_LABEL (label), TRUE); + clutter_text_set_color (CLUTTER_TEXT (label), &white); + clutter_text_set_use_markup (CLUTTER_TEXT (label), TRUE); width = (clutter_actor_get_width (new_stage) - clutter_actor_get_width (label)) / 2; height = (clutter_actor_get_height (new_stage) @@ -110,7 +110,7 @@ test_multistage_main (int argc, char *argv[]) G_CALLBACK (on_button_press), NULL); - label = clutter_label_new_with_text ("Mono 16", "Default stage"); + label = clutter_text_new_with_text ("Mono 16", "Default stage"); width = (clutter_actor_get_width (stage_default) - clutter_actor_get_width (label)) / 2; diff --git a/tests/interactive/test-project.c b/tests/interactive/test-project.c index 1ac2176b7..33560df0b 100644 --- a/tests/interactive/test-project.c +++ b/tests/interactive/test-project.c @@ -224,8 +224,8 @@ test_project_main (int argc, char *argv[]) clutter_actor_set_rotation (rect, CLUTTER_Y_AXIS, 60, 0, 0, 0); clutter_group_add (CLUTTER_GROUP (main_stage), rect); - label = clutter_label_new_with_text ("Mono 8pt", "Drag the blue rectangles"); - clutter_label_set_color (CLUTTER_LABEL (label), &white); + label = clutter_text_new_with_text ("Mono 8pt", "Drag the blue rectangles"); + clutter_text_set_color (CLUTTER_TEXT (label), &white); clutter_actor_set_position (label, 10, 10); clutter_group_add (CLUTTER_GROUP (main_stage), label); diff --git a/tests/interactive/test-random-text.c b/tests/interactive/test-random-text.c index 18ebefff0..b73ec4b4f 100644 --- a/tests/interactive/test-random-text.c +++ b/tests/interactive/test-random-text.c @@ -45,7 +45,7 @@ on_idle (gpointer data) font_names[rand () % FONT_NAME_COUNT], rand () % (MAX_FONT_SIZE - MIN_FONT_SIZE) + MIN_FONT_SIZE); - label = clutter_label_new_with_text (font_name, text); + label = clutter_text_new_with_text (font_name, text); if (clutter_actor_get_height (label) > line_height) line_height = clutter_actor_get_height (label); diff --git a/tests/interactive/test-rotate.c b/tests/interactive/test-rotate.c index 377a309db..b147a7d24 100644 --- a/tests/interactive/test-rotate.c +++ b/tests/interactive/test-rotate.c @@ -33,8 +33,8 @@ test_rotate_main (int argc, char *argv[]) clutter_actor_show (hand); clutter_container_add_actor (CLUTTER_CONTAINER (stage), hand); - label = clutter_label_new_with_text ("Mono 16", "The Wonder of the Spinning Hand"); - clutter_label_set_alignment (CLUTTER_LABEL (label), PANGO_ALIGN_CENTER); + label = clutter_text_new_with_text ("Mono 16", "The Wonder of the Spinning Hand"); + clutter_text_set_alignment (CLUTTER_TEXT (label), PANGO_ALIGN_CENTER); clutter_actor_set_position (label, 150, 150); clutter_actor_set_size (label, 500, 100); clutter_actor_show (label); diff --git a/tests/interactive/test-shader.c b/tests/interactive/test-shader.c index 4880a704a..1101d8246 100644 --- a/tests/interactive/test-shader.c +++ b/tests/interactive/test-shader.c @@ -354,7 +354,7 @@ test_shader_main (gint argc, gchar *argv[]) if (!child2) g_error("pixbuf load failed: %s", error ? error->message : "Unknown"); child3 = clutter_rectangle_new (); - child4 = clutter_label_new_with_text ("Sans 20px", "Shady stuff"); + child4 = clutter_text_new_with_text ("Sans 20px", "Shady stuff"); clutter_rectangle_set_color (child3, &color); clutter_actor_set_size (child3, 50, 50); diff --git a/tests/interactive/test-stage-read-pixels.c b/tests/interactive/test-stage-read-pixels.c index f13b7ebe7..b0d813970 100644 --- a/tests/interactive/test-stage-read-pixels.c +++ b/tests/interactive/test-stage-read-pixels.c @@ -23,14 +23,14 @@ make_label (void) gchar *text; gchar *argv[] = { "ls", "--help", NULL }; - label = clutter_label_new (); - clutter_label_set_font_name (CLUTTER_LABEL (label), "Sans 10"); + label = clutter_text_new (); + clutter_text_set_font_name (CLUTTER_TEXT (label), "Sans 10"); if (g_spawn_sync (NULL, argv, NULL, G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_SEARCH_PATH, NULL, NULL, &text, NULL, NULL, NULL)) { - clutter_label_set_text (CLUTTER_LABEL (label), text); + clutter_text_set_text (CLUTTER_TEXT (label), text); g_free (text); } diff --git a/tests/interactive/test-unproject.c b/tests/interactive/test-unproject.c index 422647e84..ac76e6768 100644 --- a/tests/interactive/test-unproject.c +++ b/tests/interactive/test-unproject.c @@ -29,11 +29,10 @@ on_event (ClutterStage *stage, actor = clutter_stage_get_actor_at_pos (stage, x, y); - if (clutter_actor_transform_stage_point (actor, - CLUTTER_UNITS_FROM_DEVICE (x), - CLUTTER_UNITS_FROM_DEVICE (y), - &xu2, &yu2)) + CLUTTER_UNITS_FROM_DEVICE (x), + CLUTTER_UNITS_FROM_DEVICE (y), + &xu2, &yu2)) { gchar *txt; @@ -52,12 +51,11 @@ on_event (ClutterStage *stage, CLUTTER_UNITS_TO_DEVICE (xu2), CLUTTER_UNITS_TO_DEVICE (yu2)); - clutter_label_set_text (CLUTTER_LABEL (label), txt); + clutter_text_set_text (CLUTTER_TEXT (label), txt); g_free (txt); } else - clutter_label_set_text (CLUTTER_LABEL (label), - "Unprojection failed."); + clutter_text_set_text (CLUTTER_TEXT (label), "Unprojection failed."); } break; @@ -75,9 +73,9 @@ test_unproject_main (int argc, char *argv[]) gchar *txt; ClutterActor *rect, *stage, *label0; int i, rotate_x = 0, rotate_y = 60, rotate_z = 0; - ClutterColor stage_clr = { 0x0, 0x0, 0x0, 0xff }, + ClutterColor stage_clr = { 0x0, 0x0, 0x0, 0xff }, white = { 0xff, 0xff, 0xff, 0xff }, - blue = { 0, 0xff, 0xff, 0xff }; + blue = { 0x0, 0xff, 0xff, 0xff }; for (i = 0; i < argc; ++i) { @@ -95,11 +93,12 @@ test_unproject_main (int argc, char *argv[]) } else if (!strncmp (argv[i], "--help", 6)) { - printf ("%s [--rotage-x=degrees] [--rotage-y=degrees] " - "[--rotage-z=degrees]\n", - argv[0]); + g_print ("%s [--rotage-x=degrees] " + "[--rotage-y=degrees] " + "[--rotage-z=degrees]\n", + argv[0]); - exit (0); + return EXIT_FAILURE; } } @@ -124,8 +123,8 @@ test_unproject_main (int argc, char *argv[]) RECT_T, RECT_T + RECT_H, rotate_x, rotate_y, rotate_z); - label0 = clutter_label_new_with_text ("Mono 8pt", txt); - clutter_label_set_color (CLUTTER_LABEL (label0), &white); + label0 = clutter_text_new_with_text ("Mono 8pt", txt); + clutter_text_set_color (CLUTTER_TEXT (label0), &white); clutter_actor_set_position (label0, 10, 10); clutter_group_add (CLUTTER_GROUP (stage), label0); @@ -133,9 +132,9 @@ test_unproject_main (int argc, char *argv[]) g_free (txt); label = - clutter_label_new_with_text ("Mono 8pt", "Click around!"); + clutter_text_new_with_text ("Mono 8pt", "Click around!"); - clutter_label_set_color (CLUTTER_LABEL (label), &blue); + clutter_text_set_color (CLUTTER_TEXT (label), &blue); clutter_actor_set_position (label, 10, 50); clutter_group_add (CLUTTER_GROUP (stage), label); From ffa37dec2067ae69d2850a611758ea4d4ea48d0b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 13:48:45 +0000 Subject: [PATCH 056/147] Update the micro-bench tests to ClutterText Test the ClutterText rendering speed instead of ClutterLabel's. --- tests/micro-bench/test-text.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/micro-bench/test-text.c b/tests/micro-bench/test-text.c index a44f39d4e..afbb8b47b 100644 --- a/tests/micro-bench/test-text.c +++ b/tests/micro-bench/test-text.c @@ -80,14 +80,14 @@ main (int argc, char *argv[]) scale = 1.0; } - label = clutter_label_new_with_text (font_name, text); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); + label = clutter_text_new_with_text (font_name, text); + clutter_text_set_color (CLUTTER_TEXT (label), &label_color); clutter_actor_set_position (label, (1.0*STAGE_WIDTH/COLS)*col, (1.0*STAGE_HEIGHT/ROWS)*row); /*clutter_actor_set_clip (label, 0,0, (1.0*STAGE_WIDTH/COLS), (1.0*STAGE_HEIGHT/ROWS));*/ clutter_actor_set_scale (label, scale, scale); - clutter_label_set_line_wrap (CLUTTER_LABEL (label), FALSE); + clutter_text_set_line_wrap (CLUTTER_TEXT (label), FALSE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), label); } } From 5a1837bc9ef117d47cc9e6dbea66245c95027a4f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 13:49:39 +0000 Subject: [PATCH 057/147] Remove ClutterEntry and ClutterLabel from the build For the time being, just don't include them or compile them; the files will be removed from the repository as soon as all the documentation annotations have been ported over to ClutterText. --- clutter/Makefile.am | 4 ---- clutter/clutter.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/clutter/Makefile.am b/clutter/Makefile.am index f51523200..1ef03bd9a 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -63,7 +63,6 @@ source_h = \ $(srcdir)/clutter-container.h \ $(srcdir)/clutter-deprecated.h \ $(srcdir)/clutter-effect.h \ - $(srcdir)/clutter-entry.h \ $(srcdir)/clutter-event.h \ $(srcdir)/clutter-feature.h \ $(srcdir)/clutter-fixed.h \ @@ -71,7 +70,6 @@ source_h = \ $(srcdir)/clutter-group.h \ $(srcdir)/clutter-interval.h \ $(srcdir)/clutter-keysyms.h \ - $(srcdir)/clutter-label.h \ $(srcdir)/clutter-list-model.h \ $(srcdir)/clutter-main.h \ $(srcdir)/clutter-media.h \ @@ -154,7 +152,6 @@ source_c = \ clutter-color.c \ clutter-container.c \ clutter-effect.c \ - clutter-entry.c \ clutter-enum-types.c \ clutter-event.c \ clutter-feature.c \ @@ -163,7 +160,6 @@ source_c = \ clutter-group.c \ clutter-id-pool.c \ clutter-interval.c \ - clutter-label.c \ clutter-list-model.c \ clutter-main.c \ clutter-marshal.c \ diff --git a/clutter/clutter.h b/clutter/clutter.h index 664c88970..212870c41 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -46,14 +46,12 @@ #include "clutter-container.h" #include "clutter-deprecated.h" #include "clutter-effect.h" -#include "clutter-entry.h" #include "clutter-event.h" #include "clutter-feature.h" #include "clutter-frame-source.h" #include "clutter-group.h" #include "clutter-interval.h" #include "clutter-keysyms.h" -#include "clutter-label.h" #include "clutter-list-model.h" #include "clutter-main.h" #include "clutter-media.h" From 0ac15903781b8deb9339027bdd4db2982f2629f3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 14:57:46 +0000 Subject: [PATCH 058/147] Lazily create the PangoContext on Text::init If we create the PangoContext for ClutterText inside the class initialization we might not have a Clutter main context yet. Ideally, we should store the Pango context inside the main context and create it on clutter_init(), but for now we can lazily create the PangoContext when we initialize a ClutterText instance for the first time. --- clutter/clutter-text.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4b657d1da..d746279aa 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1417,8 +1417,6 @@ clutter_text_class_init (ClutterTextClass *klass) ClutterBindingPool *binding_pool; GParamSpec *pspec; - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); gobject_class->set_property = clutter_text_set_property; @@ -1829,6 +1827,9 @@ clutter_text_init (ClutterText *self) ClutterTextPrivate *priv; int i; + if (G_UNLIKELY (_context == NULL)) + _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); + self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); priv->alignment = PANGO_ALIGN_LEFT; From a50dfefbc33a8c0b0c7189058f4f7f521707a8fc Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 15:00:22 +0000 Subject: [PATCH 059/147] Fix documentation issues Provide a description for the ClutterText section, and fix some misnamed arguments inside the header file. --- clutter/clutter-text.c | 16 ++++++++++++++++ clutter/clutter-text.h | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index d746279aa..c0d51543e 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -22,6 +22,22 @@ * License along with this library. If not, see . */ +/** + * SECTION:clutter-text + * @short_description: An actor for displaying and editing text + * + * #ClutterText is an actor that displays custom text using Pango + * as the text rendering engine. + * + * #ClutterText also allows inline editing of the text if the + * actor is set editable using clutter_text_set_editable(). + * + * Selection using keyboard or pointers can be enabled using + * clutter_text_set_selectable(). + * + * #ClutterText is available since Clutter 1.0 + */ + /* TODO: undo/redo hooks? */ diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 87f103486..56595b794 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -101,7 +101,7 @@ void clutter_text_set_ellipsize (ClutterText *self PangoEllipsizeMode mode); PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self); void clutter_text_set_line_wrap (ClutterText *self, - gboolean wrap); + gboolean line_wrap); gboolean clutter_text_get_line_wrap (ClutterText *self); void clutter_text_set_line_wrap_mode (ClutterText *self, PangoWrapMode wrap_mode); @@ -123,7 +123,7 @@ gboolean clutter_text_get_justify (ClutterText *self void clutter_text_insert_unichar (ClutterText *self, gunichar wc); void clutter_text_delete_chars (ClutterText *self, - guint len); + guint n_chars); void clutter_text_insert_text (ClutterText *self, const gchar *text, gssize position); From c8dbbfaacff5b3d6ed2f9b28b65a10e15241dc0f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 15:01:28 +0000 Subject: [PATCH 060/147] [docs] Update the API reference Add all the unused API to the sections file; there are still some undocumented bits, but clutter-unused.txt is empty for the time being. Also, add ClutterText to the main XML document and remove ClutterLabel and ClutterEntry. --- doc/reference/clutter/Makefile.am | 1 + doc/reference/clutter/clutter-docs.xml | 3 +- doc/reference/clutter/clutter-sections.txt | 131 ++++++++++++++++++++- doc/reference/clutter/clutter.types | 3 +- 4 files changed, 132 insertions(+), 6 deletions(-) diff --git a/doc/reference/clutter/Makefile.am b/doc/reference/clutter/Makefile.am index ca941565f..d03a283d0 100644 --- a/doc/reference/clutter/Makefile.am +++ b/doc/reference/clutter/Makefile.am @@ -55,6 +55,7 @@ CFILE_GLOB=$(top_srcdir)/clutter/*.c # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h IGNORE_HFILES=\ clutter.h \ + clutter-bezier.h \ clutter-debug.h \ clutter-deprecated.h \ clutter-enum-types.h \ diff --git a/doc/reference/clutter/clutter-docs.xml b/doc/reference/clutter/clutter-docs.xml index 90b7d0d8a..8807ed3a8 100644 --- a/doc/reference/clutter/clutter-docs.xml +++ b/doc/reference/clutter/clutter-docs.xml @@ -57,8 +57,7 @@ - - + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index dd4061fc2..9fb7924dd 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -170,6 +170,9 @@ clutter_smoothstep_inc_func clutter_smoothstep_dec_func clutter_exp_inc_func clutter_exp_dec_func +clutter_exp_in_func +clutter_exp_out_func +clutter_exp_in_out_func clutter_ease_in_func clutter_ease_out_func clutter_ease_in_out_func @@ -438,6 +441,8 @@ clutter_actor_get_reactive clutter_actor_set_shader clutter_actor_get_shader clutter_actor_set_shader_param +clutter_actor_set_shader_param_int +clutter_actor_set_shader_param_float clutter_actor_set_depthu @@ -471,6 +476,9 @@ clutter_actor_get_scalex clutter_actor_set_rotationx clutter_actor_get_rotationx + +clutter_actor_grab_key_focus + CLUTTER_TYPE_GEOMETRY CLUTTER_TYPE_ACTOR_BOX @@ -693,6 +701,8 @@ ClutterPathCallback ClutterPathNodeType clutter_path_new clutter_path_new_with_description + + clutter_path_add_move_to clutter_path_add_rel_move_to clutter_path_add_line_to @@ -702,6 +712,8 @@ clutter_path_add_rel_curve_to clutter_path_add_close clutter_path_add_string clutter_path_add_node + + clutter_path_get_n_nodes clutter_path_get_node clutter_path_get_nodes @@ -720,6 +732,21 @@ ClutterPathNode clutter_path_node_copy clutter_path_node_free clutter_path_node_equal + + +CLUTTER_TYPE_PATH +CLUTTER_TYPE_PATH_NODE +CLUTTER_PATH +CLUTTER_PATH_CLASS +CLUTTER_IS_PATH +CLUTTER_IS_PATH_CLASS +CLUTTER_PATH_GET_CLASS +CLUTTER_PATH_RELATIVE + + +ClutterPathPrivate +clutter_path_get_type +clutter_path_node_get_type
@@ -1510,11 +1537,27 @@ clutter_shader_release clutter_shader_is_compiled clutter_shader_set_is_enabled clutter_shader_get_is_enabled -clutter_shader_set_uniform_1f + + clutter_shader_set_uniform clutter_shader_get_cogl_program clutter_shader_get_cogl_fragment_shader clutter_shader_get_cogl_vertex_shader +clutter_shader_set_uniform_1f + + +ClutterShaderFloat +CLUTTER_VALUE_HOLDS_SHADER_FLOAT +clutter_value_set_shader_float +clutter_value_get_shader_float +ClutterShaderInt +CLUTTER_VALUE_HOLDS_SHADER_INT +clutter_value_set_shader_int +clutter_value_get_shader_int +ClutterShaderMatrix +CLUTTER_VALUE_HOLDS_SHADER_MATRIX +clutter_value_set_shader_matrix +clutter_value_get_shader_matrix CLUTTER_IS_SHADER @@ -1524,10 +1567,17 @@ CLUTTER_SHADER_CLASS CLUTTER_SHADER_ERROR CLUTTER_SHADER_GET_CLASS CLUTTER_TYPE_SHADER +CLUTTER_TYPE_SHADER_FLOAT +CLUTTER_TYPE_SHADER_INT +CLUTTER_TYPE_SHADER_MATRIX + ClutterShaderPrivate clutter_shader_get_type clutter_shader_error_quark +clutter_shader_float_get_type +clutter_shader_int_get_type +clutter_shader_matrix_get_type
@@ -1593,6 +1643,10 @@ clutter_interval_peek_final_value clutter_interval_set_interval clutter_interval_get_interval + +clutter_interval_compute_value +clutter_interval_validate + CLUTTER_TYPE_INTERVAL CLUTTER_INTERVAL @@ -1614,7 +1668,7 @@ ClutterBindingActionFunc clutter_binding_pool_new -clutter_binding_pool_get_from_class +clutter_binding_pool_get_for_class clutter_binding_pool_find @@ -1629,3 +1683,76 @@ clutter_binding_pool_unblock_action clutter_binding_pool_activate
+ +
+ClutterText +clutter-text +ClutterText +ClutterTextClass +clutter_text_new +clutter_text_new_full +clutter_text_new_with_text + + +clutter_text_set_text +clutter_text_get_text +clutter_text_set_activatable +clutter_text_get_activatable +clutter_text_set_alignment +clutter_text_get_alignment +clutter_text_set_attributes +clutter_text_get_attributes +clutter_text_set_color +clutter_text_get_color +clutter_text_set_ellipsize +clutter_text_get_ellipsize +clutter_text_set_font_name +clutter_text_get_font_name +clutter_text_set_invisible_char +clutter_text_get_invisible_char +clutter_text_set_justify +clutter_text_get_justify +clutter_text_get_layout +clutter_text_set_line_wrap +clutter_text_get_line_wrap +clutter_text_set_line_wrap_mode +clutter_text_get_line_wrap_mode +clutter_text_set_max_length +clutter_text_get_max_length +clutter_text_set_selectable +clutter_text_get_selectable +clutter_text_get_selection +clutter_text_set_selection_bound +clutter_text_get_selection_bound +clutter_text_set_text_visible +clutter_text_get_text_visible +clutter_text_set_use_markup +clutter_text_get_use_markup + + +clutter_text_set_editable +clutter_text_get_editable +clutter_text_insert_text +clutter_text_insert_unichar +clutter_text_delete_chars +clutter_text_delete_text +clutter_text_get_chars +clutter_text_set_cursor_color +clutter_text_get_cursor_color +clutter_text_set_cursor_position +clutter_text_get_cursor_position +clutter_text_set_cursor_visible +clutter_text_get_cursor_visible + + +CLUTTER_IS_TEXT +CLUTTER_IS_TEXT_CLASS +CLUTTER_TEXT +CLUTTER_TEXT_CLASS +CLUTTER_TEXT_GET_CLASS +CLUTTER_TYPE_TEXT + + +ClutterTextPrivate +clutter_text_get_type +
diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index 2e660766c..4a30ab962 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -6,7 +6,6 @@ clutter_stage_get_type clutter_rectangle_get_type clutter_texture_get_type clutter_clone_texture_get_type -clutter_label_get_type clutter_timeline_get_type clutter_media_get_type clutter_behaviour_get_type @@ -19,7 +18,6 @@ clutter_path_get_type clutter_behaviour_rotate_get_type clutter_behaviour_scale_get_type clutter_backend_get_type -clutter_entry_get_type clutter_script_get_type clutter_scriptable_get_type clutter_model_get_type @@ -28,3 +26,4 @@ clutter_list_model_get_type clutter_score_get_type clutter_shader_get_type clutter_child_meta_get_type +clutter_text_get_type From 39b575e6b43301354912d18a50b80085e8d92388 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 15 Dec 2008 15:08:12 +0000 Subject: [PATCH 061/147] Remove ClutterLabel and ClutterEntry Both ClutterLabel and ClutterEntry have been deprecated by ClutterText. --- clutter/clutter-entry.c | 1795 --------------------------------------- clutter/clutter-entry.h | 165 ---- clutter/clutter-label.c | 1347 ----------------------------- clutter/clutter-label.h | 128 --- 4 files changed, 3435 deletions(-) delete mode 100644 clutter/clutter-entry.c delete mode 100644 clutter/clutter-entry.h delete mode 100644 clutter/clutter-label.c delete mode 100644 clutter/clutter-label.h diff --git a/clutter/clutter-entry.c b/clutter/clutter-entry.c deleted file mode 100644 index 1ae661eda..000000000 --- a/clutter/clutter-entry.c +++ /dev/null @@ -1,1795 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * Neil Jagdish Patel - * - * Copyright (C) 2006 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:clutter-entry - * @short_description: A single line text entry actor - * - * #ClutterEntry is a #ClutterTexture that allows single line text entry. - * - * #ClutterEntry is available since Clutter 0.4. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "clutter-entry.h" - -#include "clutter-debug.h" -#include "clutter-enum-types.h" -#include "clutter-keysyms.h" -#include "clutter-main.h" -#include "clutter-marshal.h" -#include "clutter-private.h" -#include "clutter-rectangle.h" -#include "clutter-units.h" - -#include "cogl-pango.h" - -#define DEFAULT_FONT_NAME "Sans 10" -#define ENTRY_CURSOR_WIDTH 1 -#define ENTRY_PADDING 5 - -G_DEFINE_TYPE (ClutterEntry, clutter_entry, CLUTTER_TYPE_ACTOR); - -/* Probably move into main */ -static PangoContext *_context = NULL; - -static const ClutterColor default_text_color = { 0, 0, 0, 255 }; - -enum -{ - PROP_0, - - PROP_FONT_NAME, - PROP_TEXT, - PROP_COLOR, - PROP_ALIGNMENT, /* FIXME */ - PROP_POSITION, - PROP_CURSOR, - PROP_TEXT_VISIBLE, - PROP_MAX_LENGTH, - PROP_ENTRY_PADDING, - PROP_X_ALIGN -}; - -enum -{ - TEXT_CHANGED, - CURSOR_EVENT, - ACTIVATE, - - LAST_SIGNAL -}; - -static guint entry_signals[LAST_SIGNAL] = { 0, }; - -#define CLUTTER_ENTRY_GET_PRIVATE(obj) \ -(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ENTRY, ClutterEntryPrivate)) - -struct _ClutterEntryPrivate -{ - PangoContext *context; - PangoFontDescription *desc; - - ClutterColor fgcol; - - gchar *text; - gchar *font_name; - gboolean text_visible; - gunichar priv_char; - - gint extents_width; - gint extents_height; - - gint width; - gint n_chars; /* number of chars */ - gint n_bytes; /* number of bytes */ - - guint alignment : 2; - guint wrap : 1; - guint use_underline : 1; - guint use_markup : 1; - guint ellipsize : 3; - guint single_line_mode : 1; - guint wrap_mode : 3; - gint position; - gint text_x; - gint max_length; - gint entry_padding; - gdouble x_align; - - PangoAttrList *attrs; - PangoAttrList *effective_attrs; - PangoLayout *layout; - gint width_chars; - - ClutterGeometry cursor_pos; - gboolean show_cursor; -}; - -static void -clutter_entry_set_entry_padding (ClutterEntry *entry, - guint padding) -{ - ClutterEntryPrivate *priv = entry->priv; - - if (priv->entry_padding != padding) - { - priv->entry_padding = padding; - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - - g_object_notify (G_OBJECT (entry), "entry-padding"); - } -} - -static void -clutter_entry_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ClutterEntry *entry; - ClutterEntryPrivate *priv; - - entry = CLUTTER_ENTRY (object); - priv = entry->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - clutter_entry_set_font_name (entry, g_value_get_string (value)); - break; - case PROP_TEXT: - clutter_entry_set_text (entry, g_value_get_string (value)); - break; - case PROP_COLOR: - clutter_entry_set_color (entry, clutter_value_get_color (value)); - break; - case PROP_ALIGNMENT: - clutter_entry_set_alignment (entry, g_value_get_enum (value)); - break; - case PROP_POSITION: - clutter_entry_set_cursor_position (entry, g_value_get_int (value)); - break; - case PROP_CURSOR: - clutter_entry_set_visible_cursor (entry, g_value_get_boolean (value)); - break; - case PROP_TEXT_VISIBLE: - clutter_entry_set_visibility (entry, g_value_get_boolean (value)); - break; - case PROP_MAX_LENGTH: - clutter_entry_set_max_length (entry, g_value_get_int (value)); - break; - case PROP_ENTRY_PADDING: - clutter_entry_set_entry_padding (entry, g_value_get_uint (value)); - break; - case PROP_X_ALIGN: - entry->priv->x_align = g_value_get_double (value); - clutter_actor_queue_redraw (CLUTTER_ACTOR (object)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_entry_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ClutterEntry *entry; - ClutterEntryPrivate *priv; - - entry = CLUTTER_ENTRY(object); - priv = entry->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - g_value_set_string (value, priv->font_name); - break; - case PROP_TEXT: - g_value_set_string (value, priv->text); - break; - case PROP_COLOR: - clutter_value_set_color (value, &priv->fgcol); - break; - case PROP_ALIGNMENT: - g_value_set_enum (value, priv->alignment); - break; - case PROP_POSITION: - g_value_set_int (value, priv->position); - break; - case PROP_CURSOR: - g_value_set_boolean (value, priv->show_cursor); - break; - case PROP_TEXT_VISIBLE: - g_value_set_boolean (value, priv->text_visible); - break; - case PROP_MAX_LENGTH: - g_value_set_int (value, priv->max_length); - break; - case PROP_ENTRY_PADDING: - g_value_set_uint (value, priv->entry_padding); - break; - case PROP_X_ALIGN: - g_value_set_double (value, priv->x_align); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_entry_ensure_layout (ClutterEntry *entry, gint width) -{ - ClutterEntryPrivate *priv; - - priv = entry->priv; - - if (!priv->layout) - { - priv->layout = pango_layout_new (_context); - - if (priv->effective_attrs) - pango_layout_set_attributes (priv->layout, priv->effective_attrs); - - pango_layout_set_alignment (priv->layout, priv->alignment); - pango_layout_set_ellipsize (priv->layout, priv->ellipsize); - pango_layout_set_single_paragraph_mode (priv->layout, - priv->single_line_mode); - - pango_layout_set_font_description (priv->layout, priv->desc); - - if (priv->text_visible) - pango_layout_set_text (priv->layout, priv->text, priv->n_bytes); - else - { - GString *str = g_string_sized_new (priv->n_bytes); - gunichar invisible_char; - gchar buf[7]; - gint char_len, i; - - if (priv->priv_char != 0) - invisible_char = priv->priv_char; - else - invisible_char = '*'; - - /* we need to convert the string built of invisible characters - * into UTF-8 for it to be fed to the Pango layout - */ - memset (buf, 0, sizeof (buf)); - char_len = g_unichar_to_utf8 (invisible_char, buf); - - for (i = 0; i < priv->n_chars; i++) - g_string_append_len (str, buf, char_len); - - pango_layout_set_text (priv->layout, str->str, str->len); - - g_string_free (str, TRUE); - } - - if (priv->wrap) - pango_layout_set_wrap (priv->layout, priv->wrap_mode); - - if (priv->wrap && width > 0) - pango_layout_set_width (priv->layout, width * PANGO_SCALE); - else - pango_layout_set_width (priv->layout, -1); - - /* Prime the cache for the layout */ - cogl_pango_ensure_glyph_cache_for_layout (priv->layout); - } -} - -static void -clutter_entry_clear_layout (ClutterEntry *entry) -{ - if (entry->priv->layout) - { - g_object_unref (entry->priv->layout); - entry->priv->layout = NULL; - } -} - -static gint -offset_to_bytes (const gchar *text, gint pos) -{ - gchar *c = NULL; - gint i, j, len; - - if (pos < 1) - return pos; - - c = g_utf8_next_char (text); - j = 1; - len = strlen (text); - - for (i = 0; i < len; i++) - { - if (&text[i] == c) - { - if (j == pos) - break; - else - { - c = g_utf8_next_char (c); - j++; - } - } - } - return i; -} - - -static void -clutter_entry_ensure_cursor_position (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - gint index_; - PangoRectangle rect; - gint priv_char_bytes; - - priv = entry->priv; - - /* If characters are invisible, get the byte-length of the invisible - * character. If priv_char is 0, we use '*', which is ASCII (1 byte). - */ - if (!priv->text_visible && priv->priv_char) - priv_char_bytes = g_unichar_to_utf8 (priv->priv_char, NULL); - else - priv_char_bytes = 1; - - if (priv->position == -1) - { - if (priv->text_visible) - index_ = strlen (priv->text); - else - index_ = priv->n_chars * priv_char_bytes; - } - else - { - if (priv->text_visible) - index_ = offset_to_bytes (priv->text, priv->position); - else - index_ = priv->position * priv_char_bytes; - } - - pango_layout_get_cursor_pos (priv->layout, index_, &rect, NULL); - priv->cursor_pos.x = rect.x / PANGO_SCALE; - priv->cursor_pos.y = rect.y / PANGO_SCALE; - priv->cursor_pos.width = ENTRY_CURSOR_WIDTH; - priv->cursor_pos.height = rect.height / PANGO_SCALE; - - g_signal_emit (entry, entry_signals[CURSOR_EVENT], 0, &priv->cursor_pos); -} - -static void -clutter_entry_clear_cursor_position (ClutterEntry *entry) -{ - entry->priv->cursor_pos.width = 0; -} - -void -clutter_entry_paint_cursor (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - - priv = entry->priv; - - if (priv->show_cursor) - { - cogl_set_source_color4ub (priv->fgcol.red, - priv->fgcol.green, - priv->fgcol.blue, - priv->fgcol.alpha); - - cogl_rectangle (priv->cursor_pos.x, - priv->cursor_pos.y, - priv->cursor_pos.width, - priv->cursor_pos.height); - } -} - -static void -clutter_entry_paint (ClutterActor *self) -{ - ClutterEntry *entry; - ClutterEntryPrivate *priv; - PangoRectangle logical; - gint width, actor_width; - gint text_width; - gint cursor_x; - CoglColor color = { 0, }; - - entry = CLUTTER_ENTRY(self); - priv = entry->priv; - - if (priv->desc == NULL || priv->text == NULL) - { - CLUTTER_NOTE (ACTOR, "layout: %p , desc: %p, text %p", - priv->layout, - priv->desc, - priv->text); - return; - } - - if (priv->width < 0) - width = clutter_actor_get_width (self); - else - width = priv->width; - - cogl_clip_set (0, 0, - COGL_FIXED_FROM_INT (width), - COGL_FIXED_FROM_INT (clutter_actor_get_height (self))); - - actor_width = width - (2 * priv->entry_padding); - clutter_entry_ensure_layout (entry, actor_width); - clutter_entry_ensure_cursor_position (entry); - - pango_layout_get_extents (priv->layout, NULL, &logical); - text_width = logical.width / PANGO_SCALE; - - if (actor_width < text_width) - { - /* We need to do some scrolling */ - cursor_x = priv->cursor_pos.x; - - /* If the cursor is at the begining or the end of the text, the placement - * is easy, however, if the cursor is in the middle somewhere, we need to - * make sure the text doesn't move until the cursor is either in the - * far left or far right - */ - - if (priv->position == 0) - priv->text_x = 0; - else if (priv->position == -1) - { - priv->text_x = actor_width - text_width; - priv->cursor_pos.x += priv->text_x + priv->entry_padding; - } - else - { - if (priv->text_x <= 0) - { - gint diff = -1 * priv->text_x; - - if (cursor_x < diff) - priv->text_x += diff - cursor_x; - else if (cursor_x > (diff + actor_width)) - priv->text_x -= cursor_x - (diff+actor_width); - } - - priv->cursor_pos.x += priv->text_x + priv->entry_padding; - } - - } - else - { - priv->text_x = (actor_width - text_width) * priv->x_align; - priv->cursor_pos.x += priv->text_x + priv->entry_padding; - } - - cogl_color_set_from_4ub (&color, - priv->fgcol.red, - priv->fgcol.green, - priv->fgcol.blue, - clutter_actor_get_paint_opacity (self)); - - cogl_pango_render_layout (priv->layout, - priv->text_x + priv->entry_padding, 0, - &color, 0); - - if (CLUTTER_ENTRY_GET_CLASS (entry)->paint_cursor) - CLUTTER_ENTRY_GET_CLASS (entry)->paint_cursor (entry); - - cogl_clip_unset (); -} - -static void -clutter_entry_allocate (ClutterActor *self, - const ClutterActorBox *box, - gboolean absolute_origin_changed) -{ - ClutterEntry *entry = CLUTTER_ENTRY (self); - ClutterEntryPrivate *priv = entry->priv; - gint width; - - width = CLUTTER_UNITS_TO_DEVICE (box->x2 - box->x1); - - if (priv->width != width) - { - clutter_entry_clear_layout (entry); - clutter_entry_ensure_layout (entry, width); - - priv->width = width; - } - - CLUTTER_ACTOR_CLASS (clutter_entry_parent_class)->allocate (self, box, absolute_origin_changed); -} - -static inline void -clutter_entry_handle_key_event_internal (ClutterEntry *entry, - ClutterKeyEvent *event) -{ - gunichar key_unichar; - ClutterEntryPrivate *priv = entry->priv; - gint pos = priv->position; - gint len = 0; - gint keyval = clutter_key_event_symbol (event); - - if (priv->text) - len = g_utf8_strlen (priv->text, -1); - - switch (keyval) - { - case CLUTTER_Return: - case CLUTTER_KP_Enter: - case CLUTTER_ISO_Enter: - g_signal_emit (entry, entry_signals[ACTIVATE], 0); - break; - - case CLUTTER_Escape: - case CLUTTER_Up: - case CLUTTER_KP_Up: - case CLUTTER_Down: - case CLUTTER_KP_Down: - case CLUTTER_Shift_L: - case CLUTTER_Shift_R: - break; - - case CLUTTER_BackSpace: - if (pos != 0 && len != 0) - clutter_entry_delete_chars (entry, 1); - break; - - case CLUTTER_Delete: - case CLUTTER_KP_Delete: - if (len && pos != -1) - clutter_entry_delete_text (entry, pos, pos+1);; - break; - - case CLUTTER_Left: - case CLUTTER_KP_Left: - if (pos != 0 && len != 0) - { - if (pos == -1) - clutter_entry_set_cursor_position (entry, len - 1); - else - clutter_entry_set_cursor_position (entry, pos - 1); - } - break; - - case CLUTTER_Right: - case CLUTTER_KP_Right: - if (pos != -1 && len != 0) - { - if (pos != len) - clutter_entry_set_cursor_position (entry, pos + 1); - } - break; - - case CLUTTER_End: - case CLUTTER_KP_End: - clutter_entry_set_cursor_position (entry, -1); - break; - - case CLUTTER_Begin: - case CLUTTER_Home: - case CLUTTER_KP_Home: - clutter_entry_set_cursor_position (entry, 0); - break; - - default: - key_unichar = clutter_key_event_unicode (event); - if (g_unichar_validate (key_unichar)) - clutter_entry_insert_unichar (entry, key_unichar); - break; - } -} - -static gboolean -clutter_entry_key_press (ClutterActor *actor, - ClutterKeyEvent *event) -{ - clutter_entry_handle_key_event_internal (CLUTTER_ENTRY (actor), event); - - return TRUE; -} - -static void -clutter_entry_dispose (GObject *object) -{ - ClutterEntry *self = CLUTTER_ENTRY(object); - ClutterEntryPrivate *priv; - - priv = self->priv; - - if (priv->layout) - { - g_object_unref (priv->layout); - priv->layout = NULL; - } - - if (priv->context) - { - g_object_unref (priv->context); - priv->context = NULL; - } - - G_OBJECT_CLASS (clutter_entry_parent_class)->dispose (object); -} - -static void -clutter_entry_finalize (GObject *object) -{ - ClutterEntryPrivate *priv = CLUTTER_ENTRY (object)->priv; - - if (priv->desc) - pango_font_description_free (priv->desc); - - g_free (priv->text); - g_free (priv->font_name); - - G_OBJECT_CLASS (clutter_entry_parent_class)->finalize (object); -} - -static void -clutter_entry_class_init (ClutterEntryClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - GParamSpec *pspec; - - klass->paint_cursor = clutter_entry_paint_cursor; - - actor_class->paint = clutter_entry_paint; - actor_class->allocate = clutter_entry_allocate; - actor_class->key_press_event = clutter_entry_key_press; - - gobject_class->finalize = clutter_entry_finalize; - gobject_class->dispose = clutter_entry_dispose; - gobject_class->set_property = clutter_entry_set_property; - gobject_class->get_property = clutter_entry_get_property; - - /** - * ClutterEntry:font-name: - * - * The font to be used by the entry, expressed in a string that - * can be parsed by pango_font_description_from_string(). - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_FONT_NAME, - g_param_spec_string ("font-name", - "Font Name", - "Pango font description", - NULL, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:text: - * - * The text inside the entry. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_TEXT, - g_param_spec_string ("text", - "Text", - "Text to render", - NULL, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:color: - * - * The color of the text inside the entry. - * - * Since: 0.4 - */ - pspec = clutter_param_spec_color ("color", - "Color", - "The color of the text", - &default_text_color, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_COLOR, pspec); - /** - * ClutterEntry:alignment: - * - * The preferred alignment for the string. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_ALIGNMENT, - g_param_spec_enum ("alignment", - "Alignment", - "The preferred alignment for the string,", - PANGO_TYPE_ALIGNMENT, - PANGO_ALIGN_LEFT, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:position: - * - * The current input cursor position. -1 is taken to be the end of the text - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_POSITION, - g_param_spec_int ("position", - "Position", - "The cursor position", - -1, G_MAXINT, - -1, - CLUTTER_PARAM_READWRITE)); - - /** - * ClutterEntry:cursor-visible: - * - * Whether the input cursor is visible or not. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_CURSOR, - g_param_spec_boolean ( "cursor-visible", - "Cursor Visible", - "Whether the input cursor is visible", - TRUE, - CLUTTER_PARAM_READWRITE)); - - /** - * ClutterEntry:text-visible: - * - * Whether the text is visible in plain form, or replaced by the - * character set by clutter_entry_set_invisible_char(). - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_TEXT_VISIBLE, - g_param_spec_boolean ("text-visible", - "Text Visible", - "Whether the text is visible in plain form", - TRUE, - CLUTTER_PARAM_READWRITE)); - - /** - * ClutterEntry:max-length: - * - * The maximum length of the entry text. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_MAX_LENGTH, - g_param_spec_int ("max-length", - "Max Length", - "The maximum length of the entry text", - 0, G_MAXINT, - 0, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:entry-padding: - * - * The padding space between the text and the entry right and left borders. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_ENTRY_PADDING, - g_param_spec_uint ("entry-padding", - "Entry Padding", - "The padding space between the text and the left and " - "right borders", - 0, G_MAXUINT, - ENTRY_PADDING, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:x-align: - * - * Horizontal alignment to be used for the text (0.0 for left alignment, - * 1.0 for right alignment). - * - * Since: 0.6 - */ - g_object_class_install_property (gobject_class, - PROP_X_ALIGN, - g_param_spec_double ("x-align", - "Horizontal Alignment", - "The horizontal alignment to be used for the text", - 0.0, 1.0, 0.0, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry::text-changed: - * @entry: the actor which received the event - * - * The ::text-changed signal is emitted after @entry's text changes - */ - entry_signals[TEXT_CHANGED] = - g_signal_new ("text-changed", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, text_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - /** - * ClutterEntry::cursor-event: - * @entry: the actor which received the event - * @geometry: a #ClutterGeometry - * - * The ::cursor-event signal is emitted each time the input cursor's geometry - * changes, this could be a positional or size change. If you would like to - * implement your own input cursor, set the cursor-visible property to %FALSE, - * and connect to this signal to position and size your own cursor. - * - * Since: 0.4 - */ - entry_signals[CURSOR_EVENT] = - g_signal_new ("cursor-event", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, cursor_event), - NULL, NULL, - clutter_marshal_VOID__BOXED, - G_TYPE_NONE, 1, - CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE); - - /** - * ClutterEntry::activate: - * @entry: the actor which received the event - * - * The ::activate signal is emitted each time the entry is 'activated' - * by the user, normally by pressing the 'Enter' key. - * - * Since: 0.4 - */ - entry_signals[ACTIVATE] = - g_signal_new ("activate", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, activate), - NULL, NULL, - clutter_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - g_type_class_add_private (gobject_class, sizeof (ClutterEntryPrivate)); -} - -static void -clutter_entry_init (ClutterEntry *self) -{ - ClutterEntryPrivate *priv; - gdouble resolution; - gint font_size; - - self->priv = priv = CLUTTER_ENTRY_GET_PRIVATE (self); - - if (G_UNLIKELY (_context == NULL)) - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - - resolution = pango_cairo_context_get_resolution (_context); - - priv->alignment = PANGO_ALIGN_LEFT; - priv->wrap = FALSE; - priv->wrap_mode = PANGO_WRAP_WORD; - priv->ellipsize = PANGO_ELLIPSIZE_NONE; - priv->use_underline = FALSE; - priv->use_markup = FALSE; - priv->layout = NULL; - priv->text = NULL; - priv->attrs = NULL; - priv->position = -1; - priv->priv_char = '*'; - priv->text_visible = TRUE; - priv->text_x = 0; - priv->max_length = 0; - priv->entry_padding = ENTRY_PADDING; - priv->x_align = 0.0; - - priv->fgcol = default_text_color; - - priv->font_name = g_strdup (DEFAULT_FONT_NAME); - priv->desc = pango_font_description_from_string (priv->font_name); - - /* we use the font size to set the default width and height, in case - * the user doesn't call clutter_actor_set_size(). - */ - font_size = PANGO_PIXELS (pango_font_description_get_size (priv->desc)) - * resolution - / 72.0; - clutter_actor_set_size (CLUTTER_ACTOR (self), font_size * 20, 50); - - priv->show_cursor = TRUE; -} - -/** - * clutter_entry_new_with_text: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * - * Creates a new #ClutterEntry displaying @text using @font_name. - * - * Return value: the newly created #ClutterEntry - * - * Since: 0.4 - */ -ClutterActor * -clutter_entry_new_with_text (const gchar *font_name, - const gchar *text) -{ - ClutterActor *entry = clutter_entry_new (); - - g_object_set (entry, - "font-name", font_name, - "text", text, - NULL); - return entry; -} - -/** - * clutter_entry_new_full: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * @color: #ClutterColor for text - * - * Creates a new #ClutterEntry displaying @text with @color - * using @font_name. - * - * Return value: the newly created #ClutterEntry - * - * Since: 0.4 - */ -ClutterActor * -clutter_entry_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color) -{ - ClutterActor *entry; - - entry = clutter_entry_new_with_text (font_name, text); - clutter_entry_set_color (CLUTTER_ENTRY(entry), color); - - return entry; -} - -/** - * clutter_entry_new: - * - * Creates a new, empty #ClutterEntry. - * - * Returns: the newly created #ClutterEntry - */ -ClutterActor * -clutter_entry_new (void) -{ - ClutterActor *entry = g_object_new (CLUTTER_TYPE_ENTRY, - NULL); - clutter_actor_set_size (entry, 50, 50); - - return entry; -} - -/** - * clutter_entry_get_text: - * @entry: a #ClutterEntry - * - * Retrieves the text displayed by @entry. - * - * Return value: the text of the entry. The returned string is - * owned by #ClutterEntry and should not be modified or freed. - * - * Since: 0.4 - */ -G_CONST_RETURN gchar * -clutter_entry_get_text (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); - - return entry->priv->text; -} - -/** - * clutter_entry_set_text: - * @entry: a #ClutterEntry - * @text: the text to be displayed - * - * Sets @text as the text to be displayed by @entry. The - * ClutterEntry::text-changed signal is emitted. - * - * Since: 0.4 - */ -void -clutter_entry_set_text (ClutterEntry *entry, - const gchar *text) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (text != NULL); - - priv = entry->priv; - - g_object_ref (entry); - - if (priv->max_length > 0) - { - gint len = g_utf8_strlen (text, -1); - - if (len < priv->max_length) - { - g_free (priv->text); - - priv->text = g_strdup (text); - priv->n_bytes = strlen (text); - priv->n_chars = len; - } - else - { - gchar * n = g_malloc0 (priv->max_length + 1); - - g_utf8_strncpy (n, text, priv->max_length); - g_free (priv->text); - - priv->text = n; - priv->n_bytes = strlen (n); - priv->n_chars = priv->max_length; - } - } - else - { - g_free (priv->text); - - priv->text = g_strdup (text); - priv->n_bytes = strlen (text); - priv->n_chars = g_utf8_strlen (priv->text, -1); - } - - clutter_entry_clear_layout (entry); - clutter_entry_clear_cursor_position (entry); - /* Recreate the layout so the glyph cache will be primed */ - clutter_entry_ensure_layout (entry, -1); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - - g_signal_emit (G_OBJECT (entry), entry_signals[TEXT_CHANGED], 0); - - g_object_notify (G_OBJECT (entry), "text"); - g_object_unref (entry); -} - -/** - * clutter_entry_get_font_name: - * @entry: a #ClutterEntry - * - * Retrieves the font used by @entry. - * - * Return value: a string containing the font name, in a format - * understandable by pango_font_description_from_string(). The - * string is owned by #ClutterEntry and should not be modified - * or freed. - * - * Since: 0.4 - */ -G_CONST_RETURN gchar * -clutter_entry_get_font_name (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); - - return entry->priv->font_name; -} - -/** - * clutter_entry_set_font_name: - * @entry: a #ClutterEntry - * @font_name: a font name and size, or %NULL for the default font - * - * Sets @font_name as the font used by @entry. - * - * @font_name must be a string containing the font name and its - * size, similarly to what you would feed to the - * pango_font_description_from_string() function. - * - * Since: 0.4 - */ -void -clutter_entry_set_font_name (ClutterEntry *entry, - const gchar *font_name) -{ - ClutterEntryPrivate *priv; - PangoFontDescription *desc; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - if (!font_name || font_name[0] == '\0') - font_name = DEFAULT_FONT_NAME; - - priv = entry->priv; - - if (strcmp (priv->font_name, font_name) == 0) - return; - - desc = pango_font_description_from_string (font_name); - if (!desc) - { - g_warning ("Attempting to create a PangoFontDescription for " - "font name `%s', but failed.", - font_name); - return; - } - - g_object_ref (entry); - - g_free (priv->font_name); - priv->font_name = g_strdup (font_name); - - if (priv->desc) - pango_font_description_free (priv->desc); - - priv->desc = desc; - - if (entry->priv->text && entry->priv->text[0] != '\0') - { - clutter_entry_clear_layout (entry); - /* Recreate the layout so the glyph cache will be primed */ - clutter_entry_ensure_layout (entry, -1); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - } - - g_object_notify (G_OBJECT (entry), "font-name"); - g_object_unref (entry); -} - - -/** - * clutter_entry_set_color: - * @entry: a #ClutterEntry - * @color: a #ClutterColor - * - * Sets the color of @entry. - * - * Since: 0.4 - */ -void -clutter_entry_set_color (ClutterEntry *entry, - const ClutterColor *color) -{ - ClutterActor *actor; - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (color != NULL); - - priv = entry->priv; - - g_object_ref (entry); - - priv->fgcol.red = color->red; - priv->fgcol.green = color->green; - priv->fgcol.blue = color->blue; - priv->fgcol.alpha = color->alpha; - - actor = CLUTTER_ACTOR (entry); - - clutter_actor_set_opacity (actor, priv->fgcol.alpha); - - if (CLUTTER_ACTOR_IS_VISIBLE (actor)) - clutter_actor_queue_redraw (actor); - - g_object_notify (G_OBJECT (entry), "color"); - g_object_unref (entry); -} - -/** - * clutter_entry_get_color: - * @entry: a #ClutterEntry - * @color: return location for a #ClutterColor - * - * Retrieves the color of @entry. - * - * Since: 0.4 - */ -void -clutter_entry_get_color (ClutterEntry *entry, - ClutterColor *color) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (color != NULL); - - priv = entry->priv; - - color->red = priv->fgcol.red; - color->green = priv->fgcol.green; - color->blue = priv->fgcol.blue; - color->alpha = priv->fgcol.alpha; -} - -/** - * clutter_entry_get_layout: - * @entry: a #ClutterEntry - * - * Gets the #PangoLayout used to display the entry. - * The layout is useful to e.g. convert text positions to - * pixel positions. - * The returned layout is owned by the entry so need not be - * freed by the caller. - * - * Return value: the #PangoLayout for this entry - * - * Since: 0.4 - **/ -PangoLayout * -clutter_entry_get_layout (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); - - clutter_entry_ensure_layout (entry, -1); - - return entry->priv->layout; -} - -/** - * clutter_entry_set_alignment: - * @entry: a #ClutterEntry - * @alignment: A #PangoAlignment - * - * Sets text alignment of the entry. - * - * Since: 0.4 - */ -void -clutter_entry_set_alignment (ClutterEntry *entry, - PangoAlignment alignment) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->alignment != alignment) - { - g_object_ref (entry); - - priv->alignment = alignment; - - clutter_entry_clear_layout (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - - g_object_notify (G_OBJECT (entry), "alignment"); - g_object_unref (entry); - } -} - -/** - * clutter_entry_get_alignment: - * @entry: a #ClutterEntry - * - * Returns the entry's text alignment - * - * Return value: The entry's #PangoAlignment - * - * Since: 0.4 - */ -PangoAlignment -clutter_entry_get_alignment (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), FALSE); - - return entry->priv->alignment; -} - -/** - * clutter_entry_set_cursor_position: - * @entry: a #ClutterEntry - * @position: the position of the cursor. - * - * Sets the position of the cursor. The @position must be less than or - * equal to the number of characters in the entry. A value of -1 indicates - * that the position should be set after the last character in the entry. - * Note that this position is in characters, not in bytes. - * - * Since: 0.6 - */ -void -clutter_entry_set_cursor_position (ClutterEntry *entry, - gint position) -{ - ClutterEntryPrivate *priv; - gint len; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->text == NULL) - return; - - len = g_utf8_strlen (priv->text, -1); - - if (position < 0 || position >= len) - priv->position = -1; - else - priv->position = position; - - clutter_entry_clear_cursor_position (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); -} - -/** - * clutter_entry_get_cursor_position: - * @entry: a #ClutterEntry - * - * Gets the position, in characters, of the cursor in @entry. - * - * Return value: the position of the cursor. - * - * Since: 0.6 - */ -gint -clutter_entry_get_cursor_position (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), 0); - - priv = entry->priv; - - return priv->position; -} - -/** - * clutter_entry_handle_key_event: - * @entry: a #ClutterEntry - * @kev: a #ClutterKeyEvent - * - * This function will handle a #ClutterKeyEvent, like those returned in a - * key-press/release-event, and will translate it for the @entry. This includes - * non-alphanumeric keys, such as the arrows keys, which will move the - * input cursor. You should use this function inside a handler for the - * ClutterStage::key-press-event or ClutterStage::key-release-event. - * - * Since: 0.4 - * - * Deprecated: 0.8: The key events will automatically be handled when - * giving the key focus to an entry using clutter_stage_set_key_focus(). - */ -void -clutter_entry_handle_key_event (ClutterEntry *entry, - ClutterKeyEvent *kev) -{ - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - clutter_entry_handle_key_event_internal (entry, kev); -} - -/** - * clutter_entry_insert_unichar: - * @entry: a #ClutterEntry - * @wc: a Unicode character - * - * Insert a character to the right of the current position of the cursor, - * and updates the position of the cursor. - * - * Since: 0.4 - */ -void -clutter_entry_insert_unichar (ClutterEntry *entry, - gunichar wc) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - glong pos; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (g_unichar_validate (wc)); - - if (wc == 0) - return; - - priv = entry->priv; - - g_object_ref (entry); - - new = g_string_new (priv->text); - pos = offset_to_bytes (priv->text, priv->position); - new = g_string_insert_unichar (new, pos, wc); - - clutter_entry_set_text (entry, new->str); - - if (priv->position >= 0) - clutter_entry_set_cursor_position (entry, priv->position + 1); - - g_string_free (new, TRUE); - - g_object_notify (G_OBJECT (entry), "text"); - g_object_unref (entry); -} - -/** - * clutter_entry_delete_chars: - * @entry: a #ClutterEntry - * @len: the number of characters to remove. - * - * Characters are removed from before the current postion of the cursor. - * - * Since: 0.4 - */ -void -clutter_entry_delete_chars (ClutterEntry *entry, - guint num) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - gint len; - gint pos; - gint num_pos; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (!priv->text) - return; - - g_object_ref (entry); - - len = g_utf8_strlen (priv->text, -1); - new = g_string_new (priv->text); - - if (priv->position == -1) - { - num_pos = offset_to_bytes (priv->text, len - num); - new = g_string_erase (new, num_pos, -1); - } - else - { - pos = offset_to_bytes (priv->text, priv->position - num); - num_pos = offset_to_bytes (priv->text, priv->position); - new = g_string_erase (new, pos, num_pos-pos); - } - clutter_entry_set_text (entry, new->str); - - if (priv->position > 0) - clutter_entry_set_cursor_position (entry, priv->position - num); - - g_string_free (new, TRUE); - - g_object_notify (G_OBJECT (entry), "text"); - g_object_unref (entry); -} - -/** - * clutter_entry_insert_text: - * @entry: a #ClutterEntry - * @text: the text to insert - * @position: the position at which to insert the text. - * - * Insert text at a specifc position. - * - * A value of 0 indicates that the text will be inserted before the first - * character in the entry's text, and a value of -1 indicates that the text - * will be inserted after the last character in the entry's text. - * - * Since: 0.4 - */ -void -clutter_entry_insert_text (ClutterEntry *entry, - const gchar *text, - gssize position) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - new = g_string_new (priv->text); - new = g_string_insert (new, position, text); - - clutter_entry_set_text (entry, new->str); - - g_string_free (new, TRUE); -} - -/** - * clutter_entry_delete_text: - * @entry: a #ClutterEntry - * @start_pos: the starting position. - * @end_pos: the end position. - * - * Deletes a sequence of characters. The characters that are deleted are - * those characters at positions from @start_pos up to, but not including, - * @end_pos. If @end_pos is negative, then the characters deleted will be - * those characters from @start_pos to the end of the text. - * - * Since: 0.4 - */ -void -clutter_entry_delete_text (ClutterEntry *entry, - gssize start_pos, - gssize end_pos) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - gint start_bytes; - gint end_bytes; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (!priv->text) - return; - - start_bytes = offset_to_bytes (priv->text, start_pos); - end_bytes = offset_to_bytes (priv->text, end_pos); - - new = g_string_new (priv->text); - new = g_string_erase (new, start_bytes, end_bytes - start_bytes); - - clutter_entry_set_text (entry, new->str); - - g_string_free (new, TRUE); -} - -/** - * clutter_entry_set_visible_cursor: - * @entry: a #ClutterEntry - * @visible: whether the input cursor should be visible - * - * Sets the visibility of the input cursor. - * - * Since: 0.4 - */ -void -clutter_entry_set_visible_cursor (ClutterEntry *entry, - gboolean visible) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->show_cursor != visible) - { - priv->show_cursor = visible; - - g_object_notify (G_OBJECT (entry), "cursor-visible"); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - } -} - -/** - * clutter_entry_get_visible_cursor: - * @entry: a #ClutterEntry - * - * Returns the input cursor's visibility - * - * Return value: whether the input cursor is visible - * - * Since: 0.4 - */ -gboolean -clutter_entry_get_visible_cursor (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), FALSE); - - return entry->priv->show_cursor; -} - -/** - * clutter_entry_set_visibility: - * @entry: a #ClutterEntry - * @visible: %TRUE if the contents of the entry are displayed as plaintext. - * - * Sets whether the contents of the entry are visible or not. When visibility - * is set to %FALSE, characters are displayed as the invisible char, and will - * also appear that way when the text in the entry widget is copied elsewhere. - * - * The default invisible char is the asterisk '*', but it can be changed with - * clutter_entry_set_invisible_char(). - * - * Since: 0.4 - */ -void -clutter_entry_set_visibility (ClutterEntry *entry, gboolean visible) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - priv->text_visible = visible; - - clutter_entry_clear_layout (entry); - clutter_entry_clear_cursor_position (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); -} - -/** - * clutter_entry_get_visibility: - * @entry: a #ClutterEntry - * - * Returns the entry text visibility. - * - * Return value: %TRUE if the contents of the entry are displayed as plaintext. - * - * Since: 0.4 - */ -gboolean -clutter_entry_get_visibility (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), TRUE); - - return entry->priv->text_visible; -} - -/** - * clutter_entry_set_invisible_char: - * @entry: a #ClutterEntry - * @wc: a Unicode character - * - * Sets the character to use in place of the actual text when - * clutter_entry_set_visibility() has been called to set text visibility - * to %FALSE. i.e. this is the character used in "password mode" to show the - * user how many characters have been typed. The default invisible char is an - * asterisk ('*'). If you set the invisible char to 0, then the user will get - * no feedback at all; there will be no text on the screen as they type. - * - * Since: 0.4 - */ -void -clutter_entry_set_invisible_char (ClutterEntry *entry, gunichar wc) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - priv->priv_char = wc; - - if (!priv->text_visible) - return; - - clutter_entry_clear_layout (entry); - clutter_entry_clear_cursor_position (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); -} - -/** - * clutter_entry_get_invisible_char: - * @entry: a #ClutterEntry - * - * Returns the character to use in place of the actual text when text-visibility - * is set to %FALSE - * - * Return value: a Unicode character - * - **/ -gunichar -clutter_entry_get_invisible_char (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), TRUE); - - priv = entry->priv; - - return priv->priv_char; -} - -/** - * clutter_entry_set_max_length: - * @entry: a #ClutterEntry - * @max: the maximum number of characters allowed in the entry; 0 - * to disable or -1 to set the length of the current string - * - * Sets the maximum allowed length of the contents of the actor. If the - * current contents are longer than the given length, then they will be - * truncated to fit. - * - * Since: 0.4 - */ -void -clutter_entry_set_max_length (ClutterEntry *entry, - gint max) -{ - ClutterEntryPrivate *priv; - gchar *new = NULL; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->max_length != max) - { - g_object_ref (entry); - - if (max < 0) - max = g_utf8_strlen (priv->text, -1); - - priv->max_length = max; - - new = g_strdup (priv->text); - clutter_entry_set_text (entry, new); - g_free (new); - - g_object_notify (G_OBJECT (entry), "max-length"); - g_object_unref (entry); - } -} - -/** - * clutter_entry_get_max_length: - * @entry: a #ClutterEntry - * - * Gets the maximum length of text that can be set into @entry. - * See clutter_entry_set_max_length(). - * - * Return value: the maximum number of characters. - * - * Since: 0.4 - */ -gint -clutter_entry_get_max_length (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), -1); - - return entry->priv->max_length; -} diff --git a/clutter/clutter-entry.h b/clutter/clutter-entry.h deleted file mode 100644 index b43408522..000000000 --- a/clutter/clutter-entry.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * Neil Jagdish Patel . - */ - -#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __CLUTTER_ENTRY_H__ -#define __CLUTTER_ENTRY_H__ - -#include -#include -#include -#include - - -G_BEGIN_DECLS - -#define CLUTTER_TYPE_ENTRY (clutter_entry_get_type ()) - -#define CLUTTER_ENTRY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_ENTRY, ClutterEntry)) - -#define CLUTTER_ENTRY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_ENTRY, ClutterEntryClass)) - -#define CLUTTER_IS_ENTRY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_ENTRY)) - -#define CLUTTER_IS_ENTRY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_ENTRY)) - -#define CLUTTER_ENTRY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_ENTRY, ClutterEntryClass)) - -typedef struct _ClutterEntry ClutterEntry; -typedef struct _ClutterEntryClass ClutterEntryClass; -typedef struct _ClutterEntryPrivate ClutterEntryPrivate; - -struct _ClutterEntry -{ - /*< private >*/ - ClutterActor parent_instance; - - ClutterEntryPrivate *priv; -}; - -/** - * ClutterEntryClass: - * @paint_cursor: virtual function for subclasses to use to draw a custom - * cursor instead of the default one - * @text_changed: signal class handler for ClutterEntry::text-changed - * @cursor_event: signal class handler for ClutterEntry::cursor-event - * @activate: signal class handler for ClutterEntry::activate - * - * Class fo entry actors. - * - * Since: 0.4 - */ -struct _ClutterEntryClass -{ - /*< private >*/ - ClutterActorClass parent_class; - - /*< public >*/ - /* vfuncs, not signals */ - void (* paint_cursor) (ClutterEntry *entry); - - /* signals */ - void (* text_changed) (ClutterEntry *entry); - void (* cursor_event) (ClutterEntry *entry, - ClutterGeometry *geometry); - void (* activate) (ClutterEntry *entry); - - /*< private >*/ - /* padding for future */ - void (*_clutter_entry_1) (void); - void (*_clutter_entry_2) (void); - void (*_clutter_entry_3) (void); - void (*_clutter_entry_4) (void); -}; - -GType clutter_entry_get_type (void) G_GNUC_CONST; - -ClutterActor * clutter_entry_new (void); -ClutterActor * clutter_entry_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color); -ClutterActor * clutter_entry_new_with_text (const gchar *font_name, - const gchar *text); -void clutter_entry_set_text (ClutterEntry *entry, - const gchar *text); -G_CONST_RETURN gchar *clutter_entry_get_text (ClutterEntry *entry); -void clutter_entry_set_font_name (ClutterEntry *entry, - const gchar *font_name); -G_CONST_RETURN gchar *clutter_entry_get_font_name (ClutterEntry *entry); -void clutter_entry_set_color (ClutterEntry *entry, - const ClutterColor *color); -void clutter_entry_get_color (ClutterEntry *entry, - ClutterColor *color); -PangoLayout * clutter_entry_get_layout (ClutterEntry *entry); -void clutter_entry_set_alignment (ClutterEntry *entry, - PangoAlignment alignment); -PangoAlignment clutter_entry_get_alignment (ClutterEntry *entry); -void clutter_entry_set_cursor_position (ClutterEntry *entry, - gint position); -gint clutter_entry_get_cursor_position (ClutterEntry *entry); -void clutter_entry_insert_unichar (ClutterEntry *entry, - gunichar wc); -void clutter_entry_delete_chars (ClutterEntry *entry, - guint len); -void clutter_entry_insert_text (ClutterEntry *entry, - const gchar *text, - gssize position); -void clutter_entry_delete_text (ClutterEntry *entry, - gssize start_pos, - gssize end_pos); -void clutter_entry_set_visible_cursor (ClutterEntry *entry, - gboolean visible); -gboolean clutter_entry_get_visible_cursor (ClutterEntry *entry); - -void clutter_entry_set_visibility (ClutterEntry *entry, - gboolean visible); -gboolean clutter_entry_get_visibility (ClutterEntry *entry); -void clutter_entry_set_invisible_char (ClutterEntry *entry, - gunichar wc); -gunichar clutter_entry_get_invisible_char (ClutterEntry *entry); -void clutter_entry_set_max_length (ClutterEntry *entry, - gint max); -gint clutter_entry_get_max_length (ClutterEntry *entry); - -#ifndef CLUTTER_DISABLE_DEPRECATED -void clutter_entry_handle_key_event (ClutterEntry *entry, - ClutterKeyEvent *kev); -#endif - -G_END_DECLS - -#endif /* __CLUTTER_ENTRY_H__ */ diff --git a/clutter/clutter-label.c b/clutter/clutter-label.c deleted file mode 100644 index 74434e246..000000000 --- a/clutter/clutter-label.c +++ /dev/null @@ -1,1347 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * - * Copyright (C) 2006 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:clutter-label - * @short_description: Actor for displaying text - * - * #ClutterLabel is a #ClutterActor that displays text using Pango. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "clutter-label.h" -#include "clutter-main.h" -#include "clutter-enum-types.h" -#include "clutter-private.h" -#include "clutter-debug.h" -#include "clutter-units.h" - -#include "cogl-pango.h" - -#define DEFAULT_FONT_NAME "Sans 10" - -G_DEFINE_TYPE (ClutterLabel, clutter_label, CLUTTER_TYPE_ACTOR) - -/* Probably move into main */ -static PangoContext *_context = NULL; - -static const ClutterColor default_text_color = { 0, 0, 0, 255 }; - -enum -{ - PROP_0, - PROP_FONT_NAME, - PROP_TEXT, - PROP_COLOR, - PROP_ATTRIBUTES, - PROP_USE_MARKUP, - PROP_ALIGNMENT, - PROP_WRAP, - PROP_WRAP_MODE, - PROP_JUSTIFY, - PROP_ELLIPSIZE, -}; - -#define CLUTTER_LABEL_GET_PRIVATE(obj) \ -(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_LABEL, ClutterLabelPrivate)) - -typedef struct _ClutterLabelCachedLayout ClutterLabelCachedLayout; - -struct _ClutterLabelCachedLayout -{ - /* Cached layout. Pango internally caches the computed extents when - they are requested so there is no need to cache that as well */ - PangoLayout *layout; - /* The width that used to generate this layout */ - ClutterUnit width; - /* A number representing the age of this cache (so that when a new - layout is needed the last used cache is replaced) */ - guint age; -}; - -/* We need at least three cached layouts to run the allocation without - regenerating a new layout. First the layout will be generated at - full width to get the preferred width, then it will be generated at - the preferred width to get the preferred height and then it might - be regenerated at a different width to get the height for the - actual allocated width */ -#define CLUTTER_LABEL_N_CACHED_LAYOUTS 3 - -struct _ClutterLabelPrivate -{ - PangoFontDescription *font_desc; - - ClutterColor fgcol; - - gchar *text; - gchar *font_name; - - PangoAttrList *attrs; - PangoAttrList *effective_attrs; - - ClutterLabelCachedLayout cached_layouts[CLUTTER_LABEL_N_CACHED_LAYOUTS]; - guint cache_age; - - guint alignment : 2; - guint wrap : 1; - guint use_underline : 1; - guint use_markup : 1; - guint ellipsize : 3; - guint single_line_mode : 1; - guint wrap_mode : 3; - guint justify : 1; -}; - -/* - * clutter_label_create_layout_no_cache: - * @label: a #ClutterLabel - * @allocation_width: the width of the layout, or -1 - * - * Creates a new #PangoLayout for the given @allocation_width, using - * the layout properties of the @label. - * - * This function will not touch the glyphs cache. - * - * This function should be used by clutter_label_get_preferred_width() - * and clutter_label_get_preferred_height(). - */ -static PangoLayout * -clutter_label_create_layout_no_cache (ClutterLabel *label, - ClutterUnit allocation_width) -{ - ClutterLabelPrivate *priv = label->priv; - PangoLayout *layout; - - layout = pango_layout_new (_context); - - if (priv->effective_attrs) - pango_layout_set_attributes (layout, priv->effective_attrs); - - pango_layout_set_alignment (layout, priv->alignment); - pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode); - - pango_layout_set_font_description (layout, priv->font_desc); - pango_layout_set_justify (layout, priv->justify); - - if (priv->text) - { - if (!priv->use_markup) - pango_layout_set_text (layout, priv->text, -1); - else - pango_layout_set_markup (layout, priv->text, -1); - } - - if (allocation_width > 0 && - (priv->ellipsize != PANGO_ELLIPSIZE_NONE || priv->wrap)) - { - int layout_width, layout_height; - - pango_layout_get_size (layout, &layout_width, &layout_height); - - /* No need to set ellipsize or wrap if we already have enough - * space, since we don't want to make the layout wider than it - * would be otherwise. - */ - - if (CLUTTER_UNITS_FROM_PANGO_UNIT (layout_width) > allocation_width) - { - if (priv->ellipsize != PANGO_ELLIPSIZE_NONE) - { - gint width; - - width = allocation_width > 0 - ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) - : -1; - - pango_layout_set_ellipsize (layout, priv->ellipsize); - pango_layout_set_width (layout, width); - } - else if (priv->wrap) - { - gint width; - - width = allocation_width > 0 - ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) - : -1; - - pango_layout_set_wrap (layout, priv->wrap_mode); - pango_layout_set_width (layout, width); - } - } - } - - return layout; -} - -static void -clutter_label_dirty_cache (ClutterLabel *label) -{ - ClutterLabelPrivate *priv = label->priv; - int i; - - /* Delete the cached layouts so they will be recreated the next time - they are needed */ - for (i = 0; i < CLUTTER_LABEL_N_CACHED_LAYOUTS; i++) - if (priv->cached_layouts[i].layout) - { - g_object_unref (priv->cached_layouts[i].layout); - priv->cached_layouts[i].layout = NULL; - } -} - -/* - * clutter_label_create_layout: - * @label: a #ClutterLabel - * @allocation_width: the allocation width - * - * Like clutter_label_create_layout_no_cache(), but will also ensure - * the glyphs cache. If a previously cached layout generated using the - * same width is available then that will be used instead of - * generating a new one. - */ -static PangoLayout * -clutter_label_create_layout (ClutterLabel *label, - ClutterUnit allocation_width) -{ - ClutterLabelPrivate *priv = label->priv; - int i; - ClutterLabelCachedLayout *oldest_cache = priv->cached_layouts; - gboolean found_free_cache = FALSE; - - /* Search for a cached layout with the same width and keep track of - the oldest one */ - for (i = 0; i < CLUTTER_LABEL_N_CACHED_LAYOUTS; i++) - { - if (priv->cached_layouts[i].layout == NULL) - { - /* Always prefer free cache spaces */ - found_free_cache = TRUE; - oldest_cache = priv->cached_layouts + i; - } - /* If this cached layout is using the same width then we can - just return that directly */ - else if (priv->cached_layouts[i].width == allocation_width) - { - CLUTTER_NOTE (ACTOR, "ClutterLabel: %p: cache hit for width %i", - label, CLUTTER_UNITS_TO_DEVICE (allocation_width)); - return priv->cached_layouts[i].layout; - } - else if (!found_free_cache && (priv->cached_layouts[i].age - < oldest_cache->age)) - oldest_cache = priv->cached_layouts + i; - } - - CLUTTER_NOTE (ACTOR, "ClutterLabel: %p: cache miss for width %i", - label, CLUTTER_UNITS_TO_DEVICE (allocation_width)); - - /* If we make it here then we didn't have a cached version so we - need to recreate the layout */ - if (oldest_cache->layout) - g_object_unref (oldest_cache->layout); - - oldest_cache->layout - = clutter_label_create_layout_no_cache (label, allocation_width); - - cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout); - - /* Mark the 'time' this cache was created and advance the time */ - oldest_cache->age = priv->cache_age++; - - oldest_cache->width = allocation_width; - - return oldest_cache->layout; -} - -static void -clutter_label_paint (ClutterActor *self) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - ClutterLabelPrivate *priv = label->priv; - PangoLayout *layout; - ClutterActorBox alloc = { 0, }; - CoglColor color = { 0, }; - - if (priv->font_desc == NULL || priv->text == NULL) - { - CLUTTER_NOTE (ACTOR, "desc: %p, text %p", - priv->font_desc ? priv->font_desc : 0x0, - priv->text ? priv->text : 0x0); - return; - } - - CLUTTER_NOTE (PAINT, "painting label (text:`%s')", priv->text); - - clutter_actor_get_allocation_box (self, &alloc); - layout = clutter_label_create_layout (label, alloc.x2 - alloc.x1); - - cogl_color_set_from_4ub (&color, - priv->fgcol.red, - priv->fgcol.green, - priv->fgcol.blue, - clutter_actor_get_paint_opacity (self)); - - cogl_pango_render_layout (layout, 0, 0, &color, 0); -} - -static void -clutter_label_get_preferred_width (ClutterActor *self, - ClutterUnit for_height, - ClutterUnit *min_width_p, - ClutterUnit *natural_width_p) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - ClutterLabelPrivate *priv = label->priv; - PangoRectangle logical_rect = { 0, }; - PangoLayout *layout; - ClutterUnit layout_width; - - layout = clutter_label_create_layout (label, -1); - - pango_layout_get_extents (layout, NULL, &logical_rect); - - layout_width = logical_rect.width > 0 - ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.width) - : 1; - - if (min_width_p) - { - if (priv->wrap || priv->ellipsize) - *min_width_p = 1; - else - *min_width_p = layout_width; - } - - if (natural_width_p) - *natural_width_p = layout_width; -} - -static void -clutter_label_get_preferred_height (ClutterActor *self, - ClutterUnit for_width, - ClutterUnit *min_height_p, - ClutterUnit *natural_height_p) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - - if (for_width == 0) - { - if (min_height_p) - *min_height_p = 0; - - if (natural_height_p) - *natural_height_p = 0; - } - else - { - PangoLayout *layout; - PangoRectangle logical_rect = { 0, }; - ClutterUnit height; - - layout = clutter_label_create_layout (label, for_width); - - pango_layout_get_extents (layout, NULL, &logical_rect); - height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.height); - - if (min_height_p) - *min_height_p = height; - - if (natural_height_p) - *natural_height_p = height; - } -} - -static void -clutter_label_allocate (ClutterActor *self, - const ClutterActorBox *box, - gboolean origin_changed) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - ClutterActorClass *parent_class; - - /* Ensure that there is a cached label with the right width so that - we don't need to create the label during the paint run */ - clutter_label_create_layout (label, box->x2 - box->x1); - - parent_class = CLUTTER_ACTOR_CLASS (clutter_label_parent_class); - parent_class->allocate (self, box, origin_changed); -} - -static void -clutter_label_dispose (GObject *object) -{ - ClutterLabel *self = CLUTTER_LABEL(object); - ClutterLabelPrivate *priv; - - priv = self->priv; - - /* Get rid of the cached layouts */ - clutter_label_dirty_cache (self); - - G_OBJECT_CLASS (clutter_label_parent_class)->dispose (object); -} - -static void -clutter_label_finalize (GObject *object) -{ - ClutterLabel *self = CLUTTER_LABEL(object); - ClutterLabelPrivate *priv; - - priv = self->priv; - - if (priv->font_desc) - pango_font_description_free (priv->font_desc); - - g_free (priv->text); - g_free (priv->font_name); - - G_OBJECT_CLASS (clutter_label_parent_class)->finalize (object); -} - -static void -clutter_label_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ClutterLabel *label; - ClutterLabelPrivate *priv; - - label = CLUTTER_LABEL(object); - priv = label->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - clutter_label_set_font_name (label, g_value_get_string (value)); - break; - case PROP_TEXT: - clutter_label_set_text (label, g_value_get_string (value)); - break; - case PROP_COLOR: - clutter_label_set_color (label, clutter_value_get_color (value)); - break; - case PROP_ATTRIBUTES: - clutter_label_set_attributes (label, g_value_get_boxed (value)); - break; - case PROP_ALIGNMENT: - clutter_label_set_alignment (label, g_value_get_enum (value)); - break; - case PROP_USE_MARKUP: - clutter_label_set_use_markup (label, g_value_get_boolean (value)); - break; - case PROP_WRAP: - clutter_label_set_line_wrap (label, g_value_get_boolean (value)); - break; - case PROP_JUSTIFY: - clutter_label_set_justify (label, g_value_get_boolean (value)); - break; - case PROP_WRAP_MODE: - clutter_label_set_line_wrap_mode (label, g_value_get_enum (value)); - break; - case PROP_ELLIPSIZE: - clutter_label_set_ellipsize (label, g_value_get_enum (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_label_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ClutterLabel *label; - ClutterLabelPrivate *priv; - - label = CLUTTER_LABEL (object); - priv = label->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - g_value_set_string (value, priv->font_name); - break; - case PROP_TEXT: - g_value_set_string (value, priv->text); - break; - case PROP_COLOR: - clutter_value_set_color (value, &priv->fgcol); - break; - case PROP_ATTRIBUTES: - g_value_set_boxed (value, priv->attrs); - break; - case PROP_ALIGNMENT: - g_value_set_enum (value, priv->alignment); - break; - case PROP_USE_MARKUP: - g_value_set_boolean (value, priv->use_markup); - break; - case PROP_JUSTIFY: - g_value_set_boolean (value, priv->justify); - break; - case PROP_WRAP: - g_value_set_boolean (value, priv->wrap); - break; - case PROP_WRAP_MODE: - g_value_set_enum (value, priv->wrap_mode); - break; - case PROP_ELLIPSIZE: - g_value_set_enum (value, priv->ellipsize); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_label_class_init (ClutterLabelClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - GParamSpec *pspec; - - actor_class->paint = clutter_label_paint; - actor_class->get_preferred_width = clutter_label_get_preferred_width; - actor_class->get_preferred_height = clutter_label_get_preferred_height; - actor_class->allocate = clutter_label_allocate; - - gobject_class->finalize = clutter_label_finalize; - gobject_class->dispose = clutter_label_dispose; - gobject_class->set_property = clutter_label_set_property; - gobject_class->get_property = clutter_label_get_property; - - /** - * ClutterLabel:font-name: - * - * The font to be used by the #ClutterLabel, as a string - * that can be parsed by pango_font_description_from_string(). - * - * Since: 0.2 - */ - pspec = g_param_spec_string ("font-name", - "Font Name", - "The font to be used by the label", - NULL, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec); - - pspec = g_param_spec_string ("text", - "Text", - "The text to render", - NULL, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_TEXT, pspec); - - pspec = clutter_param_spec_color ("color", - "Font Color", - "Color of the font used by the label", - &default_text_color, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_COLOR, pspec); - - pspec = g_param_spec_boxed ("attributes", - "Attributes", - "A list of style attributes to apply to " - "the text of the label", - PANGO_TYPE_ATTR_LIST, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec); - - /** - * ClutterLabel:use-markup: - * - * Whether the text of the label includes Pango markup. See - * pango_layout_set_markup() in the Pango documentation. - * - * Since: 0.2 - */ - pspec = g_param_spec_boolean ("use-markup", - "Use markup", - "Whether or not the text of the label " - "includes Pango markup", - FALSE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec); - - /** - * ClutterLabel:wrap: - * - * Whether to wrap the lines of #ClutterLabel:text if the contents - * exceed the available allocation. The wrapping strategy is - * controlled by the #ClutterLabel:wrap-mode property. - * - * Since: 0.2 - */ - pspec = g_param_spec_boolean ("wrap", - "Line wrap", - "If set, wrap the lines if the text " - "becomes too wide", - FALSE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_WRAP, pspec); - - /** - * ClutterLabel:wrap-mode: - * - * If #ClutterLabel:wrap is set to %TRUE, this property will - * control how the text is wrapped. - * - * Since: 0.2 - */ - pspec = g_param_spec_enum ("wrap-mode", - "Line wrap mode", - "Control how line-wrapping is done", - PANGO_TYPE_WRAP_MODE, - PANGO_WRAP_WORD, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_WRAP_MODE, pspec); - - pspec = g_param_spec_enum ("ellipsize", - "Ellipsize", - "The preferred place to ellipsize the string, " - "if the label does not have enough room to " - "display the entire string", - PANGO_TYPE_ELLIPSIZE_MODE, - PANGO_ELLIPSIZE_NONE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec); - - /** - * ClutterLabel:alignment: - * - * The preferred alignment for the text. This property controls - * the alignment of multi-line paragraphs. - * - * Since: 0.2 - */ - pspec = g_param_spec_enum ("alignment", - "Alignment", - "The preferred alignment for the string, " - "for multi-line text", - PANGO_TYPE_ALIGNMENT, - PANGO_ALIGN_LEFT, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ALIGNMENT, pspec); - - /** - * ClutterLabel:justify: - * - * Whether the contents of the label should be justified on both - * margins. - * - * Since: 0.6 - */ - pspec = g_param_spec_boolean ("justify", - "Justify", - "Whether the contents of the label " - "should be justified", - FALSE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); - - g_type_class_add_private (gobject_class, sizeof (ClutterLabelPrivate)); -} - -static void -clutter_label_init (ClutterLabel *self) -{ - ClutterLabelPrivate *priv; - int i; - - self->priv = priv = CLUTTER_LABEL_GET_PRIVATE (self); - - if (G_UNLIKELY (_context == NULL)) - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - - priv->alignment = PANGO_ALIGN_LEFT; - priv->wrap = FALSE; - priv->wrap_mode = PANGO_WRAP_WORD; - priv->ellipsize = PANGO_ELLIPSIZE_NONE; - priv->use_underline = FALSE; - priv->use_markup = FALSE; - priv->justify = FALSE; - - for (i = 0; i < CLUTTER_LABEL_N_CACHED_LAYOUTS; i++) - priv->cached_layouts[i].layout = NULL; - - priv->text = NULL; - priv->attrs = NULL; - - priv->fgcol = default_text_color; - - priv->font_name = g_strdup (DEFAULT_FONT_NAME); - priv->font_desc = pango_font_description_from_string (priv->font_name); -} - -/** - * clutter_label_new_with_text: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * - * Creates a new #ClutterLabel displaying @text using @font_name. - * - * Return value: a #ClutterLabel - */ -ClutterActor* -clutter_label_new_with_text (const gchar *font_name, - const gchar *text) -{ - return g_object_new (CLUTTER_TYPE_LABEL, - "font-name", font_name, - "text", text, - NULL); -} - -/** - * clutter_label_new_full: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * @color: #ClutterColor for text - * - * Creates a new #ClutterLabel displaying @text with @color - * using @font_name. - * - * Return value: a #ClutterLabel - */ -ClutterActor* -clutter_label_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color) -{ - return g_object_new (CLUTTER_TYPE_LABEL, - "font-name", font_name, - "text", text, - "color", color, - NULL); -} - -/** - * clutter_label_new: - * - * Creates a new, empty #ClutterLabel. - * - * Returns: the newly created #ClutterLabel - */ -ClutterActor * -clutter_label_new (void) -{ - return g_object_new (CLUTTER_TYPE_LABEL, NULL); -} - -/** - * clutter_label_get_text: - * @label: a #ClutterLabel - * - * Retrieves the text displayed by @label - * - * Return value: the text of the label. The returned string is - * owned by #ClutterLabel and should not be modified or freed. - */ -G_CONST_RETURN gchar * -clutter_label_get_text (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - return label->priv->text; -} - -/** - * clutter_label_set_text: - * @label: a #ClutterLabel - * @text: the text to be displayed - * - * Sets @text as the text to be displayed by @label. - */ -void -clutter_label_set_text (ClutterLabel *label, - const gchar *text) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - g_free (priv->text); - - priv->text = g_strdup (text); - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "text"); -} - -/** - * clutter_label_get_font_name: - * @label: a #ClutterLabel - * - * Retrieves the font used by @label. - * - * Return value: a string containing the font name, in a format - * understandable by pango_font_description_from_string(). The - * string is owned by @label and should not be modified - * or freed. - */ -G_CONST_RETURN gchar * -clutter_label_get_font_name (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - return label->priv->font_name; -} - -/** - * clutter_label_set_font_name: - * @label: a #ClutterLabel - * @font_name: a font name and size, or %NULL for the default font - * - * Sets @font_name as the font used by @label. - * - * @font_name must be a string containing the font name and its - * size, similarly to what you would feed to the - * pango_font_description_from_string() function. - */ -void -clutter_label_set_font_name (ClutterLabel *label, - const gchar *font_name) -{ - ClutterLabelPrivate *priv; - PangoFontDescription *desc; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - if (!font_name || font_name[0] == '\0') - font_name = DEFAULT_FONT_NAME; - - priv = label->priv; - - if (strcmp (priv->font_name, font_name) == 0) - return; - - desc = pango_font_description_from_string (font_name); - if (!desc) - { - g_warning ("Attempting to create a PangoFontDescription for " - "font name `%s', but failed.", - font_name); - return; - } - - g_free (priv->font_name); - priv->font_name = g_strdup (font_name); - - if (priv->font_desc) - pango_font_description_free (priv->font_desc); - - priv->font_desc = desc; - - clutter_label_dirty_cache (label); - - if (label->priv->text && label->priv->text[0] != '\0') - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "font-name"); -} - - -/** - * clutter_label_set_color: - * @label: a #ClutterLabel - * @color: a #ClutterColor - * - * Sets the color of @label. - */ -void -clutter_label_set_color (ClutterLabel *label, - const ClutterColor *color) -{ - ClutterActor *actor; - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - g_return_if_fail (color != NULL); - - priv = label->priv; - - g_object_ref (label); - - priv->fgcol.red = color->red; - priv->fgcol.green = color->green; - priv->fgcol.blue = color->blue; - priv->fgcol.alpha = color->alpha; - - actor = CLUTTER_ACTOR (label); - - clutter_actor_set_opacity (actor, priv->fgcol.alpha); - - if (CLUTTER_ACTOR_IS_VISIBLE (actor)) - clutter_actor_queue_redraw (actor); - - g_object_notify (G_OBJECT (label), "color"); - g_object_unref (label); -} - -/** - * clutter_label_get_color: - * @label: a #ClutterLabel - * @color: return location for a #ClutterColor - * - * Retrieves the color of @label. - */ -void -clutter_label_get_color (ClutterLabel *label, - ClutterColor *color) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - g_return_if_fail (color != NULL); - - priv = label->priv; - - color->red = priv->fgcol.red; - color->green = priv->fgcol.green; - color->blue = priv->fgcol.blue; - color->alpha = priv->fgcol.alpha; -} - -/** - * clutter_label_set_ellipsize: - * @label: a #ClutterLabel - * @mode: a #PangoEllipsizeMode - * - * Sets the mode used to ellipsize (add an ellipsis: "...") to the text - * if there is not enough space to render the entire string. - * - * Since: 0.2 - **/ -void -clutter_label_set_ellipsize (ClutterLabel *label, - PangoEllipsizeMode mode) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && - mode <= PANGO_ELLIPSIZE_END); - - priv = label->priv; - - if ((PangoEllipsizeMode) priv->ellipsize != mode) - { - priv->ellipsize = mode; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "ellipsize"); - } -} - -/** - * clutter_label_get_ellipsize: - * @label: a #ClutterLabel - * - * Returns the ellipsizing position of the label. - * See clutter_label_set_ellipsize(). - * - * Return value: #PangoEllipsizeMode - * - * Since: 0.2 - **/ -PangoEllipsizeMode -clutter_label_get_ellipsize (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), PANGO_ELLIPSIZE_NONE); - - return label->priv->ellipsize; -} - -/** - * clutter_label_set_line_wrap: - * @label: a #ClutterLabel - * @wrap: the setting - * - * Toggles line wrapping within the #ClutterLabel widget. %TRUE makes - * it break lines if text exceeds the widget's size. %FALSE lets the - * text get cut off by the edge of the widget if it exceeds the widget - * size. - * - * Since: 0.2 - */ -void -clutter_label_set_line_wrap (ClutterLabel *label, - gboolean wrap) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - wrap = wrap != FALSE; - - if (priv->wrap != wrap) - { - priv->wrap = wrap; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "wrap"); - } -} - -/** - * clutter_label_get_line_wrap: - * @label: a #ClutterLabel - * - * Returns whether lines in the label are automatically wrapped. - * See clutter_label_set_line_wrap (). - * - * Return value: %TRUE if the lines of the label are automatically wrapped. - * - * Since: 0.2 - */ -gboolean -clutter_label_get_line_wrap (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->wrap; -} - -/** - * clutter_label_set_line_wrap_mode: - * @label: a #ClutterLabel - * @wrap_mode: the line wrapping mode - * - * If line wrapping is on (see clutter_label_set_line_wrap()) this controls how - * the line wrapping is done. The default is %PANGO_WRAP_WORD which means - * wrap on word boundaries. - * - * Since: 0.2 - **/ -void -clutter_label_set_line_wrap_mode (ClutterLabel *label, - PangoWrapMode wrap_mode) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->wrap_mode != wrap_mode) - { - priv->wrap_mode = wrap_mode; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "wrap-mode"); - } -} - -/** - * clutter_label_get_line_wrap_mode: - * @label: a #ClutterLabel - * - * Returns line wrap mode used by the label. - * See clutter_label_set_line_wrap_mode (). - * - * Return value: %TRUE if the lines of the label are automatically wrapped. - * - * Since: 0.2 - */ -PangoWrapMode -clutter_label_get_line_wrap_mode (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->wrap_mode; -} - -/** - * clutter_label_get_layout: - * @label: a #ClutterLabel - * - * Gets the #PangoLayout used to display the label. - * The layout is useful to e.g. convert text positions to - * pixel positions. - * The returned layout is owned by the label so need not be - * freed by the caller. - * - * Return value: the #PangoLayout for this label - * - * Since: 0.2 - **/ -PangoLayout * -clutter_label_get_layout (ClutterLabel *label) -{ - ClutterUnit width; - - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - width = clutter_actor_get_widthu (CLUTTER_ACTOR (label)); - - return clutter_label_create_layout (label, width); -} - -static inline void -clutter_label_set_attributes_internal (ClutterLabel *label, - PangoAttrList *attrs) -{ -} - -/** - * clutter_label_set_attributes: - * @label: a #ClutterLabel - * @attrs: a #PangoAttrList - * - * Sets a #PangoAttrList; the attributes in the list are applied to the - * label text. The attributes set with this function will be ignored - * if the "use_markup" property - * is %TRUE. - * - * Since: 0.2 - **/ -void -clutter_label_set_attributes (ClutterLabel *label, - PangoAttrList *attrs) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (attrs) - pango_attr_list_ref (attrs); - - if (priv->attrs) - pango_attr_list_unref (priv->attrs); - - if (!priv->use_markup) - { - if (attrs) - pango_attr_list_ref (attrs); - - if (priv->effective_attrs) - pango_attr_list_unref (priv->effective_attrs); - - priv->effective_attrs = attrs; - } - - priv->attrs = attrs; - - clutter_label_dirty_cache (label); - - g_object_notify (G_OBJECT (label), "attributes"); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); -} - -/** - * clutter_label_get_attributes: - * @label: a #ClutterLabel - * - * Gets the attribute list that was set on the label using - * clutter_label_set_attributes(), if any. - * - * Return value: the attribute list, or %NULL if none was set. - * - * Since: 0.2 - **/ -PangoAttrList * -clutter_label_get_attributes (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - return label->priv->attrs; -} - -/** - * clutter_label_set_use_markup: - * @label: a #ClutterLabel - * @setting: %TRUE if the label's text should be parsed for markup. - * - * Sets whether the text of the label contains markup in Pango's text markup - * language. - **/ -void -clutter_label_set_use_markup (ClutterLabel *label, - gboolean setting) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->use_markup != setting) - { - priv->use_markup = setting; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "use-markup"); - } -} - -/** - * clutter_label_get_use_markup: - * @label: a #ClutterLabel - * - * Returns whether the label's text is interpreted as marked up with - * the Pango text markup - * language. See clutter_label_set_use_markup (). - * - * Return value: %TRUE if the label's text will be parsed for markup. - **/ -gboolean -clutter_label_get_use_markup (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->use_markup; -} - -/** - * clutter_label_set_alignment: - * @label: a #ClutterLabel - * @alignment: A #PangoAlignment - * - * Sets text alignment of the label. - * - * The alignment will only be used when the contents of the - * label are enough to wrap, and the #ClutterLabel:wrap - * property is set to %TRUE. - **/ -void -clutter_label_set_alignment (ClutterLabel *label, - PangoAlignment alignment) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->alignment != alignment) - { - priv->alignment = alignment; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "alignment"); - } -} - -/** - * clutter_label_get_alignment: - * @label: a #ClutterLabel - * - * Returns the label's text alignment - * - * Return value: The label's #PangoAlignment - * - * Since: 0.2 - **/ -PangoAlignment -clutter_label_get_alignment (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->alignment; -} - -/** - * clutter_label_set_justify: - * @label: a #ClutterLabel - * @justify: whether the text should be justified - * - * Sets whether the text of the @label actor should be justified - * on both margins. This setting is ignored if Clutter is compiled - * against Pango < 1.18. - * - * Since: 0.6 - */ -void -clutter_label_set_justify (ClutterLabel *label, - gboolean justify) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->justify != justify) - { - priv->justify = justify; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "justify"); - } -} - -/** - * clutter_label_get_justify: - * @label: a #ClutterLabel - * - * Retrieves whether the label should justify the text on both margins. - * - * Return value: %TRUE if the text should be justified - * - * Since: 0.6 - */ -gboolean -clutter_label_get_justify (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->justify; -} diff --git a/clutter/clutter-label.h b/clutter/clutter-label.h deleted file mode 100644 index d6021c2f3..000000000 --- a/clutter/clutter-label.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * - * Copyright (C) 2006 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __CLUTTER_LABEL_H__ -#define __CLUTTER_LABEL_H__ - -#include -#include -#include - - -G_BEGIN_DECLS - -#define CLUTTER_TYPE_LABEL (clutter_label_get_type ()) - -#define CLUTTER_LABEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_LABEL, ClutterLabel)) - -#define CLUTTER_LABEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_LABEL, ClutterLabelClass)) - -#define CLUTTER_IS_LABEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_LABEL)) - -#define CLUTTER_IS_LABEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_LABEL)) - -#define CLUTTER_LABEL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_LABEL, ClutterLabelClass)) - -typedef struct _ClutterLabel ClutterLabel; -typedef struct _ClutterLabelClass ClutterLabelClass; -typedef struct _ClutterLabelPrivate ClutterLabelPrivate; - -struct _ClutterLabel -{ - ClutterActor parent; - - /*< private >*/ - ClutterLabelPrivate *priv; -}; - -struct _ClutterLabelClass -{ - /*< private >*/ - ClutterActorClass parent_class; - - void (*_clutter_label_1) (void); - void (*_clutter_label_2) (void); - void (*_clutter_label_3) (void); - void (*_clutter_label_4) (void); -}; - -GType clutter_label_get_type (void) G_GNUC_CONST; - -ClutterActor * clutter_label_new (void); - -ClutterActor* clutter_label_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color); - -ClutterActor * clutter_label_new_with_text (const gchar *font_name, - const gchar *text); -void clutter_label_set_text (ClutterLabel *label, - const gchar *text); -G_CONST_RETURN gchar *clutter_label_get_text (ClutterLabel *label); -void clutter_label_set_font_name (ClutterLabel *label, - const gchar *font_name); -G_CONST_RETURN gchar *clutter_label_get_font_name (ClutterLabel *label); -void clutter_label_set_color (ClutterLabel *label, - const ClutterColor *color); -void clutter_label_get_color (ClutterLabel *label, - ClutterColor *color); -void clutter_label_set_ellipsize (ClutterLabel *label, - PangoEllipsizeMode mode); -PangoEllipsizeMode clutter_label_get_ellipsize (ClutterLabel *label); -void clutter_label_set_line_wrap (ClutterLabel *label, - gboolean wrap); -gboolean clutter_label_get_line_wrap (ClutterLabel *label); -void clutter_label_set_line_wrap_mode (ClutterLabel *label, - PangoWrapMode wrap_mode); -PangoWrapMode clutter_label_get_line_wrap_mode (ClutterLabel *label); -PangoLayout * clutter_label_get_layout (ClutterLabel *label); -void clutter_label_set_attributes (ClutterLabel *label, - PangoAttrList *attrs); -PangoAttrList * clutter_label_get_attributes (ClutterLabel *label); -void clutter_label_set_use_markup (ClutterLabel *label, - gboolean setting); -gboolean clutter_label_get_use_markup (ClutterLabel *label); -void clutter_label_set_alignment (ClutterLabel *label, - PangoAlignment alignment); -PangoAlignment clutter_label_get_alignment (ClutterLabel *label); -void clutter_label_set_justify (ClutterLabel *label, - gboolean justify); -gboolean clutter_label_get_justify (ClutterLabel *label); - -G_END_DECLS - -#endif /* __CLUTTER_LABEL_H__ */ From 2d166b250c0e0da33171f0d2f87735c965af50e9 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 15 Dec 2008 15:10:55 +0000 Subject: [PATCH 062/147] [docs] Add annotations for ClutterText structures ClutterText and ClutterTextClass were missing annotations for gtk-doc to pick up. --- clutter/clutter-text.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 56595b794..5598b0980 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -45,6 +45,13 @@ typedef struct _ClutterText ClutterText; typedef struct _ClutterTextPrivate ClutterTextPrivate; typedef struct _ClutterTextClass ClutterTextClass; +/** + * ClutterText: + * + * The #ClutterText struct contains only private data. + * + * Since: 1.0 + */ struct _ClutterText { /*< private >*/ @@ -53,6 +60,16 @@ struct _ClutterText ClutterTextPrivate *priv; }; +/** + * ClutterTextClass: + * @text_changed: class handler for the #ClutterText::text-changed signal + * @activate: class handler for the #ClutterText::activate signal + * @cursor_event: class handler for the #ClutterText::cursor_event signal + * + * The #ClutterTextClass struct contains only private data. + * + * Since: 1.0 + */ struct _ClutterTextClass { /*< private >*/ From 2714397fd517636461088f20521c80801b696b60 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 15 Dec 2008 18:23:25 +0000 Subject: [PATCH 063/147] Constify the cursor geometry in ::cursor-event We don't allow changing the cursor geometry inside the ::cursor-event signal handlers; for starters, it would make binding the signal a huge mess, and it would also potentially break the whole actor. --- clutter/clutter-text.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 5598b0980..4001181b5 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -76,10 +76,11 @@ struct _ClutterTextClass ClutterActorClass parent_class; /*< public >*/ - void (* text_changed) (ClutterText *self); - void (* activate) (ClutterText *self); - void (* cursor_event) (ClutterText *self, - ClutterGeometry *geometry); + /* signals, not vfuncs */ + void (* text_changed) (ClutterText *self); + void (* activate) (ClutterText *self); + void (* cursor_event) (ClutterText *self, + const ClutterGeometry *geometry); /*< private >*/ /* padding for future expansion */ From e615b8cc6bd6e77cc649e71db6ea62248e37a6cd Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 15 Dec 2008 18:24:42 +0000 Subject: [PATCH 064/147] Use units in position_to_coords() We loose precision with a direct conversion for PangoUnits to pixels, so we should do the conversion as needed, inside the callers of clutter_text_position_to_coords(). --- clutter/clutter-text.c | 113 +++++++++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 32 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index c0d51543e..7d2a07751 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -61,6 +61,9 @@ #define DEFAULT_FONT_NAME "Sans 10" +/* cursor width in pixels */ +#define DEFAULT_CURSOR_SIZE 2 + /* We need at least three cached layouts to run the allocation without * regenerating a new layout. First the layout will be generated at * full width to get the preferred width, then it will be generated at @@ -150,6 +153,7 @@ struct _ClutterTextPrivate /* Where to draw the cursor */ ClutterGeometry cursor_pos; ClutterColor cursor_color; + gint cursor_size; gint max_length; @@ -438,17 +442,30 @@ clutter_text_coords_to_position (ClutterText *text, py = y * PANGO_SCALE; pango_layout_xy_to_index (clutter_text_get_layout (text), - px, py, &index_, &trailing); + px, py, + &index_, &trailing); return index_ + trailing; } +/* + * clutter_text_position_to_coords: + * @self: a #ClutterText + * @position: position in characters + * @x: return location for the X coordinate, or %NULL + * @y: return location for the Y coordinate, or %NULL + * @line_height: return location for the line height, or %NULL + * + * Retrieves the coordinates of the given @position. + * + * Return value: %TRUE if the conversion was successful + */ static gboolean clutter_text_position_to_coords (ClutterText *self, gint position, - gint *x, - gint *y, - gint *cursor_height) + ClutterUnit *x, + ClutterUnit *y, + ClutterUnit *line_height) { ClutterTextPrivate *priv = self->priv; PangoRectangle rect; @@ -463,10 +480,14 @@ clutter_text_position_to_coords (ClutterText *self, if (position == -1) { if (priv->text_visible) - index_ = strlen (priv->text); + index_ = priv->n_bytes; else index_ = priv->n_chars * priv_char_bytes; } + else if (position == 0) + { + index_ = 0; + } else { if (priv->text_visible) @@ -479,31 +500,33 @@ clutter_text_position_to_coords (ClutterText *self, &rect, NULL); if (x) - *x = rect.x / PANGO_SCALE; + *x = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.x); if (y) - *y = (rect.y + rect.height) / PANGO_SCALE; + *y = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.y); - if (cursor_height) - *cursor_height = rect.height / PANGO_SCALE; + if (line_height) + *line_height = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.height); - return TRUE; /* FIXME: should return false if coords were outside text */ + /* FIXME: should return false if coords were outside text */ + return TRUE; } static inline void clutter_text_ensure_cursor_position (ClutterText *self) { ClutterTextPrivate *priv = self->priv; - gint x, y, cursor_height; + ClutterUnit x, y, cursor_height; + x = y = cursor_height = 0; clutter_text_position_to_coords (self, priv->position, &x, &y, &cursor_height); - priv->cursor_pos.x = x; - priv->cursor_pos.y = y - cursor_height; - priv->cursor_pos.width = 2; - priv->cursor_pos.height = cursor_height; + priv->cursor_pos.x = CLUTTER_UNITS_TO_DEVICE (x); + priv->cursor_pos.y = CLUTTER_UNITS_TO_DEVICE (y); + priv->cursor_pos.width = priv->cursor_size; + priv->cursor_pos.height = CLUTTER_UNITS_TO_DEVICE (cursor_height); g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } @@ -799,13 +822,12 @@ cursor_paint (ClutterText *self) for (line_no = 0; line_no < lines; line_no++) { PangoLayoutLine *line; - gint n_ranges; + gint n_ranges; gint *ranges; - gint i; - gint y; - gint height; + gint i; gint index; gint maxindex; + ClutterUnit y, height; line = pango_layout_get_line_readonly (layout, line_no); pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL); @@ -822,11 +844,20 @@ cursor_paint (ClutterText *self) NULL, &y, &height); for (i = 0; i < n_ranges; i++) - cogl_rectangle (ranges[i * 2 + 0] / PANGO_SCALE, - y - height, - ((ranges[i * 2 + 1] - ranges[i * 2 + 0]) - / PANGO_SCALE), - height); + { + gint range_x; + gint range_width; + + range_x = ranges[i * 2] + / PANGO_SCALE; + range_width = (ranges[i * 2 + 1] - ranges[i * 2]) + / PANGO_SCALE; + + cogl_rectangle (range_x, + CLUTTER_UNITS_TO_DEVICE (y), + range_width, + CLUTTER_UNITS_TO_DEVICE (height)); + } g_free (ranges); @@ -886,8 +917,9 @@ clutter_text_motion (ClutterActor *actor, clutter_actor_transform_stage_point (actor, x, y, &x, &y); - index_ = clutter_text_coords_to_position (ttext, CLUTTER_UNITS_TO_INT (x), - CLUTTER_UNITS_TO_INT (y)); + index_ = clutter_text_coords_to_position (ttext, + CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); if (priv->selectable) clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); @@ -1023,14 +1055,21 @@ clutter_text_get_preferred_width (ClutterActor *self, ClutterTextPrivate *priv = text->priv; PangoRectangle logical_rect = { 0, }; PangoLayout *layout; + gint logical_width; ClutterUnit layout_width; layout = clutter_text_create_layout (text, -1); pango_layout_get_extents (layout, NULL, &logical_rect); - layout_width = logical_rect.width > 0 - ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.width) + /* the X coordinate of the logical rectangle might be non-zero + * according to the Pango documentation; hence, we need to offset + * the width accordingly + */ + logical_width = logical_rect.x + logical_rect.width; + + layout_width = logical_width > 0 + ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_width) : 1; if (min_width_p) @@ -1065,18 +1104,26 @@ clutter_text_get_preferred_height (ClutterActor *self, { PangoLayout *layout; PangoRectangle logical_rect = { 0, }; - ClutterUnit height; + gint logical_height; + ClutterUnit layout_height; layout = clutter_text_create_layout (text, for_width); pango_layout_get_extents (layout, NULL, &logical_rect); - height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.height); + + /* the Y coordinate of the logical rectangle might be non-zero + * according to the Pango documentation; hence, we need to offset + * the height accordingly + */ + logical_height = logical_rect.y + logical_rect.height; + + layout_height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_height); if (min_height_p) - *min_height_p = height; + *min_height_p = layout_height; if (natural_height_p) - *natural_height_p = height; + *natural_height_p = layout_height; } } @@ -1881,6 +1928,8 @@ clutter_text_init (ClutterText *self) priv->priv_char = '*'; priv->max_length = 0; + + priv->cursor_size = DEFAULT_CURSOR_SIZE; } ClutterActor * From be64cbcdc22952ce2fa9f902e153e3bd9f4a08c9 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 15 Dec 2008 18:46:08 +0000 Subject: [PATCH 065/147] Fix up/down key navigation The behaviour of ClutterText around the initial position is still a little bit erratic. This commit fixes the key navigation with Up and Down arrows around the first line of text. --- clutter/clutter-text.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 7d2a07751..51c29f29f 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1209,20 +1209,24 @@ clutter_text_real_move_up (ClutterText *self, layout = clutter_text_get_layout (self); - pango_layout_index_to_line_x (layout, - offset_to_bytes (priv->text, priv->position), + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, 0, &line_no, &x); + line_no -= 1; + if (line_no < 0) + return FALSE; + if (priv->x_pos != -1) x = priv->x_pos; else priv->x_pos = x; - line_no -= 1; - if (line_no < 0) - return FALSE; - layout_line = pango_layout_get_line_readonly (layout, line_no); if (!layout_line) return FALSE; @@ -1256,8 +1260,12 @@ clutter_text_real_move_down (ClutterText *self, layout = clutter_text_get_layout (self); - pango_layout_index_to_line_x (layout, - offset_to_bytes (priv->text, priv->position), + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, 0, &line_no, &x); From dc49dab35074ca27fcccf015e8990270eeaa4185 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 10:15:57 +0000 Subject: [PATCH 066/147] Fix line start and line end key binding behaviour Another fix for the key navigation behaviour around the zeroeth glyph in the layout. This commit adds a fast path for for the zero index when the cursor position is set as zero, in case we are using the line-start or line-end key bindings, similarly to what we did in commit be64cbcdc22952ce2fa9f902e153e3bd9f4a08c9 for the move-up and move-down bindings. --- clutter/clutter-text.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 51c29f29f..4aab2eb95 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -211,11 +211,6 @@ offset_to_bytes (const gchar *text, if (pos < 0) return strlen (text); -#if 0 - if (pos < 1) - return pos; -#endif - c = g_utf8_next_char (text); j = 1; len = strlen (text); @@ -1307,8 +1302,12 @@ clutter_text_real_line_start (ClutterText *self, layout = clutter_text_get_layout (self); - pango_layout_index_to_line_x (layout, - offset_to_bytes (priv->text, priv->position), + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, 0, &line_no, NULL); @@ -1342,7 +1341,11 @@ clutter_text_real_line_end (ClutterText *self, gint position; layout = clutter_text_get_layout (self); - index_ = offset_to_bytes (priv->text, priv->position); + + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); pango_layout_index_to_line_x (layout, index_, 0, @@ -2145,6 +2148,7 @@ clutter_text_get_selection (ClutterText *text) if (end_index == start_index) return g_strdup (""); + if (end_index < start_index) { gint temp = start_index; From ffb500d3a617ef3f0cfab4912c4ed4ca8c71599a Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 10:19:56 +0000 Subject: [PATCH 067/147] Remove unused page up/down key bindings Moving the text by a "page" depends on being able to define a "page size" in terms of lines of text. Since we don't define something similar to an Adjustment that allows us to handle this behaviour, we should defer the paging implementation to a higher level class based on ClutterText. --- clutter/clutter-text.c | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4aab2eb95..b5e91a427 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1449,24 +1449,6 @@ clutter_text_real_activate (ClutterText *self, return FALSE; } -static gboolean -clutter_text_real_page_up (ClutterText *self, - const gchar *action, - guint keyval, - ClutterModifierType modifiers) -{ - return FALSE; -} - -static gboolean -clutter_text_real_page_down (ClutterText *self, - const gchar *action, - guint keyval, - ClutterModifierType modifiers) -{ - return FALSE; -} - static void clutter_text_add_move_binding (ClutterBindingPool *pool, const gchar *action, @@ -1851,23 +1833,6 @@ clutter_text_class_init (ClutterTextClass *klass) G_CALLBACK (clutter_text_real_line_end), NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "page-up", - CLUTTER_Page_Up, 0, - G_CALLBACK (clutter_text_real_page_up), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "page-up", - CLUTTER_KP_Page_Up, 0, - G_CALLBACK (clutter_text_real_page_up), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "page-down", - CLUTTER_Page_Down, 0, - G_CALLBACK (clutter_text_real_page_down), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "page-up", - CLUTTER_KP_Page_Down, 0, - G_CALLBACK (clutter_text_real_page_down), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "delete-next", CLUTTER_Delete, 0, G_CALLBACK (clutter_text_real_del_next), From ea5a6abd9d599104ae4de7c0aecb529276d0d004 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 10:37:45 +0000 Subject: [PATCH 068/147] Add :cursor-size property to ClutterText We can control the width of the cursor when painting by using a simple property. The magic -1 number passed to the setter method will reset the cursor size to the default one of 2px. The getter method will return an unsigned integer with the current size. --- clutter/clutter-text.c | 80 +++++++++++++++++++++++++++++++++++++++++- clutter/clutter-text.h | 3 ++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index b5e91a427..86c366ca0 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -153,7 +153,7 @@ struct _ClutterTextPrivate /* Where to draw the cursor */ ClutterGeometry cursor_pos; ClutterColor cursor_color; - gint cursor_size; + guint cursor_size; gint max_length; @@ -179,6 +179,7 @@ enum PROP_CURSOR_VISIBLE, PROP_CURSOR_COLOR, PROP_CURSOR_COLOR_SET, + PROP_CURSOR_SIZE, PROP_EDITABLE, PROP_SELECTABLE, PROP_ACTIVATABLE, @@ -623,6 +624,10 @@ clutter_text_set_property (GObject *gobject, clutter_text_set_cursor_color (self, g_value_get_boxed (value)); break; + case PROP_CURSOR_SIZE: + clutter_text_set_cursor_size (self, g_value_get_int (value)); + break; + case PROP_EDITABLE: clutter_text_set_editable (self, g_value_get_boolean (value)); break; @@ -686,6 +691,10 @@ clutter_text_get_property (GObject *gobject, g_value_set_boolean (value, priv->cursor_color_set); break; + case PROP_CURSOR_SIZE: + g_value_set_int (value, priv->cursor_size); + break; + case PROP_POSITION: g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); break; @@ -1583,6 +1592,21 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR_SET, pspec); + /** + * ClutterText:cursor-size: + * + * The size of the cursor, in pixels. If set to -1 the size used will + * be the default cursor size of 2 pixels. + * + * Since: 1.0 + */ + pspec = g_param_spec_int ("cursor-size", + "Cursor Size", + "The width of the cursor, in pixels", + -1, G_MAXINT, DEFAULT_CURSOR_SIZE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CURSOR_SIZE, pspec); + /** * ClutterText:position: * @@ -2847,6 +2871,60 @@ clutter_text_set_cursor_position (ClutterText *self, clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } +/** + * clutter_text_set_cursor_size: + * @self: a #ClutterText + * @size: the size of the cursor, in pixels, or -1 to use the + * default value + * + * Sets the size of the cursor of a #ClutterText. The cursor + * will only be visible if the #ClutterText:cursor-visible property + * is set to %TRUE. + * + * Since: 1.0 + */ +void +clutter_text_set_cursor_size (ClutterText *self, + gint size) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->cursor_size != size) + { + if (size < 0) + size = DEFAULT_CURSOR_SIZE; + + priv->cursor_size = size; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "cursor-size"); + } +} + +/** + * clutter_text_get_cursor_size: + * @self: a #ClutterText + * + * Retrieves the size of the cursor of a #ClutterText actor. + * + * Return value: the size of the cursor, in pixels + * + * Since: 1.0 + */ +guint +clutter_text_get_cursor_size (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), DEFAULT_CURSOR_SIZE); + + return self->priv->cursor_size; +} + /** * clutter_text_set_text_visible: * @self: a #ClutterText diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 4001181b5..6d861c895 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -168,6 +168,9 @@ void clutter_text_set_cursor_color (ClutterText *self const ClutterColor *color); void clutter_text_get_cursor_color (ClutterText *self, ClutterColor *color); +void clutter_text_set_cursor_size (ClutterText *self, + gint size); +guint clutter_text_get_cursor_size (ClutterText *self); void clutter_text_set_selectable (ClutterText *self, gboolean selectable); gboolean clutter_text_get_selectable (ClutterText *self); From 7ebb9ff6b7bc04d2a47a0d7202f7190ce41faa80 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 10:39:52 +0000 Subject: [PATCH 069/147] Allow selecting with line-start and line-end bindings Instead of installing the line-start and line-end key bindings using the bare ClutterBindingPool API, we can use the internal clutter_text_add_move_binding(), which automatically installs the same key binding with the Shift modifier mask. This allows selecting when pressing Shift+Home or Shift+End. The selection behaviour is still incorrect around the zeroeth position, with all the text after the first line being selected. --- clutter/clutter-text.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 86c366ca0..f99b22b17 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1458,7 +1458,7 @@ clutter_text_real_activate (ClutterText *self, return FALSE; } -static void +static inline void clutter_text_add_move_binding (ClutterBindingPool *pool, const gchar *action, guint key_val, @@ -1836,26 +1836,21 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_KP_Down, G_CALLBACK (clutter_text_real_move_down)); - clutter_binding_pool_install_action (binding_pool, "line-start", - CLUTTER_Home, 0, - G_CALLBACK (clutter_text_real_line_start), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "line-start", - CLUTTER_KP_Home, 0, - G_CALLBACK (clutter_text_real_line_start), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "line-start", - CLUTTER_Begin, 0, - G_CALLBACK (clutter_text_real_line_start), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "line-end", - CLUTTER_End, 0, - G_CALLBACK (clutter_text_real_line_end), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "line-end", - CLUTTER_KP_End, 0, - G_CALLBACK (clutter_text_real_line_end), - NULL, NULL); + clutter_text_add_move_binding (binding_pool, "line-start", + CLUTTER_Home, + G_CALLBACK (clutter_text_real_line_start)); + clutter_text_add_move_binding (binding_pool, "line-start", + CLUTTER_KP_Home, + G_CALLBACK (clutter_text_real_line_start)); + clutter_text_add_move_binding (binding_pool, "line-start", + CLUTTER_Begin, + G_CALLBACK (clutter_text_real_line_start)); + clutter_text_add_move_binding (binding_pool, "line-end", + CLUTTER_End, + G_CALLBACK (clutter_text_real_line_end)); + clutter_text_add_move_binding (binding_pool, "line-end", + CLUTTER_KP_End, + G_CALLBACK (clutter_text_real_line_end)); clutter_binding_pool_install_action (binding_pool, "delete-next", CLUTTER_Delete, 0, From 32896d9aee66700f03417f3f88e0bba6d18d2e5c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 10:43:05 +0000 Subject: [PATCH 070/147] [docs] Add :cursor-size property accessors Add the ClutterText:cursor-size property accessors to the known gtk-doc symbols for the ClutterText section. --- doc/reference/clutter/clutter-sections.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 9fb7924dd..95ae63d3b 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1743,6 +1743,8 @@ clutter_text_set_cursor_position clutter_text_get_cursor_position clutter_text_set_cursor_visible clutter_text_get_cursor_visible +clutter_text_set_cursor_size +clutter_text_get_cursor_size CLUTTER_IS_TEXT From e3ba2ddf0a0cdace9530ad8227fdd3a6a94bf36f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 11:30:47 +0000 Subject: [PATCH 071/147] [docs] Annotate properties and fix signals docs Annotate the properties without gtk-doc description, to get the "Since" attribute. Also, fix the ::cursor-event and ::activate signals documentation. --- clutter/clutter-text.c | 94 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 5 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index f99b22b17..7d8badc67 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1513,6 +1513,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec); + /** + * ClutterText:text: + * + * The text to render inside the actor. + * + * Since: 1.0 + */ pspec = g_param_spec_string ("text", "Text", "The text to render", @@ -1520,6 +1527,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TEXT, pspec); + /** + * ClutterText:color: + * + * The color used to render the text. + * + * Since: 1.0 + */ pspec = clutter_param_spec_color ("color", "Font Color", "Color of the font used by the text", @@ -1531,6 +1545,8 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText:editable: * * Whether key events delivered to the actor causes editing. + * + * Since: 1.0 */ pspec = g_param_spec_boolean ("editable", "Editable", @@ -1542,7 +1558,10 @@ clutter_text_class_init (ClutterTextClass *klass) /** * ClutterText:selectable: * - * Whether it is possible to select text. + * Whether it is possible to select text, either using the pointer + * or the keyboard. + * + * Since: 1.0 */ pspec = g_param_spec_boolean ("selectable", "Selectable", @@ -1555,6 +1574,8 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText:activatable: * * Toggles whether return invokes the activate signal or not. + * + * Since: 1.0 */ pspec = g_param_spec_boolean ("activatable", "Activatable", @@ -1568,8 +1589,10 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText:cursor-visible: * * Whether the input cursor is visible or not, it will only be visible - * if both cursor-visible is set and editable is set at the same time, - * the value defaults to TRUE. + * if both #ClutterText:cursor-visible and #ClutterText:editable are + * set to %TRUE. + * + * Since: 1.0 */ pspec = g_param_spec_boolean ("cursor-visible", "Cursor Visible", @@ -1578,6 +1601,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec); + /** + * ClutterText:cursor-color: + * + * The color of the cursor. + * + * Since: 1.0 + */ pspec = clutter_param_spec_color ("cursor-color", "Cursor Color", "Cursor Color", @@ -1585,6 +1615,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec); + /** + * ClutterText:cursor-color-set: + * + * Will be set to %TRUE if #ClutterText:cursor-color has been set. + * + * Since: 1.0 + */ pspec = g_param_spec_boolean ("cursor-color-set", "Cursor Color Set", "Whether the cursor color has been set", @@ -1611,6 +1648,8 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText:position: * * The current input cursor position. -1 is taken to be the end of the text + * + * Since: 1.0 */ pspec = g_param_spec_int ("position", "Position", @@ -1624,6 +1663,8 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText:selection-bound: * * The current input cursor position. -1 is taken to be the end of the text + * + * Since: 1.0 */ pspec = g_param_spec_int ("selection-bound", "Selection-bound", @@ -1634,6 +1675,14 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec); + /** + * ClutterText:attributes: + * + * A list of #PangoStyleAttributes to be applied to the + * contents of the #ClutterText actor. + * + * Since: 1.0 + */ pspec = g_param_spec_boxed ("attributes", "Attributes", "A list of style attributes to apply to " @@ -1691,6 +1740,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_LINE_WRAP_MODE, pspec); + /** + * ClutterText:ellipsize: + * + * The preferred place to ellipsize the contents of the #ClutterText actor + * + * Since: 1.0 + */ pspec = g_param_spec_enum ("ellipsize", "Ellipsize", "The preferred place to ellipsize the string", @@ -1731,6 +1787,15 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); + /** + * ClutterText:text-visible: + * + * Whether the contents of the #ClutterText actor should be visible + * or substituted by a Unicode character, like in password text fields. + * The Unicode character is set using #ClutterText:invisible-char. + * + * Since: 1.0 + */ pspec = g_param_spec_boolean ("text-visible", "Text Visible", "Whether the text should be visible " @@ -1740,6 +1805,14 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TEXT_VISIBLE, pspec); + /** + * ClutterText:invisible-char: + * + * The Unicode character used to render the contents of the #ClutterText + * actor if #ClutterText:text-invisible is set to %TRUE. + * + * Since: 1.0 + */ pspec = g_param_spec_unichar ("invisible-char", "Invisible Character", "The Unicode character used when the " @@ -1748,6 +1821,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_INVISIBLE_CHAR, pspec); + /** + * ClutterText:max-length: + * + * The maximum length of the contents of the #ClutterText actor. + * + * Since: 1.0 + */ pspec = g_param_spec_int ("max-length", "Max Length", "Maximum length of the text inside the actor", @@ -1777,7 +1857,10 @@ clutter_text_class_init (ClutterTextClass *klass) * @self: the #ClutterText that emitted the signal * @geometry: the coordinates of the cursor * - * FIXME + * The ::cursor-event signal is emitted whenever the cursor position + * changes inside a #ClutterText actor. Inside @geometry it is stored + * the current position and size of the cursor, relative to the actor + * itself. * * Since: 1.0 */ @@ -1796,7 +1879,8 @@ clutter_text_class_init (ClutterTextClass *klass) * @self: the #ClutterText that emitted the signal * * The ::activate signal is emitted each time the actor is 'activated' - * by the user, normally by pressing the 'Enter' key. + * by the user, normally by pressing the 'Enter' key. The signal is + * emitted only if #ClutterText:activatable is set to %TRUE. * * Since: 1.0 */ From 87f38475aed1827de5e48e0db0d5bff4ea1bcef5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 11:52:57 +0000 Subject: [PATCH 072/147] [docs] Add more gtk-doc annotations for ClutterText Document the ClutterText constructors and the :editable, :selectable and :activatable properties accessors. --- clutter/clutter-text.c | 116 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 7d8badc67..dd93c5b64 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2011,12 +2011,40 @@ clutter_text_init (ClutterText *self) priv->cursor_size = DEFAULT_CURSOR_SIZE; } +/** + * clutter_text_new: + * + * Creates a new #ClutterText actor. This actor can be used to + * display and edit text. + * + * Return value: the newly created #ClutterText actor + * + * Since: 1.0 + */ ClutterActor * clutter_text_new (void) { return g_object_new (CLUTTER_TYPE_TEXT, NULL); } +/** + * clutter_text_new_full: + * @font_name: a string with a font description + * @text: the contents of the actor + * @color: the color to be used to render @text + * + * Creates a new #ClutterText actor, using @font_name as the font + * description; @text will be used to set the contents of the actor; + * and @color will be used as the color to render @text. + * + * This function is equivalent to calling clutter_text_new(), + * clutter_text_set_font_name(), clutter_text_set_text() and + * clutter_text_set_color(). + * + * Return value: the newly created #ClutterText actor + * + * Since: 1.0 + */ ClutterActor * clutter_text_new_full (const gchar *font_name, const gchar *text, @@ -2029,6 +2057,21 @@ clutter_text_new_full (const gchar *font_name, NULL); } +/** + * clutter_text_new_with_text: + * @font_name: a string with a font description + * @text: the contents of the actor + * + * Creates a new #ClutterText actor, using @font_name as the font + * description; @text will be used to set the contents of the actor. + * + * This function is equivalent to calling clutter_text_new(), + * clutter_text_set_font_name(), and clutter_text_set_text(). + * + * Return value: the newly created #ClutterText actor + * + * Since: 1.0 + */ ClutterActor * clutter_text_new_with_text (const gchar *font_name, const gchar *text) @@ -2039,6 +2082,19 @@ clutter_text_new_with_text (const gchar *font_name, NULL); } +/** + * clutter_text_set_editable: + * @self: a #ClutterText + * @editable: whether the #ClutterText should be editable + * + * Sets whether the #ClutterText actor should be editable. + * + * An editable #ClutterText with key focus set using + * clutter_actor_grab_key_focus() or clutter_stage_take_key_focus() + * will receive key events and will update its contents accordingly. + * + * Since: 1.0 + */ void clutter_text_set_editable (ClutterText *self, gboolean editable) @@ -2060,6 +2116,16 @@ clutter_text_set_editable (ClutterText *self, } } +/** + * clutter_text_get_editable: + * @self: a #ClutterText + * + * Retrieves whether a #ClutterText is editable or not. + * + * Return value: %TRUE if the actor is editable + * + * Since: 1.0 + */ gboolean clutter_text_get_editable (ClutterText *self) { @@ -2068,7 +2134,18 @@ clutter_text_get_editable (ClutterText *self) return self->priv->editable; } - +/** + * clutter_text_set_selectable: + * @self: a #ClutterText + * @selectable: whether the #ClutterText actor should be selectable + * + * Sets whether a #ClutterText actor should be selectable. + * + * A selectable #ClutterText will allow selecting its contents using + * the pointer or the keyboard. + * + * Since: 1.0 + */ void clutter_text_set_selectable (ClutterText *self, gboolean selectable) @@ -2090,6 +2167,16 @@ clutter_text_set_selectable (ClutterText *self, } } +/** + * clutter_text_get_selectable: + * @self: a #ClutterText + * + * Retrieves whether a #ClutterText is selectable or not. + * + * Return value: %TRUE if the actor is selectable + * + * Since: 1.0 + */ gboolean clutter_text_get_selectable (ClutterText *self) { @@ -2098,7 +2185,22 @@ clutter_text_get_selectable (ClutterText *self) return self->priv->selectable; } - +/** + * clutter_text_set_activatable: + * @self: a #ClutterText + * @activatable: whether the #ClutterText actor should be activatable + * + * Sets whether a #ClutterText actor should be activatable. + * + * An activatable #ClutterText actor will emit the #ClutterText::activate + * signal whenever the 'Enter' (or 'Return') key is pressed; if it is not + * activatable, a new line will be appended to the current content. + * + * An activatable #ClutterText must also be set as editable using + * clutter_text_set_editable(). + * + * Since: 1.0 + */ void clutter_text_set_activatable (ClutterText *self, gboolean activatable) @@ -2120,6 +2222,16 @@ clutter_text_set_activatable (ClutterText *self, } } +/** + * clutter_text_get_activatable: + * @self: a #ClutterText + * + * Retrieves whether a #ClutterText is activatable or not. + * + * Return value: %TRUE if the actor is activatable + * + * Since: 1.0 + */ gboolean clutter_text_get_activatable (ClutterText *self) { From 93d96dca52b21c63775a230f872de28fe0e0e3ea Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:02:58 +0000 Subject: [PATCH 073/147] Add ClutterText::activate() The clutter_text_activate() function will emit the ::activate signal if the :activatable property is set. This function is useful for subclasses or application code, for example if we are going to use ::captured-event or ::key-press-event signal handlers to intercept the Return key and emit the ::activate signal ourselves. --- clutter/clutter-text.c | 35 +++++++++++++++++++++++++++++++++++ clutter/clutter-text.h | 1 + 2 files changed, 36 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index dd93c5b64..22198a69f 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2240,6 +2240,41 @@ clutter_text_get_activatable (ClutterText *self) return self->priv->activatable; } +/** + * clutter_text_activate: + * @self: a #ClutterText + * + * Emits the #ClutterText::activate signal, if @self has been set + * as activatable using clutter_text_set_activatable(). + * + * This function can be used to emit the ::activate signal inside + * a #ClutterActor::captured-event or #ClutterActor::key-press-event + * signal handlers before the default signal handler for the + * #ClutterText is invoked. + * + * Return value: %TRUE if the ::activate signal has been emitted, + * and %FALSE otherwise + * + * Since: 1.0 + */ +gboolean +clutter_text_activate (ClutterText *self) +{ + ClutterTextPrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + priv = self->priv; + + if (priv->activatable) + { + g_signal_emit (self, text_signals[ACTIVATE], 0); + return TRUE; + } + + return FALSE; +} + void clutter_text_set_cursor_visible (ClutterText *self, gboolean cursor_visible) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 6d861c895..326947955 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -187,6 +187,7 @@ gunichar clutter_text_get_invisible_char (ClutterText *self void clutter_text_set_max_length (ClutterText *self, gint max); gint clutter_text_get_max_length (ClutterText *self); +gboolean clutter_text_activate (ClutterText *self); G_END_DECLS From 4a43b582ac0de60464fbf0dfbebb26e549d452f6 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:05:10 +0000 Subject: [PATCH 074/147] Use clutter_text_activate() Instead of repeating the same code for the ::activate signal emission, use the clutter_text_activate() function inside the 'activate' key binding handler. --- clutter/clutter-text.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 22198a69f..cfeb3a796 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1446,16 +1446,7 @@ clutter_text_real_activate (ClutterText *self, guint keyval, ClutterModifierType modifiers) { - ClutterTextPrivate *priv = self->priv; - - if (priv->activatable) - { - g_signal_emit (self, text_signals[ACTIVATE], 0); - - return TRUE; - } - - return FALSE; + return clutter_text_activate (self); } static inline void From e28fb9ae103038e769553b488d1c084426edb966 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:25:15 +0000 Subject: [PATCH 075/147] [docs] Add more gtk-doc annotations Almost reaching full documentation. --- clutter/clutter-text.c | 50 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index cfeb3a796..f6c354ef6 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2266,6 +2266,24 @@ clutter_text_activate (ClutterText *self) return FALSE; } +/** + * clutter_text_set_cursor_visible: + * @self: a #ClutterText + * @cursor_visible: whether the cursor should be visible + * + * Sets whether the cursor of a #ClutterText actor should be + * visible or not. + * + * The color of the cursor will be the same as the text color + * unless clutter_text_set_cursor_color() has been called. + * + * The size of the cursor can be set using clutter_text_set_cursor_size(). + * + * The position of the cursor can be changed programmatically using + * clutter_text_set_cursor_position(). + * + * Since: 1.0 + */ void clutter_text_set_cursor_visible (ClutterText *self, gboolean cursor_visible) @@ -2287,6 +2305,16 @@ clutter_text_set_cursor_visible (ClutterText *self, } } +/** + * clutter_text_get_cursor_visible: + * @self: a #ClutterText + * + * Retrieves whether the cursor of a #ClutterText actor is visible. + * + * Return value: %TRUE if the cursor is visible + * + * Since: 1.0 + */ gboolean clutter_text_get_cursor_visible (ClutterText *self) { @@ -2295,6 +2323,18 @@ clutter_text_get_cursor_visible (ClutterText *self) return self->priv->cursor_visible; } +/** + * clutter_text_set_cursor_color: + * @self: a #ClutterText + * @color: the color of the cursor, or %NULL to unset it + * + * Sets the color of the cursor of a #ClutterText actor. + * + * If @color is %NULL, the cursor color will be the same as the + * text color. + * + * Since: 1.0 + */ void clutter_text_set_cursor_color (ClutterText *self, const ClutterColor *color) @@ -2320,7 +2360,15 @@ clutter_text_set_cursor_color (ClutterText *self, g_object_notify (G_OBJECT (self), "cursor-color-set"); } - +/** + * clutter_text_get_cursor_color: + * @self: a #ClutterText + * @color: return location for a #ClutterColor + * + * Retrieves the color of the cursor of a #ClutterText actor. + * + * Since: 1.0 + */ void clutter_text_get_cursor_color (ClutterText *self, ClutterColor *color) From 2209e174328790b140bb4a43c53fcdf7436d4a75 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:25:45 +0000 Subject: [PATCH 076/147] Fix ClutterText::get_selection() The clutter_text_get_selection() function was not checking the passed argument, and was still accessing the contents of the Text actor using clutter_text_get_text(). This commit also adds the last few gtk-doc annotations missing from ClutterText. --- clutter/clutter-text.c | 59 +++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index f6c354ef6..d9c813790 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2383,19 +2383,30 @@ clutter_text_get_cursor_color (ClutterText *self, *color = priv->cursor_color; } +/** + * clutter_text_get_selection: + * @self: a #ClutterText + * + * Retrieves the currently selected text. + * + * Return value: a newly allocated string containing the currently + * selected text, or %NULL. Use g_free() to free the returned + * string. + * + * Since: 1.0 + */ gchar * -clutter_text_get_selection (ClutterText *text) +clutter_text_get_selection (ClutterText *self) { ClutterTextPrivate *priv; - const gchar *utf8 = clutter_text_get_text (text); - gchar *str; - gint len; - gint start_index; - gint end_index; - gint start_offset; - gint end_offset; + gchar *str; + gint len; + gint start_index, end_index; + gint start_offset, end_offset; - priv = text->priv; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + priv = self->priv; start_index = priv->position; end_index = priv->selection_bound; @@ -2410,15 +2421,28 @@ clutter_text_get_selection (ClutterText *text) end_index = temp; } - start_offset = offset_to_bytes (utf8, start_index); - end_offset = offset_to_bytes (utf8, end_index); + start_offset = offset_to_bytes (priv->text, start_index); + end_offset = offset_to_bytes (priv->text, end_index); len = end_offset - start_offset; str = g_malloc (len + 1); - g_utf8_strncpy (str, utf8 + start_offset, end_index-start_index); + g_utf8_strncpy (str, priv->text + start_offset, end_index - start_index); + return str; } +/** + * clutter_text_set_selection_bound: + * @self: a #ClutterText + * @selection_bound: the position of the end of the selection, in characters + * + * Sets the other end of the selection, starting from the current + * cursor position. + * + * If @selection_bound is -1, the selection unset. + * + * Since: 1.0 + */ void clutter_text_set_selection_bound (ClutterText *self, gint selection_bound) @@ -2440,6 +2464,17 @@ clutter_text_set_selection_bound (ClutterText *self, } } +/** + * clutter_text_get_selection_bound: + * @self: a #ClutterText + * + * Retrieves the other end of the selection of a #ClutterText actor, + * in characters from the current cursor position. + * + * Return value: the position of the other end of the selection + * + * Since: 1.0 + */ gint clutter_text_get_selection_bound (ClutterText *self) { From a3fbdb594905cec0f822150b48c56837bc23dd73 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:41:20 +0000 Subject: [PATCH 077/147] Add ClutterText::set_selection() The clutter_text_set_selection() function is a convenience method for setting the cursor position and the selection boundary to a given position in a single call, with sanity checks for the positions. --- clutter/clutter-text.c | 38 ++++++++++++++++++++++++++++++++++++++ clutter/clutter-text.h | 3 +++ 2 files changed, 41 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index d9c813790..4d70c2fc5 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2383,6 +2383,44 @@ clutter_text_get_cursor_color (ClutterText *self, *color = priv->cursor_color; } +/** + * clutter_text_set_selection: + * @self: a #ClutterText + * @start_pos: start of the selection, in characters + * @end_pos: end of the selection, in characters + * + * Selects the region of text between @start_pos and @end_pos. + * + * This function changes the position of the cursor to match + * @start_pos and the selection bound to match @end_pos. + * + * Since: 1.0 + */ +void +clutter_text_set_selection (ClutterText *self, + gssize start_pos, + gssize end_pos) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (end_pos < 0) + end_pos = priv->n_chars; + + start_pos = MIN (priv->n_chars, start_pos); + end_pos = MIN (priv->n_chars, end_pos); + + g_object_freeze_notify (G_OBJECT (self)); + + clutter_text_set_cursor_position (self, start_pos); + clutter_text_set_selection_bound (self, end_pos); + + g_object_thaw_notify (G_OBJECT (self)); +} + /** * clutter_text_get_selection: * @self: a #ClutterText diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 326947955..bd63478ee 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -177,6 +177,9 @@ gboolean clutter_text_get_selectable (ClutterText *self void clutter_text_set_selection_bound (ClutterText *self, gint selection_bound); gint clutter_text_get_selection_bound (ClutterText *self); +void clutter_text_set_selection (ClutterText *self, + gssize start_pos, + gssize end_pos); gchar * clutter_text_get_selection (ClutterText *self); void clutter_text_set_text_visible (ClutterText *self, gboolean visible); From d85a1cb5a1dfce2364ca604c9ad33855d48d73aa Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:42:44 +0000 Subject: [PATCH 078/147] [docs] Add clutter_text_set_selection to gtk-doc Insert the newly added symbol to the ClutterText section in the API reference. --- doc/reference/clutter/clutter-sections.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 95ae63d3b..fa094506a 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1721,6 +1721,7 @@ clutter_text_set_max_length clutter_text_get_max_length clutter_text_set_selectable clutter_text_get_selectable +clutter_text_set_selection clutter_text_get_selection clutter_text_set_selection_bound clutter_text_get_selection_bound From ba586b46b304bbe3fe17b12b057574b8b1e08e32 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 13:58:54 +0000 Subject: [PATCH 079/147] [tests] Make the layout cache fail at the right place The GTest report output allows the developer to know where exactly a test unit failure happened. The test-text-cache unit makes it very hard to pinpoint the exact failure location because it relies on the output to be printed out - which is not the case when the tests are run as part of a make check. This commit makes each sub-test inside the unit fail exactly where the check function fails, which makes it easier to know which sub-test did actually fail. --- tests/conform/test-text-cache.c | 38 +++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/tests/conform/test-text-cache.c b/tests/conform/test-text-cache.c index 26687fc00..d3a2cf5ec 100644 --- a/tests/conform/test-text-cache.c +++ b/tests/conform/test-text-cache.c @@ -71,7 +71,17 @@ check_result (CallbackData *data, const char *note, if (memcmp (&test_extents, &data->label_extents, sizeof (PangoRectangle))) { if (g_test_verbose ()) - g_print ("extents are different, "); + g_print ("extents are different: expected: %d, %d, %d, %d " + "-> text: %d, %d, %d, %d\n", + test_extents.x / 1024, + test_extents.y / 1024, + test_extents.width / 1024, + test_extents.height / 1024, + data->label_extents.x / 1024, + data->label_extents.y / 1024, + data->label_extents.width / 1024, + data->label_extents.height / 1024); + fail = TRUE; } else @@ -121,27 +131,27 @@ do_tests (CallbackData *data) /* TEST 1: change the text */ clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 0"); pango_layout_set_text (data->test_layout, "Counter 0", -1); - check_result (data, "Change text", TRUE); + g_assert (check_result (data, "Change text", TRUE) == FALSE); /* TEST 2: change a single character */ clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 1"); pango_layout_set_text (data->test_layout, "Counter 1", -1); - check_result (data, "Change a single character", TRUE); + g_assert (check_result (data, "Change a single character", TRUE) == FALSE); /* TEST 3: move the label */ clutter_actor_set_position (data->label, 10, 0); - check_result (data, "Move the label", FALSE); + g_assert (check_result (data, "Move the label", FALSE) == FALSE); /* TEST 4: change the font */ clutter_text_set_font_name (CLUTTER_TEXT (data->label), "Serif 15"); fd = pango_font_description_from_string ("Serif 15"); pango_layout_set_font_description (data->test_layout, fd); pango_font_description_free (fd); - check_result (data, "Change the font", TRUE); + g_assert (check_result (data, "Change the font", TRUE) == FALSE); /* TEST 5: change the color */ clutter_text_set_color (CLUTTER_TEXT (data->label), &red); - check_result (data, "Change the color", FALSE); + g_assert (check_result (data, "Change the color", FALSE) == FALSE); /* TEST 6: change the attributes */ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); @@ -154,19 +164,19 @@ do_tests (CallbackData *data) pango_layout_set_attributes (data->test_layout, attr_list_copy); pango_attr_list_unref (attr_list_copy); pango_attr_list_unref (attr_list); - check_result (data, "Change the attributes", TRUE); + g_assert (check_result (data, "Change the attributes", TRUE) == FALSE); /* TEST 7: change the text again */ clutter_text_set_attributes (CLUTTER_TEXT (data->label), NULL); clutter_text_set_text (CLUTTER_TEXT (data->label), long_text); pango_layout_set_attributes (data->test_layout, NULL); pango_layout_set_text (data->test_layout, long_text, -1); - check_result (data, "Change the text again", TRUE); + g_assert (check_result (data, "Change the text again", TRUE) == FALSE); /* TEST 8: enable markup */ clutter_text_set_use_markup (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_markup (data->test_layout, long_text, -1); - check_result (data, "Enable markup", TRUE); + g_assert (check_result (data, "Enable markup", TRUE) == FALSE); /* This part can't be a test because Clutter won't restrict the width if wrapping and ellipsizing is disabled so the extents will @@ -181,7 +191,7 @@ do_tests (CallbackData *data) clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_END); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_END); - check_result (data, "Enable ellipsize", TRUE); + g_assert (check_result (data, "Enable ellipsize", TRUE) == FALSE); clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_NONE); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_NONE); @@ -190,13 +200,13 @@ do_tests (CallbackData *data) /* TEST 10: enable line wrap */ clutter_text_set_line_wrap (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_WORD); - check_result (data, "Enable line wrap", TRUE); + g_assert (check_result (data, "Enable line wrap", TRUE) == FALSE); /* TEST 11: change wrap mode */ clutter_text_set_line_wrap_mode (CLUTTER_TEXT (data->label), PANGO_WRAP_CHAR); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_CHAR); - check_result (data, "Change wrap mode", TRUE); + g_assert (check_result (data, "Change wrap mode", TRUE) == FALSE); /* TEST 12: enable justify */ clutter_text_set_justify (CLUTTER_TEXT (data->label), TRUE); @@ -205,12 +215,12 @@ do_tests (CallbackData *data) justification after setting the text but this fixes it. See http://bugzilla.gnome.org/show_bug.cgi?id=551865 */ pango_layout_context_changed (data->test_layout); - check_result (data, "Enable justify", TRUE); + g_assert (check_result (data, "Enable justify", TRUE) == FALSE); /* TEST 13: change alignment */ clutter_text_set_alignment (CLUTTER_TEXT (data->label), PANGO_ALIGN_RIGHT); pango_layout_set_alignment (data->test_layout, PANGO_ALIGN_RIGHT); - check_result (data, "Change alignment", TRUE); + g_assert (check_result (data, "Change alignment", TRUE) == FALSE); clutter_main_quit (); From 6b782ce4e720f07bd678ce613b81987fde13ff38 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 15:53:57 +0000 Subject: [PATCH 080/147] Fix the selection behaviour around the 0th glyph After fixing the cursor position issues around the initial glyph of the layout, the selection position needs fixing as well. The fix is similar: check if the position of the selection is 0 and provide a fast path by setting the offset to 0. --- clutter/clutter-text.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4d70c2fc5..e9eb6e174 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -811,8 +811,15 @@ cursor_paint (ClutterText *self) gint end_index; gint line_no; - start_index = offset_to_bytes (utf8, priv->position); - end_index = offset_to_bytes (utf8, priv->selection_bound); + 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) { From cdb78ec4d2bdea875fb1b4514cf4ec0618e31a8e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 19 Dec 2008 12:53:57 +0000 Subject: [PATCH 081/147] [units] Do not use fixed point and units interchangeably Clutter units are, at the moment, implemented as a value in fixed point notation using the same format as CoglFixed. This is, though, an implementation detail. For this reason, units should not be treated as CoglFixed values and should be converted to and from fixed point using the provided macros. This commit updates the usage of units and fixed point values in ClutterActor and rationalises some of the transformation code that heavily relied on the equivalency between them. --- clutter/clutter-actor.c | 557 +++++++++++++++++++++------------------- 1 file changed, 292 insertions(+), 265 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 719524c91..655e2bb74 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -772,6 +772,24 @@ clutter_actor_real_allocate (ClutterActor *self, g_object_thaw_notify (G_OBJECT (self)); } +/* like ClutterVertex, but using CoglFixed and with a w component */ +typedef struct { + CoglFixed x; + CoglFixed y; + CoglFixed z; + CoglFixed w; +} fixed_vertex_t; + +/* copies a fixed vertex into a ClutterVertex */ +static inline void +fixed_vertex_to_units (const fixed_vertex_t *f, + ClutterVertex *u) +{ + u->x = CLUTTER_UNITS_FROM_FIXED (f->x); + u->y = CLUTTER_UNITS_FROM_FIXED (f->y); + u->z = CLUTTER_UNITS_FROM_FIXED (f->z); +} + /* * Utility functions for manipulating transformation matrix * @@ -779,49 +797,114 @@ clutter_actor_real_allocate (ClutterActor *self, */ #define M(m,row,col) (m)[(col) * 4 + (row)] -/* Transform point (x,y,z) by matrix */ -static void -mtx_transform (ClutterFixed m[16], - ClutterFixed *x, ClutterFixed *y, ClutterFixed *z, - ClutterFixed *w) +/* Transforms a vertex using the passed matrix; vertex is + * an in-out parameter + */ +static inline void +mtx_transform (const ClutterFixed m[], + fixed_vertex_t *vertex) { - ClutterFixed _x, _y, _z, _w; - _x = *x; - _y = *y; - _z = *z; - _w = *w; + ClutterFixed _x, _y, _z, _w; - /* We care lot about precision here, so have to use QMUL */ - *x = COGL_FIXED_MUL (M (m, 0, 0), _x) - + COGL_FIXED_MUL (M (m, 0, 1), _y) - + COGL_FIXED_MUL (M (m, 0, 2), _z) - + COGL_FIXED_MUL (M (m, 0, 3), _w); + _x = vertex->x; + _y = vertex->y; + _z = vertex->z; + _w = vertex->w; - *y = COGL_FIXED_MUL (M (m, 1, 0), _x) - + COGL_FIXED_MUL (M (m, 1, 1), _y) - + COGL_FIXED_MUL (M (m, 1, 2), _z) - + COGL_FIXED_MUL (M (m, 1, 3), _w); + /* We care lot about precision here, so have to use MUL instead + * of FAST_MUL + */ + vertex->x = COGL_FIXED_MUL (M (m, 0, 0), _x) + + COGL_FIXED_MUL (M (m, 0, 1), _y) + + COGL_FIXED_MUL (M (m, 0, 2), _z) + + COGL_FIXED_MUL (M (m, 0, 3), _w); - *z = COGL_FIXED_MUL (M (m, 2, 0), _x) - + COGL_FIXED_MUL (M (m, 2, 1), _y) - + COGL_FIXED_MUL (M (m, 2, 2), _z) - + COGL_FIXED_MUL (M (m, 2, 3), _w); + vertex->y = COGL_FIXED_MUL (M (m, 1, 0), _x) + + COGL_FIXED_MUL (M (m, 1, 1), _y) + + COGL_FIXED_MUL (M (m, 1, 2), _z) + + COGL_FIXED_MUL (M (m, 1, 3), _w); - *w = COGL_FIXED_MUL (M (m, 3, 0), _x) - + COGL_FIXED_MUL (M (m, 3, 1), _y) - + COGL_FIXED_MUL (M (m, 3, 2), _z) - + COGL_FIXED_MUL (M (m, 3, 3), _w); + vertex->z = COGL_FIXED_MUL (M (m, 2, 0), _x) + + COGL_FIXED_MUL (M (m, 2, 1), _y) + + COGL_FIXED_MUL (M (m, 2, 2), _z) + + COGL_FIXED_MUL (M (m, 2, 3), _w); - /* Specially for Matthew: was going to put a comment here, but could not - * think of anything at all to say ;) - */ + vertex->w = COGL_FIXED_MUL (M (m, 3, 0), _x) + + COGL_FIXED_MUL (M (m, 3, 1), _y) + + COGL_FIXED_MUL (M (m, 3, 2), _z) + + COGL_FIXED_MUL (M (m, 3, 3), _w); + + /* Specially for Matthew: was going to put a comment here, but could not + * think of anything at all to say ;) + */ } #undef M +/* Help macros to scale from OpenGL <-1,1> coordinates system to our + * X-window based <0,window-size> coordinates + */ +#define MTX_GL_SCALE_X(x,w,v1,v2) (COGL_FIXED_MUL (((COGL_FIXED_DIV ((x), (w)) + COGL_FIXED_1) >> 1), (v1)) + (v2)) +#define MTX_GL_SCALE_Y(y,w,v1,v2) ((v1) - COGL_FIXED_MUL (((COGL_FIXED_DIV ((y), (w)) + COGL_FIXED_1) >> 1), (v1)) + (v2)) +#define MTX_GL_SCALE_Z(z,w,v1,v2) (MTX_GL_SCALE_X ((z), (w), (v1), (v2))) + +/* transforms a 4-tuple of coordinates using @matrix and + * places the result into a fixed @vertex + */ +static inline void +fixed_vertex_transform (const ClutterFixed matrix[], + ClutterFixed x, + ClutterFixed y, + ClutterFixed z, + ClutterFixed w, + fixed_vertex_t *vertex) +{ + fixed_vertex_t tmp = { 0, }; + + tmp.x = x; + tmp.y = y; + tmp.z = z; + tmp.w = w; + + mtx_transform (matrix, &tmp); + + *vertex = tmp; +} + +/* scales a fixed @vertex using @matrix and @viewport, and + * transforms the result into ClutterUnits, filling @vertex_p + */ +static inline void +fixed_vertex_scale (const ClutterFixed matrix[], + const fixed_vertex_t *vertex, + const ClutterFixed viewport[], + ClutterVertex *vertex_p) +{ + ClutterFixed v_x, v_y, v_width, v_height; + fixed_vertex_t tmp = { 0, }; + + tmp = *vertex; + + mtx_transform (matrix, &tmp); + + v_x = viewport[0]; + v_y = viewport[1]; + v_width = viewport[2]; + v_height = viewport[3]; + + tmp.x = MTX_GL_SCALE_X (tmp.x, tmp.w, v_width, v_x); + tmp.y = MTX_GL_SCALE_Y (tmp.y, tmp.w, v_height, v_y); + tmp.z = MTX_GL_SCALE_Z (tmp.z, tmp.w, v_width, v_x); + tmp.w = 0; + + fixed_vertex_to_units (&tmp, vertex_p); +} + /* Applies the transforms associated with this actor and its ancestors, * retrieves the resulting OpenGL modelview matrix, and uses the matrix * to transform the supplied point + * + * The point coordinates are in-out parameters */ static void clutter_actor_transform_point_relative (ClutterActor *actor, @@ -832,14 +915,33 @@ clutter_actor_transform_point_relative (ClutterActor *actor, ClutterUnit *w) { ClutterFixed mtx[16]; + fixed_vertex_t vertex = { 0, }; + + vertex.x = (x != NULL) ? CLUTTER_UNITS_TO_FIXED (*x) : 0; + vertex.y = (y != NULL) ? CLUTTER_UNITS_TO_FIXED (*y) : 0; + vertex.z = (z != NULL) ? CLUTTER_UNITS_TO_FIXED (*z) : 0; + vertex.w = (w != NULL) ? CLUTTER_UNITS_TO_FIXED (*w) : 0; cogl_push_matrix(); _clutter_actor_apply_modelview_transform_recursive (actor, ancestor); + cogl_get_modelview_matrix (mtx); - mtx_transform (mtx, x, y, z, w); + mtx_transform (mtx, &vertex); cogl_pop_matrix(); + + if (x) + *x = CLUTTER_UNITS_FROM_FIXED (vertex.x); + + if (y) + *y = CLUTTER_UNITS_FROM_FIXED (vertex.y); + + if (z) + *z = CLUTTER_UNITS_FROM_FIXED (vertex.z); + + if (w) + *w = CLUTTER_UNITS_FROM_FIXED (vertex.w); } /* Applies the transforms associated with this actor and its ancestors, @@ -854,22 +956,34 @@ clutter_actor_transform_point (ClutterActor *actor, ClutterUnit *w) { ClutterFixed mtx[16]; + fixed_vertex_t vertex = { 0, }; + + vertex.x = (x != NULL) ? CLUTTER_UNITS_TO_FIXED (*x) : 0; + vertex.y = (y != NULL) ? CLUTTER_UNITS_TO_FIXED (*y) : 0; + vertex.z = (z != NULL) ? CLUTTER_UNITS_TO_FIXED (*z) : 0; + vertex.w = (w != NULL) ? CLUTTER_UNITS_TO_FIXED (*w) : 0; cogl_push_matrix(); _clutter_actor_apply_modelview_transform_recursive (actor, NULL); + cogl_get_modelview_matrix (mtx); - mtx_transform (mtx, x, y, z, w); + mtx_transform (mtx, &vertex); cogl_pop_matrix(); -} -/* Help macros to scale from OpenGL <-1,1> coordinates system to our - * X-window based <0,window-size> coordinates - */ -#define MTX_GL_SCALE_X(x,w,v1,v2) (COGL_FIXED_MUL (((COGL_FIXED_DIV ((x), (w)) + COGL_FIXED_1) >> 1), (v1)) + (v2)) -#define MTX_GL_SCALE_Y(y,w,v1,v2) ((v1) - COGL_FIXED_MUL (((COGL_FIXED_DIV ((y), (w)) + COGL_FIXED_1) >> 1), (v1)) + (v2)) -#define MTX_GL_SCALE_Z(z,w,v1,v2) (MTX_GL_SCALE_X ((z), (w), (v1), (v2))) + if (x) + *x = CLUTTER_UNITS_FROM_FIXED (vertex.x); + + if (y) + *y = CLUTTER_UNITS_FROM_FIXED (vertex.y); + + if (z) + *z = CLUTTER_UNITS_FROM_FIXED (vertex.z); + + if (w) + *w = CLUTTER_UNITS_FROM_FIXED (vertex.w); +} /** * clutter_actor_apply_relative_transform_to_point: @@ -896,21 +1010,22 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, const ClutterVertex *point, ClutterVertex *vertex) { - ClutterVertex tmp = { 0, }; ClutterFixed v[4]; - ClutterFixed w = COGL_FIXED_1; + ClutterFixed x, y, z, w; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor)); g_return_if_fail (point != NULL); g_return_if_fail (vertex != NULL); - tmp = *point; + x = CLUTTER_UNITS_TO_FIXED (vertex->x); + y = CLUTTER_UNITS_TO_FIXED (vertex->y); + z = CLUTTER_UNITS_TO_FIXED (vertex->z); + w = COGL_FIXED_1; /* First we tranform the point using the OpenGL modelview matrix */ clutter_actor_transform_point_relative (self, ancestor, - &tmp.x, &tmp.y, &tmp.z, - &w); + &x, &y, &z, &w); cogl_get_viewport (v); @@ -918,9 +1033,12 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, * The w[3] parameter should always be 1.0 here, so we ignore it; otherwise * we would have to divide the original verts with it. */ - vertex->x = COGL_FIXED_MUL ((tmp.x + COGL_FIXED_0_5), v[2]); - vertex->y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - tmp.y), v[3]); - vertex->z = COGL_FIXED_MUL ((tmp.z + COGL_FIXED_0_5), v[2]); + vertex->x = + CLUTTER_UNITS_FROM_FIXED (COGL_FIXED_MUL ((x + COGL_FIXED_0_5), v[2])); + vertex->y = + CLUTTER_UNITS_FROM_FIXED (COGL_FIXED_MUL ((COGL_FIXED_0_5 - y), v[3])); + vertex->z = + CLUTTER_UNITS_FROM_FIXED (COGL_FIXED_MUL ((z + COGL_FIXED_0_5), v[2])); } /** @@ -940,30 +1058,35 @@ clutter_actor_apply_transform_to_point (ClutterActor *self, const ClutterVertex *point, ClutterVertex *vertex) { - ClutterVertex tmp = { 0, }; ClutterFixed mtx_p[16]; ClutterFixed v[4]; - ClutterFixed w = COGL_FIXED_1; + fixed_vertex_t tmp = { 0, }; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (point != NULL); g_return_if_fail (vertex != NULL); - tmp = *point; + tmp.x = CLUTTER_UNITS_TO_FIXED (vertex->x); + tmp.y = CLUTTER_UNITS_TO_FIXED (vertex->y); + tmp.z = CLUTTER_UNITS_TO_FIXED (vertex->z); + tmp.w = COGL_FIXED_1; /* First we tranform the point using the OpenGL modelview matrix */ - clutter_actor_transform_point (self, &tmp.x, &tmp.y, &tmp.z, &w); + clutter_actor_transform_point (self, &tmp.x, &tmp.y, &tmp.z, &tmp.w); cogl_get_projection_matrix (mtx_p); cogl_get_viewport (v); /* Now, transform it again with the projection matrix */ - mtx_transform (mtx_p, &tmp.x, &tmp.y, &tmp.z, &w); + mtx_transform (mtx_p, &tmp); /* Finaly translate from OpenGL coords to window coords */ - vertex->x = MTX_GL_SCALE_X (tmp.x, w, v[2], v[0]); - vertex->y = MTX_GL_SCALE_Y (tmp.y, w, v[3], v[1]); - vertex->z = MTX_GL_SCALE_Z (tmp.z, w, v[2], v[0]); + vertex->x = + CLUTTER_UNITS_FROM_FIXED (MTX_GL_SCALE_X (tmp.x, tmp.w, v[2], v[0])); + vertex->y = + CLUTTER_UNITS_FROM_FIXED (MTX_GL_SCALE_Y (tmp.y, tmp.w, v[3], v[1])); + vertex->z = + CLUTTER_UNITS_FROM_FIXED (MTX_GL_SCALE_Z (tmp.z, tmp.w, v[2], v[0])); } /* Recursively tranform supplied vertices with the tranform for the current @@ -971,66 +1094,27 @@ clutter_actor_apply_transform_to_point (ClutterActor *self, * for all the vertices in one go). */ static void -clutter_actor_transform_vertices_relative (ClutterActor *self, - ClutterActor *ancestor, - ClutterVertex verts[4], - ClutterFixed w[4]) +clutter_actor_transform_vertices_relative (ClutterActor *self, + ClutterActor *ancestor, + fixed_vertex_t vertices[]) { - ClutterFixed mtx[16]; - ClutterFixed _x, _y, _z, _w; + ClutterActorPrivate *priv = self->priv; + ClutterFixed mtx[16]; + ClutterFixed width, height; + + width = CLUTTER_UNITS_TO_FIXED (priv->allocation.x2 - priv->allocation.x1); + height = CLUTTER_UNITS_TO_FIXED (priv->allocation.y2 - priv->allocation.y1); cogl_push_matrix(); _clutter_actor_apply_modelview_transform_recursive (self, ancestor); + cogl_get_modelview_matrix (mtx); - _x = 0; - _y = 0; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[0].x = _x; - verts[0].y = _y; - verts[0].z = _z; - w[0] = _w; - - _x = self->priv->allocation.x2 - self->priv->allocation.x1; - _y = 0; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[1].x = _x; - verts[1].y = _y; - verts[1].z = _z; - w[1] = _w; - - _x = 0; - _y = self->priv->allocation.y2 - self->priv->allocation.y1; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[2].x = _x; - verts[2].y = _y; - verts[2].z = _z; - w[2] = _w; - - _x = self->priv->allocation.x2 - self->priv->allocation.x1; - _y = self->priv->allocation.y2 - self->priv->allocation.y1; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[3].x = _x; - verts[3].y = _y; - verts[3].z = _z; - w[3] = _w; + fixed_vertex_transform (mtx, 0, 0, 0, COGL_FIXED_1, &vertices[0]); + fixed_vertex_transform (mtx, width, 0, 0, COGL_FIXED_1, &vertices[1]); + fixed_vertex_transform (mtx, 0, height, 0, COGL_FIXED_1, &vertices[2]); + fixed_vertex_transform (mtx, width, height, 0, COGL_FIXED_1, &vertices[3]); cogl_pop_matrix(); } @@ -1045,12 +1129,15 @@ clutter_actor_transform_and_project_box (ClutterActor *self, const ClutterActorBox *box, ClutterVertex verts[4]) { - ClutterActor *stage; - ClutterFixed mtx[16]; - ClutterFixed mtx_p[16]; - ClutterFixed _x, _y, _z, _w; - ClutterFixed w[4]; - ClutterFixed v[4]; + ClutterActor *stage; + ClutterFixed mtx[16]; + ClutterFixed mtx_p[16]; + ClutterFixed v[4]; + ClutterFixed width, height; + fixed_vertex_t vertices[4]; + + width = CLUTTER_UNITS_TO_FIXED (box->x2 - box->x1); + height = CLUTTER_UNITS_TO_FIXED (box->y2 - box->y1); /* We essentially have to dupe some code from clutter_redraw() here * to make sure GL Matrices etc are initialised if we're called and we @@ -1071,102 +1158,25 @@ clutter_actor_transform_and_project_box (ClutterActor *self, _clutter_stage_maybe_setup_viewport (CLUTTER_STAGE (stage)); cogl_push_matrix(); + _clutter_actor_apply_modelview_transform_recursive (self, NULL); cogl_get_modelview_matrix (mtx); - _x = 0; - _y = 0; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[0].x = _x; - verts[0].y = _y; - verts[0].z = _z; - w[0] = _w; - - _x = box->x2 - box->x1; - _y = 0; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[1].x = _x; - verts[1].y = _y; - verts[1].z = _z; - w[1] = _w; - - _x = 0; - _y = box->y2 - box->y1; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[2].x = _x; - verts[2].y = _y; - verts[2].z = _z; - w[2] = _w; - - _x = box->x2 - box->x1; - _y = box->y2 - box->y1; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[3].x = _x; - verts[3].y = _y; - verts[3].z = _z; - w[3] = _w; + fixed_vertex_transform (mtx, 0, 0, 0, COGL_FIXED_1, &vertices[0]); + fixed_vertex_transform (mtx, width, 0, 0, COGL_FIXED_1, &vertices[1]); + fixed_vertex_transform (mtx, 0, height, 0, COGL_FIXED_1, &vertices[2]); + fixed_vertex_transform (mtx, width, height, 0, COGL_FIXED_1, &vertices[3]); cogl_pop_matrix(); cogl_get_projection_matrix (mtx_p); cogl_get_viewport (v); - mtx_transform (mtx_p, - &verts[0].x, - &verts[0].y, - &verts[0].z, - &w[0]); - - verts[0].x = MTX_GL_SCALE_X (verts[0].x, w[0], v[2], v[0]); - verts[0].y = MTX_GL_SCALE_Y (verts[0].y, w[0], v[3], v[1]); - verts[0].z = MTX_GL_SCALE_Z (verts[0].z, w[0], v[2], v[0]); - - mtx_transform (mtx_p, - &verts[1].x, - &verts[1].y, - &verts[1].z, - &w[1]); - - verts[1].x = MTX_GL_SCALE_X (verts[1].x, w[1], v[2], v[0]); - verts[1].y = MTX_GL_SCALE_Y (verts[1].y, w[1], v[3], v[1]); - verts[1].z = MTX_GL_SCALE_Z (verts[1].z, w[1], v[2], v[0]); - - mtx_transform (mtx_p, - &verts[2].x, - &verts[2].y, - &verts[2].z, - &w[2]); - - verts[2].x = MTX_GL_SCALE_X (verts[2].x, w[2], v[2], v[0]); - verts[2].y = MTX_GL_SCALE_Y (verts[2].y, w[2], v[3], v[1]); - verts[2].z = MTX_GL_SCALE_Z (verts[2].z, w[2], v[2], v[0]); - - mtx_transform (mtx_p, - &verts[3].x, - &verts[3].y, - &verts[3].z, - &w[3]); - - verts[3].x = MTX_GL_SCALE_X (verts[3].x, w[3], v[2], v[0]); - verts[3].y = MTX_GL_SCALE_Y (verts[3].y, w[3], v[3], v[1]); - verts[3].z = MTX_GL_SCALE_Z (verts[3].z, w[3], v[2], v[0]); + fixed_vertex_scale (mtx_p, &vertices[0], v, &verts[0]); + fixed_vertex_scale (mtx_p, &vertices[1], v, &verts[1]); + fixed_vertex_scale (mtx_p, &vertices[2], v, &verts[2]); + fixed_vertex_scale (mtx_p, &vertices[3], v, &verts[3]); } /** @@ -1181,10 +1191,10 @@ clutter_actor_transform_and_project_box (ClutterActor *self, * actor in the plane of @ancestor. The returned vertices relate to * the #ClutterActorBox coordinates as follows: * - * v[0] contains (x1, y1) - * v[1] contains (x2, y1) - * v[2] contains (x1, y2) - * v[3] contains (x2, y2) + * @verts[0] contains (x1, y1) + * @verts[1] contains (x2, y1) + * @verts[2] contains (x1, y2) + * @verts[3] contains (x2, y2) * * * If @ancestor is %NULL the ancestor will be the #ClutterStage. In @@ -1197,12 +1207,13 @@ clutter_actor_transform_and_project_box (ClutterActor *self, void clutter_actor_get_allocation_vertices (ClutterActor *self, ClutterActor *ancestor, - ClutterVertex verts[4]) + ClutterVertex verts[]) { - ClutterFixed v[4]; - ClutterFixed w[4]; - ClutterActorPrivate *priv; - ClutterActor *stage; + ClutterActorPrivate *priv; + ClutterActor *stage; + ClutterFixed v[4]; + fixed_vertex_t vertices[4]; + fixed_vertex_t tmp = { 0, }; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor)); @@ -1234,28 +1245,33 @@ clutter_actor_get_allocation_vertices (ClutterActor *self, if (priv->needs_allocation) _clutter_stage_maybe_relayout (stage); - clutter_actor_transform_vertices_relative (self, ancestor, verts, w); + clutter_actor_transform_vertices_relative (self, ancestor, vertices); + cogl_get_viewport (v); /* - * The w[3] parameter should always be 1.0 here, so we ignore it; otherwise - * we would have to devide the original verts with it. + * The w[3] parameter should always be 1.0 here, so we ignore it; + * otherwise we would have to divide the original verts with it. */ - verts[0].x = COGL_FIXED_MUL ((verts[0].x + COGL_FIXED_0_5), v[2]); - verts[0].y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - verts[0].y), v[3]); - verts[0].z = COGL_FIXED_MUL ((verts[0].z + COGL_FIXED_0_5), v[2]); + tmp.x = COGL_FIXED_MUL ((vertices[0].x + COGL_FIXED_0_5), v[2]); + tmp.y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - vertices[0].y), v[3]); + tmp.z = COGL_FIXED_MUL ((vertices[0].z + COGL_FIXED_0_5), v[2]); + fixed_vertex_to_units (&tmp, &verts[0]); - verts[1].x = COGL_FIXED_MUL ((verts[1].x + COGL_FIXED_0_5), v[2]); - verts[1].y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - verts[1].y), v[3]); - verts[1].z = COGL_FIXED_MUL ((verts[1].z + COGL_FIXED_0_5), v[2]); + tmp.x = COGL_FIXED_MUL ((vertices[1].x + COGL_FIXED_0_5), v[2]); + tmp.y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - vertices[1].y), v[3]); + tmp.z = COGL_FIXED_MUL ((vertices[1].z + COGL_FIXED_0_5), v[2]); + fixed_vertex_to_units (&tmp, &verts[1]); - verts[2].x = COGL_FIXED_MUL ((verts[2].x + COGL_FIXED_0_5), v[2]); - verts[2].y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - verts[2].y), v[3]); - verts[2].z = COGL_FIXED_MUL ((verts[2].z + COGL_FIXED_0_5), v[2]); + tmp.x = COGL_FIXED_MUL ((vertices[2].x + COGL_FIXED_0_5), v[2]); + tmp.y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - vertices[2].y), v[3]); + tmp.z = COGL_FIXED_MUL ((vertices[2].z + COGL_FIXED_0_5), v[2]); + fixed_vertex_to_units (&tmp, &verts[2]); - verts[3].x = COGL_FIXED_MUL ((verts[3].x + COGL_FIXED_0_5), v[2]); - verts[3].y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - verts[3].y), v[3]); - verts[3].z = COGL_FIXED_MUL ((verts[3].z + COGL_FIXED_0_5), v[2]); + tmp.x = COGL_FIXED_MUL ((vertices[3].x + COGL_FIXED_0_5), v[2]); + tmp.y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - vertices[3].y), v[3]); + tmp.z = COGL_FIXED_MUL ((vertices[3].z + COGL_FIXED_0_5), v[2]); + fixed_vertex_to_units (&tmp, &verts[3]); } /** @@ -6836,17 +6852,18 @@ clutter_scriptable_iface_init (ClutterScriptableIface *iface) * Since: 0.6 */ gboolean -clutter_actor_transform_stage_point (ClutterActor *self, - ClutterUnit x, - ClutterUnit y, - ClutterUnit *x_out, - ClutterUnit *y_out) +clutter_actor_transform_stage_point (ClutterActor *self, + ClutterUnit x, + ClutterUnit y, + ClutterUnit *x_out, + ClutterUnit *y_out) { ClutterVertex v[4]; ClutterFixed ST[3][3]; ClutterFixed RQ[3][3]; int du, dv, xi, yi; - ClutterFixed xf, yf, wf, px, py, det; + ClutterUnit px, py; + ClutterFixed xf, yf, wf, det; ClutterActorPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); @@ -6859,11 +6876,12 @@ clutter_actor_transform_stage_point (ClutterActor *self, * * http://www.cs.cmu.edu/~ph/texfund/texfund.pdf * - * and the sample implementaion at http://www.cs.cmu.edu/~ph/src/texfund/. + * and the sample implementation at http://www.cs.cmu.edu/~ph/src/texfund/. * - * Our texture is a rectangle with origin [0,0], so we are mapping from quad - * to rectangle only, which significantly simplifies things; the function - * calls have been unrolled, and most of the math is done in fixed point. + * Our texture is a rectangle with origin [0, 0], so we are mapping from + * quad to rectangle only, which significantly simplifies things; the + * function calls have been unrolled, and most of the math is done in fixed + * point. */ clutter_actor_get_abs_allocation_vertices (self, v); @@ -6880,9 +6898,11 @@ clutter_actor_transform_stage_point (ClutterActor *self, #define FP2FX COGL_FIXED_FROM_FLOAT #define FX2FP COGL_FIXED_TO_DOUBLE +#define UX2FP CLUTTER_UNITS_TO_FLOAT +#define UX2FX CLUTTER_UNITS_TO_FIXED #define FP2INT CLUTTER_FLOAT_TO_INT -#define DET2X(a,b,c,d) (COGL_FIXED_MUL (a, d) - COGL_FIXED_MUL (b, c)) -#define DET2FP(a,b,c,d) (a*d - b*c) +#define DET2X(a,b,c,d) (COGL_FIXED_MUL ((a), (d)) - COGL_FIXED_MUL ((b), (c))) +#define DET2FP(a,b,c,d) ((a) * (d) - (b) * (c)) /* * First, find mapping from unit uv square to xy quadrilateral; this @@ -6894,20 +6914,21 @@ clutter_actor_transform_stage_point (ClutterActor *self, py = v[0].y - v[1].y + v[3].y - v[2].y; if (!px && !py) - { /* affine transform */ - RQ[0][0] = v[1].x - v[0].x; - RQ[1][0] = v[3].x - v[1].x; - RQ[2][0] = v[0].x; - RQ[0][1] = v[1].y - v[0].y; - RQ[1][1] = v[3].y - v[1].y; - RQ[2][1] = v[0].y; + { + /* affine transform */ + RQ[0][0] = UX2FX (v[1].x - v[0].x); + RQ[1][0] = UX2FX (v[3].x - v[1].x); + RQ[2][0] = UX2FX (v[0].x); + RQ[0][1] = UX2FX (v[1].y - v[0].y); + RQ[1][1] = UX2FX (v[3].y - v[1].y); + RQ[2][1] = UX2FX (v[0].y); RQ[0][2] = 0; RQ[1][2] = 0; RQ[2][2] = COGL_FIXED_1; } else - { /* - * projective transform + { + /* projective transform * * Must do this in floating point, as the del value can overflow the * range of ClutterFixed for large actors. @@ -6917,13 +6938,12 @@ clutter_actor_transform_stage_point (ClutterActor *self, */ double dx1, dx2, dy1, dy2, del; - dx1 = FX2FP (v[1].x - v[3].x); - dx2 = FX2FP (v[2].x - v[3].x); - dy1 = FX2FP (v[1].y - v[3].y); - dy2 = FX2FP (v[2].y - v[3].y); - - del = DET2FP (dx1,dx2, dy1,dy2); + dx1 = UX2FP (v[1].x - v[3].x); + dx2 = UX2FP (v[2].x - v[3].x); + dy1 = UX2FP (v[1].y - v[3].y); + dy2 = UX2FP (v[2].y - v[3].y); + del = DET2FP (dx1, dx2, dy1, dy2); if (!del) return FALSE; @@ -6931,16 +6951,20 @@ clutter_actor_transform_stage_point (ClutterActor *self, * The division here needs to be done in floating point for * precisions reasons. */ - RQ[0][2] = FP2FX (DET2FP (FX2FP(px),dx2, FX2FP(py),dy2) / del); - RQ[1][2] = FP2FX (DET2FP (dx1,FX2FP(px), dy1,FX2FP(py)) / del); - RQ[1][2] = FP2FX (DET2FP(dx1,FX2FP(px), dy1,FX2FP(py))/del); + RQ[0][2] = FP2FX (DET2FP (UX2FP (px), dx2, UX2FP (py), dy2) / del); + RQ[1][2] = FP2FX (DET2FP (dx1, UX2FP (px), dy1, UX2FP (py)) / del); + RQ[1][2] = FP2FX (DET2FP (dx1, UX2FP (px), dy1, UX2FP (py)) / del); RQ[2][2] = COGL_FIXED_1; - RQ[0][0] = v[1].x - v[0].x + COGL_FIXED_MUL (RQ[0][2], v[1].x); - RQ[1][0] = v[2].x - v[0].x + COGL_FIXED_MUL (RQ[1][2], v[2].x); - RQ[2][0] = v[0].x; - RQ[0][1] = v[1].y - v[0].y + COGL_FIXED_MUL (RQ[0][2], v[1].y); - RQ[1][1] = v[2].y - v[0].y + COGL_FIXED_MUL (RQ[1][2], v[2].y); - RQ[2][1] = v[0].y; + RQ[0][0] = UX2FX (v[1].x - v[0].x) + + COGL_FIXED_MUL (RQ[0][2], UX2FX (v[1].x)); + RQ[1][0] = UX2FX (v[2].x - v[0].x) + + COGL_FIXED_MUL (RQ[1][2], UX2FX (v[2].x)); + RQ[2][0] = UX2FX (v[0].x); + RQ[0][1] = UX2FX (v[1].y - v[0].y) + + COGL_FIXED_MUL (RQ[0][2], UX2FX (v[1].y)); + RQ[1][1] = UX2FX (v[2].y - v[0].y) + + COGL_FIXED_MUL (RQ[1][2], UX2FX (v[2].y)); + RQ[2][1] = UX2FX (v[0].y); } /* @@ -6979,24 +7003,27 @@ clutter_actor_transform_stage_point (ClutterActor *self, return FALSE; /* - * Now transform our point with the ST matrix; the notional w coordiance - * is 1, hence the last part is simply added. + * Now transform our point with the ST matrix; the notional w + * coordinate is 1, hence the last part is simply added. */ xi = CLUTTER_UNITS_TO_DEVICE (x); yi = CLUTTER_UNITS_TO_DEVICE (y); - xf = xi*ST[0][0] + yi*ST[1][0] + ST[2][0]; - yf = xi*ST[0][1] + yi*ST[1][1] + ST[2][1]; - wf = xi*ST[0][2] + yi*ST[1][2] + ST[2][2]; + xf = xi * ST[0][0] + yi * ST[1][0] + ST[2][0]; + yf = xi * ST[0][1] + yi * ST[1][1] + ST[2][1]; + wf = xi * ST[0][2] + yi * ST[1][2] + ST[2][2]; /* * The division needs to be done in floating point for precision reasons. */ if (x_out) *x_out = CLUTTER_UNITS_FROM_FLOAT (FX2FP (xf) / FX2FP (wf)); + if (y_out) *y_out = CLUTTER_UNITS_FROM_FLOAT (FX2FP (yf) / FX2FP (wf)); +#undef UX2FX +#undef UX2FP #undef FP2FX #undef FX2FP #undef FP2INT From f6e9a701e42ca4a01a41eaa7ab220bf48c23a49f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 19 Dec 2008 13:03:53 +0000 Subject: [PATCH 082/147] [tests] Fix the actor detection The test-unproject interactive test assumed that only the rectangle was on the stage. As it turns out, labels can be picked as well. --- tests/interactive/test-unproject.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/interactive/test-unproject.c b/tests/interactive/test-unproject.c index 422647e84..6af4a22cb 100644 --- a/tests/interactive/test-unproject.c +++ b/tests/interactive/test-unproject.c @@ -5,13 +5,15 @@ #include #include -ClutterActor *label; - #define RECT_L 200 #define RECT_T 150 #define RECT_W 320 #define RECT_H 240 +static ClutterActor *test_rectangle = NULL; +static ClutterActor *label = NULL; + + static gboolean on_event (ClutterStage *stage, ClutterEvent *event, @@ -29,15 +31,14 @@ on_event (ClutterStage *stage, actor = clutter_stage_get_actor_at_pos (stage, x, y); - if (clutter_actor_transform_stage_point (actor, - CLUTTER_UNITS_FROM_DEVICE (x), - CLUTTER_UNITS_FROM_DEVICE (y), - &xu2, &yu2)) + CLUTTER_UNITS_FROM_DEVICE (x), + CLUTTER_UNITS_FROM_DEVICE (y), + &xu2, &yu2)) { gchar *txt; - if (actor != CLUTTER_ACTOR (stage)) + if (actor == test_rectangle) txt = g_strdup_printf ("Click on rectangle\n" "Screen coords: [%d, %d]\n" "Local coords : [%d, %d]", @@ -117,6 +118,7 @@ test_unproject_main (int argc, char *argv[]) clutter_actor_set_rotation (rect, CLUTTER_Y_AXIS, rotate_y, 0, 0, 0); clutter_actor_set_rotation (rect, CLUTTER_Z_AXIS, rotate_z, 0, 0, 0); clutter_group_add (CLUTTER_GROUP (stage), rect); + test_rectangle = rect; txt = g_strdup_printf ("Rectangle: L %d, R %d, T %d, B %d\n" "Rotation : x %d, y %d, z %d", @@ -146,5 +148,8 @@ test_unproject_main (int argc, char *argv[]) clutter_main(); + test_rectangle = NULL; + label = NULL; + return EXIT_SUCCESS; } From cf5d69139d0ef7f380616e4eaf18fc82721f62f5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 19 Dec 2008 13:42:17 +0000 Subject: [PATCH 083/147] [texture] Do not mix fixed point and units values Like we did for ClutterActor in commit cdb78ec4, fix ClutterTexture usage of CoglFixed and ClutterUnit values without conversion between the two types. --- clutter/clutter-texture.c | 40 +++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index dd95fc0a1..e636795e3 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -417,7 +417,8 @@ clutter_texture_set_fbo_projection (ClutterActor *self) ClutterTexturePrivate *priv = CLUTTER_TEXTURE (self)->priv; ClutterVertex verts[4]; ClutterFixed viewport[4]; - ClutterFixed x_min, x_max, y_min, y_max; + ClutterUnit x_min, x_max, y_min, y_max; + ClutterFixed tx_min, tx_max, ty_min, ty_max; ClutterPerspective perspective; ClutterStage *stage; ClutterFixed tan_angle, near_size; @@ -450,21 +451,26 @@ clutter_texture_set_fbo_projection (ClutterActor *self) /* Convert the coordinates back to [-1,1] range */ cogl_get_viewport (viewport); - x_min = COGL_FIXED_DIV (x_min, viewport[2]) * 2 - COGL_FIXED_1; - x_max = COGL_FIXED_DIV (x_max, viewport[2]) * 2 - COGL_FIXED_1; - y_min = COGL_FIXED_DIV (y_min, viewport[3]) * 2 - COGL_FIXED_1; - y_max = COGL_FIXED_DIV (y_max, viewport[3]) * 2 - COGL_FIXED_1; + + tx_min = COGL_FIXED_DIV (CLUTTER_UNITS_TO_FIXED (x_min), viewport[2]) + * 2 - COGL_FIXED_1; + tx_max = COGL_FIXED_DIV (CLUTTER_UNITS_TO_FIXED (x_max), viewport[2]) + * 2 - COGL_FIXED_1; + ty_min = COGL_FIXED_DIV (CLUTTER_UNITS_TO_FIXED (y_min), viewport[3]) + * 2 - COGL_FIXED_1; + ty_max = COGL_FIXED_DIV (CLUTTER_UNITS_TO_FIXED (y_max), viewport[3]) + * 2 - COGL_FIXED_1; /* Set up a projection matrix so that the actor will be projected as if it was drawn at its original location */ - tan_angle = clutter_tani (CLUTTER_ANGLE_FROM_DEGX (perspective.fovy / 2)); + tan_angle = cogl_angle_tan (COGL_ANGLE_FROM_DEGX (perspective.fovy / 2)); near_size = COGL_FIXED_MUL (perspective.z_near, tan_angle); - cogl_frustum (COGL_FIXED_MUL (x_min, near_size), - COGL_FIXED_MUL (x_max, near_size), - COGL_FIXED_MUL (-y_min, near_size), - COGL_FIXED_MUL (-y_max, near_size), - perspective.z_near, perspective.z_far); + cogl_frustum (COGL_FIXED_MUL (tx_min, near_size), + COGL_FIXED_MUL (tx_max, near_size), + COGL_FIXED_MUL (-ty_min, near_size), + COGL_FIXED_MUL (-ty_max, near_size), + perspective.z_near, perspective.z_far); } static void @@ -544,8 +550,10 @@ clutter_texture_paint (ClutterActor *self) /* Restore the perspective matrix using cogl_perspective so that the inverse matrix will be right */ - cogl_perspective (perspective.fovy, perspective.aspect, - perspective.z_near, perspective.z_far); + cogl_perspective (perspective.fovy, + perspective.aspect, + perspective.z_near, + perspective.z_far); /* If there is a shader on top of the shader stack, turn it back on. */ if (shader) @@ -588,9 +596,9 @@ clutter_texture_paint (ClutterActor *self) /* Paint will have translated us */ cogl_texture_rectangle (priv->texture, 0, 0, - COGL_FIXED_FROM_INT (x_2 - x_1), - COGL_FIXED_FROM_INT (y_2 - y_1), - 0, 0, t_w, t_h); + COGL_FIXED_FROM_INT (x_2 - x_1), + COGL_FIXED_FROM_INT (y_2 - y_1), + 0, 0, t_w, t_h); } static void From 4a934c363c559896c2dede5374cc33b63a7c01a7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 22 Dec 2008 13:24:52 +0000 Subject: [PATCH 084/147] Do not include cogl-pango.h multiple times The clutter-private.h header already includes cogl-pango.h with the correct inclusion path, because the main context stores a pointer to the font map. There is no need for clutter-text.c to include cogl-pango.h again since it already includes clutter-private.h. --- clutter/clutter-private.h | 3 ++- clutter/clutter-text.c | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 81e030adf..fb8aea93b 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -38,6 +38,8 @@ #include +#include "pango/cogl-pango.h" + #include "clutter-backend.h" #include "clutter-event.h" #include "clutter-feature.h" @@ -45,7 +47,6 @@ #include "clutter-stage-manager.h" #include "clutter-stage-window.h" #include "clutter-stage.h" -#include "pango/cogl-pango.h" G_BEGIN_DECLS diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index e9eb6e174..ab2e323d7 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -54,11 +54,9 @@ #include "clutter-enum-types.h" #include "clutter-keysyms.h" #include "clutter-main.h" -#include "clutter-private.h" +#include "clutter-private.h" /* includes pango/cogl-pango.h */ #include "clutter-units.h" -#include "cogl-pango.h" - #define DEFAULT_FONT_NAME "Sans 10" /* cursor width in pixels */ From b021f518c18eb142791238cb4ecde8b1ea3707c0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 22 Dec 2008 13:29:10 +0000 Subject: [PATCH 085/147] Use "" as the default value for the :text property This follows the convention of GtkLabel/GtkEntry in GTK+ and the old ClutterEntry. It makes it easier to use strlen/strcmp etc on the output, since we can assume that it is always a string. This commit also updates the test unit for ClutterText to verify that the clutter_text_get_text() function also returns an empty string when a ClutterText actor has been created. --- clutter/clutter-text.c | 12 +++++++++--- tests/conform/test-clutter-text.c | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index ab2e323d7..480ce65e7 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1519,7 +1519,7 @@ clutter_text_class_init (ClutterTextClass *klass) pspec = g_param_spec_string ("text", "Text", "The text to render", - NULL, + "", CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TEXT, pspec); @@ -1981,7 +1981,11 @@ clutter_text_init (ClutterText *self) for (i = 0; i < N_CACHED_LAYOUTS; i++) priv->cached_layouts[i].layout = NULL; - priv->text = NULL; + /* default to "" so that clutter_text_get_text() will + * return a valid string and we can safely call strlen() + * or strcmp() on it + */ + priv->text = g_strdup (""); priv->text_color = default_text_color; priv->cursor_color = default_cursor_color; @@ -2640,7 +2644,9 @@ clutter_text_get_text (ClutterText *self) * @self: a #ClutterText * @text: the text to set * - * Sets the contents of a #ClutterText actor. + * Sets the contents of a #ClutterText actor. The @text string + * must not be %NULL; to unset the current contents of the + * #ClutterText actor simply pass "" (an empty string). * * Since: 1.0 */ diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c index b47002a40..20cdb538c 100644 --- a/tests/conform/test-clutter-text.c +++ b/tests/conform/test-clutter-text.c @@ -76,7 +76,8 @@ test_text_empty (TestConformSimpleFixture *fixture, { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); - g_assert (clutter_text_get_text (text) == NULL); + g_assert_cmpstr (clutter_text_get_text (text), ==, ""); + g_assert_cmpint (*clutter_text_get_text (text), ==, '\0'); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); clutter_actor_destroy (CLUTTER_ACTOR (text)); From b8b8b155c45e54e8c2bd4d7e161a28b55a5088e1 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 13:55:51 +0000 Subject: [PATCH 086/147] Add signals for Backend options The ClutterBackend instance at the moment lacks the ability to notify runtime changes of the font options and the resolution. For this reason, this commit adds a ::resolution-changed and a ::font-changed signals to the Backend class. The ::resolution-changed signal is emitted when set_resolution() is called with a different DPI; ::font-changed is emitted when the cairo_font_options_t* changes from the default. --- clutter/clutter-backend.c | 36 ++++++++++++++++++++++++++++++++++-- clutter/clutter-backend.h | 4 ++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 9a599c16b..086f27494 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -44,6 +44,7 @@ #include "clutter-backend.h" #include "clutter-debug.h" #include "clutter-fixed.h" +#include "clutter-marshal.h" #include "clutter-private.h" G_DEFINE_ABSTRACT_TYPE (ClutterBackend, clutter_backend, G_TYPE_OBJECT); @@ -62,6 +63,16 @@ struct _ClutterBackendPrivate cairo_font_options_t *font_options; }; +enum +{ + RESOLUTION_CHANGED, + FONT_CHANGED, + + LAST_SIGNAL +}; + +static guint backend_signals[LAST_SIGNAL] = { 0, }; + static void clutter_backend_dispose (GObject *gobject) { @@ -89,6 +100,24 @@ clutter_backend_class_init (ClutterBackendClass *klass) gobject_class->dispose = clutter_backend_dispose; g_type_class_add_private (gobject_class, sizeof (ClutterBackendPrivate)); + + backend_signals[RESOLUTION_CHANGED] = + g_signal_new (I_("resolution-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterBackendClass, resolution_changed), + NULL, NULL, + clutter_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + backend_signals[FONT_CHANGED] = + g_signal_new (I_("font-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterBackendClass, font_changed), + NULL, NULL, + clutter_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void @@ -388,8 +417,9 @@ clutter_backend_set_resolution (ClutterBackend *backend, priv->resolution = fixed_dpi; if (CLUTTER_CONTEXT ()->font_map) - cogl_pango_font_map_set_resolution (CLUTTER_CONTEXT ()->font_map, - COGL_FIXED_TO_FLOAT (fixed_dpi)); + cogl_pango_font_map_set_resolution (CLUTTER_CONTEXT ()->font_map, dpi); + + g_signal_emit (backend, backend_signals[RESOLUTION_CHANGED], 0); } /** @@ -445,6 +475,8 @@ clutter_backend_set_font_options (ClutterBackend *backend, priv->font_options = cairo_font_options_copy (options); else priv->font_options = NULL; + + g_signal_emit (backend, backend_signals[FONT_CHANGED], 0); } } diff --git a/clutter/clutter-backend.h b/clutter/clutter-backend.h index 27f49c46f..0669d4faa 100644 --- a/clutter/clutter-backend.h +++ b/clutter/clutter-backend.h @@ -77,6 +77,10 @@ struct _ClutterBackendClass ClutterStage *stage); void (* ensure_context) (ClutterBackend *backend, ClutterStage *stage); + + /* signals */ + void (* resolution_changed) (ClutterBackend *backend); + void (* font_changed) (ClutterBackend *backend); }; GType clutter_backend_get_type (void) G_GNUC_CONST; From 78628edf2f6f02db3d283169d93ae2d21e63a1ec Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 14:06:55 +0000 Subject: [PATCH 087/147] Add a per-actor PangoContext Rendering text inside an actor is pretty much impossible without using internal API to create the various pieces like the PangoContext and the font map. Each actor should have the ability to create a PangoContext, which is the only object needed to generate layouts and change the various Pango settings. This commit adds a clutter_actor_get_pango_context() function that creates a PangoContext inside the ClutterActor private data and allows the creation of PangoLayouts when needed. If the actor already has a PangoContext, the same instance is returned. The PangoContext is created only on demand. --- clutter/clutter-actor.c | 25 +++++++++++++++++++++++-- clutter/clutter-actor.h | 4 ++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 6a30c1c11..992ffde0b 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -221,7 +221,9 @@ struct _ClutterActorPrivate /* cached allocation is invalid (request has changed, probably) */ guint needs_allocation : 1; - guint has_clip : 1; + guint show_on_set_parent : 1; + guint has_clip : 1; + ClutterUnit clip[4]; /* Rotation angles */ @@ -260,7 +262,7 @@ struct _ClutterActorPrivate ShaderData *shader_data; - gboolean show_on_set_parent; + PangoContext *pango_context; }; enum @@ -7589,3 +7591,22 @@ clutter_actor_grab_key_focus (ClutterActor *self) if (parent && CLUTTER_IS_STAGE (parent)) clutter_stage_set_key_focus (CLUTTER_STAGE (parent), self); } + +PangoContext * +clutter_actor_get_pango_context (ClutterActor *self) +{ + ClutterActorPrivate *priv; + ClutterMainContext *ctx; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); + + priv = self->priv; + + if (priv->pango_context) + return priv->pango_context; + + ctx = CLUTTER_CONTEXT (); + priv->pango_context = _clutter_context_create_pango_context (ctx); + + return priv->pango_context; +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 1874e283b..9461e9fa3 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -31,6 +31,8 @@ /* clutter-actor.h */ #include +#include + #include #include #include @@ -563,6 +565,8 @@ gboolean clutter_actor_get_paint_visibility (ClutterActor *self); void clutter_actor_grab_key_focus (ClutterActor *self); +PangoContext *clutter_actor_get_pango_context (ClutterActor *self); + G_END_DECLS #endif /* _HAVE_CLUTTER_ACTOR_H */ From a865a5d4ead867072d67f1ff57ac5a70d8589159 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 14:11:27 +0000 Subject: [PATCH 088/147] Create the PangoContext of the Text actor Instead of creating a single, private PangoContext for every Text actor, use the newly added clutter_actor_get_pango_context() function. --- clutter/clutter-text.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 480ce65e7..a58c92bb3 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -75,9 +75,6 @@ typedef struct _LayoutCache LayoutCache; -/* Probably move into main */ -static PangoContext *_context = NULL; - static const ClutterColor default_cursor_color = { 0, 0, 0, 255 }; static const ClutterColor default_text_color = { 0, 0, 0, 255 }; @@ -248,9 +245,11 @@ clutter_text_create_layout_no_cache (ClutterText *text, ClutterUnit allocation_width) { ClutterTextPrivate *priv = text->priv; + PangoContext *context; PangoLayout *layout; - layout = pango_layout_new (_context); + context = clutter_actor_get_pango_context (CLUTTER_ACTOR (text)); + layout = pango_layout_new (context); pango_layout_set_font_description (layout, priv->font_desc); @@ -1965,9 +1964,6 @@ clutter_text_init (ClutterText *self) ClutterTextPrivate *priv; int i; - if (G_UNLIKELY (_context == NULL)) - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); priv->alignment = PANGO_ALIGN_LEFT; From 61d47ee3016c487312ef1e731af03960c9d42b42 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 14:15:10 +0000 Subject: [PATCH 089/147] Do not leak the PangoContext We hold a reference on the PangoContext we use for an actor, so we should remove the reference inside the dispose implementation. --- clutter/clutter-actor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 992ffde0b..7b550e9e9 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -1937,6 +1937,12 @@ clutter_actor_dispose (GObject *object) destroy_shader_data (self); + if (priv->pango_context) + { + g_object_unref (priv->pango_context); + priv->pango_context = NULL; + } + g_signal_emit (self, actor_signals[DESTROY], 0); G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object); From 213d8f0e4e165554ad2c172c2b18cb29dfce9b76 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 14:27:41 +0000 Subject: [PATCH 090/147] Store the PangoContext inside the main context The PangoContext should be stored once, and inside the main Clutter context. Each actor for which clutter_actor_get_pango_context() has been called will hold a reference on the Pango context as well. This makes it possible to update the text rendering for Clutter by using only public API. --- clutter/clutter-actor.c | 1 + clutter/clutter-main.c | 15 ++++++++++----- clutter/clutter-private.h | 7 ++++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 7b550e9e9..50ea17b36 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -7613,6 +7613,7 @@ clutter_actor_get_pango_context (ClutterActor *self) ctx = CLUTTER_CONTEXT (); priv->pango_context = _clutter_context_create_pango_context (ctx); + g_object_ref (priv->pango_context); return priv->pango_context; } diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 9edbce8c6..5b6a8d357 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -408,16 +408,21 @@ _clutter_context_create_pango_context (ClutterMainContext *self) gdouble resolution; cairo_font_options_t *font_options; + if (G_LIKELY (self->pango_context != NULL)) + context = self->pango_context; + else + { + context = cogl_pango_font_map_create_context (self->font_map); + self->pango_context = context; + } + + font_options = clutter_backend_get_font_options (self->backend); resolution = clutter_backend_get_resolution (self->backend); if (resolution < 0) resolution = 96.0; /* fall back */ - context = cogl_pango_font_map_create_context (self->font_map); - - pango_cairo_context_set_resolution (context, resolution); - - font_options = clutter_backend_get_font_options (self->backend); pango_cairo_context_set_font_options (context, font_options); + pango_cairo_context_set_resolution (context, resolution); return context; } diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index fb8aea93b..383b7df88 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -126,10 +126,11 @@ struct _ClutterMainContext gint fb_r_mask, fb_g_mask, fb_b_mask; gint fb_r_mask_used, fb_g_mask_used, fb_b_mask_used; - CoglPangoFontMap *font_map; /* Global font map */ + PangoContext *pango_context; /* Global Pango context */ + CoglPangoFontMap *font_map; /* Global font map */ - GSList *input_devices; /* For extra input devices, i.e - MultiTouch */ + GSList *input_devices; /* For extra input devices, i.e + MultiTouch */ }; #define CLUTTER_CONTEXT() (clutter_context_get_default ()) From 977bdcf89b449845f3b9aeb02d02fe1fb4577fcb Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 14:34:16 +0000 Subject: [PATCH 091/147] Update the PangoContext on backend changes When the ClutterBackend notifies of changes in the resolution or font options, update the PangoContext stored by Clutter's main context. This allows changing the backend font-related settings at runtime. --- clutter/clutter-main.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 5b6a8d357..50656101f 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -401,12 +401,27 @@ _clutter_do_pick (ClutterStage *stage, return clutter_get_actor_by_gid (id); } +static void +update_pango_context (ClutterBackend *backend, + ClutterMainContext *main_context) +{ + PangoContext *context = main_context->pango_context; + cairo_font_options_t *font_options; + gdouble resolution; + + font_options = clutter_backend_get_font_options (backend); + resolution = clutter_backend_get_resolution (backend); + if (resolution < 0) + resolution = 96.0; /* fall back */ + + pango_cairo_context_set_font_options (context, font_options); + pango_cairo_context_set_resolution (context, resolution); +} + PangoContext * _clutter_context_create_pango_context (ClutterMainContext *self) { PangoContext *context; - gdouble resolution; - cairo_font_options_t *font_options; if (G_LIKELY (self->pango_context != NULL)) context = self->pango_context; @@ -416,13 +431,14 @@ _clutter_context_create_pango_context (ClutterMainContext *self) self->pango_context = context; } - font_options = clutter_backend_get_font_options (self->backend); - resolution = clutter_backend_get_resolution (self->backend); - if (resolution < 0) - resolution = 96.0; /* fall back */ + g_signal_connect (self->backend, "resolution-changed", + G_CALLBACK (update_pango_context), + self); + g_signal_connect (self->backend, "font-changed", + G_CALLBACK (update_pango_context), + self); - pango_cairo_context_set_font_options (context, font_options); - pango_cairo_context_set_resolution (context, resolution); + update_pango_context (self->backend, self); return context; } From 72625421556c63a7f33afa262d4b60e29f48580b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 15:03:11 +0000 Subject: [PATCH 092/147] Store the default font name inside ClutterBackend The default backend stores some of the global defaults, like the font options, text resolution, double click settings. It should also store the default font name, to allow various text-based actors to share the same settings. When the font name changes, the ::font-changed signal is emitted, to allow actors to pick up the change. --- clutter/clutter-backend.c | 34 ++++++++++++++++++++++++++++++++++ clutter/clutter-backend.h | 5 +++++ 2 files changed, 39 insertions(+) diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 086f27494..597558bde 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -49,6 +49,8 @@ G_DEFINE_ABSTRACT_TYPE (ClutterBackend, clutter_backend, G_TYPE_OBJECT); +#define DEFAULT_FONT_NAME "Sans 10" + #define CLUTTER_BACKEND_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BACKEND, ClutterBackendPrivate)) @@ -61,6 +63,8 @@ struct _ClutterBackendPrivate ClutterFixed resolution; cairo_font_options_t *font_options; + + gchar *font_name; }; enum @@ -76,6 +80,7 @@ static guint backend_signals[LAST_SIGNAL] = { 0, }; static void clutter_backend_dispose (GObject *gobject) { + ClutterBackendPrivate *priv = CLUTTER_BACKEND (gobject)->priv; ClutterMainContext *clutter_context; clutter_context = clutter_context_get_default (); @@ -87,6 +92,8 @@ clutter_backend_dispose (GObject *gobject) clutter_context->events_queue = NULL; } + g_free (priv->font_name); + clutter_backend_set_font_options (CLUTTER_BACKEND (gobject), NULL); G_OBJECT_CLASS (clutter_backend_parent_class)->dispose (gobject); @@ -514,3 +521,30 @@ clutter_backend_get_font_options (ClutterBackend *backend) return priv->font_options; } +void +clutter_backend_set_font_name (ClutterBackend *backend, + const gchar *font_name) +{ + ClutterBackendPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BACKEND (backend)); + + priv = backend->priv; + + g_free (priv->font_name); + + if (font_name == NULL || *font_name == '\0') + priv->font_name = g_strdup (DEFAULT_FONT_NAME); + else + priv->font_name = g_strdup (font_name); + + g_signal_emit (backend, backend_signals[FONT_CHANGED], 0); +} + +G_CONST_RETURN gchar * +clutter_backend_get_font_name (ClutterBackend *backend) +{ + g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), NULL); + + return backend->priv->font_name; +} diff --git a/clutter/clutter-backend.h b/clutter/clutter-backend.h index 0669d4faa..fda1d3413 100644 --- a/clutter/clutter-backend.h +++ b/clutter/clutter-backend.h @@ -30,6 +30,8 @@ #include #include +#include + #include #include #include @@ -99,6 +101,9 @@ guint clutter_backend_get_double_click_distance (ClutterBackend void clutter_backend_set_font_options (ClutterBackend *backend, cairo_font_options_t *options); cairo_font_options_t *clutter_backend_get_font_options (ClutterBackend *backend); +void clutter_backend_set_font_name (ClutterBackend *backend, + const gchar *font_name); +G_CONST_RETURN gchar *clutter_backend_get_font_name (ClutterBackend *backend); G_END_DECLS From e3785f4f45d1ef8e0e65517c246cace4dade41ea Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 15:05:16 +0000 Subject: [PATCH 093/147] Use the default font from the Backend Instead of storing the default font name and size as a pre-processor macro, use the newly added ClutterBackend API to retrieve the current default font from the backend. --- clutter/clutter-text.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index a58c92bb3..b8b474076 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -57,8 +57,6 @@ #include "clutter-private.h" /* includes pango/cogl-pango.h */ #include "clutter-units.h" -#define DEFAULT_FONT_NAME "Sans 10" - /* cursor width in pixels */ #define DEFAULT_CURSOR_SIZE 2 @@ -1962,6 +1960,7 @@ static void clutter_text_init (ClutterText *self) { ClutterTextPrivate *priv; + const gchar *font_name; int i; self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); @@ -1986,8 +1985,10 @@ clutter_text_init (ClutterText *self) priv->text_color = default_text_color; priv->cursor_color = default_cursor_color; - priv->font_name = g_strdup (DEFAULT_FONT_NAME); - priv->font_desc = pango_font_description_from_string (priv->font_name); + /* get the default font name from the context */ + font_name = clutter_backend_get_font_name (clutter_get_default_backend ()); + priv->font_name = g_strdup (font_name); + priv->font_desc = pango_font_description_from_string (font_name); priv->position = -1; priv->selection_bound = -1; @@ -2549,11 +2550,13 @@ clutter_text_get_font_name (ClutterText *text) /** * clutter_text_set_font_name: * @self: a #ClutterText - * @font_name: a font name + * @font_name: a font name, or %NULL to set the default font name * * Sets the font used by a #ClutterText. The @font_name string - * must be something that can be parsed by the - * pango_font_description_from_string() function, like: + * must either be %NULL, which means that the font name from the + * default #ClutterBackend will be used; or be something that can + * be parsed by the pango_font_description_from_string() function, + * like: * * |[ * clutter_text_set_font_name (text, "Sans 10pt"); @@ -2572,8 +2575,9 @@ clutter_text_set_font_name (ClutterText *self, g_return_if_fail (CLUTTER_IS_TEXT (self)); + /* get the default font name from the backend */ if (!font_name || font_name[0] == '\0') - font_name = DEFAULT_FONT_NAME; + font_name = clutter_backend_get_font_name (clutter_get_default_backend ()); priv = self->priv; From b4a3944b2b6bde5b57f86519135b959f4607cad3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 16:27:54 +0000 Subject: [PATCH 094/147] Return the default font name if no font name is set When calling clutter_backend_get_font_name(), if no default font name has previously been set, we just set the default and return a pointer to it - like we do for the font options. --- clutter/clutter-backend.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 597558bde..f49fa66da 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -544,7 +544,16 @@ clutter_backend_set_font_name (ClutterBackend *backend, G_CONST_RETURN gchar * clutter_backend_get_font_name (ClutterBackend *backend) { + ClutterBackendPrivate *priv; + g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), NULL); - return backend->priv->font_name; + priv = backend->priv; + + if (G_LIKELY (priv->font_name)) + return priv->font_name; + + priv->font_name = g_strdup (DEFAULT_FONT_NAME); + + return priv->font_name; } From 9389d9755d9121c1f99a5dc208ef90a10ca1b004 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 16:40:26 +0000 Subject: [PATCH 095/147] Add the ability to set the text direction The direction of the text depends on the locale, and it is the basic setting needed to enable internationalization of user interfaces. This commit allows setting the direction of the PangoContext instance used by Clutter by using the CLUTTER_TEXT_DIRECTION environment variable, or by passing the --clutter-text-direction command line argument. Valid values are: ltr - for left-to-right locales rtl - for right-to-left locales The default is LTR. Ideally, this should be a value set by the localization teams on the PO file, but this step requires some build system surgery to allow the translation of the Clutter strings. --- clutter/clutter-main.c | 68 +++++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 50656101f..a7ec4b923 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -53,21 +53,23 @@ #include "pango/cogl-pango.h" /* main context */ -static ClutterMainContext *ClutterCntx = NULL; +static ClutterMainContext *ClutterCntx = NULL; /* main lock and locking/unlocking functions */ -static GMutex *clutter_threads_mutex = NULL; -static GCallback clutter_threads_lock = NULL; -static GCallback clutter_threads_unlock = NULL; +static GMutex *clutter_threads_mutex = NULL; +static GCallback clutter_threads_lock = NULL; +static GCallback clutter_threads_unlock = NULL; -static gboolean clutter_is_initialized = FALSE; -static gboolean clutter_show_fps = FALSE; -static gboolean clutter_fatal_warnings = FALSE; +static gboolean clutter_is_initialized = FALSE; +static gboolean clutter_show_fps = FALSE; +static gboolean clutter_fatal_warnings = FALSE; -static guint clutter_default_fps = 60; +static guint clutter_default_fps = 60; -static guint clutter_main_loop_level = 0; -static GSList *main_loops = NULL; +static guint clutter_main_loop_level = 0; +static GSList *main_loops = NULL; + +static PangoDirection clutter_text_direction = PANGO_DIRECTION_LTR; guint clutter_debug_flags = 0; /* global clutter debug flag */ @@ -401,21 +403,51 @@ _clutter_do_pick (ClutterStage *stage, return clutter_get_actor_by_gid (id); } +static PangoDirection +clutter_get_text_direction (void) +{ + const gchar *direction; + PangoDirection dir = PANGO_DIRECTION_LTR; + + direction = g_getenv ("CLUTTER_TEXT_DIRECTION"); + if (direction && *direction != '\0') + { + if (strcmp (direction, "rtl") == 0) + dir = PANGO_DIRECTION_RTL; + else + dir = PANGO_DIRECTION_LTR; + } + else + dir = PANGO_DIRECTION_LTR; + + return dir; +} + static void update_pango_context (ClutterBackend *backend, ClutterMainContext *main_context) { PangoContext *context = main_context->pango_context; + PangoFontDescription *font_desc; cairo_font_options_t *font_options; + const gchar *font_name; gdouble resolution; + font_name = clutter_backend_get_font_name (backend); + font_desc = pango_font_description_from_string (font_name); + font_options = clutter_backend_get_font_options (backend); + resolution = clutter_backend_get_resolution (backend); if (resolution < 0) resolution = 96.0; /* fall back */ + pango_context_set_font_description (context, font_desc); + pango_context_set_base_dir (context, clutter_text_direction); pango_cairo_context_set_font_options (context, font_options); pango_cairo_context_set_resolution (context, resolution); + + pango_font_description_free (font_desc); } PangoContext * @@ -1025,6 +1057,17 @@ clutter_get_timestamp (void) #endif } +static gboolean +clutter_arg_direction_cb (const char *key, + const char *value, + gpointer user_data) +{ + clutter_text_direction = + (strcmp (value, "rtl") == 0) ? PANGO_DIRECTION_RTL + : PANGO_DIRECTION_LTR; + + return TRUE; +} #ifdef CLUTTER_ENABLE_DEBUG static gboolean @@ -1098,6 +1141,8 @@ clutter_init_real (GError **error) cogl_pango_font_map_set_resolution (ctx->font_map, resolution); cogl_pango_font_map_set_use_mipmapping (ctx->font_map, TRUE); + clutter_text_direction = clutter_get_text_direction (); + /* Stage will give us a GL Context etc */ stage = clutter_stage_get_default (); if (!stage) @@ -1168,6 +1213,9 @@ static GOptionEntry clutter_args[] = { "Default frame rate", "FPS" }, { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &clutter_fatal_warnings, "Make all warnings fatal", NULL }, + { "clutter-text-direction", 0, 0, G_OPTION_ARG_CALLBACK, + clutter_arg_direction_cb, + "Direction for the text", "DIRECTION" }, #ifdef CLUTTER_ENABLE_DEBUG { "clutter-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_debug_cb, "Clutter debugging flags to set", "FLAGS" }, From f777bc629ac90e2e0b70705823ecd3fb74f2cded Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 16:59:14 +0000 Subject: [PATCH 096/147] [docs] Document the newly added functions Document the ClutterBackend font name accessors, and the clutter_actor_get_pango_context() getter method. --- clutter/clutter-actor.c | 21 +++++++++++++++++++ clutter/clutter-backend.c | 24 ++++++++++++++++++++++ doc/reference/clutter/clutter-sections.txt | 9 +++++--- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 50ea17b36..b4068b5d2 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -7598,6 +7598,27 @@ clutter_actor_grab_key_focus (ClutterActor *self) clutter_stage_set_key_focus (CLUTTER_STAGE (parent), self); } +/** + * clutter_actor_get_pango_context: + * @self: a #ClutterActor + * + * Retrieves the #PangoContext for @self. The actor's #PangoContext + * is already configured using the appropriate font map, resolution + * and font options. + * + * The returned #PangoContext will be updated each time the options + * stored by the default #ClutterBackend change. + * + * You can use the returned #PangoContext to create a #PangoLayout + * and render text using cogl_pango_render_layout() to reuse the + * glyphs cache also used by Clutter. + * + * Return value: the #PangoContext for a #ClutterActor. The returned + * #PangoContext is owned by the actor and should not be unreferenced + * by the application code + * + * Since: 1.0 + */ PangoContext * clutter_actor_get_pango_context (ClutterActor *self) { diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index f49fa66da..4c60d40ff 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -521,6 +521,18 @@ clutter_backend_get_font_options (ClutterBackend *backend) return priv->font_options; } +/** + * clutter_backend_set_font_name: + * @backend: a #ClutterBackend + * @font_name: the name of the font + * + * Sets the default font to be used by Clutter. The @font_name string + * must either be %NULL, which means that the font name from the + * default #ClutterBackend will be used; or be something that can + * be parsed by the pango_font_description_from_string() function. + * + * Since: 1.0 + */ void clutter_backend_set_font_name (ClutterBackend *backend, const gchar *font_name) @@ -541,6 +553,18 @@ clutter_backend_set_font_name (ClutterBackend *backend, g_signal_emit (backend, backend_signals[FONT_CHANGED], 0); } +/** + * clutter_backend_get_font_name: + * @backend: a #ClutterBackend + * + * Retrieves the default font name as set by + * clutter_backend_set_font_name(). + * + * Return value: the font name for the backend. The returned string is + * owned by the #ClutterBackend and should never be modified or freed + * + * Since: 1.0 + */ G_CONST_RETURN gchar * clutter_backend_get_font_name (ClutterBackend *backend) { diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index fa094506a..2bda1aeea 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -478,6 +478,7 @@ clutter_actor_get_rotationx clutter_actor_grab_key_focus +clutter_actor_get_pango_context CLUTTER_TYPE_GEOMETRY @@ -872,14 +873,16 @@ clutter_behaviour_ellipse_get_type clutter-backend ClutterBackend clutter_get_default_backend -clutter_backend_get_resolution clutter_backend_set_resolution -clutter_backend_get_double_click_time +clutter_backend_get_resolution clutter_backend_set_double_click_time -clutter_backend_get_double_click_distance +clutter_backend_get_double_click_time clutter_backend_set_double_click_distance +clutter_backend_get_double_click_distance clutter_backend_set_font_options clutter_backend_get_font_options +clutter_backend_set_font_name +clutter_backend_get_font_name CLUTTER_BACKEND CLUTTER_IS_BACKEND From 605ec10c3242f7024b77469ff38abf6a06ef08cc Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 17:40:41 +0000 Subject: [PATCH 097/147] [docs] Remove ClutterLabel and ClutterEntry sections Remove the sections for the Label and the Entry actors, which have been replaced by ClutterText. --- doc/reference/clutter/clutter-sections.txt | 86 ---------------------- 1 file changed, 86 deletions(-) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 2bda1aeea..6d3a339c0 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -66,48 +66,6 @@ clutter_unit_get_type clutter_param_unit_get_type -
-clutter-label -ClutterLabel -ClutterLabel -ClutterLabelClass -clutter_label_new -clutter_label_new_with_text -clutter_label_new_full -clutter_label_set_text -clutter_label_get_text -clutter_label_set_font_name -clutter_label_get_font_name -clutter_label_set_color -clutter_label_get_color -clutter_label_set_ellipsize -clutter_label_get_ellipsize -clutter_label_set_line_wrap -clutter_label_get_line_wrap -clutter_label_set_line_wrap_mode -clutter_label_get_line_wrap_mode -clutter_label_get_layout -clutter_label_set_attributes -clutter_label_get_attributes -clutter_label_set_use_markup -clutter_label_get_use_markup -clutter_label_set_alignment -clutter_label_get_alignment -clutter_label_get_justify -clutter_label_set_justify - - -CLUTTER_LABEL -CLUTTER_IS_LABEL -CLUTTER_TYPE_LABEL -CLUTTER_LABEL_CLASS -CLUTTER_IS_LABEL_CLASS -CLUTTER_LABEL_GET_CLASS - -ClutterLabelPrivate -clutter_label_get_type -
-
clutter-behaviour ClutterBehaviour @@ -1225,50 +1183,6 @@ CLUTTER_COGL CLUTTER_NO_FPU
-
-clutter-entry -ClutterEntry -ClutterEntry -ClutterEntryClass -clutter_entry_new -clutter_entry_new_with_text -clutter_entry_new_full -clutter_entry_set_text -clutter_entry_get_text -clutter_entry_set_font_name -clutter_entry_get_font_name -clutter_entry_set_color -clutter_entry_get_color -clutter_entry_get_layout -clutter_entry_set_alignment -clutter_entry_get_alignment -clutter_entry_set_cursor_position -clutter_entry_get_cursor_position -clutter_entry_handle_key_event -clutter_entry_insert_unichar -clutter_entry_delete_chars -clutter_entry_insert_text -clutter_entry_delete_text -clutter_entry_set_visible_cursor -clutter_entry_get_visible_cursor -clutter_entry_set_visibility -clutter_entry_get_visibility -clutter_entry_set_invisible_char -clutter_entry_get_invisible_char -clutter_entry_set_max_length -clutter_entry_get_max_length - -CLUTTER_ENTRY -CLUTTER_IS_ENTRY -CLUTTER_TYPE_ENTRY -CLUTTER_ENTRY_CLASS -CLUTTER_IS_ENTRY_CLASS -CLUTTER_ENTRY_GET_CLASS - -ClutterEntryPrivate -clutter_entry_get_type -
-
clutter-effect Clutter Effects From 92e8b886d2b21618277079306169a8aa31a03806 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 17:44:13 +0000 Subject: [PATCH 098/147] [docs] Add clutter_text_activate() Add the last unused symbol to the ClutterText section. --- doc/reference/clutter/clutter-sections.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 6d3a339c0..33317aae2 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1664,6 +1664,9 @@ clutter_text_get_cursor_visible clutter_text_set_cursor_size clutter_text_get_cursor_size + +clutter_text_activate + CLUTTER_IS_TEXT CLUTTER_IS_TEXT_CLASS From 55a22d57507dbafbc99fdb01820d5a7f41404e36 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 12:27:45 +0000 Subject: [PATCH 099/147] [text] Add select-all binding Bind Ctrl+A to the "select all" action. --- clutter/clutter-text.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index b8b474076..329c389d1 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1379,6 +1379,18 @@ clutter_text_real_line_end (ClutterText *self, return TRUE; } +static gboolean +clutter_text_real_select_all (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + clutter_text_set_cursor_position (self, 0); + clutter_text_set_selection_bound (self, self->priv->n_chars); + + return TRUE; +} + static gboolean clutter_text_real_del_next (ClutterText *self, const gchar *action, @@ -1929,6 +1941,11 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_KP_End, G_CALLBACK (clutter_text_real_line_end)); + clutter_binding_pool_install_action (binding_pool, "select-all", + CLUTTER_a, CLUTTER_CONTROL_MASK, + G_CALLBACK (clutter_text_real_select_all), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "delete-next", CLUTTER_Delete, 0, G_CALLBACK (clutter_text_real_del_next), From 2ed60a5270c7bb03e8046db52b2abdf71e195cce Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 5 Jan 2009 12:47:10 +0000 Subject: [PATCH 100/147] In clutter_alpha_set_mode, set priv->mode after setting the func Otherwise the call to clutter_alpha_set_func sets the mode back to CLUTTER_CUSTOM_MODE so clutter_alpha_get_mode won't get back the same value that was set. --- clutter/clutter-alpha.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-alpha.c b/clutter/clutter-alpha.c index 2114fe38e..e1c6966e9 100644 --- a/clutter/clutter-alpha.c +++ b/clutter/clutter-alpha.c @@ -565,13 +565,13 @@ clutter_alpha_set_mode (ClutterAlpha *alpha, priv = alpha->priv; - priv->mode = mode; - /* sanity check to avoid getting an out of sync enum/function mapping */ g_assert (animation_modes[mode].mode == mode); if (G_LIKELY (animation_modes[mode].func != NULL)) clutter_alpha_set_func (alpha, animation_modes[mode].func, NULL, NULL); + priv->mode = mode; + g_object_notify (G_OBJECT (alpha), "mode"); } From e56fe478ef9ae30584480827805e0dd6a8834e0b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 12:49:01 +0000 Subject: [PATCH 101/147] Allow overriding actions inside a BindingPool As of now, a key binding installed into a BindingPool is always there and cannot be changed. This is problematic for sub-classes trying to override the callback or the action for a given key binding. This commit adds the ability to override the closure for an existing key binding inside a binding pool -- assumed the caller knows the key symbol and modifiers used to install the key binding in the first place. --- clutter/clutter-binding-pool.c | 125 +++++++++++++++++++++++++++++++++ clutter/clutter-binding-pool.h | 70 ++++++++++-------- 2 files changed, 165 insertions(+), 30 deletions(-) diff --git a/clutter/clutter-binding-pool.c b/clutter/clutter-binding-pool.c index 8b567b28d..de31870b3 100644 --- a/clutter/clutter-binding-pool.c +++ b/clutter/clutter-binding-pool.c @@ -476,6 +476,131 @@ clutter_binding_pool_install_closure (ClutterBindingPool *pool, g_hash_table_insert (pool->entries_hash, entry, entry); } +/** + * clutter_binding_pool_override_action: + * @pool: a #ClutterBindingPool + * @key_val: key symbol + * @modifiers: bitmask of modifiers + * @callback: function to be called when the action is activated + * @data: data to be passed to @callback + * @notify: function to be called when the action is removed + * from the pool + * + * Allows overriding the action for @key_val and @modifiers inside a + * #ClutterBindingPool. See clutter_binding_pool_install_action(). + * + * When an action has been activated using clutter_binding_pool_activate() + * the passed @callback will be invoked (with @data). + * + * Actions can be blocked with clutter_binding_pool_block_action() + * and then unblocked using clutter_binding_pool_unblock_action(). + * + * Since: 1.0 + */ +void +clutter_binding_pool_override_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GCallback callback, + gpointer data, + GDestroyNotify notify) +{ + ClutterBindingEntry *entry; + GClosure *closure; + + g_return_if_fail (pool != NULL); + g_return_if_fail (key_val != 0); + g_return_if_fail (callback != NULL); + + entry = binding_pool_lookup_entry (pool, key_val, modifiers); + if (G_UNLIKELY (entry == NULL)) + { + g_warning ("There is no action for the given key symbol " + "of %d (modifiers: %d) installed inside the " + "binding pool.", + key_val, modifiers); + return; + } + + if (entry->closure) + { + g_closure_unref (entry->closure); + entry->closure = NULL; + } + + closure = g_cclosure_new (callback, data, (GClosureNotify) notify); + entry->closure = g_closure_ref (closure); + g_closure_sink (closure); + + if (G_CLOSURE_NEEDS_MARSHAL (closure)) + { + GClosureMarshal marshal; + + marshal = clutter_marshal_BOOLEAN__STRING_UINT_ENUM; + g_closure_set_marshal (closure, marshal); + } +} + +/** + * clutter_binding_pool_override_action: + * @pool: a #ClutterBindingPool + * @key_val: key symbol + * @modifiers: bitmask of modifiers + * @closure: a #GClosure + * + * A #GClosure variant of clutter_binding_pool_override_action(). + * + * Allows overriding the action for @key_val and @modifiers inside a + * #ClutterBindingPool. See clutter_binding_pool_install_closure(). + * + * When an action has been activated using clutter_binding_pool_activate() + * the passed @callback will be invoked (with @data). + * + * Actions can be blocked with clutter_binding_pool_block_action() + * and then unblocked using clutter_binding_pool_unblock_action(). + * + * Since: 1.0 + */ +void +clutter_binding_pool_override_closure (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GClosure *closure) +{ + ClutterBindingEntry *entry; + + g_return_if_fail (pool != NULL); + g_return_if_fail (key_val != 0); + g_return_if_fail (closure != NULL); + + entry = binding_pool_lookup_entry (pool, key_val, modifiers); + if (G_UNLIKELY (entry == NULL)) + { + g_warning ("There is no action for the given key symbol " + "of %d (modifiers: %d) installed inside the " + "binding pool.", + key_val, modifiers); + return; + } + + if (entry->closure) + { + g_closure_unref (entry->closure); + entry->closure = NULL; + } + + entry->closure = g_closure_ref (closure); + g_closure_sink (closure); + + if (G_CLOSURE_NEEDS_MARSHAL (closure)) + { + GClosureMarshal marshal; + + marshal = clutter_marshal_BOOLEAN__STRING_UINT_ENUM; + g_closure_set_marshal (closure, marshal); + } +} + gchar ** clutter_binding_pool_list_actions (ClutterBindingPool *pool) { diff --git a/clutter/clutter-binding-pool.h b/clutter/clutter-binding-pool.h index 47692403e..6e2dac4bb 100644 --- a/clutter/clutter-binding-pool.h +++ b/clutter/clutter-binding-pool.h @@ -56,40 +56,50 @@ typedef gboolean (* ClutterBindingActionFunc) (GObject *gobject, guint key_val, ClutterModifierType modifiers); -ClutterBindingPool * clutter_binding_pool_new (const gchar *name); -ClutterBindingPool * clutter_binding_pool_get_for_class (gpointer klass); -ClutterBindingPool * clutter_binding_pool_find (const gchar *name); +ClutterBindingPool * clutter_binding_pool_new (const gchar *name); +ClutterBindingPool * clutter_binding_pool_get_for_class (gpointer klass); +ClutterBindingPool * clutter_binding_pool_find (const gchar *name); -void clutter_binding_pool_install_action (ClutterBindingPool *pool, - const gchar *action_name, - guint key_val, - ClutterModifierType modifiers, - GCallback callback, - gpointer data, - GDestroyNotify notify); -void clutter_binding_pool_install_closure (ClutterBindingPool *pool, - const gchar *action_name, - guint key_val, - ClutterModifierType modifiers, - GClosure *closure); +void clutter_binding_pool_install_action (ClutterBindingPool *pool, + const gchar *action_name, + guint key_val, + ClutterModifierType modifiers, + GCallback callback, + gpointer data, + GDestroyNotify notify); +void clutter_binding_pool_install_closure (ClutterBindingPool *pool, + const gchar *action_name, + guint key_val, + ClutterModifierType modifiers, + GClosure *closure); +void clutter_binding_pool_override_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GCallback callback, + gpointer data, + GDestroyNotify notify); +void clutter_binding_pool_override_closure (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GClosure *closure); -gchar ** clutter_binding_pool_list_actions (ClutterBindingPool *pool); -G_CONST_RETURN gchar *clutter_binding_pool_find_action (ClutterBindingPool *pool, - guint key_val, - ClutterModifierType modifiers); -void clutter_binding_pool_remove_action (ClutterBindingPool *pool, - guint key_val, - ClutterModifierType modifiers); +gchar ** clutter_binding_pool_list_actions (ClutterBindingPool *pool); +G_CONST_RETURN gchar *clutter_binding_pool_find_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers); +void clutter_binding_pool_remove_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers); -gboolean clutter_binding_pool_activate (ClutterBindingPool *pool, - guint key_val, - ClutterModifierType modifiers, - GObject *gobject); +gboolean clutter_binding_pool_activate (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GObject *gobject); -void clutter_binding_pool_block_action (ClutterBindingPool *pool, - const gchar *action_name); -void clutter_binding_pool_unblock_action (ClutterBindingPool *pool, - const gchar *action_name); +void clutter_binding_pool_block_action (ClutterBindingPool *pool, + const gchar *action_name); +void clutter_binding_pool_unblock_action (ClutterBindingPool *pool, + const gchar *action_name); G_END_DECLS From fa431f64923d233cea61aac9f1df75a43e323158 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 12:52:29 +0000 Subject: [PATCH 102/147] [docs] Add the new API to the BindingPool section Add the clutter_binding_pool_override_* family of functions to the BindingPool section of the Clutter API reference. --- doc/reference/clutter/clutter-sections.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 33317aae2..7883e2e9c 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1591,6 +1591,8 @@ clutter_binding_pool_find clutter_binding_pool_install_action clutter_binding_pool_install_closure +clutter_binding_pool_override_action +clutter_binding_pool_override_closure clutter_binding_pool_list_actions clutter_binding_pool_find_action clutter_binding_pool_remove_action From 62cfc6487fc2f3ccd4e1cfdd71bd2746ed833b61 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 5 Jan 2009 12:52:46 +0000 Subject: [PATCH 103/147] Fix setting the mode on a ClutterAlpha created with animation_set_alpha(NULL) It previously attempted to set the mode on the alpha using clutter_animation_set_mode_internal, but this was setting the mode on priv->alpha. At that point in the code priv->alpha is always NULL. clutter_animation_set_mode_internal now takes a parameter to specify which alpha to modify. --- clutter/clutter-animation.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index 83845057b..4dc1aed6e 100644 --- a/clutter/clutter-animation.c +++ b/clutter/clutter-animation.c @@ -780,12 +780,11 @@ clutter_animation_get_actor (ClutterAnimation *animation) } static inline void -clutter_animation_set_mode_internal (ClutterAnimation *animation) +clutter_animation_set_mode_internal (ClutterAnimation *animation, + ClutterAlpha *alpha) { ClutterAnimationPrivate *priv = animation->priv; - ClutterAlpha *alpha; - alpha = clutter_animation_get_alpha (animation); if (alpha) clutter_alpha_set_mode (alpha, priv->mode); } @@ -810,7 +809,7 @@ clutter_animation_set_mode (ClutterAnimation *animation, priv = animation->priv; priv->mode = mode; - clutter_animation_set_mode_internal (animation); + clutter_animation_set_mode_internal (animation, priv->alpha); g_object_notify (G_OBJECT (animation), "mode"); } @@ -1066,7 +1065,7 @@ clutter_animation_set_alpha (ClutterAnimation *animation, alpha = clutter_alpha_new (); clutter_alpha_set_timeline (alpha, timeline); - clutter_animation_set_mode_internal (animation); + clutter_animation_set_mode_internal (animation, alpha); } priv->alpha = g_object_ref_sink (alpha); From 39f4848b93511d1c6b8c3a4f0eb2774a501caea0 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 5 Jan 2009 12:05:51 +0000 Subject: [PATCH 104/147] Fix some *_set functions so they work if the object is the same value Bug 1392 - behaviour_set_alpha set same alpha twice lead to warning and destroy the input alpha The following functions are fixed: clutter_actor_set_shader clutter_alpha_set_timeline clutter_behaviour_set_alpha clutter_clone_texture_set_parent_texture They either now reference the new value before destroying the old value, or just return immediately if the values are the same. --- clutter/clutter-actor.c | 20 +++++++------------- clutter/clutter-alpha.c | 3 +++ clutter/clutter-behaviour.c | 4 +++- clutter/clutter-clone-texture.c | 3 +++ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 19074e334..7e0286d9a 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -7224,11 +7224,11 @@ clutter_actor_set_shader (ClutterActor *self, g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); g_return_val_if_fail (shader == NULL || CLUTTER_IS_SHADER (shader), FALSE); - /* if shader passed in is NULL we destroy the shader */ - if (shader == NULL) - { - destroy_shader_data (self); - } + if (shader) + g_object_ref (shader); + else + /* if shader passed in is NULL we destroy the shader */ + destroy_shader_data (self); actor_priv = self->priv; shader_data = actor_priv->shader_data; @@ -7242,15 +7242,9 @@ clutter_actor_set_shader (ClutterActor *self, shader_value_free); } if (shader_data->shader) - { - g_object_unref (shader_data->shader); - shader_data->shader = NULL; - } + g_object_unref (shader_data->shader); - if (shader) - { - shader_data->shader = g_object_ref (shader); - } + shader_data->shader = shader; if (CLUTTER_ACTOR_IS_VISIBLE (self)) clutter_actor_queue_redraw (self); diff --git a/clutter/clutter-alpha.c b/clutter/clutter-alpha.c index e1c6966e9..dc1dddeba 100644 --- a/clutter/clutter-alpha.c +++ b/clutter/clutter-alpha.c @@ -395,6 +395,9 @@ clutter_alpha_set_timeline (ClutterAlpha *alpha, priv = alpha->priv; + if (priv->timeline == timeline) + return; + if (priv->timeline) { g_signal_handlers_disconnect_by_func (priv->timeline, diff --git a/clutter/clutter-behaviour.c b/clutter/clutter-behaviour.c index 11da23d35..006be1bda 100644 --- a/clutter/clutter-behaviour.c +++ b/clutter/clutter-behaviour.c @@ -570,6 +570,9 @@ clutter_behaviour_set_alpha (ClutterBehaviour *behave, priv = behave->priv; + if (alpha) + g_object_ref_sink (alpha); + if (priv->notify_id) { CLUTTER_NOTE (BEHAVIOUR, "removing previous notify-id (%d)", @@ -590,7 +593,6 @@ clutter_behaviour_set_alpha (ClutterBehaviour *behave, if (alpha) { priv->alpha = alpha; - g_object_ref_sink (priv->alpha); priv->notify_id = g_signal_connect (priv->alpha, "notify::alpha", G_CALLBACK(notify_cb), diff --git a/clutter/clutter-clone-texture.c b/clutter/clutter-clone-texture.c index c92bada82..a2350cbab 100644 --- a/clutter/clutter-clone-texture.c +++ b/clutter/clutter-clone-texture.c @@ -221,6 +221,9 @@ set_parent_texture (ClutterCloneTexture *ctexture, ClutterActor *actor = CLUTTER_ACTOR (ctexture); gboolean was_visible = CLUTTER_ACTOR_IS_VISIBLE (ctexture); + if (priv->parent_texture == texture) + return; + if (priv->parent_texture) { g_object_unref (priv->parent_texture); From ff73fe3e1f142517617cddf6c73c72783bb7fe92 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 5 Jan 2009 13:14:12 +0000 Subject: [PATCH 105/147] Fix some animation_set_* functions so they cope if the value is the same The following functions are fixed: clutter_animation_set_actor clutter_animation_set_timeline clutter_animation_set_alpha This is related to bug 1392 which discusses the problem for behaviour_set_alpha. --- clutter/clutter-animation.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index 4dc1aed6e..eae81250d 100644 --- a/clutter/clutter-animation.c +++ b/clutter/clutter-animation.c @@ -739,6 +739,8 @@ clutter_animation_set_actor (ClutterAnimation *animation, priv = animation->priv; + g_object_ref (actor); + if (priv->actor) { g_object_weak_unref (G_OBJECT (animation), @@ -750,7 +752,7 @@ clutter_animation_set_actor (ClutterAnimation *animation, g_object_unref (priv->actor); } - priv->actor = g_object_ref (actor); + priv->actor = actor; g_object_weak_ref (G_OBJECT (animation), on_animation_weak_notify, priv->actor); @@ -967,6 +969,9 @@ clutter_animation_set_timeline (ClutterAnimation *animation, priv = animation->priv; + if (timeline && priv->timeline == timeline) + return; + g_object_freeze_notify (G_OBJECT (animation)); if (priv->timeline) @@ -1047,16 +1052,6 @@ clutter_animation_set_alpha (ClutterAnimation *animation, priv = animation->priv; - if (priv->alpha) - { - if (priv->alpha_notify_id) - g_signal_handler_disconnect (priv->alpha, priv->alpha_notify_id); - - g_object_unref (priv->alpha); - priv->alpha_notify_id = 0; - priv->alpha = NULL; - } - if (!alpha) { ClutterTimeline *timeline; @@ -1068,7 +1063,19 @@ clutter_animation_set_alpha (ClutterAnimation *animation, clutter_animation_set_mode_internal (animation, alpha); } - priv->alpha = g_object_ref_sink (alpha); + g_object_ref_sink (alpha); + + if (priv->alpha) + { + if (priv->alpha_notify_id) + g_signal_handler_disconnect (priv->alpha, priv->alpha_notify_id); + + g_object_unref (priv->alpha); + priv->alpha_notify_id = 0; + priv->alpha = NULL; + } + + priv->alpha = alpha; priv->alpha_notify_id = g_signal_connect (alpha, "notify::alpha", From 33459f63d61c9cf984378ff395a6dffa4ae87d46 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 15:26:57 +0000 Subject: [PATCH 106/147] Whitespace fixes --- clutter/clutter-text.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 329c389d1..e90491722 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -38,8 +38,7 @@ * #ClutterText is available since Clutter 1.0 */ -/* TODO: undo/redo hooks? - */ +/* TODO: undo/redo hooks? */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -67,7 +66,7 @@ * be regenerated at a different width to get the height for the * actual allocated width */ -#define N_CACHED_LAYOUTS 3 +#define N_CACHED_LAYOUTS 3 #define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) @@ -866,7 +865,6 @@ cursor_paint (ClutterText *self) } g_free (ranges); - } } } From d622a4dd8933b33f9328beaf1bf825451cadd025 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 15:27:33 +0000 Subject: [PATCH 107/147] Rename the PangoContext creation functions The _clutter_context_create_pango_context() should create a new context; the function returning the PangoContext stored inside the MainContext structure should be named _get_pango_context() instead. --- clutter/clutter-main.c | 50 +++++++++++++++++++++++---------------- clutter/clutter-private.h | 1 + 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index a7ec4b923..588b00a84 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -424,21 +424,20 @@ clutter_get_text_direction (void) } static void -update_pango_context (ClutterBackend *backend, - ClutterMainContext *main_context) +update_pango_context (ClutterBackend *backend, + PangoContext *context) { - PangoContext *context = main_context->pango_context; PangoFontDescription *font_desc; cairo_font_options_t *font_options; const gchar *font_name; gdouble resolution; font_name = clutter_backend_get_font_name (backend); + font_options = clutter_backend_get_font_options (backend); + resolution = clutter_backend_get_resolution (backend); + font_desc = pango_font_description_from_string (font_name); - font_options = clutter_backend_get_font_options (backend); - - resolution = clutter_backend_get_resolution (backend); if (resolution < 0) resolution = 96.0; /* fall back */ @@ -450,26 +449,35 @@ update_pango_context (ClutterBackend *backend, pango_font_description_free (font_desc); } +PangoContext * +_clutter_context_get_pango_context (ClutterMainContext *self) +{ + if (G_LIKELY (self->pango_context)) + { + PangoContext *context; + + context = cogl_pango_font_map_create_context (self->font_map); + self->pango_context = context; + + g_signal_connect (self->backend, "resolution-changed", + G_CALLBACK (update_pango_context), + self->pango_context); + g_signal_connect (self->backend, "font-changed", + G_CALLBACK (update_pango_context), + self->pango_context); + } + + update_pango_context (self->backend, self->pango_context); + + return self->pango_context; +} + PangoContext * _clutter_context_create_pango_context (ClutterMainContext *self) { PangoContext *context; - if (G_LIKELY (self->pango_context != NULL)) - context = self->pango_context; - else - { - context = cogl_pango_font_map_create_context (self->font_map); - self->pango_context = context; - } - - g_signal_connect (self->backend, "resolution-changed", - G_CALLBACK (update_pango_context), - self); - g_signal_connect (self->backend, "font-changed", - G_CALLBACK (update_pango_context), - self); - + context = cogl_pango_font_map_create_context (self->font_map); update_pango_context (self->backend, self); return context; diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 383b7df88..c65bfb066 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -136,6 +136,7 @@ struct _ClutterMainContext #define CLUTTER_CONTEXT() (clutter_context_get_default ()) ClutterMainContext *clutter_context_get_default (void); PangoContext *_clutter_context_create_pango_context (ClutterMainContext *self); +PangoContext *_clutter_context_get_pango_context (ClutterMainContext *self); #define CLUTTER_PRIVATE_FLAGS(a) (((ClutterActor *) (a))->private_flags) #define CLUTTER_SET_PRIVATE_FLAGS(a,f) (CLUTTER_PRIVATE_FLAGS (a) |= (f)) From 982a678053a518e94928e5d7a344c4c5525dec15 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 15:29:10 +0000 Subject: [PATCH 108/147] Add ClutterActor::create_pango_context() Sometimes an actor needs to set specific font rendering options on the PangoContext without changing settings for every other text-rendering actor. In order to do this, we need a new public method to create a Pango context object -- preset with all the default settings -- owned by the developer and not shared with the rest of Clutter. This new method is called clutter_actor_create_pango_context(); while it does not strictly depend on a ClutterActor, it is a good idea to have it inside the ClutterActor API to map the current get_pango_context() method and in case we start storing screen-specific data to the Actor itself during the 1.x API cycle. --- clutter/clutter-actor.c | 36 +++++++++++++++++++++++++++++++++--- clutter/clutter-actor.h | 31 ++++++++++++++++--------------- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index b4068b5d2..49da27403 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -7606,8 +7606,9 @@ clutter_actor_grab_key_focus (ClutterActor *self) * is already configured using the appropriate font map, resolution * and font options. * - * The returned #PangoContext will be updated each time the options - * stored by the default #ClutterBackend change. + * Unlike clutter_actor_create_pango_context(), this context is owend + * by the #ClutterActor and it will be updated each time the options + * stored by the #ClutterBackend change. * * You can use the returned #PangoContext to create a #PangoLayout * and render text using cogl_pango_render_layout() to reuse the @@ -7633,8 +7634,37 @@ clutter_actor_get_pango_context (ClutterActor *self) return priv->pango_context; ctx = CLUTTER_CONTEXT (); - priv->pango_context = _clutter_context_create_pango_context (ctx); + priv->pango_context = _clutter_context_get_pango_context (ctx); g_object_ref (priv->pango_context); return priv->pango_context; } + +/** + * clutter_actor_create_pango_context: + * @self: a #ClutterActor + * + * Creates a #PangoContext for the given actor. The #PangoContext + * is already configured using the appropriate font map, resolution + * and font options. + * + * See also clutter_actor_get_pango_context(). + * + * Return value: the newly created #PangoContext. Use g_object_ref() + * on the returned value to deallocate its resources + * + * Since: 1.0 + */ +PangoContext * +clutter_actor_create_pango_context (ClutterActor *self) +{ + ClutterMainContext *ctx; + PangoContext *retval; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); + + ctx = CLUTTER_CONTEXT (); + retval = _clutter_context_create_pango_context (ctx); + + return retval; +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 9461e9fa3..af962caf1 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -422,6 +422,8 @@ void clutter_actor_set_opacity (ClutterActor guint8 opacity); guint8 clutter_actor_get_opacity (ClutterActor *self); guint8 clutter_actor_get_paint_opacity (ClutterActor *self); +gboolean clutter_actor_get_paint_visibility (ClutterActor *self); + void clutter_actor_set_name (ClutterActor *self, const gchar *name); @@ -547,25 +549,24 @@ gboolean clutter_actor_is_rotated (ClutterActor *self); gboolean clutter_actor_is_scaled (ClutterActor *self); gboolean clutter_actor_should_pick_paint (ClutterActor *self); -void clutter_actor_box_get_from_vertices (ClutterVertex vtx[4], - ClutterActorBox *box); +void clutter_actor_box_get_from_vertices (ClutterVertex vtx[4], + ClutterActorBox *box); -void clutter_actor_get_abs_allocation_vertices (ClutterActor *self, - ClutterVertex verts[4]); +void clutter_actor_get_abs_allocation_vertices (ClutterActor *self, + ClutterVertex verts[4]); -void clutter_actor_apply_transform_to_point (ClutterActor *self, - const ClutterVertex *point, - ClutterVertex *vertex); -void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, - ClutterActor *ancestor, - const ClutterVertex *point, - ClutterVertex *vertex); +void clutter_actor_apply_transform_to_point (ClutterActor *self, + const ClutterVertex *point, + ClutterVertex *vertex); +void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, + ClutterActor *ancestor, + const ClutterVertex *point, + ClutterVertex *vertex); -gboolean clutter_actor_get_paint_visibility (ClutterActor *self); +void clutter_actor_grab_key_focus (ClutterActor *self); -void clutter_actor_grab_key_focus (ClutterActor *self); - -PangoContext *clutter_actor_get_pango_context (ClutterActor *self); +PangoContext *clutter_actor_get_pango_context (ClutterActor *self); +PangoContext *clutter_actor_create_pango_context (ClutterActor *self); G_END_DECLS From c988b7b736e6d7ab7929b409f6c54c81d5862623 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 16:25:50 +0000 Subject: [PATCH 109/147] Remove BindingPool::list_actions() The clutter_binding_pool_list_actions() was not implemented. The utility of a call listing all the action names is also debatable: all the functions related to the key bindings take the key symbol and modifiers -- except the block_action() and unblock_action() pair. --- clutter/clutter-binding-pool.c | 6 ------ clutter/clutter-binding-pool.h | 1 - doc/reference/clutter/clutter-sections.txt | 1 - 3 files changed, 8 deletions(-) diff --git a/clutter/clutter-binding-pool.c b/clutter/clutter-binding-pool.c index de31870b3..065310bcf 100644 --- a/clutter/clutter-binding-pool.c +++ b/clutter/clutter-binding-pool.c @@ -601,12 +601,6 @@ clutter_binding_pool_override_closure (ClutterBindingPool *pool, } } -gchar ** -clutter_binding_pool_list_actions (ClutterBindingPool *pool) -{ - return NULL; -} - /** * clutter_binding_pool_find_action: * @pool: a #ClutterBindingPool diff --git a/clutter/clutter-binding-pool.h b/clutter/clutter-binding-pool.h index 6e2dac4bb..55b5273c8 100644 --- a/clutter/clutter-binding-pool.h +++ b/clutter/clutter-binding-pool.h @@ -83,7 +83,6 @@ void clutter_binding_pool_override_closure (ClutterBindingPool ClutterModifierType modifiers, GClosure *closure); -gchar ** clutter_binding_pool_list_actions (ClutterBindingPool *pool); G_CONST_RETURN gchar *clutter_binding_pool_find_action (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers); diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 7883e2e9c..a13b51d2f 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1593,7 +1593,6 @@ clutter_binding_pool_install_action clutter_binding_pool_install_closure clutter_binding_pool_override_action clutter_binding_pool_override_closure -clutter_binding_pool_list_actions clutter_binding_pool_find_action clutter_binding_pool_remove_action clutter_binding_pool_block_action From 700b34148bd9e052bf521caeaff05c1373200972 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 16:29:49 +0000 Subject: [PATCH 110/147] Remove the binding pool entry from the list When removing a binding entry from the binding pool we should not only remove it from the hash table, but also from the linked list we use to iterate over inside the block/unblock_action() pair. --- clutter/clutter-binding-pool.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/clutter/clutter-binding-pool.c b/clutter/clutter-binding-pool.c index 065310bcf..28082cb63 100644 --- a/clutter/clutter-binding-pool.c +++ b/clutter/clutter-binding-pool.c @@ -650,6 +650,7 @@ clutter_binding_pool_remove_action (ClutterBindingPool *pool, ClutterModifierType modifiers) { ClutterBindingEntry remove_entry = { 0, }; + GSList *l; g_return_if_fail (pool != NULL); g_return_if_fail (key_val != 0); @@ -659,6 +660,18 @@ clutter_binding_pool_remove_action (ClutterBindingPool *pool, remove_entry.key_val = key_val; remove_entry.modifiers = modifiers; + for (l = pool->entries; l != NULL; l = l->data) + { + ClutterBindingEntry *e = l->data; + + if (e->key_val == remove_entry.key_val && + e->modifiers == remove_entry.modifiers) + { + pool->entries = g_slist_remove_link (pool->entries, l); + break; + } + } + g_hash_table_remove (pool->entries_hash, &remove_entry); } From c79112bd3c4febc39eeba5cabe50319f1eb7976c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 16:44:52 +0000 Subject: [PATCH 111/147] Revert the logic of the PangoContext check The branch that creates the global PangoContext should only run if there is no global PangoContext already. --- clutter/clutter-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 588b00a84..12bef383c 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -452,7 +452,7 @@ update_pango_context (ClutterBackend *backend, PangoContext * _clutter_context_get_pango_context (ClutterMainContext *self) { - if (G_LIKELY (self->pango_context)) + if (G_UNLIKELY (self->pango_context == NULL)) { PangoContext *context; From a5f9c7269465da32a8f0209dfce27f875e4fe05b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 16:45:59 +0000 Subject: [PATCH 112/147] Pass the PangoContext, not the MainContext When updating the PangoContext with the current options (font name, options, resolution) pass the PangoContext instead of the Clutter MainContext structure pointer. --- clutter/clutter-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 12bef383c..6dc8cbb94 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -478,7 +478,7 @@ _clutter_context_create_pango_context (ClutterMainContext *self) PangoContext *context; context = cogl_pango_font_map_create_context (self->font_map); - update_pango_context (self->backend, self); + update_pango_context (self->backend, context); return context; } From 11870040998e0c7e0a30da11a1b91e0c54631c5b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 16:48:46 +0000 Subject: [PATCH 113/147] Clean up the update_pango_context() function Logically split the various operations with whitespace so that it's clear what does what. --- clutter/clutter-main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 6dc8cbb94..2bd6a7dee 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -432,6 +432,10 @@ update_pango_context (ClutterBackend *backend, const gchar *font_name; gdouble resolution; + /* update the text direction */ + pango_context_set_base_dir (context, clutter_text_direction); + + /* get the configuration for the PangoContext from the backend */ font_name = clutter_backend_get_font_name (backend); font_options = clutter_backend_get_font_options (backend); resolution = clutter_backend_get_resolution (backend); @@ -442,7 +446,6 @@ update_pango_context (ClutterBackend *backend, resolution = 96.0; /* fall back */ pango_context_set_font_description (context, font_desc); - pango_context_set_base_dir (context, clutter_text_direction); pango_cairo_context_set_font_options (context, font_options); pango_cairo_context_set_resolution (context, resolution); From e5543a658f74bcc6efb477d34953e5f2bf08d930 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 5 Jan 2009 17:05:30 +0000 Subject: [PATCH 114/147] Make libdisable-npots a bit more portable Instead of including GL/gl.h directly it now includes cogl/cogl.h instead which should include the right GL header. Instead of using dlopen to specifically open libGL it now tries to use dlsym with RTLD_NEXT. This requires defining _GNU_SOURCE on GNU systems. If RTLD_NEXT is not available it will try passing NULL which is unlikely to work but it will at least catch the case where it returns the wrapper version of glGetString to prevent infinite recursion. This should hopefully make it work on OS X where the name of the header and library are different (although this is currently untested). --- tests/tools/Makefile.am | 6 ++++++ tests/tools/disable-npots.c | 21 ++++++++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/tests/tools/Makefile.am b/tests/tools/Makefile.am index af5efce4d..43fd46ca6 100644 --- a/tests/tools/Makefile.am +++ b/tests/tools/Makefile.am @@ -10,6 +10,12 @@ libdisable_npots_la_SOURCES = disable-npots.c libdisable_npots_la_LIBADD = -ldl +INCLUDES = \ + -I$(top_srcdir)/clutter \ + -I$(top_builddir)/clutter \ + $(CLUTTER_CFLAGS) \ + -D_GNU_SOURCE + all-local : disable-npots.sh clean-local : diff --git a/tests/tools/disable-npots.c b/tests/tools/disable-npots.c index baa1f5778..2a942d2af 100644 --- a/tests/tools/disable-npots.c +++ b/tests/tools/disable-npots.c @@ -4,13 +4,22 @@ * overrides glGetString and removes the extension strings. */ -#include +/* This is just included to get the right GL header */ +#include + #include #include #include #include #include +/* If RTLD_NEXT isn't available then try just using NULL */ +#ifdef RTLD_NEXT +#define LIB_HANDLE RTLD_NEXT +#else +#define LIB_HANDLE NULL +#endif + typedef const GLubyte * (* GetStringFunc) (GLenum name); static const char * const bad_strings[] @@ -23,16 +32,14 @@ const GLubyte * glGetString (GLenum name) { const GLubyte *ret = NULL; - static void *gl_lib = NULL; static GetStringFunc func = NULL; static GLubyte *extensions = NULL; - if (gl_lib == NULL - && (gl_lib = dlopen ("libGL.so", RTLD_LAZY)) == NULL) - fprintf (stderr, "dlopen: %s\n", dlerror ()); - else if (func == NULL - && (func = (GetStringFunc) dlsym (gl_lib, "glGetString")) == NULL) + if (func == NULL + && (func = (GetStringFunc) dlsym (LIB_HANDLE, "glGetString")) == NULL) fprintf (stderr, "dlsym: %s\n", dlerror ()); + else if (func == glGetString) + fprintf (stderr, "dlsym returned the wrapper of glGetString\n"); else { ret = (* func) (name); From 8e6423a1b6e08e140a888df5398206640deea59e Mon Sep 17 00:00:00 2001 From: Takao Fujiwara Date: Tue, 6 Jan 2009 12:11:07 +0000 Subject: [PATCH 115/147] Bug 1397 - Allow localizing the command line help Clutter has a set of command line options that are added to every application by means of clutter_init() or by obtaining the Clutter GOptionGroup and using g_option_context_parse(). Thus, every Clutter application will automatically have an --help command line switch showing the list of options and their description. At the moment, Clutter does not enable localization of the help, thus making it less than useful on non-English locales. This patch enables the machinery to create a localization file and load it when initializing Clutter, by means of the GLib macros and locale.h API we already use. Signed-off-by: Emmanuele Bassi --- clutter/clutter-main.c | 18 +++++++++++------- clutter/glx/clutter-backend-glx.c | 4 +++- clutter/x11/clutter-backend-x11.c | 8 +++++--- configure.ac | 1 + po/POTFILES.in | 14 ++++++++++++++ 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 9edbce8c6..994a844da 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -1142,16 +1142,16 @@ clutter_init_real (GError **error) static GOptionEntry clutter_args[] = { { "clutter-show-fps", 0, 0, G_OPTION_ARG_NONE, &clutter_show_fps, - "Show frames per second", NULL }, + N_("Show frames per second"), NULL }, { "clutter-default-fps", 0, 0, G_OPTION_ARG_INT, &clutter_default_fps, - "Default frame rate", "FPS" }, + N_("Default frame rate"), "FPS" }, { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &clutter_fatal_warnings, - "Make all warnings fatal", NULL }, + N_("Make all warnings fatal"), NULL }, #ifdef CLUTTER_ENABLE_DEBUG { "clutter-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_debug_cb, - "Clutter debugging flags to set", "FLAGS" }, + N_("Clutter debugging flags to set"), "FLAGS" }, { "clutter-no-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_no_debug_cb, - "Clutter debugging flags to unset", "FLAGS" }, + N_("Clutter debugging flags to unset"), "FLAGS" }, #endif /* CLUTTER_ENABLE_DEBUG */ { NULL, }, }; @@ -1294,13 +1294,14 @@ clutter_get_option_group (void) context = clutter_context_get_default (); group = g_option_group_new ("clutter", - "Clutter Options", - "Show Clutter Options", + _("Clutter Options"), + _("Show Clutter Options"), NULL, NULL); g_option_group_set_parse_hooks (group, pre_parse_hook, post_parse_hook); g_option_group_add_entries (group, clutter_args); + g_option_group_set_translation_domain (group, GETTEXT_PACKAGE); /* add backend-specific options */ _clutter_backend_add_options (context->backend, group); @@ -2118,6 +2119,9 @@ clutter_base_init (void) initialised = TRUE; + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + /* initialise GLib type system */ g_type_init (); diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index 9f4f47b4a..71276d161 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -29,6 +29,8 @@ #include #endif +#include + #include #include #include @@ -150,7 +152,7 @@ static const GOptionEntry entries[] = { "vblank", 0, 0, G_OPTION_ARG_STRING, &clutter_vblank_name, - "VBlank method to be used (none, dri or glx)", "METHOD" + N_("VBlank method to be used (none, dri or glx)"), "METHOD" }, { NULL } }; diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index f05589b9f..412bb77da 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -23,6 +23,8 @@ #include "config.h" #endif +#include + #include #include #ifdef HAVE_UNISTD_H @@ -235,18 +237,18 @@ static const GOptionEntry entries[] = "display", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_STRING, &clutter_display_name, - "X display to use", "DISPLAY" + N_("X display to use"), "DISPLAY" }, { "screen", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &clutter_screen, - "X screen to use", "SCREEN" + N_("X screen to use"), "SCREEN" }, { "synch", 0, 0, G_OPTION_ARG_NONE, &clutter_synchronise, - "Make X calls synchronous", NULL, + N_("Make X calls synchronous"), NULL, }, { NULL } }; diff --git a/configure.ac b/configure.ac index 4913faf86..ccfc2e0f5 100644 --- a/configure.ac +++ b/configure.ac @@ -637,6 +637,7 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", ALL_LINGUAS="" AM_GLIB_GNU_GETTEXT +GLIB_DEFINE_LOCALEDIR(LOCALEDIR) AC_CONFIG_FILES([ Makefile diff --git a/po/POTFILES.in b/po/POTFILES.in index e69de29bb..76922d195 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -0,0 +1,14 @@ +clutter/clutter-actor.c +clutter/clutter-behaviour.c +clutter/clutter-color.c +clutter/clutter-container.c +clutter/clutter-event.c +clutter/clutter-fixed.c +clutter/clutter-fixed.h +clutter/clutter-main.c +clutter/clutter-stage-window.c +clutter/clutter-stage.c +clutter/clutter-texture.c +clutter/clutter-units.c +clutter/glx/clutter-backend-glx.c +clutter/x11/clutter-backend-x11.c From e4272fba69f9c174388eb8a1ffabd3211393461e Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 6 Jan 2009 11:39:14 +0000 Subject: [PATCH 116/147] Fix off-by-one error in clutter_stage_read_pixels It was always reading one pixel lower than requested. If y was 0 then it would try to read below the lowest line. Thanks to Geoff Gustafson for spotting. --- clutter/clutter-stage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 7e5abe76f..f6d42288f 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -1201,7 +1201,7 @@ clutter_stage_read_pixels (ClutterStage *stage, /* The y co-ordinate should be given in OpenGL's coordinate system so 0 is the bottom row */ - y = stage_height - 1 - y - height; + y = stage_height - y - height; glFinish (); From 1892f8cb1da5727e56323a42f7c2ff9b5d596c31 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 12:35:19 +0000 Subject: [PATCH 117/147] Allow localizations to change the text direction The locale translators of Clutter are also the ones that should set the default direction of the text in a Clutter user interface. This commit adds a translatable string that defines the direction of the text; the translation authors will change it to the correct value and that will determine the default direction. The default text direction can be overridden by using the CLUTTER_TEXT_DIRECTION environment variable, or by using the --clutter-text-direction command line switch. In any other case, the locale will determine the text direction, as it should. --- clutter/clutter-main.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 2bd6a7dee..38ed3e5cf 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -406,19 +406,34 @@ _clutter_do_pick (ClutterStage *stage, static PangoDirection clutter_get_text_direction (void) { - const gchar *direction; PangoDirection dir = PANGO_DIRECTION_LTR; + const gchar *direction; direction = g_getenv ("CLUTTER_TEXT_DIRECTION"); if (direction && *direction != '\0') { if (strcmp (direction, "rtl") == 0) dir = PANGO_DIRECTION_RTL; - else + else if (strcmp (direction, "ltr") == 0) dir = PANGO_DIRECTION_LTR; } else - dir = PANGO_DIRECTION_LTR; + { + /* Translate to default:RTL if you want your widgets + * to be RTL, otherwise translate to default:LTR. + * + * Do *not* translate it to "predefinito:LTR": if it + * it isn't default:LTR or default:RTL it will not work + */ + char *e = _("default:LTR"); + + if (strcmp (e, "default:RTL") == 0) + dir = PANGO_DIRECTION_RTL; + else if (strcmp (e, "default:LTR") == 0) + dir = PANGO_DIRECTION_LTR; + else + g_warning ("Whoever translated default:LTR did so wrongly."); + } return dir; } From 979b6794bb207575fdac6fa520cca5c9adf6f86e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 15:22:31 +0000 Subject: [PATCH 118/147] [text] Coalesce text visibility and password character Using two properties to set a password entry can be construed as both cumbersome and a gtk-ism. And rightly so on both counts. The :text-visible property has also conflicting semantics with the :cursor-visible one: while the latter hides the cursor, the former changes the display of the contents of the Text actor. It is, thus, not a matter of "visibility" but of "rendering". Instead of setting the :text-visible and :invisible-char properties to have a password text field, the Text actor should just have a single :password-char property holding a Unicode character. If the value of the :password-char is non-zero, the Text actor will use the Unicode character to render the contents of the text entry. This commit removes the following methods: clutter_text_set_text_visible() clutter_text_get_text_visible() clutter_text_set_invisible_char() clutter_text_get_invisible_char() And the following properties: ClutterText:text-visible ClutterText:invisible-char In favour of: clutter_text_set_password_char() clutter_text_get_password_char() And: ClutterText:password-char Thus making obvious what use the property and accessor methods are for and simplifying the process of creating a simple password text field to: text = clutter_text_new (); clutter_text_set_password_char (CLUTTER_TEXT (text), '*'); --- clutter/clutter-text.c | 177 ++++++++++------------------------------- clutter/clutter-text.h | 7 +- 2 files changed, 46 insertions(+), 138 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index e90491722..0b8deb153 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -123,7 +123,6 @@ struct _ClutterTextPrivate guint selectable : 1; guint in_select_drag : 1; guint cursor_color_set : 1; - guint text_visible : 1; /* current cursor position */ gint position; @@ -149,7 +148,7 @@ struct _ClutterTextPrivate gint max_length; - gunichar priv_char; + gunichar password_char; }; enum @@ -175,8 +174,7 @@ enum PROP_EDITABLE, PROP_SELECTABLE, PROP_ACTIVATABLE, - PROP_TEXT_VISIBLE, - PROP_INVISIBLE_CHAR, + PROP_PASSWORD_CHAR, PROP_MAX_LENGTH }; @@ -261,7 +259,7 @@ clutter_text_create_layout_no_cache (ClutterText *text, { if (!priv->use_markup) { - if (priv->text_visible) + if (G_LIKELY (priv->password_char == 0)) pango_layout_set_text (layout, priv->text, priv->n_bytes); else { @@ -270,10 +268,7 @@ clutter_text_create_layout_no_cache (ClutterText *text, gchar buf[7]; gint char_len, i; - if (priv->priv_char != 0) - invisible_char = priv->priv_char; - else - invisible_char = ' '; + invisible_char = priv->password_char; /* we need to convert the string built of invisible * characters into UTF-8 for it to be fed to the Pango @@ -459,20 +454,18 @@ clutter_text_position_to_coords (ClutterText *self, { ClutterTextPrivate *priv = self->priv; PangoRectangle rect; - gint priv_char_bytes; + gint password_char_bytes = 1; gint index_; - if (!priv->text_visible && priv->priv_char) - priv_char_bytes = g_unichar_to_utf8 (priv->priv_char, NULL); - else - priv_char_bytes = 1; + if (priv->password_char != 0) + password_char_bytes = g_unichar_to_utf8 (priv->password_char, NULL); if (position == -1) { - if (priv->text_visible) + if (priv->password_char == 0) index_ = priv->n_bytes; else - index_ = priv->n_chars * priv_char_bytes; + index_ = priv->n_chars * password_char_bytes; } else if (position == 0) { @@ -480,10 +473,10 @@ clutter_text_position_to_coords (ClutterText *self, } else { - if (priv->text_visible) + if (priv->password_char == 0) index_ = offset_to_bytes (priv->text, position); else - index_ = priv->position * priv_char_bytes; + index_ = priv->position * password_char_bytes; } pango_layout_get_cursor_pos (clutter_text_get_layout (self), index_, @@ -634,12 +627,8 @@ clutter_text_set_property (GObject *gobject, clutter_text_set_selectable (self, g_value_get_boolean (value)); break; - case PROP_TEXT_VISIBLE: - clutter_text_set_text_visible (self, g_value_get_boolean (value)); - break; - - case PROP_INVISIBLE_CHAR: - clutter_text_set_invisible_char (self, g_value_get_uint (value)); + case PROP_PASSWORD_CHAR: + clutter_text_set_password_char (self, g_value_get_uint (value)); break; case PROP_MAX_LENGTH: @@ -709,12 +698,8 @@ clutter_text_get_property (GObject *gobject, g_value_set_boolean (value, priv->activatable); break; - case PROP_TEXT_VISIBLE: - g_value_set_boolean (value, priv->text_visible); - break; - - case PROP_INVISIBLE_CHAR: - g_value_set_uint (value, priv->priv_char); + case PROP_PASSWORD_CHAR: + g_value_set_uint (value, priv->password_char); break; case PROP_MAX_LENGTH: @@ -1791,38 +1776,20 @@ clutter_text_class_init (ClutterTextClass *klass) g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); /** - * ClutterText:text-visible: + * ClutterText:password-char: * - * Whether the contents of the #ClutterText actor should be visible - * or substituted by a Unicode character, like in password text fields. - * The Unicode character is set using #ClutterText:invisible-char. + * If non-zero, the character that should be used in place of + * the actual text in a password text actor. * * Since: 1.0 */ - pspec = g_param_spec_boolean ("text-visible", - "Text Visible", - "Whether the text should be visible " - "or subsituted with an invisible " - "Unicode character", - FALSE, + pspec = g_param_spec_unichar ("password-char", + "Password Character", + "If non-zero, use this character to " + "display the actor's contents", + 0, CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_TEXT_VISIBLE, pspec); - - /** - * ClutterText:invisible-char: - * - * The Unicode character used to render the contents of the #ClutterText - * actor if #ClutterText:text-invisible is set to %TRUE. - * - * Since: 1.0 - */ - pspec = g_param_spec_unichar ("invisible-char", - "Invisible Character", - "The Unicode character used when the " - "text is set as not visible", - '*', - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_INVISIBLE_CHAR, pspec); + g_object_class_install_property (gobject_class, PROP_PASSWORD_CHAR, pspec); /** * ClutterText:max-length: @@ -2015,8 +1982,7 @@ clutter_text_init (ClutterText *self) priv->cursor_color_set = FALSE; - priv->text_visible = TRUE; - priv->priv_char = '*'; + priv->password_char = 0; priv->max_length = 0; @@ -3290,23 +3256,21 @@ clutter_text_get_cursor_size (ClutterText *self) } /** - * clutter_text_set_text_visible: + * clutter_text_set_password_char: * @self: a #ClutterText - * @visible: %TRUE if the contents of the actor are displayed as plain text. + * @wc: a Unicode character, or 0 * - * Sets whether the contents of the text actor are visible or not. When - * visibility is set to %FALSE, characters are displayed as the invisible - * char, and will also appear that way when the text in the text actor is - * copied elsewhere. + * Sets the character to use in place of the actual text in a + * password text actor. * - * The default invisible char is the asterisk '*', but it can be changed with - * clutter_text_set_invisible_char(). + * If @wc is 0 the text will be displayed as it is entered in the + * #ClutterText actor. * * Since: 1.0 */ void -clutter_text_set_text_visible (ClutterText *self, - gboolean visible) +clutter_text_set_password_char (ClutterText *self, + gunichar wc) { ClutterTextPrivate *priv; @@ -3314,88 +3278,35 @@ clutter_text_set_text_visible (ClutterText *self, priv = self->priv; - if (priv->text_visible != visible) + if (priv->password_char != wc) { - priv->text_visible = visible; + priv->password_char = wc; clutter_text_dirty_cache (self); - clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); - g_object_notify (G_OBJECT (self), "text-visible"); + g_object_notify (G_OBJECT (self), "password-char"); } } /** - * clutter_text_get_text_visible: + * clutter_text_get_password_char: * @self: a #ClutterText * - * Retrieves the actor's text visibility. + * Retrieves the character to use in place of the actual text + * as set by clutter_text_set_password_char(). * - * Return value: %TRUE if the contents of the actor are displayed as plaintext - * - * Since: 1.0 - */ -gboolean -clutter_text_get_text_visible (ClutterText *self) -{ - g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); - - return self->priv->text_visible; -} - -/** - * clutter_text_set_invisible_char: - * @self: a #ClutterText - * @wc: a Unicode character - * - * Sets the character to use in place of the actual text when - * clutter_text_set_text_visible() has been called to set text visibility - * to %FALSE. i.e. this is the character used in "password mode" to show the - * user how many characters have been typed. The default invisible char is an - * asterisk ('*'). If you set the invisible char to 0, then the user will get - * no feedback at all: there will be no text on the screen as they type. - * - * Since: 1.0 - */ -void -clutter_text_set_invisible_char (ClutterText *self, - gunichar wc) -{ - ClutterTextPrivate *priv; - - g_return_if_fail (CLUTTER_IS_TEXT (self)); - - priv = self->priv; - - priv->priv_char = wc; - - if (priv->text_visible) - { - clutter_text_dirty_cache (self); - clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); - } - - g_object_notify (G_OBJECT (self), "invisible-char"); -} - -/** - * clutter_text_get_invisible_char: - * @self: a #ClutterText - * - * Returns the character to use in place of the actual text when - * the #ClutterText:text-visibility property is set to %FALSE. - * - * Return value: a Unicode character + * Return value: a Unicode character or 0 if the password + * character is not set * * Since: 1.0 */ gunichar -clutter_text_get_invisible_char (ClutterText *self) +clutter_text_get_password_char (ClutterText *self) { - g_return_val_if_fail (CLUTTER_IS_TEXT (self), '*'); + g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); - return self->priv->priv_char; + return self->priv->password_char; } /** diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index bd63478ee..a194e2525 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -181,12 +181,9 @@ void clutter_text_set_selection (ClutterText *self gssize start_pos, gssize end_pos); gchar * clutter_text_get_selection (ClutterText *self); -void clutter_text_set_text_visible (ClutterText *self, - gboolean visible); -gboolean clutter_text_get_text_visible (ClutterText *self); -void clutter_text_set_invisible_char (ClutterText *self, +void clutter_text_set_password_char (ClutterText *self, gunichar wc); -gunichar clutter_text_get_invisible_char (ClutterText *self); +gunichar clutter_text_get_password_char (ClutterText *self); void clutter_text_set_max_length (ClutterText *self, gint max); gint clutter_text_get_max_length (ClutterText *self); From c4475c6bfc133eea9e44abea73c3fa2f121fded8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 15:29:44 +0000 Subject: [PATCH 119/147] [docs] Update the Text section After the 979b6794 commit, the section for ClutterText needs updating on the renamed and removed accessors. --- doc/reference/clutter/clutter-sections.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index a13b51d2f..52934b014 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1626,8 +1626,8 @@ clutter_text_set_ellipsize clutter_text_get_ellipsize clutter_text_set_font_name clutter_text_get_font_name -clutter_text_set_invisible_char -clutter_text_get_invisible_char +clutter_text_set_password_char +clutter_text_get_password_char clutter_text_set_justify clutter_text_get_justify clutter_text_get_layout @@ -1643,8 +1643,6 @@ clutter_text_set_selection clutter_text_get_selection clutter_text_set_selection_bound clutter_text_get_selection_bound -clutter_text_set_text_visible -clutter_text_get_text_visible clutter_text_set_use_markup clutter_text_get_use_markup From 87ab64d291781d468c55c8dd54dd9182b80921fd Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 15:30:31 +0000 Subject: [PATCH 120/147] [tests] Add unit for the ClutterText:password-char property Check that the contents of the Text actor are unaffected by the :password-char property; that the accessors are correct; and finally that the initial value for a newly constructed Text actor is valid. --- tests/conform/test-clutter-text.c | 19 +++++++++++++++++++ tests/conform/test-conform-main.c | 1 + 2 files changed, 20 insertions(+) diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c index 20cdb538c..6814013a8 100644 --- a/tests/conform/test-clutter-text.c +++ b/tests/conform/test-clutter-text.c @@ -301,6 +301,25 @@ test_text_delete_text (TestConformSimpleFixture *fixture, clutter_actor_destroy (CLUTTER_ACTOR (text)); } +void +test_text_password_char (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + g_assert_cmpint (clutter_text_get_password_char (text), ==, 0); + + clutter_text_set_text (text, "hello"); + g_assert_cmpstr (clutter_text_get_text (text), ==, "hello"); + + clutter_text_set_password_char (text, '*'); + g_assert_cmpint (clutter_text_get_password_char (text), ==, '*'); + + g_assert_cmpstr (clutter_text_get_text (text), ==, "hello"); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + static void init_event (ClutterKeyEvent *event) { diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index abf6280b2..1b8d54c0f 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -82,6 +82,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/text", test_text_event); TEST_CONFORM_SIMPLE ("/text", test_text_get_chars); TEST_CONFORM_SIMPLE ("/text", test_text_cache); + TEST_CONFORM_SIMPLE ("/text", test_text_password_char); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_size); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_color); From 3d32d464e9a83ca2a89700778ece28307f4d359e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 20:52:03 +0000 Subject: [PATCH 121/147] [text] Use cached length when possible Since clutter_text_set_text() measures the length of the text each time, we should use the cached length instead of recomputing the text length each time. This should save us some time when dealing with long, multi-byte texts. --- clutter/clutter-text.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 0b8deb153..da74de1c6 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1143,7 +1143,7 @@ clutter_text_real_move_left (ClutterText *self, gint pos = priv->position; gint len; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; if (pos != 0 && len !=0) { @@ -1169,7 +1169,7 @@ clutter_text_real_move_right (ClutterText *self, gint pos = priv->position; gint len; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; if (pos != -1 && len !=0) { @@ -3190,7 +3190,7 @@ clutter_text_set_cursor_position (ClutterText *self, priv = self->priv; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; if (position < 0 || position >= len) priv->position = -1; @@ -3335,7 +3335,7 @@ clutter_text_set_max_length (ClutterText *self, if (priv->max_length != max) { if (max < 0) - max = g_utf8_strlen (priv->text, -1); + max = priv->n_chars; priv->max_length = max; @@ -3528,7 +3528,7 @@ clutter_text_delete_chars (ClutterText *self, if (!priv->text) return; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; new = g_string_new (priv->text); if (priv->position == -1) From 8182b354b167681a89ef9c3354c1278378e4ea2c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 20:54:20 +0000 Subject: [PATCH 122/147] [text] Fix the deletion actions When using the delete-prev action from the end of the text we end up either missing the first glyph we have to delete or falling through the last one in the text. This commit fixes both issues. --- clutter/clutter-text.c | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index da74de1c6..a49312616 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1388,16 +1388,12 @@ clutter_text_real_del_next (ClutterText *self, return TRUE; pos = priv->position; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; if (len && pos != -1 && pos < len) - { - clutter_text_delete_text (self, pos, pos + 1); + clutter_text_delete_text (self, pos, pos + 1); - return TRUE; - } - - return FALSE; + return TRUE; } static gboolean @@ -1414,7 +1410,7 @@ clutter_text_real_del_prev (ClutterText *self, return TRUE; pos = priv->position; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; if (pos != 0 && len != 0) { @@ -1422,19 +1418,19 @@ clutter_text_real_del_prev (ClutterText *self, { clutter_text_set_cursor_position (self, len - 1); clutter_text_set_selection_bound (self, len - 1); + + clutter_text_delete_text (self, len - 1, len); } else { clutter_text_set_cursor_position (self, pos - 1); clutter_text_set_selection_bound (self, pos - 1); + + clutter_text_delete_text (self, pos - 1, pos); } - - clutter_text_delete_text (self, pos - 1, pos); - - return TRUE; } - return FALSE; + return TRUE; } static gboolean @@ -3480,18 +3476,15 @@ clutter_text_delete_text (ClutterText *self, if (!priv->text) return; - if (end_pos == -1) - { - start_bytes = offset_to_bytes (priv->text, - g_utf8_strlen (priv->text, -1) - 1); - end_bytes = offset_to_bytes (priv->text, - g_utf8_strlen (priv->text, -1)); - } + if (start_pos == 0) + start_bytes = 0; else - { - start_bytes = offset_to_bytes (priv->text, start_pos); - end_bytes = offset_to_bytes (priv->text, end_pos); - } + start_bytes = offset_to_bytes (priv->text, start_pos); + + if (end_pos == -1) + end_bytes = offset_to_bytes (priv->text, priv->n_chars); + else + end_bytes = offset_to_bytes (priv->text, end_pos); new = g_string_new (priv->text); new = g_string_erase (new, start_bytes, end_bytes - start_bytes); From 43f82332dd918c7e13e7896523a2508430cfa6f0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 00:25:24 +0000 Subject: [PATCH 123/147] [text] Add single-line-mode to ClutterText Allow using ClutterText as a single line text field. This is useful for text fields that accept just a single line of contents by default, and respond to the Enter key press to execute some action. The :single-line-mode property enables this behaviour inside ClutterText by clipping and scrolling the contents of the PangoLayout if they do not fit the allocated width of the Text actor. --- clutter/clutter-text.c | 175 +++++++++++++++++++++++++++++++++++++++-- clutter/clutter-text.h | 4 + 2 files changed, 172 insertions(+), 7 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index a49312616..8a3357652 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -135,6 +135,8 @@ struct _ClutterTextPrivate */ gint x_pos; + gint text_x; + /* the length of the text, in bytes */ gint n_bytes; @@ -175,7 +177,8 @@ enum PROP_SELECTABLE, PROP_ACTIVATABLE, PROP_PASSWORD_CHAR, - PROP_MAX_LENGTH + PROP_MAX_LENGTH, + PROP_SINGLE_LINE_MODE }; enum @@ -635,6 +638,10 @@ clutter_text_set_property (GObject *gobject, clutter_text_set_max_length (self, g_value_get_int (value)); break; + case PROP_SINGLE_LINE_MODE: + clutter_text_set_single_line_mode (self, g_value_get_boolean (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -706,6 +713,10 @@ clutter_text_get_property (GObject *gobject, g_value_set_int (value, priv->max_length); break; + case PROP_SINGLE_LINE_MODE: + g_value_set_boolean (value, priv->single_line_mode); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -769,8 +780,6 @@ cursor_paint (ClutterText *self) real_opacity); } - clutter_text_ensure_cursor_position (self); - if (priv->position == 0) priv->cursor_pos.x -= 2; @@ -997,6 +1006,8 @@ clutter_text_key_press (ClutterActor *actor, return FALSE; } +#define TEXT_PADDING 2 + static void clutter_text_paint (ClutterActor *self) { @@ -1006,6 +1017,8 @@ clutter_text_paint (ClutterActor *self) ClutterActorBox alloc = { 0, }; CoglColor color = { 0, }; guint8 real_opacity; + gint text_x = priv->text_x; + gboolean clip_set = FALSE; if (priv->font_desc == NULL || priv->text == NULL) { @@ -1015,23 +1028,81 @@ clutter_text_paint (ClutterActor *self) return; } - cursor_paint (text); - - CLUTTER_NOTE (PAINT, "painting text (text:`%s')", priv->text); + clutter_text_ensure_cursor_position (text); clutter_actor_get_allocation_box (self, &alloc); layout = clutter_text_create_layout (text, alloc.x2 - alloc.x1); + if (priv->single_line_mode) + { + PangoRectangle logical_rect = { 0, }; + gint actor_width, text_width; + + pango_layout_get_extents (layout, NULL, &logical_rect); + + cogl_clip_set (0, 0, + CLUTTER_UNITS_TO_FIXED (alloc.x2 - alloc.x1), + CLUTTER_UNITS_TO_FIXED (alloc.y2 - alloc.y1)); + clip_set = TRUE; + + actor_width = (CLUTTER_UNITS_TO_DEVICE (alloc.x2 - alloc.x1)) + - 2 * TEXT_PADDING; + text_width = logical_rect.width / PANGO_SCALE; + + if (actor_width < text_width) + { + gint cursor_x = priv->cursor_pos.x; + + if (priv->position == -1) + { + text_x = actor_width - text_width; + priv->cursor_pos.x += text_x + TEXT_PADDING; + } + else if (priv->position == 0) + { + text_x = 0; + } + else + { + if (text_x <= 0) + { + gint diff = -1 * text_x; + + if (cursor_x < diff) + text_x += diff - cursor_x; + else if (cursor_x > (diff + actor_width)) + text_x -= cursor_x - (diff - actor_width); + } + } + } + else + { + text_x = 0; + priv->cursor_pos.x += text_x + TEXT_PADDING; + } + } + else + text_x = 0; + + cursor_paint (text); + real_opacity = clutter_actor_get_paint_opacity (self) * priv->text_color.alpha / 255; + CLUTTER_NOTE (PAINT, "painting text (text:`%s')", priv->text); + cogl_color_set_from_4ub (&color, priv->text_color.red, priv->text_color.green, priv->text_color.blue, real_opacity); - cogl_pango_render_layout (layout, 0, 0, &color, 0); + cogl_pango_render_layout (layout, text_x, 0, &color, 0); + + if (clip_set) + cogl_clip_unset (); + + priv->text_x = text_x; } static void @@ -1801,6 +1872,26 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, pspec); + /** + * ClutterText:single-line-mode: + * + * Whether the #ClutterText actor should be in single line mode + * or not. A single line #ClutterText actor will only contain a + * single line of text, scrolling it in case its length is bigger + * than the allocated size. + * + * Setting this property will also set the #ClutterText:activatable + * property as a side-effect. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("single-line-mode", + "Single Line Mode", + "Whether the text should be a single line", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_SINGLE_LINE_MODE, pspec); + /** * ClutterText::text-changed: * @self: the #ClutterText that emitted the signal @@ -3588,3 +3679,73 @@ clutter_text_get_chars (ClutterText *self, return g_strndup (priv->text + start_index, end_index - start_index); } + +/** + * clutter_text_set_single_line_mode: + * @self: a #ClutterText + * @single_line: whether to enable single line mode + * + * Sets whether a #ClutterText actor should be in single line mode + * or not. + * + * A text actor in single line mode will not wrap text and will clip + * the the visible area to the predefined size. The contents of the + * text actor will scroll to display the end of the text if its length + * is bigger than the allocated width. + * + * When setting the single line mode the #ClutterText:activatable + * property is also set as a side effect. Instead of entering a new + * line character, the text actor will emit the #ClutterText::activate + * signal. + * + * Since: 1.0 + */ +void +clutter_text_set_single_line_mode (ClutterText *self, + gboolean single_line) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->single_line_mode != single_line) + { + g_object_freeze_notify (G_OBJECT (self)); + + priv->single_line_mode = single_line; + + if (priv->single_line_mode) + { + priv->activatable = TRUE; + + g_object_notify (G_OBJECT (self), "activatable"); + } + + clutter_text_dirty_cache (self); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "single-line-mode"); + + g_object_thaw_notify (G_OBJECT (self)); + } +} + +/** + * clutter_text_get_single_line_mode: + * @self: a #ClutterText + * + * Retrieves whether the #ClutterText actor is in single line mode. + * + * Return value: %TRUE if the #ClutterText actor is in single line mode + * + * Since: 1.0 + */ +gboolean +clutter_text_get_single_line_mode (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->single_line_mode; +} diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index a194e2525..57a3b2ed7 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -187,6 +187,10 @@ gunichar clutter_text_get_password_char (ClutterText *self void clutter_text_set_max_length (ClutterText *self, gint max); gint clutter_text_get_max_length (ClutterText *self); +void clutter_text_set_single_line_mode (ClutterText *self, + gboolean single_line); +gboolean clutter_text_get_single_line_mode (ClutterText *self); + gboolean clutter_text_activate (ClutterText *self); G_END_DECLS From 71c03df967e0725aa49f2a51de9909b2fd42d971 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 00:27:50 +0000 Subject: [PATCH 124/147] [tests] Add text field interactive test The test-text-field is a test/example that shows how to use the ClutterText as a text input field in single line mode. --- tests/interactive/Makefile.am | 3 +- tests/interactive/test-text-field.c | 117 ++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 tests/interactive/test-text-field.c diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index c07f48fa4..068bb7860 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -37,7 +37,8 @@ UNIT_TESTS = \ test-animation.c \ test-easing.c \ test-binding-pool.c \ - test-text.c + test-text.c \ + test-text-field.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-text-field.c b/tests/interactive/test-text-field.c new file mode 100644 index 000000000..ca582731d --- /dev/null +++ b/tests/interactive/test-text-field.c @@ -0,0 +1,117 @@ +#include +#include +#include + +#define FONT "Mono Bold 14px" + +static void +on_entry_paint (ClutterActor *actor, + gpointer data) +{ + ClutterActorBox allocation = { 0, }; + ClutterUnit width, height; + + clutter_actor_get_allocation_box (actor, &allocation); + width = allocation.x2 - allocation.x1; + height = allocation.y2 - allocation.y1; + + cogl_set_source_color4ub (255, 255, 255, 255); + cogl_path_round_rectangle (0, 0, + CLUTTER_UNITS_TO_FIXED (width), + CLUTTER_UNITS_TO_FIXED (height), + COGL_FIXED_FROM_INT (4), + COGL_ANGLE_FROM_DEG (1.0)); + cogl_path_stroke (); +} + +static void +on_entry_activate (ClutterText *text, + gpointer data) +{ + g_print ("Text activated: %s\n", clutter_text_get_text (text)); +} + +static ClutterActor * +create_label (const ClutterColor *color, + const gchar *text) +{ + ClutterActor *retval = clutter_text_new_full (FONT, text, color); + + clutter_text_set_editable (CLUTTER_TEXT (retval), FALSE); + clutter_text_set_selectable (CLUTTER_TEXT (retval), FALSE); + + return retval; +} + +static ClutterActor * +create_entry (const ClutterColor *color, + const gchar *text, + gunichar password_char, + gint max_length) +{ + ClutterActor *retval = clutter_text_new_full (FONT, text, color); + ClutterColor selection = { 0, }; + + clutter_actor_set_width (retval, 200); + clutter_actor_set_reactive (retval, TRUE); + + clutter_color_darken (color, &selection); + + clutter_text_set_editable (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_selectable (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_activatable (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_single_line_mode (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_password_char (CLUTTER_TEXT (retval), password_char); + clutter_text_set_cursor_color (CLUTTER_TEXT (retval), &selection); + clutter_text_set_max_length (CLUTTER_TEXT (retval), max_length); + + g_signal_connect (retval, "activate", + G_CALLBACK (on_entry_activate), + NULL); + g_signal_connect (retval, "paint", + G_CALLBACK (on_entry_paint), + NULL); + + return retval; +} + +G_MODULE_EXPORT gint +test_text_field_main (gint argc, + gchar **argv) +{ + ClutterActor *stage; + ClutterActor *text; + ClutterColor entry_color = {0x33, 0xff, 0x33, 0xff}; + ClutterColor label_color = {0xff, 0xff, 0xff, 0xff}; + ClutterColor background_color = {0x00, 0x00, 0x00, 0xff}; + gint height; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &background_color); + + text = create_label (&label_color, "Input field: "); + clutter_actor_set_position (text, 10, 10); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + height = clutter_actor_get_height (text); + + text = create_entry (&entry_color, "some text", 0, 0); + clutter_actor_set_position (text, 200, 10); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + text = create_label (&label_color, "Password field: "); + clutter_actor_set_position (text, 10, height + 12); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + text = create_entry (&entry_color, "password", '*', 8); + clutter_actor_set_position (text, 200, height + 12); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +} From 1223fcbb4fd3a0e4ba05a31ca6f71633a56045ee Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 00:29:41 +0000 Subject: [PATCH 125/147] Update the ignore file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 11ca7ca69..704c9e4ea 100644 --- a/.gitignore +++ b/.gitignore @@ -111,6 +111,7 @@ stamp-h1 /tests/interactive/test-easing /tests/interactive/test-interactive /tests/interactive/test-binding-pool +/tests/interactive/test-text-field /tests/interactive/redhand.png /tests/interactive/test-script.json /tests/conform/test-conformance @@ -171,5 +172,6 @@ stamp-h1 /clutter/x11/stamp-clutter-x11-enum-types.h /po/Makefile.in.in /po/POTFILES +/po/*.pot *.swp *~ From 7f9c384099c1ca839b34f32c0980f76fe87c19e4 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 00:30:59 +0000 Subject: [PATCH 126/147] [docs] Add newly added :single-line-mode accessors Add the ClutterText:single-line-mode property accessor methods to the API reference. --- doc/reference/clutter/clutter-sections.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 52934b014..6157b279c 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1643,6 +1643,8 @@ clutter_text_set_selection clutter_text_get_selection clutter_text_set_selection_bound clutter_text_get_selection_bound +clutter_text_set_single_line_mode +clutter_text_get_single_line_mode clutter_text_set_use_markup clutter_text_get_use_markup From f3142a70dc8e62127a22edf2ff6a8d01aac86329 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 00:43:24 +0000 Subject: [PATCH 127/147] Comments and whitespace fixes to ClutterText --- clutter/clutter-text.c | 20 +++++++++++--------- po/clutter-0.8.pot | 0 2 files changed, 11 insertions(+), 9 deletions(-) delete mode 100644 po/clutter-0.8.pot diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 8a3357652..922acc758 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -130,11 +130,15 @@ struct _ClutterTextPrivate /* current 'other end of selection' position */ gint selection_bound; - /* the x position in the pangolayout, used to + /* the x position in the PangoLayout, used to * avoid drifting when repeatedly moving up|down */ gint x_pos; + /* the x position of the PangoLayout when in + * single line mode, to scroll the contents of the + * text actor + */ gint text_x; /* the length of the text, in bytes */ @@ -192,8 +196,7 @@ enum static guint text_signals[LAST_SIGNAL] = { 0, }; -#define offset_real(t,p) \ - ((p) == -1 ? g_utf8_strlen ((t), -1) : (p)) +#define offset_real(t,p) ((p) == -1 ? g_utf8_strlen ((t), -1) : (p)) static gint offset_to_bytes (const gchar *text, @@ -226,8 +229,7 @@ offset_to_bytes (const gchar *text, return i; } -#define bytes_to_offset(t,p) \ - (g_utf8_pointer_to_offset ((t), (t) + (p))) +#define bytes_to_offset(t,p) (g_utf8_pointer_to_offset ((t), (t) + (p))) static inline void @@ -419,8 +421,8 @@ clutter_text_create_layout (ClutterText *text, static gint clutter_text_coords_to_position (ClutterText *text, - gint x, - gint y) + gint x, + gint y) { gint index_; gint px, py; @@ -1020,7 +1022,7 @@ clutter_text_paint (ClutterActor *self) gint text_x = priv->text_x; gboolean clip_set = FALSE; - if (priv->font_desc == NULL || priv->text == NULL) + if (G_UNLIKELY (priv->font_desc == NULL || priv->text == NULL)) { CLUTTER_NOTE (ACTOR, "desc: %p, text %p", priv->font_desc ? priv->font_desc : 0x0, @@ -3345,7 +3347,7 @@ clutter_text_get_cursor_size (ClutterText *self) /** * clutter_text_set_password_char: * @self: a #ClutterText - * @wc: a Unicode character, or 0 + * @wc: a Unicode character, or 0 to unset the password character * * Sets the character to use in place of the actual text in a * password text actor. diff --git a/po/clutter-0.8.pot b/po/clutter-0.8.pot deleted file mode 100644 index e69de29bb..000000000 From 328534fc95746ddd38d591efa471db142d1793c4 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 11:44:54 +0000 Subject: [PATCH 128/147] [text] Fix cursor sizing The cursor should be slightly smaller than the height of the actor, to allow for painting a border. Let's pad it by 1 pixel on the top and 1 on the bottom. Also, we should use the cursor size everywhere and not use hardcoded magic numbers. --- clutter/clutter-text.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 922acc758..ee6fcc68d 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -514,7 +514,7 @@ clutter_text_ensure_cursor_position (ClutterText *self) priv->cursor_pos.x = CLUTTER_UNITS_TO_DEVICE (x); priv->cursor_pos.y = CLUTTER_UNITS_TO_DEVICE (y); priv->cursor_pos.width = priv->cursor_size; - priv->cursor_pos.height = CLUTTER_UNITS_TO_DEVICE (cursor_height); + priv->cursor_pos.height = CLUTTER_UNITS_TO_DEVICE (cursor_height) - 2; g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } @@ -783,7 +783,7 @@ cursor_paint (ClutterText *self) } if (priv->position == 0) - priv->cursor_pos.x -= 2; + priv->cursor_pos.x -= priv->cursor_size; if (priv->position == priv->selection_bound) { From ad7d1b54bc86fe523167366583fbf463d15db631 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 11:46:22 +0000 Subject: [PATCH 129/147] Re-align ClutterText header file The addition of the single line mode accessor methods caused the re-alignment of the entire file. --- clutter/clutter-text.h | 182 ++++++++++++++++++++--------------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 57a3b2ed7..2dc4bfa04 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -96,102 +96,102 @@ struct _ClutterTextClass GType clutter_text_get_type (void) G_GNUC_CONST; -ClutterActor * clutter_text_new (void); -ClutterActor * clutter_text_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color); -ClutterActor * clutter_text_new_with_text (const gchar *font_name, - const gchar *text); +ClutterActor * clutter_text_new (void); +ClutterActor * clutter_text_new_full (const gchar *font_name, + const gchar *text, + const ClutterColor *color); +ClutterActor * clutter_text_new_with_text (const gchar *font_name, + const gchar *text); -G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *self); -void clutter_text_set_text (ClutterText *self, - const gchar *text); -PangoLayout * clutter_text_get_layout (ClutterText *self); -void clutter_text_set_color (ClutterText *self, - const ClutterColor *color); -void clutter_text_get_color (ClutterText *self, - ClutterColor *color); -void clutter_text_set_font_name (ClutterText *self, - const gchar *font_name); -G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *self); +G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *self); +void clutter_text_set_text (ClutterText *self, + const gchar *text); +PangoLayout * clutter_text_get_layout (ClutterText *self); +void clutter_text_set_color (ClutterText *self, + const ClutterColor *color); +void clutter_text_get_color (ClutterText *self, + ClutterColor *color); +void clutter_text_set_font_name (ClutterText *self, + const gchar *font_name); +G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *self); -void clutter_text_set_ellipsize (ClutterText *self, - PangoEllipsizeMode mode); -PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self); -void clutter_text_set_line_wrap (ClutterText *self, - gboolean line_wrap); -gboolean clutter_text_get_line_wrap (ClutterText *self); -void clutter_text_set_line_wrap_mode (ClutterText *self, - PangoWrapMode wrap_mode); -PangoWrapMode clutter_text_get_line_wrap_mode (ClutterText *self); -PangoLayout * clutter_text_get_layout (ClutterText *self); -void clutter_text_set_attributes (ClutterText *self, - PangoAttrList *attrs); -PangoAttrList * clutter_text_get_attributes (ClutterText *self); -void clutter_text_set_use_markup (ClutterText *self, - gboolean setting); -gboolean clutter_text_get_use_markup (ClutterText *self); -void clutter_text_set_alignment (ClutterText *self, - PangoAlignment alignment); -PangoAlignment clutter_text_get_alignment (ClutterText *self); -void clutter_text_set_justify (ClutterText *self, - gboolean justify); -gboolean clutter_text_get_justify (ClutterText *self); +void clutter_text_set_ellipsize (ClutterText *self, + PangoEllipsizeMode mode); +PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self); +void clutter_text_set_line_wrap (ClutterText *self, + gboolean line_wrap); +gboolean clutter_text_get_line_wrap (ClutterText *self); +void clutter_text_set_line_wrap_mode (ClutterText *self, + PangoWrapMode wrap_mode); +PangoWrapMode clutter_text_get_line_wrap_mode (ClutterText *self); +PangoLayout * clutter_text_get_layout (ClutterText *self); +void clutter_text_set_attributes (ClutterText *self, + PangoAttrList *attrs); +PangoAttrList * clutter_text_get_attributes (ClutterText *self); +void clutter_text_set_use_markup (ClutterText *self, + gboolean setting); +gboolean clutter_text_get_use_markup (ClutterText *self); +void clutter_text_set_alignment (ClutterText *self, + PangoAlignment alignment); +PangoAlignment clutter_text_get_alignment (ClutterText *self); +void clutter_text_set_justify (ClutterText *self, + gboolean justify); +gboolean clutter_text_get_justify (ClutterText *self); -void clutter_text_insert_unichar (ClutterText *self, - gunichar wc); -void clutter_text_delete_chars (ClutterText *self, - guint n_chars); -void clutter_text_insert_text (ClutterText *self, - const gchar *text, - gssize position); -void clutter_text_delete_text (ClutterText *self, - gssize start_pos, - gssize end_pos); -gchar * clutter_text_get_chars (ClutterText *self, - gssize start_pos, - gssize end_pos); -void clutter_text_set_editable (ClutterText *self, - gboolean editable); -gboolean clutter_text_get_editable (ClutterText *self); -void clutter_text_set_activatable (ClutterText *self, - gboolean activatable); -gboolean clutter_text_get_activatable (ClutterText *self); +void clutter_text_insert_unichar (ClutterText *self, + gunichar wc); +void clutter_text_delete_chars (ClutterText *self, + guint n_chars); +void clutter_text_insert_text (ClutterText *self, + const gchar *text, + gssize position); +void clutter_text_delete_text (ClutterText *self, + gssize start_pos, + gssize end_pos); +gchar * clutter_text_get_chars (ClutterText *self, + gssize start_pos, + gssize end_pos); +void clutter_text_set_editable (ClutterText *self, + gboolean editable); +gboolean clutter_text_get_editable (ClutterText *self); +void clutter_text_set_activatable (ClutterText *self, + gboolean activatable); +gboolean clutter_text_get_activatable (ClutterText *self); -gint clutter_text_get_cursor_position (ClutterText *self); -void clutter_text_set_cursor_position (ClutterText *self, - gint position); -void clutter_text_set_cursor_visible (ClutterText *self, - gboolean cursor_visible); -gboolean clutter_text_get_cursor_visible (ClutterText *self); -void clutter_text_set_cursor_color (ClutterText *self, - const ClutterColor *color); -void clutter_text_get_cursor_color (ClutterText *self, - ClutterColor *color); -void clutter_text_set_cursor_size (ClutterText *self, - gint size); -guint clutter_text_get_cursor_size (ClutterText *self); -void clutter_text_set_selectable (ClutterText *self, - gboolean selectable); -gboolean clutter_text_get_selectable (ClutterText *self); -void clutter_text_set_selection_bound (ClutterText *self, - gint selection_bound); -gint clutter_text_get_selection_bound (ClutterText *self); -void clutter_text_set_selection (ClutterText *self, - gssize start_pos, - gssize end_pos); -gchar * clutter_text_get_selection (ClutterText *self); -void clutter_text_set_password_char (ClutterText *self, - gunichar wc); -gunichar clutter_text_get_password_char (ClutterText *self); -void clutter_text_set_max_length (ClutterText *self, - gint max); -gint clutter_text_get_max_length (ClutterText *self); -void clutter_text_set_single_line_mode (ClutterText *self, - gboolean single_line); -gboolean clutter_text_get_single_line_mode (ClutterText *self); +gint clutter_text_get_cursor_position (ClutterText *self); +void clutter_text_set_cursor_position (ClutterText *self, + gint position); +void clutter_text_set_cursor_visible (ClutterText *self, + gboolean cursor_visible); +gboolean clutter_text_get_cursor_visible (ClutterText *self); +void clutter_text_set_cursor_color (ClutterText *self, + const ClutterColor *color); +void clutter_text_get_cursor_color (ClutterText *self, + ClutterColor *color); +void clutter_text_set_cursor_size (ClutterText *self, + gint size); +guint clutter_text_get_cursor_size (ClutterText *self); +void clutter_text_set_selectable (ClutterText *self, + gboolean selectable); +gboolean clutter_text_get_selectable (ClutterText *self); +void clutter_text_set_selection_bound (ClutterText *self, + gint selection_bound); +gint clutter_text_get_selection_bound (ClutterText *self); +void clutter_text_set_selection (ClutterText *self, + gssize start_pos, + gssize end_pos); +gchar * clutter_text_get_selection (ClutterText *self); +void clutter_text_set_password_char (ClutterText *self, + gunichar wc); +gunichar clutter_text_get_password_char (ClutterText *self); +void clutter_text_set_max_length (ClutterText *self, + gint max); +gint clutter_text_get_max_length (ClutterText *self); +void clutter_text_set_single_line_mode (ClutterText *self, + gboolean single_line); +gboolean clutter_text_get_single_line_mode (ClutterText *self); -gboolean clutter_text_activate (ClutterText *self); +gboolean clutter_text_activate (ClutterText *self); G_END_DECLS From 796294fd4e8fb78798f6000bc44cbcc646599481 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 12:16:08 +0000 Subject: [PATCH 130/147] [tests] Remove test-opacity interactive test Merge fall-out from the text-actor branch. The test-opacity test was moved to the conformance test suite. --- tests/interactive/Makefile.am | 1 - tests/interactive/test-opacity.c | 116 ------------------------------- 2 files changed, 117 deletions(-) delete mode 100644 tests/interactive/test-opacity.c diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index b5524c110..33b371437 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -21,7 +21,6 @@ UNIT_TESTS = \ test-unproject.c \ test-viewport.c \ test-fbo.c \ - test-opacity.c \ test-multistage.c \ test-cogl-primitives.c \ test-cogl-tex-tile.c \ diff --git a/tests/interactive/test-opacity.c b/tests/interactive/test-opacity.c deleted file mode 100644 index cef053446..000000000 --- a/tests/interactive/test-opacity.c +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include - -#include -#include - -#include - -G_MODULE_EXPORT int -test_opacity_main (int argc, char *argv[]) -{ - ClutterActor *stage, *group1, *group2, *label, *rect; - ClutterColor label_color = { 255, 0, 0, 128 }; - ClutterColor rect_color = { 0, 0, 255, 255 }; - ClutterColor color_check = { 0, }; - - clutter_init (&argc, &argv); - - stage = clutter_stage_get_default (); - - label = clutter_label_new_with_text ("Sans 18px", "Label, 50% opacity"); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); - - g_print ("label 50%%.get_color()/1\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); - g_assert (color_check.alpha == label_color.alpha); - - clutter_container_add (CLUTTER_CONTAINER (stage), label, NULL); - clutter_actor_set_position (label, 10, 10); - - g_print ("label 50%%.get_color()/2\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); - g_assert (color_check.alpha == label_color.alpha); - - g_print ("label 50%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (label)); - g_assert (clutter_actor_get_paint_opacity (label) == 128); - - clutter_actor_show (label); - - group1 = clutter_group_new (); - clutter_actor_set_opacity (group1, 128); - clutter_container_add (CLUTTER_CONTAINER (stage), group1, NULL); - clutter_actor_set_position (group1, 10, 30); - clutter_actor_show (group1); - - label = clutter_label_new_with_text ("Sans 18px", "Label+Group, 25% opacity"); - - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); - - g_print ("label 50%% + group 50%%.get_color()/1\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); - g_assert (color_check.alpha == label_color.alpha); - - clutter_container_add (CLUTTER_CONTAINER (group1), label, NULL); - - g_print ("label 50%% + group 50%%.get_color()/2\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); - g_assert (color_check.alpha == label_color.alpha); - - g_print ("label 50%% + group 50%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (label)); - g_assert (clutter_actor_get_paint_opacity (label) == 64); - - clutter_actor_show (label); - - group2 = clutter_group_new (); - clutter_container_add (CLUTTER_CONTAINER (group1), group2, NULL); - clutter_actor_set_position (group2, 10, 60); - clutter_actor_show (group2); - - rect = clutter_rectangle_new_with_color (&rect_color); - clutter_actor_set_size (rect, 128, 128); - - g_print ("rect 100%% + group 100%% + group 50%%.get_color()/1\n"); - clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); - g_assert (color_check.alpha == rect_color.alpha); - - clutter_container_add (CLUTTER_CONTAINER (group2), rect, NULL); - - g_print ("rect 100%% + group 100%% + group 50%%.get_color()/2\n"); - clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); - g_assert (color_check.alpha == rect_color.alpha); - - g_print ("rect 100%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (rect)); - g_assert (clutter_actor_get_paint_opacity (rect) == 128); - - clutter_actor_show (rect); - - rect = clutter_rectangle_new_with_color (&rect_color); - clutter_actor_set_size (rect, 128, 128); - clutter_actor_set_position (rect, 150, 90); - - g_print ("rect 100%%.get_color()/1\n"); - clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); - g_assert (color_check.alpha == rect_color.alpha); - - clutter_container_add (CLUTTER_CONTAINER (stage), rect, NULL); - - g_print ("rect 100%%.get_color()/2\n"); - clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); - g_assert (color_check.alpha == rect_color.alpha); - - g_print ("rect 100%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (rect)); - g_assert (clutter_actor_get_paint_opacity (rect) == 255); - - clutter_actor_show (rect); - - clutter_actor_show_all (stage); - - clutter_main (); - - return EXIT_SUCCESS; -} From c297d1ccf14c8c798773f7e179bb7d1eaa9d6521 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 12:17:09 +0000 Subject: [PATCH 131/147] [tests] Make test-scale use ClutterText The ClutterLabel actor has been superceded by ClutterText. This is merge fall-out from the text-actor branch. --- tests/interactive/test-scale.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/interactive/test-scale.c b/tests/interactive/test-scale.c index 549fa298b..587cf2016 100644 --- a/tests/interactive/test-scale.c +++ b/tests/interactive/test-scale.c @@ -29,7 +29,7 @@ set_next_gravity (ClutterActor *actor) eclass = g_type_class_ref (CLUTTER_TYPE_GRAVITY); evalue = g_enum_get_value (eclass, gravity); - clutter_label_set_text (CLUTTER_LABEL (label), evalue->value_nick); + clutter_text_set_text (CLUTTER_TEXT (label), evalue->value_nick); g_type_class_unref (eclass); if (++gindex >= G_N_ELEMENTS (gravities)) @@ -59,8 +59,8 @@ test_scale_main (int argc, char *argv[]) clutter_group_add (CLUTTER_GROUP (stage), rect); - label = clutter_label_new_with_text ("Sans 20px", ""); - clutter_label_set_color (CLUTTER_LABEL (label), + label = clutter_text_new_with_text ("Sans 20px", ""); + clutter_text_set_color (CLUTTER_TEXT (label), &(ClutterColor) { 0xff, 0xff, 0xff, 0xff }); clutter_actor_set_position (label, clutter_actor_get_x (rect), From 368df450b21e6a731a61d5f30d6b997e258ef0e3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 13:14:13 +0000 Subject: [PATCH 132/147] [text] Do not ensure the cursor if not needed If the Text actor is neither editable nor has its cursor set to visible, then we should not be ensuring the cursor position. This fixes a failure in the conformance test unit for the layout cache. --- clutter/clutter-text.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index fa3fbb402..56db985ef 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1030,12 +1030,13 @@ clutter_text_paint (ClutterActor *self) return; } - clutter_text_ensure_cursor_position (text); - clutter_actor_get_allocation_box (self, &alloc); layout = clutter_text_create_layout (text, alloc.x2 - alloc.x1); - if (priv->single_line_mode) + if (priv->editable && priv->cursor_visible) + clutter_text_ensure_cursor_position (text); + + if (priv->editable && priv->single_line_mode) { PangoRectangle logical_rect = { 0, }; gint actor_width, text_width; From c1c713119990f222b6d48ab52ca78c1e383ac332 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 13:48:45 +0000 Subject: [PATCH 133/147] [text] Do not use markup on an editable Text An editable ClutterText should not use pango_layout_set_markup(), as the contents of the text actor will not match the text. Only read-only text actors should parse the contents for Pango markup. --- clutter/clutter-text.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 56db985ef..9f3800354 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -262,7 +262,9 @@ clutter_text_create_layout_no_cache (ClutterText *text, if (priv->text) { - if (!priv->use_markup) + if (priv->use_markup && !priv->editable) + pango_layout_set_markup (layout, priv->text, -1); + else { if (G_LIKELY (priv->password_char == 0)) pango_layout_set_text (layout, priv->text, priv->n_bytes); @@ -290,8 +292,6 @@ clutter_text_create_layout_no_cache (ClutterText *text, g_string_free (str, TRUE); } } - else - pango_layout_set_markup (layout, priv->text, -1); } if (allocation_width > 0 && @@ -3145,6 +3145,9 @@ clutter_text_get_alignment (ClutterText *self) * Sets whether the contents of the #ClutterText actor contains markup * in Pango's text markup language. * + * Calling this function on an editable #ClutterText will not cause + * the actor to parse any markup. + * * Since: 1.0 */ void From 0d9c07f82464962a189cdf1c05c1afe0e10cd9bf Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 14:53:54 +0000 Subject: [PATCH 134/147] [docs] Documentation fixes Remove the causes of warnings from the Clutter gtk-doc API reference generation process. --- clutter/clutter-binding-pool.c | 2 +- doc/reference/clutter/clutter-sections.txt | 19 ++----------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/clutter/clutter-binding-pool.c b/clutter/clutter-binding-pool.c index 28082cb63..78a483f72 100644 --- a/clutter/clutter-binding-pool.c +++ b/clutter/clutter-binding-pool.c @@ -542,7 +542,7 @@ clutter_binding_pool_override_action (ClutterBindingPool *pool, } /** - * clutter_binding_pool_override_action: + * clutter_binding_pool_override_closure: * @pool: a #ClutterBindingPool * @key_val: key symbol * @modifiers: bitmask of modifiers diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index bc23d5a56..7c9ed607b 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -437,6 +437,7 @@ clutter_actor_get_rotationx clutter_actor_grab_key_focus clutter_actor_get_pango_context +clutter_actor_create_pango_context CLUTTER_TYPE_GEOMETRY @@ -702,11 +703,9 @@ CLUTTER_PATH_CLASS CLUTTER_IS_PATH CLUTTER_IS_PATH_CLASS CLUTTER_PATH_GET_CLASS -CLUTTER_BEZIER_MAX_LENGTH CLUTTER_PATH_RELATIVE -ClutterBezier ClutterPathPrivate clutter_path_get_type clutter_path_node_get_type @@ -1479,20 +1478,6 @@ CLUTTER_VALUE_HOLDS_SHADER_MATRIX clutter_value_set_shader_matrix clutter_value_get_shader_matrix - -CLUTTER_VALUE_HOLDS_SHADER_FLOAT -ClutterShaderFloat -clutter_value_set_shader_float -clutter_value_get_shader_float -CLUTTER_VALUE_HOLDS_SHADER_INT -ClutterShaderInt -clutter_value_set_shader_int -clutter_value_get_shader_int -CLUTTER_VALUE_HOLDS_SHADER_MATRIX -ClutterShaderMatrix -clutter_value_set_shader_matrix -clutter_value_get_shader_matrix - CLUTTER_IS_SHADER CLUTTER_IS_SHADER_CLASS @@ -1623,8 +1608,8 @@ clutter_binding_pool_activate
-ClutterCairoTexture clutter-cairo-texture +ClutterCairoTexture ClutterCairoTexture ClutterCairoTextureClass clutter_cairo_texture_new From 5913bcf7e3b1ac4317c33a01362c1fc6e6679704 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 15:54:08 +0000 Subject: [PATCH 135/147] [tests] Update the ClutterText interactive test Remove the unused functions and if the test fails to load the text from a file, show the error inside the text actor itself. --- tests/interactive/test-text.c | 134 ++++++---------------------------- 1 file changed, 22 insertions(+), 112 deletions(-) diff --git a/tests/interactive/test-text.c b/tests/interactive/test-text.c index 8b9bd1c65..d9bf879a7 100644 --- a/tests/interactive/test-text.c +++ b/tests/interactive/test-text.c @@ -1,94 +1,16 @@ -/* Try this text editor, it has issues but does work, - * try ctrl+A, ctrl+C, ctrl+V, ctrl+X as well as selecting text with - * mouse and keyboard, /Øyvind K - */ +#include #include #include #define FONT "Mono Bold 22px" -static gchar *clipboard = NULL; - static gchar *runes = "ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ\n" "ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ\n" "ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬\n"; -static gboolean -select_all (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - gint len; - len = g_utf8_strlen (clutter_text_get_text (ttext), -1); - - clutter_text_set_cursor_position (ttext, 0); - clutter_text_set_selection_bound (ttext, len); - - return TRUE; -} - -static gboolean -copy (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - if (clipboard) - g_free (clipboard); - clipboard = clutter_text_get_selection (ttext); - return TRUE; -} - -static gboolean -paste (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - const gchar *p; - if (!clipboard) - return TRUE; - - for (p=clipboard; *p!='\0'; p=g_utf8_next_char (p)) - { - clutter_text_insert_unichar (ttext, g_utf8_get_char_validated (p, 3)); - } - return TRUE; -} - -static gboolean -cut (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - clutter_text_action (ttext, "copy", NULL); - clutter_text_action (ttext, "truncate-selection", NULL); - return TRUE; -} - -static gboolean -pageup (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - gint i; - for (i=0;i<10;i++) - clutter_text_action (ttext, "move-up", event); - return TRUE; -} - -static gboolean -pagedown (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - gint i; - for (i=0;i<10;i++) - clutter_text_action (ttext, "move-down", event); - return TRUE; -} - static void cursor_event (ClutterText *text, ClutterGeometry *geometry) @@ -112,11 +34,12 @@ G_MODULE_EXPORT gint test_text_main (gint argc, gchar **argv) { - ClutterActor *stage; - ClutterActor *text; - ClutterColor text_color = {0x33, 0xff, 0x33, 0xff}; - ClutterColor cursor_color = {0xff, 0x33, 0x33, 0xff}; - ClutterColor background_color = {0x00, 0x00, 0x00, 0xff}; + ClutterActor *stage; + ClutterActor *text; + ClutterColor text_color = { 0x33, 0xff, 0x33, 0xff }; + ClutterColor cursor_color = { 0xff, 0x33, 0x33, 0xff }; + ClutterColor background_color = { 0x00, 0x00, 0x00, 0xff }; + clutter_init (&argc, &argv); stage = clutter_stage_get_default (); @@ -136,38 +59,24 @@ test_text_main (gint argc, clutter_text_set_selectable (CLUTTER_TEXT (text), TRUE); clutter_text_set_cursor_color (CLUTTER_TEXT (text), &cursor_color); -#if 0 - clutter_text_add_action (CLUTTER_TEXT (text), "select-all", select_all); - clutter_text_add_action (CLUTTER_TEXT (text), "copy", copy); - clutter_text_add_action (CLUTTER_TEXT (text), "paste", paste); - clutter_text_add_action (CLUTTER_TEXT (text), "cut", cut); - clutter_text_add_action (CLUTTER_TEXT (text), "pageup", pageup); - clutter_text_add_action (CLUTTER_TEXT (text), "pagedown", pagedown); - - clutter_text_add_mapping (CLUTTER_TEXT (text), - CLUTTER_a, CLUTTER_CONTROL_MASK, "select-all"); - clutter_text_add_mapping (CLUTTER_TEXT (text), - CLUTTER_c, CLUTTER_CONTROL_MASK, "copy"); - clutter_text_add_mapping (CLUTTER_TEXT (text), - CLUTTER_v, CLUTTER_CONTROL_MASK, "paste"); - clutter_text_add_mapping (CLUTTER_TEXT (text), - CLUTTER_x, CLUTTER_CONTROL_MASK, "cut"); - clutter_text_add_mapping (CLUTTER_TEXT (text), - CLUTTER_Page_Up, 0, "pageup"); - clutter_text_add_mapping (CLUTTER_TEXT (text), - CLUTTER_Page_Down, 0, "pagedown"); -#endif - if (argv[1]) { - gchar *utf8; - g_file_get_contents (argv[1], &utf8, NULL, NULL); + GError *error = NULL; + gchar *utf8; + + g_file_get_contents (argv[1], &utf8, NULL, &error); + if (error) + { + utf8 = g_strconcat ("Unable to open '", argv[1], "':\n", + error->message, + NULL); + g_error_free (error); + } + clutter_text_set_text (CLUTTER_TEXT (text), utf8); } else - { - clutter_text_set_text (CLUTTER_TEXT (text), runes); - } + clutter_text_set_text (CLUTTER_TEXT (text), runes); g_signal_connect (text, "cursor-event", G_CALLBACK (cursor_event), NULL); @@ -175,5 +84,6 @@ test_text_main (gint argc, clutter_actor_show (stage); clutter_main (); - return 0; + + return EXIT_SUCCESS; } From 5d5b93bd2a11ba8e6fb5e331f1849640f1153a3b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 16:26:03 +0000 Subject: [PATCH 136/147] Rename a variable masking index() The maintainer CFLAGS found another masking of the index() function by an helpless variable. --- clutter/clutter-text.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 9f3800354..4b7ae3f13 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -826,7 +826,7 @@ cursor_paint (ClutterText *self) gint n_ranges; gint *ranges; gint i; - gint index; + gint index_; gint maxindex; ClutterUnit y, height; @@ -838,10 +838,10 @@ cursor_paint (ClutterText *self) pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges); - pango_layout_line_x_to_index (line, 0, &index, NULL); + pango_layout_line_x_to_index (line, 0, &index_, NULL); clutter_text_position_to_coords (self, - bytes_to_offset (utf8, index), + bytes_to_offset (utf8, index_), NULL, &y, &height); for (i = 0; i < n_ranges; i++) From cc8cd8392f109edeec073fcecab12443bbbaedd2 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 7 Jan 2009 12:08:43 +0000 Subject: [PATCH 137/147] [test-depth] Use a gint for width, not guint, when calculating -width/2 It was a fluke that this worked out due to how clutter_actor_set_depth internally converts the incorrect integer result to fixed point. --- tests/interactive/test-depth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/interactive/test-depth.c b/tests/interactive/test-depth.c index 02e5e8bce..34c024ede 100644 --- a/tests/interactive/test-depth.c +++ b/tests/interactive/test-depth.c @@ -26,7 +26,7 @@ raise_top (gpointer ignored) static ClutterActor * clone_box (ClutterTexture *original) { - guint width, height; + gint width, height; ClutterActor *group; ClutterActor *clone; From e4b1859077797049918d97d9ba1ec7b0c3cac0b8 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 7 Jan 2009 12:33:40 +0000 Subject: [PATCH 138/147] [test-clip] Use gint for hand_{width,height} when calulating -hand_* /2 It was a fluke that this worked out due to how the incorrect integer result gets converted by CLUTTER_INT_TO_FIXED. --- tests/interactive/test-clip.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/interactive/test-clip.c b/tests/interactive/test-clip.c index 1d3d05a33..5ccddfbcc 100644 --- a/tests/interactive/test-clip.c +++ b/tests/interactive/test-clip.c @@ -15,7 +15,7 @@ typedef enum struct _Clip { ClipType type; - gint x1, y1, x2, y2; + gint x1, y1, x2, y2; }; struct _CallbackData @@ -24,7 +24,7 @@ struct _CallbackData CoglHandle hand; Clip current_clip; - + GSList *clips; }; @@ -122,7 +122,7 @@ on_paint (ClutterActor *actor, CallbackData *data) { int i; ClutterGeometry stage_size; - guint hand_width, hand_height; + gint hand_width, hand_height; GSList *node; clutter_actor_get_allocation_geometry (data->stage, &stage_size); @@ -242,7 +242,7 @@ on_motion (ClutterActor *stage, ClutterMotionEvent *event, { data->current_clip.x2 = event->x; data->current_clip.y2 = event->y; - + clutter_actor_queue_redraw (stage); } From e9ee7f049d51c5d3a273385dae0d073c373ca51c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 8 Jan 2009 15:45:22 +0000 Subject: [PATCH 139/147] Fix last improper usage of ClutterUnits ClutterUnits should not be used interchangeably as, or with ClutterFixed values. ClutterUnits should also not be assumed to be integers. This commit fixes the last few improper usages of ClutterUnit values, and adds a CLUTTER_UNITS_FORMAT macro for safely printing ClutterUnit values with printf(). --- clutter/clutter-actor.c | 44 ++++++++++++--------- clutter/clutter-units.h | 2 + tests/interactive/test-project.c | 65 +++++++++++++++----------------- 3 files changed, 59 insertions(+), 52 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 7d616266e..2f39c4f0c 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -1013,7 +1013,8 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, ClutterVertex *vertex) { ClutterFixed v[4]; - ClutterFixed x, y, z, w; + ClutterUnit x, y, z, w; + fixed_vertex_t tmp; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor)); @@ -1026,8 +1027,7 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, w = COGL_FIXED_1; /* First we tranform the point using the OpenGL modelview matrix */ - clutter_actor_transform_point_relative (self, ancestor, - &x, &y, &z, &w); + clutter_actor_transform_point_relative (self, ancestor, &x, &y, &z, &w); cogl_get_viewport (v); @@ -1035,12 +1035,12 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, * The w[3] parameter should always be 1.0 here, so we ignore it; otherwise * we would have to divide the original verts with it. */ - vertex->x = - CLUTTER_UNITS_FROM_FIXED (COGL_FIXED_MUL ((x + COGL_FIXED_0_5), v[2])); - vertex->y = - CLUTTER_UNITS_FROM_FIXED (COGL_FIXED_MUL ((COGL_FIXED_0_5 - y), v[3])); - vertex->z = - CLUTTER_UNITS_FROM_FIXED (COGL_FIXED_MUL ((z + COGL_FIXED_0_5), v[2])); + tmp.x = COGL_FIXED_MUL (CLUTTER_UNITS_TO_FIXED (x) + COGL_FIXED_0_5, v[2]); + tmp.y = COGL_FIXED_MUL (COGL_FIXED_0_5 - CLUTTER_UNITS_TO_FIXED (y), v[3]); + tmp.z = COGL_FIXED_MUL (CLUTTER_UNITS_TO_FIXED (z) + COGL_FIXED_0_5, v[2]); + tmp.w = 0; + + fixed_vertex_to_units (&tmp, vertex); } /** @@ -1060,6 +1060,7 @@ clutter_actor_apply_transform_to_point (ClutterActor *self, const ClutterVertex *point, ClutterVertex *vertex) { + ClutterUnit x, y, z, w; ClutterFixed mtx_p[16]; ClutterFixed v[4]; fixed_vertex_t tmp = { 0, }; @@ -1068,13 +1069,18 @@ clutter_actor_apply_transform_to_point (ClutterActor *self, g_return_if_fail (point != NULL); g_return_if_fail (vertex != NULL); - tmp.x = CLUTTER_UNITS_TO_FIXED (vertex->x); - tmp.y = CLUTTER_UNITS_TO_FIXED (vertex->y); - tmp.z = CLUTTER_UNITS_TO_FIXED (vertex->z); - tmp.w = COGL_FIXED_1; + x = point->x; + y = point->y; + z = point->z; + w = CLUTTER_UNITS_FROM_INT (1); /* First we tranform the point using the OpenGL modelview matrix */ - clutter_actor_transform_point (self, &tmp.x, &tmp.y, &tmp.z, &tmp.w); + clutter_actor_transform_point (self, &x, &y, &z, &w); + + tmp.x = CLUTTER_UNITS_TO_FIXED (x); + tmp.y = CLUTTER_UNITS_TO_FIXED (y); + tmp.z = CLUTTER_UNITS_TO_FIXED (z); + tmp.w = CLUTTER_UNITS_TO_FIXED (w); cogl_get_projection_matrix (mtx_p); cogl_get_viewport (v); @@ -3244,8 +3250,9 @@ clutter_actor_get_preferred_width (ClutterActor *self, if (natural_width < min_width) { - g_warning ("Actor of type %s reported a natural width of %d (%d px) " - "lower than min width %d (%d px)", + g_warning ("Actor of type %s reported a natural width " + "of %" CLUTTER_UNITS_FORMAT " (%d px) lower " + "than min width %" CLUTTER_UNITS_FORMAT " (%d px)", G_OBJECT_TYPE_NAME (self), natural_width, CLUTTER_UNITS_TO_DEVICE (natural_width), min_width, CLUTTER_UNITS_TO_DEVICE (min_width)); @@ -3314,8 +3321,9 @@ clutter_actor_get_preferred_height (ClutterActor *self, if (natural_height < min_height) { - g_warning ("Actor of type %s reported a natural height of %d " - "(%d px) lower than min height %d (%d px)", + g_warning ("Actor of type %s reported a natural height " + "of %" CLUTTER_UNITS_FORMAT " (%d px) lower than " + "min height %" CLUTTER_UNITS_FORMAT " (%d px)", G_OBJECT_TYPE_NAME (self), natural_height, CLUTTER_UNITS_TO_DEVICE (natural_height), min_height, CLUTTER_UNITS_TO_DEVICE (min_height)); diff --git a/clutter/clutter-units.h b/clutter/clutter-units.h index 8337d19ff..b85375a98 100644 --- a/clutter/clutter-units.h +++ b/clutter/clutter-units.h @@ -59,6 +59,8 @@ typedef gint32 ClutterUnit; #define CLUTTER_UNITS_FROM_FIXED(x) (x) #define CLUTTER_UNITS_TO_FIXED(x) (x) +#define CLUTTER_UNITS_FORMAT "d" + /** * CLUTTER_UNITS_FROM_DEVICE: * @x: value in pixels diff --git a/tests/interactive/test-project.c b/tests/interactive/test-project.c index 33560df0b..7c9403014 100644 --- a/tests/interactive/test-project.c +++ b/tests/interactive/test-project.c @@ -21,19 +21,19 @@ init_handles () clutter_actor_set_position (p[i], 0, 0); clutter_group_add (CLUTTER_GROUP (main_stage), p[i]); - clutter_actor_set_position (p[i], - CLUTTER_FIXED_TO_INT (v[i].x) - - clutter_actor_get_width (p[i])/2, - CLUTTER_FIXED_TO_INT (v[i].y) - - clutter_actor_get_height (p[i])/2); + clutter_actor_set_positionu (p[i], + v[i].x - + clutter_actor_get_widthu (p[i])/2, + v[i].y - + clutter_actor_get_heightu (p[i])/2); clutter_actor_raise_top (p[i]); clutter_actor_show (p[i]); } - v1.x = CLUTTER_INT_TO_FIXED (clutter_actor_get_width (rect)/2); - v1.y = CLUTTER_INT_TO_FIXED (clutter_actor_get_height (rect)/2); + v1.x = clutter_actor_get_widthu (rect) / 2; + v1.y = clutter_actor_get_heightu (rect) / 2; v1.z = 0; clutter_actor_apply_transform_to_point (rect, &v1, &v2); @@ -41,11 +41,11 @@ init_handles () clutter_actor_set_size (p[4], 5, 5); clutter_actor_set_position (p[4], 0, 0); clutter_group_add (CLUTTER_GROUP (main_stage), p[4]); - clutter_actor_set_position (p[4], - CLUTTER_FIXED_TO_INT (v2.x) - - clutter_actor_get_width (p[4])/2, - CLUTTER_FIXED_TO_INT (v2.y) - - clutter_actor_get_height (p[4])/2); + clutter_actor_set_positionu (p[4], + v2.x - + clutter_actor_get_widthu (p[4])/2, + v2.y - + clutter_actor_get_heightu (p[4])/2); clutter_actor_raise_top (p[4]); @@ -62,23 +62,21 @@ place_handles () clutter_actor_get_abs_allocation_vertices (rect, v); for (i = 0; i < 4; ++i) { - clutter_actor_set_position (p[i], - CLUTTER_FIXED_TO_INT (v[i].x) - - clutter_actor_get_width (p[i])/2, - CLUTTER_FIXED_TO_INT (v[i].y) - - clutter_actor_get_height (p[i])/2); + clutter_actor_set_positionu (p[i], + v[i].x - + clutter_actor_get_widthu (p[i])/2, + v[i].y - + clutter_actor_get_heightu (p[i])/2); } - v1.x = CLUTTER_INT_TO_FIXED (clutter_actor_get_width (rect)/2); - v1.y = CLUTTER_INT_TO_FIXED (clutter_actor_get_height (rect)/2); + v1.x = clutter_actor_get_widthu (rect)/2; + v1.y = clutter_actor_get_heightu (rect)/2; v1.z = 0; clutter_actor_apply_transform_to_point (rect, &v1, &v2); - clutter_actor_set_position (p[4], - CLUTTER_FIXED_TO_INT (v2.x) - - clutter_actor_get_width (p[4])/2, - CLUTTER_FIXED_TO_INT (v2.y) - - clutter_actor_get_height (p[4])/2); + clutter_actor_set_positionu (p[4], + v2.x - clutter_actor_get_widthu (p[4])/2, + v2.y - clutter_actor_get_heightu (p[4])/2); } #define M(m,row,col) (m)[col*4+row] @@ -127,7 +125,7 @@ on_event (ClutterStage *stage, gint x, y; gint i; ClutterActorBox box1, box2; - ClutterFixed xp, yp; + ClutterUnit xp, yp; i = find_handle_index (dragging); @@ -139,25 +137,24 @@ on_event (ClutterStage *stage, clutter_actor_get_allocation_box (dragging, &box1); clutter_actor_get_allocation_box (rect, &box2); - xp = CLUTTER_INT_TO_FIXED (x-3) - box1.x1; - yp = CLUTTER_INT_TO_FIXED (y-3) - box1.y1; + xp = CLUTTER_UNITS_FROM_DEVICE (x - 3) - box1.x1; + yp = CLUTTER_UNITS_FROM_DEVICE (y - 3) - box1.y1; if (i == 4) { g_debug ("moving box by %f, %f", - CLUTTER_FIXED_TO_FLOAT (xp), - CLUTTER_FIXED_TO_FLOAT (yp)); + CLUTTER_UNITS_TO_FLOAT (xp), + CLUTTER_UNITS_TO_FLOAT (yp)); - clutter_actor_move_by (rect, - CLUTTER_FIXED_TO_INT(xp), - CLUTTER_FIXED_TO_INT(yp)); + clutter_actor_move_byu (rect, xp, yp); } else { g_debug ("adjusting box by %f, %f, handle %d", - CLUTTER_FIXED_TO_FLOAT (xp), - CLUTTER_FIXED_TO_FLOAT (yp), + CLUTTER_UNITS_TO_FLOAT (xp), + CLUTTER_UNITS_TO_FLOAT (yp), i); + switch (i) { case 0: From efd7ad7e55357e3f656a9158c911521646898a2e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 8 Jan 2009 17:06:04 +0000 Subject: [PATCH 140/147] [text] Fix GObject properties in ClutterText Some of the read-write properties of ClutterText were missing an implementation in clutter_text_get_property(), as well as the :position and :selection-bound properties being wrongly converted from fixed point to integer, passing through floating point values. --- clutter/clutter-text.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4b7ae3f13..76721a6d2 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -688,11 +688,11 @@ clutter_text_get_property (GObject *gobject, break; case PROP_POSITION: - g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); + g_value_set_int (value, priv->position); break; case PROP_SELECTION_BOUND: - g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->selection_bound)); + g_value_set_int (value, priv->selection_bound); break; case PROP_EDITABLE: @@ -719,6 +719,30 @@ clutter_text_get_property (GObject *gobject, g_value_set_boolean (value, priv->single_line_mode); break; + case PROP_ELLIPSIZE: + g_value_set_enum (value, priv->ellipsize); + break; + + case PROP_LINE_WRAP: + g_value_set_boolean (value, priv->wrap); + break; + + case PROP_LINE_WRAP_MODE: + g_value_set_enum (value, priv->wrap_mode); + break; + + case PROP_ALIGNMENT: + g_value_set_enum (value, priv->alignment); + break; + + case PROP_JUSTIFY: + g_value_set_boolean (value, priv->justify); + break; + + case PROP_ATTRIBUTES: + g_value_set_boxed (value, priv->attrs); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } From 00a3c698686f25e193d0311ad25c903f0ad71e8b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 9 Jan 2009 12:06:46 +0000 Subject: [PATCH 141/147] [x11] Fix a race condition when resizing a stage There is a race condition when we resize a stage before showing it on X11. The race goes like this: - clutter_init() creates the default stage and realize it, which will cause a 640x480 Window to be created - call set_size(800, 600) on the stage will cause the Window to be resized to 800x600 - call show() on the stage for the first time will cause COGL to set up an 800 by 600 GL viewport - the Window will be mapped, which will cause X to notify the window manager that the Window should be resized to 800x600 - the window manager will approve the resize - X resizes the drawable to 800x600 To fix the race, we need to defer COGL from setting up the viewport until we receive a ConfigureNotify event and the X server has resized the Drawable. In order to defer the call to cogl_setup_viewport() we add a new private flag, CLUTTER_STAGE_IN_RESIZE; the flag is checked whenever we need to change the viewport size along with the SYNC_MATRICES private flag. Thus, cogl_setup_viewport() will be called only if SYNC_MATRICES is set and IN_RESIZE is not set. --- clutter/clutter-main.c | 3 ++- clutter/clutter-private.h | 3 ++- clutter/x11/clutter-event-x11.c | 6 ++++++ clutter/x11/clutter-stage-x11.c | 35 ++++++++++++++++++++++----------- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 745fcbbd7..edcb44cf0 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -147,7 +147,8 @@ _clutter_stage_maybe_relayout (ClutterActor *stage) void _clutter_stage_maybe_setup_viewport (ClutterStage *stage) { - if (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES) + if ((CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES) && + !(CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_STAGE_IN_RESIZE)) { ClutterPerspective perspective; guint width, height; diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index c65bfb066..b1f141bf7 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -62,7 +62,8 @@ typedef enum { */ CLUTTER_ACTOR_IN_PAINT = 1 << 4, /* Used to avoid recursion */ CLUTTER_ACTOR_IN_RELAYOUT = 1 << 5, /* Used to avoid recursion */ - CLUTTER_TEXTURE_IN_CLONE_PAINT = 1 << 6 /* Used for safety in clones */ + CLUTTER_TEXTURE_IN_CLONE_PAINT = 1 << 6, /* Used for safety in clones */ + CLUTTER_STAGE_IN_RESIZE = 1 << 7 /* Used to mark stage resizes */ } ClutterPrivateFlags; typedef enum { diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 06e27e766..090e2c28c 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -438,6 +438,12 @@ event_translate (ClutterBackend *backend, xevent->xconfigure.height); stage_x11->handling_configure = FALSE; + + /* the resize process is complete, so we can remove the + * in-resize flag and allow the viewport to be resized + */ + CLUTTER_UNSET_PRIVATE_FLAGS (CLUTTER_ACTOR (stage_x11->wrapper), + CLUTTER_STAGE_IN_RESIZE); } res = FALSE; break; diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index 010063951..bfc86ba9f 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -282,13 +282,30 @@ clutter_stage_x11_allocate (ClutterActor *self, queue. Handling the first event will undo the work of setting the second property which will cause it to keep generating events in an infinite loop. See bug #810 */ - if (stage_x11->xwin != None - && !stage_x11->is_foreign_xwin - && !stage_x11->handling_configure) - XResizeWindow (stage_x11->xdpy, - stage_x11->xwin, - stage_x11->xwin_width, - stage_x11->xwin_height); + if (stage_x11->xwin != None && + !stage_x11->is_foreign_xwin && + !stage_x11->handling_configure) + { + XResizeWindow (stage_x11->xdpy, + stage_x11->xwin, + stage_x11->xwin_width, + stage_x11->xwin_height); + + /* resizing is an asynchronous process; to avoid races + * with the window manager, we flag the wrapper as being + * "in resize", so that the SYNC_MATRICES flag will not + * cause a call to cogl_get_viewport(). + * + * the flag is unset inside clutter-event-x11.c, after + * we receive a ConfigureNotify event. XXX - need to + * check what happens when running without a window manager + */ + CLUTTER_SET_PRIVATE_FLAGS (CLUTTER_ACTOR (stage_x11->wrapper), + CLUTTER_STAGE_IN_RESIZE); + } + + CLUTTER_SET_PRIVATE_FLAGS (CLUTTER_ACTOR (stage_x11->wrapper), + CLUTTER_ACTOR_SYNC_MATRICES); clutter_stage_x11_fix_window_size (stage_x11); @@ -298,9 +315,6 @@ clutter_stage_x11_allocate (ClutterActor *self, clutter_actor_unrealize (self); clutter_actor_realize (self); } - - CLUTTER_SET_PRIVATE_FLAGS (CLUTTER_ACTOR (stage_x11->wrapper), - CLUTTER_ACTOR_SYNC_MATRICES); } /* chain up to fill in actor->priv->allocation */ @@ -769,4 +783,3 @@ clutter_stage_x11_unmap (ClutterStageX11 *stage_x11) CLUTTER_ACTOR_UNSET_FLAGS (stage_x11, CLUTTER_ACTOR_MAPPED); CLUTTER_ACTOR_UNSET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_MAPPED); } - From 2693ea3ddc4f7a70109a56c425280b6836ca9924 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 9 Jan 2009 14:26:35 +0000 Subject: [PATCH 142/147] [docs] Documentation warnings Fix the various warnings issued by gtk-doc when enabling the API reference generation for both COGL and Clutter. --- clutter/clutter-cairo-texture.h | 14 +++++ clutter/cogl/cogl-types.h | 72 ++++++++++++---------- clutter/cogl/common/cogl-mesh.c | 2 + doc/reference/clutter/clutter-docs.xml | 2 +- doc/reference/clutter/clutter-sections.txt | 55 ++++++++--------- 5 files changed, 83 insertions(+), 62 deletions(-) diff --git a/clutter/clutter-cairo-texture.h b/clutter/clutter-cairo-texture.h index a5cca866c..b7816fef6 100644 --- a/clutter/clutter-cairo-texture.h +++ b/clutter/clutter-cairo-texture.h @@ -49,6 +49,13 @@ typedef struct _ClutterCairoTexture ClutterCairoTexture; typedef struct _ClutterCairoTextureClass ClutterCairoTextureClass; typedef struct _ClutterCairoTexturePrivate ClutterCairoTexturePrivate; +/** + * ClutterCairoTexture: + * + * The #ClutterCairoTexture struct contains only private data. + * + * Since: 1.0 + */ struct _ClutterCairoTexture { /*< private >*/ @@ -57,6 +64,13 @@ struct _ClutterCairoTexture ClutterCairoTexturePrivate *priv; }; +/** + * ClutterCairoTextureClass: + * + * The #ClutterCairoTextureClass struct contains only private data. + * + * Since: 1.0 + */ struct _ClutterCairoTextureClass { /*< private >*/ diff --git a/clutter/cogl/cogl-types.h b/clutter/cogl/cogl-types.h index 4fb69953a..3ae0d9caa 100644 --- a/clutter/cogl/cogl-types.h +++ b/clutter/cogl/cogl-types.h @@ -86,27 +86,29 @@ typedef struct _CoglTextureVertex CoglTextureVertex; /** * CoglPixelFormat: - * @COGL_PIXEL_FORMAT_ANY: - * @COGL_PIXEL_FORMAT_A_8: - * @COGL_PIXEL_FORMAT_RGB_888: - * @COGL_PIXEL_FORMAT_BGR_888: - * @COGL_PIXEL_FORMAT_RGBA_8888: - * @COGL_PIXEL_FORMAT_BGRA_8888: - * @COGL_PIXEL_FORMAT_ARGB_8888: - * @COGL_PIXEL_FORMAT_ABGR_8888: - * @COGL_PIXEL_FORMAT_RGBA_8888_PRE: - * @COGL_PIXEL_FORMAT_BGRA_8888_PRE: - * @COGL_PIXEL_FORMAT_ARGB_8888_PRE: - * @COGL_PIXEL_FORMAT_ABGR_8888_PRE: - * @COGL_PIXEL_FORMAT_RGB_565: - * @COGL_PIXEL_FORMAT_RGBA_4444: - * @COGL_PIXEL_FORMAT_RGBA_5551: - * @COGL_PIXEL_FORMAT_RGBA_4444_PRE: - * @COGL_PIXEL_FORMAT_RGBA_5551_PRE: - * @COGL_PIXEL_FORMAT_YUV: - * @COGL_PIXEL_FORMAT_G_8: + * @COGL_PIXEL_FORMAT_ANY: Any format + * @COGL_PIXEL_FORMAT_A_8: 8 bits alpha mask + * @COGL_PIXEL_FORMAT_RGB_565: RGB, 16 bits + * @COGL_PIXEL_FORMAT_RGBA_4444: RGBA, 16 bits + * @COGL_PIXEL_FORMAT_RGBA_5551: RGBA, 16 bits + * @COGL_PIXEL_FORMAT_YUV: FIXME + * @COGL_PIXEL_FORMAT_G_8: FIXME + * @COGL_PIXEL_FORMAT_RGB_888: RGB, 24 bits + * @COGL_PIXEL_FORMAT_BGR_888: BGR, 24 bits + * @COGL_PIXEL_FORMAT_RGBA_8888: RGBA, 32 bits + * @COGL_PIXEL_FORMAT_BGRA_8888: BGRA, 32 bits + * @COGL_PIXEL_FORMAT_ARGB_8888: ARGB, 32 bits + * @COGL_PIXEL_FORMAT_ABGR_8888: ABGR, 32 bits + * @COGL_PIXEL_FORMAT_RGBA_8888_PRE: Premultiplied RGBA, 32 bits + * @COGL_PIXEL_FORMAT_BGRA_8888_PRE: Premultiplied BGRA, 32 bits + * @COGL_PIXEL_FORMAT_ARGB_8888_PRE: Premultiplied ARGB, 32 bits + * @COGL_PIXEL_FORMAT_ABGR_8888_PRE: Premultiplied ABGR, 32 bits + * @COGL_PIXEL_FORMAT_RGBA_4444_PRE: Premultiplied RGBA, 16 bits + * @COGL_PIXEL_FORMAT_RGBA_5551_PRE: Premultiplied RGBA, 16 bits * * Pixel formats used by COGL. + * + * Since: 0.8 */ typedef enum { @@ -173,19 +175,21 @@ typedef enum /** * CoglFeatureFlags: - * @COGL_FEATURE_TEXTURE_RECTANGLE: - * @COGL_FEATURE_TEXTURE_NPOT: - * @COGL_FEATURE_TEXTURE_YUV: - * @COGL_FEATURE_TEXTURE_READ_PIXELS: - * @COGL_FEATURE_SHADERS_GLSL: - * @COGL_FEATURE_OFFSCREEN: - * @COGL_FEATURE_OFFSCREEN_MULTISAMPLE: - * @COGL_FEATURE_OFFSCREEN_BLIT: - * @COGL_FEATURE_FOUR_CLIP_PLANES: - * @COGL_FEATURE_STENCIL_BUFFER: - * @COGL_FEATURE_VBOS: + * @COGL_FEATURE_TEXTURE_RECTANGLE: ARB_texture_rectangle support + * @COGL_FEATURE_TEXTURE_NPOT: ARB_texture_non_power_of_two support + * @COGL_FEATURE_TEXTURE_YUV: ycbcr conversion support + * @COGL_FEATURE_TEXTURE_READ_PIXELS: glReadPixels() support + * @COGL_FEATURE_SHADERS_GLSL: GLSL support + * @COGL_FEATURE_OFFSCREEN: FBO support + * @COGL_FEATURE_OFFSCREEN_MULTISAMPLE: Multisample support on FBOs + * @COGL_FEATURE_OFFSCREEN_BLIT: Blit support on FBOs + * @COGL_FEATURE_FOUR_CLIP_PLANES: At least 4 clip planes available + * @COGL_FEATURE_STENCIL_BUFFER: Stencil buffer support + * @COGL_FEATURE_VBOS: VBO support * * Flags for the supported features. + * + * Since: 0.8 */ typedef enum { @@ -204,11 +208,13 @@ typedef enum /** * CoglBufferTarget: - * @COGL_WINDOW_BUFFER: - * @COGL_MASK_BUFFER: - * @COGL_OFFSCREEN_BUFFER: + * @COGL_WINDOW_BUFFER: FIXME + * @COGL_MASK_BUFFER: FIXME + * @COGL_OFFSCREEN_BUFFER: FIXME * + * Target flags for FBOs. * + * Since: 0.8 */ typedef enum { diff --git a/clutter/cogl/common/cogl-mesh.c b/clutter/cogl/common/cogl-mesh.c index 88a0ba473..f7287f953 100644 --- a/clutter/cogl/common/cogl-mesh.c +++ b/clutter/cogl/common/cogl-mesh.c @@ -209,6 +209,8 @@ COGL_HANDLE_DEFINE (Mesh, mesh, mesh_handles); * * This creates a Cogl handle for a new mesh that you can then start to add * attributes too. + * + * Return value: a new #CoglHandle */ CoglHandle cogl_mesh_new (guint n_vertices) diff --git a/doc/reference/clutter/clutter-docs.xml b/doc/reference/clutter/clutter-docs.xml index e29317036..baeb0b5bf 100644 --- a/doc/reference/clutter/clutter-docs.xml +++ b/doc/reference/clutter/clutter-docs.xml @@ -57,8 +57,8 @@ - + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 7c9ed607b..3f0a8429f 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -28,6 +28,7 @@ clutter_media_get_type clutter-units Unit conversion ClutterUnit +CLUTTER_UNITS_FORMAT CLUTTER_UNITS_FROM_DEVICE CLUTTER_UNITS_TO_DEVICE CLUTTER_UNITS_FROM_FIXED @@ -1497,9 +1498,6 @@ clutter_shader_float_get_type clutter_shader_int_get_type clutter_shader_matrix_get_type clutter_shader_error_quark -clutter_shader_float_get_type -clutter_shader_int_get_type -clutter_shader_matrix_get_type
@@ -1582,31 +1580,6 @@ ClutterIntervalPrivate clutter_interval_get_type
-
-Key Bindings -clutter-binding-pool -ClutterBindingPool -ClutterBindingActionFunc - - -clutter_binding_pool_new -clutter_binding_pool_get_for_class -clutter_binding_pool_find - - -clutter_binding_pool_install_action -clutter_binding_pool_install_closure -clutter_binding_pool_override_action -clutter_binding_pool_override_closure -clutter_binding_pool_find_action -clutter_binding_pool_remove_action -clutter_binding_pool_block_action -clutter_binding_pool_unblock_action - - -clutter_binding_pool_activate -
-
clutter-cairo-texture ClutterCairoTexture @@ -1634,6 +1607,7 @@ CLUTTER_CAIRO_TEXTURE_GET_CLASS ClutterCairoTexturePrivate clutter_cairo_texture_get_type +
ClutterText @@ -1713,3 +1687,28 @@ CLUTTER_TYPE_TEXT ClutterTextPrivate clutter_text_get_type
+ +
+Key Bindings +clutter-binding-pool +ClutterBindingPool +ClutterBindingActionFunc + + +clutter_binding_pool_new +clutter_binding_pool_get_for_class +clutter_binding_pool_find + + +clutter_binding_pool_install_action +clutter_binding_pool_install_closure +clutter_binding_pool_override_action +clutter_binding_pool_override_closure +clutter_binding_pool_find_action +clutter_binding_pool_remove_action +clutter_binding_pool_block_action +clutter_binding_pool_unblock_action + + +clutter_binding_pool_activate +
From 4f6cc0b25f958ea720604db09b43896a985e1f50 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Jan 2009 11:15:41 +0000 Subject: [PATCH 143/147] [x11] Proper fix for the ClutterStage resize race Continuation of the fix in commit 00a3c698686f25e193d0311ad25c903f0ad71e8b. Instead of using a separate flag for the resize process, just delay the setting of the CLUTTER_ACTOR_SYNC_MATRICES flag on the stage to the point when we receive a ConfigureNotify event from X11. This commit will break the stage embedding into other toolkits. --- clutter/clutter-backend.c | 3 --- clutter/clutter-main.c | 3 +-- clutter/clutter-private.h | 3 +-- clutter/x11/clutter-event-x11.c | 14 ++++++++++---- clutter/x11/clutter-stage-x11.c | 27 ++++++++++----------------- 5 files changed, 22 insertions(+), 28 deletions(-) diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 4c60d40ff..efeeac185 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -251,9 +251,6 @@ _clutter_backend_ensure_context (ClutterBackend *backend, * potential issue of GL calls with no context) */ current_context_stage = stage; - - if (stage) - CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES); } else CLUTTER_NOTE (MULTISTAGE, "Stage is the same"); diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index edcb44cf0..745fcbbd7 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -147,8 +147,7 @@ _clutter_stage_maybe_relayout (ClutterActor *stage) void _clutter_stage_maybe_setup_viewport (ClutterStage *stage) { - if ((CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES) && - !(CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_STAGE_IN_RESIZE)) + if (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES) { ClutterPerspective perspective; guint width, height; diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index b1f141bf7..c65bfb066 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -62,8 +62,7 @@ typedef enum { */ CLUTTER_ACTOR_IN_PAINT = 1 << 4, /* Used to avoid recursion */ CLUTTER_ACTOR_IN_RELAYOUT = 1 << 5, /* Used to avoid recursion */ - CLUTTER_TEXTURE_IN_CLONE_PAINT = 1 << 6, /* Used for safety in clones */ - CLUTTER_STAGE_IN_RESIZE = 1 << 7 /* Used to mark stage resizes */ + CLUTTER_TEXTURE_IN_CLONE_PAINT = 1 << 6 /* Used for safety in clones */ } ClutterPrivateFlags; typedef enum { diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 090e2c28c..b83a0b2f3 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -433,17 +433,23 @@ event_translate (ClutterBackend *backend, it from trying to resize the window again */ stage_x11->handling_configure = TRUE; + CLUTTER_NOTE (BACKEND, "%s: ConfigureNotify[%x] (%d, %d)", + G_STRLOC, + (unsigned int) stage_x11->xwin, + xevent->xconfigure.width, + xevent->xconfigure.height); + clutter_actor_set_size (CLUTTER_ACTOR (stage), xevent->xconfigure.width, xevent->xconfigure.height); stage_x11->handling_configure = FALSE; - /* the resize process is complete, so we can remove the - * in-resize flag and allow the viewport to be resized + /* the resize process is complete, so we can ask the stage + * to set up the GL viewport with the new size */ - CLUTTER_UNSET_PRIVATE_FLAGS (CLUTTER_ACTOR (stage_x11->wrapper), - CLUTTER_STAGE_IN_RESIZE); + CLUTTER_SET_PRIVATE_FLAGS (CLUTTER_ACTOR (stage_x11->wrapper), + CLUTTER_ACTOR_SYNC_MATRICES); } res = FALSE; break; diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index bfc86ba9f..d01d8e2b1 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -286,27 +286,18 @@ clutter_stage_x11_allocate (ClutterActor *self, !stage_x11->is_foreign_xwin && !stage_x11->handling_configure) { + CLUTTER_NOTE (BACKEND, "%s: XResizeWindow[%x] (%d, %d)", + G_STRLOC, + (unsigned int) stage_x11->xwin, + stage_x11->xwin_width, + stage_x11->xwin_height); + XResizeWindow (stage_x11->xdpy, stage_x11->xwin, stage_x11->xwin_width, stage_x11->xwin_height); - - /* resizing is an asynchronous process; to avoid races - * with the window manager, we flag the wrapper as being - * "in resize", so that the SYNC_MATRICES flag will not - * cause a call to cogl_get_viewport(). - * - * the flag is unset inside clutter-event-x11.c, after - * we receive a ConfigureNotify event. XXX - need to - * check what happens when running without a window manager - */ - CLUTTER_SET_PRIVATE_FLAGS (CLUTTER_ACTOR (stage_x11->wrapper), - CLUTTER_STAGE_IN_RESIZE); } - CLUTTER_SET_PRIVATE_FLAGS (CLUTTER_ACTOR (stage_x11->wrapper), - CLUTTER_ACTOR_SYNC_MATRICES); - clutter_stage_x11_fix_window_size (stage_x11); if (stage_x11->xpixmap != None) @@ -411,6 +402,8 @@ clutter_stage_x11_set_fullscreen (ClutterStageWindow *stage_window, if (!stage) return; + CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES); + if (is_fullscreen) { int width, height; @@ -493,8 +486,6 @@ clutter_stage_x11_set_fullscreen (ClutterStageWindow *stage_window, stage_x11->fullscreen_on_map = FALSE; } } - - CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES); } static void @@ -754,6 +745,8 @@ clutter_x11_set_stage_foreign (ClutterStage *stage, clutter_actor_set_geometry (actor, &geom); clutter_actor_realize (actor); + CLUTTER_SET_PRIVATE_FLAGS (actor, CLUTTER_ACTOR_SYNC_MATRICES); + return TRUE; } From f911a3a7a13a778b44fedb4760ea55105d6bcd46 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Jan 2009 11:18:11 +0000 Subject: [PATCH 144/147] Allow ensuring that a stage viewport is updated Since we only update the GL viewport when we receive a ConfigureNotify event on X11, we also need a function to allow other toolkits to tell a stage that the viewport should be updated. This commit adds clutter_stage_ensure_viewport(), a function that simply sets the private SYNC_MATRICES flag on the stage and then queues a redraw. This function should be called by libraries integrating Clutter with other toolkits, like clutter-gtk or clutter-qt. --- clutter/clutter-stage.c | 25 +++++++++++++++++++++++++ clutter/clutter-stage.h | 1 + 2 files changed, 26 insertions(+) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index f6d42288f..3b312df2f 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -1801,6 +1801,31 @@ clutter_stage_ensure_current (ClutterStage *stage) _clutter_backend_ensure_context (ctx->backend, stage); } +/** + * clutter_stage_ensure_viewport: + * @stage: a #ClutterStage + * + * Ensures that the GL viewport is updated with the current + * stage window size. + * + * This function will queue a redraw of @stage. + * + * This function should not be called by applications; it is used + * when embedding a #ClutterStage into a toolkit with another + * windowing system, like GTK+. + * + * Since: 1.0 + */ +void +clutter_stage_ensure_viewport (ClutterStage *stage) +{ + g_return_if_fail (CLUTTER_IS_STAGE (stage)); + + CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES); + + clutter_stage_queue_redraw (stage); +} + static gboolean redraw_update_idle (gpointer user_data) { diff --git a/clutter/clutter-stage.h b/clutter/clutter-stage.h index cb8ca9063..7b174ee30 100644 --- a/clutter/clutter-stage.h +++ b/clutter/clutter-stage.h @@ -229,6 +229,7 @@ ClutterActor * clutter_stage_get_key_focus (ClutterStage *stage); void clutter_stage_ensure_current (ClutterStage *stage); void clutter_stage_queue_redraw (ClutterStage *stage); gboolean clutter_stage_is_default (ClutterStage *stage); +void clutter_stage_ensure_viewport (ClutterStage *stage); /* Commodity macro */ #define clutter_stage_add(stage,actor) G_STMT_START { \ From ed991fe3c5558bb2bb9058ec0bdeef2071236056 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Jan 2009 11:21:06 +0000 Subject: [PATCH 145/147] Declare G_LOG_DOMAIN for COGL In order to get properly namespaced debug and warning messages inside COGL code we need to define the G_LOG_DOMAIN macro. --- clutter/cogl/common/Makefile.am | 1 + clutter/cogl/gl/Makefile.am | 1 + clutter/cogl/gles/Makefile.am | 1 + 3 files changed, 3 insertions(+) diff --git a/clutter/cogl/common/Makefile.am b/clutter/cogl/common/Makefile.am index db0f0b42d..fb2adf28f 100644 --- a/clutter/cogl/common/Makefile.am +++ b/clutter/cogl/common/Makefile.am @@ -5,6 +5,7 @@ INCLUDES = \ -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \ -I$(top_builddir)/clutter \ -I$(top_builddir)/clutter/cogl \ + -DG_LOG_DOMAIN=\"Cogl-Common\" \ -DCLUTTER_COMPILATION \ $(CLUTTER_CFLAGS) \ $(CLUTTER_DEBUG_CFLAGS) \ diff --git a/clutter/cogl/gl/Makefile.am b/clutter/cogl/gl/Makefile.am index 2d90ba9c0..7635b1b90 100644 --- a/clutter/cogl/gl/Makefile.am +++ b/clutter/cogl/gl/Makefile.am @@ -20,6 +20,7 @@ INCLUDES = \ -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \ -I$(top_builddir)/clutter \ -I$(top_builddir)/clutter/cogl \ + -DG_LOG_DOMAIN=\"Cogl-GL\" \ -DCLUTTER_COMPILATION \ $(CLUTTER_CFLAGS) \ $(CLUTTER_DEBUG_CFLAGS) \ diff --git a/clutter/cogl/gles/Makefile.am b/clutter/cogl/gles/Makefile.am index 19cf0a0d7..1731d2cb5 100644 --- a/clutter/cogl/gles/Makefile.am +++ b/clutter/cogl/gles/Makefile.am @@ -20,6 +20,7 @@ INCLUDES = \ -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \ -I$(top_builddir)/clutter \ -I$(top_builddir)/clutter/cogl \ + -DG_LOG_DOMAIN=\"Cogl-GLES\" \ -DCLUTTER_COMPILATION \ $(CLUTTER_CFLAGS) \ $(CLUTTER_DEBUG_CFLAGS) \ From f09b221ade120c16a4696f3160509ea98adfada3 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 12 Jan 2009 13:02:19 +0000 Subject: [PATCH 146/147] [ClutterGLXTexturePixmap] Use an RGB texture (not ARGB) for 24bpp pixmaps By creating an ARGB texture for 24bpp pixmaps we were exposing an undefined alpha channel to the blending and texture combine stages which resulted in nasty artefacts. (This issue was seen on i945 + DRI2) --- clutter/glx/clutter-glx-texture-pixmap.c | 30 ++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/clutter/glx/clutter-glx-texture-pixmap.c b/clutter/glx/clutter-glx-texture-pixmap.c index 280f34216..0dfe23941 100644 --- a/clutter/glx/clutter-glx-texture-pixmap.c +++ b/clutter/glx/clutter-glx-texture-pixmap.c @@ -317,8 +317,25 @@ create_cogl_texture (ClutterTexture *texture, { ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture); ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv; - CoglHandle handle; - gboolean using_rectangle; + CoglHandle handle; + gboolean using_rectangle; + GLint gl_format; + CoglPixelFormat cogl_format; + guint depth; + + g_object_get (G_OBJECT (texture_glx), "pixmap-depth", &depth, NULL); + if (depth == 32) + { + gl_format = GL_RGBA; + cogl_format = COGL_PIXEL_FORMAT_RGBA_8888; + } + else if (depth == 24) + { + gl_format = GL_RGB; + cogl_format = COGL_PIXEL_FORMAT_RGB_888; + } + else + g_critical ("Can't create a TFP cogl texture for pixmap with depth < 24"); /* We want to use the GL_ARB_texture_rectangle extension on some chipsets because GL_ARB_texture_non_power_of_two is not always @@ -332,21 +349,20 @@ create_cogl_texture (ClutterTexture *texture, glGenTextures (1, &tex); glBindTexture (CGL_TEXTURE_RECTANGLE_ARB, tex); glTexImage2D (CGL_TEXTURE_RECTANGLE_ARB, 0, - GL_RGBA, width, height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + GL_RGB, width, height, + 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); handle = cogl_texture_new_from_foreign (tex, CGL_TEXTURE_RECTANGLE_ARB, width, height, 0, 0, - COGL_PIXEL_FORMAT_RGBA_8888 - | COGL_BGR_BIT); + cogl_format | COGL_BGR_BIT); } else { handle = cogl_texture_new_with_size (width, height, -1, FALSE, - COGL_PIXEL_FORMAT_RGBA_8888|COGL_BGR_BIT); + cogl_format | COGL_BGR_BIT); using_rectangle = FALSE; } From be462b2ea8d2ed908520a498908a83c5ba37a3b7 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Mon, 12 Jan 2009 14:19:48 +0000 Subject: [PATCH 147/147] Bug 1087 - virtualize stage_queue_redraw Add a ClutterStage::queue-redraw signal. The purpose of this signal is to allow combining the Clutter redraw idle with another redraw idle such as gtk's (or any other one really; this is desirable anytime Clutter is not the only thing drawing to a toplevel window). To override the default, you would connect to ::queue-redraw and then stop the signal emission. --- clutter/clutter-stage.c | 118 ++++++++++++++++++++++++++++++---------- clutter/clutter-stage.h | 2 + 2 files changed, 91 insertions(+), 29 deletions(-) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 3b312df2f..6a4e850b8 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -115,6 +115,8 @@ enum UNFULLSCREEN, ACTIVATE, DEACTIVATE, + QUEUE_REDRAW, + LAST_SIGNAL }; @@ -335,6 +337,42 @@ clutter_stage_real_fullscreen (ClutterStage *stage) clutter_actor_allocate (CLUTTER_ACTOR (stage), &box, FALSE); } +static gboolean +redraw_update_idle (gpointer user_data) +{ + ClutterStage *stage = user_data; + ClutterStagePrivate *priv = stage->priv; + + if (priv->update_idle) + { + g_source_remove (priv->update_idle); + priv->update_idle = 0; + } + + CLUTTER_NOTE (MULTISTAGE, "redrawing via idle for stage:%p", stage); + clutter_redraw (stage); + + return FALSE; +} + +static void +clutter_stage_real_queue_redraw (ClutterStage *stage) +{ + ClutterStagePrivate *priv = stage->priv; + + if (priv->update_idle == 0) + { + CLUTTER_TIMESTAMP (SCHEDULER, "Adding idle source for stage: %p", stage); + + /* FIXME: weak_ref self in case we dissapear before paint? */ + priv->update_idle = + clutter_threads_add_idle_full (CLUTTER_PRIORITY_REDRAW, + redraw_update_idle, + stage, + NULL); + } +} + static void clutter_stage_set_property (GObject *object, guint prop_id, @@ -687,8 +725,58 @@ clutter_stage_class_init (ClutterStageClass *klass) NULL, NULL, clutter_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * ClutterStage::queue-redraw: + * @stage: the stage which was queued for redraw + * + * The ::queue-redraw signal is emitted each time a #ClutterStage + * has been queued for a redraw. You can use this signal to know + * when clutter_stage_queue_redraw() has been called. + * + * Toolkits embedding a #ClutterStage which require a redraw and + * relayout cycle can stop the emission of this signal using the + * GSignal API, redraw the UI and then call clutter_redraw() + * themselves, like: + * + * |[ + * static void + * on_redraw_complete (void) + * { + * /* execute the Clutter drawing pipeline */ + * clutter_redraw (); + * } + * + * static void + * on_stage_queue_redraw (ClutterStage *stage) + * { + * /* this prevents the default handler to run */ + * g_signal_stop_emission_by_name (stage, "queue-redraw"); + * + * /* queue a redraw with the host toolkit and call + * * a function when the redraw has been completed + * */ + * queue_a_redraw (G_CALLBACK (on_redraw_complete)); + * } + * ]| + * + * This signal is emitted before the Clutter paint + * pipeline is run. If you want to know when the pipeline has been + * completed you should connect to the ::paint signal on the Stage + * with g_signal_connect_after(). + * + * Since: 1.0 + */ + stage_signals[QUEUE_REDRAW] = + g_signal_new (I_("queue-redraw"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ClutterStageClass, queue_redraw), + NULL, NULL, + clutter_marshal_VOID__VOID, + G_TYPE_NONE, 0); klass->fullscreen = clutter_stage_real_fullscreen; + klass->queue_redraw = clutter_stage_real_queue_redraw; g_type_class_add_private (gobject_class, sizeof (ClutterStagePrivate)); } @@ -1826,24 +1914,6 @@ clutter_stage_ensure_viewport (ClutterStage *stage) clutter_stage_queue_redraw (stage); } -static gboolean -redraw_update_idle (gpointer user_data) -{ - ClutterStage *stage = user_data; - ClutterStagePrivate *priv = stage->priv; - - if (priv->update_idle) - { - g_source_remove (priv->update_idle); - priv->update_idle = 0; - } - - CLUTTER_NOTE (MULTISTAGE, "redrawing via idle for stage:%p", stage); - clutter_redraw (stage); - - return FALSE; -} - /** * clutter_stage_queue_redraw: * @stage: the #ClutterStage @@ -1860,17 +1930,7 @@ clutter_stage_queue_redraw (ClutterStage *stage) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); - if (!stage->priv->update_idle) - { - CLUTTER_TIMESTAMP (SCHEDULER, "Adding idle source for stage: %p", stage); - - /* FIXME: weak_ref self in case we dissapear before paint? */ - stage->priv->update_idle = - clutter_threads_add_idle_full (CLUTTER_PRIORITY_REDRAW, - redraw_update_idle, - stage, - NULL); - } + g_signal_emit (stage, stage_signals[QUEUE_REDRAW], 0); } /** diff --git a/clutter/clutter-stage.h b/clutter/clutter-stage.h index 7b174ee30..9cbbdeb92 100644 --- a/clutter/clutter-stage.h +++ b/clutter/clutter-stage.h @@ -106,6 +106,8 @@ struct _ClutterStageClass void (* activate) (ClutterStage *stage); void (* deactivate) (ClutterStage *stage); + void (* queue_redraw) (ClutterStage *stage); + /*< private >*/ /* padding for future expansion */ gpointer _padding_dummy[32];