Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different delegates for QML ListView

I would like to know if it's possible to use (several) different delegates for a QML ListView.

Depending on the individual object in the ListView model, I would like to visualize the objects with different delegates.

This piece of code explains what I want to achieve:

main.qml

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2

ApplicationWindow {
    title: qsTr("Hello World")
    width: 640
    height: 480
    visible: true

    ListModel {
        id: contactsModel
        ListElement {
            name: "Bill Smith"
            position: "Engineer"
        }
        ListElement {
            name: "John Brown"
            position: "Engineer"
        }
        ListElement {
            name: "Sam Wise"
            position: "Manager"
        }
    }

    ListView {
        id: contactsView
        anchors.left: parent.left
        anchors.top: parent.top
        width: parent.width
        height: parent.height
        orientation: Qt.Vertical
        spacing: 10
        model: contactsModel
        delegate: {
            if (position == "Engineer") return Employee;  //<--- depending on condition, load Contact{}
            else if (position == "Manager") return Manager; //<--- depending on condition, load Person{}
        }
    }
}

Employee.qml (One possible Component which I would like to use as a delegate)

import QtQuick 2.4

Rectangle{
    width: 200
    height: 50
    color: ListView.isCurrentItem ? "#003366" : "#585858"
    border.color: "gray"
    border.width: 1

    Text{
        anchors.centerIn: parent
        color: "white"
        text: name
    }
}

Manager.qml (other Component I would like to use as a delegate)

import QtQuick 2.4

Rectangle{
    width: 200
    height: 50
    color: "red"
    border.color: "blue"
    border.width: 1

    Text{
        anchors.centerIn: parent
        color: "white"
        text: name
    }
}

I would appreciate any advice! Thanks!

like image 937
dh1tw Avatar asked Aug 13 '15 10:08

dh1tw


4 Answers

I've had the same problem, the Qt documentation is providing a pretty good answer: http://doc.qt.io/qt-5/qml-qtquick-loader.html#using-a-loader-within-a-view-delegate

The easiest solution is an inline Component with a Loader to set a source file:

ListView {
    id: contactsView
    anchors.left: parent.left
    anchors.top: parent.top
    width: parent.width
    height: parent.height
    orientation: Qt.Vertical
    spacing: 10
    model: contactsModel
    delegate: Component {
        Loader {
            source: switch(position) {
                case "Engineer": return "Employee.qml"
                case "Manager": return "Manager.qml"
            }
        }
    }
}

Any attempt to use Loader.srcComponent will result in missing any variable from the model (including index). The only way for the variables to be present is the children Component to be inside the main Component, but then only one can be present, so it is useless.

like image 200
Aurélien Lambert Avatar answered Nov 12 '22 07:11

Aurélien Lambert


I believe it would be better to implement one base delegate for all kind of position which loads concrete implementation depending on position or any other data properties using Loader

BaseDelegate {
    property var position

    Loader {
        sourceComponent: {
            switch(position) {
                case "Engineer": return engineerDelegate
            }
        }
    }

    Component {
        id: engineerDelegate
        Rectangle {
             Text {  }
        }
    }
}
like image 34
Andrii Avatar answered Nov 12 '22 07:11

Andrii


I implemented it as follow:

ListView {
    id: iranCitiesList
    model: sampleModel
    delegate: Loader {
        height: childrenRect.height
        width: parent.width
        sourceComponent: {
            switch(itemType) {
            case "image" :
                return imageDel;
            case "video":
                return videoDel;
            }
        }
    }
    ImageDelegate { id: imageDel }
    VideoDelegate { id: videoDel }
}


ImageDelegate.qml

Component {
    Image { /*...*/ }
}


VideoDelegate.qml

Component {
    Item { /*....*/ }
}

Last note, check width and height of delegates. In my case, I had to set width and height of my delegate in Loader again.
Good luck - Mousavi

like image 31
S.M.Mousavi Avatar answered Nov 12 '22 06:11

S.M.Mousavi


The simplest way to do this now is using DelegateChooser. This also allows you to edit the properties of the delegates, which is something that is more difficult to do with Loader!

Example inspired from the docs:

import QtQuick 2.14
import QtQuick.Controls 2.14
import Qt.labs.qmlmodels 1.0

ListView {
    width: 640; height: 480

    ListModel {
        id: contactsModel
    ListElement {
        name: "Bill Smith"
        position: "Engineer"
    }
    ListElement {
        name: "John Brown"
        position: "Engineer"
    }
    ListElement {
        name: "Sam Wise"
        position: "Manager"
    }
   }

    DelegateChooser {
        id: chooser
        role: "position"
        DelegateChoice { roleValue: "Manager"; Manager { ... } }
        DelegateChoice { roleValue: "Employee"; Employee { ... } }
    }

    model: contractsModel
    delegate: chooser
}
like image 30
Snibbor Avatar answered Nov 12 '22 07:11

Snibbor