Comments, Code and Qt. Some words about the wonderful world of software engineering

23Jan/121

Sunday evening fun: Windows Phone 7 styled progress indicator in QML

I started a new coding hobby project today. But instead of actually getting very far with the productive part of the project I got side tracked on something fun I wanted to try out (don't you just love when that happens 🙂 That's not possible when coding at work...). I wanted to share this day's outcome with you. Let me introduce this humble video show casing a Windows Phone 7 styled loading indicator in QML 🙂 You can get the source code for this small thing (GPL licensed) from here: https://gitorious.org/qtquicktests/qtquicktests/trees/master/wp7-loading.

Code breakdown

It's a pretty small thing, but I can run the code through quickly. I apologize for the encoded code paste that makes reading it very hard. WordPress is not very good at this...

LoadingIndicator.qml

This is your component that you will include in your project where ever you want the progress indicator to be shown. You call the start() method and it will start the animation. Notice how this method starts a timer that will call some JavaScript code. It's here we create the moving objects dynamically and it's thanks to the timer we get the spacing between the items that are moving on the screen. The items themselves do not obviously know anything of each other so it's the job of this timer to create them a bit separate from each other and this is how we get the effect of moving particles after each other.
import QtQuick 1.0
import "progressindicator.js" as Indicator

Item {
    id: progressBarComponent
    height: 70

    property bool running: false;
    property int numberOfElements: 0;


    function start() {
        console.log("Animation started.");

        indicatorCreationTimer.restart();
        progressBarComponent.running = true;
    }

    function stop() {
        console.log("Animation stopped.");

        progressBarComponent.running = false;
        Indicator.stopIndicators();
    }

    Timer {
        id: indicatorCreationTimer;
        running: false
        repeat: true;
        interval: 200;
        onTriggered: {
            if (!Indicator.allStarted) {
                Indicator.startNextIndicator();
            } else {
                indicatorCreationTimer.stop();
            }
        }
    }

}

 progressindicator.js

Here we have the JavaScript that creates the moving rectangles on the screen. As you can see, we can have as many as we like because the items are created dynamically and stored into an array. The timer calls the startNextIndicator() method which will first check if we have enough rectangles, in which case we don't create more and the timer stops calling this method. Otherwise we create a new rectangle object and call its animation to start (moving on the screen). This piece of JavaScript also takes care of stopping each moving particle and hiding them when we don't want to show the progress bar anymore. Notice also that the items are not re-created but reused when we want to show the progress bar again.
var numOfElements = 4;
var indicatorRects = new Array(numOfElements);
var allStarted = false;
var currentIndicator = 0;


function createIndicatorObject(currentIndicator) {
    var component = Qt.createComponent("qrc:/qml/ProgressIndicatorRectangle.qml");
    indicatorRects[currentIndicator] = component.createObject(progressBarComponent, {"width": progressBarComponent.width, "height": progressBarComponent.height});
}

function startNextIndicator() {
    if (currentIndicator > numOfElements) {
        currentIndicator = 0;
        allStarted = true;
        return;
    }

    if (indicatorRects[currentIndicator] == null) {
        createIndicatorObject(currentIndicator);
    }

    indicatorRects[currentIndicator].startAnimation();
    currentIndicator++;
}

function stopIndicators() {
    allStarted = false;
    currentIndicator = 0;

    for(var i=0; i<Indicator.numOfElements; i++) {
        var indicator = indicatorRects[i];
        indicator.stopAnimation();
    }
}

ProgressIndicatorRectangle.qml

Here we have the items moving on the screen. They animate themselves in a few steps to have the illusion to move like they do on the Windows Phone 7. Maybe the interesting thing here is that when the progress indicator is stopped, instead of just hiding them immediately the items continue to move off the screen where they hide until the progress indicator is shown again.
import QtQuick 1.0

Item {
    id: loadingIndicator
    width: 400

    function startAnimation() {
        loadingAnimation.start();
    }

    function stopAnimation() {
        loadingAnimation.restart();
        loadingAnimation.stop();
        effectRect.x = 0;
    }

    Rectangle {
        id: effectRect
        width: 6
        height: 6
        color: "steelblue"
        anchors.verticalCenter: parent.verticalCenter
        x: 0
    }

    SequentialAnimation {
        id: loadingAnimation
        alwaysRunToEnd: true
        loops: Animation.Infinite

        PropertyAction { target: effectRect; property: "visible"; value: true }
        PropertyAction { target: effectRect; property: "x"; value: 0 }
        PropertyAnimation { target: effectRect; property: "x"; to: loadingIndicator.width/3; duration: 400; }
        PropertyAnimation { target: effectRect; property: "x"; to: (loadingIndicator.width/3)*2; duration: 1000; }
        PropertyAnimation { target: effectRect; property: "x"; to: loadingIndicator.width; duration: 400; }
        PropertyAction { target: effectRect; property: "visible"; value: false }
        PauseAnimation { duration: 700 }
    }

}

Technorati Tags: , , ,

  • Raja Varma

    Really nice. Will try your code in my app soon.