]> Emmanuele Bassi
&author_mail;
2009 Intel Corporation This document is distributed under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. A copy of this license can be found in the file COPYING included with the source code of this program. The Clutter Cookbook for Clutter &apiversion;
Preface The Perl Cookbook Let me show you that easy way, so others may easily follow. There is a wonderful simile in the preface of the Perl Cookbook: approaching a programming problem is oftentimes similar to balancing Columbus's egg. The initial difficulties of dealing with, and more importantly solving, problems in the computer programming field sometimes can only be overcome if somebody shows you how to use a new tool. This is true for programming languages but also for programming libraries. This book has been written to try and give you a reference on how to solve common issues that you might have to face when using the Clutter toolkit. This book is not meant to be a replacement for the API reference, even though there will be descriptions of how Clutter works and how its API looks like. We will require knowledge of the Clutter API, but we will also point out where to find more information on the API that examples have used. Indeed, this book should be used as a companion to the API reference, expanding the examples and showing how to achieve a specific result. This is not a book for learning Clutter. This is also not a book for learning C, or GObject or even GUI development. Above all, this is a book for learning more about Clutter, and about how to use it in the most efficient and easiest way. It is meant to help you move past the basic usage of Clutter. This book is divided into chapters. Each chapter is dedicated to a specific class, like ClutterTexture, or a specific area, like animations. Each chapter starts with a short introduction, followed by different recipes. Each recipe starts with a problem, or a short statement describing what we want to achieve; a solution, containing the source code; and a discussion section, where the code is explained, where alternative approaches might be useful, caveats and references to the Clutter API for furher studying. This book, in the cookbook spirit, can be accessed mostly at random.
About Clutter Clutter is an free and open source software library for creating fast, visually rich and animated graphical user interfaces. Clutter uses OpenGL (and, optionally, OpenGL ES on mobile and embedded platforms) for rendering the user interface elements, but at the same time it exposes an application program interface that hides the underlying complexity of the OpenGL state machine from the developer. The program interface of Clutter is intended to be easy to use, efficient, flexible and as self-documenting as possible.
About this document This document is available in various formats like HTML, and PDF. The latest version is always available at &docurl;.
Where to get Clutter You can obtain Clutter from &appurl;. Clutter is also available on all major GNU/Linux distributions, in various package formats. On OSX, Clutter is available with both Fink and MacPorts. Binaries for Microsoft Windows are also available.
Actors Edmon Gween, actor, on his deathbed An actor's a guy who if you ain't talkin' about him, ain't listening.
Introduction When building a User Interface with Clutter, the visible part of the UI — that is, what is displayed on the screen — is commonly referred to as "the scene graph". Like every graph, a scene graph is composed by nodes. Every node on the Clutter scene graph is an actor. Every actor has a single relationship with the others: it can be the parent of another actor, or a child of another actor. The Stage is an actor that can have children but cannot have any parent. Actors have different attributes: a position, a size, a scale factor, a rotation angle on each axis (relative to a specific center on the normal plane for that axis), an opacity factor. The scene graph is not fixed: it can be changed, not only by adding or removing actors, but also by changing the parent-child relationship: it is possible, for instance, to move an entire section of the scene graph from one parent actor to another.
Knowing when an actor position or size change
Problem You want to know when the position or the size, or both, of an actor change, for instance to update an unrelated actor or some internal state.
Solution You can use the notify signal, detailed with the coordinate or the dimension you want to know has changed: g_signal_connect (actor, "notify::x", G_CALLBACK (on_x_changed), NULL); g_signal_connect (actor, "notify::height", G_CALLBACK (on_height_changed), NULL); g_signal_connect (actor, "notify::depth", G_CALLBACK (on_depth_changed), NULL); If you want to know if any of the coordinates or dimensions of an actor have been changed, except for depth, you can use the allocation-changed signal: g_signal_connect (actor, "allocation-changed", G_CALLBACK (on_allocation_changed), NULL); The signature for the handler of the "notify" signal is: void on_notify (GObject *gobject, GParamSpec *pspec, gpointer user_data); While the signature for the handler of the "allocation-changed" signal is: void on_allocation_changed (ClutterActor *actor, const ClutterActorBox *allocation, ClutterAllocationFlags flags, gpointer user_data);
Discussion Any change the position and size of an actor will cause a change in the allocation of the actor itself. This will update the values of the :x, :y, :width and :height properties as well. The first technique allows a greater deal of granularity, allowing you to know what exactly changed. Inside the callback for the signal you can query the value of the property: void on_x_changed (GObject *gobject, GParamSpec *pspec, gpointer user_data) { gint x_value = 0; /* Round the X coordinate to the nearest pixel */ x_value = floorf (clutter_actor_get_x (CLUTTER_ACTOR (gobject))) + 0.5; g_print ("The new X coordinate is '%d' pixels\n", x_value); } The second technique is more indicated if you want to get notification that any of the positional or dimensional attributes changed, except for the depth: void on_allocation_changed (ClutterActor *actor, const ClutterActorBox *allocation, ClutterAllocationFlags flags, gpointer user_data) { ClutterActor *actor = CLUTTER_ACTOR (gobject); g_print ("The bounding box is now: (%.2f, %.2f) (%.2f x %.2f)\n", clutter_actor_box_get_x (allocation), clutter_actor_box_get_y (allocation), clutter_actor_box_get_width (allocation), clutter_actor_box_get_height (allocation)); } All actors will update these properties when their size or position change. Note that the Stage, on the other hand, will not notify on position changes, so it is not possible to use the :x and :y properties to know that the platform-specific window embedding the stage has been moved — if the platform supports a windowing system. In order to achieve that you will have to use backend-specific API to extract the surface used by the Stage and then platform-specific API to retrieve its coordinates.
Overriding the paint sequence
Problem You want to override the way an actor paints itself without creating a subclass.
Solution You can use the paint signal to invoke a callback that will be executed before the actor's paint implementation: g_signal_connect (actor, "paint", G_CALLBACK (on_paint), NULL); You can paint something after the actor's paint implementation by using the g_signal_connect_after() function instead of g_signal_connect(): g_signal_connect_after (actor, "paint", G_CALLBACK (on_paint_after), NULL); The signature for the handler of the "paint" signal is: void on_paint (ClutterActor *actor, gpointer user_data);
Discussion The paint cycle in Clutter works its way recursively from the Stage through every child. Whenever an Actor is going to be painted it will be positioned in a new frame of reference according to the list of transformations (scaling, rotation and additional traslations). After that, the "paint" signal will be emitted. The "paint" signal is defined as run-last, that is the signal handlers connected to it using g_signal_connetc() will be called first; then the default handler defined by the Actor's sub-class will be called; finally, all the signal handlers connected to the signal using g_signal_connect_after() will be called. This allows pre- and post-default paint handlers, and it also allows completely overriding the way an Actor draws itself by default; for instance: void on_paint (ClutterActor *actor) { do_my_paint (actor); g_signal_stop_emission_by_name (actor, "paint"); } The code above will prevent the default paint implementation of the actor from running.
Textures the author of the epigraph a short epigraph
Introduction introduction
Drawing 2D graphics onto a texture
Problem You want to draw 2D graphics inside a Clutter application.
Solution Create a ClutterCairoTexture, then draw onto the Cairo context it wraps using the Cairo API: ClutterActor *texture; cairo_t *cr; guint width, height; width = 800; height = 600; texture = clutter_cairo_texture_new (width, height); cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (texture)); /* * write onto the Cairo context cr using the Cairo API; * see the Cairo API reference for details */ cairo_move_to (cr, 0, 0); cairo_line_to (cr, 800, 600); cairo_stroke (cr); /* does the actual drawing onto the texture */ cairo_destroy (cr); Here's a useful Cairo tutorial if you want to learn more about the Cairo API itself.
Discussion A ClutterCairoTexture is a standard ClutterActor, so it can be added to ClutterContainers (e.g. a ClutterStage or ClutterGroup), animated, resized etc. in the usual ways. Other useful operations: To draw on part of the texture: use clutter_cairo_texture_create_region to retrieve a Cairo context for the region you want to draw on. To clear existing content from a texture: use clutter_cairo_texture_clear. You may need to do this as the texture reuses the same Cairo context each time you call clutter_cairo_texture_create or clutter_cairo_texture_create_region. To resize the Cairo context wrapped by a texture, use clutter_cairo_texture_set_surface_size.
Drawing pages from a PDF onto a ClutterCairoContext Other libraries may provide an API for writing onto a Cairo context; you can make use of these APIs on the exposed Cairo context of a ClutterCairoTexture. For example, you can use the poppler-glib API to display pages from a PopplerDocument inside a Clutter application: /* snipped setup code (as above) */ /* * cast to CLUTTER_CAIRO_TEXTURE, as the functions * used below require that type */ ClutterCairoTexture *cc_texture = CLUTTER_CAIRO_TEXTURE (texture); clutter_cairo_texture_clear (cc_texture); gchar *file_uri = "file:///path/to/file.pdf"; guint page_num = 0; double page_width, page_height; PopplerDocument *doc; PopplerPage *page; GError *error = NULL; doc = poppler_document_new_from_file (file_uri, NULL, &error); page = poppler_document_get_page (doc, page_num); poppler_page_get_size (page, &page_width, &page_height); cr = clutter_cairo_texture_create (cc_texture); /* render the page to the context */ poppler_page_render (page, cr); cairo_destroy (cr); ]]> Note that if the page is larger than the Cairo context, some of it might not be visible. Similarly, if the ClutterCairoTexture is larger than the stage, some of that might not be visible. So you may need to do some work to make the ClutterCairoTexture fit inside the stage properly (e.g. resize the stage), and/or some work to make the PDF page sit inside the Cairo context (e.g. scale the PDF page or put it inside a scrollable actor).
Contributing to this document This document is written in Docbook XML. The source file for this document is located in the subdirectory "doc/cookbook" of the source directory of Clutter.