# Script

Alfred Hitchcock

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".

## Introduction

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.

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.

ClutterScript enables a similar separation: you can define the UI declaratively using JSON, 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:

- Separation of UI element declarations from control logic (see above).

- 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).

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

- Creating and configuring some objects (e.g. animations, layouts) can be much simpler in JSON.

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

The following sections are intended to give an overview of how ClutterScript works, and how to use it in an application. The recipes in this chapter then provide more detail about particular aspects of ClutterScript, 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 ClutterScript API reference.

### Basic principles of ClutterScript

Clutter is built on top of GObject, an object system for C. ClutterScript provides a way to create instances of GObjects and set their properties. For example:

#### Example UI definition in JSON for use with ClutterScript

    [
      {
        "id" : "stage",
        "type" : "ClutterStage",
        "width" : 400,
        "height" : 400,
        "color" : "#333355ff",
        "children" : [ "box" ]
      },

      {
        "id" : "box",
        "type" : "ClutterBox",
        "width" : 400,
        "height" : 400,

        "layout-manager" : {
          "type" : "ClutterBinLayout",
          "x-align" : "center",
          "y-align" : "center"
        },

        "children" : [
          {
            "id" : "rectangle",
            "type" : "ClutterRectangle",
            "width" : 200,
            "height" : 200,
            "color" : "red"
          }
        ]
      }
    ]

N.B. The numbers in brackets in the example further explain the JSON structure, and are not part of the UI definition.

All the objects defined for the UI sit inside a JSON list structure, marked with square brackets.

A pair of braces surrounds each object definition; inside the braces, key-value pairs set properties on the object. See the section on datatypes for more about the acceptable values.

An `id` is required for objects which are referred to elsewhere in the JSON or which need to be accessible from code (see this recipe for the basics of using object IDs from code).

In cases where an object doesn't need to be accessible from code and is not referenced elsewhere in the JSON file, the `id` can be omitted.

The `type` key is mandatory, and specifies the type of the object; usually this will be one of the Clutter object types.

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 `pango_color_from_string()` function.

Children can be associated with a parent through the `children` property. Children are either added to the `children` 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 `children`.

This uses the nickname for a value in an enumeration (in this case, the nickname for `CLUTTER_BIN_ALIGNMENT_CENTER`).

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:

- `CLUTTER_ALIGN_X_AXIS` has the nickname `x-axis`

- `CLUTTER_GRAVITY_NORTH` has the nickname `north`

- `CLUTTER_REQUEST_HEIGHT_FOR_WIDTH` has the nickname `height-for-width`

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 Properties sections of the API reference for each Clutter type. Any of the properties described there can be set using ClutterScript.

Having said this, there are some special properties which aren't obvious, but which can be set via JSON; layout properties are one example. These aren't listed as properties of ClutterActor but can be set as part of a ClutterActor object definition (using the `layout::<property name>` syntax for the key). Some of these are covered in recipes later in this chapter.

### Data types

ClutterScript 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 number in the JSON, when the Clutter property is expecting a gfloat, may cause errors.

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:

C data type (Clutter/GLib) | Maps to JSON | Example (C => JSON)
--- | --- | ---
floating point number (gfloat, gdouble) | number (int frac, int exp, int frac exp) | `1.0` => `1.0` `1e-1` => `1e-1` `1E-1` => `1E-1` `0.1E-1` => `0.1E-1`
integer (guint8, gint) | number (int) | `1` => `1` `0x00` => `0` (no hex in JSON) `01` => `1` (no octal in JSON)
gboolean | true/false | `TRUE` => `true` `FALSE` => `false`
gchar | string | `"hello world"` => `"hello world"`
enum (e.g. Clutter constants) | string | `CLUTTER_ALIGN_X_AXIS` => `"CLUTTER_ALIGN_X_AXIS"` or `"x-axis"` (the latter is the GEnum nickname for the constant)
ClutterColor | color string | `clutter_color_new (255, 0, 0, 255)` => `"red"` or `"#f00f"` or `"#ff0000ff"`; alternatively, `"#f00"` or `"#ff0000"` (implicitly sets alpha value to 255)
ClutterActor (or other Clutter type) | object | `clutter_rectangle_new ()` => `{ "type" : "ClutterRectangle" }`

Property which takes a list or array of values

array of objects and/or IDs

