diff options
Diffstat (limited to 'src/qml/types')
-rw-r--r-- | src/qml/types/qqmldelegatemodel.cpp | 38 | ||||
-rw-r--r-- | src/qml/types/qqmldelegatemodel_p.h | 7 | ||||
-rw-r--r-- | src/qml/types/qqmldelegatemodel_p_p.h | 4 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodel.cpp | 8 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodel_p.h | 4 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodelworkeragent.cpp | 4 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodelworkeragent_p.h | 4 | ||||
-rw-r--r-- | src/qml/types/qqmlmodelsmodule.cpp | 41 | ||||
-rw-r--r-- | src/qml/types/qqmlmodelsmodule_p.h | 5 | ||||
-rw-r--r-- | src/qml/types/qqmlobjectmodel.cpp | 4 | ||||
-rw-r--r-- | src/qml/types/qqmlobjectmodel_p.h | 5 | ||||
-rw-r--r-- | src/qml/types/qqmltableinstancemodel_p.h | 2 | ||||
-rw-r--r-- | src/qml/types/qqmltablemodel.cpp | 1059 | ||||
-rw-r--r-- | src/qml/types/qqmltablemodel_p.h | 170 | ||||
-rw-r--r-- | src/qml/types/qqmltablemodelcolumn.cpp | 200 | ||||
-rw-r--r-- | src/qml/types/qqmltablemodelcolumn_p.h | 224 | ||||
-rw-r--r-- | src/qml/types/qquickworkerscript.cpp | 12 | ||||
-rw-r--r-- | src/qml/types/qquickworkerscript_p.h | 3 | ||||
-rw-r--r-- | src/qml/types/types.pri | 8 |
19 files changed, 1748 insertions, 54 deletions
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 572f58339f..0e57119368 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -1127,7 +1127,7 @@ QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index) return QQmlIncubator::Ready; } -QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) +QVariant QQmlDelegateModelPrivate::variantValue(QQmlListCompositor::Group group, int index, const QString &name) { Compositor::iterator it = m_compositor.find(group, index); if (QQmlAdaptorModel *model = it.list<QQmlAdaptorModel>()) { @@ -1139,20 +1139,20 @@ QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index while (dot > 0) { QObject *obj = qvariant_cast<QObject*>(value); if (!obj) - return QString(); - int from = dot+1; + return QVariant(); + const int from = dot + 1; dot = name.indexOf(QLatin1Char('.'), from); value = obj->property(name.midRef(from, dot - from).toUtf8()); } - return value.toString(); + return value; } - return QString(); + return QVariant(); } -QString QQmlDelegateModel::stringValue(int index, const QString &name) +QVariant QQmlDelegateModel::variantValue(int index, const QString &role) { Q_D(QQmlDelegateModel); - return d->stringValue(d->m_compositorGroup, index, name); + return d->variantValue(d->m_compositorGroup, index, role); } int QQmlDelegateModel::indexOf(QObject *item, QObject *) const @@ -2419,17 +2419,15 @@ void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::G bool QQmlDelegateModelGroupPrivate::isChangedConnected() { Q_Q(QQmlDelegateModelGroup); - IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV4Handle &,const QQmlV4Handle &)); + IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QJSValue &,const QJSValue &)); } void QQmlDelegateModelGroupPrivate::emitChanges(QV4::ExecutionEngine *v4) { Q_Q(QQmlDelegateModelGroup); if (isChangedConnected() && !changeSet.isEmpty()) { - QV4::Scope scope(v4); - QV4::ScopedValue removed(scope, engineData(scope.engine)->array(v4, changeSet.removes())); - QV4::ScopedValue inserted(scope, engineData(scope.engine)->array(v4, changeSet.inserts())); - emit q->changed(QQmlV4Handle(removed), QQmlV4Handle(inserted)); + emit q->changed(QJSValue(v4, engineData(v4)->array(v4, changeSet.removes())), + QJSValue(v4, engineData(v4)->array(v4, changeSet.inserts()))); } if (changeSet.difference() != 0) emit q->countChanged(); @@ -2607,18 +2605,18 @@ void QQmlDelegateModelGroup::setDefaultInclude(bool include) \endlist */ -QQmlV4Handle QQmlDelegateModelGroup::get(int index) +QJSValue QQmlDelegateModelGroup::get(int index) { Q_D(QQmlDelegateModelGroup); if (!d->model) - return QQmlV4Handle(QV4::Encode::undefined()); + return QJSValue(); QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); if (!model->m_context || !model->m_context->isValid()) { - return QQmlV4Handle(QV4::Encode::undefined()); + return QJSValue(); } else if (index < 0 || index >= model->m_compositor.count(d->group)) { qmlWarning(this) << tr("get: index out of range"); - return QQmlV4Handle(QV4::Encode::undefined()); + return QJSValue(); } Compositor::iterator it = model->m_compositor.find(d->group, index); @@ -2630,7 +2628,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index) cacheItem = model->m_adaptorModel.createItem( model->m_cacheMetaType, it.modelIndex()); if (!cacheItem) - return QQmlV4Handle(QV4::Encode::undefined()); + return QJSValue(); cacheItem->groups = it->flags; model->m_cache.insert(it.cacheIndex, cacheItem); @@ -2646,7 +2644,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index) o->setPrototypeOf(p); ++cacheItem->scriptRef; - return QQmlV4Handle(o); + return QJSValue(v4, o->asReturnedValue()); } bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const @@ -3338,9 +3336,9 @@ QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item) return flags; } -QString QQmlPartsModel::stringValue(int index, const QString &role) +QVariant QQmlPartsModel::variantValue(int index, const QString &role) { - return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); + return QQmlDelegateModelPrivate::get(m_model)->variantValue(m_compositorGroup, index, role); } void QQmlPartsModel::setWatchedRoles(const QList<QByteArray> &roles) diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h index 0ad8939732..2684162514 100644 --- a/src/qml/types/qqmldelegatemodel_p.h +++ b/src/qml/types/qqmldelegatemodel_p.h @@ -59,7 +59,6 @@ #include <QtCore/qabstractitemmodel.h> #include <QtCore/qstringlist.h> -#include <private/qv8engine_p.h> #include <private/qqmlglobal_p.h> QT_REQUIRE_CONFIG(qml_delegate_model); @@ -114,7 +113,7 @@ public: QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; ReleaseFlags release(QObject *object) override; void cancel(int index) override; - QString stringValue(int index, const QString &role) override; + QVariant variantValue(int index, const QString &role) override; void setWatchedRoles(const QList<QByteArray> &roles) override; QQmlIncubator::Status incubationStatus(int index) override; @@ -179,7 +178,7 @@ public: bool defaultInclude() const; void setDefaultInclude(bool include); - Q_INVOKABLE QQmlV4Handle get(int index); + Q_INVOKABLE QJSValue get(int index); public Q_SLOTS: void insert(QQmlV4Function *); @@ -195,7 +194,7 @@ Q_SIGNALS: void countChanged(); void nameChanged(); void defaultIncludeChanged(); - void changed(const QQmlV4Handle &removed, const QQmlV4Handle &inserted); + void changed(const QJSValue &removed, const QJSValue &inserted); private: Q_DECLARE_PRIVATE(QQmlDelegateModelGroup) }; diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index 0028849828..7f10bbf370 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -276,7 +276,7 @@ public: void requestMoreIfNecessary(); QObject *object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode); QQmlDelegateModel::ReleaseFlags release(QObject *object); - QString stringValue(Compositor::Group group, int index, const QString &name); + QVariant variantValue(Compositor::Group group, int index, const QString &name); void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) { @@ -378,7 +378,7 @@ public: bool isValid() const override; QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; ReleaseFlags release(QObject *item) override; - QString stringValue(int index, const QString &role) override; + QVariant variantValue(int index, const QString &role) override; QList<QByteArray> watchedRoles() const { return m_watchedRoles; } void setWatchedRoles(const QList<QByteArray> &roles) override; QQmlIncubator::Status incubationStatus(int index) override; diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 565e60b3c1..5b5bcd8464 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -2545,7 +2545,7 @@ void QQmlListModel::append(QQmlV4Function *args) \sa append() */ -QQmlV4Handle QQmlListModel::get(int index) const +QJSValue QQmlListModel::get(int index) const { QV4::Scope scope(engine()); QV4::ScopedValue result(scope, QV4::Value::undefinedValue()); @@ -2568,7 +2568,7 @@ QQmlV4Handle QQmlListModel::get(int index) const } } - return QQmlV4Handle(result); + return QJSValue(engine(), result->asReturnedValue()); } /*! @@ -2587,10 +2587,10 @@ QQmlV4Handle QQmlListModel::get(int index) const \sa append() */ -void QQmlListModel::set(int index, const QQmlV4Handle &handle) +void QQmlListModel::set(int index, const QJSValue &value) { QV4::Scope scope(engine()); - QV4::ScopedObject object(scope, handle); + QV4::ScopedObject object(scope, QJSValuePrivate::getValue(&value)); if (!object) { qmlWarning(this) << tr("set: value is not an object"); diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index 95b797c898..471e33aa5a 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -100,8 +100,8 @@ public: Q_INVOKABLE void remove(QQmlV4Function *args); Q_INVOKABLE void append(QQmlV4Function *args); Q_INVOKABLE void insert(QQmlV4Function *args); - Q_INVOKABLE QQmlV4Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV4Handle &); + Q_INVOKABLE QJSValue get(int index) const; + Q_INVOKABLE void set(int index, const QJSValue &value); Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); Q_INVOKABLE void move(int from, int to, int count); Q_INVOKABLE void sync(); diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qml/types/qqmllistmodelworkeragent.cpp index fe3eaa3198..f7cb08dcf4 100644 --- a/src/qml/types/qqmllistmodelworkeragent.cpp +++ b/src/qml/types/qqmllistmodelworkeragent.cpp @@ -114,12 +114,12 @@ void QQmlListModelWorkerAgent::insert(QQmlV4Function *args) m_copy->insert(args); } -QQmlV4Handle QQmlListModelWorkerAgent::get(int index) const +QJSValue QQmlListModelWorkerAgent::get(int index) const { return m_copy->get(index); } -void QQmlListModelWorkerAgent::set(int index, const QQmlV4Handle &value) +void QQmlListModelWorkerAgent::set(int index, const QJSValue &value) { m_copy->set(index, value); } diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h index ae2d4b11e0..69d1785618 100644 --- a/src/qml/types/qqmllistmodelworkeragent_p.h +++ b/src/qml/types/qqmllistmodelworkeragent_p.h @@ -85,8 +85,8 @@ public: Q_INVOKABLE void remove(QQmlV4Function *args); Q_INVOKABLE void append(QQmlV4Function *args); Q_INVOKABLE void insert(QQmlV4Function *args); - Q_INVOKABLE QQmlV4Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV4Handle &); + Q_INVOKABLE QJSValue get(int index) const; + Q_INVOKABLE void set(int index, const QJSValue &value); Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); Q_INVOKABLE void move(int from, int to, int count); Q_INVOKABLE void sync(); diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp index bb72771cd9..840b435d18 100644 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ b/src/qml/types/qqmlmodelsmodule.cpp @@ -47,9 +47,44 @@ #include <private/qqmldelegatecomponent_p.h> #endif #include <private/qqmlobjectmodel_p.h> +#include <private/qqmltablemodel_p.h> +#include <private/qqmltablemodelcolumn_p.h> +#include <private/qqmlinstantiator_p.h> +#include <private/qquickpackage_p.h> QT_BEGIN_NAMESPACE +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + +void QQmlModelsModule::registerQmlTypes() +{ + // Don't add anything here. These are only for backwards compatibility. + qmlRegisterType<QQmlInstantiator>("QtQml", 2, 1, "Instantiator"); // Only available in >= 2.1 + qmlRegisterType<QQmlInstanceModel>(); +} + +void QQmlModelsModule::registerQuickTypes() +{ + // Don't add anything here. These are only for backwards compatibility. + + const char uri[] = "QtQuick"; + + qmlRegisterType<QQmlInstantiator>(uri, 2, 1, "Instantiator"); + qmlRegisterType<QQmlInstanceModel>(); +#if QT_CONFIG(qml_list_model) + qmlRegisterType<QQmlListElement>(uri, 2, 0, "ListElement"); + qmlRegisterCustomType<QQmlListModel>(uri, 2, 0, "ListModel", new QQmlListModelParser); +#endif + qmlRegisterType<QQuickPackage>(uri, 2, 0, "Package"); +#if QT_CONFIG(qml_delegate_model) + qmlRegisterType<QQmlDelegateModel>(uri, 2, 0, "VisualDataModel"); + qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 0, "VisualDataGroup"); +#endif + qmlRegisterType<QQmlObjectModel>(uri, 2, 0, "VisualItemModel"); +} + +#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void QQmlModelsModule::defineModule() { const char uri[] = "QtQml.Models"; @@ -66,6 +101,10 @@ void QQmlModelsModule::defineModule() qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel"); qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel"); + + qmlRegisterType<QQuickPackage>(uri, 2, 14, "Package"); + qmlRegisterType<QQmlInstantiator>(uri, 2, 14, "Instantiator"); + qmlRegisterType<QQmlInstanceModel>(); } void QQmlModelsModule::defineLabsModule() @@ -77,6 +116,8 @@ void QQmlModelsModule::defineLabsModule() qmlRegisterType<QQmlDelegateChooser>(uri, 1, 0, "DelegateChooser"); qmlRegisterType<QQmlDelegateChoice>(uri, 1, 0, "DelegateChoice"); #endif + qmlRegisterType<QQmlTableModel>(uri, 1, 0, "TableModel"); + qmlRegisterType<QQmlTableModelColumn>(uri, 1, 0, "TableModelColumn"); } QT_END_NAMESPACE diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h index 939ecc1500..2bb04f1e11 100644 --- a/src/qml/types/qqmlmodelsmodule_p.h +++ b/src/qml/types/qqmlmodelsmodule_p.h @@ -58,6 +58,11 @@ QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QQmlModelsModule { public: +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + static void registerQmlTypes(); + static void registerQuickTypes(); +#endif + static void defineModule(); static void defineLabsModule(); }; diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp index 2f4d427430..b6330b4295 100644 --- a/src/qml/types/qqmlobjectmodel.cpp +++ b/src/qml/types/qqmlobjectmodel.cpp @@ -273,12 +273,12 @@ QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item) return nullptr; } -QString QQmlObjectModel::stringValue(int index, const QString &name) +QVariant QQmlObjectModel::variantValue(int index, const QString &role) { Q_D(QQmlObjectModel); if (index < 0 || index >= d->children.count()) return QString(); - return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); + return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(role); } QQmlIncubator::Status QQmlObjectModel::incubationStatus(int) diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h index 4ac4f1c65b..1284ba1780 100644 --- a/src/qml/types/qqmlobjectmodel_p.h +++ b/src/qml/types/qqmlobjectmodel_p.h @@ -79,7 +79,8 @@ public: virtual QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) = 0; virtual ReleaseFlags release(QObject *object) = 0; virtual void cancel(int) {} - virtual QString stringValue(int, const QString &) = 0; + QString stringValue(int index, const QString &role) { return variantValue(index, role).toString(); } + virtual QVariant variantValue(int, const QString &) = 0; virtual void setWatchedRoles(const QList<QByteArray> &roles) = 0; virtual QQmlIncubator::Status incubationStatus(int index) = 0; @@ -119,7 +120,7 @@ public: bool isValid() const override; QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; ReleaseFlags release(QObject *object) override; - QString stringValue(int index, const QString &role) override; + QVariant variantValue(int index, const QString &role) override; void setWatchedRoles(const QList<QByteArray> &) override {} QQmlIncubator::Status incubationStatus(int index) override; diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h index 3dd5c4e4ce..39ec66d136 100644 --- a/src/qml/types/qqmltableinstancemodel_p.h +++ b/src/qml/types/qqmltableinstancemodel_p.h @@ -122,7 +122,7 @@ public: QQmlIncubator::Status incubationStatus(int index) override; - QString stringValue(int, const QString &) override { Q_UNREACHABLE(); return QString(); } + QVariant variantValue(int, const QString &) override { Q_UNREACHABLE(); return QVariant(); } void setWatchedRoles(const QList<QByteArray> &) override { Q_UNREACHABLE(); } int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; } diff --git a/src/qml/types/qqmltablemodel.cpp b/src/qml/types/qqmltablemodel.cpp new file mode 100644 index 0000000000..4a96e7a46b --- /dev/null +++ b/src/qml/types/qqmltablemodel.cpp @@ -0,0 +1,1059 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltablemodel_p.h" + +#include <QtCore/qloggingcategory.h> +#include <QtQml/qqmlinfo.h> +#include <QtQml/qqmlengine.h> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcTableModel, "qt.qml.tablemodel") + +/*! + \qmltype TableModel + \instantiates QQmlTableModel + \inqmlmodule Qt.labs.qmlmodels + \brief Encapsulates a simple table model. + \since 5.14 + + The TableModel type stores JavaScript/JSON objects as data for a table + model that can be used with \l TableView. It is intended to support + very simple models without requiring the creation of a custom + QAbstractTableModel subclass in C++. + + \snippet qml/tablemodel/fruit-example-simpledelegate.qml file + + The model's initial row data is set with either the \l rows property or by + calling \l appendRow(). Each column in the model is specified by declaring + a \l TableModelColumn instance, where the order of each instance determines + its column index. Once the model's \l Component.completed() signal has been + emitted, the columns and roles will have been established and are then + fixed for the lifetime of the model. + + To access a specific row, the \l getRow() function can be used. + It's also possible to access the model's JavaScript data + directly via the \l rows property, but it is not possible to + modify the model data this way. + + To add new rows, use \l appendRow() and \l insertRow(). To modify + existing rows, use \l setRow(), \l moveRow(), \l removeRow(), and + \l clear(). + + It is also possible to modify the model's data via the delegate, + as shown in the example above: + + \snippet qml/tablemodel/fruit-example-simpledelegate.qml delegate + + If the type of the data at the modified role does not match the type of the + data that is set, it will be automatically converted via + \l {QVariant::canConvert()}{QVariant}. + + \section1 Supported Row Data Structures + + TableModel is designed to work with JavaScript/JSON data, where each row + is a simple key-pair object: + + \code + { + // Each property is one cell/column. + checked: false, + amount: 1, + fruitType: "Apple", + fruitName: "Granny Smith", + fruitPrice: 1.50 + }, + // ... + \endcode + + As model manipulation in Qt is done via row and column indices, + and because object keys are unordered, each column must be specified via + TableModelColumn. This allows mapping Qt's built-in roles to any property + in each row object. + + Complex row structures are supported, but with limited functionality. + As TableModel has no way of knowing how each row is structured, + it cannot manipulate it. As a consequence of this, the copy of the + model data that TableModel has stored in \l rows is not kept in sync + with the source data that was set in QML. For these reasons, TableModel + relies on the user to handle simple data manipulation. + + For example, suppose you wanted to have several roles per column. One way + of doing this is to use a data source where each row is an array and each + cell is an object. To use this data source with TableModel, define a + getter and setter: + + \code + TableModel { + TableModelColumn { + display: function(modelIndex) { return rows[modelIndex.row][0].checked } + setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][0].checked = cellData } + } + // ... + + rows: [ + [ + { checked: false, checkable: true }, + { amount: 1 }, + { fruitType: "Apple" }, + { fruitName: "Granny Smith" }, + { fruitPrice: 1.50 } + ] + // ... + ] + } + \endcode + + The row above is one example of a complex row. + + \note Row manipulation functions such as \l appendRow(), \l removeRow(), + etc. are not supported when using complex rows. + + \section1 Using DelegateChooser with TableModel + + For most real world use cases, it is recommended to use DelegateChooser + as the delegate of a TableView that uses TableModel. This allows you to + use specific roles in the relevant delegates. For example, the snippet + above can be rewritten to use DelegateChooser like so: + + \snippet qml/tablemodel/fruit-example-delegatechooser.qml file + + The most specific delegates are declared first: the columns at index \c 0 + and \c 1 have \c bool and \c integer data types, so they use a + \l [QtQuickControls2]{CheckBox} and \l [QtQuickControls2]{SpinBox}, + respectively. The remaining columns can simply use a + \l [QtQuickControls2]{TextField}, and so that delegate is declared + last as a fallback. + + \sa TableModelColumn, TableView, QAbstractTableModel +*/ + +QQmlTableModel::QQmlTableModel(QObject *parent) + : QAbstractTableModel(parent) +{ +} + +QQmlTableModel::~QQmlTableModel() +{ +} + +/*! + \qmlproperty object TableModel::rows + + This property holds the model data in the form of an array of rows: + + \snippet qml/tablemodel/fruit-example-simpledelegate.qml rows + + \sa getRow(), setRow(), moveRow(), appendRow(), insertRow(), clear(), rowCount, columnCount +*/ +QVariant QQmlTableModel::rows() const +{ + return mRows; +} + +void QQmlTableModel::setRows(const QVariant &rows) +{ + if (rows.userType() != qMetaTypeId<QJSValue>()) { + qmlWarning(this) << "setRows(): \"rows\" must be an array; actual type is " << rows.typeName(); + return; + } + + const QJSValue rowsAsJSValue = rows.value<QJSValue>(); + const QVariantList rowsAsVariantList = rowsAsJSValue.toVariant().toList(); + if (rowsAsVariantList == mRows) { + // No change. + return; + } + + if (!componentCompleted) { + // Store the rows until we can call doSetRows() after component completion. + mRows = rowsAsVariantList; + return; + } + + doSetRows(rowsAsVariantList); +} + +void QQmlTableModel::doSetRows(const QVariantList &rowsAsVariantList) +{ + Q_ASSERT(componentCompleted); + + // By now, all TableModelColumns should have been set. + if (mColumns.isEmpty()) { + qmlWarning(this) << "No TableModelColumns were set; model will be empty"; + return; + } + + const bool firstTimeValidRowsHaveBeenSet = mColumnMetadata.isEmpty(); + if (!firstTimeValidRowsHaveBeenSet) { + // This is not the first time rows have been set; validate each one. + for (int rowIndex = 0; rowIndex < rowsAsVariantList.size(); ++rowIndex) { + // validateNewRow() expects a QVariant wrapping a QJSValue, so to + // simplify the code, just create one here. + const QVariant row = QVariant::fromValue(rowsAsVariantList.at(rowIndex)); + if (!validateNewRow("setRows()", row, rowIndex, SetRowsOperation)) + return; + } + } + + const int oldRowCount = mRowCount; + const int oldColumnCount = mColumnCount; + + beginResetModel(); + + // We don't clear the column or role data, because a TableModel should not be reused in that way. + // Once it has valid data, its columns and roles are fixed. + mRows = rowsAsVariantList; + mRowCount = mRows.size(); + + // Gather metadata the first time rows is set. + if (firstTimeValidRowsHaveBeenSet && !mRows.isEmpty()) + fetchColumnMetadata(); + + endResetModel(); + + emit rowsChanged(); + + if (mRowCount != oldRowCount) + emit rowCountChanged(); + if (mColumnCount != oldColumnCount) + emit columnCountChanged(); +} + +QQmlTableModel::ColumnRoleMetadata QQmlTableModel::fetchColumnRoleData(const QString &roleNameKey, + QQmlTableModelColumn *tableModelColumn, int columnIndex) const +{ + const QVariant firstRow = mRows.first(); + ColumnRoleMetadata roleData; + + QJSValue columnRoleGetter = tableModelColumn->getterAtRole(roleNameKey); + if (columnRoleGetter.isUndefined()) { + // This role is not defined, which is fine; just skip it. + return roleData; + } + + if (columnRoleGetter.isString()) { + // The role is set as a string, so we assume the row is a simple object. + if (firstRow.type() != QVariant::Map) { + qmlWarning(this).quote() << "expected row for role " + << roleNameKey << " of TableModelColumn at index " + << columnIndex << " to be a simple object, but it's " + << firstRow.typeName() << " instead: " << firstRow; + return roleData; + } + const QVariantMap firstRowAsMap = firstRow.toMap(); + const QString rolePropertyName = columnRoleGetter.toString(); + const QVariant roleProperty = firstRowAsMap.value(rolePropertyName); + + roleData.isStringRole = true; + roleData.name = rolePropertyName; + roleData.type = roleProperty.type(); + roleData.typeName = QString::fromLatin1(roleProperty.typeName()); + } else if (columnRoleGetter.isCallable()) { + // The role is provided via a function, which means the row is complex and + // the user needs to provide the data for it. + const auto modelIndex = index(0, columnIndex); + const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(modelIndex); + const QVariant cellData = columnRoleGetter.call(args).toVariant(); + + // We don't know the property name since it's provided through the function. + // roleData.name = ??? + roleData.isStringRole = false; + roleData.type = cellData.type(); + roleData.typeName = QString::fromLatin1(cellData.typeName()); + } else { + // Invalid role. + qmlWarning(this) << "TableModelColumn role for column at index " + << columnIndex << " must be either a string or a function; actual type is: " + << columnRoleGetter.toString(); + } + + return roleData; +} + +void QQmlTableModel::fetchColumnMetadata() +{ + qCDebug(lcTableModel) << "gathering metadata for" << mColumnCount << "columns from first row:"; + + static const auto supportedRoleNames = QQmlTableModelColumn::supportedRoleNames(); + + // Since we support different data structures at the row level, we require that there + // is a TableModelColumn for each column. + // Collect and cache metadata for each column. This makes data lookup faster. + for (int columnIndex = 0; columnIndex < mColumns.size(); ++columnIndex) { + QQmlTableModelColumn *column = mColumns.at(columnIndex); + qCDebug(lcTableModel).nospace() << "- column " << columnIndex << ":"; + + ColumnMetadata metaData; + const auto builtInRoleKeys = supportedRoleNames.keys(); + for (const int builtInRoleKey : builtInRoleKeys) { + const QString builtInRoleName = supportedRoleNames.value(builtInRoleKey); + ColumnRoleMetadata roleData = fetchColumnRoleData(builtInRoleName, column, columnIndex); + if (roleData.type == QVariant::Invalid) { + // This built-in role was not specified in this column. + continue; + } + + qCDebug(lcTableModel).nospace() << " - added metadata for built-in role " + << builtInRoleName << " at column index " << columnIndex + << ": name=" << roleData.name << " typeName=" << roleData.typeName + << " type=" << roleData.type; + + // This column now supports this specific built-in role. + metaData.roles.insert(builtInRoleName, roleData); + // Add it if it doesn't already exist. + mRoleNames[builtInRoleKey] = builtInRoleName.toLatin1(); + } + mColumnMetadata.insert(columnIndex, metaData); + } +} + +/*! + \qmlmethod TableModel::appendRow(object row) + + Adds a new row to the end of the model, with the + values (cells) in \a row. + + \code + model.appendRow({ + checkable: true, + amount: 1, + fruitType: "Pear", + fruitName: "Williams", + fruitPrice: 1.50, + }) + \endcode + + \sa insertRow(), setRow(), removeRow() +*/ +void QQmlTableModel::appendRow(const QVariant &row) +{ + if (!validateNewRow("appendRow()", row, -1, AppendOperation)) + return; + + doInsert(mRowCount, row); +} + +/*! + \qmlmethod TableModel::clear() + + Removes all rows from the model. + + \sa removeRow() +*/ +void QQmlTableModel::clear() +{ + QQmlEngine *engine = qmlEngine(this); + Q_ASSERT(engine); + setRows(QVariant::fromValue(engine->newArray())); +} + +/*! + \qmlmethod object TableModel::getRow(int rowIndex) + + Returns the row at \a rowIndex in the model. + + Note that this equivalent to accessing the row directly + through the \l rows property: + + \code + Component.onCompleted: { + // These two lines are equivalent. + console.log(model.getRow(0).display); + console.log(model.rows[0].fruitName); + } + \endcode + + \note the returned object cannot be used to modify the contents of the + model; use setRow() instead. + + \sa setRow(), appendRow(), insertRow(), removeRow(), moveRow() +*/ +QVariant QQmlTableModel::getRow(int rowIndex) +{ + if (!validateRowIndex("getRow()", "rowIndex", rowIndex)) + return QVariant(); + + return mRows.at(rowIndex); +} + +/*! + \qmlmethod TableModel::insertRow(int rowIndex, object row) + + Adds a new row to the list model at position \a rowIndex, with the + values (cells) in \a row. + + \code + model.insertRow(2, { + checkable: true, checked: false, + amount: 1, + fruitType: "Pear", + fruitName: "Williams", + fruitPrice: 1.50, + }) + \endcode + + The \a rowIndex must be to an existing item in the list, or one past + the end of the list (equivalent to \l appendRow()). + + \sa appendRow(), setRow(), removeRow(), rowCount +*/ +void QQmlTableModel::insertRow(int rowIndex, const QVariant &row) +{ + if (!validateNewRow("insertRow()", row, rowIndex)) + return; + + doInsert(rowIndex, row); +} + +void QQmlTableModel::doInsert(int rowIndex, const QVariant &row) +{ + beginInsertRows(QModelIndex(), rowIndex, rowIndex); + + // Adding rowAsVariant.toList() will add each invidual variant in the list, + // which is definitely not what we want. + const QVariant rowAsVariant = row.value<QJSValue>().toVariant(); + mRows.insert(rowIndex, rowAsVariant); + ++mRowCount; + + qCDebug(lcTableModel).nospace() << "inserted the following row to the model at index " + << rowIndex << ":\n" << rowAsVariant.toMap(); + + // Gather metadata the first time a row is added. + if (mColumnMetadata.isEmpty()) + fetchColumnMetadata(); + + endInsertRows(); + emit rowCountChanged(); +} + +void QQmlTableModel::classBegin() +{ +} + +void QQmlTableModel::componentComplete() +{ + componentCompleted = true; + + mColumnCount = mColumns.size(); + if (mColumnCount > 0) + emit columnCountChanged(); + + doSetRows(mRows); +} + +/*! + \qmlmethod TableModel::moveRow(int fromRowIndex, int toRowIndex, int rows) + + Moves \a rows from the index at \a fromRowIndex to the index at + \a toRowIndex. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the list: + + \code + model.moveRow(0, model.rowCount - 3, 3) + \endcode + + \sa appendRow(), insertRow(), removeRow(), rowCount +*/ +void QQmlTableModel::moveRow(int fromRowIndex, int toRowIndex, int rows) +{ + if (fromRowIndex == toRowIndex) { + qmlWarning(this) << "moveRow(): \"fromRowIndex\" cannot be equal to \"toRowIndex\""; + return; + } + + if (rows <= 0) { + qmlWarning(this) << "moveRow(): \"rows\" is less than or equal to 0"; + return; + } + + if (!validateRowIndex("moveRow()", "fromRowIndex", fromRowIndex)) + return; + + if (!validateRowIndex("moveRow()", "toRowIndex", toRowIndex)) + return; + + if (fromRowIndex + rows > mRowCount) { + qmlWarning(this) << "moveRow(): \"fromRowIndex\" (" << fromRowIndex + << ") + \"rows\" (" << rows << ") = " << (fromRowIndex + rows) + << ", which is greater than rowCount() of " << mRowCount; + return; + } + + if (toRowIndex + rows > mRowCount) { + qmlWarning(this) << "moveRow(): \"toRowIndex\" (" << toRowIndex + << ") + \"rows\" (" << rows << ") = " << (toRowIndex + rows) + << ", which is greater than rowCount() of " << mRowCount; + return; + } + + qCDebug(lcTableModel).nospace() << "moving " << rows + << " row(s) from index " << fromRowIndex + << " to index " << toRowIndex; + + // Based on the same call in QQmlListModel::moveRow(). + beginMoveRows(QModelIndex(), fromRowIndex, fromRowIndex + rows - 1, QModelIndex(), + toRowIndex > fromRowIndex ? toRowIndex + rows : toRowIndex); + + // Based on ListModel::moveRow(). + if (fromRowIndex > toRowIndex) { + // Only move forwards - flip if moving backwards. + const int from = fromRowIndex; + const int to = toRowIndex; + fromRowIndex = to; + toRowIndex = to + rows; + rows = from - to; + } + + QVector<QVariant> store; + store.reserve(rows); + for (int i = 0; i < (toRowIndex - fromRowIndex); ++i) + store.append(mRows.at(fromRowIndex + rows + i)); + for (int i = 0; i < rows; ++i) + store.append(mRows.at(fromRowIndex + i)); + for (int i = 0; i < store.size(); ++i) + mRows[fromRowIndex + i] = store[i]; + + qCDebug(lcTableModel).nospace() << "after moving, rows are:\n" << mRows; + + endMoveRows(); +} + +/*! + \qmlmethod TableModel::removeRow(int rowIndex, int rows = 1) + + Removes the row at \a rowIndex from the model. + + \sa clear(), rowCount +*/ +void QQmlTableModel::removeRow(int rowIndex, int rows) +{ + if (!validateRowIndex("removeRow()", "rowIndex", rowIndex)) + return; + + if (rows <= 0) { + qmlWarning(this) << "removeRow(): \"rows\" is less than or equal to zero"; + return; + } + + if (rowIndex + rows - 1 >= mRowCount) { + qmlWarning(this) << "removeRow(): \"rows\" " << rows + << " exceeds available rowCount() of " << mRowCount + << " when removing from \"rowIndex\" " << rowIndex; + return; + } + + beginRemoveRows(QModelIndex(), rowIndex, rowIndex + rows - 1); + + auto firstIterator = mRows.begin() + rowIndex; + // The "last" argument to erase() is exclusive, so we go one past the last item. + auto lastIterator = firstIterator + rows; + mRows.erase(firstIterator, lastIterator); + mRowCount -= rows; + + endRemoveRows(); + emit rowCountChanged(); + + qCDebug(lcTableModel).nospace() << "removed " << rows + << " items from the model, starting at index " << rowIndex; +} + +/*! + \qmlmethod TableModel::setRow(int rowIndex, object row) + + Changes the row at \a rowIndex in the model with \a row. + + All columns/cells must be present in \c row, and in the correct order. + + \code + model.setRow(0, { + checkable: true, + amount: 1, + fruitType: "Pear", + fruitName: "Williams", + fruitPrice: 1.50, + }) + \endcode + + If \a rowIndex is equal to \c rowCount(), then a new row is appended to the + model. Otherwise, \a rowIndex must point to an existing row in the model. + + \sa appendRow(), insertRow(), rowCount +*/ +void QQmlTableModel::setRow(int rowIndex, const QVariant &row) +{ + if (!validateNewRow("setRow()", row, rowIndex)) + return; + + if (rowIndex != mRowCount) { + // Setting an existing row. + mRows[rowIndex] = row; + + // For now we just assume the whole row changed, as it's simpler. + const QModelIndex topLeftModelIndex(createIndex(rowIndex, 0)); + const QModelIndex bottomRightModelIndex(createIndex(rowIndex, mColumnCount - 1)); + emit dataChanged(topLeftModelIndex, bottomRightModelIndex); + } else { + // Appending a row. + doInsert(rowIndex, row); + } +} + +QQmlListProperty<QQmlTableModelColumn> QQmlTableModel::columns() +{ + return QQmlListProperty<QQmlTableModelColumn>(this, nullptr, + &QQmlTableModel::columns_append, + &QQmlTableModel::columns_count, + &QQmlTableModel::columns_at, + &QQmlTableModel::columns_clear); +} + +void QQmlTableModel::columns_append(QQmlListProperty<QQmlTableModelColumn> *property, + QQmlTableModelColumn *value) +{ + QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object); + QQmlTableModelColumn *column = qobject_cast<QQmlTableModelColumn*>(value); + if (column) + model->mColumns.append(column); +} + +int QQmlTableModel::columns_count(QQmlListProperty<QQmlTableModelColumn> *property) +{ + const QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object); + return model->mColumns.count(); +} + +QQmlTableModelColumn *QQmlTableModel::columns_at(QQmlListProperty<QQmlTableModelColumn> *property, int index) +{ + const QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object); + return model->mColumns.at(index); +} + +void QQmlTableModel::columns_clear(QQmlListProperty<QQmlTableModelColumn> *property) +{ + QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object); + return model->mColumns.clear(); +} + +/*! + \qmlmethod QModelIndex TableModel::index(int row, int column) + + Returns a \l QModelIndex object referencing the given \a row and \a column, + which can be passed to the data() function to get the data from that cell, + or to setData() to edit the contents of that cell. + + \code + import QtQml 2.14 + import Qt.labs.qmlmodels 1.0 + + TableModel { + id: model + + TableModelColumn { display: "fruitType" } + TableModelColumn { display: "fruitPrice" } + + rows: [ + { fruitType: "Apple", fruitPrice: 1.50 }, + { fruitType: "Orange", fruitPrice: 2.50 } + ] + + Component.onCompleted: { + for (var r = 0; r < model.rowCount; ++r) { + console.log("An " + model.data(model.index(r, 0)).display + + " costs " + model.data(model.index(r, 1)).display.toFixed(2)) + } + } + } + \endcode + + \sa {QModelIndex and related Classes in QML}, data() +*/ +// Note: we don't document the parent argument, because you never need it, because +// cells in a TableModel don't have parents. But it is there because this function is an override. +QModelIndex QQmlTableModel::index(int row, int column, const QModelIndex &parent) const +{ + return row >= 0 && row < rowCount() && column >= 0 && column < columnCount() && !parent.isValid() + ? createIndex(row, column) + : QModelIndex(); +} + +/*! + \qmlproperty int TableModel::rowCount + \readonly + + This read-only property holds the number of rows in the model. + + This value changes whenever rows are added or removed from the model. +*/ +int QQmlTableModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return mRowCount; +} + +/*! + \qmlproperty int TableModel::columnCount + \readonly + + This read-only property holds the number of columns in the model. + + The number of columns is fixed for the lifetime of the model + after the \l rows property is set or \l appendRow() is called for the first + time. +*/ +int QQmlTableModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return mColumnCount; +} + +/*! + \qmlmethod variant TableModel::data(QModelIndex index, string role) + + Returns the data from the table cell at the given \a index belonging to the + given \a role. + + \sa index() +*/ +QVariant QQmlTableModel::data(const QModelIndex &index, const QString &role) const +{ + const int iRole = mRoleNames.key(role.toUtf8(), -1); + if (iRole >= 0) + return data(index, iRole); + return QVariant(); +} + +QVariant QQmlTableModel::data(const QModelIndex &index, int role) const +{ + const int row = index.row(); + if (row < 0 || row >= rowCount()) + return QVariant(); + + const int column = index.column(); + if (column < 0 || column >= columnCount()) + return QVariant(); + + const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column()); + const QString roleName = QString::fromUtf8(mRoleNames.value(role)); + if (!columnMetadata.roles.contains(roleName)) { + qmlWarning(this) << "setData(): no role named " << roleName + << " at column index " << column << ". The available roles for that column are: " + << columnMetadata.roles.keys(); + return QVariant(); + } + + const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName); + if (roleData.isStringRole) { + // We know the data structure, so we can get the data for the user. + const QVariantMap rowData = mRows.at(row).toMap(); + const QString propertyName = columnMetadata.roles.value(roleName).name; + const QVariant value = rowData.value(propertyName); + return value; + } + + // We don't know the data structure, so the user has to modify their data themselves. + // First, find the getter for this column and role. + QJSValue getter = mColumns.at(column)->getterAtRole(roleName); + + // Then, call it and return what it returned. + const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(index); + return getter.call(args).toVariant(); +} + +/*! + \qmlmethod bool TableModel::setData(QModelIndex index, string role, variant value) + + Inserts or updates the data field named by \a role in the table cell at the + given \a index with \a value. Returns true if sucessful, false if not. + + \sa index() +*/ +bool QQmlTableModel::setData(const QModelIndex &index, const QString &role, const QVariant &value) +{ + const int intRole = mRoleNames.key(role.toUtf8(), -1); + if (intRole >= 0) + return setData(index, value, intRole); + return false; +} + +bool QQmlTableModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + const int row = index.row(); + if (row < 0 || row >= rowCount()) + return false; + + const int column = index.column(); + if (column < 0 || column >= columnCount()) + return false; + + const QString roleName = QString::fromUtf8(mRoleNames.value(role)); + + qCDebug(lcTableModel).nospace() << "setData() called with index " + << index << ", value " << value << " and role " << roleName; + + // Verify that the role exists for this column. + const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column()); + if (!columnMetadata.roles.contains(roleName)) { + qmlWarning(this) << "setData(): no role named \"" << roleName + << "\" at column index " << column << ". The available roles for that column are: " + << columnMetadata.roles.keys(); + return false; + } + + // Verify that the type of the value is what we expect. + // If the value set is not of the expected type, we can try to convert it automatically. + const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName); + QVariant effectiveValue = value; + if (value.type() != roleData.type) { + if (!value.canConvert(int(roleData.type))) { + qmlWarning(this).nospace() << "setData(): the value " << value + << " set at row " << row << " column " << column << " with role " << roleName + << " cannot be converted to " << roleData.typeName; + return false; + } + + if (!effectiveValue.convert(int(roleData.type))) { + qmlWarning(this).nospace() << "setData(): failed converting value " << value + << " set at row " << row << " column " << column << " with role " << roleName + << " to " << roleData.typeName; + return false; + } + } + + if (roleData.isStringRole) { + // We know the data structure, so we can set it for the user. + QVariantMap modifiedRow = mRows.at(row).toMap(); + modifiedRow[roleData.name] = value; + + mRows[row] = modifiedRow; + } else { + // We don't know the data structure, so the user has to modify their data themselves. + auto engine = qmlEngine(this); + auto args = QJSValueList() + // arg 0: modelIndex. + << engine->toScriptValue(index) + // arg 1: cellData. + << engine->toScriptValue(value); + // Do the actual setting. + QJSValue setter = mColumns.at(column)->setterAtRole(roleName); + setter.call(args); + + /* + The chain of events so far: + + - User did e.g.: model.edit = textInput.text + - setData() is called + - setData() calls the setter + (remember that we need to emit the dataChanged() signal, + which is why the user can't just set the data directly in the delegate) + + Now the user's setter function has modified *their* copy of the + data, but *our* copy of the data is old. Imagine the getters and setters looked like this: + + display: function(modelIndex) { return rows[modelIndex.row][1].amount } + setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][1].amount = cellData } + + We don't know the structure of the user's data, so we can't just do + what we do above for the isStringRole case: + + modifiedRow[column][roleName] = value + + This means that, besides getting the implicit row count when rows is initially set, + our copy of the data is unused when it comes to complex columns. + + Another point to note is that we can't pass rowData in to the getter as a convenience, + because we would be passing in *our* copy of the row, which is not up-to-date. + Since the user already has access to the data, it's not a big deal for them to do: + + display: function(modelIndex) { return rows[modelIndex.row][1].amount } + + instead of: + + display: function(modelIndex, rowData) { return rowData[1].amount } + */ + } + + QVector<int> rolesChanged; + rolesChanged.append(role); + emit dataChanged(index, index, rolesChanged); + + return true; +} + +QHash<int, QByteArray> QQmlTableModel::roleNames() const +{ + return mRoleNames; +} + +QQmlTableModel::ColumnRoleMetadata::ColumnRoleMetadata() +{ +} + +QQmlTableModel::ColumnRoleMetadata::ColumnRoleMetadata( + bool isStringRole, const QString &name, QVariant::Type type, const QString &typeName) : + isStringRole(isStringRole), + name(name), + type(type), + typeName(typeName) +{ +} + +bool QQmlTableModel::ColumnRoleMetadata::isValid() const +{ + return !name.isEmpty(); +} + +bool QQmlTableModel::validateRowType(const char *functionName, const QVariant &row) const +{ + if (!row.canConvert<QJSValue>()) { + qmlWarning(this) << functionName << ": expected \"row\" argument to be a QJSValue," + << " but got " << row.typeName() << " instead:\n" << row; + return false; + } + + const QJSValue rowAsJSValue = row.value<QJSValue>(); + if (!rowAsJSValue.isObject() && !rowAsJSValue.isArray()) { + qmlWarning(this) << functionName << ": expected \"row\" argument " + << "to be an object or array, but got:\n" << rowAsJSValue.toString(); + return false; + } + + return true; +} + +bool QQmlTableModel::validateNewRow(const char *functionName, const QVariant &row, + int rowIndex, NewRowOperationFlag operation) const +{ + if (mColumnMetadata.isEmpty()) { + // There is no column metadata, so we have nothing to validate the row against. + // Rows have to be added before we can gather metadata from them, so just this + // once we'll return true to allow the rows to be added. + return true; + } + + // Don't require each row to be a QJSValue when setting all rows, + // as they won't be; they'll be QVariantMap. + if (operation != SetRowsOperation && !validateRowType(functionName, row)) + return false; + + if (operation == OtherOperation) { + // Inserting/setting. + if (rowIndex < 0) { + qmlWarning(this) << functionName << ": \"rowIndex\" cannot be negative"; + return false; + } + + if (rowIndex > mRowCount) { + qmlWarning(this) << functionName << ": \"rowIndex\" " << rowIndex + << " is greater than rowCount() of " << mRowCount; + return false; + } + } + + const QVariant rowAsVariant = operation == SetRowsOperation + ? row : row.value<QJSValue>().toVariant(); + if (rowAsVariant.type() != QVariant::Map) { + qmlWarning(this) << functionName << ": row manipulation functions " + << "do not support complex rows (row index: " << rowIndex << ")"; + return false; + } + + const QVariantMap rowAsMap = rowAsVariant.toMap(); + const int columnCount = rowAsMap.size(); + if (columnCount < mColumnCount) { + qmlWarning(this) << functionName << ": expected " << mColumnCount + << " columns, but only got " << columnCount; + return false; + } + + // We can't validate complex structures, but we can make sure that + // each simple string-based role in each column is correct. + for (int columnIndex = 0; columnIndex < mColumns.size(); ++columnIndex) { + QQmlTableModelColumn *column = mColumns.at(columnIndex); + const QHash<QString, QJSValue> getters = column->getters(); + const auto roleNames = getters.keys(); + const ColumnMetadata columnMetadata = mColumnMetadata.at(columnIndex); + for (const QString &roleName : roleNames) { + const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName); + if (!roleData.isStringRole) + continue; + + if (!rowAsMap.contains(roleData.name)) { + qmlWarning(this).quote() << functionName << ": expected a property named " + << roleData.name << " in row at index " << rowIndex << ", but couldn't find one"; + return false; + } + + const QVariant rolePropertyValue = rowAsMap.value(roleData.name); + if (rolePropertyValue.type() != roleData.type) { + qmlWarning(this).quote() << functionName << ": expected the property named " + << roleData.name << " to be of type " << roleData.typeName + << ", but got " << QString::fromLatin1(rolePropertyValue.typeName()) << " instead"; + return false; + } + } + } + + return true; +} + +bool QQmlTableModel::validateRowIndex(const char *functionName, const char *argumentName, int rowIndex) const +{ + if (rowIndex < 0) { + qmlWarning(this) << functionName << ": \"" << argumentName << "\" cannot be negative"; + return false; + } + + if (rowIndex >= mRowCount) { + qmlWarning(this) << functionName << ": \"" << argumentName + << "\" " << rowIndex << " is greater than or equal to rowCount() of " << mRowCount; + return false; + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmltablemodel_p.h b/src/qml/types/qqmltablemodel_p.h new file mode 100644 index 0000000000..a1bb97e7d4 --- /dev/null +++ b/src/qml/types/qqmltablemodel_p.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTABLEMODEL_P_H +#define QQMLTABLEMODEL_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 <QtCore/QObject> +#include <QtCore/QAbstractTableModel> +#include <QtQml/qqml.h> +#include <QtQml/private/qtqmlglobal_p.h> +#include <QtQml/private/qqmltablemodelcolumn_p.h> +#include <QtQml/QJSValue> +#include <QtQml/QQmlListProperty> + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QQmlTableModel : public QAbstractTableModel, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(int columnCount READ columnCount NOTIFY columnCountChanged FINAL) + Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged FINAL) + Q_PROPERTY(QVariant rows READ rows WRITE setRows NOTIFY rowsChanged FINAL) + Q_PROPERTY(QQmlListProperty<QQmlTableModelColumn> columns READ columns CONSTANT FINAL) + Q_INTERFACES(QQmlParserStatus) + Q_CLASSINFO("DefaultProperty", "columns") + +public: + QQmlTableModel(QObject *parent = nullptr); + ~QQmlTableModel() override; + + QVariant rows() const; + void setRows(const QVariant &rows); + + Q_INVOKABLE void appendRow(const QVariant &row); + Q_INVOKABLE void clear(); + Q_INVOKABLE QVariant getRow(int rowIndex); + Q_INVOKABLE void insertRow(int rowIndex, const QVariant &row); + Q_INVOKABLE void moveRow(int fromRowIndex, int toRowIndex, int rows = 1); + Q_INVOKABLE void removeRow(int rowIndex, int rows = 1); + Q_INVOKABLE void setRow(int rowIndex, const QVariant &row); + + QQmlListProperty<QQmlTableModelColumn> columns(); + + static void columns_append(QQmlListProperty<QQmlTableModelColumn> *property, QQmlTableModelColumn *value); + static int columns_count(QQmlListProperty<QQmlTableModelColumn> *property); + static QQmlTableModelColumn *columns_at(QQmlListProperty<QQmlTableModelColumn> *property, int index); + static void columns_clear(QQmlListProperty<QQmlTableModelColumn> *property); + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + Q_INVOKABLE QVariant data(const QModelIndex &index, const QString &role) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + Q_INVOKABLE bool setData(const QModelIndex &index, const QString &role, const QVariant &value); + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override; + QHash<int, QByteArray> roleNames() const override; + +Q_SIGNALS: + void columnCountChanged(); + void rowCountChanged(); + void rowsChanged(); + +private: + class ColumnRoleMetadata + { + public: + ColumnRoleMetadata(); + ColumnRoleMetadata(bool isStringRole, const QString &name, QVariant::Type type, const QString &typeName); + + bool isValid() const; + + // If this is false, it's a function role. + bool isStringRole = false; + QString name; + QVariant::Type type = QVariant::Invalid; + QString typeName; + }; + + struct ColumnMetadata + { + // Key = role name that will be made visible to the delegate + // Value = metadata about that role, including actual name in the model data, type, etc. + QHash<QString, ColumnRoleMetadata> roles; + }; + + enum NewRowOperationFlag { + OtherOperation, // insert(), set(), etc. + SetRowsOperation, + AppendOperation + }; + + void doSetRows(const QVariantList &rowsAsVariantList); + ColumnRoleMetadata fetchColumnRoleData(const QString &roleNameKey, + QQmlTableModelColumn *tableModelColumn, int columnIndex) const; + void fetchColumnMetadata(); + + bool validateRowType(const char *functionName, const QVariant &row) const; + bool validateNewRow(const char *functionName, const QVariant &row, + int rowIndex, NewRowOperationFlag operation = OtherOperation) const; + bool validateRowIndex(const char *functionName, const char *argumentName, int rowIndex) const; + + void doInsert(int rowIndex, const QVariant &row); + + void classBegin() override; + void componentComplete() override; + + bool componentCompleted = false; + QVariantList mRows; + QList<QQmlTableModelColumn *> mColumns; + int mRowCount = 0; + int mColumnCount = 0; + // Each entry contains information about the properties of the column at that index. + QVector<ColumnMetadata> mColumnMetadata; + // key = property index (0 to number of properties across all columns) + // value = role name + QHash<int, QByteArray> mRoleNames; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlTableModel) + +#endif // QQMLTABLEMODEL_P_H diff --git a/src/qml/types/qqmltablemodelcolumn.cpp b/src/qml/types/qqmltablemodelcolumn.cpp new file mode 100644 index 0000000000..93da0642de --- /dev/null +++ b/src/qml/types/qqmltablemodelcolumn.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltablemodelcolumn_p.h" + +#include <QtQml/qqmlinfo.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype TableModelColumn + \instantiates QQmlTableModelColumn + \inqmlmodule Qt.labs.qmlmodels + \brief Represents a column in a model. + \since 5.14 + + \section1 Supported Roles + + TableModelColumn supports all of \l {Qt::ItemDataRole}{Qt's roles}, + with the exception of \c Qt::InitialSortOrderRole. + + \sa TableModel, TableView +*/ + +static const QString displayRoleName = QStringLiteral("display"); +static const QString decorationRoleName = QStringLiteral("decoration"); +static const QString editRoleName = QStringLiteral("edit"); +static const QString toolTipRoleName = QStringLiteral("toolTip"); +static const QString statusTipRoleName = QStringLiteral("statusTip"); +static const QString whatsThisRoleName = QStringLiteral("whatsThis"); + +static const QString fontRoleName = QStringLiteral("font"); +static const QString textAlignmentRoleName = QStringLiteral("textAlignment"); +static const QString backgroundRoleName = QStringLiteral("background"); +static const QString foregroundRoleName = QStringLiteral("foreground"); +static const QString checkStateRoleName = QStringLiteral("checkState"); + +static const QString accessibleTextRoleName = QStringLiteral("accessibleText"); +static const QString accessibleDescriptionRoleName = QStringLiteral("accessibleDescription"); + +static const QString sizeHintRoleName = QStringLiteral("sizeHint"); + + +QQmlTableModelColumn::QQmlTableModelColumn(QObject *parent) + : QObject(parent) +{ +} + +QQmlTableModelColumn::~QQmlTableModelColumn() +{ +} + +#define DEFINE_ROLE_PROPERTIES(getterGetterName, getterSetterName, getterSignal, setterGetterName, setterSetterName, setterSignal, roleName) \ +QJSValue QQmlTableModelColumn::getterGetterName() const \ +{ \ + return mGetters.value(roleName); \ +} \ +\ +void QQmlTableModelColumn::getterSetterName(const QJSValue &stringOrFunction) \ +{ \ + if (!stringOrFunction.isString() && !stringOrFunction.isCallable()) { \ + qmlWarning(this).quote() << "getter for " << roleName << " must be a function"; \ + return; \ + } \ + if (stringOrFunction.strictlyEquals(decoration())) \ + return; \ +\ + mGetters[roleName] = stringOrFunction; \ + emit decorationChanged(); \ +} \ +\ +QJSValue QQmlTableModelColumn::setterGetterName() const \ +{ \ + return mSetters.value(roleName); \ +} \ +\ +void QQmlTableModelColumn::setterSetterName(const QJSValue &function) \ +{ \ + if (!function.isCallable()) { \ + qmlWarning(this).quote() << "setter for " << roleName << " must be a function"; \ + return; \ + } \ +\ + if (function.strictlyEquals(getSetDisplay())) \ + return; \ +\ + mSetters[roleName] = function; \ + emit setDisplayChanged(); \ +} + +DEFINE_ROLE_PROPERTIES(display, setDisplay, displayChanged, + getSetDisplay, setSetDisplay, setDisplayChanged, displayRoleName) +DEFINE_ROLE_PROPERTIES(decoration, setDecoration, decorationChanged, + getSetDecoration, setSetDecoration, setDecorationChanged, decorationRoleName) +DEFINE_ROLE_PROPERTIES(edit, setEdit, editChanged, + getSetEdit, setSetEdit, setEditChanged, editRoleName) +DEFINE_ROLE_PROPERTIES(toolTip, setToolTip, toolTipChanged, + getSetToolTip, setSetToolTip, setToolTipChanged, toolTipRoleName) +DEFINE_ROLE_PROPERTIES(statusTip, setStatusTip, statusTipChanged, + getSetStatusTip, setSetStatusTip, setStatusTipChanged, statusTipRoleName) +DEFINE_ROLE_PROPERTIES(whatsThis, setWhatsThis, whatsThisChanged, + getSetWhatsThis, setSetWhatsThis, setWhatsThisChanged, whatsThisRoleName) + +DEFINE_ROLE_PROPERTIES(font, setFont, fontChanged, + getSetFont, setSetFont, setFontChanged, fontRoleName) +DEFINE_ROLE_PROPERTIES(textAlignment, setTextAlignment, textAlignmentChanged, + getSetTextAlignment, setSetTextAlignment, setTextAlignmentChanged, textAlignmentRoleName) +DEFINE_ROLE_PROPERTIES(background, setBackground, backgroundChanged, + getSetBackground, setSetBackground, setBackgroundChanged, backgroundRoleName) +DEFINE_ROLE_PROPERTIES(foreground, setForeground, foregroundChanged, + getSetForeground, setSetForeground, setForegroundChanged, foregroundRoleName) +DEFINE_ROLE_PROPERTIES(checkState, setCheckState, checkStateChanged, + getSetCheckState, setSetCheckState, setCheckStateChanged, checkStateRoleName) + +DEFINE_ROLE_PROPERTIES(accessibleText, setAccessibleText, accessibleTextChanged, + getSetAccessibleText, setSetAccessibleText, setAccessibleTextChanged, accessibleTextRoleName) +DEFINE_ROLE_PROPERTIES(accessibleDescription, setAccessibleDescription, accessibleDescriptionChanged, + getSetAccessibleDescription, setSetAccessibleDescription, setAccessibleDescriptionChanged, accessibleDescriptionRoleName) + +DEFINE_ROLE_PROPERTIES(sizeHint, setSizeHint, sizeHintChanged, + getSetSizeHint, setSetSizeHint, setSizeHintChanged, sizeHintRoleName) + +QJSValue QQmlTableModelColumn::getterAtRole(const QString &roleName) +{ + auto it = mGetters.find(roleName); + if (it == mGetters.end()) + return QJSValue(); + return *it; +} + +QJSValue QQmlTableModelColumn::setterAtRole(const QString &roleName) +{ + auto it = mSetters.find(roleName); + if (it == mSetters.end()) + return QJSValue(); + return *it; +} + +const QHash<QString, QJSValue> QQmlTableModelColumn::getters() const +{ + return mGetters; +} + +const QHash<int, QString> QQmlTableModelColumn::supportedRoleNames() +{ + QHash<int, QString> names; + names[Qt::DisplayRole] = QLatin1String("display"); + names[Qt::DecorationRole] = QLatin1String("decoration"); + names[Qt::EditRole] = QLatin1String("edit"); + names[Qt::ToolTipRole] = QLatin1String("toolTip"); + names[Qt::StatusTipRole] = QLatin1String("statusTip"); + names[Qt::WhatsThisRole] = QLatin1String("whatsThis"); + names[Qt::FontRole] = QLatin1String("font"); + names[Qt::TextAlignmentRole] = QLatin1String("textAlignment"); + names[Qt::BackgroundRole] = QLatin1String("background"); + names[Qt::ForegroundRole] = QLatin1String("foreground"); + names[Qt::CheckStateRole] = QLatin1String("checkState"); + names[Qt::AccessibleTextRole] = QLatin1String("accessibleText"); + names[Qt::AccessibleDescriptionRole] = QLatin1String("accessibleDescription"); + names[Qt::SizeHintRole] = QLatin1String("sizeHint"); + return names; +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmltablemodelcolumn_p.h b/src/qml/types/qqmltablemodelcolumn_p.h new file mode 100644 index 0000000000..41c02482c0 --- /dev/null +++ b/src/qml/types/qqmltablemodelcolumn_p.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTABLEMODELCOLUMN_P_H +#define QQMLTABLEMODELCOLUMN_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 <QtCore/QObject> +#include <QtQml/qqml.h> +#include <QtQml/private/qtqmlglobal_p.h> +#include <QtQml/qjsvalue.h> + +QT_BEGIN_NAMESPACE + +class Q_QML_AUTOTEST_EXPORT QQmlTableModelColumn : public QObject +{ + Q_OBJECT + Q_PROPERTY(QJSValue display READ display WRITE setDisplay NOTIFY displayChanged FINAL) + Q_PROPERTY(QJSValue setDisplay READ getSetDisplay WRITE setSetDisplay NOTIFY setDisplayChanged) + Q_PROPERTY(QJSValue decoration READ decoration WRITE setDecoration NOTIFY decorationChanged FINAL) + Q_PROPERTY(QJSValue setDecoration READ getSetDecoration WRITE setSetDecoration NOTIFY setDecorationChanged FINAL) + Q_PROPERTY(QJSValue edit READ edit WRITE setEdit NOTIFY editChanged FINAL) + Q_PROPERTY(QJSValue setEdit READ getSetEdit WRITE setSetEdit NOTIFY setEditChanged FINAL) + Q_PROPERTY(QJSValue toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged FINAL) + Q_PROPERTY(QJSValue setToolTip READ getSetToolTip WRITE setSetToolTip NOTIFY setToolTipChanged FINAL) + Q_PROPERTY(QJSValue statusTip READ statusTip WRITE setStatusTip NOTIFY statusTipChanged FINAL) + Q_PROPERTY(QJSValue setStatusTip READ getSetStatusTip WRITE setSetStatusTip NOTIFY setStatusTipChanged FINAL) + Q_PROPERTY(QJSValue whatsThis READ whatsThis WRITE setWhatsThis NOTIFY whatsThisChanged FINAL) + Q_PROPERTY(QJSValue setWhatsThis READ getSetWhatsThis WRITE setSetWhatsThis NOTIFY setWhatsThisChanged FINAL) + + Q_PROPERTY(QJSValue font READ font WRITE setFont NOTIFY fontChanged FINAL) + Q_PROPERTY(QJSValue setFont READ getSetFont WRITE setSetFont NOTIFY setFontChanged FINAL) + Q_PROPERTY(QJSValue textAlignment READ textAlignment WRITE setTextAlignment NOTIFY textAlignmentChanged FINAL) + Q_PROPERTY(QJSValue setTextAlignment READ getSetTextAlignment WRITE setSetTextAlignment NOTIFY setTextAlignmentChanged FINAL) + Q_PROPERTY(QJSValue background READ background WRITE setBackground NOTIFY backgroundChanged FINAL) + Q_PROPERTY(QJSValue setBackground READ getSetBackground WRITE setSetBackground NOTIFY setBackgroundChanged FINAL) + Q_PROPERTY(QJSValue foreground READ foreground WRITE setForeground NOTIFY foregroundChanged FINAL) + Q_PROPERTY(QJSValue setForeground READ getSetForeground WRITE setSetForeground NOTIFY setForegroundChanged FINAL) + Q_PROPERTY(QJSValue checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL) + Q_PROPERTY(QJSValue setCheckState READ getSetCheckState WRITE setSetCheckState NOTIFY setCheckStateChanged FINAL) + + Q_PROPERTY(QJSValue accessibleText READ accessibleText WRITE setAccessibleText NOTIFY accessibleTextChanged FINAL) + Q_PROPERTY(QJSValue setAccessibleText READ getSetAccessibleText WRITE setSetAccessibleText NOTIFY setAccessibleTextChanged FINAL) + Q_PROPERTY(QJSValue accessibleDescription READ accessibleDescription + WRITE setAccessibleDescription NOTIFY accessibleDescriptionChanged FINAL) + Q_PROPERTY(QJSValue setAccessibleDescription READ getSetAccessibleDescription + WRITE setSetAccessibleDescription NOTIFY setAccessibleDescriptionChanged FINAL) + + Q_PROPERTY(QJSValue sizeHint READ sizeHint WRITE setSizeHint NOTIFY sizeHintChanged FINAL) + Q_PROPERTY(QJSValue setSizeHint READ getSetSizeHint WRITE setSetSizeHint NOTIFY setSizeHintChanged FINAL) + +public: + QQmlTableModelColumn(QObject *parent = nullptr); + ~QQmlTableModelColumn() override; + + QJSValue display() const; + void setDisplay(const QJSValue &stringOrFunction); + QJSValue getSetDisplay() const; + void setSetDisplay(const QJSValue &function); + + QJSValue decoration() const; + void setDecoration(const QJSValue &stringOrFunction); + QJSValue getSetDecoration() const; + void setSetDecoration(const QJSValue &function); + + QJSValue edit() const; + void setEdit(const QJSValue &stringOrFunction); + QJSValue getSetEdit() const; + void setSetEdit(const QJSValue &function); + + QJSValue toolTip() const; + void setToolTip(const QJSValue &stringOrFunction); + QJSValue getSetToolTip() const; + void setSetToolTip(const QJSValue &function); + + QJSValue statusTip() const; + void setStatusTip(const QJSValue &stringOrFunction); + QJSValue getSetStatusTip() const; + void setSetStatusTip(const QJSValue &function); + + QJSValue whatsThis() const; + void setWhatsThis(const QJSValue &stringOrFunction); + QJSValue getSetWhatsThis() const; + void setSetWhatsThis(const QJSValue &function); + + QJSValue font() const; + void setFont(const QJSValue &stringOrFunction); + QJSValue getSetFont() const; + void setSetFont(const QJSValue &function); + + QJSValue textAlignment() const; + void setTextAlignment(const QJSValue &stringOrFunction); + QJSValue getSetTextAlignment() const; + void setSetTextAlignment(const QJSValue &function); + + QJSValue background() const; + void setBackground(const QJSValue &stringOrFunction); + QJSValue getSetBackground() const; + void setSetBackground(const QJSValue &function); + + QJSValue foreground() const; + void setForeground(const QJSValue &stringOrFunction); + QJSValue getSetForeground() const; + void setSetForeground(const QJSValue &function); + + QJSValue checkState() const; + void setCheckState(const QJSValue &stringOrFunction); + QJSValue getSetCheckState() const; + void setSetCheckState(const QJSValue &function); + + QJSValue accessibleText() const; + void setAccessibleText(const QJSValue &stringOrFunction); + QJSValue getSetAccessibleText() const; + void setSetAccessibleText(const QJSValue &function); + + QJSValue accessibleDescription() const; + void setAccessibleDescription(const QJSValue &stringOrFunction); + QJSValue getSetAccessibleDescription() const; + void setSetAccessibleDescription(const QJSValue &function); + + QJSValue sizeHint() const; + void setSizeHint(const QJSValue &stringOrFunction); + QJSValue getSetSizeHint() const; + void setSetSizeHint(const QJSValue &function); + + QJSValue getterAtRole(const QString &roleName); + QJSValue setterAtRole(const QString &roleName); + + const QHash<QString, QJSValue> getters() const; + + static const QHash<int, QString> supportedRoleNames(); + +Q_SIGNALS: + void indexChanged(); + void displayChanged(); + void setDisplayChanged(); + void decorationChanged(); + void setDecorationChanged(); + void editChanged(); + void setEditChanged(); + void toolTipChanged(); + void setToolTipChanged(); + void statusTipChanged(); + void setStatusTipChanged(); + void whatsThisChanged(); + void setWhatsThisChanged(); + + void fontChanged(); + void setFontChanged(); + void textAlignmentChanged(); + void setTextAlignmentChanged(); + void backgroundChanged(); + void setBackgroundChanged(); + void foregroundChanged(); + void setForegroundChanged(); + void checkStateChanged(); + void setCheckStateChanged(); + + void accessibleTextChanged(); + void setAccessibleTextChanged(); + void accessibleDescriptionChanged(); + void setAccessibleDescriptionChanged(); + void sizeHintChanged(); + void setSizeHintChanged(); + +private: + int mIndex = -1; + + // We store these in hashes because QQuickTableModel needs string-based lookup in certain situations. + QHash<QString, QJSValue> mGetters; + QHash<QString, QJSValue> mSetters; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlTableModelColumn) + +#endif // QQMLTABLEMODELCOLUMN_P_H diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index edb112276c..c081c9e7fc 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -39,10 +39,6 @@ #include "qtqmlglobal_p.h" #include "qquickworkerscript_p.h" -#if QT_CONFIG(qml_list_model) -#include "qqmllistmodel_p.h" -#include "qqmllistmodelworkeragent_p.h" -#endif #include <private/qqmlengine_p.h> #include <private/qqmlexpression_p.h> @@ -651,12 +647,10 @@ void QQuickWorkerScript::componentComplete() bool QQuickWorkerScript::event(QEvent *event) { if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - QQmlEngine *engine = qmlEngine(this); - if (engine) { + if (QQmlEngine *engine = qmlEngine(this)) { + QV4::ExecutionEngine *v4 = engine->handle(); WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); - QV4::Scope scope(engine->handle()); - QV4::ScopedValue value(scope, QV4::Serialize::deserialize(workerEvent->data(), scope.engine)); - emit message(QQmlV4Handle(value)); + emit message(QJSValue(v4, QV4::Serialize::deserialize(workerEvent->data(), v4))); } return true; } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { diff --git a/src/qml/types/qquickworkerscript_p.h b/src/qml/types/qquickworkerscript_p.h index 1a8d2ab076..87cf2e9754 100644 --- a/src/qml/types/qquickworkerscript_p.h +++ b/src/qml/types/qquickworkerscript_p.h @@ -83,7 +83,6 @@ private: }; class QQmlV4Function; -class QQmlV4Handle; class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus { Q_OBJECT @@ -102,7 +101,7 @@ public Q_SLOTS: Q_SIGNALS: void sourceChanged(); - void message(const QQmlV4Handle &messageObject); + void message(const QJSValue &messageObject); protected: void classBegin() override; diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index e74c89b1f1..5a56208dc4 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -6,7 +6,9 @@ SOURCES += \ $$PWD/qqmlobjectmodel.cpp \ $$PWD/qquickpackage.cpp \ $$PWD/qqmlinstantiator.cpp \ - $$PWD/qqmltableinstancemodel.cpp + $$PWD/qqmltableinstancemodel.cpp \ + $$PWD/qqmltablemodel.cpp \ + $$PWD/qqmltablemodelcolumn.cpp HEADERS += \ $$PWD/qqmlbind_p.h \ @@ -17,7 +19,9 @@ HEADERS += \ $$PWD/qquickpackage_p.h \ $$PWD/qqmlinstantiator_p.h \ $$PWD/qqmlinstantiator_p_p.h \ - $$PWD/qqmltableinstancemodel_p.h + $$PWD/qqmltableinstancemodel_p.h \ + $$PWD/qqmltablemodel_p.h \ + $$PWD/qqmltablemodelcolumn_p.h qtConfig(qml-worker-script) { SOURCES += \ |