Philosophy

UI Layout: Natural versus dependent size

Elements have either a natural size, a dependent size, or combination of the two. The size of an element influences its placement. The user and creator of a UI layout system must have shared expectations about sizing.

This article is part of a series on Writing a UI Engine.

A Rectangle

Let’s work through the concepts first using a basic Rectangle. Without any other specification, a Rectangle doesn’t have any size. If you were asked, what the natural size of it was, you wouldn’t have an answer, or you might potentially answer zero.

Contrast now to a Rectangle{Width=100 Height=50}. This rectangle has a fixed size 100,50. The layout engine can ask for its size, and it will get this value; it knows how big this rectangle is supposed to be.

A layout engine that required everything to have a fixed sized wouldn’t be useful. We need a way to give the first plain Rectangle a size. Instead of saying it has no size, we define its size relative to its parent. Consider this basic UI tree:

Panel{Width=100 Height=100}
    Rectangle

We allow the rectangle to expand to the available space: the size of the parent. Our rectangle now has a size of 100,100. The expanding child is an example of dependent sizing.

It’s also convenient to have a partially dependent size, such as in this example:

Panel{Width=100 Height=100}
    Rectangle{Width=50}

We take the width from the rectangle properties, and the height from the parent, resulting in a size of 50,100. This type of dependent sizing is common in layout. There is, of course, the issue of where to place that rectangle in the panel now, but we’ll get to that later.

An Image

An image is interesting as it can both have a natural size and a dependent size. Consider an image, fun.png, with the dimensions 200×100. The dimensions of the image seem like a reasonable natural size — the UI engine can ask for the size of the image and get 200,100 as a response.

We have to be careful in interpreting what 200,150 means here. This is the source resolution, but does that line up with device points, pixels, millimetres, or something else? You may also have images suitable for different density displays. A 200×100 image is for a 1:1 pixel-to-point density, whereas a 400×200 may be intended for a 2:1 pixel-to-point density.

Images can also completely stretch to fill their parent.

Panel{Width=300 Height=200}
    Image{File=fun.png}

The Image element will have a size of 300,200. Now, it’s up to the image type itself to decide how to stretch the actual image to fill that area. The difference between an element’s size, and the actual size of its contents, will be a recurring concept . (We’ll get back to image sizing in a later article)

An image defines a ratio; our 200×100 image has a 2:1 size ratio. This is also good for defining partially dependent sizes. Consider stacking images in a StackPanel:

StackPanel{Width=300}
    Image{File=fun.png}

The parent has a width of 300, and the image is expected to fit inside. In this situation we use the size ratio of 2:1 to come up with a height of 150. This gives the image a reasonable size based on the parent and retains the aspect ratio — as to not distort the image.

Text

Text also has a fixed and dependent size, but the relationship is different than an image. Let’s first consider a case that doesn’t involve soft wrapping: long lines do not wrap around. Absent of wrapping text has a fixed natural size. Sure, it’s dependent on the font, but that’s part of the properties of the text, not the layout.

Text{Font=Arial FontSize=32 Value="Hello"}

The string Hello has a fixed size using Arial font at point size 32.

Wrapped text has a dependent size. Consider this example:

Panel{Width=200}
    Text{Font=Arial FontSize=32 Wrap Value="Welcome a bit longer text that might span multiple lines"}

Given that the parent panel has a width of 200 we’d expect the text not to be wider than 200. When queried for its size the text element will use the knowledge of this width, calculate line wrapping, and come up with a height to cover all the lines.

Moving on

It’s critical to understand natural and dependent sizing to write a UI engine. Here we’ve only touched on the basic possibilities, defining what this means, and giving the basis for reasoning about size.

In later articles, we’ll cover how an element gets its dependency information to calculate its size.

Read more articles about writing a UI Engine. Follow me on Twitter or Facebook, and to keep me writing, consider becoming a patron.

Categories: Philosophy, Programming

Tagged as: , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s