QML fundamentals

The Qt Modeling Language (QML) is a declarative language that's based on JavaScript and is included as part of Qt. It was developed as a simple but powerful language for developers to use for creating UIs. You can use QML to describe the structure and behavior of a set of UI components. QML uses concepts such as objects, properties, and signals and slots to let objects communicate with each other.

In Cascades, QML follows the same rules and structure as it does in a standard Qt application. However, instead of using standard Qt components for building a UI, Cascades provides its own UI framework under the bb/cascades namespace. Although Cascades has its own UI framework, you can still use other QML libraries that come with Qt. To learn more about what QML libraries are supported in Cascades, see Supported QML libraries.

If you're new to QML, Qt offers a useful Introduction to the QML Language to help you get started. Continue reading below to learn how some of the key concepts apply to Cascades.

QML syntax

Here's an example of a simple Cascades UI that's defined in QML:

import bb.cascades 1.2
 
Page {
    Container {
        background: Color.Blue

        Button {
            preferredWidth: 200
            text: "Click me"
        }
        ImageView {
            imageSource: "assets:///myImage.png
        }
    }
}

The UI consists of four objects: a Button ImageView, Container , and Page. QML makes it easy to identify the relationships between the various objects in an app. In the code sample above, the Button and ImageView both belong to the Container, which itself belongs to the Page. QML lets you quickly see how controls are related to each other and how they might be laid out visually in your app.

You'll notice that several of the objects have properties assigned to them as well. Properties are specified using property: value syntax. In the above example, the Button object has a text property, with a value of "Click me."

Object names and IDs

Every QML object can be assigned an id and an objectName that other objects can use to refer to the object. The difference between the two is that the id is for referencing the object within QML, while the objectName is required for referencing the object from C++.

Here's an example of how to use IDs and object names in QML. The first Label specifies an id and a string of text that is displayed. The second Label binds its text property to the text property of the first Label so that it uses the same text string. The Container uses an object name so that it can be accessed from C++. To learn more about accessing QML objects from C++, see QML and C++ integration.

import bb.cascades 1.2
 
Page {
    Container {
        objectName: "rootContainer"

        Label {
            id: label1
            text: "Label text"
        }
        Label {
            text: label1.text
        }
    }
}

Properties

QML supports a number of basic property types, such as int, real, bool, string, and color. For a complete list of basic types, see QML Basic Types on the Qt website.

CheckBox {
    enabled: true  // bool
    text: "My check box"  // string
    preferredWidth: 120.0  // real
}

QML properties are type-safe, meaning that you'll get an error if you try to assign a value that doesn't match the property type.

Button {
    text: 100  // Throws an error
    preferredWidth: "120"  // Throws an error
}

When a property value changes, you can receive a signal of the change by creating a signal handler with on<Property>Changed syntax. For example, here's how you could receive notifications whenever the enabled property changes for a CheckBox:

CheckBox {
    enabled: true
    text: "My check box"
    preferredWidth: 120.0

    onEnabledChanged: {
        // Do something in response
    }
}

List properties

List properties allow you to specify a list of comma-separated elements enclosed by a square bracket. The UIObject::attachedObjects property is a commonly used example of this.

Container {    
    Label { 
        text: "Title: "+ object1.title 
    }     
    Label { 
        text: "Subject: "+ object2.subject
    }    
    
    attachedObjects: [
        MyTitleObject { 
            id: object1
            title: "Hello World"
        },
        MySubjectObject { 
            id: object2
            subject: "Nice Day"
        }   
    ]
}

Default properties

Some components have a default property that allows you to specify the value while leaving out the property tag. A common example of this is the Page::content property.

With the property tag:

Page {
    content: Container {
        // ...
    }
}

Without the property tag:

Page {
    Container {
        // ...
    }
}

Grouped properties

In some cases, similar properties are grouped together logically, such as the input properties for TextArea and TextField. You can access grouped properties in a couple of different ways.

Like this:

TextArea {
    input.submitKey: SubmitKey.Connect
    input.submitKeyFocusBehavior: SubmitKeyFocusBehavior.Keep
}

And like this:

TextArea {
    input {
        submitKey: SubmitKey.Connect
        submitKeyFocusBehavior: SubmitKeyFocusBehavior.Keep
    }
}

Attached properties

Some objects can be used to attach properties to another object. These properties use a Class.property syntax. A notable example is ListItemData, which uses attached properties to access data from a DataModel so that it can be displayed in a ListItemComponent.

ListItemComponent {
    type: "listItem"
    
    Container {
        layout: StackLayout {
            orientation: LayoutOrientation.LeftToRight
        }
        CheckBox {
            checked: ListItemData.checked
        }
        Label {
            text: ListItemData.title
        }
    }
}

Custom properties

You can also define your own custom properties by using the property keyword and specifying a name and type for the property. To listen for changes to a custom property value, you can create a signal handler using the on<Property>Changed syntax.

Container {
    property string text: "Hello"

    onTextChanged: {
        // Respond to the property change
    }
}

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

Signal handlers and scripting

In QML, you can use signal handlers to run JavaScript code in response to an event. As mentioned previously, these signal handlers are often used as property change notifications. Here's a signal handler that notifies you when a CheckBox is enabled or disabled:

CheckBox {
    text: "My check box"

    onEnabledChanged: {
        console.log("Enabled state has changed")    
    }
}

Many signal handler properties pass along a parameter containing the new property value. Using the example above, here's how you can be notified as to what the new state is:

CheckBox {
    text: "My check box"

    onEnabledChanged: {
        if (enabled == true)
            console.log("Check box enabled!")
        else
            console.log("Check box disabled!")
    }
}

To learn more about how to use JavaScript in QML, see JavaScript in QML.

Custom components

Even though Cascades comes with lots of useful controls right out of the box, you'll likely come to a point where you must create your own custom components. Custom components are reusable UI components that can be used just like any other component included in the framework.

A common way of creating custom components is by compositing existing Cascades controls. For example, in a messaging application, you might want to create a ContactView control that displays a user's name and profile picture, and a DetailsView control that lists a user's contact information.

To learn more about how to create custom components in QML, see Custom QML components.

Additional resources

Last modified: 2014-05-14



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

comments powered by Disqus