Skip to main content

Layout

Layout is probably the most difficult topic in any UI system. It's not only how the API gets used but its often a mindset about how to approach the problem at hand. Sometimes you get lucky and the design you are implementing is simple enough that built in layout components can mostly get you there, but in almost every design I've ever personally worked with, I needed to get my hands dirty and get creative with how the layout was implemented.

The goal of this document is to orient you with 'how-to-think-about-layout' in Evolve.

The basics

We'll start relatively simple. We have two cases: template and typography. Both cases follow the same default rule: they size themselves according to their content. If there is no content, the size is 0 by default.

So what is 'content'? This can be text content in the case of typogrophy, padding, and/or other child elements.

The box model

Evolve uses the standard CSS border-box model.

+-----------------------------+
| Margin |
| +---------------------+ |
| | Border | |
| | +---------------+ | |
| | | Padding | | |
| | | +---------+ | | |
| | | | Content | | | |
| | | +---------+ | | |
| | +---------------+ | |
| +---------------------+ |
+-----------------------------+
  • Margin -- Space between sibling elements in
  • Padding -- Space between the edge of element's border and it's content
  • Border -- Space between the theoretical edge of the element and it's padding
  • Content -- All the things inside the element, ie text, images, other elements

Sizes

Evolve uses a handful of measurement types to measure elements, paddings, offsets, alignments, etc. The most important ones are UIMeasurement for sizing elements and UISpaceSize for paddings and margins.

Then to actually set an element's size, we use the style properties

  • PreferredWidth
  • PreferredHeight
  • MinWidth
  • MinHeight
  • MaxWidth
  • MaxHeight

We also have the shorthands which set both width and height axes at the same time

  • PreferredSize
  • MinSize
  • MaxSize

We use Preferred width/height because the layout system has some ways of increasing/reducing the size of elements based on the algorithm used, ie if the PreferredWidth is 300px but the MaxWidth is 200px, the element will clamp to it's max size, disregarding the PreferredWidth value.

See the docs on style properties for more details.

Templates

Templates will default to their content size on both the width and the height axis, and will arrange their children vertically. A side effect of defaulting to content size is that by default, with no content, an element's size is 0 on both width and height.

template Element; // no content, no style means these have no size unless they have content. 
template Basics {
// 3 elements with no size means the parent has no size.
render {
Element();
Element();
Element();
}
}

If we were to render this template, we'd see nothing. Let's give those some sizes:

style big {
PreferredSize = 300px;
BackgroundColor = red;
}
style small {
PreferredSize = 100px;
BackgroundColor = blue;
}
template Element; // no content, no style means these have no size unless they have content.
template Basics {
// 3 elements with no size means the parent has no size.
render {
Element(style = [@small]);
Element(style = [@big]);
Element(style = [@small]);
}
}

This would render the boxes vertically with their respective sizes, so the total width of Basics would be 300px, and the total height would be 500px

We could change this to a horizontal layout like this:

// the <> syntax matches based on an element name
style <Basics> {
LayoutType = Horizontal;
}

style big {
PreferredSize = 300px;
}

style small {
PreferredSize = 100px;
}

template Element;

template Basics {
render {
Element(style = [@small]);
Element(style = [@big]);
Element(style = [@small]);
}
}

This would render the boxes vertically with their respective sizes, so the total width of Basics would be 500px, and the total height would be 300px

Typography

Typography actually follows the exact same rules, except that usually when we think about typography elements, we assume they contain some text. So text is content, which means that a typography element will default to be as big as its text.

This also means that because the text layout is greedy, unless a text is given a width or some explicit new lines, it will not wrap by default.

In order for text to wrap, it needs to be given a non-content sized width, or it needs to contain new lines and have it's LineBreakModeset to NewLinesOnly.