79a4dbb329
GEnum nicknames can be used to set properties in JSON definitions, so added a callout to the JSON example explaining this, and showing how to derive the nickname for an enumeration value. Modified the example code to use nicknames as well.
465 lines
18 KiB
XML
465 lines
18 KiB
XML
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
|
|
|
<chapter id="script"
|
|
xmlns:xi="http://www.w3.org/2003/XInclude">
|
|
<title>Script</title>
|
|
|
|
<epigraph>
|
|
<attribution>Alfred Hitchcock</attribution>
|
|
<para>When an actor comes to me and wants to discuss his character,
|
|
I say, "It's in the script". If he says, "But what's my motivation?",
|
|
I say, "Your salary".</para>
|
|
</epigraph>
|
|
|
|
<section id="script-introduction">
|
|
<title>Introduction</title>
|
|
|
|
<para>User interfaces can become difficult to maintain when
|
|
described entirely in code: declarations of UI
|
|
elements become entwined with procedural code for
|
|
handling interactions. This can make refactoring tough, as
|
|
you have to find the right place in the code to modify the UI
|
|
("Where did I set the color of that rectangle?") and make sure
|
|
your UI modifications don't break any behaviour.</para>
|
|
|
|
<para>Many frameworks separate presentation from programming
|
|
logic, making it easier to change the appearance of the UI
|
|
without affecting its behaviour (and vice versa). For example,
|
|
in web development you can use HTML and CSS to define
|
|
presentation, and JavaScript to implement application logic.</para>
|
|
|
|
<para><type>ClutterScript</type> enables a similar separation:
|
|
you can define the UI declaratively using
|
|
<ulink href="http://www.json.org/">JSON</ulink>, load
|
|
the UI from the JSON, then handle interactions with it through Clutter code
|
|
(in C, Python, Vala or some other language). This has several
|
|
benefits, including:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Separation of UI element declarations from control logic
|
|
(see above).</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>More concise code: typically, describing a UI in JSON
|
|
requires far fewer characters than the equivalent procedural
|
|
code (at least, once you have more than three or four actors in
|
|
your application).</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>If you write your JSON in external files, you can make the
|
|
structure of the UI evident in the layout of the file. For
|
|
example, child elements can be indented within the parent
|
|
element. This can make identifying relationships between
|
|
elements simpler and less error-prone.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>Creating and configuring some objects (e.g. animations,
|
|
layouts) can be much simpler in JSON.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>Less compilation (if you're using a compiled language):
|
|
because you can change the UI by editing external JSON files,
|
|
you can make changes to it without needing to recompile
|
|
the whole application.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The following sections are intended
|
|
to give an overview of how <type>ClutterScript</type> works, and
|
|
how to use it in an application. The recipes in this chapter
|
|
then provide more detail about particular aspects of
|
|
<type>ClutterScript</type>, such as how to connect signals to handlers,
|
|
how to merge multiple JSON definitions in a single script, etc.
|
|
There is also a lot of useful information in the <type>ClutterScript</type>
|
|
API reference.</para>
|
|
|
|
<section>
|
|
<title>Basic principles of <type>ClutterScript</type></title>
|
|
|
|
<para>Clutter is built on top of
|
|
<ulink href="http://library.gnome.org/devel/gobject/">GObject</ulink>,
|
|
an object system for C. <type>ClutterScript</type>
|
|
provides a way to create instances of GObjects and
|
|
set their properties. For example:</para>
|
|
|
|
<example>
|
|
<title>Example UI definition in JSON for use with
|
|
<type>ClutterScript</type></title>
|
|
|
|
<programlistingco>
|
|
<programlisting>
|
|
[ <co id="script-ui-introduction-json-list-bracket" />
|
|
{ <co id="script-ui-introduction-json-object-bracket" />
|
|
"id" : "stage", <co id="script-ui-introduction-json-id" />
|
|
"type" : "ClutterStage", <co id="script-ui-introduction-json-type" />
|
|
"width" : 400,
|
|
"height" : 400,
|
|
"color" : "#333355ff", <co id="script-ui-introduction-json-color-html" />
|
|
"children" : [ "box" ] <co id="script-ui-introduction-json-child-by-id" />
|
|
},
|
|
|
|
{
|
|
"id" : "box",
|
|
"type" : "ClutterBox",
|
|
"width" : 400,
|
|
"height" : 400,
|
|
|
|
"layout-manager" : { <co id="script-ui-introduction-json-no-id" />
|
|
"type" : "ClutterBinLayout",
|
|
"x-align" : "center", <co id="script-ui-introduction-json-nickname" />
|
|
"y-align" : "center"
|
|
},
|
|
|
|
"children" : [ <co id="script-ui-introduction-json-child-by-embedding" />
|
|
{
|
|
"id" : "rectangle",
|
|
"type" : "ClutterRectangle",
|
|
"width" : 200,
|
|
"height" : 200,
|
|
"color" : "red" <co id="script-ui-introduction-json-color-word" />
|
|
}
|
|
]
|
|
}
|
|
]
|
|
</programlisting>
|
|
|
|
<note>
|
|
<para>N.B. The numbers in brackets in the example further
|
|
explain the JSON structure, and are not part of the UI
|
|
definition.</para>
|
|
</note>
|
|
|
|
<calloutlist>
|
|
<callout arearefs="script-ui-introduction-json-list-bracket">
|
|
<para>All the objects defined for the UI sit inside a JSON
|
|
list structure, marked with square brackets.</para>
|
|
</callout>
|
|
<callout arearefs="script-ui-introduction-json-object-bracket">
|
|
<para>A pair of braces surrounds each object definition;
|
|
inside the braces, key-value pairs set properties on the
|
|
object. See the
|
|
<link linkend="script-introduction-data-types">section on
|
|
datatypes</link> for more about the acceptable values.</para>
|
|
</callout>
|
|
<callout arearefs="script-ui-introduction-json-id
|
|
script-ui-introduction-json-no-id">
|
|
<para>An <varname>id</varname> is required for objects which
|
|
are referred to elsewhere in the JSON or which need to be
|
|
accessible from code (see
|
|
<link linkend="script-ui">this recipe</link> for the basics of
|
|
using object IDs from code).</para>
|
|
<para>In cases where an object doesn't need to be accessible
|
|
from code and is not referenced elsewhere in the JSON file,
|
|
the <varname>id</varname> can be omitted.</para>
|
|
</callout>
|
|
<callout arearefs="script-ui-introduction-json-type">
|
|
<para>The <varname>type</varname> key is mandatory, and
|
|
specifies the type of the object; usually this will be
|
|
one of the Clutter object types.</para>
|
|
</callout>
|
|
<callout arearefs="script-ui-introduction-json-color-html
|
|
script-ui-introduction-json-color-word">
|
|
<para>Colors can be set using hexadecimal color code strings,
|
|
as used in HTML and CSS; or by using color words. The
|
|
range of acceptable values is as for the
|
|
<function>pango_color_from_string()</function> function.</para>
|
|
</callout>
|
|
<callout arearefs="script-ui-introduction-json-child-by-id
|
|
script-ui-introduction-json-child-by-embedding">
|
|
<para>Children can be associated with a parent through
|
|
the <varname>children</varname> property. Children are
|
|
either added to the <varname>children</varname> list by ID;
|
|
or by directly embedding the child JSON object as an element
|
|
within the list. The two can be mixed in a single
|
|
list of <varname>children</varname>.</para>
|
|
</callout>
|
|
<callout arearefs="script-ui-introduction-json-nickname">
|
|
<para>This uses the nickname for a value in an enumeration
|
|
(in this case, the nickname for
|
|
<constant>CLUTTER_BIN_ALIGNMENT_CENTER</constant>).</para>
|
|
<para>To get the nickname for an enumeration value, take
|
|
the component which is unique to that value in the
|
|
enumeration, lowercase it, and replace any underscores
|
|
with hyphens. Some examples:</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><constant>CLUTTER_ALIGN_X_AXIS</constant> has
|
|
the nickname <code>x-axis</code></para>
|
|
</listitem>
|
|
<listitem>
|
|
<para><constant>CLUTTER_GRAVITY_NORTH</constant> has
|
|
the nickname <code>north</code></para>
|
|
</listitem>
|
|
<listitem>
|
|
<para><constant>CLUTTER_REQUEST_HEIGHT_FOR_WIDTH</constant>
|
|
has the nickname <code>height-for-width</code></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</callout>
|
|
</calloutlist>
|
|
|
|
</programlistingco>
|
|
</example>
|
|
|
|
<para>Once you grasp that Clutter objects are GObjects, and you
|
|
are setting their properties, you can work out what is "scriptable"
|
|
by referring to the <emphasis>Properties</emphasis> sections
|
|
of the API reference for each Clutter type. Any of the properties
|
|
described there can be set using <type>ClutterScript</type>.</para>
|
|
|
|
<para>Having said this, there are some special properties which
|
|
aren't obvious, but which can be set via JSON;
|
|
<emphasis>layout properties</emphasis> are one example. These aren't
|
|
listed as properties of <type>ClutterActor</type> but can be set
|
|
as part of a <type>ClutterActor</type> object definition
|
|
(using the <code>layout::<property name></code>
|
|
syntax for the key). Some of these are covered in recipes later in
|
|
this chapter.</para>
|
|
|
|
</section>
|
|
|
|
<section id="script-introduction-data-types">
|
|
<title>Data types</title>
|
|
|
|
<para><type>ClutterScript</type> uses the standard JSON format.
|
|
It is very important that you respect the data type of the property
|
|
you are setting, ensuring that you use the right JSON data type.
|
|
You may get unexpected results or errors if you try to set a property
|
|
using the wrong data type: for example, setting a property
|
|
to an integer <type>number</type> in the JSON, when the Clutter property
|
|
is expecting a <type>gfloat</type>, may cause errors.</para>
|
|
|
|
<para>To assist in using the right data types in your JSON
|
|
definitions, the table below shows how Clutter and GLib data
|
|
types map to JSON:</para>
|
|
|
|
<informaltable>
|
|
<thead>
|
|
<tr>
|
|
<th>C data type (Clutter/GLib)</th>
|
|
<th>Maps to JSON</th>
|
|
<th>Example (C => JSON)</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>floating point number (gfloat, gdouble)</td>
|
|
<td>number (int frac, int exp, int frac exp)</td>
|
|
<td>
|
|
<para><code>1.0</code> => <code>1.0</code></para>
|
|
<para><code>1e-1</code> => <code>1e-1</code></para>
|
|
<para><code>1E-1</code> => <code>1E-1</code></para>
|
|
<para><code>0.1E-1</code> => <code>0.1E-1</code></para>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>integer (guint8, gint)</td>
|
|
<td>number (int)</td>
|
|
<td>
|
|
<para><code>1</code> => <code>1</code></para>
|
|
<para><code>0x00</code> => <code>0</code> (no hex in JSON)</para>
|
|
<para><code>01</code> => <code>1</code> (no octal in JSON)</para>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>gboolean</td>
|
|
<td>true/false</td>
|
|
<td>
|
|
<para><code>TRUE</code> => <code>true</code></para>
|
|
<para><code>FALSE</code> => <code>false</code></para>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>gchar</td>
|
|
<td>string</td>
|
|
<td><code>"hello world"</code> => <code>"hello world"</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>enum (e.g. Clutter constants)</td>
|
|
<td>string</td>
|
|
<td>
|
|
<code>CLUTTER_ALIGN_X_AXIS</code> =>
|
|
<code>"CLUTTER_ALIGN_X_AXIS"</code> or <code>"x-axis"</code>
|
|
(the latter is the GEnum nickname for the constant)
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>ClutterColor</td>
|
|
<td>color string</td>
|
|
<td>
|
|
<code>clutter_color_new (255, 0, 0, 255)</code> =>
|
|
<code>"red"</code> or <code>"#f00f"</code> or
|
|
<code>"#ff0000ff"</code>; alternatively,
|
|
<code>"#f00"</code> or <code>"#ff0000"</code>
|
|
(implicitly sets alpha value to 255)
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>ClutterActor (or other Clutter type)</td>
|
|
<td>object</td>
|
|
<td>
|
|
<code>clutter_rectangle_new ()</code> =>
|
|
<code>{ "type" : "ClutterRectangle" }</code>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Property which takes a list or array of values</td>
|
|
<td>array of objects and/or IDs</td>
|
|
<td>
|
|
<code>clutter_container_add_actor (stage, rectangle)</code> =>
|
|
<programlisting>
|
|
{
|
|
"id" : "stage",
|
|
"type" : "ClutterStage",
|
|
...,
|
|
|
|
"children" : [
|
|
{
|
|
"id" : "rectangle",
|
|
"type" : "ClutterRectangle",
|
|
...
|
|
}
|
|
]
|
|
}
|
|
</programlisting>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>NULL</code></td>
|
|
<td><code>null</code></td>
|
|
<td>-</td>
|
|
</tr>
|
|
</tbody>
|
|
</informaltable>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section id="script-ui">
|
|
<title>Defining a user interface with JSON</title>
|
|
|
|
<section>
|
|
<title>Problem</title>
|
|
|
|
<para>You want to create a user interface as quickly as
|
|
possible; you also need to change it easily as requirements shift.</para>
|
|
|
|
<para>This need can arise when:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>you are prototyping a user interface, and you need to
|
|
quickly test new ideas.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>the user interface you are building is likely to contain many
|
|
elements and relationships between them.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Solution</title>
|
|
|
|
<para>Define the user interface in an external JSON file. Then
|
|
create a <type>ClutterScript</type> object and load the JSON
|
|
into it from the file.</para>
|
|
|
|
<para>This keeps the UI definition separate from the application
|
|
logic and makes it easier to manage.</para>
|
|
|
|
<note>
|
|
<para>See <link linkend="script-introduction">the introduction</link>
|
|
for the reasons why <type>ClutterScript</type> is a good solution,
|
|
and for an overview of how JSON definitions work.</para>
|
|
</note>
|
|
|
|
<para>Here's an example JSON definition to put in the file:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<xi:include href="examples/script-ui.json" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>In the application, load the JSON from the file with
|
|
<function>clutter_script_load_from_file()</function>. (You can
|
|
also load JSON from a string (<type>gchar*</type>) with
|
|
<function>clutter_script_load_from_data()</function>.)</para>
|
|
|
|
<para>Then retrieve objects by ID to use them in your code:</para>
|
|
|
|
<example>
|
|
<title>Loading JSON from a file and retrieving objects
|
|
defined by it</title>
|
|
<programlisting>
|
|
<xi:include href="examples/script-ui.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<para>Although we only retrieved the stage in the example above,
|
|
<function>clutter_script_get_objects()</function> can
|
|
retrieve multiple objects with a single call:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
ClutterScript *script;
|
|
script = clutter_script_new ();
|
|
|
|
/* ...load JSON file etc. */
|
|
|
|
ClutterStage *stage;
|
|
ClutterActor *actor1;
|
|
ClutterActor *actor2;
|
|
|
|
/* use a NULL-terminated argument list of id,variable pairs */
|
|
clutter_script_get_objects (script,
|
|
"stage", &stage,
|
|
"actor1", &actor1,
|
|
"actor2", &actor2,
|
|
NULL);
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>You can also use <function>clutter_script_get_object()</function>
|
|
to retrieve a single object, though you may have to cast
|
|
it to the right type before use; for example:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
ClutterStage *stage = CLUTTER_STAGE (clutter_script_get_object (script, "stage));
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Discussion</title>
|
|
|
|
<para>In the sample code, the stage is part of the JSON definition.
|
|
However, it doesn't have to be: it is possible to create the
|
|
stage in application code; then load more components from one
|
|
or more JSON definitions and attach them to the stage you
|
|
constructed in code.</para>
|
|
|
|
<para>However, keeping most of the user interface definition
|
|
in external JSON files makes it easier to change
|
|
the UI without having to touch any code. If you have some user
|
|
interface elements constructed in code and some in JSON, it can
|
|
make refactoring more difficult.</para>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
</chapter>
|