Chapter 8. Script

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

Alfred Hitchcock

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

1.1. 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 8.1. Example UI definition in JSON for use with ClutterScript

[   (1)
  {   (2)
    "id" : "stage",   (3)
    "type" : "ClutterStage",   (4)
    "width" : 400,
    "height" : 400,
    "color" : "#333355ff",   (5)
    "children" : [ "box" ]   (6)
  },

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

    "layout-manager" : {   (7)
      "type" : "ClutterBinLayout",
      "x-align" : "center",   (8)
      "y-align" : "center"
    },

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

Note

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

(1)

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

(2)

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.

(3) (7)

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.

(4)

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

(5) (10)

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.

(6) (9)

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.

(8)

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.

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