Create your UI with QML

Now for the fun part - creating your UI! Let's start by double-clicking the main.qml file to open it in the editor. You'll find it in the assets folder of your Cascades project.

In the editor, you can edit both C++ files and QML files. The editor features syntax highlighting, formatting, code folding, and code completion for both C++ and QML.

If you are editing a QML file, you have five buttons at the top of the window. These buttons help you switch between the different layout settings that are available.

Animation showing the layout settings that are available in the QML perspective.

These buttons don't appear if you are editing a C/C++ file.

Let's look at the default code in our main.qml file:

import bb.cascades 1.2

Page {
    Container {
        //Todo: fill me with QML
        Label {
            // Localized text with the dynamic translation 
            // and locale updates support
            text: qsTr("Hello World") 
                + Retranslate.onLocaleOrLanguageChanged
            textStyle.base: SystemDefaults.TextStyles.BigText
        }
    }
}

The root component for the QML document is Page, a subclass of AbstractPane, which essentially represents a page or screen that you want to display in your application. The next component is the root Container for the application. In this case, the only object in the container is a basic Label that has a textStyle set and is marked for translations using the qsTr() function.

Lay out your application

Cascades has a powerful and flexible layout system that positions controls in two ways. The first way is the type of layout that you set for the parent container. For example, a StackLayout specifies that a container's children are positioned beside each other either vertically or horizontally. The second way is the layout properties that you apply to the container's children. These properties specify how the children are positioned relative to each other within the parent's layout, using parameters such as spaceQuota or x and y coordinates.

One thing that you must remember when using layouts and layout properties is that they must match up. For example, when using a StackLayout, children of that container must use StackLayoutProperties. For more information about layouts, see Layouts.

By using the layout system, you can create a UI that adapts to orientation changes, different screen sizes, other languages, and so on. The layout system also helps when you transform your UI during runtime. You can add or remove controls, move controls, or apply animations without having to worry about specific pixel coordinates.

Let's take a look at the different pieces of our UI to see what we need to do to get them arranged properly using layouts:

  • A root container
  • An upper container with two layered images (night image and day image)
  • A lower container with a slider and two icons (moon and sun)
Screen showing the layout of the Lightning Crossfade sample app.

Create the root container

The first thing we must do is create a root Container.

This container will hold all of the app's UI elements. Before we can add the container though, we must remove the Label and Container that's already there.

If you're copying QML code from the tutorial and pasting it directly into your project, you'll notice that errors will sometimes occur. This tutorial is designed so that you can copy and paste each snippet one after another, so the closing braces for some components will not be included until you get to the end of the tutorial.

To add the root container, we'll use the Components view. If you don't see the Components view in your IDE, you can open it using Window > Show View > Other > QML Editing > Components. The Components view contains a set of code templates for controls and navigation elements that you can drag and drop directly into your QML file. When you drop the component into your code, the QML file is automatically populated with the template for the control.

To add the root container, click the Container component, drag it into your QML file, and drop it inside the Page control.

Animation demonstrating how to drag a component from the Components view to your main.qml file.

The template for the container includes an id property, which is used as a unique identifier for the component. We'll set the Container ID to containerID. In addition to the id, there are some other properties we must specify for the container.

To specify properties for our container, we use the QML Properties view. The QML Properties view displays various properties associated with a QML component, such as its layout and layout properties, appearance, and even transformations.

Just click on or within the Container component and the QML Properties view will be populated with all the information about the Container, including a list of other properties that you can use. As you modify the values in the QML Properties view, the QML code in the editor is updated automatically.

In the QML Properties view, select StackLayout as the container's layout. In the Margins & Sizes drop-down, give it 20 pixels of padding at the top.

Screen showing the QML Properties view in the Momentics IDE.

