Marcus
Marcus

Reputation: 1930

Populate GridLayout with Repeater

I try to add cells to my GridLayout by using a Repeater. My data is stored in a model and containing two properties per element:

My goal is to get a GridLayout containing the Title in first cell and the Value in the second cell of each row.

GridLayout {
    id: someId
    columns: 2
    rowSpacing: 5
    columnSpacing: 5
    anchors.margins: 5
    anchors.left: parent.left
    anchors.right: parent.right

    Repeater {
        model: myModel
        Label {
            text: modelData.title
        }
        TextArea {
            text: modelData.value
        }
    }
}

But QML Repeater allows only one element. Any ideas how I could get the layout I want?

+------------+---------------------------------------+
|            |                                       |
|  title0    |         value0                        |
|            |                                       |
|            |                                       |
+------------+---------------------------------------+
|            |                                       |
|            |                                       |
|  title1    |         value1                        |
|            |                                       |
|            |                                       |
+------------+---------------------------------------+
|            |                                       |
|  title2    |         value2                        |
|            |                                       |
|            |                                       |
+------------+---------------------------------------+

Upvotes: 9

Views: 12910

Answers (4)

m7913d
m7913d

Reputation: 11064

You can use GridLayout.flow to specify in which order the cells should be filled, i.e. row- (GridLayout.LeftToRight) or column-wise (GridLayout.TopToBottom). Note that you should specify the number of rows when using GridLayout.TopToBottom.

Using this solution, the (simplified) example of skypjack would become:

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4

Window {
    width: 600; height: 400; visible: true

    GridLayout {
        anchors.fill: parent

        // specify the flow and number of rows
        flow: GridLayout.TopToBottom
        rows: repeater.count

        Repeater {
            id: repeater
            model: [ "title1", "title2", "title3", "title4", "title5", "title6" ] // example model
            Label {
                Layout.fillWidth: true
                Layout.fillHeight: true
                text: modelData
            }
        }

        Repeater {
            model: [ "value1", "value2", "value3", "value4", "value5", "value6" ]  // example model
            TextArea {
                Layout.fillWidth: true
                Layout.fillHeight: true
                text: modelData
            }
        }
    }
}

Upvotes: 7

pixelgrease
pixelgrease

Reputation: 2098

Nest as many elements as you want inside an Item in the Repeater's delegate and re-parent them to the GridLayout when the Item completes.

GridLayout {
    id: grid
    anchors.centerIn: parent
    columns: 2
    rowSpacing: 5
    columnSpacing: 5
    anchors.margins: 5

    Repeater {
        model: [
            { title: "title1 that's long", value: "value1" },
            { title: "title2 medium",      value: "value2" },
            { title: "title3",             value: "value3" }
        ]
        delegate: Item {
            Component.onCompleted: {
                while (children.length) { children[0].parent = grid; }
            }

            Label {
                Layout.alignment: Qt.AlignRight
                color: "gray"
                text: modelData.title
            }
            TextArea {
                Layout.fillWidth: true
                font.bold: true
                text: modelData.value
            }
        }
    }
}

Screenshot of running QML code

Upvotes: 1

skypjack
skypjack

Reputation: 50540

You can simply use two Repeaters within a GridLayout, as follows:

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4

Window {
    width: 600; height: 400; visible: true

    GridLayout {
        id: grid
        anchors.fill: parent
        columns: 2
        rowSpacing: 5
        columnSpacing: 5
        anchors.margins: 5
        // example models
        property var titles: [ "title1", "title2", "title3", "title4", "title5" ]
        property var values: [ "value1", "value2", "value3", "value4", "value5" ]

        Repeater {
            model: grid.titles
            Label {
                Layout.row: index
                Layout.column: 0
                Layout.fillWidth: true
                Layout.fillHeight: true
                text: modelData
            }
        }

        Repeater {
            model: grid.values
            TextArea {
                Layout.row: index
                Layout.column: 1
                Layout.fillWidth: true
                Layout.fillHeight: true
                text: modelData
            }
        }
    }
}

The index parameter is freely available and store the current row of the model.

By using the Layout.fillWidth attached property you can control the width of the single column.

Of course, each cell that belongs to a column has the same size of all the other cells of that column, unlike what happens using two Column components.

This solution has a few drawbacks, but it's good if your purpose is mainly to print plain data from a model.

Upvotes: 18

folibis
folibis

Reputation: 12854

The model-view principle assumes that each model node displays by different delegate component object. So I advice you to listen to @BaCaRoZzo's comment and do that with Column instead of GridLayout. Sure, QML is very flexible and you can do something like that:

Component {
    id: labelDelegate
    Label { text: myList.get(_index / 2).title }
}

Component {
    id: textAreaDelegate
    TextArea { text: myList.get(_index / 2).value }
}

ListModel {
    id: myList
    ListElement {title: "title1"; value: "value1"}
    ListElement {title: "title2"; value: "value2"}
    ListElement {title: "title3"; value: "value3"}
}

GridLayout {
    anchors.fill: parent
    columns: 2
    Repeater {
        model: myList.count * 2
        delegate: Loader {
            property int _index: index
            sourceComponent: {
                if(index % 2)
                    return textAreaDelegate;
                else
                    return labelDelegate;
            }
        }
    }
}

but that's too weird to use it in real project.

Upvotes: 4

Related Questions