UI Layout: Natural versus dependent size

edA‑qa mort‑ora‑y - Mar 15 '18 - - Dev Community

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
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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 200x100. 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 200x100 image is for a 1:1 pixel-to-point density, whereas a 400x200 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}
Enter fullscreen mode Exit fullscreen mode

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 200x100 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}
Enter fullscreen mode Exit fullscreen mode

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"}
Enter fullscreen mode Exit fullscreen mode

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"}
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player