Programming With Clutter
Programming With Clutter
Murray Cumming
We very much appreciate any reports of inaccuracies or other errors in this document. Contributions are also most
welcome. Post your suggestions, critiques or addenda to the team (mailto:[email protected]).
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or
any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. You
may obtain a copy of the GNU Free Documentation License from the Free Software Foundation by visiting their Web site or by writing to: Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Table of Contents
1. Introduction............................................................................................................................................1
1.1. This book.....................................................................................................................................1
1.2. Clutter..........................................................................................................................................1
2. Installation..............................................................................................................................................3
2.1. Prebuilt Packages ........................................................................................................................3
2.2. Installing From Source................................................................................................................3
2.2.1. Dependencies..................................................................................................................3
3. Header Files And Linking.....................................................................................................................4
4. The Stage ................................................................................................................................................5
4.1. Stage Basics ................................................................................................................................5
4.1.1. Example..........................................................................................................................5
4.2. Stage Widget ...............................................................................................................................7
4.2.1. GTK+ integration ...........................................................................................................7
4.2.2. Example..........................................................................................................................8
4.3. Stage Widget Scrolling..............................................................................................................10
4.3.1. Example........................................................................................................................11
5. Actors ....................................................................................................................................................14
5.1. Actor Basics ..............................................................................................................................14
5.1.1. Example........................................................................................................................14
5.2. Transformations ........................................................................................................................16
5.2.1. Scaling ..........................................................................................................................16
5.2.2. Rotation ........................................................................................................................16
5.2.3. Clipping ........................................................................................................................17
5.2.4. Movement.....................................................................................................................17
5.2.5. Example........................................................................................................................17
5.3. Containers .................................................................................................................................19
5.3.1. Example........................................................................................................................20
5.4. Events........................................................................................................................................21
5.4.1. Example........................................................................................................................22
6. Timelines...............................................................................................................................................27
6.1. Using Timelines ........................................................................................................................27
6.2. Markers .....................................................................................................................................27
6.3. Example ....................................................................................................................................28
6.4. Grouping TimeLines in a Score ................................................................................................30
6.5. Example ....................................................................................................................................30
7. Animations............................................................................................................................................34
7.1. Using Animations .....................................................................................................................34
7.2. Using Alpha Functions..............................................................................................................34
7.3. Example ....................................................................................................................................35
8. Behaviours ............................................................................................................................................38
8.1. Using Behaviours ......................................................................................................................38
8.2. Example ....................................................................................................................................40
iii
iv
List of Figures
4-1. Stage .....................................................................................................................................................5
4-2. Stage Widget ........................................................................................................................................8
4-3. Stage Widget Scrolling.......................................................................................................................11
5-1. Actor...................................................................................................................................................15
5-2. Actor...................................................................................................................................................17
5-3. Group..................................................................................................................................................20
5-4. Actor Events .......................................................................................................................................23
6-1. Timeline..............................................................................................................................................28
6-2. Score...................................................................................................................................................31
7-1. Animation...........................................................................................................................................35
8-1. Effects of alpha functions on a path. ..................................................................................................38
8-2. Behaviour ...........................................................................................................................................40
9-1. ClutterText..........................................................................................................................................44
10-1. Full Example ....................................................................................................................................47
A-1. Behaviour ..........................................................................................................................................59
A-2. Behaviour ..........................................................................................................................................68
B-1. Scrolling Container............................................................................................................................79
Chapter 1. Introduction
1.1. This book
This book assumes a good understanding of C, and how to create C programs.
This book attempts to explain key Clutter concepts and introduce some of the more commonly used user
interface elements ("actors"). For full API information you should follow the links into the reference
documentation. This document covers the API in Clutter version 1.0.
Each chapter contains very simple examples. These are meant to show the use of the API rather than
show an impressive visual result. However, the full example should give some idea of what can be
achieved with Clutter
The Clutter platform uses techniques found in the GTK+ (http://www.gtk.org) platform, so you will
sometimes wish to refer to the GTK+ documentation.
We would very much like to hear of any problems you have learning Clutter with this document, and
would appreciate input regarding improvements. Please see the Contributing section for further
information.
1.2. Clutter
Clutter is a C programming API that allows you to create simple but visually appealing and involving
user interfaces. It offers a variety of objects (actors) which can be placed on a canvas (stage) and
manipulated by the application or the user. It is therefore a "retained mode" graphics API. Unlike
traditional 2D canvas APIs, Clutter allows these actors to move partly in the Z dimension.
This concept simplifies the creation of 3D interfaces compared to direct use of OpenGL or other 3D
drawing APIs. For instance, it restricts the user interaction to the 2D plane facing the user, which is
appropriate for todays devices allowing interaction only with a 2D plane such as a touchscreen. In
addition, your application does not need to provide visual context to show the user which objects are, for
instance, small rather than far away.
In addition, Clutter provides timeline and behavior abstractions which simplify animation by allowing
you to associate actor properties (such as position, rotation, or opacity) with callback functions,
including pre-defined functions of time such as sine waves.
Chapter 1. Introduction
Clutter uses the popular OpenGL 3D graphics API on regular desktop PCs, allowing it access to
hardware acceleration. On handheld devices it can use OpenGL ES, a subset of the OpenGL API aimed
at embedded devices. So, where necessary, you may also use OpenGL or OpenGL ES directly.
In the next few chapters you will learn how to place actors on the stage, how to set their properties, how
to change their properties (including their position) over time by using timelines and behaviours, and
how to do all this in response to user interaction.
Chapter 2. Installation
2.1. Prebuilt Packages
Clutter packages are probably available from your Linux distribution. For instance, on Ubuntu Linux or
Debian you can install the libclutter-1.0-dev package.
The configure script will check to make sure all of the required dependencies are already installed. If
you are missing any dependencies it will exit and display an error.
By default, Clutter will be installed under the /usr/local directory.
If you want to help develop Clutter or experiment with new features, you can also install Clutter from
SVN. Details are available at the Clutter web site (http://www.clutter-project.org/).
2.2.1. Dependencies
Before attempting to install Clutter, you should first install these other packages:
GTK+
libgl (Mesa)
These dependencies have their own dependencies, including the following applications and libraries:
pkg-config
glib
ATK
Pango
However, if you are using the "autotools" (automake, autoconf, etc) build system, you will find it more
convenient to use the PKG_CHECK_MODULES macro in your configure.ac file. For instance:
PKG_CHECK_MODULES(EXAMPLE, clutter-1.0)
AC_SUBST(EXAMPLE_CFLAGS)
AC_SUBST(EXAMPLE_LIBS)
You should then use the generated _CFLAGS and _LIBS definitions in your Makefile.am files. Note that
you may mention other libraries in the same PKG_CHECK_MODULES call, separated by spaces. For
instance, some examples in this tutorial require extra Clutter libraries, such as clutter-gtk-1.0,
clutter-cairo-1.0 or clutter-gst-1.0.
ClutterStage also implements the ClutterContainer interface, allowing it to contain child actors
via calls to clutter_container_add().
Call clutter_main() to start a main loop so that the stage can animate its contents and respond to user
interaction.
Reference (http://clutter-project.org/docs/clutter/1.0/ClutterStage.html)
4.1.1. Example
The following example shows a ClutterStage and handles clicks on the stage. There are no actors yet
so all you will see is a black rectangle.
You can create an executable from this code like so, being careful to use backticks around the call to
pkg-config. See also the Header Files And Linking section.
--libs
4.2.2. Example
The following example shows a GtkClutterEmbed GTK+ widget and changes the stage color when a
button is clicked.
Note that this example requires the clutter-gtk-1.0 library as well as the standard clutter-1.0
library. You can create an executable from this code like so, being careful to use backticks around the
call to pkg-config. See also the Header Files And Linking section.
--libs
<gtk/gtk.h>
<clutter/clutter.h>
<clutter-gtk/clutter-gtk.h>
<stdlib.h>
10
4.3.1. Example
This example is a simple image viewer that allows scrolling of the image. Note the layout of the
GtkTable, with the two scrollbars.
Figure 4-3. Stage Widget Scrolling
11
12
13
Chapter 5. Actors
5.1. Actor Basics
As mentioned in the introduction, Clutter is a canvas API for 2D surfaces in 3D space. Standard Clutter
actors have 2D shapes and can be positioned and rotated in all three dimensions, but they have no depth.
Theoretically, therefore, most actors would be invisible if they were exactly rotated so that only their
edge faced the screen. When complex 3D objects are needed, you may use the full OpenGL ES API, as
mentioned in the Implementing Actors appendix, but lets look at the standard actors for now:
Each actor should be added to the stage with clutter_container_add() and its positions should then
be specified. All actors derive from ClutterActor so you can call
clutter_actor_set_position() to set the x and y coordinates, and the z coordinate can be set with
clutter_actor_set_depth(), with larger values placing the actor further away from the observer.
clutter_actor_set_size() sets the width and height.
The actors position is relative to the top-left (0, 0) of the parent container (such as the stage), but this
origin can be changed by calling clutter_actor_set_anchor_point().
By default, actors are hidden, so remember to call clutter_actor_show(). You may later call
clutter_actor_hide() to temporarily hide the object again.
Like GTK+ widgets, Clutter actors have a "floating reference" when they are first instantiated with a
function such as clutter_rectangle_new(). This reference is then taken when the actor is added to a
container, such as the stage. This means that you do not need to unreference the actor after creating it.
Actors may also be transformed by scaling or rotation, and may be made partly transparent.
Reference (http://clutter-project.org/docs/clutter/1.0/ClutterActor.html)
14
Chapter 5. Actors
5.1.1. Example
The following example demonstrates two unmoving actors in a stage:
Figure 5-1. Actor
15
Chapter 5. Actors
/* Add a rectangle to the stage: */
ClutterActor *rect = clutter_rectangle_new_with_color (&actor_color);
clutter_actor_set_size (rect, 100, 100);
clutter_actor_set_position (rect, 20, 20);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
clutter_actor_show (rect);
/* Add a label to the stage: */
ClutterActor *label = clutter_text_new_full ("Sans 12", "Some Text", &actor_color);
clutter_actor_set_size (label, 500, 500);
clutter_actor_set_position (label, 20, 150);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), label);
clutter_actor_show (label);
/* Show the stage: */
clutter_actor_show (stage);
/* Start the main loop, so we can respond to events: */
clutter_main ();
return EXIT_SUCCESS;
}
5.2. Transformations
Actors can be scaled, rotated, and moved.
5.2.1. Scaling
Call clutter_actor_set_scale() to increase or decrease the apparent size of the actor. Note that
this will not change the result of clutter_actor_get_width() and
clutter_actor_get_height() because it only changes the size of the actor as seen by the user.
Calling clutter_actor_set_scale() again will replace the first scale rather than multiplying it.
5.2.2. Rotation
Call clutter_actor_set_rotation() to rotate the actor around an axis, specifying either
CLUTTER_X_AXIS, CLUTTER_Y_AXIS or CLUTTER_Z_AXIS and the desired angle. Only two of the x, y,
and z coordinates are used, depending on the specified axis. For instance, when using
CLUTTER_X_AXIS, the y and z parameters specify the center of rotation on the plane of the x axis.
16
Chapter 5. Actors
Like the clutter_actor_set_scale(), this does not affect the position, width, or height of the actor
as returned by functions such as clutter_actor_get_x().
5.2.3. Clipping
Actors may be "clipped" so that only one rectangular part of the actor is visible, by calling
clutter_actor_set_clip(), providing a position relative to the actor, along with the size. For
instance, you might implement scrolling by creating a large container actor and setting a clip rectangle so
that only a small part of the whole is visible at any one time. Scrolling up could then be implemented by
moving the actor down while moving the clip up. Clipping can be reverted by calling
clutter_actor_remove_clip().
The area outside of the clip does not consume video memory and generally does not require much
processing.
5.2.4. Movement
Clutter does not have a translation function that behaves similarly to clutter_actor_set_scale()
and clutter_actor_set_rotation(), but you can move the actor by calling
clutter_actor_move_by() or clutter_actor_set_depth.
Unlike the scaling and rotation functions, clutter_actor_move_by() does change the result of
functions such as clutter_actor_get_x().
5.2.5. Example
The following example demonstrates two unmoving actors in a stage, using rotation, scaling and
movement:
17
Chapter 5. Actors
Figure 5-2. Actor
18
Chapter 5. Actors
clutter_actor_show (rect);
/* Rotate it 20 degrees away from us around the x axis
* (around its top edge)
*/
clutter_actor_set_rotation (rect, CLUTTER_X_AXIS, -20, 0, 0, 0);
5.3. Containers
Some clutter actors implement the ClutterContainer interface. These actors can contain child actors
and may position them in relation to each other, for instance in a list or a table formation. In addition,
transformations or property changes may be applied to all children. Child actors can be added to a
container with the clutter_container_add() function.
The main ClutterStage is itself a container, allowing it to contain all the child actors. The only other
container in Core Clutter is ClutterGroup, which can contain child actors, with positions relative to the
parent ClutterGroup. Scaling, rotation and clipping of the group applies to the child actors, which can
simplify your code.
19
Chapter 5. Actors
Additional Clutter containers can be found in the Tidy toolkit library. See also the Implementing
Containers section.
ClutterContainer Reference (http://clutter-project.org/docs/clutter/1.0/ClutterContainer.html)
ClutterGroup Reference (http://clutter-project.org/docs/clutter/1.0/ClutterGroup.html)
5.3.1. Example
The following example shows the use of the ClutterGroup container, with two child actors being
rotated together.
Figure 5-3. Group
20
Chapter 5. Actors
{
ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
ClutterColor actor_color = { 0xff, 0xff, 0xff, 0x99 };
clutter_init (&argc, &argv);
/* Get the stage and set its size and color: */
ClutterActor *stage = clutter_stage_get_default ();
clutter_actor_set_size (stage, 200, 200);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
/* Add a group to the stage: */
ClutterActor *group = clutter_group_new ();
clutter_actor_set_position (group, 40, 40);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
clutter_actor_show (group);
/* Add a rectangle to the group: */
ClutterActor *rect = clutter_rectangle_new_with_color (&actor_color);
clutter_actor_set_size (rect, 50, 50);
clutter_actor_set_position (rect, 0, 0);
clutter_container_add_actor (CLUTTER_CONTAINER (group), rect);
clutter_actor_show (rect);
/* Add a label to the group: */
ClutterActor *label = clutter_text_new_full ("Sans 9", "Some Text", &actor_color);
clutter_actor_set_position (label, 0, 60);
clutter_container_add_actor (CLUTTER_CONTAINER (group), label);
clutter_actor_show (label);
/* Scale the group 120% along the x axis:
*/
clutter_actor_set_scale (group, 3.00, 1.0);
/* Rotate it around the z axis: */
clutter_actor_set_rotation (group, CLUTTER_Z_AXIS, 10, 0, 0, 0);
21
Chapter 5. Actors
5.4. Events
The base ClutterActor has several signals that are emitted when the user interacts with the actor:
button-press-event:
Emitted when the user presses the mouse over the actor.
button-release-event:
motion-event:
Emitted when the user releases the mouse over the actor.
Emitted when the user moves the mouse over the actor.
enter-event:
Emitted when the user moves the mouse in to the actors area.
leave-event:
Emitted when the user moves the mouse out of the actors area.
For instance, you can detect button clicks on an actor like so:
g_signal_connect (rect, "button-press-event", G_CALLBACK (on_rect_button_press), NULL);
Alternatively, you might just handle signals from the parent ClutterStage and use
clutter_stage_get_actor_at_pos to discover which actor should be affected.
However, as a performance optimization, Clutter does not emit all event signals by default. For instance,
to receive event signals for an actor instead of just the stage, you must call
clutter_actor_set_reactive(). If you dont need the motion event signals (motion-event,
enter-event and leave-event), you may call the global
clutter_set_motion_events_enabled() function with FALSE to further optimize performance.
Your event signal handler should return true when it has fully handled the event, or false if you want
the event to be sent also to the next actor in the event chain. Clutter first allows the stage to handle each
event via the captured-event signal. But if the stage does not handle the event then it will be passed
down to the child actor, first passing through the actors parent containers, giving each actor in the
hierarchy a chance to handle the event via a captured-event signal handler. If the event has still not
been handled fully by any actor then the event will then be emitted via a specific signal (such as
button-press-event or key-press-event. These specific signals are emitted first from the child
actor, then by its parent, passing all they way back up to the stage if no signal handler returns true to
indicate that it has handled the event fully.
Actors usually only receive keyboard events when the actor has key focus, but you can give an actor
exclusive access to any events by grabbing either the pointer or the keyboard, using
clutter_grab_pointer or clutter_grab_keyboard.
22
Chapter 5. Actors
5.4.1. Example
The following example demonstrates handing of clicks on an actor:
Figure 5-4. Actor Events
23
Chapter 5. Actors
if (!rect)
return FALSE;
if (CLUTTER_IS_RECTANGLE (rect))
g_print (" A rectangle is at that position.\n");
return TRUE; /* Stop further handling of this event. */
}
static gboolean
on_rect_button_press (ClutterRectangle *rect, ClutterEvent *event, gpointer data)
{
gfloat x = 0;
gfloat y = 0;
clutter_event_get_coords (event, &x, &y);
g_print ("Clicked rectangle at (%f, %f)\n", x, y);
/* clutter_main_quit(); */
return TRUE; /* Stop further handling of this event. */
}
static gboolean
on_rect_button_release (ClutterRectangle *rect, ClutterEvent *event, gpointer data)
{
gfloat x = 0;
gfloat y = 0;
clutter_event_get_coords (event, &x, &y);
g_print ("Click-release on rectangle at (%f, %f)\n", x, y);
return TRUE; /* Stop further handling of this event. */
}
static gboolean
on_rect_motion (ClutterRectangle *rect, ClutterEvent *event, gpointer data)
{
g_print ("Motion in the rectangle.\n");
return TRUE; /* Stop further handling of this event. */
}
static gboolean
on_rect_enter (ClutterRectangle *rect, ClutterEvent *event, gpointer data)
{
g_print ("Entered rectangle.\n");
return TRUE; /* Stop further handling of this event. */
}
static gboolean
on_rect_leave (ClutterRectangle *rect, ClutterEvent *event, gpointer data)
24
Chapter 5. Actors
{
g_print ("Left rectangle.\n");
return TRUE; /* Stop further handling of this event. */
}
25
Chapter 5. Actors
return EXIT_SUCCESS;
}
26
Chapter 6. Timelines
6.1. Using Timelines
A ClutterTimeline can be used to change the position or appearance of an actor over time. These can
be used directly as described in this chapter, or together with an animation or behaviour, as you will see
in the following chapters.
The timeline object emits its new-frame signal for each frame that should be drawn, for as many frames
per second as appropriate. In your signal handler you can set the actors properties. For instance, the
actor might be moved and rotated over time, and its color might change while this is happening. You
could even change the properties of several actors to animate the entire stage.
The clutter_timeline_new() constructor function takes a duration in milliseconds. The actual
number of frames per second requested by the timeline will depend on the behaviour of your entire
program, the performance of your hardware, and your monitors refresh rate. It may even vary over time
as conditions change. At best, the new-frame signal will be emitted at your monitors refresh rate. At
worst it will be called once at the start and once at the end of your timelines duration.
Clutter will not attempt to redraw the scene if the new frame has no change compared to the previous
frame, so you dont need to do your own optimization to prevent unnecessary redraws.
You may also use clutter_timeline_set_loop() to cause the timeline to repeat for ever, or until
you call clutter_timeline_stop(). The timeline does not start until you call
clutter_timeline_start().
Remember to unref the timeline when you are finished with it. Unlike actors, this does not have a
"floating reference". You may either do this after your mainloop has finished, or when the timeline has
finished, by handling the timelines completed signal.
Reference (http://clutter-project.org/docs/clutter/1.0/ClutterTimeline.html)
6.2. Markers
You may want an action to happen at a specific moment in the timeline. Instead of polling with
clutter_timeline_get_progress() you should instead use timeline markers. Timeline markers
can be added with the clutter_timeline_add_marker_at_time() function. Handle the
marker-reached signal to start the appropriate action at that moment, after checking the provided
27
Chapter 6. Timelines
marker name. To handle only the signal only for a particular marker, you may connect to the
marker-reached::my_marker signal, where my-marker is the name of your marker.
You can also use markers to navigate through the timeline using the
clutter_timeline_advance_to_marker() function.
6.3. Example
The following example demonstrates the use of a timeline to rotate a rectangle around the x axis while
changing its color:
Figure 6-1. Timeline
28
Chapter 6. Timelines
gint color_change_count = 0;
void
on_timeline_new_frame (ClutterTimeline *timeline,
gint frame_num, gpointer data)
{
rotation_angle += 1;
if(rotation_angle >= 360)
rotation_angle = 0;
/* Rotate the rectangle clockwise around the z axis, around its top-left corner: */
clutter_actor_set_rotation (rect, CLUTTER_X_AXIS,
rotation_angle, 0, 0, 0);
/* Change the color
* (This is a silly example, making the rectangle flash):
*/
++color_change_count;
if(color_change_count > 100)
color_change_count = 0;
if(color_change_count == 0)
{
ClutterColor rect_color = { 0xff, 0xff, 0xff, 0x99 };
clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), &rect_color);
}
else if (color_change_count == 50)
{
ClutterColor rect_color = { 0x10, 0x40, 0x90, 0xff };
clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), &rect_color);
}
}
void
on_timeline_marker_reached (ClutterTimeline* timeline,
gchar*
marker_name,
gint
frame_num,
gpointer
user_data)
{
printf ("Reached marker %s at frame %d.\n",
marker_name, frame_num);
}
int main(int argc, char *argv[])
{
ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
ClutterColor rect_color = { 0xff, 0xff, 0xff, 0x99 };
clutter_init (&argc, &argv);
/* Get the stage and set its size and color: */
ClutterActor *stage = clutter_stage_get_default ();
clutter_actor_set_size (stage, 200, 200);
29
Chapter 6. Timelines
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
/* Add a rectangle to the stage: */
rect = clutter_rectangle_new_with_color (&rect_color);
clutter_actor_set_size (rect, 70, 70);
clutter_actor_set_position (rect, 50, 100);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
clutter_actor_show (rect);
/* Show the stage: */
clutter_actor_show (stage);
30
Chapter 6. Timelines
6.5. Example
The following example demonstrates the use of a score containing two timelines, with one starting when
the other ends, and the whole score running as a loop. The first timeline rotates the rectangle as in the
previous example, and the second timeline moves the rectangle horizontally.
Figure 6-2. Score
31
Chapter 6. Timelines
if(rotation_angle >= 360)
rotation_angle = 0;
/* Rotate the rectangle clockwise around the z axis, around its top-left corner: */
clutter_actor_set_rotation (rect, CLUTTER_X_AXIS,
rotation_angle, 0, 0, 0);
/* Change the color
* (This is a silly example, making the rectangle flash):
*/
++color_change_count;
if(color_change_count > 100)
color_change_count = 0;
if(color_change_count == 0)
{
ClutterColor rect_color = { 0xff, 0xff, 0xff, 0x99 };
clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), &rect_color);
}
else if (color_change_count == 50)
{
ClutterColor rect_color = { 0x10, 0x40, 0x90, 0xff };
clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), &rect_color);
}
}
/* Move the rectangle. */
void
on_timeline_move_new_frame (ClutterTimeline *timeline,
gint frame_num, gpointer data)
{
gint x_position = clutter_actor_get_x (rect);
x_position += 1;
if(x_position >= 150)
x_position = 0;
clutter_actor_set_x (rect, x_position);
}
32
Chapter 6. Timelines
/* Add a rectangle to the stage: */
rect = clutter_rectangle_new_with_color (&rect_color);
clutter_actor_set_size (rect, 70, 70);
clutter_actor_set_position (rect, 50, 100);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
clutter_actor_show (rect);
/* Show the stage: */
clutter_actor_show (stage);
/* Create a score and add two timelines to it,
* so the second timeline starts when the first one stops: */
ClutterScore *score = clutter_score_new ();
clutter_score_set_loop (score, TRUE);
33
Chapter 7. Animations
7.1. Using Animations
The clutter_actor_animate(), clutter_actor_animate_with_timeline(), and
clutter_actor_animate_with_alpha() functions change the properties of a single actor over time,
using either a standard ClutterAnimationMode or a simple numeric calculation. In many cases this is
an easier way to implement animation.
These are convenience functions that use the ClutterAnimation object. You should not need to use
ClutterAnimation directly.
For instance, you could use clutter_actor_animate() to fade an actor by changing its "opacity"
property, while also moving it to a specified position by changing its "x" and "y" properties, changing all
three property values linearly over 1 second (1000 milliseconds). You should specify the values that the
properties should reach, regardless of their initial values.
34
Chapter 7. Animations
The ClutterAlpha object is constructed with a calculation callback and a ClutterTimeline which
tells it when a new frame needs a new value to be calculated. Your alpha callback will need to call
clutter_alpha_get_timeline() so it can return a value based on the timelines current progress,
using the clutter_timeline_get_progress() function.
Like actors, ClutterAlpha has a "floating references" so you dont need to unref it if you have added it
to a behaviour. However, the behaviours do not have a floating reference, so you should unref them when
you are finished with them. You might do this at the same time as you unref the timeline, for instance in a
signal handler for the timelines completed signal.
See the Behaviours chapter for an example that uses a ClutterAlpha.
7.3. Example
The following example demonstrates the use of an animation to achieve a fade and a movement on the
same actor, changing a rectangles opacity while it is moved along a straight line:
Figure 7-1. Animation
35
Chapter 7. Animations
#include <clutter/clutter.h>
#include <stdlib.h>
ClutterActor *rect = NULL;
36
Chapter 7. Animations
clutter_actor_animate_with_alpha (rect, alpha,
"x", 150.0,
"y", 150.0,
"opacity", 0,
NULL);
/* Start the main loop, so we can respond to events: */
clutter_main ();
g_object_unref (animation);
return EXIT_SUCCESS;
}
37
Chapter 8. Behaviours
8.1. Using Behaviours
The Animation API is simple but you will often need more control, while still avoiding the complication
of using ClutterTimeline directly.
Although the ClutterTimelines new-frame signal allows you to set actor properties for each frame,
Clutter also provides Behaviours which can change specific properties of one specific actor over time,
using a simple numeric calculation. However, unlike the simplified Animation API, using behaviours
directly allows you to combine them to control multiple actors simultaneously and allows you to change
the parameters of the behaviours while the timeline is running.
For instance, ClutterBehaviourPath moves the actor along a specified path, calculating the position
on the path once per frame by calling a supplied alpha callback. See the Using Alpha Functions section
in the Animation chapter.
38
Chapter 8. Behaviours
Figure 8-1. Effects of alpha functions on a path.
39
Chapter 8. Behaviours
ClutterBehaviour Reference (http://clutter-project.org/docs/clutter/1.0/ClutterBehaviour.html)
ClutterAlpha Reference (http://clutter-project.org/docs/clutter/1.0/ClutterAlpha.html)
The following standard behaviours are available in Clutter:
ClutterBehaviourDepth (http://clutter-project.org/docs/clutter/1.0/ClutterBehaviourDepth.html):
Moves the actor along the z axis.
ClutterBehaviourEllipse (http://clutter-project.org/docs/clutter/1.0/ClutterBehaviourEllipse.html):
Moves the actor around an ellipse.
ClutterBehaviourOpacity (http://clutter-project.org/docs/clutter/1.0/ClutterBehaviourOpacity.html):
Changes the opacity of the actor.
ClutterBehaviourRotate (http://clutter-project.org/docs/clutter/1.0/ClutterBehaviourRotate.html):
Rotates the actor.
8.2. Example
The following example demonstrates the use of a ClutterBehaviourPath with a simple custom alpha
callback. This simply moves the rectangle from the top-left to the bottom-right of the canvas at constant
speed:
40
Chapter 8. Behaviours
Figure 8-2. Behaviour
41
Chapter 8. Behaviours
{
ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
ClutterColor rect_color = { 0xff, 0xff, 0xff, 0x99 };
clutter_init (&argc, &argv);
/* Get the stage and set its size and color: */
ClutterActor *stage = clutter_stage_get_default ();
clutter_actor_set_size (stage, 200, 200);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
/* Add a rectangle to the stage: */
rect = clutter_rectangle_new_with_color (&rect_color);
clutter_actor_set_size (rect, 40, 40);
clutter_actor_set_position (rect, 10, 10);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
clutter_actor_show (rect);
/* Show the stage: */
clutter_actor_show (stage);
ClutterTimeline *timeline = clutter_timeline_new(5000 /* milliseconds */);
clutter_timeline_set_loop(timeline, TRUE);
clutter_timeline_start(timeline);
/* Instead of our custom callback,
* we could use a standard callback. For instance, CLUTTER_ALPHA_SINE_INC.
*/
ClutterAlpha *alpha = clutter_alpha_new_with_func (timeline, &on_alpha, NULL, NULL);
ClutterKnot knot[2];
knot[0].x = 10;
knot[0].y = 10;
knot[1].x= 150;
knot[1].y= 150;
42
For single line text entry, similar to a GtkEntry in a normal GTK+ application, create a
ClutterText using clutter_text_new() and clutter_text_set_single_line_mode().
You may call clutter_text_set_activatable() and connect to the activate signal to react
when the user presses Enter.
For full-featured multi-line text editing, ClutterText gives you access to the cursor position using
the clutter_text_get/set_cursor_*() functions and to the selection using the
clutter_text_get/set_selection*() functions. You can also add and remove text at any
position.
43
9.2. Example
This is a very basic example showing how to use a ClutterText for information display or multi-line
text editing.
Figure 9-1. ClutterText
44
45
46
47
48
/* We dont need to unref the actor because the floating reference was taken by the stage.
g_object_unref (item->ellipse_behaviour);
49
(gdouble)pixbuf_height : 0;
}
void load_images(const gchar* directory_path)
{
g_return_if_fail(directory_path);
/* Clear any existing images: */
g_slist_foreach (list_items, on_foreach_clear_list_items, NULL);
g_slist_free (list_items);
/* Create a new list: */
list_items = NULL;
/* Discover the images in the directory: */
GError *error = NULL;
GDir* dir = g_dir_open (directory_path, 0, &error);
if(error)
{
g_warning("g_dir_open() failed: %s\n", error->message);
g_clear_error(&error);
return;
}
const gchar* filename = NULL;
while ( (filename = g_dir_read_name(dir)) )
{
gchar* path = g_build_filename (directory_path, filename, NULL);
/* Try to load the file as an image: */
ClutterActor *actor = clutter_texture_new_from_file (path, NULL);
if(actor)
{
Item* item = g_new0(Item, 1);
item->actor = actor;
item->filepath = g_strdup(path);
/* Make sure that all images are shown with the same height: */
scale_texture_default (item->actor);
list_items = g_slist_append (list_items, item);
}
50
g_free (path);
}
}
51
52
/* Start the timeline and handle its "completed" signal so we can unref it. */
g_signal_connect (timeline_moveup, "completed", G_CALLBACK (on_timeline_moveup_completed),
clutter_timeline_start (timeline_moveup);
/* Note that ClutterAlpha has a floating reference so we dont need to unref it. */
}
void rotate_all_until_item_is_at_front(Item *item)
{
g_return_if_fail (item);
clutter_timeline_stop(timeline_rotation);
/* Stop the other timeline in case that is active at the same time: */
if(timeline_moveup)
clutter_timeline_stop (timeline_moveup);
clutter_actor_set_opacity (label_filename, 0);
/* Get the items position in the list: */
const gint pos = g_slist_index (list_items, item);
g_assert (pos != -1);
if(!item_at_front && list_items)
item_at_front = (Item*)list_items->data;
gint pos_front = 0;
if(item_at_front)
pos_front = g_slist_index (list_items, item_at_front);
g_assert (pos_front != -1);
/* const gint pos_offset_before_start = pos_front - pos; */
53
}
/* TODO: Set the number of frames, depending on the angle.
* otherwise the actor will take the same amount of time to reach
* the end angle regardless of how far it must move, causing it to
* move very slowly if it does not have far to move.
*/
angle_end += angle_step;
angle_start += angle_step;
list = g_slist_next (list);
}
54
static gboolean
on_texture_button_press (ClutterActor *actor, ClutterEvent *event, gpointer user_data)
{
/* Ignore the events if the timeline_rotation is running (meaning, if the objects are movi
* to simplify things:
*/
if(timeline_rotation && clutter_timeline_is_playing (timeline_rotation))
{
printf("on_texture_button_press(): ignoring\n");
return FALSE;
}
else
printf("on_texture_button_press(): handling\n");
Item *item = (Item*)user_data;
rotate_all_until_item_is_at_front (item);
return TRUE;
}
int main(int argc, char *argv[])
{
ClutterColor stage_color = { 0xB0, 0xB0, 0xB0, 0xff }; /* light gray */
clutter_init (&argc, &argv);
/* Get the stage and set its size and color: */
stage = clutter_stage_get_default ();
clutter_actor_set_size (stage, 800, 600);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
55
56
57
You should then specify your objects implementation of the ClutterActor::paint() virtual
function in your class_init function:
static void
clutter_triangle_class_init (ClutterTriangleClass *klass)
{
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
actor_class->paint = clutter_triangle_paint;
...
}
Your ClutterActor::paint() implementation should use the OpenGL API to actually paint
something. You will probably need some information from your objects generic ClutterActor base
class, for instance by calling clutter_actor_get_geometry() and
clutter_actor_get_opacity(), and by using your objects specific property values.
To make your code work with both OpenGL ES and regular OpenGL (and maybe even future Clutter
backends), you may wish to use Clutters cogl abstraction API which provides functions such as
cogl_rectangle() and cogl_push_matrix(). You can also detect whether the platform has support
for either the OpenGL or OpenGL ES API by ifdefing for CLUTTER_COGL_HAS_GL or
CLUTTER_COGL_HAS_GLES.
You should also implement the ClutterActor::pick() virtual function, painting a silhouette of your
actor in the provided color. Clutter uses this to draw each actors silhouette offscreen in a unique color,
using the color to quickly identify the actor under the cursor. If your actor is simple then you can
probably reuse the code from your paint() implementation.
58
A.2. Example
The following example demonstrates the implementation of a new triangle Actor type.
Figure A-1. Behaviour
59
parent;
(void);
(const ClutterColor *color);
void
(ClutterTriangle
ClutterColor
(ClutterTriangle
clutter_triangle_get_color
void
clutter_triangle_set_color
const ClutterColor *color);
*triangle,
*color);
*triangle,
G_END_DECLS
#endif
File: main.c
60
File: triangle_actor.c
#include "triangle_actor.h"
#include <cogl/cogl.h>
G_DEFINE_TYPE (ClutterTriangle, clutter_triangle, CLUTTER_TYPE_ACTOR);
enum
{
PROP_0,
PROP_COLOR
};
#define CLUTTER_TRIANGLE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TRIANGLE, ClutterTrianglePrivate))
61
62
63
static void
clutter_triangle_finalize (GObject *object)
{
G_OBJECT_CLASS (clutter_triangle_parent_class)->finalize (object);
}
static void
clutter_triangle_dispose (GObject *object)
{
G_OBJECT_CLASS (clutter_triangle_parent_class)->dispose (object);
}
static void
clutter_triangle_class_init (ClutterTriangleClass *klass)
{
GObjectClass
*gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
/* Provide implementations for ClutterActor vfuncs: */
actor_class->paint = clutter_triangle_paint;
actor_class->pick = clutter_triangle_pick;
gobject_class->finalize
gobject_class->dispose
gobject_class->set_property
gobject_class->get_property
=
=
=
=
clutter_triangle_finalize;
clutter_triangle_dispose;
clutter_triangle_set_property;
clutter_triangle_get_property;
/**
* ClutterTriangle:color:
*
* The color of the triangle.
*/
g_object_class_install_property (gobject_class,
PROP_COLOR,
g_param_spec_boxed ("color",
"Color",
"The color of the triangle",
CLUTTER_TYPE_COLOR,
G_PARAM_READABLE | G_PARAM_WRITABLE))
g_type_class_add_private (gobject_class, sizeof (ClutterTrianglePrivate));
}
static void
clutter_triangle_init (ClutterTriangle *self)
{
ClutterTrianglePrivate *priv;
self->priv = priv = CLUTTER_TRIANGLE_GET_PRIVATE (self);
64
65
color->red = priv->color.red;
color->green = priv->color.green;
color->blue = priv->color.blue;
color->alpha = priv->color.alpha;
}
/**
* clutter_triangle_set_color:
* @triangle: a #ClutterTriangle
* @color: a #ClutterColor
*
* Sets the color of @triangle.
*/
void
clutter_triangle_set_color (ClutterTriangle
const ClutterColor *color)
{
ClutterTrianglePrivate *priv;
*triangle,
66
67
A.4. Example
The following example demonstrates the implementation of a box container that lays its child actors out
horizontally. A real container should probably allow optional padding around the container and spacing
between the child actors. You might also want to allow some child actors to expand to fill the available
space, or align differently inside the container.
Figure A-2. Behaviour
68
EXAMPLE_TYPE_BOX
EXAMPLE_BOX(obj)
EXAMPLE_IS_BOX(obj)
EXAMPLE_BOX_CLASS(klass)
EXAMPLE_IS_BOX_CLASS(klass)
EXAMPLE_BOX_GET_CLASS(obj)
(example_box_get_type ())
(G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_BOX
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXAMPLE_TYPE_BOX
(G_TYPE_CHECK_CLASS_CAST ((klass), EXAMPLE_TYPE_BOX,
(G_TYPE_CHECK_CLASS_TYPE ((klass), EXAMPLE_TYPE_BOX)
(G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_BOX,
ExampleBoxChild;
ExampleBox;
ExampleBoxClass;
struct _ExampleBox
{
/*< private >*/
ClutterActor parent_instance;
/* List of ExampleBoxChild structures */
GList *children;
};
struct _ExampleBoxClass
{
/*< private >*/
ClutterActorClass parent_class;
};
69
File: examplebox.c
70
71
72
73
if (CLUTTER_ACTOR_IS_VISIBLE (child))
{
float child_min_width = 0;
float child_natural_width = 0;
clutter_actor_get_preferred_width (child, for_height, &child_min_width, &child_nat
min_width += child_min_width;
natural_width += child_natural_width;
}
}
if (min_width_p)
*min_width_p = min_width;
if (natural_width_p)
*natural_width_p = natural_width;
}
74
if (CLUTTER_ACTOR_IS_VISIBLE (child))
{
float child_min_height = 0;
float child_natural_height = 0;
clutter_actor_get_preferred_height (child, -1, &child_min_height, &child_natural_h
min_height = MAX (min_height, child_min_height);
natural_height = MAX (natural_height, child_natural_height);
}
}
if (min_height_p)
*min_height_p = min_height;
if (natural_height_p)
*natural_height_p = natural_height;
}
/* An implementation for the ClutterActor::allocate() vfunc: */
static void
example_box_allocate (ClutterActor
*actor,
const ClutterActorBox *box,
ClutterAllocationFlags absolute_origin_changed)
{
ExampleBox *ebox = EXAMPLE_BOX (actor);
/* Look at each child actor: */
float child_x = 0;
GList *l = NULL;
for (l = ebox->children; l; l = l->next)
{
ClutterActor *child = l->data;
75
76
/**
* example_box_remove_all:
* @box: a #ExampleBox
*
* Removes all child actors from the #ExampleBox
*/
void
77
/**
* example_box_new:
*
* Creates a new box.
*
* Return value: the newly created #ExampleBox
*/
ClutterActor *
example_box_new (void)
{
return g_object_new (EXAMPLE_TYPE_BOX, NULL);
}
78
B.2. Example
This example places three rectangles in a custom container which scrolls its child widgets to the left
when the user clicks on the stage.
Real-world applications will of course want to implement more specific behaviour, depending on their
needs. For instance, adding scrollbars that show accurate scroll positions, scrolling smoothly with
animation, efficiently drawing only objects that should be visible when dealing with large numbers of
rows.
79
EXAMPLE_TYPE_SCROLLING_CONTAINER
EXAMPLE_SCROLLING_CONTAINER(obj)
EXAMPLE_IS_SCROLLING_CONTAINER(obj)
EXAMPLE_SCROLLING_CONTAINER_CLASS(klass)
EXAMPLE_IS_SCROLLING_CONTAINER_CLASS(klass)
EXAMPLE_SCROLLING_CONTAINER_GET_CLASS(obj)
(example_scrolling_container_get_typ
(G_TYPE_CHECK_INSTANCE_CAST ((obj),
(G_TYPE_CHECK_INSTANCE_TYPE ((obj),
(G_TYPE_CHECK_CLASS_CAST ((klass), E
(G_TYPE_CHECK_CLASS_TYPE ((klass), E
(G_TYPE_INSTANCE_GET_CLASS ((obj), E
ExampleScrollingContainerChild;
ExampleScrollingContainer;
ExampleScrollingContainerClass;
struct _ExampleScrollingContainer
{
80
File: main.c
#include <clutter/clutter.h>
#include "scrollingcontainer.h"
#include <stdlib.h>
ClutterActor *scrolling = NULL;
static gboolean
on_stage_button_press (ClutterStage *stage, ClutterEvent *event, gpointer data)
{
printf ("Scrolling\n");
/* Scroll the container: */
example_scrolling_container_scroll_left (
EXAMPLE_SCROLLING_CONTAINER (scrolling), 10);
return TRUE; /* Stop further handling of this event. */
81
82
83
84
85
=
=
=
=
child_x;
0;
child_box.x1 + width;
child_box.y1 + height;
static void
example_scrolling_container_dispose (GObject *gobject)
{
/* Destroy each child actor when this container is destroyed: */
ExampleScrollingContainer *self = EXAMPLE_SCROLLING_CONTAINER (gobject);
GList *l;
for (l =
{
self->children; l; l = l->next)
86
87
*self,
/**
* example_scrolling_container_remove_all:
* @self: a #ExampleScrollingContainer
*
* Removes all child actors from the #ExampleScrollingContainer
*/
void
example_scrolling_container_remove_all (ExampleScrollingContainer *self)
{
GList *children;
g_return_if_fail (EXAMPLE_IS_SCROLLING_CONTAINER (self));
children = self->children;
while (children)
{
ClutterActor *child = children->data;
children = children->next;
clutter_container_remove_actor (CLUTTER_CONTAINER (self), child);
}
}
/**
* example_scrolling_container_new:
*
* Creates a new self.
*
* Return value: the newly created #ExampleScrollingContainer
*/
ClutterActor *
example_scrolling_container_new (void)
88
/**
* example_scrolling_container_scroll_left:
* @self: a #ExampleScrollingContainer
* @distance: The number of pixels by which to scroll left.
*
* Scroll all the child widgets left,
* resulting in some parts being hidden,
* and some parts becoming visible.
*/
void example_scrolling_container_scroll_left (ExampleScrollingContainer *self, gint distance
{
g_return_if_fail (EXAMPLE_IS_SCROLLING_CONTAINER (self));
self->offset += distance;
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
}
89
90