Apocalypse 1

Back to Clutter/Apocalypses


or Deconstructing the Actor.

What we define as an actor is a node in the scene graph.

This node holds the following information:

  • Geometry
    • preferred size
    • size request mode
      • width for height
      • height for width
      • fixed size
    • allocated bounding box
    • fixed z position
    • transformations
      • scaling
      • rotation (x, y, z)
    • paint volume

The node also has the following scenegraph related methods:

  • Children
    • add child
    • insert child: at index
    • insert child: above sibling
    • insert child: below sibling
    • remove child
    • parent
    • children

Additionally, the node has the following attributes:

  • automatic expand flag (x, y)
  • alignment flag (x, y)
  • margin (top, right, bottom, left)
  • background-color
  • content
  • minification-filter
  • magnification-filter


The main design goal is to have an Actor class that encapsulates all the needed capabilities as a node in the scene graph, without requiring additional subclassing; specific behaviour should be provided through delegation, instead, to allow code reuse and sharing.

Given that an Actor is defined as a node in the scene graph, it descends that the overall children management logic should reside in the Actor class itself. We alre helped by the fact that the Actor already does this internally, and by the fact that the Container API is an interface that can be added without any API penalty.

Having a single API for controlling the scene graph helps in three areas:

  • it respects the Principle of Least Astonishment: developers are supposed to learn (and implement) a single class instead of a class exposing the guts of the scene and an interface papering over it.
  • it removes the need to have two public entry points: a low-level API for containers to use, and a high level API for application code; plus, it removes the need for having a distinction of containers and composite actors, which is fully artificial and introduces special casing in the memory management.
  • it allows us to simplify the logic of the base Actor class by removing edge cases that can only be introduced by using an Actor as a Container without the Container interface - like internal children.

Hiding child actors as "internal" has always been a hack: the scene graph API still very much needs to be aware of all the children of an actor, so all internal actors had to provide the same level of support as the public ones; the only benefit was the ability to not have programmatic access to private children - something that could easily be defeated with the judicious application of bits and blobs of API. The Actor API allows application code to iterate over every single child, regardless of whether it is considered internal or not; this just means that more care will have to be placed in designing composite actors.

Size negotiation for the eventual children of an actor is also part of the responsibilities of a container; instead of deferring the size negotiation to a subclass, we can use the currently available Layout Manager class as the delegate. A Layout Manager should take over the preferred size, geometry request, and allocation of the children of an Actor. Whether or not an Actor should have a default layout manager is still undecided; a Fixed layout manager would provide basic functionality that would fit in with an explicit positioning and sizing model, but having something similar to the CAConstraintLayoutManager would probably go a long way into helping people create applications immediately and with ease. The scope for the layout manager delegate is better defined inside the 2nd Apocalypse.

The other main delegation point is painting the actor's contents, aside from its children.

Content includes:

  • solid fills
  • texture data
  • Cairo surfaces

Each type of content is responsible for setting up the pipeline, as well as uploading the geometry primitives. Delegation allows to decouple the actual paint operation from the scene graph: a Content would need to paint using the modelview of the Actor as its frame of reference, and never escape the Actor's paint volume. The Content delegate is a base class that provides at least the first point - and it is better defined inside the 3rd Apocalypse.

A scene should thus be constructed through only the Actor API; implementing complex actors can be delegated to simple subclasses that build the sub-nodes of the scene during instance initialisation or construction, and all the logic for layout and painting is deferred to delegate objects that can be interchangeably used and implemented without requiring changes or extensions to the Actor API.


Actor hierarchy

Managing the list of children

Status: DONE

 ClutterActor *clutter_actor_get_first_child (ClutterActor *self);
 ClutterActor *clutter_actor_get_previous_sibling (ClutterActor *self);
 ClutterActor *clutter_actor_get_next_sibling (ClutterActor *self);
 ClutterActor *clutter_actor_get_last_child (ClutterActor *self);

 void clutter_actor_add_child (ClutterActor *self, ClutterActor *child);
 void clutter_actor_insert_child_at_index (ClutterActor *self, ClutterActor *child, int index_);
 void clutter_actor_insert_child_above (ClutterActor *self, ClutterActor *child, ClutterActor *sibling);
 void clutter_actor_insert_child_below (ClutterActor *self, ClutterActor *child, ClutterActor *sibling);
 void clutter_actor_replace_child (ClutterActor *self, ClutterActor *old_child, ClutterActor *new_child);
 void clutter_actor_remove_child (ClutterActor *self, ClutterActor *child);
 void clutter_actor_remove_all_children (ClutterActor *self);
 GList *clutter_actor_get_children (ClutterActor *self);
 int clutter_actor_get_n_children (ClutterActor *self);
 ClutterActor *clutter_actor_get_child_at_index (ClutterActor *self, int index_);

Paint and layout sequence

Status: DONE

 void clutter_actor_set_child_above_sibling (ClutterActor *self, ClutterActor *child, ClutterActor *sibling);
 void clutter_actor_set_child_below_sibling (ClutterActor *self, ClutterActor *child, ClutterActor *sibling);
 void clutter_actor_set_child_at_index (ClutterActor *self, ClutterActor *child, int index_);


Status: DONE

 ClutterActor *clutter_actor_get_parent (ClutterActor *self);
 ClutterActor *clutter_actor_get_stage (ClutterActor *self);
 gboolean clutter_actor_contains (ClutterActor *self, ClutterActor *actor);

Actor geometry

Size negotiation

Status: DONE

 void clutter_actor_set_request_mode (ClutterActor *self, ClutterRequestMode mode);
 ClutterRequestMode clutter_actor_get_request_mode (ClutterActor *self);
 void clutter_actor_get_preferred_width (ClutterActor *self, float for_height, float *minimum_width, float *natural_width);
 void clutter_actor_get_preferred_height (ClutterActor *self, float for_width, float *minimum_height, float *natural_height);
 void clutter_actor_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags);

Layout management for children

Status: DONE

 void clutter_actor_set_layout_manager (ClutterActor *self, ClutterLayoutManager *manager);
 ClutterLayoutManager *clutter_actor_get_layout_manager (ClutterActor *self);

Automatic expansion

Status: DONE

 void clutter_actor_set_x_expand (ClutterActor *self, gboolean x_expand);
 gboolean clutter_actor_get_x_expand (ClutterActor *self);
 void clutter_actor_set_y_expand (ClutterActor *self, gboolean y_expand);
 gboolean clutter_actor_get_y_expand (ClutterActor *self);
 gboolean clutter_actor_needs_x_expand (ClutterActor *self);
 gboolean clutter_actor_needs_y_expand (ClutterActor *self);


Status: DONE

 void clutter_actor_set_x_align (ClutterActor *self, ClutterActorAlign x_align);
 ClutterActorAlign clutter_actor_get_x_align (ClutterActor *self);
 void clutter_actor_set_y_align (ClutterActor *self, ClutterActorAlign y_align);
 ClutterActorAlign clutter_actor_get_y_align (ClutterActor *self);


Status: DONE

 void clutter_actor_set_margin_top (ClutterActor *self, float margin);
 float clutter_actor_get_margin_top (ClutterActor *self);
 void clutter_actor_set_margin_right (ClutterActor *self, float margin);
 float clutter_actor_get_margin_right (ClutterActor *self);
 void clutter_actor_set_margin_bottom (ClutterActor *self, float margin);
 float clutter_actor_get_margin_bottom (ClutterActor *self);
 void clutter_actor_set_margin_left (ClutterActor *self, float margin);
 float clutter_actor_get_margin_left (ClutterActor *self);
 void clutter_actor_set_margin (ClutterActor *self, const ClutterMargin *margin);
 void clutter_actor_get_margin (ClutterActor *self, ClutterMargin *margin);

Fixed position and size setters

Status: DONE

 void clutter_actor_set_x (ClutterActor *self, float fixed_x);
 void clutter_actor_set_y (ClutterActor *self, float fixed_y);
 void clutter_actor_set_width (ClutterActor *self, float fixed_width);
 void clutter_actor_set_height (ClutterActor *self, float fixed_height);

Bounding box getters

Status: DONE

 void clutter_actor_get_allocation_box (ClutterActor *self, ClutterActorBox *box);
 void clutter_actor_get_paint_box (ClutterActor *self, ClutterActorBox *box);
 float clutter_actor_get_x (ClutterActor *self);
 float clutter_actor_get_y (ClutterActor *self);
 float clutter_actor_get_width (ClutterActor *self);
 float clutter_actor_get_height (ClutterActor *self);


Basic actors

  /* stage */
  ClutterActor *stage = clutter_stage_new ();

  /* main container */
  ClutterActor *vase = clutter_actor_new ();
  clutter_actor_set_layout_manager (vase, clutter_box_layout_new ());
  clutter_actor_set_background_color (vase, CLUTTER_COLOR_Blue);
  clutter_actor_add_child (stage, vase);

  /* children */
  ClutterActor *flowers[3];
  flowers[0] = clutter_actor_new ();
  clutter_actor_set_background_color (flowers[0], CLUTTER_COLOR_Red);
  clutter_actor_set_size (flowers[0], 100, 100);
  clutter_actor_add_child (vase, flowers[0]);

  flowers[1] = clutter_actor_new ();
  clutter_actor_set_background_color (flowers[1], CLUTTER_COLOR_Yellow);
  clutter_actor_set_size (flowers[1], 100, 100);
  clutter_actor_add_child (vase, flowers[1]);

  flowers[2] = clutter_actor_new ();
  clutter_actor_set_background_color (flowers[2], CLUTTER_COLOR_Green);
  clutter_actor_set_size (flowers[2], 100, 100);
  clutter_actor_add_child (vase, flowers[2]);

Projects/Clutter/Apocalypses/Apocalypse1 (last edited 2013-11-22 18:46:18 by WilliamJonMcCann)