[SOLVED] Nested ListViews - Problem with indexing - QtQuick 2.0

  • I am working on an application in which a ListView may have multiple delegate types, one of which may itself be a list view.

    The following screenshots demonstrate the basic functionality.

    Vertical scrolling through main ListView:
    !https://lh3.googleusercontent.com/twdgan47Mj8zvwBGTJeTRdQiCzRw118l4k5b6bOp5fMLmVdCuDfbUQm2bVd3cxvAayKscDgJ10k(img 1)! !https://lh6.googleusercontent.com/ePeMrXYgHzE8PB_KReKjzd6E1F31mPdtwebQ5a0XN4ocC11KnxSSCpFaZb3s97-awYysiNAA-4M(img 2)! !https://lh5.googleusercontent.com/hjj8TywMDqKQm8U5RJ_rqvduA77xkvTMTp0TYsJMfR2FLv0duGS3Jplp8nuEj3NUcRrGXVkh6ec(img 3)!

    Horizontal scrolling through nested ListView:
    !https://lh3.googleusercontent.com/twdgan47Mj8zvwBGTJeTRdQiCzRw118l4k5b6bOp5fMLmVdCuDfbUQm2bVd3cxvAayKscDgJ10k(img 1)! !https://lh4.googleusercontent.com/AMKHGqlYWWSeRFUddPQ7cepKw0QAlgYKTbd0PAXR4PC5w-lifgVTwjrbnkG7B6qUPCtk4dqH3hY(img 4)! !https://lh4.googleusercontent.com/qgSkpXDKEpoirB1ojWxjNxKPoKfL4ncu9EnBPGG-SInBILYTM91yU3f_61nHaZEg5CUS-RagKes(img 5)!

    Nested ListView maintains its currentIndex when scrolling through main ListView:
    !https://lh5.googleusercontent.com/oXY-Bsjum56eBozKtWK8Yrg67BpfF38d7jzQgVEahQWwjIPeAowb06_wgCknja0fjcyBOZn93rE(img 6)! !https://lh6.googleusercontent.com/O4XPzM2uDLrG0B58tRJTtd2N4knkEZ16Sfg_LwLu-beRmZhrlFx4kqaylPKbMEJ1oLDgWGIiMDk(img 7)!

    I'm able to get most of the functionality I'm after, but I have not been able to find a way to access and store the index state of the nested ListViews when a different element is selected in the main list (it is ~faked in the screenshots).

  • CODE
    The simplified version of the views/model


    Create a simple vertical ListView
    @import QtQuick 2.0

    Rectangle {
    id: root
    width: 200
    height: 280

    ListView {  //Main Vertical ListView
        id: list
        anchors.fill: parent
        model: TestModel{}
        delegate: testDelegate
        highlight:Rectangle{color: "#FF404040";}

    The delegate component selects from 2 different components based on the a 'type' role in the ListItem.
    @Component{ //Delegate Component
    id: delegateItem
    width: root.width
    //Loader picks correct delegate based on 'type' role in listModel
    Loader {
    anchors.fill: parent
    sourceComponent: chooseDelegate(type)
    function chooseDelegate(type){
    if (type=="text") return textDelegate;
    else if (type=="quick") return rowDelegate;

    The textDelegate simply displays the 'name' role
    @//Delegate for 'text' elements
    id: textDelegate
    text: name
    font.pixelSize: 32
    color: {if(delegateItem.ListView.isCurrentItem) return "white";else return "darkgrey";}
    }//end text delegate@

    The rowDelegate creates an embedded horizontal ListView
    @//Delegate for 'row' elements
    id: rowDelegate
    id: rowView
    spacing: 20
    orientation: ListView.Horizontal
    model: attributes
    currentIndex: list.model.get(0).index
    delegate: Text{
    text: name
    font.pixelSize: 24
    color:if(rowItemDelegate.ListView.isCurrentItem && delegateItem.ListView.isCurrentItem) return "white";else return "darkgrey";
    }//end row listview
    }//end row delegate
    }//end delegate item
    }//end delegate component@
    The nested ListView retrieves its 'currentIndex' from the 'index' role of the first ListElement in the ListModel.
    currentIndex: list.model.get(0).index
    The problem with this is that only 1 nested ListView element can be in the main list, and I have to know its position in the list to pass to the get method. I can change the line to currentIndex: list.model.get(list.currentIndex).index and scrolling the nested ListView will work, but as soon as I select a new element in the main ListView, the 'index' role is no longer valid, and the nested ListView bounces back to the beginning.

    I use the keyboard to directly augment the 'index' role of the ListModel
    @ focus:true
    Keys.onPressed: {
    if( event.key === Qt.Key_Q )
    event.accepted = true;
    else if( event.key === Qt.Key_Left)
    event.accepted = true;
    console.debug("quick item "+list.model.get(list.currentIndex).index);

        else if( event.key === Qt.Key_Right)
            event.accepted = true;
                    console.debug("quick item "+list.model.get(list.currentIndex).index);
        else if( event.key === Qt.Key_Down){
            event.accepted = true;
            if(list.currentIndex<list.count-1) list.currentIndex++;
        else if( event.key === Qt.Key_Up){
            event.accepted = true;
            if(list.currentIndex>0) list.currentIndex--;
    }//end key event handler

    }// end root Rectangle@

    Finally, the ListModel: TestModel.qml
    @import QtQuick 2.0

    name: "shortcuts"
    type: "quick"
    index: 0
    ListElement {type: "text"; name: "A "}
    ListElement {type: "text"; name: "B "}
    ListElement {type: "text"; name: "C "}
    ListElement {type: "text"; name: "D "}
    ListElement {type: "text"; name: "E "}
    ListElement{type: "text"; name: "1"}
    ListElement{type: "text"; name: "2"}
    ListElement{type: "text"; name: "3"}
    ListElement{type: "text"; name: "4"}
    ListElement{type: "text"; name: "5"}

    So there may be 3 potential solutions -

    Is there any way for the nested ListView, which is part of a delegate component, to retrieve the index of the ListElement it is associated with?

    Is there any way to directly access the nested ListView.currentIndex property in the key handlers?

    Or do I need to subclass one of the Qt abstract models and extend a custom tree data model to QML?

    greatly appreciate any help,


  • Hello,

    I have read -carefully- your code.... and eventually i have an alternative point of view :)
    If i have understood correctly, you have different ways of displaying your elements.
    So you choose to have two delegates.
    Why don't you create only one delegate, "embedding" the two you have created, and then choosing if the "inside View" is displayed or not ?
    I know that a bit different, but it will probably be simplier for you to manage your indexes.


  • [quote author="dmcr" date="1348739606"]
    Why don't you create only one delegate, "embedding" the two you have created, and then choosing if the "inside View" is displayed or not ?

    This is essentially what I'm doing - the main ListView always uses the testDelegate component. I use a Loader in the testDelegate component to decide what to display because I thought it would be more resource efficient (especially in the real application, where there will be far more delegate types icons etc.).

    I don't see how your suggestions would solve my problem; I still need a way to access data stored in a delegate instance from outside, or for a delegate instance to retrieve its index in a listview.

  • I figured it out. The problem was that by defining an 'index' role in my ListModel, which I use to track the index in the embedded attributes model, I ended up overwriting the built in 'index' role of the ListElements. I changed this new role to 'subIndex', and changed
    currentIndex: list.model.get(0).index to currentIndex: list.model.get(index).subIndex
    and it works fine now.

  • Sometimes i read the code given too fast.....
    However yeah it was a nasty bug!--

Log in to reply

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.