Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Can't get my ListView Scrollbehavior correct in my simple example



  • cross-posted from: https://stackoverflow.com/questions/57079018/qml-cant-get-my-listview-scrollbehavior-correct-in-my-simple-example

    Its my first time to use ListView and i need to implement a decent scroll behavior for my application
    it "works" but sometimes the animation behavior is a little bit strange

    I've wrote a small QML example (runnable with qmlscene tool) to show my problem

    alt text

    Sample source code:

    import QtQuick 2.2
    import QtQuick.Window 2.5
    
    Window {
        width: 500
        height: 700
        visible: true
    
        // my model ctrl is in C++
        // faked it here with pure QML to simplify the example
        ListModel {
            id: cppModel
            // different size - item type depdendend (just const here)
            ListElement { name: "Item 1"; active: false; height: 50 }
            ListElement { name: "Item 2"; active: false; height: 100 }
            ListElement { name: "Item 3"; active: false; height: 150 }
            ListElement { name: "Item 4"; active: false; height: 200 }
            ListElement { name: "Item 5"; active: false; height: 250 }
            ListElement { name: "Item 6"; active: false; height: 300 }
    
            property int currentItemIndex: -1
    
            function activate_item(index) {
                // auto deactivate all others
                for(var i = 0; i < rowCount(); i++) {
                    var active = (index === i)
                    get(i).active = active
                    if(active) {
                        currentItemIndex = i
                    }
                }
            }
        }
    
        Component {
            id: listViewDelegate
    
            Rectangle
            {
                height: model.active ? model.height : 30
                width: 600
                color: "transparent"
                border {color: model.active ? "yellow" : "black"; width: 2}
    
                Behavior on height {
                    NumberAnimation {
                        easing.type: Easing.InCirc
                        duration: 5000
                    }
                }
    
                Text {
                    text: model.name;
                    font.pixelSize: 24
                }
    
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        cppModel.activate_item(model.index)
                    }
                }
            }
        }
    
        Rectangle
        {
            width: 400
            height: 600
            border {color: "steelblue"; width: 2}
    
            ListView {
                id: myList
                anchors.fill: parent
                clip: true
                model: cppModel
                delegate: listViewDelegate
    
                header: Rectangle {
                    width: parent.width; height: 50
                    border {color: "red"; width: 2}
                    Text {
                        anchors.centerIn: parent
                        text: "Header"
                        font.pixelSize: 32
                    }
                }
    
                highlightMoveDuration: 5000
                highlightMoveVelocity: -1
                highlightResizeDuration: 5000
                highlightResizeVelocity: -1
    
                preferredHighlightBegin: 0
                preferredHighlightEnd: currentItem.height
                highlightRangeMode: ListView.StrictlyEnforceRange
    
                snapMode: ListView.SnapToItem
    
                currentIndex: -1
    
                onMovementStarted: {
                    // show header again
                    highlightRangeMode = ListView.ApplyRange
                }
                onCurrentIndexChanged: {
                    // highlight current
                    highlightRangeMode = ListView.StrictlyEnforceRange
                }
            }
    
            Connections {
                target: cppModel
                onCurrentItemIndexChanged: { // change comming from C++
                    if ( myList.currentIndex !== cppModel.currentItemIndex ) {
                        myList.currentIndex = cppModel.currentItemIndex
                    }
                }
            }
        }
    }
    

    first: the item that is activated should be scrolled to top (that works) seems ok, only the animation i get are a little strange

    my 2 "bad" animation scenarios:

    if you activate Item 1..6 downwards, starting with Item 3 the Item Text is moved too far up and then comes back, the up scrolling should always stop at the items top, no bouncing etc. - i've already tried to replace the highlighter but gives me no better result

    if you open for example Item 6 and the scroll with mouse to Item 1 and activates item 1, the animation (seems to) first closes the Item 6 and then scrolls the list, better would be that the scrolling/and resize to the activated item 1 is prio and the closing animation of the former item does not block/stutter the item 1 animation

    would be glad if someone could kick me in the right direction how to solve the animation problems

    thanks



  • a played a while with the ListView options but can't get the animations right
    so i've tried to implement my own list view like item - as an example how the animation would feel

    working sample, with an fake c++ model, with the animations i want to see

    import QtQuick 2.5
    import QtQuick.Controls 2.5
    import QtQuick.Window 2.5
    
    Window {
        width: 500
        height: 700
        visible: true
    
        // my model ctrl is in C++
        // faked it here with pure QML to simplify the example
        ListModel {
            id: cppModel
            // different size - item type depdendend (just const here)
            ListElement { name: "Item 1"; active: false; height: 100 }
            ListElement { name: "Item 2"; active: false; height: 150 }
            ListElement { name: "Item 3"; active: false; height: 200 }
            ListElement { name: "Item 4"; active: false; height: 250 }
            ListElement { name: "Item 5"; active: false; height: 300 }
            ListElement { name: "Item 6"; active: false; height: 350 }
    
            property int currentItemIndex: -1
    
            function activate_item(index) {
                // auto deactivate all others
                for(var i = 0; i < rowCount(); i++) {
                    var active = (index === i)
                    get(i).active = active
                    if(active) {
                        currentItemIndex = i
                    }
                }
            }
    
            function remove_item(index) {
                if(currentItemIndex === -1) {
                    console.log("can remove not active item")
                    return
                }
    
                remove(currentItemIndex);
            }
    
            function deactivate_current_item()
            {
                if(currentItemIndex === -1) {
                    console.log("can remove not active item")
                    return
                }
                get(currentItemIndex).active = false
            }
        }
    
        Column {
    
            Rectangle {
                width: 320
                height: 480
    
                border { width: 2; color: "steelblue" }
    
                Flickable {
                    id: flickable
                    clip: true
                    anchors.fill: parent
                    contentWidth: 320
                    contentHeight: 600
    
                    function goto_header()
                    {
                        contentY = 0
                    }
    
                    Behavior on contentY {
                        NumberAnimation {
                            easing.type: Easing.OutCubic
                            duration: 500
                        }
                    }
    
                    Rectangle {
                        id: myList
    
                        anchors.top: parent.top
                        anchors.left: parent.left
                        width: flickable.contentWidth
                        height: flickable.contentHeight
    
                        Column {
                            Rectangle {
                                id: header
                                width: 320
                                height: 40
                                color: "red"
                                border { width: 2; color: "black" }
                                Text { text: "Header"}
                            }
    
                            Repeater {
                                model: cppModel
                                Rectangle {
                                    readonly property int reducedSize: 30
                                    width: 320
                                    height: model.active ? model.height : reducedSize
                                    border { width: 2; color: "black" }
                                    Text {
                                        text: model.name
                                    }
    
                                    MouseArea {
                                        anchors.fill: parent
                                        onClicked: {
                                            // find new list contentY
                                            // works also if item above is shrinking
                                            // but only if shrinked height is for all items the same
                                            var newContentY = (model.index * reducedSize) + header.height
    
                                            cppModel.activate_item(model.index)
    
                                            // scroll to this element
                                            flickable.contentY = newContentY
                                        }
                                    }
    
                                    Behavior on height {
                                        NumberAnimation {
                                            easing.type: Easing.OutCubic
                                            duration: 500
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
    
            Rectangle {
                height: 100
                width: 320
    
                Row
                {
                    Button {
                        text: "Delete"
                        onClicked: {
                            console.log("Delete clicked")
                            cppModel.remove_item();
                            flickable.goto_header();
                        }
                    }
                    Button {
                        text: "Save"
                        onClicked: {
                            console.log("Save clicked")
                            cppModel.deactivate_current_item()
                        }
                    }
                }
            }
        }
    }
    
    

    Screenshot of the sample program

    0_1563517642889_Screenshot from 2019-07-19 08-27-03.png


Log in to reply