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:
Emmanuele Bassi 2011-01-31 13:58:12 +00:00
commit bb44d51fc9
5 changed files with 917 additions and 1 deletions

View File

@ -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>

View File

@ -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

View 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;
}

View 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);
}

View 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__ */