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

Infinite listview - sometimes is visible first element



  • Hi

    I have listview with numbers. When I am using wheel of mouse, then is everything ok, but when I am using button of mouse and I am trying scroll very fast ( especially from up to down), then I see first element.
    Problem is with currentIndex. You can see value of currentIndex in log. After some time, currentindex is decreasing to 0 and then I see first element and I don't know how to fix it.

    Function setValues is using to append values into list.
    onCurrentItemChanged is using to move last item to beginning and first item at last position - this function doesn't work as expected.
    onWheel - the same what onCurrentItemChanged but it use wheel of mouse - it works as expected (I hope so ;-) )

    I tried to use
    onCurrentIndexChanged and AtYEndChanged (There is visible space after last element or before first element) and it doesn't work as expected, or I can't implement it.

    You can create this object, using this code

        property var component_value: Qt.createComponent("qrc:/controls/infinite_listview.qml")
        property var object_value: component_value.createObject(mainWindow)
    
    object_value.setNumberOfVisibleElements(3)
    object_value.setValues(0, 6, 2, '0')
    

    Below, there is file with listview

    import QtQuick 2.3
    import QtQuick.Window 2.15
    import QtQuick.Controls 1.4
    import QtQuick.Layouts 1.15
    import QtQml 2.12
    
    Item
    //Window
    {
        x: 0
        y: 0
        id: mainListView
    
        width: parent.width/3
        height: parent.height
    
        property color font_color: "black"
    
        //Zmienne uzywane w funkcji "setNumberOfVisibleElements".
        //Nie dało się dostac bezposrednio do parametrow obiektu dziecka,
        //wiec trzeba bylo stworzyc te zmienne i dopiero te zmienne mozna bylo przypisac do parametrow obiektu dziecka.
        //Modyfikujac te parametry, modyfikuje sie parametry znajdujace sie w obiekcie dziecka
        property int delegate_height: listviewID.height/3
        property var preferredHighlightBegin: mainListView.height/3
        property var preferredHighlightEnd: mainListView.height/3
    
        signal signal_value_changed(int itemID, string number, int current_index)
    
        ListModel
        {
            id: modelID
    
    //        ListElement
    //        {
    //            itemID: 0
    //            number: 0
    //        }
        }
    
        Component
        {
            id: delegateID
            Rectangle
            {
                id: itemDelegate
    //            height: listviewID.height/3     //Tu zmienic na "listviewID.height", zeby byl jeden wiersz, listviewID.height/3 - 3 wiersze, listviewID.height/7 - 7 wierszy (currentitem nie jest na srodku)
                height: delegate_height
                width: listviewID.width
                x: 0
                y: 0
    
                Text
                {
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                    height: parent.height
                    width: parent.width
                    text: number
    
                    font.pixelSize: parent.height
                    fontSizeMode: Text.Fit
                    color: font_color
                }
            }
        }
    
        ListView
        {
            id: listviewID
    
            snapMode: ListView.SnapToItem
    
            property var first_value: 0
            property var last_value: 99
            property var fill_length_value: 2
            property var fill_sign_value: '0'
    
            property var gradient_color: 'gray'
            property var border_size: 1
    
            width: parent.width
            height: parent.height
    
            //Ucina listview do konkretnego rozmiaru. PRzydatne, gdy uzyje sie opcji preferredHighlightBegin i preferredHighlightEnd
            clip: true
    
            //Ustawia ilosc widocznego listview na poczatku i na koncu. To jest dodatkowa przestrzen widoczna. PRzydaje sie, zeby elementy plynnie pojawialy sie i znikaly Dane podawane w pikselach.
            preferredHighlightBegin: mainListView.preferredHighlightBegin
            preferredHighlightEnd: mainListView.preferredHighlightEnd
            highlightRangeMode: ListView.StrictlyEnforceRange
            cacheBuffer: delegate_height*2
    
            highlightMoveDuration: 1000
            highlightMoveVelocity: -1
    
            property bool set_values_first_time: false
    
            model: modelID
            delegate: delegateID
    
            Rectangle
            {
                //Obramowanie srodkowej czesci listy
                id: current_index_border
                x: 0
                y: parent.height/3
                width: parent.width
                height: parent.height/3
                color: "transparent"
                border.width: 0
            }
    
            Rectangle
            {
                anchors.fill: parent
                border.width: listviewID.border_size
                gradient: Gradient
                {
                    GradientStop { position: 0.0; color: listviewID.gradient_color }
                    GradientStop { position: 0.33; color: "transparent" }
                    GradientStop { position: 0.66; color: "transparent" }
                    GradientStop { position: 1.0; color: listviewID.gradient_color }
                }
            }
    
            MouseArea
            {
                anchors.fill: parent
                onWheel:
                {
                    //Kod odpowiadajacy za nieskoczone przewijanie przy uzyciu scroola myszy
                    if (wheel.angleDelta.y < 0)
                    {
                        var curr_index_tmp = listviewID.currentIndex
                        if(listviewID.currentIndex > (modelID.count/2))
                        {
                            modelID.move(0, modelID.count - 1, 1)
                            modelID.move(0, modelID.count - 1, 1)
                            modelID.move(0, modelID.count - 1, 1)
    //                        modelID.move(0, modelID.count - 1, 1)
    //                        modelID.move(0, modelID.count - 1, 1)
                            listviewID.currentIndex = curr_index_tmp-3
                        }
                        listviewID.incrementCurrentIndex()
                    }
                    else if (wheel.angleDelta.y > 0)
                    {
                        var curr_index_tmp = listviewID.currentIndex
                        if(listviewID.currentIndex < (modelID.count/2))
                        {
                            modelID.move(modelID.count - 1, 0, 1)
                            modelID.move(modelID.count - 1, 0, 1)
                            modelID.move(modelID.count - 1, 0, 1)
    //                        modelID.move(modelID.count - 1, 0, 1)
    //                        modelID.move(modelID.count - 1, 0, 1)
                            listviewID.currentIndex = curr_index_tmp+3
                        }
                        listviewID.decrementCurrentIndex()
                    }
                }
    
            }
    
            onCurrentItemChanged:
            {
                mainListView.signal_value_changed(modelID.get(listviewID.currentIndex).itemID, modelID.get(listviewID.currentIndex).number, listviewID.currentIndex)
    
                //Kod odpowiedzialny za nieskonczone przewijanie przy uzyciu myszy (wcisniety lewy przycisk myszy) i dotyku na Androidzie. Kod przenosi elemeny z konca listy na poczatek i odwrotnie
                if(listviewID.currentIndex < (modelID.count/2))
                {
                    modelID.move(modelID.count - 1, 0, 1)
                }
    
                if(listviewID.currentIndex > (modelID.count/2))
                {
                    modelID.move(0, modelID.count - 1, 1)
                }
            }
    
            Component.onCompleted:
            {
            }
        }
    
        function setValues(first = 0, last = 59, fill_length = 2, fill_value = '0')
        {
            listviewID.first_value = Number(first)
            listviewID.last_value = Number(last)
            listviewID.fill_length_value = Number(fill_length)
            listviewID.fill_sign_value = fill_value
    
            if (listviewID.first_value > listviewID.last_value)
            {
                //Pierwsza wartosc musi byc wieksza, niz zdruga
                modelID.clear()
                modelID.append( {"itemID": 0, "number": "ERROR: 1"} )
                return -1
            }
            else if ( numberOfElements( listviewID.first_value, listviewID.last_value) <= 3)
            {
                //Ilosc elementow miedzy pierwszym, a ostatnim elementem musi byc min 3 elementy, zeby wszystkie pola byly zajete w liscie
                modelID.clear()
                modelID.append( {"itemID": 0, "number": "ERROR: 2"} )
                return -2
            }
    
            //Obliczenia zwiazane z okresleniem srodkowego elementu listy, ktory ma byc na poczatku listy
            var sum = numberOfElements(listviewID.first_value, listviewID.last_value)
            var half1 = 0
            var half2 = 0
    
            var resultOfMod = sum % 2
            if (resultOfMod === 0)
            {
                half1 = Math.floor(sum/2)
            }
            else
            {
                half1 = Math.floor(sum/2)
                half2 = sum - half1
            }
    
            modelID.clear()
            //Dodawania elementow do listy
            for (var index_tmp = listviewID.last_value - half1, i = listviewID.first_value ; i <= listviewID.last_value; i++, index_tmp++)
            {
                if ( index_tmp <= listviewID.last_value)
                {
                    modelID.append( {"itemID": index_tmp, "number": index_tmp.toString().padStart(listviewID.fill_length_value, listviewID.fill_sign_value)} )
                }
                else
                {
                    //Jak dodawany element dojdzie do wartosci listviewID.last_value, to licznik sie resetuje, zeby lista zaczynala sie na srodku, na srodku byla wartosc 0 i ostatni element takze byl na srodku listy
                    index_tmp = listviewID.first_value
                    modelID.append( {"itemID": index_tmp, "number": index_tmp.toString().padStart(listviewID.fill_length_value, listviewID.fill_sign_value)} )
                }
            }
    
            listviewID.currentIndex = -1    //Z tym ustaiwniem, poprawnie sie ustawia na 0, ale uzywajac scroola, przeskakuje w dzziwne miejsce
            for (var i = listviewID.first_value, index_tmp = 0 ; i <= listviewID.last_value; i++, index_tmp++)
            {
                if(modelID.get(index_tmp).itemID === listviewID.first_value)
                {
                    listviewID.positionViewAtIndex(index_tmp, ListView.Center)
                    listviewID.currentIndex = index_tmp
                    break
                }
            }
            listviewID.set_values_first_time = true
    
            return 0
        }
    
        function numberOfElements(a, b)
        {
            //Zwraca ilosc elementow, niezaleznie, czy pierwsza, lub ostatnia liczba jest dodatnia, lub ujemna
            return Math.abs(a-b) + 1
        }
    
        function getValue()
        {
            return modelID.get(listviewID.currentIndex).number
        }
    
        function setGradientColor(color = 'gray')
        {
            listviewID.gradient_color = color
        }
    
        function setBorderSize(size = 1)
        {
            listviewID.border_size = size
        }
    
        function setValue(value = 0)
        {
            if(value >= listviewID.first_value && value <= listviewID.last_value)
            {
                for (var i = listviewID.first_value, index_tmp = 0 ; i <= listviewID.last_value; i++, index_tmp++)
                {
                    if(modelID.get(index_tmp).itemID === Number(value))
                    {
                        listviewID.currentIndex = index_tmp
                        break
                    }
                }
            }
        }
    
        function setCustomValue(value = ":")
        {
            modelID.clear()
            modelID.append( {"itemID": 0, "number": value} )
        }
    
        function setFontColor(font_color = "black")
        {
            mainListView.font_color = font_color
        }
    
        function setNumberOfVisibleElements(numberOfVisibleElements = 3)
        {
            //Funkcja umozliwiajacy wybor ilosci wierszy w liscie. Dostepne sa opcje 1 i 3 wiersze
            if(numberOfVisibleElements === 3)
            {
                mainListView.delegate_height = Qt.binding(() => listviewID.height/3)
                mainListView.preferredHighlightBegin = Qt.binding(() => mainListView.height/3)
                mainListView.preferredHighlightEnd = Qt.binding(() => mainListView.height/3)
            }
            else if(numberOfVisibleElements === 1)
            {
                mainListView.delegate_height = Qt.binding(() => listviewID.height/1)
                mainListView.preferredHighlightBegin = Qt.binding(() => 0)
                mainListView.preferredHighlightEnd = Qt.binding(() => mainListView.height)
            }
        }
    
        function setSize(width, height)
        {
            mainListView.width = Qt.binding(() => width)
            mainListView.height = Qt.binding(() => height)
        }
    
        function setPosition(x, y)
        {
            mainListView.x = Qt.binding(() => x)
            mainListView.y = Qt.binding(() => y)
        }
    
        onSignal_value_changed:
        {
            console.log(itemID + " : " + number + " : " + current_index)
        }
    }
    
    
    

Log in to reply