Horizontal Flow

Horizontal Flow example shows how to implement a horizontally flickable list of items like a list of album covers. The component is based on the PathView element. Also, horizontal alphabetic scrollbar is provided on the bottom of the page for quickly jumping to a new position. Horizontal Flow also supports arrow key-based navigation for media center-style UIs.

Show code ยป

horizontalflow.qml:

import QtQuick 1.0

Rectangle {
    color: "black"
    width: 800; height: 280
    HorizontalPathView {
        id: horizontalPathView
        model: Model { id: model }
        delegate: Delegate {}
        anchors.fill: parent
    }
    AlphabeticScrollbar {
        view: horizontalPathView
        alphabets: model.alphabets
        alphabetIndeces: model.alphabetIndeces
        anchors {
            left: parent.left
            right: parent.right
            bottom: parent.bottom
            margins: 3
        }
    }
}

HorizontalPathView.qml:

import QtQuick 1.0

PathView {
    id: horizontalPathView
    focus: true
    highlight: Item {}
    pathItemCount: 11
    preferredHighlightBegin: 0.5; preferredHighlightEnd: 0.5
    path: Path {
        startX: -400; startY: horizontalPathView.height/2-70
        PathAttribute { name: "iconScale"; value: 0.5 }
        PathQuad {
            x: horizontalPathView.width/2
            y: horizontalPathView.height/2-20
            controlX: horizontalPathView.width/4
            controlY: horizontalPathView.height/2-45
        }
        PathAttribute { name: "iconScale"; value: 1.0 }
        PathQuad {
            x: horizontalPathView.width+400
            y: horizontalPathView.height/2-70
            controlX: 3*horizontalPathView.width/4
            controlY: horizontalPathView.height/2-45
        }
        PathAttribute { name: "iconScale"; value: 0.5 }
    }
    Keys.onLeftPressed: decrementCurrentIndex()
    Keys.onRightPressed: incrementCurrentIndex()
    function zLevel(index) {
        var dist = Math.abs((currentIndex - index) % count)
        if (dist > (pathItemCount/2.0 + 1))
            dist = count - dist
        return Math.floor(pathItemCount/2.0) - dist
    }
}

Model.qml:

import QtQuick 1.0

ListModel {
    property variant alphabets: []
    property variant alphabetIndeces: []
    function calculateAlphabets() {
        var newAlphabets = []
        var newAlphabetIndeces = []
        var previousItem, item = " "
        for (var index = 0; index < count; index++) {
            previousItem = item
            item = get(index).item
            if (previousItem.charAt(0) != item.charAt(0)) {
                newAlphabets[newAlphabets.length] = item.charAt(0)
                newAlphabetIndeces[newAlphabetIndeces.length] = index
            }
        }
        alphabets = newAlphabets
        alphabetIndeces = newAlphabetIndeces
    }
    Component.onCompleted: populate()
    function populate() {
        // go from A to Z
        for (var index = 65; index < 91; index++) {
            var alphabet = String.fromCharCode(index)
            var alphabetCount = Math.floor(Math.random()*5)
            for (var index2 = 0; index2 < alphabetCount; index2++)
                append({"number": count, "item": alphabet})
        }
        calculateAlphabets()
    }
}

Delegate.qml:

import QtQuick 1.0

Rectangle {
    width: 200; height: 200
    scale: PathView.iconScale
    z: PathView.view.zLevel(index)
    color: Qt.rgba(0.5+(PathView.view.count - number)*Math.random()/PathView.view.count,
                   0.3+number*Math.random()/PathView.view.count, 0.3*Math.random(), 0.7)
    signal clicked
    Keys.onReturnPressed: clicked()
    onClicked: PathView.view.currentIndex = index
    MouseArea {
        id: delegateMouse
        anchors.fill: parent
        onClicked: parent.clicked()
    }
    Rectangle {
        color: Qt.rgba(0.2, 0.3, 0.8)
        opacity: delegateMouse.pressed ? 0.8 : 0.0
        anchors.fill: parent
    }
    Text {
        smooth: true
        color: "white"
        font.pixelSize: 30
        text: "ITEM\n" + item
        anchors.centerIn: parent
        horizontalAlignment: Text.AlignHCenter
    }
}

AlphabeticScrollbar.qml:

import QtQuick 1.0

MouseArea {
    width: 400; height: 50
    property variant view
    property variant alphabets
    property variant alphabetIndeces
    property int currentIndex
    property real letterWidth: (width + 10)/alphabets.length
    onPressed: updatePosition(mouse.x)
    onPositionChanged: updatePosition(mouse.x)
    function updatePosition(x) {
        var index = Math.round((x-10)/letterWidth);
        if (index >= 0 && index < alphabetIndeces.length) {
            currentIndex = index;
            view.currentIndex = alphabetIndeces[index];
        }
    }
    Rectangle {
        radius: 10
        color: "white"
        visible: parent.pressed
        height: letterWidth+60; width: letterWidth+30
        x: Math.min(Math.max(0,parent.mouseX-width/2), parent.width-width)
        anchors { bottom: parent.bottom; bottomMargin: -10 }
        Text {
            font.pixelSize: 34
            text: alphabets[currentIndex]
            anchors { centerIn: parent; verticalCenterOffset: -20 }
        }
    }
    Rectangle {
        height: 20
        color: Qt.rgba(1.0, 1.0, 1.0, 0.2)
        anchors {left: parent.left; right: parent.right; bottom: parent.bottom }
    }
}

2 thoughts on “Horizontal Flow”

  1. really cool and informative to understand QML and Path View!!!

    I’m trying to write a Dock in QML that will be very close to the MacOSX dock:

    Here’s what I’ve done so far:

    http://tinypic.com/r/2mmipoj/5

    I’ve been using a QML ListView for this, but after looking at your Horizontal Flow, maybe I should be using a QML PathView ????

    Any thoughts?

  2. Nice dock! ๐Ÿ™‚ Conceptually I think PathView would be more appropriate, but for sure you can create a dock with either ListView or PathView. PathView allows more freedom in specifying the path/route of the items being animated and allows carousel-style looping, but it is missing some flexibility, properties (positioning functions, section and spacing support, etc.) and some cases maturity of the more commonly used ListView.

Leave a Reply to jpetrell Cancel reply

Your email address will not be published.