2007-06-16 20:56:40 +00:00
|
|
|
<chapter id="clutter-subclassing-ClutterActor">
|
|
|
|
<chapterinfo>
|
|
|
|
<author>
|
|
|
|
<firstname>Emmanuele</firstname>
|
|
|
|
<surname>Bassi</surname>
|
|
|
|
<affiliation>
|
|
|
|
<address>
|
|
|
|
<email>ebassi@openedhand.com</email>
|
|
|
|
</address>
|
|
|
|
</affiliation>
|
|
|
|
</author>
|
|
|
|
</chapterinfo>
|
|
|
|
|
|
|
|
<title>Implementing a new actor</title>
|
|
|
|
|
|
|
|
<para>In order to implement a new #ClutterActor subclass the usual
|
|
|
|
machinery for subclassing a GObject should be used. After that, the
|
|
|
|
ClutterActor::query_coords() and ClutterActor::request_coords() virtual
|
|
|
|
functions should be overidden. Optionally, also the ClutterActor::paint()
|
|
|
|
virtual functions should be overridden.</para>
|
|
|
|
|
|
|
|
<para>The ClutterActor::query_coords() method of a #ClutterActor is
|
|
|
|
invoked when clutter_actor_query_coords() is called on an instance
|
|
|
|
of that actor class. It is used to return a #ClutterActorBox containing
|
|
|
|
the coordinates of the bounding box for the actor (the coordinates
|
|
|
|
of the top left corner and of the bottom right corner of the rectangle
|
|
|
|
that fully contains the actor). Container actors, or composite actors
|
|
|
|
with internal children, should call clutter_actor_query_coords() on
|
|
|
|
each visible child. Remember: the returned coordinates must be relative
|
|
|
|
to the parent actor.</para>
|
|
|
|
|
2008-02-15 10:46:20 +00:00
|
|
|
<note>All the coordinates are expressed using #ClutterUnit<!-- -->s,
|
|
|
|
the internal high-precision unit type, which guarantee sub-pixel
|
|
|
|
precision. #ClutterUnit has the same limitation that #ClutterFixed
|
|
|
|
has, see the <link linkend="clutter-Fixed-Point-Support">fixed point page</link>.</note>
|
|
|
|
|
2007-06-16 20:56:40 +00:00
|
|
|
<example id="clutter-actor-query-coords-example">
|
|
|
|
<para>This example shows how an actor class should override the
|
|
|
|
query_coords() virtual function of #ClutterActor. In this case,
|
|
|
|
the returned bounding box is the sum of the bounding boxes of all
|
|
|
|
the <structname>FooActor</structname> children.</para>
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
static void
|
|
|
|
foo_actor_query_coords (ClutterActor *actor,
|
|
|
|
ClutterActorBox *box)
|
|
|
|
{
|
|
|
|
FooActor *foo_actor = FOO_ACTOR (actor);
|
|
|
|
GList *child;
|
2008-02-15 10:46:20 +00:00
|
|
|
|
|
|
|
/* Clutter uses high-precision units which can be converted from
|
|
|
|
* and into pixels, typographic points, percentages, etc.
|
|
|
|
*/
|
2008-01-18 11:45:12 +00:00
|
|
|
ClutterUnit width, height;
|
2007-06-16 20:56:40 +00:00
|
|
|
|
|
|
|
/* initialize our size */
|
|
|
|
width = height = 0;
|
|
|
|
|
|
|
|
for (l = foo_actor->children; l != NULL; l = l->next)
|
|
|
|
{
|
|
|
|
ClutterActor *child_actor = child->data;
|
|
|
|
|
2008-02-15 10:46:20 +00:00
|
|
|
/* we consider only visible actors */
|
2007-06-16 20:56:40 +00:00
|
|
|
if (CLUTTER_ACTOR_IS_VISIBLE (child_actor))
|
|
|
|
{
|
2008-01-18 11:45:12 +00:00
|
|
|
ClutterActorBox child_box = { 0, };
|
2007-06-16 20:56:40 +00:00
|
|
|
|
|
|
|
clutter_actor_query_coords (child_actor, &child_box);
|
|
|
|
|
2008-01-18 11:45:12 +00:00
|
|
|
width += child_box.x2 - child_box.x2;
|
2007-06-16 20:56:40 +00:00
|
|
|
height += child_box.y2 - child_box.y1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-18 11:45:12 +00:00
|
|
|
box->x2 = box->x1 + width
|
|
|
|
box->y2 = box->y1 + height;
|
2007-06-16 20:56:40 +00:00
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<para>The ClutterActor::request_coords() method of a #ClutterActor
|
|
|
|
is invoked when clutter_actor_request_coords() is called on an instance
|
|
|
|
of that actor class. It is used to set the coordinates of the bounding
|
|
|
|
box for the actor. Container actors, or composite actors with internal
|
2007-11-23 17:12:27 +00:00
|
|
|
children, should override the request_coords() virtual function and call
|
|
|
|
clutter_actor_request_coords() on each visible child. Every actor class
|
|
|
|
overriding the request_coords() virtual function must chain up to the
|
|
|
|
parent class request_coords() method.</para>
|
2007-06-16 20:56:40 +00:00
|
|
|
|
|
|
|
<para>The ClutterActor::paint() method should be overridden if the
|
|
|
|
actor needs to control its drawing process, by using the GL API
|
|
|
|
directly. Actors performing transformations should push the GL matrix
|
|
|
|
first and then pop the GL matrix before returning. Container actors
|
|
|
|
or composite actors with internal children should do the same, and call
|
|
|
|
clutter_actor_paint() on every visible child:</para>
|
|
|
|
|
2008-02-15 10:46:20 +00:00
|
|
|
<para>When inside the ClutterActor::paint() method the actor is already
|
|
|
|
positioned at the coordinates specified by its bounding box; all the
|
|
|
|
paint operations should then take place from the (0, 0) coordinates.</para>
|
|
|
|
|
2007-06-16 20:56:40 +00:00
|
|
|
<example id="clutter-actor-paint-example">
|
|
|
|
<programlisting>
|
|
|
|
static void
|
|
|
|
foo_actor_paint (ClutterActor *actor)
|
|
|
|
{
|
|
|
|
FooActor *foo_actor = FOO_ACTOR (actor);
|
|
|
|
GList *child;
|
|
|
|
|
2008-01-18 11:45:12 +00:00
|
|
|
/* by including <clutter/cogl.h> it's possible to use the internal
|
|
|
|
* COGL abstraction API, which is also used by Clutter itself and avoids
|
|
|
|
* changing the GL calls depending on the target platform (GL or GL/ES).
|
|
|
|
*/
|
|
|
|
cogl_push_matrix ();
|
2007-06-16 20:56:40 +00:00
|
|
|
|
|
|
|
for (child = foo_actor->children; child != NULL; child = child->next)
|
|
|
|
{
|
|
|
|
ClutterActor *child_actor = child->data;
|
|
|
|
|
2008-01-18 11:45:12 +00:00
|
|
|
if (CLUTTER_ACTOR_IS_MAPPED (child_actor))
|
|
|
|
clutter_actor_paint (child_actor);
|
2007-06-16 20:56:40 +00:00
|
|
|
}
|
|
|
|
|
2008-01-18 11:45:12 +00:00
|
|
|
cogl_pop_matrix ();
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
2008-02-15 11:27:34 +00:00
|
|
|
<para>When painting a blended actor, cogl_enable() should be called with
|
|
|
|
the %CGL_BLEND flag; or, alternatively, glEnable() with %GL_BLEND on
|
|
|
|
OpenGL.</para>
|
|
|
|
|
2008-01-18 11:45:12 +00:00
|
|
|
<para>If the actor has a non-rectangular shape, or it has internal childrens
|
|
|
|
that needs to be distinguished by the events delivery mechanism, the
|
|
|
|
ClutterActor::pick() method should also be overridden. The ::pick() method
|
|
|
|
works exactly like the ::paint() method, but the actor should paint just
|
|
|
|
its shape with the passed colour:</para>
|
|
|
|
|
|
|
|
<example id="clutter-actor-pick-example">
|
|
|
|
<programlisting>
|
|
|
|
static void
|
|
|
|
foo_actor_pick (ClutterActor *actor,
|
|
|
|
const ClutterColor *pick_color)
|
|
|
|
{
|
|
|
|
FooActor *foo_actor = FOO_ACTOR (actor);
|
|
|
|
guint width, height;
|
|
|
|
|
2008-02-15 10:46:20 +00:00
|
|
|
/* it is possible to avoid a costly paint by checking whether the
|
|
|
|
* actor should really be painted in pick mode
|
|
|
|
*/
|
|
|
|
if (!clutter_actor_should_pick_paint (actor))
|
|
|
|
return;
|
|
|
|
|
2008-01-18 11:45:12 +00:00
|
|
|
/* by including <clutter/cogl.h> it's possible to use the internal
|
|
|
|
* COGL abstraction API, which is also used by Clutter itself and avoids
|
|
|
|
* changing the GL calls depending on the target platform (GL or GL/ES).
|
|
|
|
*/
|
|
|
|
cogl_color (pick_color);
|
|
|
|
|
|
|
|
clutter_actor_get_size (actor, &width, &height);
|
|
|
|
|
2008-02-15 10:46:20 +00:00
|
|
|
/* it is also possible to use raw GL calls, at the cost of losing
|
|
|
|
* portability
|
|
|
|
*/
|
2008-01-18 11:45:12 +00:00
|
|
|
glEnable (GL_BLEND);
|
|
|
|
|
|
|
|
/* draw a triangular shape */
|
|
|
|
glBegin (GL_POLYGON);
|
|
|
|
glVertex2i (width / 2, 0 );
|
|
|
|
glVertex2i (width , height);
|
|
|
|
glVertex2i (0 , height);
|
|
|
|
glEnd ();
|
|
|
|
|
|
|
|
cogl_pop_matrix ();
|
2007-06-16 20:56:40 +00:00
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
2008-02-15 11:27:34 +00:00
|
|
|
<section id="implementing-clutter-container">
|
|
|
|
<title>Implementing Containers</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The #ClutterContainer interface should be implemented by subclasses
|
|
|
|
of #ClutterActor who wants to provide a general API for adding child
|
|
|
|
actors.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
If the #ClutterActor subclass only handles internal children, or it's
|
|
|
|
not suitable for having generic actors added to it, it should not
|
|
|
|
implement the #ClutterContainer interface, but simply use
|
|
|
|
clutter_actor_set_parent():
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<example id="clutter-actor-set-parent-example">
|
|
|
|
<programlisting>
|
|
|
|
void
|
|
|
|
foo_actor_add_baz (FooActor *foo_actor,
|
|
|
|
BazActor *baz_actor)
|
|
|
|
{
|
|
|
|
g_return_if_fail (FOO_IS_ACTOR (foo_actor));
|
|
|
|
g_return_if_fail (BAZ_IS_ACTOR (baz_actor));
|
|
|
|
|
|
|
|
/* unparent the previous BazActor; this will automatically call
|
|
|
|
* g_object_unref() on the actor
|
|
|
|
*/
|
|
|
|
if (foo_actor->baz)
|
|
|
|
clutter_actor_unparent (foo_actor->baz);
|
|
|
|
|
|
|
|
foo_actor->baz = baz_actor;
|
|
|
|
|
|
|
|
/* this will cause the initial floating reference to disappear,
|
|
|
|
* and add a new reference on baz_actor. foo_actor has now taken
|
|
|
|
* ownership of baz_actor
|
|
|
|
*/
|
|
|
|
clutter_actor_set_parent (CLUTTER_ACTOR (baz_actor),
|
|
|
|
CLUTTER_ACTOR (foo_actor));
|
|
|
|
|
|
|
|
g_signal_emit (foo_actor, foo_actor_signals[BAZ_CHANGED], 0, baz_actor);
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
In order to implement the #ClutterContainer interface, these virtual
|
|
|
|
functions must be defined:
|
|
|
|
|
|
|
|
<variablelist>
|
|
|
|
<varlistentry>
|
|
|
|
<term>ClutterContainer::add</term>
|
|
|
|
<listitem>
|
|
|
|
<para></para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>ClutterContainer::remove</term>
|
|
|
|
<listitem>
|
|
|
|
<para></para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>ClutterContainer::foreach</term>
|
|
|
|
<listitem>
|
|
|
|
<para></para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>ClutterContainer::raise</term>
|
|
|
|
<listitem>
|
|
|
|
<para></para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>ClutterContainer::lower</term>
|
|
|
|
<listitem>
|
|
|
|
<para></para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>ClutterContainer::sort_depth_order</term>
|
|
|
|
<listitem>
|
|
|
|
<para></para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
</variablelist>
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
</section>
|
2008-02-15 10:46:20 +00:00
|
|
|
|
2007-06-16 20:56:40 +00:00
|
|
|
</chapter>
|