diff --git a/.gitignore b/.gitignore index 0ff441116..c3d05ec40 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 @@ -158,6 +159,7 @@ stamp-h1 /clutter/x11/stamp-clutter-x11-enum-types.h /po/Makefile.in.in /po/POTFILES +/po/*.pot *.swn *.swo *.swp diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 4db7c9fe2..faa9480cc 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -64,7 +64,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 \ @@ -72,7 +71,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 \ @@ -87,6 +85,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 \ @@ -155,7 +154,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 \ @@ -164,7 +162,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 \ @@ -182,6 +179,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-actor.c b/clutter/clutter-actor.c index d804e41d2..2f39c4f0c 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 @@ -772,6 +774,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 +799,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 +917,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 +958,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 +1012,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; + 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)); 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); + clutter_actor_transform_point_relative (self, ancestor, &x, &y, &z, &w); cogl_get_viewport (v); @@ -918,9 +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 = 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]); + 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); } /** @@ -940,30 +1060,41 @@ clutter_actor_apply_transform_to_point (ClutterActor *self, const ClutterVertex *point, ClutterVertex *vertex) { - ClutterVertex tmp = { 0, }; + ClutterUnit x, y, z, w; 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; + 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, &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); /* 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 +1102,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 +1137,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 +1166,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 +1199,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 +1215,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 +1253,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]); } /** @@ -1941,6 +1965,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); @@ -3220,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)); @@ -3290,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)); @@ -6842,17 +6874,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); @@ -6865,11 +6898,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); @@ -6886,9 +6920,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 @@ -6900,20 +6936,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. @@ -6923,13 +6960,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; @@ -6937,16 +6973,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); } /* @@ -6985,24 +7025,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 @@ -7197,11 +7240,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; @@ -7215,15 +7258,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); @@ -7570,3 +7607,99 @@ 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); +} + +/** + * 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. + * + * 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 + * 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) +{ + 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_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 e964a9ef0..af962caf1 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -31,6 +31,8 @@ /* clutter-actor.h */ #include +#include + #include #include #include @@ -420,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); @@ -545,21 +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); + +PangoContext *clutter_actor_get_pango_context (ClutterActor *self); +PangoContext *clutter_actor_create_pango_context (ClutterActor *self); G_END_DECLS diff --git a/clutter/clutter-alpha.c b/clutter/clutter-alpha.c index 2114fe38e..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, @@ -565,13 +568,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"); } diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index 83845057b..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); @@ -780,12 +782,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 +811,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"); } @@ -968,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) @@ -1048,6 +1052,19 @@ clutter_animation_set_alpha (ClutterAnimation *animation, priv = animation->priv; + if (!alpha) + { + ClutterTimeline *timeline; + + timeline = clutter_animation_get_timeline (animation); + + alpha = clutter_alpha_new (); + clutter_alpha_set_timeline (alpha, timeline); + clutter_animation_set_mode_internal (animation, alpha); + } + + g_object_ref_sink (alpha); + if (priv->alpha) { if (priv->alpha_notify_id) @@ -1058,18 +1075,7 @@ clutter_animation_set_alpha (ClutterAnimation *animation, priv->alpha = NULL; } - if (!alpha) - { - ClutterTimeline *timeline; - - timeline = clutter_animation_get_timeline (animation); - - alpha = clutter_alpha_new (); - clutter_alpha_set_timeline (alpha, timeline); - clutter_animation_set_mode_internal (animation); - } - - priv->alpha = g_object_ref_sink (alpha); + priv->alpha = alpha; priv->alpha_notify_id = g_signal_connect (alpha, "notify::alpha", diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 9a599c16b..efeeac185 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -44,10 +44,13 @@ #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); +#define DEFAULT_FONT_NAME "Sans 10" + #define CLUTTER_BACKEND_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BACKEND, ClutterBackendPrivate)) @@ -60,11 +63,24 @@ struct _ClutterBackendPrivate ClutterFixed resolution; cairo_font_options_t *font_options; + + gchar *font_name; }; +enum +{ + RESOLUTION_CHANGED, + FONT_CHANGED, + + LAST_SIGNAL +}; + +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 (); @@ -76,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); @@ -89,6 +107,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 @@ -215,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"); @@ -388,8 +421,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 +479,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); } } @@ -482,3 +518,63 @@ 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) +{ + 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); +} + +/** + * 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) +{ + ClutterBackendPrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), NULL); + + 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; +} diff --git a/clutter/clutter-backend.h b/clutter/clutter-backend.h index 27f49c46f..fda1d3413 100644 --- a/clutter/clutter-backend.h +++ b/clutter/clutter-backend.h @@ -30,6 +30,8 @@ #include #include +#include + #include #include #include @@ -77,6 +79,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; @@ -95,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 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-binding-pool.c b/clutter/clutter-binding-pool.c index 8b567b28d..78a483f72 100644 --- a/clutter/clutter-binding-pool.c +++ b/clutter/clutter-binding-pool.c @@ -476,10 +476,129 @@ clutter_binding_pool_install_closure (ClutterBindingPool *pool, g_hash_table_insert (pool->entries_hash, entry, entry); } -gchar ** -clutter_binding_pool_list_actions (ClutterBindingPool *pool) +/** + * 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) { - return NULL; + 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_closure: + * @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); + } } /** @@ -531,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); @@ -540,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); } diff --git a/clutter/clutter-binding-pool.h b/clutter/clutter-binding-pool.h index 47692403e..55b5273c8 100644 --- a/clutter/clutter-binding-pool.h +++ b/clutter/clutter-binding-pool.h @@ -56,40 +56,49 @@ 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); +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 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/clutter-clone-texture.c b/clutter/clutter-clone-texture.c index c84b048af..0810a90fe 100644 --- a/clutter/clutter-clone-texture.c +++ b/clutter/clutter-clone-texture.c @@ -248,6 +248,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); 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__ */ diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 9edbce8c6..745fcbbd7 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,23 +403,100 @@ _clutter_do_pick (ClutterStage *stage, return clutter_get_actor_by_gid (id); } +static PangoDirection +clutter_get_text_direction (void) +{ + 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 if (strcmp (direction, "ltr") == 0) + dir = PANGO_DIRECTION_LTR; + } + else + { + /* 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; +} + +static void +update_pango_context (ClutterBackend *backend, + PangoContext *context) +{ + PangoFontDescription *font_desc; + cairo_font_options_t *font_options; + 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); + + font_desc = pango_font_description_from_string (font_name); + + if (resolution < 0) + resolution = 96.0; /* fall back */ + + pango_context_set_font_description (context, font_desc); + pango_cairo_context_set_font_options (context, font_options); + pango_cairo_context_set_resolution (context, resolution); + + pango_font_description_free (font_desc); +} + +PangoContext * +_clutter_context_get_pango_context (ClutterMainContext *self) +{ + if (G_UNLIKELY (self->pango_context == NULL)) + { + 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; - gdouble resolution; - cairo_font_options_t *font_options; - - 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); + update_pango_context (self->backend, context); return context; } @@ -1004,6 +1083,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 @@ -1077,6 +1167,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) @@ -1142,16 +1234,19 @@ 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 }, + { "clutter-text-direction", 0, 0, G_OPTION_ARG_CALLBACK, + clutter_arg_direction_cb, + N_("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" }, + 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 +1389,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 +2214,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/clutter-private.h b/clutter/clutter-private.h index 81e030adf..c65bfb066 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 @@ -125,15 +126,17 @@ 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 ()) 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)) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 7e5abe76f..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)); } @@ -1201,7 +1289,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 (); @@ -1801,22 +1889,29 @@ clutter_stage_ensure_current (ClutterStage *stage) _clutter_backend_ensure_context (ctx->backend, stage); } -static gboolean -redraw_update_idle (gpointer user_data) +/** + * 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) { - ClutterStage *stage = user_data; - ClutterStagePrivate *priv = stage->priv; + g_return_if_fail (CLUTTER_IS_STAGE (stage)); - if (priv->update_idle) - { - g_source_remove (priv->update_idle); - priv->update_idle = 0; - } + CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES); - CLUTTER_NOTE (MULTISTAGE, "redrawing via idle for stage:%p", stage); - clutter_redraw (stage); - - return FALSE; + clutter_stage_queue_redraw (stage); } /** @@ -1835,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 cb8ca9063..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]; @@ -229,6 +231,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 { \ diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c new file mode 100644 index 000000000..76721a6d2 --- /dev/null +++ b/clutter/clutter-text.c @@ -0,0 +1,3781 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2008 Intel Corporation. + * + * 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 + * 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 . + */ + +/** + * 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? */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#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-private.h" /* includes pango/cogl-pango.h */ +#include "clutter-units.h" + +/* 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 + * 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; + +static const ClutterColor default_cursor_color = { 0, 0, 0, 255 }; +static const ClutterColor default_text_color = { 0, 0, 0, 255 }; + +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; + guint selectable : 1; + guint in_select_drag : 1; + guint cursor_color_set : 1; + + /* current cursor position */ + gint position; + + /* 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; + + /* 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 */ + gint n_bytes; + + /* the length of the text, in characters */ + gint n_chars; + + /* Where to draw the cursor */ + ClutterGeometry cursor_pos; + ClutterColor cursor_color; + guint cursor_size; + + gint max_length; + + gunichar password_char; +}; + +enum +{ + PROP_0, + + PROP_FONT_NAME, + PROP_TEXT, + PROP_COLOR, + PROP_USE_MARKUP, + PROP_ATTRIBUTES, + PROP_ALIGNMENT, + PROP_LINE_WRAP, + PROP_LINE_WRAP_MODE, + PROP_JUSTIFY, + PROP_ELLIPSIZE, + PROP_POSITION, + PROP_SELECTION_BOUND, + PROP_CURSOR_VISIBLE, + PROP_CURSOR_COLOR, + PROP_CURSOR_COLOR_SET, + PROP_CURSOR_SIZE, + PROP_EDITABLE, + PROP_SELECTABLE, + PROP_ACTIVATABLE, + PROP_PASSWORD_CHAR, + PROP_MAX_LENGTH, + PROP_SINGLE_LINE_MODE +}; + +enum +{ + TEXT_CHANGED, + CURSOR_EVENT, + ACTIVATE, + + LAST_SIGNAL +}; + +static guint text_signals[LAST_SIGNAL] = { 0, }; + +#define offset_real(t,p) ((p) == -1 ? g_utf8_strlen ((t), -1) : (p)) + +static gint +offset_to_bytes (const gchar *text, + gint pos) +{ + gchar *c = NULL; + gint i, j, len; + + if (pos < 0) + return strlen (text); + + 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))) + + +static inline void +clutter_text_clear_selection (ClutterText *self) +{ + ClutterTextPrivate *priv = self->priv; + + priv->selection_bound = priv->position; +} + +static PangoLayout * +clutter_text_create_layout_no_cache (ClutterText *text, + ClutterUnit allocation_width) +{ + ClutterTextPrivate *priv = text->priv; + PangoContext *context; + PangoLayout *layout; + + context = clutter_actor_get_pango_context (CLUTTER_ACTOR (text)); + 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 && !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); + else + { + GString *str = g_string_sized_new (priv->n_bytes); + gunichar invisible_char; + gchar buf[7]; + gint char_len, i; + + 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 + * 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); + } + } + } + + 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 index_; + gint px, py; + gint trailing; + + px = x * PANGO_SCALE; + py = y * PANGO_SCALE; + + pango_layout_xy_to_index (clutter_text_get_layout (text), + 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, + ClutterUnit *x, + ClutterUnit *y, + ClutterUnit *line_height) +{ + ClutterTextPrivate *priv = self->priv; + PangoRectangle rect; + gint password_char_bytes = 1; + gint index_; + + if (priv->password_char != 0) + password_char_bytes = g_unichar_to_utf8 (priv->password_char, NULL); + + if (position == -1) + { + if (priv->password_char == 0) + index_ = priv->n_bytes; + else + index_ = priv->n_chars * password_char_bytes; + } + else if (position == 0) + { + index_ = 0; + } + else + { + if (priv->password_char == 0) + index_ = offset_to_bytes (priv->text, position); + else + index_ = priv->position * password_char_bytes; + } + + pango_layout_get_cursor_pos (clutter_text_get_layout (self), index_, + &rect, NULL); + + if (x) + *x = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.x); + + if (y) + *y = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.y); + + if (line_height) + *line_height = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.height); + + /* 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; + 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 = 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) - 2; + + g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); +} + +static gboolean +clutter_text_truncate_selection (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); + + 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 (self, start_index, end_index); + + priv->position = start_index; + priv->selection_bound = start_index; + + return TRUE; +} + +static void +clutter_text_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterText *self = CLUTTER_TEXT (gobject); + + switch (prop_id) + { + case PROP_TEXT: + clutter_text_set_text (self, g_value_get_string (value)); + break; + + case PROP_COLOR: + clutter_text_set_color (self, clutter_value_get_color (value)); + break; + + case PROP_FONT_NAME: + 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 (self, g_value_get_int (value)); + break; + + case PROP_SELECTION_BOUND: + clutter_text_set_selection_bound (self, g_value_get_int (value)); + break; + + case PROP_CURSOR_VISIBLE: + clutter_text_set_cursor_visible (self, g_value_get_boolean (value)); + break; + + case PROP_CURSOR_COLOR: + 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; + + case PROP_ACTIVATABLE: + clutter_text_set_activatable (self, g_value_get_boolean (value)); + break; + + case PROP_SELECTABLE: + clutter_text_set_selectable (self, g_value_get_boolean (value)); + break; + + case PROP_PASSWORD_CHAR: + clutter_text_set_password_char (self, g_value_get_uint (value)); + break; + + case PROP_MAX_LENGTH: + 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); + } +} + +static void +clutter_text_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + 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_CURSOR_COLOR_SET: + 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, priv->position); + break; + + case PROP_SELECTION_BOUND: + g_value_set_int (value, 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; + + case PROP_PASSWORD_CHAR: + g_value_set_uint (value, priv->password_char); + break; + + case PROP_MAX_LENGTH: + g_value_set_int (value, priv->max_length); + break; + + case PROP_SINGLE_LINE_MODE: + 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); + } +} + +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); + + G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject); +} + +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, + 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, + real_opacity); + } + + if (priv->position == 0) + priv->cursor_pos.x -= priv->cursor_size; + + 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 + { + PangoLayout *layout = clutter_text_get_layout (self); + const gchar *utf8 = priv->text; + gint lines; + gint start_index; + gint end_index; + gint line_no; + + if (priv->position == 0) + start_index = 0; + else + start_index = offset_to_bytes (utf8, priv->position); + + if (priv->selection_bound == 0) + end_index = 0; + else + end_index = offset_to_bytes (utf8, priv->selection_bound); + + if (start_index > end_index) + { + gint temp = start_index; + start_index = end_index; + end_index = temp; + } + + lines = pango_layout_get_line_count (layout); + + for (line_no = 0; line_no < lines; line_no++) + { + PangoLayoutLine *line; + gint n_ranges; + gint *ranges; + gint i; + gint index_; + gint maxindex; + ClutterUnit y, height; + + line = pango_layout_get_line_readonly (layout, line_no); + pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL); + if (maxindex < start_index) + continue; + + pango_layout_line_get_x_ranges (line, start_index, end_index, + &ranges, + &n_ranges); + pango_layout_line_x_to_index (line, 0, &index_, NULL); + + clutter_text_position_to_coords (self, + bytes_to_offset (utf8, index_), + NULL, &y, &height); + + for (i = 0; i < n_ranges; i++) + { + gint range_x; + gint range_width; + + range_x = ranges[i * 2] + / PANGO_SCALE; + 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); + } + } + } +} + +static gboolean +clutter_text_button_press (ClutterActor *actor, + ClutterButtonEvent *event) +{ + ClutterText *self = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = self->priv; + ClutterUnit x, y; + gint index_; + + x = CLUTTER_UNITS_FROM_INT (event->x); + y = CLUTTER_UNITS_FROM_INT (event->y); + + clutter_actor_transform_stage_point (actor, x, y, &x, &y); + + index_ = clutter_text_coords_to_position (self, + CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); + + 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); + + 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_text_get_text (ttext); + + 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_button_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 gboolean +clutter_text_key_press (ClutterActor *actor, + ClutterKeyEvent *event) +{ + ClutterText *self = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = self->priv; + ClutterBindingPool *pool; + gboolean res; + gint keyval; + + if (!priv->editable) + return FALSE; + + keyval = clutter_key_event_symbol (event); + + pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor)); + g_assert (pool != NULL); + + /* 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); + + /* 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); + + return TRUE; + } + } + + return FALSE; +} + +#define TEXT_PADDING 2 + +static void +clutter_text_paint (ClutterActor *self) +{ + ClutterText *text = CLUTTER_TEXT (self); + ClutterTextPrivate *priv = text->priv; + PangoLayout *layout; + ClutterActorBox alloc = { 0, }; + CoglColor color = { 0, }; + guint8 real_opacity; + gint text_x = priv->text_x; + gboolean clip_set = FALSE; + + if (G_UNLIKELY (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_actor_get_allocation_box (self, &alloc); + layout = clutter_text_create_layout (text, alloc.x2 - alloc.x1); + + 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; + + 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, text_x, 0, &color, 0); + + if (clip_set) + cogl_clip_unset (); + + priv->text_x = text_x; +} + +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; + gint logical_width; + ClutterUnit layout_width; + + layout = clutter_text_create_layout (text, -1); + + pango_layout_get_extents (layout, NULL, &logical_rect); + + /* 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) + { + 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, }; + gint logical_height; + ClutterUnit layout_height; + + layout = clutter_text_create_layout (text, for_width); + + pango_layout_get_extents (layout, NULL, &logical_rect); + + /* 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 = layout_height; + + if (natural_height_p) + *natural_height_p = layout_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 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 = priv->n_chars; + + 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 = priv->n_chars; + + 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); + + 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; + + 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); + + 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); + + 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); + + 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); + + 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); + + 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); + + 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_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, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (self)) + return TRUE; + + pos = priv->position; + len = priv->n_chars; + + if (len && pos != -1 && pos < len) + clutter_text_delete_text (self, pos, pos + 1); + + return TRUE; +} + +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 TRUE; + + pos = priv->position; + len = priv->n_chars; + + if (pos != 0 && len != 0) + { + if (pos == -1) + { + 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); + } + } + + return TRUE; +} + +static gboolean +clutter_text_real_activate (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + return clutter_text_activate (self); +} + +static inline 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; + + 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; + 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_button_press; + actor_class->button_release_event = clutter_text_button_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: 1.0 + */ + 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); + + /** + * ClutterText:text: + * + * The text to render inside the actor. + * + * Since: 1.0 + */ + pspec = g_param_spec_string ("text", + "Text", + "The text to render", + "", + 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", + &default_text_color, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_COLOR, pspec); + + /** + * ClutterText:editable: + * + * Whether key events delivered to the actor causes editing. + * + * Since: 1.0 + */ + 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, either using the pointer + * or the keyboard. + * + * Since: 1.0 + */ + 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. + * + * Since: 1.0 + */ + 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: + * + * Whether the input cursor is visible or not, it will only be visible + * if both #ClutterText:cursor-visible and #ClutterText:editable are + * set to %TRUE. + * + * Since: 1.0 + */ + 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); + + /** + * ClutterText:cursor-color: + * + * The color of the cursor. + * + * Since: 1.0 + */ + pspec = clutter_param_spec_color ("cursor-color", + "Cursor Color", + "Cursor Color", + &default_cursor_color, + 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", + FALSE, + 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: + * + * 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", + "The cursor position", + -1, G_MAXINT, + -1, + CLUTTER_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 + * + * Since: 1.0 + */ + pspec = g_param_spec_int ("selection-bound", + "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); + + /** + * 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 " + "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); + + /** + * 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", + 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:password-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_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_PASSWORD_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", + -1, G_MAXINT, 0, + 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 + * + * The ::text-changed signal is emitted after @actor's text changes + * + * Since: 1.0 + */ + text_signals[TEXT_CHANGED] = + g_signal_new ("text-changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterTextClass, text_changed), + NULL, NULL, + 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 + * + * 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 + */ + text_signals[CURSOR_EVENT] = + g_signal_new ("cursor-event", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterTextClass, cursor_event), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE); + + /** + * ClutterText::activate + * @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. The signal is + * emitted only if #ClutterText:activatable is set to %TRUE. + * + * Since: 1.0 + */ + text_signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterTextClass, activate), + 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_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, "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), + 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 +clutter_text_init (ClutterText *self) +{ + ClutterTextPrivate *priv; + const gchar *font_name; + 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; + + /* 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; + + /* 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; + + priv->x_pos = -1; + priv->cursor_visible = TRUE; + priv->editable = FALSE; + priv->selectable = TRUE; + + priv->cursor_color_set = FALSE; + + priv->password_char = 0; + + priv->max_length = 0; + + 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, + const ClutterColor *color) +{ + return g_object_new (CLUTTER_TYPE_TEXT, + "font-name", font_name, + "text", text, + "color", color, + 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) +{ + return g_object_new (CLUTTER_TYPE_TEXT, + "font-name", font_name, + "text", text, + 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) +{ + ClutterTextPrivate *priv; + + 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"); + } +} + +/** + * 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) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + 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) +{ + ClutterTextPrivate *priv; + + 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"); + } +} + +/** + * 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) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + 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) +{ + ClutterTextPrivate *priv; + + 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"); + } +} + +/** + * 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) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + 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; +} + +/** + * 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) +{ + ClutterTextPrivate *priv; + + 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"); + } +} + +/** + * 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) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + 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) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (color) + { + priv->cursor_color = *color; + priv->cursor_color_set = TRUE; + } + else + 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"); +} + +/** + * 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) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (color != NULL); + + priv = self->priv; + + *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 + * + * 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 *self) +{ + ClutterTextPrivate *priv; + gchar *str; + gint len; + gint start_index, end_index; + gint start_offset, end_offset; + + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + priv = self->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 (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, 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) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + 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"); + } +} + +/** + * 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) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1); + + 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) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + + return text->priv->font_name; +} + +/** + * clutter_text_set_font_name: + * @self: a #ClutterText + * @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 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"); + * 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) +{ + ClutterTextPrivate *priv; + PangoFontDescription *desc; + + 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 = clutter_backend_get_font_name (clutter_get_default_backend ()); + + priv = self->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 (self); + + if (priv->text && priv->text[0] != '\0') + clutter_actor_queue_relayout (CLUTTER_ACTOR (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) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + return self->priv->text; +} + +/** + * clutter_text_set_text: + * @self: a #ClutterText + * @text: the text to set + * + * 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 + */ +void +clutter_text_set_text (ClutterText *self, + const gchar *text) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (text != NULL); + + priv = self->priv; + + 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_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 = strlen (text); + priv->n_chars = g_utf8_strlen (text, -1); + } + + 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 (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) +{ + ClutterUnit width; + + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + width = clutter_actor_get_widthu (CLUTTER_ACTOR (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) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (color != NULL); + + priv = self->priv; + + priv->text_color = *color; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (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) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (color != NULL); + + priv = self->priv; + + *color = priv->text_color; +} + +/** + * 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) +{ + ClutterTextPrivate *priv; + + 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; +} + +/** + * 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) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + 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) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->wrap != line_wrap) + { + priv->wrap = line_wrap; + + clutter_text_dirty_cache (self); + + 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. + * + * Calling this function on an editable #ClutterText will not cause + * the actor to parse any markup. + * + * 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: 1.0 + */ +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; +} + +/** + * 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) +{ + ClutterTextPrivate *priv; + gint len; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + len = priv->n_chars; + + 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)); +} + +/** + * 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_password_char: + * @self: a #ClutterText + * @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. + * + * If @wc is 0 the text will be displayed as it is entered in the + * #ClutterText actor. + * + * Since: 1.0 + */ +void +clutter_text_set_password_char (ClutterText *self, + gunichar wc) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->password_char != wc) + { + priv->password_char = wc; + + clutter_text_dirty_cache (self); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "password-char"); + } +} + +/** + * clutter_text_get_password_char: + * @self: a #ClutterText + * + * Retrieves the character to use in place of the actual text + * as set by clutter_text_set_password_char(). + * + * Return value: a Unicode character or 0 if the password + * character is not set + * + * Since: 1.0 + */ +gunichar +clutter_text_get_password_char (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); + + return self->priv->password_char; +} + +/** + * 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: 1.0 + */ +void +clutter_text_set_max_length (ClutterText *self, + gint max) +{ + ClutterTextPrivate *priv; + gchar *new = NULL; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->max_length != max) + { + if (max < 0) + max = priv->n_chars; + + priv->max_length = max; + + new = g_strdup (priv->text); + clutter_text_set_text (self, new); + g_free (new); + + g_object_notify (G_OBJECT (self), "max-length"); + } +} + +/** + * clutter_text_get_max_length: + * @self: a #ClutterText + * + * Gets the maximum length of text that can be set into a text actor. + * + * See clutter_text_set_max_length(). + * + * Return value: the maximum number of characters. + * + * Since: 1.0 + */ +gint +clutter_text_get_max_length (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); + + 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) +{ + 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; + + priv = self->priv; + + new = g_string_new (priv->text); + + 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); + + 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); +} + +/** + * 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, + 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); +} + +/** + * 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, + gssize end_pos) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + gint start_bytes; + gint end_bytes; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (!priv->text) + return; + + if (start_pos == 0) + start_bytes = 0; + else + 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); + + clutter_text_set_text (self, new->str); + + 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) +{ + 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 = priv->n_chars; + new = g_string_new (priv->text); + + if (priv->position == -1) + { + num_pos = offset_to_bytes (priv->text, priv->n_chars - 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"); +} + +/** + * clutter_text_get_chars: + * @self: a #ClutterText + * @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); + + 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); +} + +/** + * 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 new file mode 100644 index 000000000..2dc4bfa04 --- /dev/null +++ b/clutter/clutter-text.h @@ -0,0 +1,198 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2008 Intel Corporation. + * + * 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 + * 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_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; + +/** + * ClutterText: + * + * The #ClutterText struct contains only private data. + * + * Since: 1.0 + */ +struct _ClutterText +{ + /*< private >*/ + ClutterActor parent_instance; + + 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 >*/ + ClutterActorClass parent_class; + + /*< public >*/ + /* 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 */ + 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 (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); + +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); + +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); + +G_END_DECLS + +#endif /* __CLUTTER_TEXT_H__ */ diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 52a671b37..21709aee6 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -434,7 +434,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; @@ -467,21 +468,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 @@ -564,8 +570,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) 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/clutter/clutter.h b/clutter/clutter.h index 15208ce17..817dbef6c 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -47,14 +47,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" @@ -69,6 +67,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/clutter/cogl/cogl-texture.h b/clutter/cogl/cogl-texture.h index 2780b5db2..0b09f71c6 100644 --- a/clutter/cogl/cogl-texture.h +++ b/clutter/cogl/cogl-texture.h @@ -416,6 +416,29 @@ void cogl_material_rectangle (CoglFixed x1, gint tex_coords_len, const CoglFixed *tex_coords); +/** + * 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, + guint n_rects); + G_END_DECLS #endif /* __COGL_TEXTURE_H__ */ 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/Makefile.am b/clutter/cogl/common/Makefile.am index f78a2c645..c87f42a5d 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/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/clutter/cogl/gl/Makefile.am b/clutter/cogl/gl/Makefile.am index 819675df7..d95dd95cb 100644 --- a/clutter/cogl/gl/Makefile.am +++ b/clutter/cogl/gl/Makefile.am @@ -22,6 +22,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/gl/cogl-context.c b/clutter/cogl/gl/cogl-context.c index f74df873a..362c1f154 100644 --- a/clutter/cogl/gl/cogl-context.c +++ b/clutter/cogl/gl/cogl-context.c @@ -56,8 +56,10 @@ 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->material_handles = NULL; _context->material_layer_handles = NULL; @@ -153,6 +155,11 @@ cogl_destroy_context () if (_context->program_handles) g_array_free (_context->program_handles, TRUE); + 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 e54489d3c..1a48572a8 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -63,8 +63,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; + GLenum texture_wrap_mode; /* Materials */ GArray *material_handles; diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index 2d6d54778..3dc3f66d5 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -1911,6 +1911,95 @@ 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) + { + 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, + sizeof (CoglTextureGLVertex), p->t ) ); + + GE( glBindTexture (ctx->texture_target, ctx->texture_current) ); + GE( ctx->pf_glDrawRangeElements (GL_TRIANGLES, + 0, ctx->texture_vertices->len - 1, + needed_indices, + GL_UNSIGNED_SHORT, + ctx->texture_indices->data) ); + + g_array_set_size (ctx->texture_vertices, 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; + + _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++; +} + static void _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed x1, @@ -1931,11 +2020,7 @@ _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_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -1943,26 +2028,13 @@ _cogl_texture_quad_sw (CoglTexture *tex, printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); #endif - /* Prepare GL state */ - if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) - enable_flags |= COGL_ENABLE_TEXTURE_RECT; - else - enable_flags |= COGL_ENABLE_TEXTURE_2D; - - 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); - /* We can't use hardware repeat so we need to set clamp to edge otherwise it might pull in edge pixels from the other side */ + if (ctx->texture_vertices->len > 0 + && ctx->texture_wrap_mode != GL_CLAMP_TO_EDGE) + _cogl_texture_flush_vertices (); _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_EDGE); + ctx->texture_wrap_mode = GL_CLAMP_TO_EDGE; /* If the texture coordinates are backwards then swap both the geometry and texture coordinates so that the texture will be @@ -1987,9 +2059,6 @@ _cogl_texture_quad_sw (CoglTexture *tex, 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); @@ -2087,24 +2156,22 @@ _cogl_texture_quad_sw (CoglTexture *tex, iter_y.index * iter_x.array->len + iter_x.index); - 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; -#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); - - 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)); } } } @@ -2120,13 +2187,10 @@ _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_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); + GLenum wrap_mode; #if COGL_DEBUG printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); @@ -2134,24 +2198,6 @@ _cogl_texture_quad_hw (CoglTexture *tex, _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Prepare GL state */ - if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) - enable_flags |= COGL_ENABLE_TEXTURE_RECT; - else - enable_flags |= COGL_ENABLE_TEXTURE_2D; - - 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 coords are all in the range [0,1] then we want to clamp the coords to the edge otherwise it can pull in edge pixels from the wrong side when scaled */ @@ -2159,16 +2205,24 @@ _cogl_texture_quad_hw (CoglTexture *tex, && tx2 >= 0 && tx2 <= COGL_FIXED_1 && ty1 >= 0 && ty1 <= COGL_FIXED_1 && ty2 >= 0 && ty2 <= COGL_FIXED_1) - _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_EDGE); + wrap_mode = GL_CLAMP_TO_EDGE; else - _cogl_texture_set_wrap_mode_parameter (tex, GL_REPEAT); - - GE( glTexCoordPointer (2, GL_FLOAT, 0, tex_coords) ); - GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords) ); + wrap_mode = GL_REPEAT; /* 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 + || ctx->texture_wrap_mode != wrap_mode)) + _cogl_texture_flush_vertices (); + ctx->texture_target = tex->gl_target; + ctx->texture_current = gl_handle; + ctx->texture_wrap_mode = wrap_mode; + + _cogl_texture_set_wrap_mode_parameter (tex, wrap_mode); /* Don't include the waste in the texture coordinates */ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); @@ -2189,36 +2243,26 @@ _cogl_texture_quad_hw (CoglTexture *tex, ty2 *= y_span->size; } -#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) ); - -#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 -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; + CoglTexture *tex; + gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Check if valid texture */ if (!cogl_is_texture (handle)) @@ -2235,22 +2279,73 @@ cogl_texture_rectangle (CoglHandle handle, if (tex->slice_gl_handles->len == 0) return; - if (tx1 == tx2 || ty1 == ty2) - return; - - /* 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) - || (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); + /* Prepare GL state */ + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; else - _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + enable_flags |= COGL_ENABLE_TEXTURE_2D; + + 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); + + 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 +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 @@ -2299,22 +2394,8 @@ 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; - - ctx->texture_vertices = g_realloc (ctx->texture_vertices, - nsize - * sizeof (CoglTextureGLVertex)); - } + g_array_set_size (ctx->texture_vertices, n_vertices); + p = (CoglTextureGLVertex *) ctx->texture_vertices->data; /* Prepare GL state */ enable_flags = (COGL_ENABLE_VERTEX_ARRAY @@ -2332,14 +2413,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); @@ -2362,9 +2441,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++) { CoglFixed tx, ty; diff --git a/clutter/cogl/gles/Makefile.am b/clutter/cogl/gles/Makefile.am index 14f6a925b..f552df269 100644 --- a/clutter/cogl/gles/Makefile.am +++ b/clutter/cogl/gles/Makefile.am @@ -22,6 +22,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) \ diff --git a/clutter/cogl/gles/cogl-context.c b/clutter/cogl/gles/cogl-context.c index c6ac08809..ee64cbab5 100644 --- a/clutter/cogl/gles/cogl-context.c +++ b/clutter/cogl/gles/cogl-context.c @@ -59,8 +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->material_handles = NULL; _context->material_layer_handles = NULL; _context->source_material = COGL_INVALID_HANDLE; @@ -107,8 +110,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 cc1abdd3e..88e02e2aa 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; + /* Materials */ GArray *material_handles; GArray *material_layer_handles; diff --git a/clutter/cogl/gles/cogl-texture.c b/clutter/cogl/gles/cogl-texture.c index 1fbd6c9fb..5d4933e85 100644 --- a/clutter/cogl/gles/cogl-texture.c +++ b/clutter/cogl/gles/cogl-texture.c @@ -2050,6 +2050,94 @@ 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) + { + 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, + sizeof (CoglTextureGLVertex), p->t ) ); + + GE( glBindTexture (ctx->texture_target, ctx->texture_current) ); + GE( glDrawElements (GL_TRIANGLES, + needed_indices, + GL_UNSIGNED_SHORT, + ctx->texture_indices->data) ); + + g_array_set_size (ctx->texture_vertices, 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; + + _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++; +} + static void _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed x1, @@ -2070,31 +2158,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 @@ -2118,10 +2188,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); @@ -2161,17 +2228,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) ; @@ -2191,12 +2257,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)); @@ -2213,26 +2279,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)); } } } @@ -2248,41 +2311,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); @@ -2293,22 +2342,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 @@ -2322,47 +2429,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 @@ -2408,23 +2486,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 @@ -2450,14 +2514,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); @@ -2467,7 +2529,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 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/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; } 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); } 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/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 06e27e766..b83a0b2f3 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -433,11 +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 ask the stage + * to set up the GL viewport with the new size + */ + 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 010063951..d01d8e2b1 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -282,13 +282,21 @@ 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) + { + 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); + } clutter_stage_x11_fix_window_size (stage_x11); @@ -298,9 +306,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 */ @@ -397,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; @@ -479,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 @@ -740,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; } @@ -769,4 +776,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); } - 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/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 ffd06a561..baeb0b5bf 100644 --- a/doc/reference/clutter/clutter-docs.xml +++ b/doc/reference/clutter/clutter-docs.xml @@ -57,9 +57,8 @@ + - - diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 5948dd14a..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 @@ -66,48 +67,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 @@ -476,6 +435,11 @@ clutter_actor_get_scalex clutter_actor_set_rotationx clutter_actor_get_rotationx + +clutter_actor_grab_key_focus +clutter_actor_get_pango_context +clutter_actor_create_pango_context + CLUTTER_TYPE_GEOMETRY CLUTTER_TYPE_ACTOR_BOX @@ -698,6 +662,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 @@ -708,6 +674,8 @@ clutter_path_add_close clutter_path_add_string clutter_path_add_node clutter_path_add_cairo_path + + clutter_path_get_n_nodes clutter_path_get_node clutter_path_get_nodes @@ -736,11 +704,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 @@ -869,14 +835,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 @@ -1219,50 +1187,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 @@ -1533,23 +1457,25 @@ 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 -CLUTTER_VALUE_HOLDS_SHADER_FLOAT ClutterShaderFloat +CLUTTER_VALUE_HOLDS_SHADER_FLOAT clutter_value_set_shader_float clutter_value_get_shader_float -CLUTTER_VALUE_HOLDS_SHADER_INT ClutterShaderInt +CLUTTER_VALUE_HOLDS_SHADER_INT clutter_value_set_shader_int clutter_value_get_shader_int -CLUTTER_VALUE_HOLDS_SHADER_MATRIX ClutterShaderMatrix +CLUTTER_VALUE_HOLDS_SHADER_MATRIX clutter_value_set_shader_matrix clutter_value_get_shader_matrix @@ -1638,8 +1564,8 @@ clutter_interval_set_interval clutter_interval_get_interval -clutter_interval_validate clutter_interval_compute_value +clutter_interval_validate CLUTTER_TYPE_INTERVAL @@ -1655,32 +1581,8 @@ 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_list_actions -clutter_binding_pool_find_action -clutter_binding_pool_remove_action -clutter_binding_pool_block_action -clutter_binding_pool_unblock_action - - -clutter_binding_pool_activate -
- -
-ClutterCairoTexture clutter-cairo-texture +ClutterCairoTexture ClutterCairoTexture ClutterCairoTextureClass clutter_cairo_texture_new @@ -1706,3 +1608,107 @@ CLUTTER_CAIRO_TEXTURE_GET_CLASS ClutterCairoTexturePrivate clutter_cairo_texture_get_type
+ +
+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_password_char +clutter_text_get_password_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_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 + + +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_text_set_cursor_size +clutter_text_get_cursor_size + + +clutter_text_activate + + +CLUTTER_IS_TEXT +CLUTTER_IS_TEXT_CLASS +CLUTTER_TEXT +CLUTTER_TEXT_CLASS +CLUTTER_TEXT_GET_CLASS +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 +
diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index 2bac85c6e..f59698f02 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 @@ -29,3 +27,4 @@ clutter_score_get_type clutter_shader_get_type clutter_child_meta_get_type clutter_cairo_texture_get_type +clutter_text_get_type diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index fe81ddab1..b30cf9efe 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 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 diff --git a/po/clutter-0.8.pot b/po/clutter-0.8.pot deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index ec6899c0c..e3c36a873 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -17,15 +17,15 @@ 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 \ - $(NULL) + test-clutter-text.c \ + test-text-cache.c \ + $(NULL) # For convenience, this provides a way to easily run individual unit tests: .PHONY: wrappers clean-wrappers diff --git a/tests/conform/test-clutter-entry.c b/tests/conform/test-clutter-entry.c deleted file mode 100644 index 6562836b1..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; - -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-clutter-text.c b/tests/conform/test-clutter-text.c new file mode 100644 index 000000000..6814013a8 --- /dev/null +++ b/tests/conform/test-clutter-text.c @@ -0,0 +1,431 @@ +#include +#include +#include + +#include "test-conform-common.h" + +typedef struct { + gunichar unichar; + const char bytes[6]; + gint nbytes; +} TestData; + +static const TestData +test_text_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_text_data); i++) + { + const TestData *t = &test_text_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_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)); +} + +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); + + /* FIXME: cursor position should be -1? + clutter_text_set_text (text, ""); + 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_text_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); + } + + 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_text_data); i++) + { + const TestData *t = &test_text_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_text_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); + + 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_text_data); i++) + { + const TestData *t = &test_text_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_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) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_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)); +} + +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) +{ + 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 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; + + /* only editable entries listen to events */ + clutter_text_set_editable (text, TRUE); + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_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; + + /* only editable entries listen to events */ + clutter_text_set_editable (text, TRUE); + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_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..1b8d54c0f 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -68,22 +68,21 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/picking", test_pick); - TEST_CONFORM_SIMPLE ("/label", test_label_cache); - - 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); + 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 ("/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); 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); diff --git a/tests/conform/test-label-cache.c b/tests/conform/test-text-cache.c similarity index 70% rename from tests/conform/test-label-cache.c rename to tests/conform/test-text-cache.c index e9298f8fd..d3a2cf5ec 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) @@ -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 @@ -119,29 +129,29 @@ 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); + g_assert (check_result (data, "Change text", TRUE) == FALSE); /* 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); + 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_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); + g_assert (check_result (data, "Change the font", TRUE) == FALSE); /* TEST 5: change the color */ - clutter_label_set_color (CLUTTER_LABEL (data->label), &red); - check_result (data, "Change the color", FALSE); + clutter_text_set_color (CLUTTER_TEXT (data->label), &red); + g_assert (check_result (data, "Change the color", FALSE) == FALSE); /* TEST 6: change the attributes */ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); @@ -150,23 +160,23 @@ 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); + g_assert (check_result (data, "Change the attributes", TRUE) == FALSE); /* 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); + g_assert (check_result (data, "Change the text again", TRUE) == FALSE); /* 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); + 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 @@ -178,39 +188,39 @@ 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), + 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); 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); + g_assert (check_result (data, "Enable line wrap", TRUE) == FALSE); /* 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); + g_assert (check_result (data, "Change wrap mode", TRUE) == FALSE); /* 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. 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_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); + g_assert (check_result (data, "Change alignment", TRUE) == FALSE); clutter_main_quit (); @@ -218,7 +228,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 +236,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 +247,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 +256,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); diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 05b5977a1..cd05130fb 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 \ @@ -22,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 \ @@ -40,7 +38,9 @@ UNIT_TESTS = \ test-layout.c \ test-animation.c \ test-easing.c \ - test-binding-pool.c + test-binding-pool.c \ + test-text.c \ + test-text-field.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-clip.c b/tests/interactive/test-clip.c index 3b52fdb81..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); } @@ -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..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; @@ -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..c11900f10 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-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; -} diff --git a/tests/interactive/test-project.c b/tests/interactive/test-project.c index 1ac2176b7..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: @@ -224,8 +221,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-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), 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-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; +} diff --git a/tests/interactive/test-text.c b/tests/interactive/test-text.c new file mode 100644 index 000000000..d9bf879a7 --- /dev/null +++ b/tests/interactive/test-text.c @@ -0,0 +1,89 @@ +#include + +#include +#include + +#define FONT "Mono Bold 22px" + +static gchar *runes = +"ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ\n" +"ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ\n" +"ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬\n"; + + +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_text_set_line_wrap (CLUTTER_TEXT (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); + + if (argv[1]) + { + 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); + + 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 EXIT_SUCCESS; +} 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); diff --git a/tests/interactive/test-unproject.c b/tests/interactive/test-unproject.c index 422647e84..95a83cfde 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]", @@ -52,12 +53,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 +75,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 +95,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; } } @@ -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", @@ -124,8 +126,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 +135,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); @@ -146,5 +148,8 @@ test_unproject_main (int argc, char *argv[]) clutter_main(); + test_rectangle = NULL; + label = NULL; + return EXIT_SUCCESS; } 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); } } 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);