ClutterActor Invariants
===============================================================================

ClutterActor behaviour has invariants that will be kept with the same API and
ABI guarantees as the whole Clutter library.

This document refers to the 0.8 release of Clutter.

Sections:

   i. Flags
        a. Public ClutterActor Flags
        b. Private ClutterActor Flags
        c. Private Pick Modes
  ii. Invariants
 iii. State changes
  iv. Responsibilities of a ClutterActor
        a. Adding to a container
        b. Removing from a container
        c. Initial state

 i. Flags
-------------------------------------------------------------------------------

This section describes the various flags and enumerations used by
ClutterActor.

 a. Public ClutterActor Flags

CLUTTER_ACTOR_REALIZED
        Means: the actor has GPU resources associated to its paint
        cycle. Note however that an actor is allowed to allocate Cogl
        resources before being realized because Clutter only ever uses
        one rendering context which is always current. An actor is
        free to create resources at any time.

        Set by clutter_actor_realize(), unset by
        clutter_actor_unrealize(). Generally set implicitly when the
        actor becomes MAPPED (see below).

        May only be set if one of the actor's ancestors is a toplevel.
        May only be set if all of the actor's ancestors are realized.

        Once realized an actor remains realized until removed from the
        toplevel. Hide, reparent will not unrealize; but unparent or
        destroy will unrealize since they remove the actor from the
        toplevel.

CLUTTER_ACTOR_MAPPED
        Means: the actor will be painted if the stage is mapped.

        On non-toplevels, will be set if all of the following are
        true, and unset otherwise:
        * the actor's VISIBLE flag is set
        * all of the actor's non-toplevel ancestors have the MAPPED
          flag set
        * the actor has a toplevel ancestor
        * the toplevel ancestor's VISIBLE flag is set
        * the toplevel ancestor's REALIZED flag is set

        On toplevels, MAPPED is set asynchronously when the window
        system notifies Clutter that the toplevel has been made
        visible on the screen.

        The MAPPED flag requires that an actor is REALIZED. When
        Clutter sets the MAPPED flag, it forces realization; this is
        the "normal" way for realization to occur, though explicit
        realization with clutter_actor_realize() is permitted.

CLUTTER_ACTOR_VISIBLE
        Means: the actor's "visible" property was set to true by
        the application programmer.

        Set by clutter_actor_show(), unset by clutter_actor_hide().

        This is an application-controlled property, while MAPPED and
        REALIZED are usually managed by Clutter (with the exception
        that applications can "realize early" with
        clutter_actor_realize()).

        If VISIBLE is unset, the actor (and any children) must
        be immediately unmapped, to maintain the invariants for
        the MAPPED flag.

CLUTTER_ACTOR_REACTIVE
        Set and unset by clutter_actor_set_reactive()
        Means: the actor is now reactive to events.
        Notes:
          * If parents need to be reactive for child its up to the
            parent implementation. In the case of ClutterGroup it
            being marked unreactive does not mark all children unreactive.
          * Clutter stage is always reactive.


 b. Private ClutterActor flags

CLUTTER_ACTOR_IN_DESTRUCTION
        Set internally by clutter_actor_destroy().
        Used to avoid uneeded overhead when freeing GPU resources on unrealize

CLUTTER_ACTOR_IS_TOPLEVEL
        Set internally by the initialization of ClutterStage

CLUTTER_ACTOR_IN_REPARENT
        Set internally by clutter_actor_reparent(). This flag
        optimizes the reparent process by avoiding the need
        to pass through an unrealized state when the actor is
        removed from the old parent.

CLUTTER_ACTOR_ABOUT_TO_UNPARENT
        Set internally during part of clutter_actor_unparent().
        Causes the actor to pretend it has no parent, then
        update invariants; which effectively forces the actor
        to unrealize. The purpose of this is to unrealize _before_ the
        actor is removed from the stage, so unrealize implementations
        can use clutter_actor_get_stage().

CLUTTER_ACTOR_IN_PAINT:
        Set internally by clutter_actor_paint()

CLUTTER_ACTOR_IN_RELAYOUT
        Set internally by clutter_relayout()

 c. Private Pick Modes

CLUTTER_PICK_NONE
        No pick operation is performed during the paint

CLUTTER_PICK_REACTIVE
        Only reactive actors will be picked

CLUTTER_PICK_ALL
        All visible actors will be picked

 ii. Invariants
-------------------------------------------------------------------------------

This section describes the various constraints and invariants on ClutterActor.

In the following

        A  => B         means   if A is true then B is true
        A <=> B         means   A is true if and only if B is true
                                (equivalent to A => B && A <= B)

1) CLUTTER_ACTOR_IN_DESTRUCTION => !CLUTTER_ACTOR_IS_MAPPED (actor) &&
                                   !CLUTTER_ACTOR_IS_REALIZED (actor)

        clutter_actor_destroy() will cause an actor to be unparented,
        which means the actor must be unmapped and unrealized as
        well.

2) CLUTTER_ACTOR_IS_MAPPED (actor) => CLUTTER_ACTOR_IS_REALIZED (actor)

        when an actor is mapped, it must first be realized.

        This is the most common way an actor becomes realized.

