Measurement and Layout

When laying out interface elements, you often need to know what size something is. Images have an intrinsic size; text is sized based on characters in a font. You can use measure to determine this size:

var size = measure(0, 0, text("Hello!"))

The first two parameters to measure are the proposed width and height. If the drawable can resize itself, it should give a measurement as "close" as possible to the proposed size. What "close" means is up to the drawable itself. For example, a multiline drawable uses the proposed width to wrap text:

var txt = multiline("A quick brown fox jumps over the lazy dog.")
show(measure(100, 1000, txt).height)
show(measure(200, 1000, txt).height)

To measure a drawable, the code in the drawable is run, but nothing is actually drawn. Drawables indicate their size using the measurement statement, which takes a width and a height expression:

drawable box {
    draw fill(red)
    measurement 200, 100
show(measure(0, 0, box))

This measurement statement is ignored when drawing, but it acts like a return when measuring. The first measurement is passed out of the drawable and used as the return value of measure. If no measurement statement is run, measure simply returns the proposed width and height it was called with.

Notice how centered uses the box's measurement to determine where to position it. Try changing the box's measurement and see how its position changes.

Inside of a drawable, you can determine the current width and height using @width and @height.

drawable showDimensions {
    draw fill(gray(0.8))
    draw centered(text("\(@width) x \(@height)"))
draw at(0, 0, 200, 100, showDimensions)
draw at(0, 200, 250, 250, showDimensions)

The @ symbol itself represents the current size as a record:


While measuring, @width and @height are set to the proposed width and height:

drawable box {
    draw fill(red)
    measurement @width, @width / 2
draw centered(box)

Here we've constrained the height of box to be half its width. Notice how the box reacts to being centered.

You can use this system to create drawables which lay out other drawables based on their measurements. For example, here's the definition of centered:

drawable centered(d) {
    var size = measure(@width, @height, d)
    draw at((@width - size.width) / 2,
        (@height - size.height) / 2,
        size.width, size.height, d)
    measurement max(@width, size.width), max(@height, size.height)