Control structures

Binding


/* main.sma */
use core
use base
use display
use gui

_main_
Component root {
  Frame f ("Hello World!", 0, 0, 500, 400)
  Exit quit (0, 1)
  
  Clock cl (1000)
  cl.tick->quit
}

          

The line cl.tick->quit creates a component called a Binding. A Binding creates an activation link between a source component, here the tick of a Clock, and a destination component, here an Exit component. Each time the source is activated, the Binding propagates the activation to the destination. Because a Binding is itself a Component, it can be activated or deactivated.

Connector


/* main.sma */
use core
use base
use display
use gui
          
_main_
Component root {
  Frame f ("Hello World!", 0, 0, 500, 400)
  Exit quit (0, 1)
  f.close->quit

  FillColor fc (200, 50, 50)
  Rectangle r (0, 0, 50, 100, 0, 0)
  f.height => r.height
  f.width =:> r.width
}
          
          

The line f.height => r.height creates a component called a Connector. A Connector is a data-flow component that copies the value of a source property to a destination property each time the value of the source is changed. In this program, the height of the Rectangle will be always the same as the one of the Frame. The next line is also a Connector. The difference is that there is a first copy on tree traversal. Thus the width of the rectangle will be changed at the start of the program while the height will be changed only if there is a change in the window geometry. The left side of the Connector operator can be any arithmetic or logical expression. For example, if one wants that the width of the rectangle be proportional to the width of the frame, one can write f.width / 5 => r.width.

Assignment


/* main.sma */
use core
use base
use gui
          
_main_
Component root {
  Frame f ("Hello World!", 0, 0, 500, 400)
  Exit quit (0, 1)
  f.close->quit

  FillColor fc (200, 50, 50)
  Rectangle r (0, 0, 50, 100, 0, 0)
  f.height =: r.height
}
          

          

The line f.height =: r.height creates a component called an Assignment. An assigment copies the value of a source property (left side) to a destination property (right side) but only one time, on activation. In this piece of code, the assignment is activated on tree traversal when the root component is started. To activate it another time, it is necessary to embed it into a named AssignmentSequence so that we can bind another component to it:


/* main.sma */
use core
use base
use gui
          
_main_
Component root {
  Frame f ("Hello World!", 0, 0, 500, 400)
  Exit quit (0, 1)
  f.close->quit

  FillColor fc (200, 50, 50)
  Rectangle r (0, 0, 50, 100, 0, 0)
  AssignmentSequence a (1) {
    f.height =: r.height
  }
  r.press->a
}
          

          

Within the AssignmentSequence declaration, a special parameter has been set to 1. This parameter specifies if the AssignmentSequence must be activated on tree traversal or not. If set to 1, it is not activated on tree traversal.

Switch

A Switch is a container, that is a component to which it is possible to add any number of children. Its main feature is that only one of its children can be active at a time. A Switch has a built-in child named state which is a text property. Each time the value of this property is changed, the Switch looks for a child with the same name. If one is found, then the current activated child is deactivated and the other one is activated.


/* main.sma */
use core
use base
use gui

_main_
Component root {
  Frame f ("f", 0, 0, 500, 300)
  Exit ex (0, 1)
  f.close -> ex

  /* style */
  FillColor bg (70, 151, 255)
  OutlineColor oc (240, 240, 240)
  OutlineWidth w (2)
  /* shape */
  Rectangle r (10, 10, 200, 110, 5, 5)

  /* behavior */
  Switch sw (greater) {
    Component greater {
      f.width / 2 => r.x  
    }
    Component lower {
        Double x (10)
        x =: r.x
    }
  }
  f.width > 400 ? "greater" : "lower" => sw.state
}


          

In this example, the string "greater" or "lower" is assigned to the state of the Switch depending on the size of the frame, thus changing the position of the rectangle.

Finite State Machine (FSM)

The Finite State Machine works in a very usual way, that is by specifying states and transition. States are container to which any kind of components can be added, including another FSM. A component can be activated on transition. Below is a code for a very simple button.


  /* main.sma */
  use core
  use base
  use gui

  _main_
  Component root {
    Frame f ("f", 0, 0, 500, 300)
    Exit ex (0, 1)
    f.close -> ex
    Spike click

    FSM fsm {
      State idle {
        FillColor fc (50, 50, 50)
        Rectangle r (0, 0, 100, 70, 10, 10)
      }
      State pressed {
        FillColor fc (150, 50, 50)
        Rectangle r (0, 0, 100, 70, 10, 10)
      }
      idle->pressed (idle.r.press)
      pressed->idle (pressed.r.release, click) // here click is activated
      pressed->idle (f.release)
    }
  }


Combining Switch and FSM

A FSM has also a built-in child named state whose value equals the name of the current state of the machine. It is thus possible to combine a FSM and a Switch by connecting their state properties. If you adopt this pattern, be careful to choose the same name for Switch's children and FSM's states.


  /* main.sma */
  use core
  use base
  use gui

  _main_
  Component root {
    Frame f ("f", 0, 0, 500, 300)
    Exit ex (0, 1)
    f.close -> ex
    Spike click

    Switch sw (idle) {
      Component idle {
        FillColor fc (50, 50, 50)
        Rectangle r (0, 0, 100, 70, 10, 10)
      }
      Component pressed {
        FillColor fc (150, 50, 50)
        Rectangle r (0, 0, 100, 70, 10, 10)
      }
    }
    FSM fsm {
      State idle
      State pressed
      idle->pressed (sw.idle.r.press)
      pressed->idle (sw.pressed.r.release, click) // here click is activated
      pressed->idle (f.release)
    }
    fsm.state => sw.state // don't forget to connect FSM and Switch
  }