Skip to content
enderger edited this page Jun 15, 2020 · 2 revisions

Layout DSL

The layout DSL (Domain Specific Language) is designed to help with common use-case layout definitions. Here is an example:

import nimx / [ window, button, layout ]

let
   w = newWindow(newRect(50,50,400,200))
   margin = 5.0

w.makeLayout:
   - Button:
      leading == super.leading + margin
      trailing == super.trailing - margin
      top == super.top + margin
      bottom == super.bottom - margin

      title: "Hello"
      onAction:
         echo "Hello, world!"

The makeLayout macro accepts the View as a first argument. The view is where the layout should happen. The second argument is the body of the DSL. It has the following syntax:

DSL ::= ViewConfiguration

ViewConfiguration ::= ViewConfigurationStatement*

ViewConfigurationStatement ::=
      "discard"
    | SubviewDefinition
    | PropertyDefinition
    | ConstraintDefinition

SubviewDefinition ::= "-" ( ViewType | ViewCreationExpression ) ( "as" Identifier )? ":" ViewConfiguration
PropertyDefinition ::= PropertyName ":" PropertyValue
ConstraintDefinition ::= ConstraintExpression (ConstraintPriority)?
  • In the example above, the - Button: and everything that follows is the SubviewDefinition. Button is the ViewType. Note that the types have to be valid symbols in the scope of the layout definition, that's why the sample code imports button.

  • title: "Hello" is the property definiton of the button. If the button was bound to a variable myButton, this would be equivalent to myButton.title = "Hello".

  • onAction: ... is also a property, but with a special rule: Properties starting with "on" and with an uppercased third letter are treated as callback properties. So this would expand to roughly

    myButton.onAction = proc() =
       echo "Hello, world!"

    If your callback takes arguments or returns something, you can use the do-notation:

    - MyControl:
       onSomeEvent do(e: EventData) -> EventDataResult:
          echo "hi"
  • leading == super + margin is a constraint definition, consisting of constraint expression, and default priority. Constraint expression should always be one of the following comparison expressions: <=, ==, or >=. Constraint expressions can refer to the following "variables": width, height, left, right, x, y, top, bottom, leading, trailing, centerX, centerY, origin, center, size.

Examples

  • mySubView should completely fill the mainView:
mainView.makeLayout:
 - View as mySubView:
    center == super.center
    width == 20
    height == 20
  • mySubView should be of size 20x20 and be centered within the mainView:
mainView.makeLayout:
 - View as mySubView:
    center == super.center
    width == 20
    height == 20

Note that there is a special word super in the above examples. This is a placeholder designating the superview of the currently defined view. There are other placeholders:

  • self - The currently defined view
  • prev - The previous sibiling of the currently defined view
  • next - The next sibining of the currently defined view

Note that the pattern of something == super.something is pretty common, so there is a special case to make it shorter:

mainView.makeLayout:
 - View:
    center == super

This works because the left side of the constraint consists of exactly one identifier which is treated as the subject. When super, self, prev, or next are met in the expression and it is not within the dot-expression, it is treated as a dot expression within the subject.

If the left side of the expression is not a single identifier, then there is no subject and thus such a shortcut would not work:

mainView.makeLayout:
   - View:
      width + 20 == super # Will not compile
      width + 20 == super.width # Will compile
      width == super - 20 # Will compile
Clone this wiki locally