aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/quick/delegatechooser/delegatechooser.pro8
-rw-r--r--examples/quick/delegatechooser/delegatechooser.qml145
-rw-r--r--examples/quick/delegatechooser/main.cpp51
-rw-r--r--examples/quick/delegatechooser/qml.qrc5
-rw-r--r--examples/quick/quick.pro1
-rw-r--r--src/imports/imports.pro3
-rw-r--r--src/imports/labsmodels/labsmodels.pro11
-rw-r--r--src/imports/labsmodels/plugin.cpp83
-rw-r--r--src/imports/labsmodels/qmldir3
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp23
-rw-r--r--src/qml/qml/qqmlcomponent_p.h2
-rw-r--r--src/qml/types/qqmldelegatecomponent.cpp285
-rw-r--r--src/qml/types/qqmldelegatecomponent_p.h155
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp74
-rw-r--r--src/qml/types/qqmldelegatemodel_p.h1
-rw-r--r--src/qml/types/qqmldelegatemodel_p_p.h4
-rw-r--r--src/qml/types/qqmlmodelsmodule.cpp10
-rw-r--r--src/qml/types/qqmlmodelsmodule_p.h1
-rw-r--r--src/qml/types/qqmltableinstancemodel.cpp32
-rw-r--r--src/qml/types/qqmltableinstancemodel_p.h6
-rw-r--r--src/qml/types/types.pri6
-rw-r--r--src/qml/util/qqmladaptormodel.cpp5
-rw-r--r--src/qml/util/qqmladaptormodel_p.h1
-rw-r--r--tests/auto/qmltest/listview/data/MultiDelegate.qml106
-rw-r--r--tests/auto/qmltest/listview/data/MultiDelegate2.qml101
-rw-r--r--tests/auto/qmltest/listview/data/logo.pngbin0 -> 1478 bytes
-rw-r--r--tests/auto/qmltest/listview/listview.pro1
-rw-r--r--tests/auto/qmltest/listview/tst_listview.qml51
28 files changed, 1144 insertions, 30 deletions
diff --git a/examples/quick/delegatechooser/delegatechooser.pro b/examples/quick/delegatechooser/delegatechooser.pro
new file mode 100644
index 0000000000..f06864ba89
--- /dev/null
+++ b/examples/quick/delegatechooser/delegatechooser.pro
@@ -0,0 +1,8 @@
+TEMPLATE = app
+QT += quick qml
+
+SOURCES += main.cpp
+RESOURCES += qml.qrc ../shared/shared.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/delegatechooser
+INSTALLS += target
diff --git a/examples/quick/delegatechooser/delegatechooser.qml b/examples/quick/delegatechooser/delegatechooser.qml
new file mode 100644
index 0000000000..afb8518abf
--- /dev/null
+++ b/examples/quick/delegatechooser/delegatechooser.qml
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQml.Models 2.12
+import QtQuick.Layouts 1.12
+import Qt.labs.qmlmodels 1.0
+
+Rectangle {
+ visible: true
+ width: 640
+ height: 640
+
+ ListModel {
+ id: listModel
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image"; source: "../shared/images/qt-logo.png" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image"; source: "../shared/images/qt-logo.png" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ }
+
+ ListModel {
+ id: listModel2
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image"; source: "../shared/images/qt-logo.png" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image"; source: "../shared/images/qt-logo.png" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "lightsteelblue" }
+ ListElement { dataType: "rect"; color: "fuchsia" }
+ ListElement { dataType: "rect"; color: "lime" }
+ }
+
+ DelegateChooser {
+ id: fancyDelegate
+ role: "dataType"
+ DelegateChoice {
+ roleValue: "rect"
+ delegate: DelegateChooser {
+ DelegateChoice {
+ row: 0
+ Rectangle {
+ width: parent.width
+ height: 50
+ color: "red"
+ border.color: "black"
+ border.width: 1
+ }
+ }
+ DelegateChoice {
+ Rectangle {
+ width: parent.width
+ height: 50
+ color: model.color
+ border.color: "black"
+ border.width: 1
+ }
+ }
+ }
+ }
+ DelegateChoice {
+ roleValue: "image"
+ delegate: Image {
+ width: parent.width
+ height: 100
+ source: model.source
+ fillMode: Image.PreserveAspectFit
+ }
+ }
+ }
+
+ Item {
+ anchors.fill: parent
+ id: ite
+ RowLayout {
+ ListView {
+ Layout.preferredHeight: ite.height
+ Layout.preferredWidth: ite.width * 0.5
+ model: listModel
+ delegate: fancyDelegate
+ }
+ ListView {
+ Layout.preferredHeight: ite.height
+ Layout.preferredWidth: ite.width * 0.5
+ model: listModel2
+ delegate: fancyDelegate
+ }
+ }
+ }
+}
diff --git a/examples/quick/delegatechooser/main.cpp b/examples/quick/delegatechooser/main.cpp
new file mode 100644
index 0000000000..5df9207e24
--- /dev/null
+++ b/examples/quick/delegatechooser/main.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "../shared/shared.h"
+DECLARATIVE_EXAMPLE_MAIN(delegatechooser)
diff --git a/examples/quick/delegatechooser/qml.qrc b/examples/quick/delegatechooser/qml.qrc
new file mode 100644
index 0000000000..0788f84c64
--- /dev/null
+++ b/examples/quick/delegatechooser/qml.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>delegatechooser.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro
index deef437046..0bb90e737b 100644
--- a/examples/quick/quick.pro
+++ b/examples/quick/quick.pro
@@ -25,6 +25,7 @@ SUBDIRS = quick-accessibility \
imageresponseprovider \
window \
particles \
+ delegatechooser \
shapes
#OpenGL Support Required
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
index b5f5155502..828382ba39 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -4,7 +4,8 @@ SUBDIRS += \
builtins \
qtqml \
folderlistmodel \
- models
+ models \
+ labsmodels
qtHaveModule(sql): SUBDIRS += localstorage
qtConfig(settings): SUBDIRS += settings
diff --git a/src/imports/labsmodels/labsmodels.pro b/src/imports/labsmodels/labsmodels.pro
new file mode 100644
index 0000000000..1795ae5e43
--- /dev/null
+++ b/src/imports/labsmodels/labsmodels.pro
@@ -0,0 +1,11 @@
+CXX_MODULE = qml
+TARGET = labsmodelsplugin
+TARGETPATH = Qt/labs/qmlmodels
+IMPORT_VERSION = 1.0
+
+SOURCES += \
+ plugin.cpp
+
+QT = qml-private
+
+load(qml_plugin)
diff --git a/src/imports/labsmodels/plugin.cpp b/src/imports/labsmodels/plugin.cpp
new file mode 100644
index 0000000000..e18f08b70b
--- /dev/null
+++ b/src/imports/labsmodels/plugin.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins 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 <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+
+#include <private/qqmlmodelsmodule_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule Qt.labs.qmlmodels 1.0
+ \title Qt QML Models experimental QML Types
+ \ingroup qmlmodules
+ \brief Provides QML experimental types for data models
+ \since 5.12
+
+ This QML module contains experimental QML types related to data models.
+
+ To use the types in this module, import the module with the following line:
+
+ \code
+ import Qt.labs.qmlmodels 1.0
+ \endcode
+*/
+
+//![class decl]
+class QtQmlLabsModelsPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+public:
+ QtQmlLabsModelsPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
+ void registerTypes(const char *uri) override
+ {
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.qmlmodels"));
+ Q_UNUSED(uri);
+ QQmlModelsModule::defineLabsModule();
+
+ qmlRegisterModule(uri, 1, 0);
+ }
+};
+//![class decl]
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/labsmodels/qmldir b/src/imports/labsmodels/qmldir
new file mode 100644
index 0000000000..9c735711c4
--- /dev/null
+++ b/src/imports/labsmodels/qmldir
@@ -0,0 +1,3 @@
+module Qt.labs.qmlmodels
+plugin labsmodelsplugin
+classname QtQmlLabsModelsPlugin
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index 1695f3e5d6..a03f69576d 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -44,6 +44,7 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
+#include <private/qqmldelegatecomponent_p.h>
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -804,13 +805,21 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
auto *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
Q_ASSERT(tr);
- if (tr->type.isValid()) {
- if (tr->type.metaObject() == &QQmlComponent::staticMetaObject)
- continue;
- } else if (tr->compilationUnit) {
- if (tr->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject)
- continue;
- }
+
+ const QMetaObject *firstMetaObject = nullptr;
+ if (tr->type.isValid())
+ firstMetaObject = tr->type.metaObject();
+ else if (tr->compilationUnit)
+ firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject();
+ // 1: test for QQmlComponent
+ if (firstMetaObject && firstMetaObject == &QQmlComponent::staticMetaObject)
+ continue;
+ // 2: test for QQmlAbstractDelegateComponent
+ while (firstMetaObject && firstMetaObject != &QQmlAbstractDelegateComponent::staticMetaObject)
+ firstMetaObject = firstMetaObject->superClass();
+ if (firstMetaObject)
+ continue;
+ // if here, not a QQmlComponent or a QQmlAbstractDelegateComponent, so needs wrapping
QQmlPropertyData *pd = nullptr;
if (binding->propertyNameIndex != quint32(0)) {
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index 9b2db4bccf..4d9e4c6c15 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -88,7 +88,7 @@ public:
void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate);
static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v);
- void incubateObject(
+ virtual void incubateObject(
QQmlIncubator *incubationTask,
QQmlComponent *component,
QQmlEngine *engine,
diff --git a/src/qml/types/qqmldelegatecomponent.cpp b/src/qml/types/qqmldelegatecomponent.cpp
new file mode 100644
index 0000000000..e52a7879a4
--- /dev/null
+++ b/src/qml/types/qqmldelegatecomponent.cpp
@@ -0,0 +1,285 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "qqmldelegatecomponent_p.h"
+#include <QtQml/private/qqmladaptormodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlAbstractDelegateComponent::QQmlAbstractDelegateComponent(QObject *parent)
+ : QQmlComponent(parent)
+{
+}
+
+QQmlAbstractDelegateComponent::~QQmlAbstractDelegateComponent()
+{
+}
+
+QVariant QQmlAbstractDelegateComponent::value(QQmlAdaptorModel *adaptorModel, int row, int column, const QString &role) const
+{
+ if (!adaptorModel)
+ return QVariant();
+ return adaptorModel->value(adaptorModel->indexAt(row, column), role);
+}
+
+/*!
+ \qmltype DelegateChoice
+ \instantiates QQmlDelegateChoice
+ \inqmlmodule Qt.labs.qmlmodels
+ \brief Encapsulates a delegate and when to use it
+
+ The DelegateChoice type wraps a delegate and defines the circumstances
+ in which it should be chosen.
+ DelegateChoices can be nested inside a DelegateChooser.
+
+ \sa DelegateChooser
+*/
+
+/*!
+ \qmlproperty string QtQml.Models::DelegateChoice::roleValue
+ This property holds the value used to match the role data for the role provided by \l DefaultDelegateChooser::role.
+*/
+QVariant QQmlDelegateChoice::roleValue() const
+{
+ return m_value;
+}
+
+void QQmlDelegateChoice::setRoleValue(const QVariant &value)
+{
+ if (m_value == value)
+ return;
+ m_value = value;
+ emit roleValueChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty index QtQml.Models::DelegateChoice::row
+ This property holds the value used to match the row value of model elements.
+ With models that have only the index property (and thus only one column), this property
+ should be intended as index, and set to the desired index value.
+
+ \note Setting both row and index has undefined behavior. The two are equivalent and only
+ one should be used.
+
+ \sa QtQml.Models::DelegateChoice::index
+*/
+
+/*!
+ \qmlproperty index QtQml.Models::DelegateChoice::index
+ This property holds the value used to match the index value of model elements.
+ This is effectively an alias for \l QtQml.Models::DelegateChoice::row
+
+ \sa QtQml.Models::DelegateChoice::row
+*/
+int QQmlDelegateChoice::row() const
+{
+ return m_row;
+}
+
+void QQmlDelegateChoice::setRow(int r)
+{
+ if (m_row == r)
+ return;
+ m_row = r;
+ emit rowChanged();
+ emit indexChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty index QtQml.Models::DelegateChoice::column
+ This property holds the value used to match the column value of model elements.
+*/
+int QQmlDelegateChoice::column() const
+{
+ return m_column;
+}
+
+void QQmlDelegateChoice::setColumn(int c)
+{
+ if (m_column == c)
+ return;
+ m_column = c;
+ emit columnChanged();
+ emit changed();
+}
+
+QQmlComponent *QQmlDelegateChoice::delegate() const
+{
+ return m_delegate;
+}
+
+/*!
+ \qmlproperty Component QtQml.Models::DelegateChoice::delegate
+ This property holds the delegate to use if this choice matches the model item.
+*/
+void QQmlDelegateChoice::setDelegate(QQmlComponent *delegate)
+{
+ if (m_delegate == delegate)
+ return;
+ QQmlAbstractDelegateComponent *adc = static_cast<QQmlAbstractDelegateComponent *>(m_delegate);
+ if (adc)
+ disconnect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged);
+ m_delegate = delegate;
+ adc = static_cast<QQmlAbstractDelegateComponent *>(delegate);
+ if (adc)
+ connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged);
+ emit delegateChanged();
+ emit changed();
+}
+
+bool QQmlDelegateChoice::match(int row, int column, const QVariant &value) const
+{
+ if (!m_value.isValid() && m_row < 0 && m_column < 0)
+ return true;
+
+ const bool roleMatched = (m_value.isValid()) ? value == m_value : true;
+ const bool rowMatched = (m_row < 0 ) ? true : m_row == row;
+ const bool columnMatched = (m_column < 0 ) ? true : m_column == column;
+ return roleMatched && rowMatched && columnMatched;
+}
+
+/*!
+ \qmltype QQmlDelegateChooser
+ \instantiates QQmlDelegateChooser
+ \inqmlmodule Qt.labs.qmlmodels
+ \brief Allows a view to use different delegates for different types of items in the model.
+
+ The DelegateChooser is a special \l Component type intended for those scenarios where a Component is required
+ by a view and used as a delegate.
+ DelegateChooser encapsulates a set of \l DelegateChoices.
+ These choices are used determine the delegate that will be instantiated for each
+ item in the model.
+ The selection of the choice is performed based on the value that a model item has for \l role,
+ and also based on index.
+
+ \note This type is intended to transparently work only with TableView and any DelegateModel-based view.
+ Views (including user-defined views) that aren't internally based on a DelegateModel need to explicitly support
+ this type of component to make it function as described.
+
+ \sa DelegateChoice
+*/
+
+/*!
+ \qmlproperty string QtQml.Models::DelegateChooser::role
+ This property holds the role used to determine the delegate for a given model item.
+
+ \sa DelegateChoice
+*/
+void QQmlDelegateChooser::setRole(const QString &role)
+{
+ if (m_role == role)
+ return;
+ m_role = role;
+ emit roleChanged();
+}
+
+/*!
+ \qmlproperty list<DelegateChoice> QtQml.Models::DelegateChooser::choices
+ \default
+ The list of DelegateChoices for the chooser.
+
+ The list is treated as an ordered list, where the first DelegateChoice to match
+ will be used be a view.
+
+ It should not generally be necessary to refer to the \c choices property,
+ as it is the default property for DefaultDelegateChooser and thus all child items are
+ automatically assigned to this property.
+*/
+
+QQmlListProperty<QQmlDelegateChoice> QQmlDelegateChooser::choices()
+{
+ return QQmlListProperty<QQmlDelegateChoice>(this, nullptr,
+ QQmlDelegateChooser::choices_append,
+ QQmlDelegateChooser::choices_count,
+ QQmlDelegateChooser::choices_at,
+ QQmlDelegateChooser::choices_clear);
+}
+
+void QQmlDelegateChooser::choices_append(QQmlListProperty<QQmlDelegateChoice> *prop, QQmlDelegateChoice *choice)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
+ q->m_choices.append(choice);
+ connect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged);
+ q->delegateChanged();
+}
+
+int QQmlDelegateChooser::choices_count(QQmlListProperty<QQmlDelegateChoice> *prop)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object);
+ return q->m_choices.count();
+}
+
+QQmlDelegateChoice *QQmlDelegateChooser::choices_at(QQmlListProperty<QQmlDelegateChoice> *prop, int index)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object);
+ return q->m_choices.at(index);
+}
+
+void QQmlDelegateChooser::choices_clear(QQmlListProperty<QQmlDelegateChoice> *prop)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
+ for (QQmlDelegateChoice *choice : q->m_choices)
+ disconnect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged);
+ q->m_choices.clear();
+ q->delegateChanged();
+}
+
+QQmlComponent *QQmlDelegateChooser::delegate(QQmlAdaptorModel *adaptorModel, int row, int column) const
+{
+ QVariant v;
+ if (!m_role.isNull())
+ v = value(adaptorModel, row, column, m_role);
+ if (!v.isValid()) { // check if the row only has modelData, for example if the row is a QVariantMap
+ v = value(adaptorModel, row, column, QStringLiteral("modelData"));
+ if (v.isValid())
+ v = v.toMap().value(m_role);
+ }
+ // loop through choices, finding first one that fits
+ for (int i = 0; i < m_choices.count(); ++i) {
+ const QQmlDelegateChoice *choice = m_choices.at(i);
+ if (choice->match(row, column, v))
+ return choice->delegate();
+ }
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/types/qqmldelegatecomponent_p.h b/src/qml/types/qqmldelegatecomponent_p.h
new file mode 100644
index 0000000000..c925ed9a60
--- /dev/null
+++ b/src/qml/types/qqmldelegatecomponent_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 QQMLDELEGATECOMPONENT_P_H
+#define QQMLDELEGATECOMPONENT_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 <private/qtqmlglobal_p.h>
+#include <qqmlcomponent.h>
+
+QT_REQUIRE_CONFIG(qml_delegate_model);
+
+QT_BEGIN_NAMESPACE
+
+// TODO: consider making QQmlAbstractDelegateComponent public API
+class QQmlAbstractDelegateComponentPrivate;
+class QQmlAdaptorModel;
+class Q_QML_PRIVATE_EXPORT QQmlAbstractDelegateComponent : public QQmlComponent
+{
+ Q_OBJECT
+public:
+ QQmlAbstractDelegateComponent(QObject *parent = nullptr);
+ ~QQmlAbstractDelegateComponent() override;
+
+ virtual QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = 0) const = 0;
+
+signals:
+ void delegateChanged();
+
+protected:
+ QVariant value(QQmlAdaptorModel *adaptorModel,int row, int column, const QString &role) const;
+
+private:
+ Q_DECLARE_PRIVATE(QQmlAbstractDelegateComponent)
+ Q_DISABLE_COPY(QQmlAbstractDelegateComponent)
+};
+
+class Q_QML_PRIVATE_EXPORT QQmlDelegateChoice : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged)
+ Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged)
+ Q_PROPERTY(int index READ row WRITE setRow NOTIFY indexChanged)
+ Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged)
+ Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_CLASSINFO("DefaultProperty", "delegate")
+public:
+ QVariant roleValue() const;
+ void setRoleValue(const QVariant &roleValue);
+
+ int row() const;
+ void setRow(int r);
+
+ int column() const;
+ void setColumn(int c);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *delegate);
+
+ virtual bool match(int row, int column, const QVariant &value) const;
+
+signals:
+ void roleValueChanged();
+ void rowChanged();
+ void indexChanged();
+ void columnChanged();
+ void delegateChanged();
+ void changed();
+
+private:
+ QVariant m_value;
+ int m_row = -1;
+ int m_column = -1;
+ QQmlComponent *m_delegate = nullptr;
+};
+
+class Q_QML_PRIVATE_EXPORT QQmlDelegateChooser : public QQmlAbstractDelegateComponent
+{
+ Q_OBJECT
+ Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged)
+ Q_PROPERTY(QQmlListProperty<QQmlDelegateChoice> choices READ choices CONSTANT)
+ Q_CLASSINFO("DefaultProperty", "choices")
+
+public:
+ QString role() const { return m_role; }
+ void setRole(const QString &role);
+
+ virtual QQmlListProperty<QQmlDelegateChoice> choices();
+ static void choices_append(QQmlListProperty<QQmlDelegateChoice> *, QQmlDelegateChoice *);
+ static int choices_count(QQmlListProperty<QQmlDelegateChoice> *);
+ static QQmlDelegateChoice *choices_at(QQmlListProperty<QQmlDelegateChoice> *, int);
+ static void choices_clear(QQmlListProperty<QQmlDelegateChoice> *);
+
+ QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = -1) const override;
+
+signals:
+ void roleChanged();
+
+private:
+ QString m_role;
+ QList<QQmlDelegateChoice *> m_choices;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlDelegateChoice)
+QML_DECLARE_TYPE(QQmlDelegateChooser)
+
+#endif // QQMLDELEGATECOMPONENT_P_H
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp
index bf2fe3b52a..41970ce626 100644
--- a/src/qml/types/qqmldelegatemodel.cpp
+++ b/src/qml/types/qqmldelegatemodel.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qqmldelegatemodel_p_p.h"
+#include "qqmldelegatecomponent_p.h"
#include <QtQml/qqmlinfo.h>
@@ -200,7 +201,8 @@ QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent)
*/
QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt)
- : m_cacheMetaType(nullptr)
+ : m_delegateChooser(nullptr)
+ , m_cacheMetaType(nullptr)
, m_context(ctxt)
, m_parts(nullptr)
, m_filterGroup(QStringLiteral("items"))
@@ -454,6 +456,8 @@ void QQmlDelegateModel::setModel(const QVariant &model)
The delegate provides a template defining each item instantiated by a view.
The index is exposed as an accessible \c index property. Properties of the
model are also available depending upon the type of \l {qml-data-models}{Data Model}.
+
+ \sa DelegateComponent
*/
QQmlComponent *QQmlDelegateModel::delegate() const
{
@@ -468,22 +472,25 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate)
qmlWarning(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated.");
return;
}
+ if (d->m_delegate == delegate)
+ return;
bool wasValid = d->m_delegate != nullptr;
d->m_delegate.setObject(delegate, this);
d->m_delegateValidated = false;
- if (wasValid && d->m_complete) {
- for (int i = 1; i < d->m_groupCount; ++i) {
- QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.remove(
- 0, d->m_compositor.count(Compositor::Group(i)));
- }
- }
- if (d->m_complete && d->m_delegate) {
- for (int i = 1; i < d->m_groupCount; ++i) {
- QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.insert(
- 0, d->m_compositor.count(Compositor::Group(i)));
+ if (d->m_delegateChooser)
+ QObject::disconnect(d->m_delegateChooserChanged);
+
+ d->m_delegateChooser = nullptr;
+ if (delegate) {
+ QQmlAbstractDelegateComponent *adc =
+ qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ if (adc) {
+ d->m_delegateChooser = adc;
+ d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged,
+ [d](){ d->delegateChanged(); });
}
}
- d->emitChanges();
+ d->delegateChanged(d->m_delegate, wasValid);
}
/*!
@@ -1042,7 +1049,18 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
cacheItem->incubationTask->forceCompletion();
}
} else if (!cacheItem->object) {
- QQmlContext *creationContext = m_delegate->creationContext();
+ QQmlComponent *delegate = nullptr;
+ if (m_delegateChooser) {
+ QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
+ do {
+ delegate = chooser->delegate(&m_adaptorModel, index);
+ chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ } while (chooser);
+ }
+ if (!delegate)
+ delegate = m_delegate;
+
+ QQmlContext *creationContext = delegate->creationContext();
cacheItem->scriptRef += 1;
@@ -1067,10 +1085,10 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
}
}
- QQmlComponentPrivate *cp = QQmlComponentPrivate::get(m_delegate);
+ QQmlComponentPrivate *cp = QQmlComponentPrivate::get(delegate);
cp->incubateObject(
cacheItem->incubationTask,
- m_delegate,
+ delegate,
m_context->engine(),
ctxt,
QQmlContextData::get(m_context));
@@ -1568,6 +1586,32 @@ void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet,
emit q->countChanged();
}
+void QQmlDelegateModelPrivate::delegateChanged(bool add, bool remove)
+{
+ Q_Q(QQmlDelegateModel);
+ if (!m_complete)
+ return;
+
+ if (m_transaction) {
+ qmlWarning(q) << QQmlDelegateModel::tr("The delegates of a DelegateModel cannot be changed within onUpdated.");
+ return;
+ }
+
+ if (remove) {
+ for (int i = 1; i < m_groupCount; ++i) {
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(
+ 0, m_compositor.count(Compositor::Group(i)));
+ }
+ }
+ if (add) {
+ for (int i = 1; i < m_groupCount; ++i) {
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(
+ 0, m_compositor.count(Compositor::Group(i)));
+ }
+ }
+ emitChanges();
+}
+
void QQmlDelegateModelPrivate::emitChanges()
{
if (m_transaction || !m_complete || !m_context || !m_context->isValid())
diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h
index 5c66a90798..0ad8939732 100644
--- a/src/qml/types/qqmldelegatemodel_p.h
+++ b/src/qml/types/qqmldelegatemodel_p.h
@@ -67,7 +67,6 @@ QT_REQUIRE_CONFIG(qml_delegate_model);
QT_BEGIN_NAMESPACE
class QQmlChangeSet;
-class QQmlComponent;
class QQuickPackage;
class QQmlV4Function;
class QQmlDelegateModelGroup;
diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h
index 43d288b5c5..2d6fdf228e 100644
--- a/src/qml/types/qqmldelegatemodel_p_p.h
+++ b/src/qml/types/qqmldelegatemodel_p_p.h
@@ -67,6 +67,7 @@ QT_BEGIN_NAMESPACE
typedef QQmlListCompositor Compositor;
class QQmlDelegateModelAttachedMetaObject;
+class QQmlAbstractDelegateComponent;
class Q_QML_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount
{
@@ -303,6 +304,7 @@ public:
void itemsChanged(const QVector<Compositor::Change> &changes);
void emitChanges();
void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override;
+ void delegateChanged(bool add = true, bool remove = true);
bool insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups);
@@ -319,6 +321,8 @@ public:
QQmlAdaptorModel m_adaptorModel;
QQmlListCompositor m_compositor;
QQmlStrongJSQObjectReference<QQmlComponent> m_delegate;
+ QQmlAbstractDelegateComponent *m_delegateChooser;
+ QMetaObject::Connection m_delegateChooserChanged;
QQmlDelegateModelItemMetaType *m_cacheMetaType;
QPointer<QQmlContext> m_context;
QQmlDelegateModelParts *m_parts;
diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp
index 9c170cb008..30915d96fd 100644
--- a/src/qml/types/qqmlmodelsmodule.cpp
+++ b/src/qml/types/qqmlmodelsmodule.cpp
@@ -44,6 +44,7 @@
#endif
#if QT_CONFIG(qml_delegate_model)
#include <private/qqmldelegatemodel_p.h>
+#include <private/qqmldelegatecomponent_p.h>
#endif
#include <private/qqmlobjectmodel_p.h>
@@ -67,4 +68,13 @@ void QQmlModelsModule::defineModule()
qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel");
}
+void QQmlModelsModule::defineLabsModule()
+{
+ const char uri[] = "Qt.labs.qmlmodels";
+
+ qmlRegisterUncreatableType<QQmlAbstractDelegateComponent>(uri, 1, 0, "AbstractDelegateComponent", QQmlAbstractDelegateComponent::tr("Cannot create instance of abstract class AbstractDelegateComponent."));
+ qmlRegisterType<QQmlDelegateChooser>(uri, 1, 0, "DelegateChooser");
+ qmlRegisterType<QQmlDelegateChoice>(uri, 1, 0, "DelegateChoice");
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h
index bac9bea81e..939ecc1500 100644
--- a/src/qml/types/qqmlmodelsmodule_p.h
+++ b/src/qml/types/qqmlmodelsmodule_p.h
@@ -59,6 +59,7 @@ class Q_QML_PRIVATE_EXPORT QQmlModelsModule
{
public:
static void defineModule();
+ static void defineLabsModule();
};
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmltableinstancemodel.cpp b/src/qml/types/qqmltableinstancemodel.cpp
index d667b884fc..33d13e3bad 100644
--- a/src/qml/types/qqmltableinstancemodel.cpp
+++ b/src/qml/types/qqmltableinstancemodel.cpp
@@ -38,7 +38,7 @@
****************************************************************************/
#include "qqmltableinstancemodel_p.h"
-#include "qqmltableinstancemodel_p.h"
+#include "qqmldelegatecomponent_p.h"
#include <QtCore/QTimer>
@@ -107,6 +107,23 @@ QQmlTableInstanceModel::~QQmlTableInstanceModel()
drainReusableItemsPool(0);
}
+QQmlComponent *QQmlTableInstanceModel::resolveDelegate(int index)
+{
+ QQmlComponent *delegate = nullptr;
+ if (m_delegateChooser) {
+ const int row = m_adaptorModel.rowAt(index);
+ const int column = m_adaptorModel.columnAt(index);
+ QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
+ do {
+ delegate = chooser->delegate(&m_adaptorModel, row, column);
+ chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ } while (chooser);
+ }
+ if (!delegate)
+ delegate = m_delegate;
+ return delegate;
+}
+
QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int index)
{
// Check if an item for the given index is already loaded and ready
@@ -114,7 +131,7 @@ QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int index)
if (modelItem)
return modelItem;
- QQmlComponent *delegate = m_delegate;
+ QQmlComponent *delegate = resolveDelegate(index);
// Check if the pool contains an item that can be reused
modelItem = takeFromReusableItemsPool(delegate);
@@ -478,6 +495,17 @@ QQmlComponent *QQmlTableInstanceModel::delegate() const
void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate)
{
+ if (m_delegate == delegate)
+ return;
+
+ m_delegateChooser = nullptr;
+ if (delegate) {
+ QQmlAbstractDelegateComponent *adc =
+ qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ if (adc)
+ m_delegateChooser = adc;
+ }
+
m_delegate = delegate;
}
diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h
index 93ef4a697f..03761af326 100644
--- a/src/qml/types/qqmltableinstancemodel_p.h
+++ b/src/qml/types/qqmltableinstancemodel_p.h
@@ -57,6 +57,7 @@
QT_BEGIN_NAMESPACE
class QQmlTableInstanceModel;
+class QQmlAbstractDelegateComponent;
class QQmlTableInstanceModelIncubationTask : public QQDMIncubationTask
{
@@ -128,8 +129,11 @@ Q_SIGNALS:
void itemReused(int index, QObject *object);
private:
- QQmlComponent *m_delegate = nullptr;
+ QQmlComponent *resolveDelegate(int index);
+
QQmlAdaptorModel m_adaptorModel;
+ QQmlAbstractDelegateComponent *m_delegateChooser = nullptr;
+ QQmlComponent *m_delegate = nullptr;
QPointer<QQmlContext> m_qmlContext;
QQmlDelegateModelItemMetaType *m_metaType;
diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri
index 6a74d70a0e..492f408271 100644
--- a/src/qml/types/types.pri
+++ b/src/qml/types/types.pri
@@ -34,11 +34,13 @@ qtConfig(qml-list-model) {
qtConfig(qml-delegate-model) {
SOURCES += \
- $$PWD/qqmldelegatemodel.cpp
+ $$PWD/qqmldelegatemodel.cpp \
+ $$PWD/qqmldelegatecomponent.cpp
HEADERS += \
$$PWD/qqmldelegatemodel_p.h \
- $$PWD/qqmldelegatemodel_p_p.h
+ $$PWD/qqmldelegatemodel_p_p.h \
+ $$PWD/qqmldelegatecomponent_p.h
}
qtConfig(qml-animation) {
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
index e460c376da..b5caaba727 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -1005,6 +1005,11 @@ int QQmlAdaptorModel::columnAt(int index) const
return count <= 0 ? -1 : index / count;
}
+int QQmlAdaptorModel::indexAt(int row, int column) const
+{
+ return column * rowCount() + row;
+}
+
void QQmlAdaptorModel::objectDestroyed(QObject *)
{
setModel(QVariant(), nullptr, nullptr);
diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h
index c4cfc0f466..b834704163 100644
--- a/src/qml/util/qqmladaptormodel_p.h
+++ b/src/qml/util/qqmladaptormodel_p.h
@@ -123,6 +123,7 @@ public:
int columnCount() const;
int rowAt(int index) const;
int columnAt(int index) const;
+ int indexAt(int row, int column) const;
inline bool adaptsAim() const { return qobject_cast<QAbstractItemModel *>(object()); }
inline QAbstractItemModel *aim() { return static_cast<QAbstractItemModel *>(object()); }
diff --git a/tests/auto/qmltest/listview/data/MultiDelegate.qml b/tests/auto/qmltest/listview/data/MultiDelegate.qml
new file mode 100644
index 0000000000..b9640c18b4
--- /dev/null
+++ b/tests/auto/qmltest/listview/data/MultiDelegate.qml
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQml.Models 2.12
+import Qt.labs.qmlmodels 1.0
+
+ListView {
+ width: 400
+ height: 400
+ model: ListModel {
+ ListElement { dataType: "rect"; color: "red" }
+ ListElement { dataType: "image"; source: "logo.png" }
+ ListElement { dataType: "text"; text: "Hello" }
+ ListElement { dataType: "text"; text: "World" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image"; source: "logo.png" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "" }
+ }
+
+ delegate: DelegateChooser {
+ role: "dataType"
+ DelegateChoice {
+ roleValue: "image"
+ delegate: Image {
+ width: parent.width
+ height: 50
+ fillMode: Image.PreserveAspectFit
+ source: model.source
+ }
+ }
+ DelegateChoice {
+ roleValue: "rect"
+ delegate: Rectangle {
+ width: parent.width
+ height: 50
+ color: model.color
+ }
+ }
+ DelegateChoice {
+ roleValue: "text"
+ delegate: Text {
+ width: parent.width
+ height: 50
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: model.text
+ }
+ }
+
+ DelegateChoice {
+ delegate: Item {
+ width: parent.width
+ height: 50
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmltest/listview/data/MultiDelegate2.qml b/tests/auto/qmltest/listview/data/MultiDelegate2.qml
new file mode 100644
index 0000000000..f623204b98
--- /dev/null
+++ b/tests/auto/qmltest/listview/data/MultiDelegate2.qml
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQml.Models 2.12
+import Qt.labs.qmlmodels 1.0
+
+ListView {
+ width: 400
+ height: 400
+ model: 8
+
+ delegate: DelegateChooser {
+ DelegateChoice {
+ index: 0
+ delegate: Item {
+ property string choiceType: "1"
+ width: parent.width
+ height: 50
+ }
+ }
+ DelegateChoice {
+ index: 1
+ delegate: Item {
+ property string choiceType: "2"
+ width: parent.width
+ height: 50
+ }
+ }
+ DelegateChoice {
+ index: 2
+ delegate: Item {
+ property string choiceType: "3"
+ width: parent.width
+ height: 50
+ }
+ }
+ DelegateChoice {
+ index: 5
+ delegate: Item {
+ property string choiceType: "3"
+ width: parent.width
+ height: 50
+ }
+ }
+ DelegateChoice {
+ delegate: Item {
+ property string choiceType: "4"
+ width: parent.width
+ height: 50
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmltest/listview/data/logo.png b/tests/auto/qmltest/listview/data/logo.png
new file mode 100644
index 0000000000..d75936b007
--- /dev/null
+++ b/tests/auto/qmltest/listview/data/logo.png
Binary files differ
diff --git a/tests/auto/qmltest/listview/listview.pro b/tests/auto/qmltest/listview/listview.pro
index a7938e7003..b942729ffa 100644
--- a/tests/auto/qmltest/listview/listview.pro
+++ b/tests/auto/qmltest/listview/listview.pro
@@ -1 +1,2 @@
CONFIG += qmltestcase
+DISTFILES += $$PWD/tst_listview.qml $$files($$PWD/data/*.qml)
diff --git a/tests/auto/qmltest/listview/tst_listview.qml b/tests/auto/qmltest/listview/tst_listview.qml
index f7a34cbce2..5e9bb22e8e 100644
--- a/tests/auto/qmltest/listview/tst_listview.qml
+++ b/tests/auto/qmltest/listview/tst_listview.qml
@@ -50,6 +50,7 @@
import QtQuick 2.1
import QtTest 1.1
+import "data"
Item {
id: top
@@ -204,6 +205,14 @@ Item {
ListElement { component: "data/asynclistviewloader.qml" }
}
+ MultiDelegate {
+ id: multiDelegate
+ }
+
+ MultiDelegate2 {
+ id: multiDelegate2
+ }
+
TestCase {
name: "ListView"
when: windowShown
@@ -363,5 +372,47 @@ Item {
function test_viewWithAction() {
compare(viewWithActionModel.funcResult, "one")
}
+
+ function test_multipleDelegates_data() {
+ return [
+ { y: 25, type: "Rectangle", value: "red" },
+ { y: 75, type: "Image", value: Qt.resolvedUrl("data/logo.png") },
+ { y: 125, type: "Text", value: "Hello" },
+ { y: 175, type: "Text", value: "World" },
+ { y: 225, type: "Rectangle", value: "green" },
+ { y: 275, type: "Image", value: Qt.resolvedUrl("data/logo.png") },
+ { y: 325, type: "Rectangle", value: "blue" },
+ { y: 375, type: "Item", value: "" }
+ ]
+ }
+
+ function test_multipleDelegates(row) {
+ var delegate = multiDelegate.itemAt(10, row.y)
+ verify(delegate.toString().includes(row.type))
+ switch (row.type) {
+ case "Rectangle": verify(Qt.colorEqual(delegate.color, row.value)); break
+ case "Text": compare(delegate.text, row.value); break
+ case "Image": compare(delegate.source, row.value); break
+ case "Item": break
+ }
+ }
+
+ function test_multipleDelegates2_data() {
+ return [
+ { y: 25, type: "1" },
+ { y: 75, type: "2" },
+ { y: 125, type: "3" },
+ { y: 175, type: "4" },
+ { y: 225, type: "4" },
+ { y: 275, type: "3" },
+ { y: 325, type: "4" },
+ { y: 375, type: "4" }
+ ]
+ }
+
+ function test_multipleDelegates2(row) {
+ var delegate = multiDelegate2.itemAt(10, row.y)
+ compare(delegate.choiceType, row.type)
+ }
}
}