`clutter_container_add_actor (stage, rectangle)` =>

    {
      "id" : "stage",
      "type" : "ClutterStage",
      ...,

      "children" : [
        {
          "id" : "rectangle",
          "type" : "ClutterRectangle",
          ...
        }
      ]
    }

`NULL` | `null` | -

## Defining a user interface with JSON

### Problem

You want to create a user interface as quickly as possible; you also need to change it easily as requirements shift.

This need can arise when:

- you are prototyping a user interface, and you need to quickly test new ideas.

- the user interface you are building is likely to contain many elements and relationships between them.

### Solution

Define the user interface in an external JSON file. Then create a ClutterScript object and load the JSON into it from the file.

This keeps the UI definition separate from the application logic and makes it easier to manage.

See the introduction for the reasons why ClutterScript is a good solution, and for an overview of how JSON definitions work.

Here's an example JSON definition to put in the file:

      a code sample should be here... but isn't

In the application, load the JSON from the file with `clutter_script_load_from_file()`. (You can also load JSON from a string (gchar*) with `clutter_script_load_from_data()`.)

Then retrieve objects by ID to use them in your code:

#### Loading JSON from a file and retrieving objects defined by it

      a code sample should be here... but isn't

Although we only retrieved the stage in the example above, `clutter_script_get_objects()` can retrieve multiple objects with a single call:

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

