Merge remote branch 'elliot/cookbook-actors-composite'
* elliot/cookbook-actors-composite: docs: Add reference to useful GObject tutorial docs: Explain why destroy() is implemented docs: Implement destroy() rather than dispose() docs: Don't use clutter_stage_get_default() docs: Change text on button docs: Add a note about other state variables docs: Complete composite actor recipe docs: Change order of functions in example to match docs docs: Add more comments on how allocate() works docs: Include code examples in the recipe docs: Explain enums for properties and signals docs: Don't set explicit size on button docs: Add example of preferred_height() and preferred_width() docs: Add recipe for creating a custom ClutterActor with composition docs: Add more comments on code example for composite actor docs: Improve example code formatting docs: Add some gtk-doc annotations to example docs: Add custom ClutterActor example which uses composition
This commit is contained in:
commit
bb44d51fc9
@ -1,7 +1,8 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<chapter id="actors">
|
||||
<chapter id="actors"
|
||||
xmlns:xi="http://www.w3.org/2003/XInclude">
|
||||
<title>Actors</title>
|
||||
|
||||
<epigraph>
|
||||
@ -37,6 +38,305 @@
|
||||
|
||||
</section>
|
||||
|
||||
<section id="actors-composite">
|
||||
<title>Implementing a simple custom actor</title>
|
||||
|
||||
<section id="actors-composite-problem">
|
||||
<title>Problem</title>
|
||||
|
||||
<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">
|
||||
<title>Solution</title>
|
||||
|
||||
<para>Implement a custom actor composed from a <type>ClutterBox</type>
|
||||
packed with other <type>ClutterActors</type>. The custom actor
|
||||
provides a facade over these internal actors, simplifying
|
||||
access to their properties and behavior.</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. You might also find
|
||||
<ulink url="http://diuf.unifr.ch/pai/wiki/doku.php/research:projects:gobject_tutorial">this
|
||||
tutorial</ulink> a useful introduction.</para>
|
||||
</tip>
|
||||
|
||||
<para>The code for this solution is structured like standard GObject
|
||||
C library code:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The header file <filename>cb-button.h</filename>
|
||||
declares the class' public API (function prototypes, macros,
|
||||
structs).</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The code file <filename>cb-button.c</filename>
|
||||
contains the class implementation.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>One more example file, <filename>actors-composite-main.c</filename>,
|
||||
shows how to use <type>CbButton</type> in an application.</para>
|
||||
|
||||
<para>Each of these files is described in more detail below.</para>
|
||||
|
||||
<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>
|
||||
|
||||
<para>This defines the public API for the class, including
|
||||
GObject type macros, class and object structures, and
|
||||
function prototypes.</para>
|
||||
|
||||
<programlisting>
|
||||
<xi:include href="examples/cb-button.h" parse="text">
|
||||
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||
</xi:include>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<example id="actors-composite-cb-button-c">
|
||||
<title><filename>cb-button.c</filename>: <type>ClutterActor</type>
|
||||
and GObject implementation</title>
|
||||
|
||||
<para>This is the main C code file which implements both
|
||||
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>
|
||||
|
||||
<programlisting>
|
||||
<xi:include href="examples/cb-button.c" parse="text">
|
||||
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||
</xi:include>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<example id="actors-composite-actors-composite-main-c">
|
||||
<title><filename>actors-composite-main.c</filename>: trivial
|
||||
application demonstrating usage of <type>CbButton</type></title>
|
||||
|
||||
<para>Note how any of the <type>ClutterActor</type>
|
||||
functions (like <function>clutter_actor_set_size()</function>
|
||||
and <function>clutter_actor_add_constraint()</function>) can
|
||||
be applied to instances of our <type>ClutterActor</type>
|
||||
implementation.</para>
|
||||
|
||||
<programlisting>
|
||||
<xi:include href="examples/actors-composite-main.c" parse="text">
|
||||
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||
</xi:include>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="actors-composite-discussion">
|
||||
<title>Discussion</title>
|
||||
|
||||
<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>; and we use a <type>ClutterClickAction</type>
|
||||
to simplify implementation of a click signal.</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>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>
|
||||
|
||||
<section id="actors-composite-discussion-clutter-virtual-functions">
|
||||
<title>Implementing <type>ClutterActor</type> virtual functions</title>
|
||||
|
||||
<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>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
|
||||
<formalpara>
|
||||
<title>Object destruction:
|
||||
<function>cb_button_destroy()</function></title>
|
||||
|
||||
<para><type>ClutterActor</type> subclasses based
|
||||
on composition should implement the <function>destroy()</function>
|
||||
virtual function. This is called on an actor when its
|
||||
container is destroyed to clean up the resources
|
||||
allocated to the actor; it also emits a
|
||||
<emphasis>destroy</emphasis> signal which other code can
|
||||
hook onto.</para>
|
||||
</formalpara>
|
||||
|
||||
<para>In the case of <type>CbButton</type>, the
|
||||
<function>destroy()</function> implementation calls
|
||||
<function>clutter_actor_destroy()</function> on the child
|
||||
<type>ClutterBox</type>, then sets that child to
|
||||
<constant>NULL</constant>. Finally, it checks for a
|
||||
<function>destroy()</function> implementation on the parent
|
||||
class, then calls it if one exists.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
<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>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="actors-allocation-notify">
|
||||
<title>Knowing when an actor's position or size changes</title>
|
||||
|
||||
|
@ -3,6 +3,7 @@ include $(top_srcdir)/build/autotools/Makefile.am.silent
|
||||
NULL =
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
actors-composite-main \
|
||||
animations-complex \
|
||||
animations-looping-animator \
|
||||
animations-looping-implicit \
|
||||
@ -62,6 +63,7 @@ AM_CFLAGS = \
|
||||
|
||||
AM_LDFLAGS = $(CLUTTER_LIBS) -export-dynamic
|
||||
|
||||
actors_composite_main_SOURCES = cb-button.c actors-composite-main.c
|
||||
animations_complex_SOURCES = animations-complex.c
|
||||
animations_looping_animator_SOURCES = animations-looping-animator.c
|
||||
animations_looping_implicit_SOURCES = animations-looping-implicit.c
|
||||
|
79
doc/cookbook/examples/actors-composite-main.c
Normal file
79
doc/cookbook/examples/actors-composite-main.c
Normal file
@ -0,0 +1,79 @@
|
||||
#include <stdlib.h>
|
||||
#include "cb-button.h"
|
||||
|
||||
/* colors */
|
||||
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
|
||||
static const ClutterColor white_color = { 0xff, 0xff, 0xff, 0xff };
|
||||
static const ClutterColor yellow_color = { 0x88, 0x88, 0x00, 0xff };
|
||||
|
||||
/* click handler */
|
||||
static void
|
||||
clicked (CbButton *button,
|
||||
gpointer data)
|
||||
{
|
||||
const gchar *current_text;
|
||||
|
||||
g_debug ("Clicked");
|
||||
|
||||
current_text = cb_button_get_text (button);
|
||||
|
||||
if (g_strcmp0 (current_text, "hello") == 0)
|
||||
cb_button_set_text (button, "world");
|
||||
else
|
||||
cb_button_set_text (button, "hello");
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
ClutterActor *stage;
|
||||
ClutterActor *button;
|
||||
ClutterConstraint *align_x_constraint;
|
||||
ClutterConstraint *align_y_constraint;
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
stage = clutter_stage_new ();
|
||||
clutter_actor_set_size (stage, 400, 400);
|
||||
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
||||
|
||||
button = cb_button_new ();
|
||||
cb_button_set_text (CB_BUTTON (button), "hello");
|
||||
|
||||
/* the following is equivalent to the two lines above:
|
||||
*
|
||||
* button = g_object_new (CB_TYPE_BUTTON,
|
||||
* "text", "winkle",
|
||||
* NULL);
|
||||
*
|
||||
* because we defined a set_property function, which can accept
|
||||
* a PROP_TEXT parameter, GObject can create a button and set one
|
||||
* or more properties with a single call to g_object_new()
|
||||
*/
|
||||
|
||||
/* note that the size of the button is left to Clutter's size requisition */
|
||||
cb_button_set_text_color (CB_BUTTON (button), &white_color);
|
||||
cb_button_set_background_color (CB_BUTTON (button), &yellow_color);
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (clicked), NULL);
|
||||
|
||||
align_x_constraint = clutter_align_constraint_new (stage,
|
||||
CLUTTER_ALIGN_X_AXIS,
|
||||
0.5);
|
||||
|
||||
align_y_constraint = clutter_align_constraint_new (stage,
|
||||
CLUTTER_ALIGN_Y_AXIS,
|
||||
0.5);
|
||||
|
||||
clutter_actor_add_constraint (button, align_x_constraint);
|
||||
clutter_actor_add_constraint (button, align_y_constraint);
|
||||
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (stage), button);
|
||||
|
||||
clutter_actor_show (stage);
|
||||
|
||||
clutter_main ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
451
doc/cookbook/examples/cb-button.c
Normal file
451
doc/cookbook/examples/cb-button.c
Normal file
@ -0,0 +1,451 @@
|
||||
#include "cb-button.h"
|
||||
|
||||
/**
|
||||
* SECTION:cb-button
|
||||
* @short_description: Button widget
|
||||
*
|
||||
* A button widget with support for a text label and background color.
|
||||
*/
|
||||
|
||||
/* convenience macro for GType implementations; see:
|
||||
* http://library.gnome.org/devel/gobject/2.27/gobject-Type-Information.html#G-DEFINE-TYPE:CAPS
|
||||
*/
|
||||
G_DEFINE_TYPE (CbButton, cb_button, CLUTTER_TYPE_ACTOR);
|
||||
|
||||
/* macro for accessing the object's private structure */
|
||||
#define CB_BUTTON_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CB_TYPE_BUTTON, CbButtonPrivate))
|
||||
|
||||
/* private structure - should only be accessed through the public API;
|
||||
* this is used to store member variables whose properties
|
||||
* need to be accessible from the implementation; for example, if we
|
||||
* intend to create wrapper functions which modify properties on the
|
||||
* actors composing an object, we should keep a reference to the actors
|
||||
* here
|
||||
*
|
||||
* this is also the place where other state variables go:
|
||||
* for example, you might record the current state of the button
|
||||
* (toggled on or off) or a background image
|
||||
*/
|
||||
struct _CbButtonPrivate
|
||||
{
|
||||
ClutterActor *child;
|
||||
ClutterActor *label;
|
||||
ClutterAction *click_action;
|
||||
gchar *text;
|
||||
};
|
||||
|
||||
/* enumerates property identifiers for this class;
|
||||
* note that property identifiers should be non-zero integers,
|
||||
* so we add an unused PROP_0 to occupy the 0 position in the enum
|
||||
*/
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_TEXT
|
||||
};
|
||||
|
||||
/* enumerates signal identifiers for this class;
|
||||
* LAST_SIGNAL is not used as a signal identifier, but is instead
|
||||
* used to delineate the size of the cache array for signals (see below)
|
||||
*/
|
||||
enum {
|
||||
CLICKED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
/* cache array for signals */
|
||||
static guint cb_button_signals[LAST_SIGNAL] = { 0, };
|
||||
|
||||
/* from http://mail.gnome.org/archives/gtk-devel-list/2004-July/msg00158.html:
|
||||
*
|
||||
* "The finalize method finishes releasing the remaining
|
||||
* resources just before the object itself will be freed from memory, and
|
||||
* therefore it will only be called once. The two step process helps break
|
||||
* cyclic references. Both dispose and finalize must chain up to their
|
||||
* parent objects by calling their parent's respective methods *after* they
|
||||
* have disposed or finalized their own members."
|
||||
*/
|
||||
static void
|
||||
cb_button_finalize (GObject *gobject)
|
||||
{
|
||||
CbButtonPrivate *priv = CB_BUTTON (gobject)->priv;
|
||||
|
||||
g_free (priv->text);
|
||||
|
||||
/* call the parent class' finalize() method */
|
||||
G_OBJECT_CLASS (cb_button_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
/* enables objects to be uniformly treated as GObjects;
|
||||
* also exposes properties so they become scriptable, e.g.
|
||||
* through ClutterScript
|
||||
*/
|
||||
static void
|
||||
cb_button_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
CbButton *button = CB_BUTTON (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_TEXT:
|
||||
cb_button_set_text (button, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* enables objects to be uniformly treated as GObjects */
|
||||
static void
|
||||
cb_button_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
CbButtonPrivate *priv = CB_BUTTON (gobject)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_TEXT:
|
||||
g_value_set_string (value, priv->text);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ClutterActor implementation
|
||||
*
|
||||
* we only implement destroy(), get_preferred_height(), get_preferred_width(),
|
||||
* allocate(), and paint(), as this is the minimum we can get away with
|
||||
*/
|
||||
|
||||
/* composite actors should implement destroy(), and inside their
|
||||
* implementation destroy any actors they are composed from;
|
||||
* in this case, we just destroy the child ClutterBox
|
||||
*/
|
||||
static void
|
||||
cb_button_destroy (ClutterActor *self)
|
||||
{
|
||||
CbButtonPrivate *priv = CB_BUTTON (self)->priv;
|
||||
|
||||
/* we just destroy the child, and let the child
|
||||
* deal with destroying _its_ children; note that we have a guard
|
||||
* here in case the child has already been destroyed
|
||||
*/
|
||||
if (priv->child)
|
||||
{
|
||||
clutter_actor_destroy (priv->child);
|
||||
priv->child = NULL;
|
||||
}
|
||||
|
||||
/* chain up to destroy() on the parent ClutterActorClass;
|
||||
* note that we check the parent class has a destroy() implementation
|
||||
* before calling it
|
||||
*/
|
||||
if (CLUTTER_ACTOR_CLASS (cb_button_parent_class)->destroy)
|
||||
CLUTTER_ACTOR_CLASS (cb_button_parent_class)->destroy (self);
|
||||
}
|
||||
|
||||
/* get_preferred_height and get_preferred_width defer to the
|
||||
* internal ClutterBox, adding 20px padding on each axis;
|
||||
* min_*_p is the minimum height or width the actor should occupy
|
||||
* to be useful; natural_*_p is the height or width the actor
|
||||
* would occupy if not constrained
|
||||
*
|
||||
* note that if we required explicit sizing for CbButtons
|
||||
* (i.e. a developer must set their height and width),
|
||||
* we wouldn't need to implement these functions
|
||||
*/
|
||||
static void
|
||||
cb_button_get_preferred_height (ClutterActor *self,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
CbButtonPrivate *priv = CB_BUTTON (self)->priv;
|
||||
|
||||
clutter_actor_get_preferred_height (priv->child,
|
||||
for_width,
|
||||
min_height_p,
|
||||
natural_height_p);
|
||||
|
||||
*min_height_p += 20.0;
|
||||
*natural_height_p += 20.0;
|
||||
}
|
||||
|
||||
static void
|
||||
cb_button_get_preferred_width (ClutterActor *self,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
CbButtonPrivate *priv = CB_BUTTON (self)->priv;
|
||||
|
||||
clutter_actor_get_preferred_width (priv->child,
|
||||
for_height,
|
||||
min_width_p,
|
||||
natural_width_p);
|
||||
|
||||
*min_width_p += 20.0;
|
||||
*natural_width_p += 20.0;
|
||||
}
|
||||
|
||||
/* use the actor's allocation for the ClutterBox */
|
||||
static void
|
||||
cb_button_allocate (ClutterActor *actor,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
CbButtonPrivate *priv = CB_BUTTON (actor)->priv;
|
||||
ClutterActorBox child_box = { 0, };
|
||||
|
||||
/* set the allocation for the whole button */
|
||||
CLUTTER_ACTOR_CLASS (cb_button_parent_class)->allocate (actor, box, flags);
|
||||
|
||||
/* make the child (the ClutterBox) fill the parent;
|
||||
* note that this allocation box is relative to the
|
||||
* coordinates of the whole button actor, so we can't just
|
||||
* use the box passed into this function; instead, it
|
||||
* is adjusted to span the whole of the actor, from its
|
||||
* top-left corner (0,0) to its bottom-right corner
|
||||
* (width,height)
|
||||
*/
|
||||
child_box.x1 = 0.0;
|
||||
child_box.y1 = 0.0;
|
||||
child_box.x2 = clutter_actor_box_get_width (box);
|
||||
child_box.y2 = clutter_actor_box_get_height (box);
|
||||
|
||||
clutter_actor_allocate (priv->child, &child_box, flags);
|
||||
}
|
||||
|
||||
/* paint function implementation: just calls paint() on the ClutterBox */
|
||||
static void
|
||||
cb_button_paint (ClutterActor *actor)
|
||||
{
|
||||
CbButtonPrivate *priv = CB_BUTTON (actor)->priv;
|
||||
|
||||
clutter_actor_paint (priv->child);
|
||||
}
|
||||
|
||||
/* proxy ClickAction signals so they become signals from the actor */
|
||||
static void
|
||||
cb_button_clicked (ClutterClickAction *action,
|
||||
ClutterActor *actor,
|
||||
gpointer user_data)
|
||||
{
|
||||
/* emit signal via the cache array */
|
||||
g_signal_emit (actor, cb_button_signals[CLICKED], 0);
|
||||
}
|
||||
|
||||
/* GObject class and instance initialization functions; note that
|
||||
* these have been placed after the Clutter implementation, as
|
||||
* they refer to the static function implementations above
|
||||
*/
|
||||
|
||||
/* class init: attach functions to superclasses, define properties
|
||||
* and signals
|
||||
*/
|
||||
static void
|
||||
cb_button_class_init (CbButtonClass *klass)
|
||||
{
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
gobject_class->finalize = cb_button_finalize;
|
||||
gobject_class->set_property = cb_button_set_property;
|
||||
gobject_class->get_property = cb_button_get_property;
|
||||
|
||||
actor_class->destroy = cb_button_destroy;
|
||||
actor_class->get_preferred_height = cb_button_get_preferred_height;
|
||||
actor_class->get_preferred_width = cb_button_get_preferred_width;
|
||||
actor_class->allocate = cb_button_allocate;
|
||||
actor_class->paint = cb_button_paint;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (CbButtonPrivate));
|
||||
|
||||
/**
|
||||
* CbButton:text:
|
||||
*
|
||||
* The text shown on the #CbButton
|
||||
*/
|
||||
pspec = g_param_spec_string ("text",
|
||||
"Text",
|
||||
"Text of the button",
|
||||
NULL,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
|
||||
|
||||
/**
|
||||
* CbButton::clicked:
|
||||
* @button: the #CbButton that emitted the signal
|
||||
*
|
||||
* The ::clicked signal is emitted when the internal #ClutterClickAction
|
||||
* associated with a #CbButton emits its own ::clicked signal
|
||||
*/
|
||||
cb_button_signals[CLICKED] =
|
||||
g_signal_new ("clicked",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (CbButtonClass, clicked),
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
}
|
||||
|
||||
/* object init: create a private structure and pack
|
||||
* composed ClutterActors into it
|
||||
*/
|
||||
static void
|
||||
cb_button_init (CbButton *self)
|
||||
{
|
||||
CbButtonPrivate *priv;
|
||||
ClutterLayoutManager *layout;
|
||||
|
||||
priv = self->priv = CB_BUTTON_GET_PRIVATE (self);
|
||||
|
||||
clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
|
||||
|
||||
/* the only child of this actor is a ClutterBox with a
|
||||
* ClutterBinLayout: painting and allocation of the actor basically
|
||||
* involves painting and allocating this child box
|
||||
*/
|
||||
layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
|
||||
CLUTTER_BIN_ALIGNMENT_CENTER);
|
||||
|
||||
priv->child = clutter_box_new (layout);
|
||||
|
||||
/* set the parent of the ClutterBox to this instance */
|
||||
clutter_actor_set_parent (priv->child,
|
||||
CLUTTER_ACTOR (self));
|
||||
|
||||
/* add text label to the button; see the ClutterText API docs
|
||||
* for more information about available properties
|
||||
*/
|
||||
priv->label = g_object_new (CLUTTER_TYPE_TEXT,
|
||||
"line-alignment", PANGO_ALIGN_CENTER,
|
||||
"ellipsize", PANGO_ELLIPSIZE_END,
|
||||
NULL);
|
||||
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (priv->child),
|
||||
priv->label);
|
||||
|
||||
/* add a ClutterClickAction on this actor, so we can proxy its
|
||||
* "clicked" signal into a signal from this actor
|
||||
*/
|
||||
priv->click_action = clutter_click_action_new ();
|
||||
clutter_actor_add_action (CLUTTER_ACTOR (self), priv->click_action);
|
||||
|
||||
g_signal_connect (priv->click_action,
|
||||
"clicked",
|
||||
G_CALLBACK (cb_button_clicked),
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* public API */
|
||||
/* examples of public API functions which wrap functions
|
||||
* on internal actors
|
||||
*/
|
||||
|
||||
/**
|
||||
* cb_button_set_text:
|
||||
* @self: a #CbButton
|
||||
* @text: the text to display on the button
|
||||
*
|
||||
* Set the text on the button
|
||||
*/
|
||||
void
|
||||
cb_button_set_text (CbButton *self,
|
||||
const gchar *text)
|
||||
{
|
||||
CbButtonPrivate *priv;
|
||||
|
||||
/* public API should check its arguments;
|
||||
* see also g_return_val_if_fail for functions which
|
||||
* return a value
|
||||
*/
|
||||
g_return_if_fail (CB_IS_BUTTON (self));
|
||||
|
||||
priv = self->priv;
|
||||
|
||||
g_free (priv->text);
|
||||
|
||||
if (text)
|
||||
priv->text = g_strdup (text);
|
||||
else
|
||||
priv->text = g_strdup ("");
|
||||
|
||||
/* call a function on the ClutterText inside the layout */
|
||||
clutter_text_set_text (CLUTTER_TEXT (priv->label), priv->text);
|
||||
}
|
||||
|
||||
/**
|
||||
* cb_button_set_background_color:
|
||||
* @self: a #CbButton
|
||||
* @color: the #ClutterColor to use for the button's background
|
||||
*
|
||||
* Set the color of the button's background
|
||||
*/
|
||||
void
|
||||
cb_button_set_background_color (CbButton *self,
|
||||
const ClutterColor *color)
|
||||
{
|
||||
g_return_if_fail (CB_IS_BUTTON (self));
|
||||
|
||||
clutter_box_set_color (CLUTTER_BOX (self->priv->child), color);
|
||||
}
|
||||
|
||||
/**
|
||||
* cb_button_set_text_color:
|
||||
* @self: a #CbButton
|
||||
* @color: the #ClutterColor to use as the color for the button text
|
||||
*
|
||||
* Set the color of the text on the button
|
||||
*/
|
||||
void
|
||||
cb_button_set_text_color (CbButton *self,
|
||||
const ClutterColor *color)
|
||||
{
|
||||
g_return_if_fail (CB_IS_BUTTON (self));
|
||||
|
||||
clutter_text_set_color (CLUTTER_TEXT (self->priv->label), color);
|
||||
}
|
||||
|
||||
/**
|
||||
* cb_button_get_text:
|
||||
* @self: a #CbButton
|
||||
*
|
||||
* Get the text displayed on the button
|
||||
*
|
||||
* Returns: the button's text. This must not be freed by the application.
|
||||
*/
|
||||
G_CONST_RETURN gchar *
|
||||
cb_button_get_text (CbButton *self)
|
||||
{
|
||||
g_return_val_if_fail (CB_IS_BUTTON (self), NULL);
|
||||
|
||||
return self->priv->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* cb_button_new:
|
||||
*
|
||||
* Creates a new #CbButton instance
|
||||
*
|
||||
* Returns: a new #CbButton
|
||||
*/
|
||||
ClutterActor *
|
||||
cb_button_new (void)
|
||||
{
|
||||
return g_object_new (CB_TYPE_BUTTON, NULL);
|
||||
}
|
84
doc/cookbook/examples/cb-button.h
Normal file
84
doc/cookbook/examples/cb-button.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* inclusion guard */
|
||||
#ifndef __CB_BUTTON_H__
|
||||
#define __CB_BUTTON_H__
|
||||
|
||||
/* include any dependencies */
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
/* GObject implementation */
|
||||
|
||||
/* declare this function signature to remove compilation errors with -Wall;
|
||||
* the cb_button_get_type() function is actually added via the
|
||||
* G_DEFINE_TYPE macro in the .c file
|
||||
*/
|
||||
GType cb_button_get_type (void);
|
||||
|
||||
/* GObject type macros */
|
||||
/* returns the class type identifier (GType) for CbButton */
|
||||
#define CB_TYPE_BUTTON (cb_button_get_type ())
|
||||
|
||||
/* cast obj to a CbButton object structure*/
|
||||
#define CB_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CB_TYPE_BUTTON, CbButton))
|
||||
|
||||
/* check whether obj is a CbButton */
|
||||
#define CB_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CB_TYPE_BUTTON))
|
||||
|
||||
/* cast klass to CbButtonClass class structure */
|
||||
#define CB_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CB_TYPE_BUTTON, CbButtonClass))
|
||||
|
||||
/* check whether klass is a member of the CbButtonClass */
|
||||
#define CB_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CB_TYPE_BUTTON))
|
||||
|
||||
/* get the CbButtonClass structure for a CbButton obj */
|
||||
#define CB_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CB_TYPE_BUTTON, CbButtonClass))
|
||||
|
||||
/*
|
||||
* Private instance fields; see
|
||||
* http://www.gotw.ca/gotw/024.htm for the rationale
|
||||
*/
|
||||
typedef struct _CbButtonPrivate CbButtonPrivate;
|
||||
typedef struct _CbButton CbButton;
|
||||
typedef struct _CbButtonClass CbButtonClass;
|
||||
|
||||
/* object structure */
|
||||
struct _CbButton
|
||||
{
|
||||
/*<private>*/
|
||||
ClutterActor parent_instance;
|
||||
|
||||
/* structure containing private members */
|
||||
/*<private>*/
|
||||
CbButtonPrivate *priv;
|
||||
};
|
||||
|
||||
/* class structure */
|
||||
struct _CbButtonClass
|
||||
{
|
||||
/* signals */
|
||||
void (* clicked)(CbButton *button);
|
||||
|
||||
/*<private>*/
|
||||
ClutterActorClass parent_class;
|
||||
};
|
||||
|
||||
/* public API */
|
||||
|
||||
/* constructor - note this returns a ClutterActor instance */
|
||||
ClutterActor *cb_button_new (void);
|
||||
|
||||
/* getter */
|
||||
G_CONST_RETURN gchar * cb_button_get_text (CbButton *self);
|
||||
|
||||
/* setters - these are wrappers round functions
|
||||
* which change properties of the internal actors
|
||||
*/
|
||||
void cb_button_set_text (CbButton *self,
|
||||
const gchar *text);
|
||||
|
||||
void cb_button_set_background_color (CbButton *self,
|
||||
const ClutterColor *color);
|
||||
|
||||
void cb_button_set_text_color (CbButton *self,
|
||||
const ClutterColor *color);
|
||||
|
||||
#endif /* __CB_BUTTON_H__ */
|
Loading…
Reference in New Issue
Block a user