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.

If you are editing a C/C++ file, these buttons don't appear.

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

import bb.cascades 1.4

Page {
    Container {
        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 app. The next component is the root Container for the app. 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 app

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 look at the different pieces of our UI to see what we need to do to get them arranged correctly 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 holds 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 notice that errors sometimes occur. This tutorial is designed so that you can copy and paste each snippet one after another. The closing braces for some components aren't included until you get to the end of the tutorial.

To add the root container, we 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.

To specify properties for the root 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.

Click on or within the Container component and the QML Properties view is populated with all the information about the Container, including a list of other properties that you can use.

As you change 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 and set the Container ID to containerID. The StackLayout allows child controls to be stacked in any direction (in this case we're using the default, TopToBottom).

In the Margins & Sizes drop-down, give the container 2du of padding at the top, on the right, and on the left.

Screen showing the QML Properties view in the Momentics IDE.

The padding at the edges is used to create some space between the edges of the container and the first child added to it. We use design units (du) so that our UI adapts to different screen densities. Design units are device-independent values that you use to assign dimensions to components in your UI. To learn more, see Design units.

There's one more property that we need to specify on the root container, but you must add this one manually: the background color. For our background, let's use a nice bright color that looks good for all device themes. To learn more about themes and how they differ for each device, see Themes.

background: Color.create ("#f8f8f8")

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 color constants.

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

import bb.cascades 1.4

Page {
    Container {
        id: containerID
        layout: StackLayout {}
        topPadding: ui.du(2.0)
        background: Color.create ("#f8f8f8")
        leftPadding: ui.du(2.0)
        rightPadding: ui.du(2.0)

    }
}

Now that our app has a background, let's have a look at it. We could start the app on a device, but since we want to see the background, the QML Preview works 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. You can also edit your code and see your preview in the same window by using a split mode. Here's what Horizonatal split mode looks like so far:


Image showing what the app looks like in QML Preview in the tutorial.

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 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. In Windows and Linux, on the Window menu, click Preferences. In Mac OS, on the Momentics menu, click Preferences.
  2. Expand BlackBerry and click QML Editor.
  3. In the QML Preview area, 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 holds our images.

        // Put the two images on top of each other 
        // in the images container
        Container {
            horizontalAlignment: HorizontalAlignment.Center
            
            layout: DockLayout {
            }

Unlike the root Container, this container uses a DockLayout, because 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. Because 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.

If you are testing your app on a device with a different screen resolution, you don't have to specify that you want to use alternate images. The static asset selector does all the work for you. For more information about this feature, see Static asset selection.

        // Put the two images on top of each other 
        // in the images container
        Container {
            horizontalAlignment: HorizontalAlignment.Center
            
            layout: DockLayout {
            }

            // Add the night image at the same position  
            // as the day image and behind it
            ImageView {
                id: night
                imageSource: "asset:///images/night.jpg"
                horizontalAlignment: HorizontalAlignment.Center
            }

            // Add the day image in front of the night image and
            // hide the day image by setting its opacity value to 0
            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 difference 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 app's image assets in the IDE. 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. 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 app contains custom QML components, they are listed in their own section called Custom Components. Like any other asset, you can drag and drop these assets 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 holds our slider and our sun and moon icons.

        // Add a container for the slider at the bottom of the page
        // When the slider gets an onImmediateValueChanged() signal,
        // change the opacity of the image so that it disaappears

        Container {
            leftPadding: ui.du(1.0)
            rightPadding: ui.du(1.0)
            topPadding: ui.du(4.0)
            bottomPadding: ui.du(2.0)
            layout: StackLayout {
                orientation: LayoutOrientation.LeftToRight
            }

In this container, we change 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. We use design units to make sure that this padding scales nicely on all devices.

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 minimum position of the slider means 
            // nighttime, so add an image of a moon at this
            // side of the slider
            ImageView {
                imageSource: "asset:///images/moon.png"
                verticalAlignment: VerticalAlignment.Center
            }

            // Add the slider component
            Slider {
                leftMargin: ui.du(1.0)
                rightMargin: ui.du(1.0)
                horizontalAlignment: HorizontalAlignment.Fill
                
                layoutProperties: StackLayoutProperties {
                    spaceQuota: 1
                }
                
                onImmediateValueChanged: {
                    // Change the opacity of the day image to 
                    // make it appear
                    day.opacity = immediateValue;
                }
            }

            // The maximum position of the slider means 
            // daytime, so add an image of a sun at this 
            // side of the slider
            ImageView {
                imageSource: "asset:///images/sun.png"
                verticalAlignment: VerticalAlignment.Center
                
            } // Sun icon Image
        } // Slider Container
    } // Content Container
} // Page

The Slider has a fromValue and a toValue property, which you can use to set a scale for the slider. In this app, we don't specify these values since the default values are 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 changing 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.

Build and run the app and you're done.

To learn more, see Build your project.

Screen showing the Lightning Crossfade sample on an all-touch device.
import bb.cascades 1.4

Page {
    // Set the background color of this container to off-white
    // and stack the images and the slider in this container
    Container {
        id: containerID
        topPadding: ui.du(2.0)
        leftPadding: ui.du(2.0)
        rightPadding: ui.du(2.0)
        background: Color.create("#f8f8f8")
        
        layout: StackLayout {
        }

        // Put the two images on top of each other 
        // in the images container
        Container {
            horizontalAlignment: HorizontalAlignment.Center
            
            layout: DockLayout {
            }

            // Add the night image at the same position  
            // as the day image and behind it
            ImageView {
                id: night
                imageSource: "asset:///images/night.jpg"
                horizontalAlignment: HorizontalAlignment.Center
            }

            // Add the day image in front of the night image and
            // hide the day image by setting its opacity value to 0
            ImageView {
                id: day
                opacity: 0
                imageSource: "asset:///images/day.jpg"
                horizontalAlignment: HorizontalAlignment.Center
            }
        } // End of the images container

        // Add a container for the slider at the bottom of the page
        // When the slider gets an onImmediateValueChanged() signal,
        // change the opacity of the image so that it disaappears

        Container {
            leftPadding: ui.du(1.0)
            rightPadding: ui.du(1.0)
            topPadding: ui.du(4.0)
            bottomPadding: ui.du(2.0)
            layout: StackLayout {
                orientation: LayoutOrientation.LeftToRight
            }

            // The minimum position of the slider means 
            // nighttime, so add an image of a moon at this
            // side of the slider
            ImageView {
                imageSource: "asset:///images/moon.png"
                verticalAlignment: VerticalAlignment.Center
            }

            // Add the slider component
            Slider {
                leftMargin: ui.du(1.0)
                rightMargin: ui.du(1.0)
                horizontalAlignment: HorizontalAlignment.Fill
                
                layoutProperties: StackLayoutProperties {
                    spaceQuota: 1
                }
                
                onImmediateValueChanged: {
                    // Change the opacity of the day image to 
                    // make it appear
                    day.opacity = immediateValue;
                }
            }

            // The maximum position of the slider means 
            // daytime, so add an image of a sun at this 
            // side of the slider
            ImageView {
                imageSource: "asset:///images/sun.png"
                verticalAlignment: VerticalAlignment.Center
                
            } // Sun icon Image
        } // Slider Container
    } // Content Container
} // Page

Build your project

After you create a project, you need to build it before you can run it on a launch target (a device or simulator). When you build your project, the Momentics IDE creates binaries that are configured to run on the target that you selected in the Launch target drop-down list.

To build the binaries for your project:

  1. In the Launch mode drop-down list, click Run.

    Screen showing the toolbar in Momentics IDE 2.1.

  2. Make sure that the launch configuration for your project is showing in the Launch configuration window or select it from the drop-down list.
  3. Make sure that your target is showing in the Launch target drop-down list.
  4. Click Momentics IDE build button.

For more information about building projects and build configurations, see Building and launching.

To run an app on your launch target, you need a launch configuration. If you don't create a launch configuration, the Momentics IDE creates a default configuration for you when you create your project. For more details about launch configurations, see Create a launch configuration .

When you've built the app binaries, your app is ready to run on a launch target.

To run your app:

  1. In the Launch mode drop-down list, select Run.
  2. Make sure that the launch configuration for your project is showing in the Launch configuration window or select it from the drop-down list.
  3. Make sure that your target is showing in the Launch target drop-down list.
  4. Click Momentics IDE launch button.

Congratulations! You've successfully created your first Cascades app using QML! Check out the console messages. In the console, you can see lots of information about the settings that are active in your environment.

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

Last modified: 2015-03-31



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus