Marc Van Daele
Marc Van Daele

Reputation: 2734

Two-dimensional table with nested scrolling areas in QML

I want to create, in QML, a TV-schedule where the vertical axis is a list of Channels and the horizontal axis is time-based. For example something like

enter image description here
(source: zappware.com)

Initially, I created

So far so good. Only drawback is that the horizontal ListViews scroll one by one while they should scroll together.

So somehow, the contentX property of every horizontal ListView should be bound to the contentX property of the moving/flicking horizontal ListView. Note that this binding is dynamic: when flicking in the first row, all other rows should bind to the contentX of the first row. But this should be changed when flicking in the second row.

Any advice on how this can be done?

I tried a somewhat different approach by

This resulted in nice synchronous scrolling but I still have some issues

Feedback appreciated!

Upvotes: 1

Views: 1819

Answers (1)

dtech
dtech

Reputation: 49289

I'd say have only one vertical list view for the channels. But the channel names only, not the actual programs. Instead of a horizontal view for the programs, you can cram them all together in a single flickable, using the begin time and duration to layout the programs in the flickable by binding their x and width properties to the former.

Then you can bind the channel list view together with the vertical scrolling of the program items, so that you have the programs corresponding to their appropriate channels. This way you can scroll vertically from both, and only scroll horizontally the programs.

Here is a quick example:

ApplicationWindow {
    id: main
    width: 500
    height: 100
    visible: true
    color: "white"

    ListModel {
        id: modC
        ListElement { name: "Ch1" }
        ListElement { name: "Ch2" }
        ListElement { name: "Ch3" }
    }

    ListModel {
        id: modP1
        ListElement { name: "p1"; start: 0; duration: 6 }
        ListElement { name: "p2"; start: 6; duration: 6 }
        ListElement { name: "p3"; start: 12; duration: 6 }
        ListElement { name: "p4"; start: 18; duration: 6 }
    }
    ListModel {
        id: modP2
        ListElement { name: "p1"; start: 0; duration: 12 }
        ListElement { name: "p2"; start: 12; duration: 12 }
    }
    ListModel {
        id: modP3
        ListElement { name: "p1"; start: 0; duration: 8 }
        ListElement { name: "p2"; start: 8; duration: 8 }
        ListElement { name: "p3"; start: 16; duration: 8 }
    }

    property var subMod : [ modP1, modP2, modP3 ]

    Component {
        id: progDelegate
        Rectangle {
            property var source
            x: source.start * 50
            width: source.duration * 50
            height: 50
            color: "lightblue"
            border.color: "black"
            Text {
                text: source.name
            }
        }
    }

    Row {
        anchors.fill: parent
        ListView {
            id: list
            height: parent.height
            width: 100
            model: modC

            delegate: Item {
                width: 100
                height: 50
                Rectangle {
                    anchors.fill: parent
                    color: "red"
                    border.color: "black"
                    Text {
                        anchors.centerIn: parent
                        text: name
                    }
                }
                Component.onCompleted: {
                    var mod = subMod[index]
                    for (var i = 0; i < mod.count; ++i) progDelegate.createObject(flick.contentItem, {"source": mod.get(i), "y": index * 50})
                }
            }
        }
        Flickable {
            id: flick
            height: parent.height
            width: parent.width - list.width
            contentWidth: 1200
            contentHeight: contentItem.childrenRect.height
            clip: true
            flickableDirection: Flickable.HorizontalFlick
            contentY: list.contentY
        }
    }
}

Upvotes: 3

Related Questions