the actor tree is only the representation of what your user interface is composed of: textures, reactive items, containers to give them structure. if nothing is painted, though, it’s not much of a user interface.
with the current stable version of Clutter, if you want to get something on the screen you either use one of the pre-canned sub-classes of ClutterActor
provided by Clutter itself — ClutterTexture
, ClutterRectangle
, and ClutterCairoTexture
— or you end up using something written outside of Clutter — Mx widgets, for instance; or you write your own UI elements. if you chose to write something from scratch, you will have to sub-class ClutterActor
and override the ClutterActor::paint
virtual function. ((yes, it’s a signal, which means that you can also attach a handler to an existing actor and completely change how said actor paints itself; this is incredibly evil, and will not be possible in 2.0, so if you’re doing that, stop right now, or I will tell mom, and you won’t be invited to the Buffy marathon next time.))
what happens inside a paint virtual function implementation is usually this:
- you use Cogl API to set up the state of the graphics pipeline;
- you use Cogl API to submit geometry to the graphics pipeline;
- you recursively call
clutter_actor_paint()
on your children, if any.
the third step can now be taken care of Clutter itself, as any actor now has access to the section of the actor tree with itself as the local root. the first and second steps are kind of tricky: you have to remember a lot of things about how to use the Cogl API. it would be nice if Clutter provided some convenience API to Do The Right Thing™ instead.
it would also be nicer if we could structure that API to build a representation of an entire frame, decoupling what gets painted from what is the logical unit of the user interface, because then we could keep it (either as parts or the entire thing) around between frames, manipulating the former without affecting the latter. the keen readers will already have said: you want a retained drawing model. good job! ten points for Gryffindor!
this new API is provided by paint nodes; each paint node is an element on the render tree — and every time you should paint an actor, you get passed a local root that you can use to attach your nodes. each paint node contains the pipeline state and the geometry to submit. once the tree has been built, Clutter will decide how to paint it. the base entry point for this operation is the ClutterActor::paint_node()
virtual function. inside it, you’re supposed to paint only the actor — the children will be painted by Clutter itself. the paint_node()
virtual should be considered orthogonal to the paint()
one, though the latter will probably remain in 2.0 to give you control over which of your children you may decide to paint. Clutter provides predefined paint nodes for texture, solid colors, text, and even for custom pipelines; to each node you can add a rectangle (with or without texture coordinates), paths, or even Cogl primitive objects if you decide to use the experimental Cogl 2.0 API.
currently, given that we allow app and toolkit developers to use a direct drawing model, we still need to maintain that invariant, otherwise stuff will break; but we can already start migrating towards a retained drawing model, and have it fully in place by the time we hit 2.0. instead of setting up all the transformations and modifying the modelview and clip states, we’ll be able to attach transformation and clip nodes, and even be able to discard sections of the render tree without hitting the GPU. from there, we could go anywhere; we could hand off rendering into a separate thread and never block the main loop; or we could do the reverse, and thread everything else instead — event processing, layouting — without having application code left to deal with the fact that only one thread at a time can access a GL context.
obviously, most developers will not want to care about this; apps usually draw textures and text anyway, fancy as they may be. app developers should get a better API to achieve that, though, and the current model of sub-classes of ClutterActor
is not serving them well enough; if you sub-class ClutterTexture
you get a ton of behaviours, and most of them are really not at all nice, especially if all you want was to get some image data on the screen. I won’t even speak about ClutterCairoTexture
, which encodes an implementation detail (that is not even true nowadays) into the class structure.
so, for 1.10 I introduced the ClutterContent
interface, and a couple of trivial implementations for displaying image data and for drawing using Cairo. the classes are side-effect-free because they only know about one thing: painting some state. implementing a ClutterContent
to paint something custom is easier than sub-classing ClutterActor
, just like it’s easier to implement a ClutterLayoutManager
to manage the layout policy of the children of an actor. all the cleverness on where and when to paint the content associated to an actor is where it belongs: inside the actor itself, leaving you to care just about what to paint.