docs: Complete composite actor recipe

Add some extra detail to the Discussion section of the
composite actor recipe, concentrating on the pros and
cons of this approach.

Also explain more about the Clutter parts of the implementation.

Also general tidy up of language and style.
This commit is contained in:
Elliot Smith 2011-01-31 10:53:52 +00:00
parent 238fd52c4b
commit 08f5dc08d0

View File

@ -44,9 +44,10 @@
<section id="actors-composite-problem">
<title>Problem</title>
<para>You want to implement your own <type>ClutterActor</type>. For
example, a widget (button, scrollbar, checkbox etc.) composed of
several Clutter primitives.</para>
<para>You want to implement your own <type>ClutterActor</type>;
for example, a very simple button widget. But you want to base it
on existing Clutter primitives (rectangles, text) to minimise
the work required.</para>
</section>
<section id="actors-composite-solution">
@ -54,19 +55,25 @@
<para>Implement a custom actor composed from a <type>ClutterBox</type>
packed with other <type>ClutterActors</type>. The custom actor
effectively provides a facade for these internal actors, simplifying
provides a facade over these internal actors, simplifying
access to their properties and behavior.</para>
<para>In this recipe, we make the simplest possible button widget
(<type>CbButton</type>) to demonstrate the basic principles of
implementing a <type>ClutterActor</type>. It is not a complete
button implementation: see
<ulink url="http://git.clutter-project.org/mx/tree/mx/mx-button.c"><type>MxButton</type></ulink>
for a more
comprehensive example (and the basis for this recipe). But
it does cover the most important parts of a <type>ClutterActor</type>
implementation, as well some useful <type>GObject</type>-related
code.</para>
<para>In this recipe, we subclass <type>ClutterActor</type> using this
approach to create a very simple button widget, <type>CbButton</type>.
It is not a complete button implementation: see
<ulink url="http://git.clutter-project.org/mx/tree/mx/mx-button.c">
<type>MxButton</type></ulink> for a more comprehensive example
(and the basis for this recipe). But this recipe does cover the most
important parts of a <type>ClutterActor</type> implementation,
as well some useful <type>GObject</type>-related code.</para>
<tip>
<para>As Clutter is a GObject-based library, it relies
heavily on GObject concepts and idioms. If you are unfamiliar with
GObject, please read
<ulink url="http://library.gnome.org/devel/gobject/unstable/">the GObject
Reference Manual</ulink> before proceeding.</para>
</tip>
<para>The code for this solution is structured like standard GObject
C library code:</para>
@ -88,13 +95,19 @@
<para>Each of these files is described in more detail below.</para>
<tip>
<para>As Clutter is a GObject-based library, it relies
heavily on GObject concepts and idioms. If you are unfamiliar with GObject,
please read
<ulink url="http://library.gnome.org/devel/gobject/unstable/">the GObject
Reference Manual</ulink> before proceeding.</para>
</tip>
<note>
<para>In a more realistic context, <type>CbButton</type> would
have some build infrastructure (for example, autotooling)
so it could be compiled, installed, and reused in a variety of
applications. However, for the purposes of cookbook examples,
these issues are ignored here.</para>
<para>If you <emphasis>are</emphasis> planning on building
your own widgets using Clutter as part of an application, or
to create your own library, the
<ulink url="http://git.clutter-project.org/mx/">Mx toolkit</ulink>
provides an excellent example of how to autotool your project.</para>
</note>
<example id="actors-composite-cb-button-h">
<title><filename>cb-button.h</filename>: header file</title>
@ -115,10 +128,12 @@
and GObject implementation</title>
<para>This is the main C code file which implements both
the GObject and Clutter parts of <type>CbButton</type>.
The example below is commented liberally, and gives some samples
of gtk-doc annotations to generate API docs for the
widget. The <link linkend="actors-composite-discussion">discussion
the GObject and Clutter elements of <type>CbButton</type>.
The example below is liberally commented, and also gives some samples
of annotations to generate
<ulink url="http://www.gtk.org/gtk-doc/">gtk-docs</ulink> for the
widget. The
<link linkend="actors-composite-discussion-clutter-virtual-functions">discussion
section</link> comments more specifically about the Clutter-specific
parts of it.</para>
@ -151,40 +166,145 @@
<section id="actors-composite-discussion">
<title>Discussion</title>
<para>Explain about GObject a bit???</para>
<para>Note size requisition in the example application???</para>
<para>The actor implemented here is put together through
simple composition. This has the advantage of simplifying the
code for the subclass: we can just wrap a facade round
existing Clutter classes, rather than writing code to implement
everything ourselves. In the example here, we make use of a
<para>The actor implemented here is based on
simple composition: bundling several actors together and wrapping
their behavior and properties. In the example here, we make use of a
<type>ClutterLayoutManager</type> to handle positioning of
the <type>ClutterText</type>; we change the background color of
the button by changing the color of the
<type>ClutterBox</type>; we use a <type>ClutterClickAction</type>
<type>ClutterBox</type>; and we use a <type>ClutterClickAction</type>
to simplify implementation of a click signal.</para>
<para>On the other hand, it puts some constraints on the outline of
the actor: it makes it harder to use a custom outline, for
example, to round the corners of the button. This approach may
not be suitable where you need to do a lot of custom animation
and drawing.</para>
<para>You may find that this approach is appropriate if you need
to implement a simple rectangular actor. However, it puts some
constraints on the outline of the actor, making it harder to
use a custom outline: for example, a rectangle with rounded corners
or a shape which can't be approximated by a rectangle. Such cases
require both <function>pick()</function> and <function>paint()</function>
implementations using Cogl (or similar): see
<link linkend="actors-non-rectangular">this recipe</link>
for more details.</para>
<para>This isn't the whole story: if you aren't using this simple
composition approach, you may need to do more: see the notes in
the Clutter reference manual. Also, if you're implementing a
container, you'll need to do more.</para>
<para>The composition approach may also be inappropriate where
you need to do a lot of custom animation and drawing; and it is
likely to be inappropriate for implementing a container
actor. See the notes on implementing a new actor in the Clutter
reference manual for more details of what may be required
in these cases.</para>
<para>use Mx for inspiration???</para>
<section id="actors-composite-discussion-clutter-virtual-functions">
<title>Implementing <type>ClutterActor</type> virtual functions</title>
<para>GObject implementation parts vs. Clutter implementation???</para>
<para>While most of the <type>CbButton</type> implementation
revolves around GObject, there are some elements of it
specific to Clutter. Due to the simplicity of
the <type>CbButton</type> actor, the implementation of
these functions is fairly trivial, as explained below:</para>
<para>something about how the ClutterBox is parented onto the
ClutterActor subclass we are implementing; we just create
an allocation for the ClutterBox based on the allocation of its
parent???</para>
<itemizedlist>
<listitem>
<formalpara>
<title>Size requisition:
<function>cb_button_get_preferred_height()</function>
and <function>cb_button_get_preferred_width()</function></title>
<para>During the size requisition phase, Clutter asks each
actor the minimum size it should be to remain useful,
and the maximum size it would be if unconstrained. This is done
by calling the <function>get_preferred_height()</function>
and <function>get_preferred_width()</function> functions
on each actor in turn.</para>
</formalpara>
<para>If an actor will only ever be explictly sized
(via <function>clutter_actor_set_size()</function>,
<function>clutter_actor_set_height()</function> and/or
<function>clutter_actor_set_width()</function>),
there is no need to implement the <function>get_preferred_*()</function>
functions. (Some actors like <type>ClutterRectangle</type>
work this way and require explicit sizing.)</para>
<para>However, if an actor's size should be negotiated during
the size requisition phase, you can implement these functions,
using the size of the child actors as a basis for the
preferred height and width. In the case of
<type>CbButton</type>, a preferred height and width can be
computed; these are based on the height and width of
the child <type>ClutterBox</type>, plus 20 pixels on each
axis. Because the size of the box is itself dependent on
the size of the <type>ClutterText</type> inside it, the net
result is that the <type>CbButton</type> preferred size
is the size of the text actor inside it, plus 20 pixels on each
axis.</para>
</listitem>
<listitem>
<formalpara>
<title>Allocation:
<function>cb_button_allocate()</function></title>
<para>The requests gathered during size requisition
are then negotiated by Clutter, each actor
receiving some allocation of the available space. At the
end of this process, each actor is allocated a
<emphasis>box</emphasis>, representing the space available
to it on the stage.</para>
</formalpara>
<para>An actor implementation is responsible for distributing
space from its allocation box to its children as it sees
fit. In the case of <type>CbButton</type>, there is only a single
<type>ClutterBox</type> actor which needs allocation;
<function>cb_button_allocate()</function> therefore
allocates all of the button's space to its child
<type>ClutterBox</type>.</para>
</listitem>
<listitem>
<formalpara>
<title>Painting and picking:
<function>cb_button_paint()</function></title>
<para>Clutter works its way through the actors on the
stage, following the actor hierarchy (top level
actors directly inside the stage first);
<function>clutter_actor_paint()</function>
is called on each actor. This, in turn, calls the actor's
<function>paint()</function> implementation. If the actor
is a container, it may iterate over its children,
calling <function>paint()</function> on each; the children
may call <function>paint()</function> on their children...;
and so on, until the leaves of the actor hierarchy are
reached.</para>
</formalpara>
<para>As our actor consists of a single <type>ClutterBox</type>
child, its <function>paint()</function> implementation simply
has to retrieve the reference to that <type>ClutterBox</type>
(from its private structure) and call
<function>clutter_actor_paint()</function>
on it. Painting of the <type>ClutterBox's</type> child
(the <type>ClutterText</type>) is handled by the
<type>ClutterBox</type>.</para>
<para>In cases where an actor is non-rectangular, you also
need to implement a <function>pick()</function> function.
(This is used to determine which actor was the recipient of
an event occurring within the stage.) However, because
the actor in this recipe is a simple rectangle, there is no
need to implement <function>pick()</function>.</para>
</listitem>
</itemizedlist>
</section>
</section>