// Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLLISTMODEL_P_P_H #define QQMLLISTMODEL_P_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "qqmllistmodel_p.h" #include #include #include #include #include QT_REQUIRE_CONFIG(qml_list_model); QT_BEGIN_NAMESPACE class DynamicRoleModelNode; class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject { public: DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); ~DynamicRoleModelNodeMetaObject(); bool m_enabled; protected: void propertyWrite(int index) override; void propertyWritten(int index) override; private: DynamicRoleModelNode *m_owner; }; class DynamicRoleModelNode : public QObject { Q_OBJECT public: DynamicRoleModelNode(QQmlListModel *owner, int uid); static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner); void updateValues(const QVariantMap &object, QVector &roles); QVariant getValue(const QString &name) const { return m_meta->value(name.toUtf8()); } bool setValue(const QByteArray &name, const QVariant &val) { return m_meta->setValue(name, val); } void setNodeUpdatesEnabled(bool enable) { m_meta->m_enabled = enable; } int getUid() const { return m_uid; } static QVector sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target); private: QQmlListModel *m_owner; int m_uid; DynamicRoleModelNodeMetaObject *m_meta; friend class DynamicRoleModelNodeMetaObject; }; class ModelNodeMetaObject : public QQmlOpenMetaObject { public: ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex); ~ModelNodeMetaObject(); QMetaObject *toDynamicMetaObject(QObject *object) override; static ModelNodeMetaObject *get(QObject *obj); bool m_enabled; QQmlListModel *m_model; int m_elementIndex; void updateValues(); void updateValues(const QVector &roles); bool initialized() const { return m_initialized; } protected: void propertyWritten(int index) override; private: using QQmlOpenMetaObject::setValue; void emitDirectNotifies(const int *changedRoles, int roleCount); void initialize(); bool m_initialized; }; namespace QV4 { namespace Heap { struct ModelObject : public QObjectWrapper { void init(QObject *object, QQmlListModel *model) { QObjectWrapper::init(object); m_model = model; QObjectPrivate *op = QObjectPrivate::get(object); m_nodeModelMetaObject = static_cast(op->metaObject); } void destroy() { QObjectWrapper::destroy(); } int elementIndex() const { return m_nodeModelMetaObject->m_elementIndex; } QQmlListModel *m_model; ModelNodeMetaObject *m_nodeModelMetaObject; }; } struct ModelObject : public QObjectWrapper { V4_OBJECT2(ModelObject, QObjectWrapper) V4_NEEDS_DESTROY ListModel *listModel() const { return d()->m_model->m_listModel; } protected: static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver); static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); }; } // namespace QV4 class ListLayout { public: ListLayout() : currentBlock(0), currentBlockOffset(0) {} ListLayout(const ListLayout *other); ~ListLayout(); class Role { public: Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} explicit Role(const Role *other); ~Role(); // This enum must be kept in sync with the roleTypeNames variable in qqmllistmodel.cpp enum DataType { Invalid = -1, String, Number, Bool, List, QObject, VariantMap, DateTime, Url, Function, MaxDataType }; QString name; DataType type; int blockIndex; int blockOffset; int index; ListLayout *subLayout; }; const Role *getRoleOrCreate(const QString &key, const QVariant &data); const Role &getRoleOrCreate(QV4::String *key, Role::DataType type); const Role &getRoleOrCreate(const QString &key, Role::DataType type); const Role &getExistingRole(int index) const { return *roles.at(index); } const Role *getExistingRole(const QString &key) const; const Role *getExistingRole(QV4::String *key) const; int roleCount() const { return roles.size(); } static void sync(ListLayout *src, ListLayout *target); private: const Role &createRole(const QString &key, Role::DataType type); int currentBlock; int currentBlockOffset; QVector roles; QStringHash roleHash; }; struct StringOrTranslation { ~StringOrTranslation(); bool isSet() const { return binding || arrayData; } bool isTranslation() const { return binding && !arrayData; } void setString(const QString &s); void setTranslation(const QV4::CompiledData::Binding *binding); QString toString(const QQmlListModel *owner) const; QString asString() const; private: void clear(); union { char16_t *stringData = nullptr; const QV4::CompiledData::Binding *binding; }; QTypedArrayData *arrayData = nullptr; uint stringSize = 0; }; /*! \internal */ class ListElement { public: enum ObjectIndestructible { Indestructible = 1, ExplicitlySet = 2 }; enum { BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelNodeMetaObject *) }; ListElement(); ListElement(int existingUid); ~ListElement(); static QVector sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout); private: void destroy(ListLayout *layout); int setVariantProperty(const ListLayout::Role &role, const QVariant &d); int setJsProperty(const ListLayout::Role &role, const QV4::Value &d, QV4::ExecutionEngine *eng); int setStringProperty(const ListLayout::Role &role, const QString &s); int setDoubleProperty(const ListLayout::Role &role, double n); int setBoolProperty(const ListLayout::Role &role, bool b); int setListProperty(const ListLayout::Role &role, ListModel *m); int setQObjectProperty(const ListLayout::Role &role, QV4::QObjectWrapper *o); int setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o); int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); int setUrlProperty(const ListLayout::Role &role, const QUrl &url); int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f); int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b); void setStringPropertyFast(const ListLayout::Role &role, const QString &s); void setDoublePropertyFast(const ListLayout::Role &role, double n); void setBoolPropertyFast(const ListLayout::Role &role, bool b); void setQObjectPropertyFast(const ListLayout::Role &role, QV4::QObjectWrapper *o); void setListPropertyFast(const ListLayout::Role &role, ListModel *m); void setVariantMapFast(const ListLayout::Role &role, QV4::Object *o); void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); void setUrlPropertyFast(const ListLayout::Role &role, const QUrl &url); void setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f); void clearProperty(const ListLayout::Role &role); QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng); ListModel *getListProperty(const ListLayout::Role &role); StringOrTranslation *getStringProperty(const ListLayout::Role &role); QV4::QObjectWrapper *getQObjectProperty(const ListLayout::Role &role); QV4::PersistentValue *getGuardProperty(const ListLayout::Role &role); QVariantMap *getVariantMapProperty(const ListLayout::Role &role); QDateTime *getDateTimeProperty(const ListLayout::Role &role); QUrl *getUrlProperty(const ListLayout::Role &role); QJSValue *getFunctionProperty(const ListLayout::Role &role); inline char *getPropertyMemory(const ListLayout::Role &role); int getUid() const { return uid; } ModelNodeMetaObject *objectCache(); char data[BLOCK_SIZE]; ListElement *next; int uid; QObject *m_objectCache; friend class ListModel; }; /*! \internal */ class ListModel { public: ListModel(ListLayout *layout, QQmlListModel *modelCache); ~ListModel() {} void destroy(); int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); int setExistingProperty(int uid, const QString &key, const QV4::Value &data, QV4::ExecutionEngine *eng); QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng); ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); void updateTranslations(); int roleCount() const { return m_layout->roleCount(); } const ListLayout::Role &getExistingRole(int index) const { return m_layout->getExistingRole(index); } const ListLayout::Role *getExistingRole(QV4::String *key) const { return m_layout->getExistingRole(key); } const ListLayout::Role &getOrCreateListRole(const QString &name) { return m_layout->getRoleOrCreate(name, ListLayout::Role::List); } int elementCount() const { return elements.count(); } enum class SetElement {WasJustInserted, IsCurrentlyUpdated}; void set(int elementIndex, QV4::Object *object, QVector *roles); void set(int elementIndex, QV4::Object *object, SetElement reason = SetElement::IsCurrentlyUpdated); int append(QV4::Object *object); void insert(int elementIndex, QV4::Object *object); Q_REQUIRED_RESULT QVector> remove(int index, int count); int appendElement(); void insertElement(int index); void move(int from, int to, int n); static bool sync(ListModel *src, ListModel *target); QObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); private: QPODVector elements; ListLayout *m_layout; QQmlListModel *m_modelCache; struct ElementSync { ListElement *src = nullptr; ListElement *target = nullptr; int srcIndex = -1; int targetIndex = -1; QVector changedRoles; }; void newElement(int index); void updateCacheIndices(int start = 0, int end = -1); template void setArrayLike(QV4::ScopedObject *o, QV4::String *propertyName, ListElement *e, ArrayLike *a) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); if (r.type == ListLayout::Role::List) { ListModel *subModel = new ListModel(r.subLayout, nullptr); int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { *o = a->get(j); subModel->append(*o); } e->setListPropertyFast(r, subModel); } } friend class ListElement; friend class QQmlListModelWorkerAgent; friend class QQmlListModelParser; }; QT_END_NAMESPACE Q_DECLARE_METATYPE(ListModel *); #endif // QQUICKLISTMODEL_P_P_H