[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/modelMenuTest.qml
Create a simple vertical ListView
@import QtQuick 2.0Rectangle {
id: root
width: 200
height: 280ListView { //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:testDelegate
Item{
id: delegateItem
width: root.width
height:45
//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
Component{
id: textDelegate
Text{
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
Component{
id: rowDelegate
ListView{
id: rowView
spacing: 20
orientation: ListView.Horizontal
highlight:Rectangle{color:"gray"}
model: attributes
currentIndex: list.model.get(0).index
delegate: Text{
id:rowItemDelegate
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;
Qt.quit();
}
else if( event.key === Qt.Key_Left)
{
event.accepted = true;
if(list.model.get(list.currentIndex).type=="quick"){
if(list.model.get(list.currentIndex).index>0){
list.model.get(list.currentIndex).index--;
console.debug("quick item "+list.model.get(list.currentIndex).index);
}
}} else if( event.key === Qt.Key_Right) { event.accepted = true; if(list.model.get(list.currentIndex).type=="quick"){ if(list.model.get(list.currentIndex).index<list.model.get(list.currentIndex).attributes.count-1){ list.model.get(list.currentIndex).index++; 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.0ListModel{
ListElement{
name: "shortcuts"
type: "quick"
index: 0
attributes{
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,
AF
-
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.Democrie
-
[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 ?
Democrie
[/quote]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!--