Phrogz
Phrogz

Reputation: 303530

QML List of Lists (2D data)

Summary: How can I nest one ListView inside another, feeding data from the outer model to an inner model inside the delegate?

Data and Desire

I have an array of arrays of data in JS:

[[ {t:0, label:"Big Bang"}, {t:5, label:"Earth Forms"} ],
 [ {t:0, label:"Alternate Bang"}, {t:3, label:"Cool Stuff"} ],
 [ {t:1, label:"Late Bang"}, {t:4, label:"Whee"}, {t:5, label:"More" } ]]

I want to display this as a horizontal-scrolling "timeline", where the top-level array represents rows of data and each sub-array represents items within that row, placed at various times.

Excel-based mockup

Note that the timeline is only discrete at the millisecond level, with values out to the hour range. Unlike what this Excel-based mockup shows, a 2D grid of merged cells would be infeasible.

Code and Problem

I'm passing the array to a ListView via a custom ListModel:

Item {
    property var timeline: []
    onTimelineChanged: {
      rowData.clear();
      timeline.forEach(function(evts){
        rowData.append({ events:evts });
      });
    }
    ListView {
        id: rows
        model: ListModel { id:rowData }
        delegate: Item {
            Text { text: "Idx #"+index }
            ListView {
                orientation: ListView.Horizontal
                model: ListModel { id:events }
                delegate: …
            }
        }
    }
}

This is populating the rows properly, but I cannot figure out how to pass the events data role from the outer ListView into the events model within each delegate.

Additionally, as I wrote this question up, I realized that each row may try to scroll horizontally independent of one another. I want them all to scroll as a group. This makes me wonder if nested ListView is the appropriate tool for this job, or if I should be using some other data-driven QML structure.

Upvotes: 1

Views: 2321

Answers (1)

mcchu
mcchu

Reputation: 3369

You can simply assign events to the inner ListView.model, for example:

ListView {
    anchors.fill: parent
    model: ListModel { id:rowData }
    delegate: Rectangle {
        width: parent.width; height: 50
        ListView {
            anchors.fill: parent; orientation: ListView.Horizontal
            model: events
            delegate: Item {
                width: 80; height: 20
                Text { text: label }
            }
        }
    }
}

When a JavaScript array is appended to a ListModel row, it will be automatically converted to a ListModel (however, Qt documentation says nothing about this). So when you access the role name events in the delegate in the outer ListView, what you get is something like this:

ListModel { //row 0
    id: events
    ListElement {t:0, label:"Big Bang"}
    ListElement {t:5, label:"Earth Forms"}
}

And I think it's fine to horizontally scroll all inner ListView at the same time by setting contentX or positionViewAtIndex to all inner ListView.

Upvotes: 1

Related Questions