cookbook: Split the cookbook chapters
Use separate files, to avoid making the XML too big and uneditable. This breaks the PDF build.
This commit is contained in:
parent
9c11538bee
commit
089b9b545e
@ -1,10 +1,21 @@
|
||||
NULL =
|
||||
|
||||
HTML_DIR = $(datadir)/gtk-doc/html
|
||||
TARGET_DIR = $(HTML_DIR)/clutter-cookbook
|
||||
|
||||
XML_FILES = \
|
||||
actors.xml \
|
||||
animations.xml \
|
||||
events.xml \
|
||||
introduction.xml \
|
||||
recipe-template.xml \
|
||||
textures.xml \
|
||||
$(NULL)
|
||||
|
||||
HTML_FILES = html/*.html
|
||||
IMAGE_FILES = images/clutter-logo.png
|
||||
|
||||
EXTRA_DIST = clutter-cookbook.xml.in $(IMAGE_FILES)
|
||||
EXTRA_DIST = clutter-cookbook.xml.in $(IMAGE_FILES) $(XML_FILES)
|
||||
|
||||
CLEANFILES = \
|
||||
pdf-build.stamp \
|
||||
|
260
doc/cookbook/actors.xml
Normal file
260
doc/cookbook/actors.xml
Normal file
@ -0,0 +1,260 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<chapter id="actors">
|
||||
<title>Actors</title>
|
||||
|
||||
<epigraph>
|
||||
<attribution>Edmon Gween, actor, on his deathbed</attribution>
|
||||
<para>An actor's a guy who if you ain't talkin' about him, ain't
|
||||
listening.</para>
|
||||
</epigraph>
|
||||
|
||||
<section id="actors-introduction">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>When building a User Interface with Clutter, the visible part
|
||||
of the UI — that is, what is displayed on the screen — is
|
||||
commonly referred to as "the scene graph". Like every graph, a scene
|
||||
graph is composed by nodes.</para>
|
||||
|
||||
<para>Every node on the Clutter scene graph is an
|
||||
<emphasis>actor</emphasis>. Every actor has a single relationship
|
||||
with the others: it can be the parent of another actor, or a child of
|
||||
another actor.</para>
|
||||
|
||||
<note><para>The Stage is an actor that can have children but cannot have
|
||||
any parent.</para></note>
|
||||
|
||||
<para>Actors have different attributes: a position, a size, a
|
||||
scale factor, a rotation angle on each axis (relative to a specific
|
||||
center on the normal plane for that axis), an opacity factor.</para>
|
||||
|
||||
<para>The scene graph is not fixed: it can be changed, not only
|
||||
by adding or removing actors, but also by changing the parent-child
|
||||
relationship: it is possible, for instance, to move an entire
|
||||
section of the scene graph from one parent actor to another.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="actors-allocation-notify">
|
||||
<title>Knowing when an actor position or size change</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
|
||||
<para>You want to know when the position or the size, or
|
||||
both, of an actor change, for instance to update an unrelated
|
||||
actor or some internal state.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution</title>
|
||||
|
||||
<para>You can use the <emphasis>notify</emphasis> signal,
|
||||
detailed with the coordinate or the dimension you want
|
||||
to know has changed:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
g_signal_connect (actor, "notify::x",
|
||||
G_CALLBACK (on_x_changed),
|
||||
NULL);
|
||||
g_signal_connect (actor, "notify::height",
|
||||
G_CALLBACK (on_height_changed),
|
||||
NULL);
|
||||
g_signal_connect (actor, "notify::depth",
|
||||
G_CALLBACK (on_depth_changed),
|
||||
NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>If you want to know if any of the coordinates or dimensions of
|
||||
an actor have been changed, except for depth, you can use the
|
||||
<emphasis>allocation-changed</emphasis> signal:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
g_signal_connect (actor, "allocation-changed",
|
||||
G_CALLBACK (on_allocation_changed),
|
||||
NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>The signature for the handler of the "notify" signal is:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
void
|
||||
on_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>While the signature for the handler of the "allocation-changed"
|
||||
signal is:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
void
|
||||
on_allocation_changed (ClutterActor *actor,
|
||||
const ClutterActorBox *allocation,
|
||||
ClutterAllocationFlags flags,
|
||||
gpointer user_data);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<para>Any change the position and size of an actor will cause a
|
||||
change in the allocation of the actor itself. This will update the
|
||||
values of the :x, :y, :width and :height properties as well.</para>
|
||||
|
||||
<para>The first technique allows a greater deal of granularity,
|
||||
allowing you to know what exactly changed. Inside the callback
|
||||
for the signal you can query the value of the property:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
void
|
||||
on_x_changed (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
gint x_value = 0;
|
||||
|
||||
/* Round the X coordinate to the nearest pixel */
|
||||
x_value = floorf (clutter_actor_get_x (CLUTTER_ACTOR (gobject))) + 0.5;
|
||||
|
||||
g_print ("The new X coordinate is '%d' pixels\n", x_value);
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>The second technique is more indicated if you want to
|
||||
get notification that any of the positional or dimensional
|
||||
attributes changed, except for the depth:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
void
|
||||
on_allocation_changed (ClutterActor *actor,
|
||||
const ClutterActorBox *allocation,
|
||||
ClutterAllocationFlags flags,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterActor *actor = CLUTTER_ACTOR (gobject);
|
||||
|
||||
g_print ("The bounding box is now: (%.2f, %.2f) (%.2f x %.2f)\n",
|
||||
clutter_actor_box_get_x (allocation),
|
||||
clutter_actor_box_get_y (allocation),
|
||||
clutter_actor_box_get_width (allocation),
|
||||
clutter_actor_box_get_height (allocation));
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>All actors will update these properties when their size
|
||||
or position change.</para>
|
||||
|
||||
<para>Note that the Stage, on the other hand, will not notify on
|
||||
position changes, so it is not possible to use the :x and :y
|
||||
properties to know that the platform-specific window embedding the
|
||||
stage has been moved — if the platform supports a windowing
|
||||
system. In order to achieve that you will have to use backend-specific
|
||||
API to extract the surface used by the Stage and then platform-specific
|
||||
API to retrieve its coordinates.</para>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="actors-paint-wrappers">
|
||||
<title>Overriding the paint sequence</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
|
||||
<para>You want to override the way an actor paints itself
|
||||
without creating a subclass.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution</title>
|
||||
|
||||
<para>You can use the <emphasis>paint</emphasis> signal to
|
||||
invoke a callback that will be executed before the actor's
|
||||
paint implementation:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
g_signal_connect (actor, "paint", G_CALLBACK (on_paint), NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>You can paint something after the actor's paint implementation
|
||||
by using the <function>g_signal_connect_after()</function> function
|
||||
instead of <function>g_signal_connect()</function>:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
g_signal_connect_after (actor, "paint", G_CALLBACK (on_paint_after), NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>The signature for the handler of the "paint" signal is:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
void on_paint (ClutterActor *actor, gpointer user_data);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<para>The paint cycle in Clutter works its way recursively from the
|
||||
Stage through every child.</para>
|
||||
|
||||
<para>Whenever an Actor is going to be painted it will be positioned in
|
||||
a new frame of reference according to the list of transformations
|
||||
(scaling, rotation and additional traslations). After that, the "paint"
|
||||
signal will be emitted.</para>
|
||||
|
||||
<para>The "paint" signal is defined as <emphasis>run-last</emphasis>,
|
||||
that is the signal handlers connected to it using
|
||||
<function>g_signal_connetc()</function> will be called first; then the
|
||||
default handler defined by the Actor's sub-class will be called;
|
||||
finally, all the signal handlers connected to the signal using
|
||||
<function>g_signal_connect_after()</function> will be called.</para>
|
||||
|
||||
<para>This allows pre- and post-default paint handlers, and it also
|
||||
allows completely overriding the way an Actor draws itself by default;
|
||||
for instance:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
void
|
||||
on_paint (ClutterActor *actor)
|
||||
{
|
||||
do_my_paint (actor);
|
||||
|
||||
g_signal_stop_emission_by_name (actor, "paint");
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>The code above will prevent the default paint implementation of
|
||||
the actor from running.</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
|
42
doc/cookbook/animations.xml
Normal file
42
doc/cookbook/animations.xml
Normal file
@ -0,0 +1,42 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<chapter id="animations">
|
||||
<title>Animations</title>
|
||||
|
||||
<epigraph>
|
||||
<attribution>the author of the epigraph</attribution>
|
||||
<para>a short epigraph</para>
|
||||
</epigraph>
|
||||
|
||||
<section id="animations-introduction">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>introduction</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Inverting Animations</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
|
||||
<para>You want to have an animation exactly mirroring another one
|
||||
that you just played.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution</title>
|
||||
|
||||
<para>...</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<para>...</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
</chapter>
|
File diff suppressed because it is too large
Load Diff
337
doc/cookbook/events.xml
Normal file
337
doc/cookbook/events.xml
Normal file
@ -0,0 +1,337 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<chapter id="events">
|
||||
<title>Events</title>
|
||||
|
||||
<epigraph>
|
||||
<attribution>The Tenth Doctor (David Tennant)</attribution>
|
||||
<para>Crossing into established events is strictly forbidden. Except for
|
||||
cheap tricks.</para>
|
||||
</epigraph>
|
||||
|
||||
<section id="events-introduction">
|
||||
<title>Introduction</title>
|
||||
<para>Once you have set up a scene on the stage, in order to respond
|
||||
to user interaction you will have to handle events coming from the
|
||||
underlying platform.</para>
|
||||
|
||||
<para>Events are relayed to actors by Clutter in form of
|
||||
<emphasis>signals</emphasis>; signals are a facility provided by the
|
||||
GObject framework to call functions depending on a unique name. A signal
|
||||
can be thought as a message that an object instance broadcasts to various
|
||||
listener functions.</para>
|
||||
|
||||
<para>There are various events that Clutter will handle: mostly, they
|
||||
deal with input devices, like a mouse pointer or a keyboard; but they can
|
||||
also come from the windowing system, like the
|
||||
<emphasis>delete-event</emphasis> signal that is emitted when the user
|
||||
closes the window of the stage.</para>
|
||||
|
||||
<para>Each event has a particular <emphasis>source</emphasis>, that is
|
||||
the actor that received the event. The event handling sequence is divided
|
||||
in two phases:</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem><para>the <emphasis>capture</emphasis> phase, which consists
|
||||
in an emission of the <emphasis>captured-event</emphasis> signal
|
||||
starting from the stage to, following the parent-child relationship,
|
||||
the source of the event;</para></listitem>
|
||||
<listitem><para>the <emphasis>bubble</emphasis> phase, which consists
|
||||
in an emission of the <emphasis>event</emphasis> signal starting from
|
||||
the the source of the event to, following the parent-child
|
||||
relationship, the stage.</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>At any point during the event emission sequence a handler of either
|
||||
the captured-event or the event signals can stop it, by returning a value
|
||||
of TRUE, which means that the event has been handled. If an event hasn't
|
||||
been handled, FALSE should be returned instead.</para>
|
||||
</section>
|
||||
|
||||
<section id="events-handling-key-events">
|
||||
<title>Handling key events</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
<para>You want to respond to key presses on an actor.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solutions</title>
|
||||
|
||||
<para>There are two possible solutions:</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para><emphasis>Solution 1:</emphasis> Connect a callback to the
|
||||
actor; inside the callback, manually analyse which key and
|
||||
modifier(s) were pressed and react accordingly.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><emphasis>Solution 2:</emphasis> Use an actor's
|
||||
ClutterBindingPool to declaratively assign actions to specific
|
||||
key and modifier combinations.</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>Each solution is covered below.</para>
|
||||
|
||||
<section>
|
||||
<title>Solution 1</title>
|
||||
|
||||
<para>Connect the key-press-event signal for an actor to a callback;
|
||||
then examine the event in the callback to determine which key and
|
||||
modifiers were pressed.</para>
|
||||
|
||||
<para>First, connect an actor's key-press-event signal
|
||||
to a callback:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
g_signal_connect (actor, "key-press-event", G_CALLBACK (_key_press_cb), NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Then, in the callback, check which key was pressed and which
|
||||
modifiers were down at the same time. For example, this callback
|
||||
checks for a press on the up arrow key and whether
|
||||
the Shift and/or Control key were down:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
static gboolean
|
||||
_key_press_cb (ClutterActor *actor,
|
||||
ClutterEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
guint keyval = clutter_event_get_key_symbol (event);
|
||||
|
||||
if (CLUTTER_Up == keyval)
|
||||
{
|
||||
ClutterModifierType modifiers = clutter_event_get_state (event);
|
||||
|
||||
switch (modifiers)
|
||||
{
|
||||
case CLUTTER_SHIFT_MASK:
|
||||
g_debug ("Up and shift pressed");
|
||||
break;
|
||||
|
||||
case CLUTTER_SHIFT_MASK + CLUTTER_CONTROL_MASK:
|
||||
g_debug ("Up and shift and control pressed");
|
||||
break;
|
||||
|
||||
default:
|
||||
g_debug ("Up pressed");
|
||||
break;
|
||||
}
|
||||
|
||||
/* The event was handled, and the emission should stop */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* The event was not handled, and the emission should continue */
|
||||
return FALSE;
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Note that Clutter provides a range of key value definitions
|
||||
(like CLUTTER_Up, used above). These are generated from the list in the
|
||||
<ulink url="http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h">X.Org source code</ulink>
|
||||
(replace "XK" with "CLUTTER" in the definitions there to get the
|
||||
CLUTTER equivalents; alternatively, look at the clutter-keysyms.h
|
||||
header file for the list).</para>
|
||||
|
||||
<para>CLUTTER_SHIFT_MASK, CLUTTER_CONTROL_MASK and other modifiers are
|
||||
defined in the ClutterModifierType enum.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution 2</title>
|
||||
|
||||
<para>Assign actions to an actor's ClutterBindingPool.
|
||||
A binding pool stores mappings from a key press (either a single key
|
||||
or a key plus modifiers) to actions; an action is simply a callback
|
||||
function with a specific signature.</para>
|
||||
|
||||
<para>While this approach is trickier to implement, it is more
|
||||
flexible and removes the drudgery of writing branching code to
|
||||
handle different key presses. See the Discussion section for
|
||||
more details.</para>
|
||||
|
||||
<para>To use this approach with an actor which will receive key press
|
||||
events, first get that actor's binding pool. In the example below,
|
||||
we're using the binding pool for the default ClutterStage:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
ClutterBindingPool *binding_pool;
|
||||
GObjectClass *stage_class;
|
||||
|
||||
stage_class = CLUTTER_STAGE_GET_CLASS (stage);
|
||||
binding_pool = clutter_binding_pool_get_for_class (stage_class);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Next, install actions into the binding pool. For example, to
|
||||
install an action bound to the up arrow key, which calls the _move_up
|
||||
function when that key is pressed, you would do:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
clutter_binding_pool_install_action (binding_pool,
|
||||
"move-up", /* identifier */
|
||||
CLUTTER_Up, /* up arrow pressed */
|
||||
0, /* no modifiers pressed */
|
||||
G_CALLBACK (_move_up),
|
||||
NULL, /* no user data passed */
|
||||
NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Another example, binding up + Shift + Control to an action which
|
||||
calls _move_up_shift_control when activated:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
clutter_binding_pool_install_action (binding_pool,
|
||||
"move-up-shift-control",
|
||||
CLUTTER_Up,
|
||||
CLUTTER_SHIFT_MASK + CLUTTER_CONTROL_MASK,
|
||||
G_CALLBACK (_move_up_shift_control),
|
||||
NULL,
|
||||
NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>The function called when an action is activated looks like this
|
||||
(for _move_up):</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
static void
|
||||
_move_up (GObject *instance,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_debug ("Up pressed");
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Then bind the key-press-event for the actor (in our case,
|
||||
the stage) to a callback:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
g_signal_connect (stage,
|
||||
"key-press-event",
|
||||
G_CALLBACK (_key_press_cb),
|
||||
NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Finally, inside the callback, pass control to the actor's
|
||||
binding pool rather than dissecting the key press event
|
||||
yourself:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
static gboolean
|
||||
_key_press_cb (ClutterActor *actor,
|
||||
ClutterEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterBindingPool *pool;
|
||||
|
||||
pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor));
|
||||
|
||||
return clutter_binding_pool_activate (pool,
|
||||
clutter_event_get_key_symbol (event),
|
||||
clutter_event_get_state (event),
|
||||
G_OBJECT (actor));
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Now, when a key plus modifiers that has been bound to an action
|
||||
is pressed on the actor, the appropriate action is activated.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<section>
|
||||
<title>Pros and cons of Solution 1 and Solution 2</title>
|
||||
|
||||
<para>Solution 1 is the simplest (in terms of the amount of code you
|
||||
have to write for simple cases), but could quickly turn into a mess if
|
||||
you need many conditions or want to capture many key combinations.
|
||||
Also, if multiple actors need to respond to key press events, you'll
|
||||
need similar event dissection code in each callback.</para>
|
||||
|
||||
<para>Solution 2 is more complicated to implement, but scales better
|
||||
if you have many different key combinations on multiple actors.
|
||||
The binding pool protects you from the minutiae of detecting which
|
||||
keys were pressed, leaving you to concentrate on the
|
||||
triggered actions instead. This could simplify your control
|
||||
logic.</para>
|
||||
|
||||
<para>In addition, Solution 2 lets you write a single callback to
|
||||
handle all key press events for all actors. This callback could then
|
||||
use clutter_binding_pool_find (as in the example code) to determine
|
||||
which binding pool to activate (depending on which actor received the
|
||||
key press event).</para>
|
||||
|
||||
<para>Finally, a binding pool allows you to block and unblock actions.
|
||||
This means you can make the response to a key press event conditional
|
||||
on application state. For example, let's say you wanted the up arrow
|
||||
key to move an actor, but only when the actor is at the bottom
|
||||
of the stage. To implement this, you could disable the up arrow key
|
||||
action in the binding pool initially; then, once the actor reaches the
|
||||
bottom of the stage, enable the up arrow key action again. While this
|
||||
is possible with Solution 1, you would have to implement more of the
|
||||
state management code yourself.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Other useful things to know about key press events</title>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>A ClutterKeyEvent contains only a <emphasis>single</emphasis>
|
||||
key value, plus possibly one or more modifier keys (like Shift,
|
||||
Control, Alt etc.). There are no functions in the
|
||||
Clutter API which return events for tracking near-simultaneous
|
||||
presses on multiple keys.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>By default, the stage receives all key events.
|
||||
To make another actor receive key events, use
|
||||
clutter_stage_set_key_focus:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
/*
|
||||
* stage is a ClutterStage instance;
|
||||
* actor is the ClutterActor instance which should receive key events
|
||||
*/
|
||||
clutter_stage_set_key_focus (stage, actor);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
</chapter>
|
103
doc/cookbook/introduction.xml
Normal file
103
doc/cookbook/introduction.xml
Normal file
@ -0,0 +1,103 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
|
||||
<!ENTITY appurl "http://www.clutter-project.org">
|
||||
<!ENTITY docurl "http://docs.clutter-project.org/docs/clutter-cookbook">
|
||||
]>
|
||||
|
||||
<chapter id="introduction">
|
||||
<title>Preface</title>
|
||||
|
||||
<epigraph>
|
||||
<attribution>The Perl Cookbook</attribution>
|
||||
<para>Let me show you that easy way, so others may easily follow.</para>
|
||||
</epigraph>
|
||||
|
||||
<para>There is a wonderful simile in the preface of the <emphasis>Perl
|
||||
Cookbook</emphasis>: approaching a programming problem is oftentimes
|
||||
similar to balancing Columbus's egg. The initial difficulties of dealing
|
||||
with, and more importantly solving, problems in the computer programming
|
||||
field sometimes can only be overcome if somebody shows you how to use a
|
||||
new tool. This is true for programming languages but also for programming
|
||||
libraries.</para>
|
||||
|
||||
<para>This book has been written to try and give you a reference on
|
||||
how to solve common issues that you might have to face when using
|
||||
the Clutter toolkit.</para>
|
||||
|
||||
<para>This book is not meant to be a replacement for the API reference,
|
||||
even though there will be descriptions of how Clutter works and how
|
||||
its API looks like. We will require knowledge of the Clutter API, but
|
||||
we will also point out where to find more information on the API that
|
||||
examples have used.</para>
|
||||
|
||||
<para>Indeed, this book should be used as a companion to the API reference,
|
||||
expanding the examples and showing how to achieve a specific result.</para>
|
||||
|
||||
<para>This is not a book for learning Clutter. This is also not a book
|
||||
for learning C, or GObject or even GUI development.</para>
|
||||
|
||||
<para>Above all, this is a book for learning <emphasis>more</emphasis>
|
||||
about Clutter, and about how to use it in the most efficient and easiest
|
||||
way. It is meant to help you move past the basic usage of Clutter.</para>
|
||||
|
||||
<para>This book is divided into chapters. Each chapter is dedicated to
|
||||
a specific class, like ClutterTexture, or a specific area, like animations.
|
||||
Each chapter starts with a short introduction, followed by different
|
||||
<emphasis>recipes</emphasis>. Each recipe starts with a problem, or a short
|
||||
statement describing what we want to achieve; a solution, containing the
|
||||
source code; and a discussion section, where the code is explained, where
|
||||
alternative approaches might be useful, caveats and references to the
|
||||
Clutter API for furher studying.</para>
|
||||
|
||||
<para>This book, in the cookbook spirit, can be accessed mostly at
|
||||
random.</para>
|
||||
|
||||
<section>
|
||||
<title>About Clutter</title>
|
||||
|
||||
<para>Clutter is an free and open source software library for creating
|
||||
portable, dynamic, compelling and fast graphical user interfaces.</para>
|
||||
|
||||
<para>Clutter uses OpenGL (and, optionally, OpenGL ES on mobile and
|
||||
embedded platforms) for rendering the user interface elements, but
|
||||
at the same time it exposes an application program interface that hides
|
||||
the underlying complexity of the OpenGL state machine from the
|
||||
developer.</para>
|
||||
|
||||
<para>The program interface of Clutter is intended to be easy to use,
|
||||
efficient, flexible and as self-documenting as possible.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>About this document</title>
|
||||
|
||||
<para>This document is available in various formats like HTML, and
|
||||
PDF.</para>
|
||||
|
||||
<para>The latest version is always available at
|
||||
<ulink url="&docurl;">&docurl;</ulink>.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Acknowledgments</title>
|
||||
|
||||
<para>This book has been written taking the inspiration from the Perl
|
||||
Cookbook, authored by Tom Christiansen and Nathan Torkington.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Where to get Clutter</title>
|
||||
|
||||
<para>You can obtain Clutter from <ulink url="&appurl;">&appurl;</ulink>.</para>
|
||||
|
||||
<para>Clutter is also available on all major GNU/Linux distributions,
|
||||
in various package formats.</para>
|
||||
|
||||
<para>On OSX, Clutter is available with both Fink and MacPorts.</para>
|
||||
|
||||
<para>Binaries for Microsoft Windows are also available.</para>
|
||||
</section>
|
||||
|
||||
</chapter>
|
24
doc/cookbook/recipe-template.xml
Normal file
24
doc/cookbook/recipe-template.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<!--
|
||||
<section id="recipe">
|
||||
<title>Recipe</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
|
||||
<para>...</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution</title>
|
||||
|
||||
<para>...</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<para>...</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
-->
|
483
doc/cookbook/textures.xml
Normal file
483
doc/cookbook/textures.xml
Normal file
@ -0,0 +1,483 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<chapter id="textures">
|
||||
<title>Textures</title>
|
||||
|
||||
<epigraph>
|
||||
<attribution>the author of the epigraph</attribution>
|
||||
<para>a short epigraph</para>
|
||||
</epigraph>
|
||||
|
||||
<section id="textures-introduction">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>introduction</para>
|
||||
</section>
|
||||
|
||||
<section id="textures-drawing-with-cairo">
|
||||
<title>Drawing 2D graphics onto a texture</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
|
||||
<para>You want to draw 2D graphics inside a Clutter application.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution</title>
|
||||
|
||||
<para>Create a ClutterCairoTexture, then draw onto the Cairo context
|
||||
it wraps using the Cairo API:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
ClutterActor *texture;
|
||||
cairo_t *cr;
|
||||
|
||||
guint width, height;
|
||||
width = 800;
|
||||
height = 600;
|
||||
|
||||
texture = clutter_cairo_texture_new (width, height);
|
||||
|
||||
cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (texture));
|
||||
|
||||
/*
|
||||
* write onto the Cairo context cr using the Cairo API;
|
||||
* see <ulink url="http://cairographics.org/manual/">the Cairo API reference</ulink> for details
|
||||
*/
|
||||
cairo_move_to (cr, 0, 0);
|
||||
cairo_line_to (cr, 800, 600);
|
||||
cairo_stroke (cr);
|
||||
|
||||
/* does the actual drawing onto the texture */
|
||||
cairo_destroy (cr);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Here's a <ulink url="http://cairographics.org/tutorial/">useful
|
||||
Cairo tutorial</ulink> if you want to learn more about the Cairo API
|
||||
itself.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<para>A ClutterCairoTexture is a standard ClutterActor, so it can be
|
||||
added to ClutterContainers (e.g. a ClutterStage or ClutterGroup),
|
||||
animated, resized etc. in the usual ways.</para>
|
||||
|
||||
<para>Other useful operations:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para><emphasis>To draw on part of the texture:</emphasis>
|
||||
use <function>clutter_cairo_texture_create_region</function> to
|
||||
retrieve a Cairo context for the region you want to draw on.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><emphasis>To clear existing content from a texture:</emphasis>
|
||||
use <function>clutter_cairo_texture_clear</function>.</para>
|
||||
|
||||
<para>You may need to do this as the texture reuses the same
|
||||
Cairo context each time you call
|
||||
<function>clutter_cairo_texture_create</function> or
|
||||
<function>clutter_cairo_texture_create_region</function>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><emphasis>To resize the Cairo context wrapped
|
||||
by a texture</emphasis>, use
|
||||
<function>clutter_cairo_texture_set_surface_size</function>.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<section>
|
||||
<title>Drawing pages from a PDF onto a ClutterCairoContext</title>
|
||||
|
||||
<para>Other libraries may provide an API for writing onto a
|
||||
Cairo context; you can make use of these APIs on the exposed
|
||||
Cairo context of a ClutterCairoTexture. For example, you
|
||||
can use the poppler-glib API to display pages
|
||||
from a PopplerDocument inside a Clutter application:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
#include <poppler/glib/poppler.h>
|
||||
|
||||
/* snipped setup code (as above) */
|
||||
|
||||
/*
|
||||
* cast to CLUTTER_CAIRO_TEXTURE, as the functions
|
||||
* used below require that type
|
||||
*/
|
||||
ClutterCairoTexture *cc_texture = CLUTTER_CAIRO_TEXTURE (texture);
|
||||
|
||||
clutter_cairo_texture_clear (cc_texture);
|
||||
|
||||
gchar *file_uri = "file:///path/to/file.pdf";
|
||||
guint page_num = 0;
|
||||
double page_width, page_height;
|
||||
|
||||
PopplerDocument *doc;
|
||||
PopplerPage *page;
|
||||
GError *error = NULL;
|
||||
|
||||
doc = poppler_document_new_from_file (file_uri, NULL, &error);
|
||||
|
||||
page = poppler_document_get_page (doc, page_num);
|
||||
|
||||
poppler_page_get_size (page, &page_width, &page_height);
|
||||
|
||||
cr = clutter_cairo_texture_create (cc_texture);
|
||||
|
||||
/* render the page to the context */
|
||||
poppler_page_render (page, cr);
|
||||
|
||||
cairo_destroy (cr);
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Note that if the page is larger than the Cairo context,
|
||||
some of it might not be visible. Similarly, if the ClutterCairoTexture
|
||||
is larger than the stage, some of that might not be visible. So you
|
||||
may need to do some work to make the ClutterCairoTexture fit
|
||||
inside the stage properly (e.g. resize the stage), and/or some work
|
||||
to make the PDF page sit inside the Cairo context (e.g. scale the PDF
|
||||
page or put it inside a scrollable actor).</para>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="textures-aspect-ratio">
|
||||
<title>Maintaining the aspect ratio when loading an
|
||||
image into a texture</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
|
||||
<para>You want want to load an image into a texture
|
||||
and scale it, while retaining the underlying image's aspect ratio.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution</title>
|
||||
|
||||
<para>Set the texture to keep the aspect ratio of the
|
||||
underlying image (so it doesn't distort when it's scaled); use
|
||||
the actor's request-mode property to set the correct
|
||||
geometry management (see the discussion section); then
|
||||
resize the texture along one dimension (height or width).
|
||||
Now, when an image is loaded into the texture, the image is
|
||||
scaled to fit the set height or width; the other dimension
|
||||
is automatically scaled by the same factor so the image fits
|
||||
the texture:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
ClutterActor *texture;
|
||||
texture = clutter_texture_new ();
|
||||
|
||||
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture), TRUE);
|
||||
|
||||
/*
|
||||
* this setting means the height of the scaled image is based on its width;
|
||||
* it's not strictly necessary to set this, as this is the default
|
||||
*/
|
||||
clutter_actor_set_request_mode (texture, CLUTTER_REQUEST_HEIGHT_FOR_WIDTH);
|
||||
|
||||
/* set the width, which causes height to be scaled by the same factor */
|
||||
clutter_actor_set_width (texture, 300);
|
||||
|
||||
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
|
||||
"/path/to/image.jpg",
|
||||
NULL);
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<para>The request mode for an actor determines how
|
||||
geometry requisition is performed; in this case, this
|
||||
includes how scaling is applied if you change the actor's
|
||||
width or height. There are two possible values for
|
||||
request-mode:</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>If set to <emphasis>CLUTTER_REQUEST_HEIGHT_FOR_WIDTH</emphasis>
|
||||
(the default), changing the width causes the height
|
||||
to be scaled by the same factor as the width.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>If set to <emphasis>CLUTTER_REQUEST_WIDTH_FOR_HEIGHT</emphasis>,
|
||||
changing the height causes the width to be scaled by the
|
||||
same factor as the height.</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>In the example above, the texture is set to keep its
|
||||
aspect ratio then fixed to a width of 300 pixels; the
|
||||
request-mode is set to CLUTTER_REQUEST_HEIGHT_FOR_WIDTH. If a
|
||||
standard, photo-sized image in landscape orientation were
|
||||
loaded into it (2848 pixels wide x 2136 high), it would be scaled
|
||||
down to 300 pixels wide; then, its height would be scaled by the
|
||||
same factor as the width (i.e. scaled down to 225 pixels).</para>
|
||||
|
||||
<para>With request-mode set to CLUTTER_REQUEST_WIDTH_FOR_HEIGHT,
|
||||
you would get the same effect by setting the height first;
|
||||
then, computation of the width for the scaled image would be
|
||||
based on the scaling factor applied to its height instead.</para>
|
||||
|
||||
<para>You can work out which side of the source image is longest using
|
||||
clutter_texture_base_size() to get its width and height. This can
|
||||
be useful when trying to scale images with different orientations
|
||||
to fit into uniform rows or columns:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
gint width;
|
||||
gint height;
|
||||
|
||||
clutter_texture_get_base_size (CLUTTER_TEXTURE (texture), &width, &height);
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Note that if you explicitly set the size (both width and height)
|
||||
of a texture with clutter_actor_set_size() (or
|
||||
with clutter_actor_set_width() and clutter_actor_set_height()), any
|
||||
image loaded into the texture is automatically stretched/shrunk to
|
||||
fit the texture. This is the case regardless of any other settings
|
||||
(like whether to keep aspect ratio).</para>
|
||||
|
||||
<para>Also note that a texture won't try to fit itself inside the bounds of
|
||||
its parent container: so if it's bigger than its container, only part
|
||||
of it may be visible.</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="textures-image-loading">
|
||||
<title>Loading image data into a texture</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
|
||||
<para>You want to display an image inside a Clutter
|
||||
application.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution</title>
|
||||
|
||||
<para>Create a ClutterTexture directly from an image file:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
ClutterActor *texture;
|
||||
GError *error = NULL;
|
||||
gchar *image_path = "/path/to/image";
|
||||
|
||||
texture = clutter_texture_new_from_file (image_path, &error);
|
||||
|
||||
if (error != NULL)
|
||||
{
|
||||
// handle error
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Or create a texture and set its source to an image
|
||||
file:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
ClutterActor *texture;
|
||||
GError *error = NULL;
|
||||
gchar *image_path = "/path/to/image";
|
||||
gboolean loaded;
|
||||
|
||||
texture = clutter_texture_new ();
|
||||
|
||||
/*
|
||||
* returns FALSE if file could not be loaded or texture
|
||||
* could not be set from image data in the file
|
||||
*/
|
||||
loaded = clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
|
||||
image_path,
|
||||
&error);
|
||||
|
||||
if (error != NULL)
|
||||
{
|
||||
// handle error
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<para>Bear the following in mind when loading images into a
|
||||
texture:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>An image load may fail if:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The file does not exist.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The image format is unsupported: most of the
|
||||
common bitmap formats (PNG, JPEG, BMP, GIF, TIFF, XPM)
|
||||
are supported, but more exotic ones may not be.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Whether you're creating a texture from an image file,
|
||||
or loading an image from a file into an existing texture,
|
||||
you should specify the filesystem path to the file, rather
|
||||
than a URI.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<section>
|
||||
<title>Synchronous vs. asynchronous image loading</title>
|
||||
|
||||
<para>The code examples above show the simplest approach:
|
||||
loading an image into a texture synchronously. This means that
|
||||
the application waits for each image to be loaded before continuing;
|
||||
which is acceptable in this case, but may not be when
|
||||
loading images into multiple textures.</para>
|
||||
|
||||
<para>Another approach is to load data into textures
|
||||
asynchronously. This requires some extra set up in your code:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Call g_thread_init() (from the GLib library) prior
|
||||
to calling clutter_init(), so that a local thread is used
|
||||
to load the file, rather than the main loop. (Note that
|
||||
this is not necessary if you're using GLib version >= 2.24,
|
||||
since GObject initializes threading with the type system.)</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Set the texture to load data asynchronously.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Connect a callback to the texture's load-finished
|
||||
signal to handle any errors which occur during loading,
|
||||
and/or to do extra work if data loads successfully.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The code below shows how to put these together:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
/* callback to invoke when a texture finishes loading image data */
|
||||
static void
|
||||
_load_finished_cb (ClutterTexture *texture,
|
||||
gpointer error,
|
||||
gpointer user_data)
|
||||
{
|
||||
GError *err = error;
|
||||
const gchar *image_path = user_data;
|
||||
|
||||
if (err != NULL)
|
||||
g_warning ("Could not load image from file %s; message: %s",
|
||||
image_path,
|
||||
err->message);
|
||||
else
|
||||
g_debug ("Image loaded from %s", image_path);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
/* initialize GLib's default threading implementation */
|
||||
g_thread_init (NULL);
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
/* ... get stage etc. */
|
||||
|
||||
ClutterActor *texture;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = clutter_texture_new ();
|
||||
|
||||
/* load data asynchronously */
|
||||
clutter_texture_set_load_async (CLUTTER_TEXTURE (texture), TRUE);
|
||||
|
||||
/* connect a callback to the "load-finished" signal */
|
||||
g_signal_connect (texture,
|
||||
"load-finished",
|
||||
G_CALLBACK (_load_finished_cb),
|
||||
image_path);
|
||||
|
||||
/* load the image from a file */
|
||||
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
|
||||
image_path,
|
||||
&error);
|
||||
|
||||
/* ... clutter_main () etc. */
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Other ways to load image data into a texture</title>
|
||||
|
||||
<para>While it's useful to load image data into a texture directly
|
||||
from a file, there are occasions where you may have image data
|
||||
in some other (non-file) format:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Various GNOME libraries provide image data in GdkPixbuf
|
||||
structures; clutter-gtk has functions for
|
||||
creating or setting a texture from a GdkPixbuf:
|
||||
gtk_clutter_texture_new_from_pixbuf()
|
||||
and gtk_clutter_texture_set_from_pixbuf() respectively.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>If you have raw RGB or YUV pixel data, ClutterTexture has
|
||||
clutter_texture_set_from_rgb_data() and
|
||||
clutter_texture_set_from_yuv_data() methods for loading it.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
</chapter>
|
Loading…
Reference in New Issue
Block a user