The StackLayout allows child controls to be stacked in any direction (in this case we're using the default, TopToBottom). The padding at the top is used to create some space between the top of the container and the first child added to it.

There's one more property that we need to specify on the root container, but this one must typed in manually: the background color. For our background, let's use a nice, dark color.

background: Color.create ("#262626")

In this instance, the Color is created by specifying RGB values in hex, but there's also the option of using floating point RGB values, or choosing from a selection of predefined color constants.

After you specify the background, your QML file should look something like this:

import bb.cascades 1.2
 
Page {
    Container {
        id: containerID
        background: Color.create ("#262626")
        topPadding: 20.0
        layout: StackLayout {}  
    }
}

Now that our application has a background, let's have a look at it. We could launch the application on a device, but since we just want to see the background, the QML Preview will work just fine. The QML Preview renders your QML code as you type, so you can easily spot anything in your UI that isn't aligned correctly. As you update your code, the QML Preview is updated along with it. You can switch to the preview by clicking the Design Mode button at the top of the editor.


Animation showing what the app looks like in QML Preview at this point in the tutorial and demonstrating the QML preview options.

In the top-right corner of the editor, there is a drop-down list that allows you to change the target resolution. You can also change the orientation of the preview to landscape or portrait orientation. With the help of the buttons at the bottom of the preview area, you can zoom, fit the preview to size of the screen, and set a predefined zoom ratio for your preview.

The Momentics IDE automatically detects when your Native SDK version is incompatible with the QML Preview tool and disables the preview feature. This issue might occur if you are using an older version of the Native SDK or your app was created with a previously incompatible version of the Native SDK. You can upgrade to the latest version of the Native SDK, clean your project, and enable QML Preview.

To enable QML Preview:

  1. Click Windows > Preferences.
  2. Expand BlackBerry and click QML Editor.
  3. Clear the Disable check box.

Now that we've explored the tools and created our root container, let's start adding some more components to our app.

Add images

Now we can start adding our images and other UI components. In the root Container that we created already, we need to create another one. This one will hold our images.

// The images container
Container {
    horizontalAlignment: HorizontalAlignment.Center
    layout: DockLayout {}

Unlike the root Container, this container uses a DockLayout, since we want to layer its children in front of one another and dock them to the center of the container. The alignment of the Container is set to horizontal center alignment so that it is centered within its parent's stack layout.

Now we can add the images to the container. The order in which you add the images to the container determines how they are layered. Since we want the night time image at the back and day time image at the front, we add the night time image first followed by the day time image.

You don't have to specify that you want to use alternate images if you are testing your app on a device with a physical keyboard. The static asset selector does all the work for you. For more information about this feature, see Using the static asset selector.

    // The night image
    ImageView {
        id: night
        imageSource: "asset:///images/night.jpg"
        horizontalAlignment: HorizontalAlignment.Center
    }
 
    // The day image. Opacity is set to 0.0 on construction
    // so that the night image is visible behind it.
    ImageView {
        id: day
        opacity: 0
        imageSource: "asset:///images/day.jpg"
        horizontalAlignment: HorizontalAlignment.Center
    }
} // End of the images container

Each image alignment is set so that the images are centered horizontally within their parent's DockLayout.

The only differences between the images is that the day time image has an opacity set to 0.0.

We assign the image an ID so that we can access its opacity property from our slider. The opacity is initially set to 0.0 so that when the app starts, the image is invisible, but is revealed gradually as you move the Slider to the right.

You can use the image viewer to view your application's image assets in the IDE. Just double-click the image in your project and it opens in the editor window.

The most powerful feature of the image viewer is the ability to specify nine-slice scaling properties directly on an image. When nine-slice scaling is turned on and you have an associated .amd file, you can drag the margins vertically and horizontally to where you need them on the image. Then, you can save the slice margin properties in your .amd file. We won't apply any nine-slice scaling to the images in this tutorial.

For more information about images and nine-slice scaling in Cascades, see Images.

Screen showing the image viewer in the QML Editing perspective.

The image viewer also has features for displaying a grid overlay, revealing transparent pixels using a checkered background, and measuring pixel dimensions.

Screen showing the Cascades first app assets in the Assets view.

You can use the Assets view to see a list of all the assets in your project. Just open a QML file in the editor and the view is populated with the list of items in your project.

In this view, you can select and drag assets directly into the editor. When you drop the asset into place, the QML code for loading the asset is automatically created. The Assets view also has a search bar that allows you to filter the assets that are currently displayed.

If your application contains custom QML components, they are listed in their own section called Custom Components. Like any other asset, you can drag and drop these directly into the editor.

For more information about custom QML components, see Custom QML components.

Add a slider

Finally, we must create one more container. This one is also a child of the root container that we created earlier. This container will hold our slider and our sun and moon icons.

// The slider container
Container {
    leftPadding: 20
    rightPadding: 20
    topPadding: 25
    bottomPadding: 25
    layout: StackLayout {
        orientation: LayoutOrientation.LeftToRight
    }

The one major difference with this container is that we're changing the direction of the layout so that its children are stacked from left to right, instead of top to bottom. We also set padding on each side of the container so that its content isn't pushed right up against the edge of the screen and the container above it.

The last thing we must do is add our image icons and slider to the container. We add margins to each end of the slider so the sun and moon icons are not flush against its edges. This little bit of space makes the UI seem less crowded and more visually appealing. We also set the spaceQuota to 1 so that the slider takes up any extra available space in the container.

            // The moon icon
            ImageView {
                imageSource: "asset:///images/moon.png"
                verticalAlignment: VerticalAlignment.Center
            }
            // The slider used to adjust the opacity of the image
            Slider {
                leftMargin: 20
                rightMargin: 20
                horizontalAlignment: HorizontalAlignment.Fill
                layoutProperties: StackLayoutProperties {
                    spaceQuota: 1
                }               
                onImmediateValueChanged: {
                    // This is where the day-night opacity
                    // value is set.
                    day.opacity = immediateValue;
                }
            }
            // The sun icon
            ImageView {
                imageSource: "asset:///images/sun.png"
                verticalAlignment: VerticalAlignment.Center
            }
        } // End of the slider container
    } // End of the root container
} // End of the page

