13

I am trying to create a scrollbar in QtQuick 2.0,

I found that Scrollbar component is available in QtQuick 1.0 but I can't find such component in QtQuick 2.0. How can I create scrollbar for ListView in QtQuick 2.0?

Any help? Thanks in advance.

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
snehal
  • 1,798
  • 4
  • 17
  • 24

4 Answers4

28

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 !

TheBootroo
  • 7,408
  • 2
  • 31
  • 43
  • Thank you very much ! Very flexible and efficient solution. The only thing it is missing is the horizontal orientation. Since ListView supports it, I will be adding a property binding to scroll horizontally – Massimo Callegari Apr 25 '15 at 14:11
  • If we have interactive changing of the number of delegates, binding loop is detected for this: `Binding { target: flickable; property: "contentY"; value: (handle.y * (flickable.contentHeight - flickable.height) / clicker.drag.maximumY); when: (clicker.drag.active || clicker.pressed); }` And the whole scrolling is broken then. We can scroll father then contentHeight. Can you, please, help with this? – VALOD9 May 26 '15 at 09:24
  • It works, but spams "_QML Binding: Binding loop detected for property "value"_" to the log whenever I drag the handle. Any solution on this? – warranty_void Jul 16 '15 at 15:59
  • Very useful, thanks. But I get the same binding loop warning. – Hyndrix Dec 20 '15 at 08:28
  • Good solution but I've noticed that the scrollbar position become wrong when elements are dynamically inserted into the list (in my case at the top). – mrAlmond Feb 03 '16 at 17:25
12

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
}
BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
  • 2
    +1 - Thanks so much for posting your solution! I was searching a simple always-visible scrollbar without background for a long time. There are some (alleged) solutions out there, but this is the first one that worked, even out of the box. And on top of that, it is very easy to adapt it to my own needs. Good work – Franz B. Mar 20 '15 at 10:27
  • footer and header is not considered in this scrollbar – Mahdi Khalili Oct 12 '19 at 07:06
  • You mean the arrows @MahdiKhalili? That is intended and implied in the description about the OSX feel, I guess. – BaCaRoZzo Oct 14 '19 at 07:29
  • its using anchors for positioning its not working as expected in listview because of footer and header i solved it with this https://docs.huihoo.com/qt/5.x/qtquick-customitems-scrollbar-example.html – Mahdi Khalili Oct 14 '19 at 07:35
  • Oh, I see now. Well, don't remember the details to be honest. But if you have fixed it yourself good. – BaCaRoZzo Oct 14 '19 at 08:23
11

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 {
        ...
    }
}
AbuTabl
  • 111
  • 1
  • 2
  • This structure works as described, but note that only mouse navigation then works, keyboard navigation is disabled. See the QT bug reports [ScrollView breaks keyboard navigation in ListView](https://bugreports.qt-project.org/browse/QTBUG-31976) and [Allow disabling of Mouse Interaction with ListView (Flickables?) while still retaining keyboard interaction](https://bugreports.qt-project.org/browse/QTBUG-17051) – eatyourgreens Dec 12 '13 at 09:37
  • 1
    I thought this would help me a lot to get the scroller working. Unfortunately, some items are missing at the bottom when I have a grouped list. – Slesa Mar 09 '16 at 14:44
4

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.

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
Aleksei Petrenko
  • 6,698
  • 10
  • 53
  • 87