diff options
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 Binary files differnew file mode 100644 index 0000000000..d75936b007 --- /dev/null +++ b/tests/auto/qmltest/listview/data/logo.png 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) + } } } |