The Slider has a fromValue and a toValue property, which you can use to set a scale for the slider. In this application, we don't specify these values since the default values are just fine for what we need (fromValue = 0.0, toValue = 1.0). This scale dictates that the value of the slider is 0.0 when it is in the left-most position, and 1.0 when it is in the right-most position. We can use these values directly when modifying the opacity of the image, since opacity is also on a scale from 0.0 to 1.0.

The slider also has an immediateValueChanged() signal, which is emitted while the value of the slider is changing.

In order to capture that signal, we need to implement the corresponding signal handler. A signal handler in QML is simply the signal name with the "on" prefix (in this case, it's onImmediateValueChanged). Within the signal handler, we then set the opacity of the day time image to the new position of the slider.

Just build and run the application and you're done.

Screen showing the Lightning Crossfade sample on an all-touch device.

import bb.cascades 1.2

Page {
    Container {
        id: containerID
        background: Color.create ("#262626")
        topPadding: 20.0
        layout: StackLayout {}
        
        // The images container
        Container {
            horizontalAlignment: HorizontalAlignment.Center
            layout: DockLayout {} 
            // The night image
            ImageView {
                id: night
                imageSource: "asset:///images/night.jpg"
                horizontalAlignment: HorizontalAlignment.Center
            }
            
            // The day image. Opacity is set to 0.0 on construction
            // so that the night image is visible behind it.
            ImageView {
                id: day
                opacity: 0
                imageSource: "asset:///images/day.jpg"
                horizontalAlignment: HorizontalAlignment.Center
            }
        } // End of the images container 
        
        // The slider container
        Container {
            leftPadding: 20
            rightPadding: 20
            topPadding: 25
            bottomPadding: 25
            layout: StackLayout {
                orientation: LayoutOrientation.LeftToRight
            }
            
            // The moon icon
            ImageView {
                imageSource: "asset:///images/moon.png"
                verticalAlignment: VerticalAlignment.Center
            }
            
            // The slider used to adjust the opacity of the image
            Slider {
                leftMargin: 20
                rightMargin: 20
                horizontalAlignment: HorizontalAlignment.Fill
                layoutProperties: StackLayoutProperties {
                    spaceQuota: 1
                }               
                onImmediateValueChanged: {
                    // This is where the day-night opacity
                    // value is set.
                    day.opacity = immediateValue;
                }
            }
            // The sun icon
            ImageView {
                imageSource: "asset:///images/sun.png"
                verticalAlignment: VerticalAlignment.Center
            }
        } // End of the slider container
    } // End of the root container
} // End of the page

Congratulations! You've successfully created your first Cascades application using QML!

If you want to see how to create the same UI using C++, move on to the next part of the tutorial.

Last modified: 2014-01-23

comments powered by Disqus