You can also use `clutter_script_get_object()` to retrieve a single object, though you may have to cast it to the right type before use; for example:

    ClutterStage *stage = CLUTTER_STAGE (clutter_script_get_object (script, "stage));

### Discussion

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.

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.

## Connecting to signals in ClutterScript

### Problem

You have declared an actor using JSON, and want to add handlers for signals emitted by it.

### Solution

Add a `signals` property to the actor's JSON definition.

Here's how to connect a ClutterStage's `destroy` signal to the `clutter_main_quit()` function:

    {
      "id" : "stage",
      "type" : "ClutterStage",
      "width" : 300,
      "height" : 300,

      "signals" : [
        { "name" : "destroy", "handler" : "clutter_main_quit" }
      ]
    }

The highlighted part of the code is where the signal is connected. In this case, a Clutter function is used as the handler; in most cases, you'll want to define your own handlers, rather than using functions from other libraries, as follows:

    {
      "id" : "rectangle",
      "type" : "ClutterRectangle",
      "width" : 200,
      "height" : 200,
      "reactive" : true,

      "signals" : [
        { "name" : "motion-event", "handler" : "foo_pointer_motion_cb" }
      ]
    }

This signal handler definition sets `foo_pointer_motion_cb()` as the handler for the `motion-event` signal on the rectangle. (NB the rectangle has `reactive` set to true, otherwise it can't emit this signal.)

As per standard event handling in Clutter, you define the handler function next. For example:

    /* handler which just prints the position of the pointer at each motion event */
    gboolean
    foo_pointer_motion_cb (ClutterActor *actor,
                           ClutterEvent *event,
                           gpointer      user_data)
    {
      gfloat x, y;
      clutter_event_get_coords (event, &x, &y);

      g_print ("Pointer movement at %.0f,%.0f\n", x, y);

      return TRUE;
    }

See the Discussion section for more about writing handler functions.

To make the signal connections active in your code, call the `clutter_script_connect_signals()` function after loading the JSON:

    GError *error = NULL;

    /* load JSON from a file */
    ClutterScript *ui = clutter_script_new ();
    clutter_script_load_from_file (ui, filename, &error);

    /* ...handle errors etc... */

    /* connect the signals defined in the JSON file
     *
     * the first argument is the script into which the JSON
     * definition was loaded
     *
     * the second argument is passed as user_data to all
     * handlers: in this case, we pass the script as user_data
     * to all handlers, so that all the objects in the UI
     * are available to callback functions
     */
    clutter_script_connect_signals (ui, ui);

### Discussion

#### Options for connecting signals to handlers

Every connection between a signal and handler requires a JSON object with `name` and `handler` keys. The `name` is the name of the signal you're connecting a handler to; the `handler` is the name of the function which will handle the signal.

You can also specify these optional keys for a handler object:

1. `"after" : true` configures the handler to run after the default handler for the signal. (Default is `"after" : false`).

2. `"swapped" : true` specifies that the instance and the user data passed to the handler function are swapped around; i.e. the instance emitting the signal is passed in as the user data argument (usually the last argument), and any user data is passed in as the first argument. (Default is `"swapped" : false`).

While the connections to signals were specified in JSON above, it is still possible to connect handlers to signals in code (e.g. if you need to conditionally connect a handler). Just retrieve the object from the ClutterScript and connect to its signals with `g_signal_connect()`.

#### Writing handler functions

The handler function has the usual signature required for the signal. However, the function cannot be static, otherwise the function is invisible to GModule (the mechanism used by ClutterScript to look up functions named in the JSON definition). Consequently, callback functions should be namespaced in such a way that they won't clash with function definitions in other parts of your code or in libraries you link to.

You should also ensure that you use the `-export-dynamic` flag when you compile your application: either by passing it on the command line (if you're calling `gcc` directly); or by adding it to the appropriate `LDFLAGS` variable in your `Makefile` (if you're using `make`); or by whatever other mechanism is appropriate for your build environment.

#### Passing objects to handler functions

In a typical Clutter application, handler functions require access to objects other than the one which emitted a signal. For example, a button may move another actor when clicked. Typically, you would pass any required objects to the handler function as user data, like this:

    g_signal_connect (button,
                      "clicked",
                      G_CALLBACK (_button_clicked_cb),
                      actor_to_move);

Note how `actor_to_move` is passed as user data to the handler.

However, the JSON definition doesn't allow you to specify that different user data be passed to different handlers. So, to get at all required objects in the handler, a simple solution is to pass the ClutterScript to every handler function; then inside each handler function, retrieve the required objects from the script.

This was done in the code example above, by passing the ClutterScript instance as two arguments to `clutter_script_connect_signals()`: the first argument specifies the script which defines the signal handlers; the second specifies the user data passed to every handler function. This ensures that each handler has access to all of the elements defined in the JSON file.

Alternatively, you could create some other structure to hold the objects you need and pass it to all handler functions. But this would effectively be a reimplementation of some aspects of ClutterScript.

### Full examples

#### ClutterScript JSON with signal handler definitions

      a code sample should be here... but isn't

#### Loading a JSON file into a ClutterScript and connecting signal handlers

      a code sample should be here... but isn't

## Connecting ClutterState states in ClutterScript

### Problem

You have declared an actor using JSON, and want to connect signals to ClutterState transitions.

### Solution

Connect the ClutterState states to the signals using the `states` and `target-state` keys of the `signals` definition, and call `clutter_script_connect_signals()`; for instance, the following JSON declares that the enter-event signal should transition to the hover state and the leave-event should transition to the base state:

    {
      "id" : "rectangle",
      "type" : "ClutterRectangle",
      "width" : 200,
      "height" : 200,
      "reactive" : true,

      "signals" : [
        { "name" : "enter-event", "states" : "rectangle-states", "target-state" : "hover" },
        { "name" : "leave-event", "states" : "rectangle-states", "target-state" : "base" }
      ]
    }

The rectangle-states state machine holds the various states.

### Discussion

Connecting a ClutterState state transition to a signal defined inside a ClutterScript JSON without requiring a real function to wrap `clutter_state_set_state()` allows to minimize the amount of code that has to be written, and ties the state to the UI element being defined.

The connection between a signal and a ClutterState state is similar to the connection between a signal and a handler function. Each definition must contain the name of the signal; the script id of the ClutterState object that is used to store the target state definition; and the target state of the transition.

The states key can also contain a full definition of the ClutterState.

The target-state key works exactly like the argument of `clutter_state_set_state()`: it will transition the ClutterState from the current state to the desired state.

The ClutterState instance that will be used to resolve the target state can be defined in JSON like any other object, but it is also possible to create a ClutterState in code, and associate it to a ClutterScript instance prior to parsing the signal connection JSON, through the `clutter_script_add_states()` function of ClutterScript.

The warp boolean key can be used to perform a transition to the target state without an animation, similarly to what `clutter_state_warp_to_state()` does, for instance:

    {
      "signals" : [
        {
          "name" : "enter-event",
          "states" : "rectangle-states",
          "target-state" : "hover",
          "warp" : true
        }
      ]
    }

will not animate the transition between the current state and the target hover state when the signal is emitted.

### Full examples

#### ClutterScript JSON with state definitions

      a code sample should be here... but isn't

#### Loading a JSON file into a ClutterScript and connecting states

      a code sample should be here... but isn't