Blog

DigitalRune Game UI: The GUI Layout Process

Sep 26

Written by:
Monday, September 26, 2011  RssIcon

Here is another in-depth article about the DigitalRune Game UI. This article discusses the layout process of GUI controls.

UIControl_LayoutMembersDefining the Size

Each UIControl has a Width and a Height property which define the control size. The default value is NaN , which means that the control automatically determines its size. For example: The size of an Image control is determined by the drawn image. The size of a TextBlock is determined by the text. If a control has child controls, like a Panel or a ContentControl, the size is usually determined by the child controls.

A control also has the properties MinWidth, MinHeight, MaxWidth and MaxHeight. They default to 0 for the minimums and NaN for the maximums, which means that there are no size restrictions. These properties limit the actual control size.

Defining the Position

There a several mechanisms that influence the position of a control.

Parent Controls

GUI controls are organized in a tree. The control at the root is the UIScreen. The UIScreen contains other child controls, like Windows, Panels, Buttons. Each control can in turn contain other controls. A parent control defines how it positions child elements. Most other controls allow the children to position themselves freely within the parent control space using the properties described below.

Panels are special controls – their whole purpose is to arrange child elements using a specific layout strategy. Current panel controls are the Canvas, which allows free positioning of the children, and the StackPanel, which arranges child controls in a horizontal or vertical stack.

Margin and Padding

Margin and Padding are two UIControl properties. Each is a 4-dimensional vector specifying the left, top, right, and bottom values. The margin is added around a control. Per default, it is (0, 0, 0, 0). In a panel (e.g. a StackPanel) a margin can be added to the controls to create some empty space between them. The padding is usually the space between the outer border of a control and contained child controls. However, different types of controls may interpret the Padding property differently.

HorizontalAlignment and VerticalAlignment

Using the properties HorizontalAlignment and VerticalAlignment, a control can determine its alignment in the area of the parent control.

X and Y

Each UIControl has an X and a Y property. These two properties are only relevant for children of a UIScreen or a Canvas panel. X/Y determine the position of the control’s top left corner on the screen or in the canvas. – X and Y should only be used for controls that are left/top aligned.

The Layout Process

As mentioned above, GUI controls are organized in a tree. The control at the root is the UIScreen. The screen contains child controls, which in turn contain other controls, and so on. The layout process consists of two traversals of the whole control tree: The measure pass and the arrange pass. First all controls are measured and determine their desired own size. In the second pass all controls are told where on the screen they should be rendered.

The layout process is started by calling UpdateLayout() on the root control. It is not necessary to call this manually. The UIScreen will automatically call UpdateLayout() when needed.

The Measure Pass

In the measure pass the method Measure(Vector2F availableSize) is called on each control with the available size for the control. Each control can determine how big it wants to be. The measure results are stored in the DesiredWidth and DesiredHeight properties.

Controls can override the method OnMeasure(Vector2F availableSize). This method should call Measure() on child controls and then return the desired size. A control can ignore the availabeSize parameter and return any value. Some controls will want to adapt to the available size (e.g. a TextBlock with text wrapping will wrap the text at the bounds defined by the available size).

To learn how to implement OnMeasure() in custom controls, have a look at the DigitalRune Game UI source code. See how the UIControl base class implements the method. Then look at a control that is similar to the control you want to create and see how OnMeasure() is implemented.

The results of the measure pass are cached until a UI control property with the AffectsMeasure flag (see article UI Control Properties) is modified or UIControl.InvalidateMeasure() is called manually. The flag UIControl.IsMeasureValid indicates whether the current measure results are valid.

The Arrange Pass

In the arrange pass the method Arrange(Vector2F position, Vector2F size) is called on each control. The method tells a control the rectangle on screen that the control can use. Each control subtracts its own margin and sets its ActualX, ActualY, ActualWidth and ActualHeight properties. This properties define the rectangle relative to the screen that this control can use. A UIControl also has an ActualBounds property to get this rectangle:

public RectangleF ActualBounds
{
  get { return new RectangleF(ActualX, ActualY, ActualWidth, ActualHeight); }
}

Often a control will get exactly its desired size to arrange into. If it gets more space, it is allowed to fill the whole space. It can also happen that the actual size is less than the desired size when there is not enough space on the screen or in a window. In this case a well-behaved control only draws within the actual bounds. A misbehaving control may draw outside the allowed bounds and hope that the parent control automatically clips the drawing. Some controls like the ContentControls have a ClipContent property. If this is set to true, they will clip the content automatically during the render process.

If the desired size of a child control is smaller than the actual size of the parent control, then the properties HorizontalAlignment and VerticalAlignment are used to determine the relative position of the child control in the parent control.

Arrange pass results are cached until a UI control property with the AffectsMeasure flag or a AffectsArrange flag is modified or UIControl.InvalidateMeasure() or UIControl.InvalidateArrange() is called. The flag UIControl.IsArrangeValid indicates whether the current actual positions are valid.

Conclusion

This GUI layout mechanism was heavily inspired by Silverlight and WPF. It allows to create flexible GUI layouts. And by creating new Panel controls, which override OnMeasure() and OnArrange(), developers can add new layout strategies.


Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel