Layouts

The Cascades framework uses layouts to construct UIs. A layout provides the basic design for a container in your application. Cascades supports a variety of different layouts that you can use to arrange and position controls in your apps. You can stack controls horizontally or vertically, align controls to the top or sides of a container, and even place controls at specific positions on the screen.

Each layout has its own set of properties that determine how child controls are arranged in the layout (for example, whether controls should be arranged from left to right or from top to bottom). You can also specify layout properties for each child control to determine the position and relative size of the control. By using the right layouts in your applications, you can create a UI that looks great and scales easily even if the sizes of your UI components change.

Overview of the available layouts

Cascades provides three types of layouts. You can use as many layouts as you want in your apps, and you can also use layouts within other layouts (for example, a stack layout inside an absolute layout).

Stack layout

StackLayout is the default layout for containers in Cascades. It allows you to stack controls vertically or horizontally. You can specify properties that stack the controls in a particular direction (such as from left to right or from bottom to top), and you can set margins (empty space) between the controls.

You could use this layout if you want to arrange controls next to each other without specifying the precise position of each control. This layout also provides a lot of flexibility when adding and removing controls. When you remove a control from a stack layout, the surrounding controls are rearranged automatically to fill the empty space.

Screen showing four Button controls arranged in a stack layout.

Dock layout

DockLayout aligns controls to different positions in a container, such as the top, right, bottom, or left edge. You can also specify that a control should fill the available width or height of the container.

This layout is a good choice if you want to specify the positions of controls in relation to other controls and the overall UI. For example, a control that's aligned to the top of its parent container will always remain at the top, even if the size or position of the parent container changes or if you rescale your UI.

Screen showing three Button controls arranged in a dock layout.

Absolute layout

AbsoluteLayout lets you specify exactly where controls should be located on the screen. You specify x and y coordinates (in pixels) for the location of the controls in their parent container.

This is an appropriate layout to use if you're creating a complicated UI that requires precise positioning of controls. You can also choose to use this layout if you're creating your app for a specific screen resolution. Keep in mind that while stack layouts and dock layouts make it easy to create a UI that's independent of screen resolution, this isn't necessarily the case with absolute layouts. When you use an absolute layout, your UI may not appear correctly if the containers in your app change in size or position, or if your app runs on a device with a different screen resolution.

Screen showing three Button controls arranged in an absolute layout.

Using layouts

It's easy to start designing screens with layouts. In QML, simply set the  layout  property of a container to the type of layout that you want to use: StackLayout, DockLayout, or AbsoluteLayout. Here's how to create a container that uses a stack layout:

import bb.cascades 1.0
 
Page {
    content: Container {
        layout: StackLayout {}
    }
}

To do the same thing in C++, you create an object to represent the container, as well as an object to represent the layout that you want to use. Then, you set the layout for the container by using the setLayout() function:

// Create the root page and container
Page* root = new Page;
Container* myContainer = new Container;
 
// Create a stack layout and set it on the container
StackLayout* layout = new StackLayout;
myContainer->setLayout(layout);
 
// Set the content of the page
root->setContent(myContainer);

In Cascades, all content for a screen must be added to a root element that inherits from the AbstractPane class. In the examples above, the Page class is used as the root element. For simplicity, some of the subsequent code samples might omit the Page element.

Layout properties

Each layout has a set of supported properties that determine how child controls are arranged. For example, stack layouts support a property called orientation, which determines the direction that controls are added in. The default layout orientation for a stack layout is from top to bottom, but you can use layout orientations in the  LayoutOrientation class (such as LayoutOrientation.LeftToRight and LayoutOrientation.BottomToTop) to change that behavior.

You add the values for various properties inside the layout object that you specify for layout (that is, inside StackLayout DockLayout, and so on). Here's how to create a stack layout with a left-to-right layout orientation in QML:

Container {
    layout: StackLayout {
        orientation: LayoutOrientation.LeftToRight
    }
}

In C++, you can create the stack layout by using a builder (denoted by StackLayout::create()). The StackLayout class is one of many C++ classes in Cascades that can use the builder design pattern to create objects.

Container* myContainer = new Container;
StackLayout* layout = StackLayout::create()
                         .orientation(LayoutOrientation::LeftToRight);
myContainer->setLayout(layout);

For a list of all layout properties and builder arguments that each layout class supports, see the API reference for the layout class that you're interested in.

Child layout properties

In addition to the properties that you set for a layout, you can also set properties for the child controls that you add to the layout. You use the layoutProperties property to specify items such as alignment (in the case of a stack layout) and position (in the case of an absolute layout). This property accepts an object that matches the layout of the control's parent container. For example, if the container uses a  StackLayout, then layoutProperties accepts a  StackLayoutProperties object containing the property values that you want to set.

Here's how to create a container with an absolute layout, in QML, and add two Label controls to the layout. Layout properties are used to position each label at a precise location inside the absolute layout.

Container {
    layout: AbsoluteLayout {}
     
    Label {
        layoutProperties: AbsoluteLayoutProperties {
            positionX: 200
            positionY: 300
        }
        text: "Label 1"
    }
     
    Label {
        layoutProperties: AbsoluteLayoutProperties {
            positionX: 400
            positionY: 700
        }
        text: "Label 2"
    }
}

Here's how to create the same layout in C++.

// Create the container and layout
Container* myContainer = new Container;
AbsoluteLayout* layout = new AbsoluteLayout;
myContainer->setLayout(layout);
 
// Create the layout properties
AbsoluteLayoutProperties* alp1 = AbsoluteLayoutProperties::create()
                                 .x(200)
                                 .y(300);
AbsoluteLayoutProperties* alp2 = AbsoluteLayoutProperties::create()
                                 .x(400)
                                 .y(700);                         
                               
// Add the labels and set the layout properties for each
myContainer->add(Label::create()
                 .text("Label 1")
                 .layoutProperties(alp1));
myContainer->add(Label::create()
                 .text("Label 2")
                 .layoutProperties(alp2));

The layoutProperties property applies only to stack layouts and absolute layouts. When you use a dock layout, you can specify properties for the child controls directly inside each control. For example, if you want a Button to be centered horizontally inside a dock layout, you can simply use the horizontalAlignment property directly in the Button.

The following code sample creates a container with a dock layout, in QML, and adds two Button controls to the layout. Each button is located in a different horizontal and vertical position in the container, and the layoutProperties property isn't required.

Container {
    layout: DockLayout {}
 
    Button {
        horizontalAlignment: HorizontalAlignment.Right
        verticalAlignment: VerticalAlignment.Bottom
    }
 
    Button {
        horizontalAlignment: HorizontalAlignment.Center
        verticalAlignment: VerticalAlignment.Center
    }
}

Here's how to accomplish the same thing in C++:

// Create the container and layout
Container* myContainer = new Container;
DockLayout* layout = new DockLayout;
myContainer->setLayout(layout);
                               
// Add buttons and specify their alignments
myContainer->add(Button::create()
                 .horizontal(HorizontalAlignment::Right)
                 .vertical(VerticalAlignment::Bottom));
myContainer->add(Button::create()
                 .horizontal(HorizontalAlignment::Center)
                 .vertical(VerticalAlignment::Center));

Using margins

By default, UI controls are positioned with a small amount of space between each other in a layout. If you want to add more space between controls, you can set margins by specifying values for a control's margin properties: leftMarginrightMargintopMargin, and bottomMargin.

Margins refer only to the space between adjacent controls in a layout. You can also add space between a control and the edge of its parent container. This space is called padding, and is discussed in the next section.

If two controls with margins are positioned next to each other in a layout, their margins collapse and the space between them becomes the larger of the two margins. For example, consider two controls that are positioned next to each other in a left-to-right stack layout. If the right margin of the first control is 20 pixels and the left margin of the second control is 10 pixels, the actual space between the controls will be 20 pixels. The 10 pixel margin collapses into the larger 20 pixel margin.

The image below illustrates how margins collapse between controls. The gray areas represent the margins around each text control, and you can see how the margins between adjacent controls overlap (collapse). In your own apps, you might use different margins for different text sizes, but use the same margins for all other controls.


Diagram showing margin overlap.

Here's how to create a top-to-bottom stack layout with three buttons, each of which has a top and bottom margin, in QML. The margin between the second and third buttons is collapsed and takes on the value of the larger margin.

Container {
    layout: StackLayout {}
 
    Button {
        bottomMargin: 10
    }
 
    Button {
        bottomMargin: 10
    }
 
    Button {
        topMargin: 50
    }
}

In C++, the process is similar:

// Create the container and layout
Container* myContainer = new Container;
StackLayout* layout = new StackLayout;
myContainer->setLayout(layout);
 
// Create buttons with margins
myContainer->add(Button::create()
              .bottomMargin(10));
myContainer->add(Button::create()
              .bottomMargin(10));
myContainer->add(Button::create()
              .topMargin(50));

The code samples above both generate the button arrangement that you see to the right.

A screen shot showing three Button controls in a stack layout.

Using padding

When you add UI controls to a container, the controls are positioned at the edges of the container unless you specify a center alignment for the control. You can use padding to add empty space between the edges of the container and the controls inside it. By using padding, you can create the same amount of space between a container's edge and all of its child controls, instead of specifying margins for each child control separately.

You specify padding directly inside the control that you want the padding applied to (for example, inside a container if you want to create space between the edge of the container and the controls inside it). You can use the topPaddingbottomPaddingleftPadding, and rightPadding properties to create padding for the corresponding edge of the container.

The following images demonstrate what padding looks like. The image on the left doesn't include any padding, while the image on the right uses the topPadding and leftPadding properties:


Diagram showing the use of the topPadding and leftPadding properties.

Here's how to create the layout that you see in the image on the right, in both QML and C++:

Code sample: Applying padding in QML

Container {
    layout: StackLayout {}
    topPadding: 30
    leftPadding: 30
 
    Button {}
 
    Button {}
}

Code sample: Applying padding in C++

Container* myContainer = Container::create()
                         .top(30)
                         .left(30);
StackLayout* myLayout = new StackLayout;
myContainer->setLayout(myLayout);
 
myContainer->add(Button::create());
myContainer->add(Button::create());

Using space quotas

You can specify the size of UI controls in several ways in your applications. A common way is to use the preferredWidth and preferredHeight properties to determine the size of a control. However, this approach might not work very well if the screen resolution changes or if you change the structure of your UI. Instead, you can direct UI controls to use as much space as is available in their parent container, as well as maintain sizes relative to each other, by using space quotas.

Space quotas apply only to controls in a stack layout and are set by using the spaceQuota child layout property. The spaceQuota property determines how much a control should expand or shrink to fill the available space in a layout. This property can have a negative or positive value:

  • A negative spaceQuota value indicates that the control uses as much space as necessary to appear correctly in the layout, but no more. The control won't expand or shrink to fill any available space in the layout. This is the default behavior if you don't specify a value for spaceQuota.
  • A positive spaceQuota value indicates that the control uses as much space as it can with respect to other controls in the same container, and expands and shrinks depending on the available space. The amount that the control expands or shrinks depends on the value of spaceQuota. A control with a space quota of 2 expands to use twice as much space as a control with a space quota of 1. Similarly, a control with a space quota of 2 shrinks twice as much as a control with a space quota of 1.

You can think of positive space quotas as forming a ratio that represents the relative sizes of the controls. A control with a space quota of 3 and a control with a space quota of 2 form a ratio of 3:2, and the sizes of the controls will always remain in that ratio. Space quotas of 3 and 2 are equivalent to space quotas of 6 and 4 (or 27 and 18) because the ratio is still 3:2.

The following image demonstrates how different space quotas determine the sizes of controls in a layout. Each row of buttons represents a container with a left-to-right stack layout, and the numbers on each button indicate the value of the button's spaceQuota property:


Diagram showing the use of the spaceQuota propery on a series of Button controls.

In the image above, the last row of buttons contains a button with a negative space quota. When combined with controls that have positive space quotas, controls with negative space quotas take as much space as the control needs (the default height or width of the control). Any remaining space is divided between the controls with positive space quotas, depending on their specific space quota values.

The following code samples create a left-to-right stack layout, in QML, and add three buttons to the layout. Each button has a different space quota that determines its relative size.

Container {
    layout: StackLayout {
        orientation: LayoutOrientation.LeftToRight
    }
 
    Button {
        layoutProperties: StackLayoutProperties {
            spaceQuota: 1
        }
    }
 
    Button {
        layoutProperties: StackLayoutProperties {
            spaceQuota: 2
        }
    }
 
    Button {
        layoutProperties: StackLayoutProperties {
            spaceQuota: 3
        }
    }
}

Here's the same layout in C++:

// Create the container and layout
Container* myContainer = new Container;
StackLayout* layout = StackLayout::create()
                       .orientation(LayoutOrientation::LeftToRight);
myContainer->setLayout(layout);
 
// Create three sets of stack layout properties
StackLayoutProperties* slp1 = StackLayoutProperties::create()
                               .spaceQuota(1);
StackLayoutProperties* slp2 = StackLayoutProperties::create()
                               .spaceQuota(2);
StackLayoutProperties* slp3 = StackLayoutProperties::create()
                               .spaceQuota(3);
 
// Add buttons that use the stack layout properties
myContainer->add(Button::create().layoutProperties(slp1));
myContainer->add(Button::create().layoutProperties(slp2));
myContainer->add(Button::create().layoutProperties(slp3));

The code samples above both generate the following button arrangement:


Diagram showing three Button controls in a left-to-right layout orientation.

Last modified: 2013-12-21

comments powered by Disqus