3) if clutter_actor_set_parent (actor, parent):
     ((parent_is_not_toplevel && CLUTTER_ACTOR_IS_MAPPED (parent)) ||
      (parent_is_toplevel && CLUTTER_ACTOR_IS_VISIBLE(parent))) &&
     CLUTTER_ACTOR_IS_VISIBLE (actor)
                                  => CLUTTER_ACTOR_IS_MAPPED (actor)

        calling clutter_actor_set_parent() on an actor and a mapped
        parent will map the actor if it has been shown.

4) if clutter_actor_unparent (actor):
     CLUTTER_ACTOR_IS_MAPPED (actor) <=> CLUTTER_ACTOR_IN_REPARENT

        calling clutter_actor_unparent() on an actor will unmap and
        unrealize the actor since it no longer has a toplevel.

        calling clutter_actor_reparent() on an actor will leave the
        actor mapped and realized (if it was before) until it has a
        new parent, at which point the invariants implied by the new
        parent's state are applied.

5) CLUTTER_ACTOR_IS_REALIZED(actor) => CLUTTER_ACTOR_IS_REALIZED(parent)

        Actors may only be realized if their parent is realized.
        However, they may be unrealized even though their parent
        is realized.

        This implies that an actor may not be realized unless
        it has a parent, or is a toplevel.

        Since only toplevels can realize without a parent, no actor
        can be realized unless it either is a toplevel or has a
        toplevel ancestor.

        As long as they are unmapped, actors may be unrealized. This
        will force all children of the actor to be unrealized, since
        children may not be realized while parents are unrealized.

6) CLUTTER_ACTOR_IS_MAPPED(actor) <=>
      (  (  (CLUTTER_ACTOR_IS_VISIBLE(toplevel_parent) &&
             CLUTTER_ACTOR_IS_REALIZED(toplevel_parent)) ||
             CLUTTER_ACTOR_IS_MAPPED(non_toplevel_parent)  )  ) &&
       CLUTTER_ACTOR_IS_VISIBLE(actor)

       Actors _must_ be mapped if and only if they are visible and
       their parent is mapped, or they are visible and their
       parent is a toplevel that's realized and visible.

       This invariant enables us to track whether an actor will
       be painted (whether it's MAPPED) without ever traversing
       the actor graph.

 iii. State changes
-------------------------------------------------------------------------------

clutter_actor_show:
        1. sets VISIBLE
        2. sets MAPPED if invariants are met; mapping in turn sets
           REALIZED

clutter_actor_hide:
        1. sets !VISIBLE
        2. unsets MAPPED if actor was mapped previously
        3. does not affect REALIZED

clutter_actor_destroy:
        1. sets CLUTTER_ACTOR_IN_DESTRUCTION
        2. unparents the actor, which in turn implies unmap and unrealize

clutter_actor_realize:
        1. attempts to set REALIZED on all parents, failing if
           invariants are not met, e.g. not in a toplevel yet
        2. sets REALIZED on actor if parent was successfully realized

clutter_actor_unrealize:
        1. sets !VISIBLE which forces !MAPPED
        2. sets !REALIZED
        3. !MAPPED and !REALIZED forces unmap and unrealize of all
           children

clutter_actor_set_parent:
        1. sets actor->parent
        2. if actor->show_on_set_parent is TRUE calls clutter_actor_show
        3. sets MAPPED if all prerequisites are now met for map
        4. if !CLUTTER_ACTOR_IN_REPARENT emits ::parent-set with
           old_parent set to NULL

clutter_actor_unparent:
        1. unsets actor->parent
        2. if !CLUTTER_ACTOR_IN_REPARENT, sets !MAPPED and !REALIZED
           since the invariants for those flags are no longer met
        3. if !CLUTTER_ACTOR_IN_REPARENT emits ::parent-set with
           old_parent set to the previous parent

clutter_actor_reparent:
        1. sets CLUTTER_ACTOR_IN_REPARENT
        2. emits ::parent-set with old_parent set to the previous parent
           equivalent to:
                clutter_actor_unparent
                clutter_actor_set_parent
        3. updates state of the actor to match invariants
           (may change MAPPED or REALIZED in either direction,
           depending on state of the new parent)


 iv. Responsibilities of a ClutterActor
-------------------------------------------------------------------------------

 a. Adding to a container

When adding an actor to a container, the container must:

 1. call clutter_actor_set_parent (actor, container)
 2. call clutter_actor_queue_relayout (container) if
    adding the actor changes the container's preferred
    size

 b. Removing from a container

When removing an actor from a container, the container must:

 1. call clutter_actor_unparent (actor)
 2. call clutter_actor_queue_relayout (container) if removing
    the actor changes the container's preferred size

Notes:

* here a container actor is any actor that contains children actors; it
  does not imply the implementation of the ClutterContainer interface.

* clutter_actor_unparent() will unmap and unrealize the actor except
  in the special case when CLUTTER_ACTOR_IN_REPARENT is set.

* 'Composite' Clutter actors need to pass down any allocations to children.

 c. Initial state

When creating an actor, the initial state is:

 1. !CLUTTER_ACTOR_REALIZED
 2. !CLUTTER_ACTOR_MAPPED

===============================================================================
$LastChangedDate$
