diff options
author | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2018-08-21 00:01:31 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2018-08-21 00:01:31 +0200 |
commit | e33080767e73c1fe9156a5423ff4a7c3e00e8470 (patch) | |
tree | 3bfb5c17674bf475943fb807b9a063728ea65047 /src/qml | |
parent | 36ea464b3dcce6319c5252553d48dd5ac6ba8f41 (diff) | |
parent | e47bd1fc0e6fc2b2e3ca82c48ccb2f8f7dd5ee92 (diff) |
Merge dev into 5.12
Change-Id: Ib4dd56aa31ea0f2026b61dbae061d0b333426d0f
Diffstat (limited to 'src/qml')
23 files changed, 667 insertions, 84 deletions
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/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index d3c5582b28..e2533a0505 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -56,8 +56,9 @@ ByteCodeHandler::~ByteCodeHandler() { \ INSTR_##instr(MOTH_DECODE_WITH_BASE) \ Q_UNUSED(base_ptr); \ + _currentOffset = _nextOffset; \ + _nextOffset = code - start; \ startInstruction(Instr::Type::instr); \ - _offset = code - start; \ INSTR_##instr(DISPATCH) \ endInstruction(Instr::Type::instr); \ continue; \ diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h index 5f1121306f..ca6abf3dc3 100644 --- a/src/qml/compiler/qv4bytecodehandler_p.h +++ b/src/qml/compiler/qv4bytecodehandler_p.h @@ -91,7 +91,8 @@ public: void decode(const char *code, uint len); - int instructionOffset() const { return _offset; } + int currentInstructionOffset() const { return _currentOffset; } + int nextInstructionOffset() const { return _nextOffset; } static std::vector<int> collectLabelsInBytecode(const char *code, uint len); @@ -102,7 +103,8 @@ protected: virtual void endInstruction(Moth::Instr::Type instr) = 0; private: - int _offset = 0; + int _currentOffset = 0; + int _nextOffset = 0; }; } // Moth namespace diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index bf442de741..2e0f357574 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -73,7 +73,7 @@ void BaselineJIT::generate() // qDebug()<<"done"; } -#define STORE_IP() as->storeInstructionPointer(instructionOffset()) +#define STORE_IP() as->storeInstructionPointer(nextInstructionOffset()) #define STORE_ACC() as->saveAccumulatorInFrame() void BaselineJIT::generate_Ret() @@ -562,7 +562,7 @@ void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv) void BaselineJIT::generate_SetUnwindHandler(int offset) { if (offset) - as->setUnwindHandler(instructionOffset() + offset); + as->setUnwindHandler(absoluteOffsetForJump(offset)); else as->clearUnwindHandler(); } @@ -574,7 +574,7 @@ void BaselineJIT::generate_UnwindDispatch() void BaselineJIT::generate_UnwindToLabel(int level, int offset) { - as->unwindToLabel(level, instructionOffset() + offset); + as->unwindToLabel(level, absoluteOffsetForJump(offset)); } @@ -824,11 +824,11 @@ void BaselineJIT::generate_ToObject() } -void BaselineJIT::generate_Jump(int offset) { as->jump(instructionOffset() + offset); } -void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(instructionOffset() + offset); } -void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(instructionOffset() + offset); } -void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(instructionOffset() + offset); } -void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(instructionOffset() + offset); } +void BaselineJIT::generate_Jump(int offset) { as->jump(absoluteOffsetForJump(offset)); } +void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(absoluteOffsetForJump(offset)); } +void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(absoluteOffsetForJump(offset)); } +void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(absoluteOffsetForJump(offset)); } +void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(absoluteOffsetForJump(offset)); } void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); } void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); } @@ -932,7 +932,7 @@ void BaselineJIT::generate_LoadQmlImportedScripts(int result) void BaselineJIT::startInstruction(Instr::Type /*instr*/) { if (hasLabel()) - as->addLabel(instructionOffset()); + as->addLabel(currentInstructionOffset()); } void BaselineJIT::endInstruction(Instr::Type instr) diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index 71b9dda9b9..6f066bb9a5 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -214,7 +214,10 @@ public: protected: bool hasLabel() const - { return std::find(labels.cbegin(), labels.cend(), instructionOffset()) != labels.cend(); } + { return std::find(labels.cbegin(), labels.cend(), currentInstructionOffset()) != labels.cend(); } + + int absoluteOffsetForJump(int relativeOffset) const + { return nextInstructionOffset() + relativeOffset; } private: QV4::Function *function; diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 2a8e236905..05a9f70247 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -496,6 +496,38 @@ int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor, return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); } +template<typename T, int metaObjectRevision> +int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor, + const char *qmlName, QQmlCustomParser *parser) +{ + QML_GETTYPENAMES + + QQmlPrivate::RegisterType type = { + 1, + + qRegisterNormalizedMetaType<T *>(pointerName.constData()), + qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()), + sizeof(T), QQmlPrivate::createInto<T>, + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + QQmlPrivate::attachedPropertiesFunc<T>(), + QQmlPrivate::attachedPropertiesMetaObject<T>(), + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + nullptr, nullptr, + + parser, + metaObjectRevision + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + template<typename T, typename E> int qmlRegisterCustomExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, QQmlCustomParser *parser) 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/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index e487aec4ab..26187ca086 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -220,13 +220,15 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int qmlRegisterType<QObject>(uri,versionMajor,versionMinor,"QtObject"); qmlRegisterType<QQmlBind>(uri, versionMajor, versionMinor,"Binding"); qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8 - qmlRegisterType<QQmlConnections,1>(uri, versionMajor, (versionMinor < 3 ? 3 : versionMinor), "Connections"); //Only available in >=2.3 - qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections"); + qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, 0, "Connections", new QQmlConnectionsParser); + if (!strcmp(uri, "QtQuick")) + qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 7, "Connections", new QQmlConnectionsParser); //Only available in QtQuick >=2.7 + else + qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 3, "Connections", new QQmlConnectionsParser); //Only available in QtQml >=2.3 #if QT_CONFIG(qml_animation) qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer"); #endif qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1 - qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser); qmlRegisterType<QQmlInstanceModel>(); qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, 8, "LoggingCategory"); //Only available in >=2.8 diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 88eda9c020..0388215630 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -1360,19 +1360,21 @@ template <typename StringVisitor> int visitEnumerations(const QMetaObject &mo, StringVisitor visitString) { const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data); - const int intsPerEnumerator = 4; + const int intsPerEnumerator = priv->revision >= 8 ? 5 : 4; int fieldCount = priv->enumeratorCount * intsPerEnumerator; for (int i = 0; i < priv->enumeratorCount; ++i) { const uint *enumeratorData = mo.d.data + priv->enumeratorData + i * intsPerEnumerator; - const uint keyCount = enumeratorData[2]; + const uint keyCount = enumeratorData[intsPerEnumerator == 5 ? 3 : 2]; fieldCount += keyCount * 2; visitString(enumeratorData[0]); // name + if (intsPerEnumerator == 5) + visitString(enumeratorData[1]); // enum name - const uint keyOffset = enumeratorData[3]; + const uint keyOffset = enumeratorData[intsPerEnumerator == 5 ? 4 : 3]; for (uint j = 0; j < keyCount; ++j) { visitString(mo.d.data[keyOffset + 2 * j]); @@ -1412,7 +1414,7 @@ bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fie int *stringCount) { const QMetaObjectPrivate *priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data); - if (priv->revision != 7) { + if (priv->revision < 7 || priv->revision > 8) { return false; } diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 270414a676..2b21591017 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -67,8 +67,7 @@ struct QQmlValueTypeFactoryImpl QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl() { - for (unsigned int ii = 0; ii < QVariant::UserType; ++ii) - valueTypes[ii] = nullptr; + std::fill_n(valueTypes, int(QVariant::UserType), nullptr); // See types wrapped in qqmlmodelindexvaluetype_p.h qRegisterMetaType<QItemSelectionRange>(); @@ -521,38 +520,33 @@ void QQmlEasingValueType::setBezierCurve(const QVariantList &customCurveVariant) if (customCurveVariant.isEmpty()) return; - QVariantList variantList = customCurveVariant; - if ((variantList.count() % 6) == 0) { - bool allRealsOk = true; - QVector<qreal> reals; - const int variantListCount = variantList.count(); - reals.reserve(variantListCount); - for (int i = 0; i < variantListCount; i++) { - bool ok; - const qreal real = variantList.at(i).toReal(&ok); - reals.append(real); - if (!ok) - allRealsOk = false; - } - if (allRealsOk) { - QEasingCurve newEasingCurve(QEasingCurve::BezierSpline); - for (int i = 0; i < reals.count() / 6; i++) { - const qreal c1x = reals.at(i * 6); - const qreal c1y = reals.at(i * 6 + 1); - const qreal c2x = reals.at(i * 6 + 2); - const qreal c2y = reals.at(i * 6 + 3); - const qreal c3x = reals.at(i * 6 + 4); - const qreal c3y = reals.at(i * 6 + 5); - - const QPointF c1(c1x, c1y); - const QPointF c2(c2x, c2y); - const QPointF c3(c3x, c3y); - - newEasingCurve.addCubicBezierSegment(c1, c2, c3); - v = newEasingCurve; - } - } + if ((customCurveVariant.count() % 6) != 0) + return; + + auto convert = [](const QVariant &v, qreal &r) { + bool ok; + r = v.toReal(&ok); + return ok; + }; + + QEasingCurve newEasingCurve(QEasingCurve::BezierSpline); + for (int i = 0, ei = customCurveVariant.size(); i < ei; i += 6) { + qreal c1x, c1y, c2x, c2y, c3x, c3y; + if (!convert(customCurveVariant.at(i ), c1x)) return; + if (!convert(customCurveVariant.at(i + 1), c1y)) return; + if (!convert(customCurveVariant.at(i + 2), c2x)) return; + if (!convert(customCurveVariant.at(i + 3), c2y)) return; + if (!convert(customCurveVariant.at(i + 4), c3x)) return; + if (!convert(customCurveVariant.at(i + 5), c3y)) return; + + const QPointF c1(c1x, c1y); + const QPointF c2(c2x, c2y); + const QPointF c3(c3x, c3y); + + newEasingCurve.addCubicBezierSegment(c1, c2, c3); } + + v = newEasingCurve; } QVariantList QQmlEasingValueType::bezierCurve() const diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index 6b063d5602..bd03d7e152 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -69,7 +69,7 @@ class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatu Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) - Q_REVISION(1) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged REVISION 1) Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) public: 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..b5e0881078 100644 --- a/src/qml/util/qqmladaptormodel_p.h +++ b/src/qml/util/qqmladaptormodel_p.h @@ -54,7 +54,7 @@ #include <QtCore/qabstractitemmodel.h> #include "private/qqmllistaccessor_p.h" - +#include <private/qqmlglobal_p.h> #include <private/qqmlguard_p.h> #include <private/qqmlnullablevalue_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()); } |