Friday, March 28, 2014

QML Button Click Counter

A person posted a question on Stack Overflow asking how to count clicks of a button in QML for BlackBerry 10. As you can see below the question is quite easy to answer if you are familiar with QML. Unfortunately five members of Stack Overflow with enough reputation to put the question on hold couldn't figure out how to answer it and decided the question was unworthy as it is.

The problem of counting events in any programming language will have similar elements. A variable to hold the count, an event to be counted, a means to output the count and optionally a way to reset the count.

In this case the best way to store the count is using a property. In QML properties are variables but they have the added feature of generating signals when they are changed. So we have a property named count, initialized to zero. And we have a signal handler onCountChanged that updates the display whenever the count is changed.

We have two buttons, one that generates clicks to be counted, and one that resets the count. The onClicked signal handler manipulates the value of count. We don't need to worry about the display update because the onCountChanged takes care of that for us. This is a very powerful feature of QML. Any other objects that we create that also need to know when the value of count changes can use a similar signal handler.

I've created a label to display the value, put some properties on the Container that holds these elements to make it pretty. All the rest comes from a project template included with the Momentics IDE for BlackBerry 10 development.

/*
 * Copyright (c) 2011-2013 BlackBerry Limited.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import bb.cascades 1.2

NavigationPane {
    id: navigationPane
    property int count: 0
    onCountChanged: {
        countDisplay.text = "The count is: " + count;
    }
    

    Page {
        titleBar: TitleBar {
            // Localized text with the dynamic translation and locale updates support
            title: qsTr("Page 1") + Retranslate.onLocaleOrLanguageChanged
        }
        
 
        Container {

           Button {
                id: counted
                text: "Click Me"
                
                onClicked: {
                    count++;
                }
                horizontalAlignment: HorizontalAlignment.Center

            }
            Label {
                id: countDisplay
                text: "No clicks yet."
                verticalAlignment: VerticalAlignment.Center
                horizontalAlignment: HorizontalAlignment.Center

            }
            Button {
                id: reset
                text: "Reset"
                
                onClicked: {
                    count = 0;
                }
                horizontalAlignment: HorizontalAlignment.Center
                verticalAlignment: VerticalAlignment.Bottom
            }
            

        }

        actions: ActionItem {
            title: qsTr("Second page") + Retranslate.onLocaleOrLanguageChanged
            ActionBar.placement: ActionBarPlacement.OnBar

            onTriggered: {
                // A second Page is created and pushed when this action is triggered.
                navigationPane.push(secondPageDefinition.createObject());
            }
        }
    }

    attachedObjects: [
        // Definition of the second Page, used to dynamically create the Page above.
        ComponentDefinition {
            id: secondPageDefinition
            source: "DetailsPage.qml"
        }
    ]

    onPopTransitionEnded: {
        // Destroy the popped Page once the back transition has ended.
        page.destroy();
    }
}


And here is a screen shot:


Once you are comfortable with that, you can actually get rid of the signal handler onCountChanged and re-write the Label as follows:

            Label {
                id: countDisplay
                text: "Number of clicks: " + count
                verticalAlignment: VerticalAlignment.Center
                horizontalAlignment: HorizontalAlignment.Center

            }
This works because QML is a curious mix of declarative and functional programming. Since the QML interpreter knows count is a property it "creates" the functional programming behind the scenes to make sure that any other properties that depend on count are kept current by using signal handlers.

No comments:

Post a Comment