How to create scrollbar in QtQuick 2.0?

Loved the solution by TheBootroo (+1 for him!) but found his solution only few days ago, by following a comment to a recent question. Meanwhile, I've independently developed mine for a project I was working on and I'm going to share such a solution here. Hope it can be useful. :)

My scrollbar has a (sort of) "OS X feel" (intended) so e.g. it does not include scrolling arrows on the sides.

Here is the code:

import QtQuick 2.0

Item {
   id: scrollbar

    property Flickable flk : undefined
    property int basicWidth: 10
    property int expandedWidth: 20
    property alias color : scrl.color
    property alias radius : scrl.radius

    width: basicWidth
    anchors.right: flk.right;
    anchors.top: flk.top
    anchors.bottom: flk.bottom

    clip: true
    visible: flk.visible
    z:1

    Binding {
        target: scrollbar
        property: "width"
        value: expandedWidth
        when: ma.drag.active || ma.containsMouse
    }
    Behavior on width {NumberAnimation {duration: 150}}

    Rectangle {
        id: scrl
        clip: true
        anchors.left: parent.left
        anchors.right: parent.right
        height: flk.visibleArea.heightRatio * flk.height
        visible: flk.visibleArea.heightRatio < 1.0
        radius: 10
        color: "gray"

        opacity: ma.pressed ? 1 : ma.containsMouse ? 0.65 : 0.4
        Behavior on opacity {NumberAnimation{duration: 150}}

        Binding {
            target: scrl
            property: "y"
            value: !isNaN(flk.visibleArea.heightRatio) ? (ma.drag.maximumY * flk.contentY) / (flk.contentHeight * (1 - flk.visibleArea.heightRatio)) : 0
            when: !ma.drag.active
        }

        Binding {
            target: flk
            property: "contentY"
            value: ((flk.contentHeight * (1 - flk.visibleArea.heightRatio)) * scrl.y) / ma.drag.maximumY
            when: ma.drag.active && flk !== undefined
        }

        MouseArea {
            id: ma
            anchors.fill: parent
            hoverEnabled: true
            drag.target: parent
            drag.axis: Drag.YAxis
            drag.minimumY: 0
            drag.maximumY: flk.height - scrl.height
            preventStealing: true
        }
    }
}

And here is the code to use it. All the fields are optional expect for the flickable, obviously. Values set are the default ones:

ScrollBar {
    flk: privacyFlick
    radius: 10          // Optional
    basicWidth: 10      // Optional
    expandedWidth: 20   // Optional
    color: "grey"       // Optional
}

I think this will do the trick

http://qt-project.org/doc/qt-5.1/qtquickcontrols/qml-qtquick-controls1-scrollview.html

import QtQuick 2.0
import QtQuick.Controls 1.0
ScrollView{
    ListView {
        ...
    }
}

ScrollBar/ScrollIndicator is easy to do, and the code would be identical in QQ1 or QQ2 (except the import) :

///////// ScrollBar.qml //////////////

import QtQuick 2.0;

Item {
    id: scrollbar;
    width: (handleSize + 2 * (backScrollbar.border.width +1));
    visible: (flickable.visibleArea.heightRatio < 1.0);
    anchors {
        top: flickable.top;
        right: flickable.right;
        bottom: flickable.bottom;
        margins: 1;
    }

    property Flickable flickable               : null;
    property int       handleSize              : 20;

    function scrollDown () {
        flickable.contentY = Math.min (flickable.contentY + (flickable.height / 4), flickable.contentHeight - flickable.height);
    }
    function scrollUp () {
        flickable.contentY = Math.max (flickable.contentY - (flickable.height / 4), 0);
    }

   Binding {
        target: handle;
        property: "y";
        value: (flickable.contentY * clicker.drag.maximumY / (flickable.contentHeight - flickable.height));
        when: (!clicker.drag.active);
    }
    Binding {
        target: flickable;
        property: "contentY";
        value: (handle.y * (flickable.contentHeight - flickable.height) / clicker.drag.maximumY);
        when: (clicker.drag.active || clicker.pressed);
    }
    Rectangle {
        id: backScrollbar;
        radius: 2;
        antialiasing: true;
        color: Qt.rgba(0.5, 0.5, 0.5, 0.85);
        border {
            width: 1;
            color: "darkgray";
        }
        anchors { fill: parent; }

        MouseArea {
            anchors.fill: parent;
            onClicked: { }
        }
    }
    MouseArea {
        id: btnUp;
        height: width;
        anchors {
            top: parent.top;
            left: parent.left;
            right: parent.right;
            margins: (backScrollbar.border.width +1);
        }
        onClicked: { scrollUp (); }

        Text {
            text: "V";
            color: (btnUp.pressed ? "blue" : "black");
            rotation: -180;
            anchors.centerIn: parent;
        }
    }
    MouseArea {
        id: btnDown;
        height: width;
        anchors {
            left: parent.left;
            right: parent.right;
            bottom: parent.bottom;
            margins: (backScrollbar.border.width +1);
        }
        onClicked: { scrollDown (); }

        Text {
            text: "V";
            color: (btnDown.pressed ? "blue" : "black");
            anchors.centerIn: parent;
        }
    }
    Item {
        id: groove;
        clip: true;
        anchors {
            fill: parent;
            topMargin: (backScrollbar.border.width +1 + btnUp.height +1);
            leftMargin: (backScrollbar.border.width +1);
            rightMargin: (backScrollbar.border.width +1);
            bottomMargin: (backScrollbar.border.width +1 + btnDown.height +1);
        }

        MouseArea {
            id: clicker;
            drag {
                target: handle;
                minimumY: 0;
                maximumY: (groove.height - handle.height);
                axis: Drag.YAxis;
            }
            anchors { fill: parent; }
            onClicked: { flickable.contentY = (mouse.y / groove.height * (flickable.contentHeight - flickable.height)); }
        }
        Item {
            id: handle;
            height: Math.max (20, (flickable.visibleArea.heightRatio * groove.height));
            anchors {
                left: parent.left;
                right: parent.right;
            }

            Rectangle {
                id: backHandle;
                color: (clicker.pressed ? "blue" : "black");
                opacity: (flickable.moving ? 0.65 : 0.35);
                anchors { fill: parent; }

                Behavior on opacity { NumberAnimation { duration: 150; } }
            }
        }
    }
}

To use it :

import QtQuick 2.0;

Rectangle {
    width: 400;
    height: 300;

    ListView {
        id: list;
        anchors.fill: parent;
        model: 100;
        delegate: Rectangle {
            height: 50;
            width: parent.width;
            color: (model.index %2 === 0 ? "darkgray" : "lightgray");
        }
    }
    ScrollBar {
        flickable: list;
    }
}

Enjoy !


Qt 5.6 introduces new controls as the technical preview "Qt Labs Controls". Among other stuff, the controls introduce a built-in ScrollBar type (interactive) and ScrollIndicator type (not interactive).

In Qt 5.7 new controls exited technical preview and are now renamed "Quick Controls 2", to stress the fact that they superseed the previous controls.

If you are using Qt 5.6, which is an LTS version and will be around for quite sometime, ScrollBar can be used as follows:

import QtQuick 2.6
import Qt.labs.controls 1.0
import QtQuick.Window 2.2

ApplicationWindow {
    visible: true
    width: 400
    height: 600

    Flickable {
        anchors.fill: parent
        contentWidth: image.width
        contentHeight: image.height

        //ScrollIndicator.vertical: ScrollIndicator { }  // uncomment to test
        ScrollBar.vertical: ScrollBar { }
        ScrollBar.horizontal: ScrollBar { }

        Image {
            id: image
            source: "http://i.ytimg.com/vi/tntOCGkgt98/maxresdefault.jpg"
        }
    }
}

Whereas in Qt 5.7 and onward you can use ScrollBar or ScrollIndicator as follows:

import QtQuick 2.6
import QtQuick.Controls 2.0
import QtQuick.Window 2.2

ApplicationWindow {
    visible: true
    width: 600
    height: 300

    Flickable {
        anchors.fill: parent
        contentWidth: image.width
        contentHeight: image.height

        ScrollIndicator.vertical: ScrollIndicator { }
        //ScrollBar.vertical: ScrollBar { }       // uncomment to test    

        Image {
            id: image
            source: "http://s-media-cache-ak0.pinimg.com/736x/92/9d/3d/929d3d9f76f406b5ac6020323d2d32dc.jpg"
        }
    }
}

Usage syntax is pretty much the same whereas a major refactoring occured in the styling code as can be seen in e.g. Labs Controls ScrollIndicator customization page in comparison to Quick Controls 2 ScrollIndicator customization page.

Tags:

Qt

Qml

Qtquick2