#pragma once #include #include #include #include #include #include #include #include #include #include #include "doc.hpp" ///! View into a list of objets /// Typed view into a list of objects. /// /// An ObjectModel works as a QML [Data Model], allowing efficient interaction with /// components that act on models. It has a single role named `modelData`, to match the /// behavior of lists. /// The same information contained in the list model is available as a normal list /// via the `values` property. /// /// #### Differences from a list /// Unlike with a list, the following property binding will never be updated when `model[3]` changes. /// ```qml /// // will not update reactively /// property var foo: model[3] /// ``` /// /// You can work around this limitation using the @@values property of the model to view it as a list. /// ```qml /// // will update reactively /// property var foo: model.values[3] /// ``` /// /// [Data Model]: https://doc.qt.io/qt-6/qtquick-modelviewsdata-modelview.html#qml-data-models class UntypedObjectModel: public QAbstractListModel { QSDOC_CNAME(ObjectModel); Q_OBJECT; /// The content of the object model, as a QML list. /// The values of this property will always be of the type of the model. Q_PROPERTY(QList values READ values NOTIFY valuesChanged); QML_NAMED_ELEMENT(ObjectModel); QML_UNCREATABLE("ObjectModels cannot be created directly."); public: explicit UntypedObjectModel(QObject* parent): QAbstractListModel(parent) {} [[nodiscard]] qint32 rowCount(const QModelIndex& parent) const override; [[nodiscard]] QVariant data(const QModelIndex& index, qint32 role) const override; [[nodiscard]] QHash roleNames() const override; [[nodiscard]] QList values() const { return this->valuesList; }; void removeAt(qsizetype index); Q_INVOKABLE qsizetype indexOf(QObject* object); static UntypedObjectModel* emptyInstance(); signals: void valuesChanged(); /// Sent immediately before an object is inserted into the list. void objectInsertedPre(QObject* object, qsizetype index); /// Sent immediately after an object is inserted into the list. void objectInsertedPost(QObject* object, qsizetype index); /// Sent immediately before an object is removed from the list. void objectRemovedPre(QObject* object, qsizetype index); /// Sent immediately after an object is removed from the list. void objectRemovedPost(QObject* object, qsizetype index); protected: void insertObject(QObject* object, qsizetype index = -1); bool removeObject(const QObject* object); // Assumes only one instance of a specific value void diffUpdate(const QVector& newValues); QVector valuesList; private: static qsizetype valuesCount(QQmlListProperty* property); static QObject* valueAt(QQmlListProperty* property, qsizetype index); }; template class ObjectModel: public UntypedObjectModel { public: explicit ObjectModel(QObject* parent): UntypedObjectModel(parent) {} [[nodiscard]] QVector& valueList() { return *std::bit_cast*>(&this->valuesList); } [[nodiscard]] const QVector& valueList() const { return *std::bit_cast*>(&this->valuesList); } void insertObject(T* object, qsizetype index = -1) { this->UntypedObjectModel::insertObject(object, index); } void insertObjectSorted(T* object, const std::function& compare) { auto& list = this->valueList(); auto iter = list.begin(); while (iter != list.end()) { if (!compare(object, *iter)) break; ++iter; } auto idx = iter - list.begin(); this->UntypedObjectModel::insertObject(object, idx); } void removeObject(const T* object) { this->UntypedObjectModel::removeObject(object); } // Assumes only one instance of a specific value void diffUpdate(const QVector& newValues) { this->UntypedObjectModel::diffUpdate(*std::bit_cast*>(&newValues)); } static ObjectModel* emptyInstance() { return static_cast*>(UntypedObjectModel::emptyInstance()); } };