jco
jco

Reputation: 1405

Getting the ListView index of a QList object

I've exposed a QList<MyItem*> as a model for a ListView. But how can I get the ListView index of a MyItem* object?

Context: I'm making a findObject() function in C++ and when I've found the object (a MyItem*), I want to scroll to the corresponding item in the ListView.

Some code (full source code and Qt Creator project can be found at https://github.com/joncol/qml_test).

The item class:

class MyItem : public QObject
{
    Q_OBJECT
    Q_PROPERTY(qint32 data READ data NOTIFY dataChanged)

public:
    MyItem() : m_data(s_counter++) {}

    quint32 data() const { return m_data; }

signals:
    void dataChanged();

private:
    static int s_counter;
    int m_data;
};

The model:

class MyCppModel : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QQmlListProperty<MyItem> itemList READ itemList NOTIFY itemListChanged)

public:
    MyCppModel()
    {
        m_specialItem = new MyItem;

        m_model.append(new MyItem);
        m_model.append(new MyItem);
        m_model.append(new MyItem);
        m_model.append(m_specialItem);
        m_model.append(new MyItem);
    }

    QQmlListProperty<MyItem> itemList()
    {
        return QQmlListProperty<MyItem>(this, m_model);
    }

    Q_INVOKABLE QVariant specialItem()
    {
        return QVariant::fromValue(m_specialItem);
    }

signals:
    void itemListChanged();

private:
    QList<MyItem*> m_model;
    MyItem* m_specialItem; // simulate found item
};

The 'main' function:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;
    QQmlContext* c = engine.rootContext();

    MyCppModel* myCppModel = new MyCppModel;
    c->setContextProperty("myCppModel", myCppModel);

    qmlRegisterUncreatableType<MyItem>("CppModel", 1, 0, "MyItem", "");

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

And, finally the QML:

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

    ColumnLayout {
        anchors.centerIn: parent

        Rectangle {
            width: 150
            height: 25
            color: "#e67e22"
            border.width: 1
            border.color: "black"

            Text {
                anchors.centerIn: parent
                text: "Mark Special Item"
            }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    var item = myCppModel.specialItem()
                    console.debug("how to color the special item: " + item)
                }
            }
        }

        ListView {
            id: itemList
            width: 200
            height: 25 * count

            model: myCppModel.itemList

            delegate: Item {
                width: parent.width
                height: 25

                Rectangle {
                    width: parent.width
                    height: 20
                    color: "#34495e"
                    border.width: 1
                    border.color: "black"

                    Text {
                        x: 10
                        anchors.verticalCenter: parent.verticalCenter
                        text: modelData.data
                        color: "white"
                    }
                }
            }
        }
    }
}

Upvotes: 2

Views: 1151

Answers (1)

Mitch
Mitch

Reputation: 24416

There are a few ways to do this, depending on your application logic.

1. Add a isSpecialItem property to MyItem

class MyItem : public QObject
{
    Q_OBJECT
    Q_PROPERTY(qint32 data READ data NOTIFY dataChanged)
    Q_PROPERTY(bool isSpecialItem READ isSpecialItem WRITE setIsSpecialItem NOTIFY isSpecialItemChanged)
    // ...
}

Your delegate would then look like this:

Rectangle {
    width: parent.width
    height: 20
    color: "#34495e"
    border.width: 1
    border.color: isSpecialItem ? "red" : "black"

    Text {
        x: 10
        anchors.verticalCenter: parent.verticalCenter
        text: modelData.data
        color: "white"
    }
}

This is the easiest approach, but pollutes MyItem with something that you might consider more UI-specific.

2. Make MyCppModel derive from QAbstractItemModel

With this option, you could have a SpecialItem role that is available in the delegate, with the QML code for the delegate being identical to option #1.

I would call this the canonical approach to this problem, especially if itemList does not need to be a QQmlListProperty. It doesn't require adding the potentially UI-specific isSpecialItem property to MyItem, and can be stored in the same way that you're currently storing it; in your data() function, you'd write something like:

if (role == SpecialItem) {
    return QVariant(m_specialItem == m_model.at(index.row()));
}

3. Expose the special item as an index property of the list

To do it this way, you'd turn the specialItem() function into a property of MyCppModel:

Q_PROPERTY(int specialItemIndex READ specialItemIndex WRITE specialItemIndex NOTIFY specialItemIndexChanged)

Then, your delegate would look very similar to option 2, except for one line:

border.color: myCppModel.specialItemIndex == index ? "red" : "black"

Upvotes: 1

Related Questions