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 | |
parent | 36ea464b3dcce6319c5252553d48dd5ac6ba8f41 (diff) | |
parent | e47bd1fc0e6fc2b2e3ca82c48ccb2f8f7dd5ee92 (diff) |
Merge dev into 5.12
Change-Id: Ib4dd56aa31ea0f2026b61dbae061d0b333426d0f
Diffstat (limited to 'src')
42 files changed, 1390 insertions, 249 deletions
diff --git a/src/3rdparty/masm/wtf/Assertions.h b/src/3rdparty/masm/wtf/Assertions.h index e2c04ac0bb..be25d43826 100644 --- a/src/3rdparty/masm/wtf/Assertions.h +++ b/src/3rdparty/masm/wtf/Assertions.h @@ -171,7 +171,7 @@ WTF_EXPORT_PRIVATE void WTFInstallReportBacktraceOnCrashHook(); #define CRASH() \ (WTFReportBacktrace(), \ WTFInvokeCrashHook(), \ - (*reinterpret_cast<int *>(uintptr_t(0xbbadbeef)) = 0), \ + (*(int *)(uintptr_t)0xbbadbeef = 0), \ __builtin_trap()) #else #define CRASH() \ @@ -256,7 +256,7 @@ inline void assertUnused(T& x) { (void)x; } (void)0) #define ASSERT_NOT_REACHED() do { \ - WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, nullptr); \ + WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, NULL); \ CRASH(); \ } while (0) 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/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h index b1f7053c36..d0b104dfad 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -72,7 +72,7 @@ class QV4DebugServiceImpl : public QQmlConfigurableDebugService<QV4DebugService> { Q_OBJECT public: - explicit QV4DebugServiceImpl(QObject *parent = 0); + explicit QV4DebugServiceImpl(QObject *parent = nullptr); ~QV4DebugServiceImpl() override; void engineAdded(QJSEngine *engine) override; 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()); } diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp index ef6fb4eb7e..4651357403 100644 --- a/src/quick/handlers/qquickhoverhandler.cpp +++ b/src/quick/handlers/qquickhoverhandler.cpp @@ -79,7 +79,7 @@ void QQuickHoverHandler::componentComplete() bool QQuickHoverHandler::wantsPointerEvent(QQuickPointerEvent *event) { QQuickEventPoint *point = event->point(0); - if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(point)) { + if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(point) && parentContains(point)) { // assume this is a mouse event, so there's only one point setPointId(point->pointId()); return true; diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 83d383d7c0..c2efebf3d8 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1515,6 +1515,8 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) d->movementEndingTimer.start(MovementEndingTimerInterval, this); #endif return; + default: + return; } if (event->source() == Qt::MouseEventNotSynthesized || event->pixelDelta().isNull()) { diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 6b5d595c26..8e2d32a818 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2566,8 +2566,19 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo from = item->parentItem(); } bool skip = false; - QQuickItem * startItem = item; - QQuickItem * firstFromItem = from; + + QQuickItem *startItem = item; + // Protect from endless loop: + // If we start on an invisible item we will not find it again. + // If there is no other item which can become the focus item, we have a forever loop, + // since the protection only works if we encounter the first item again. + while (startItem && !startItem->isVisible()) { + startItem = startItem->parentItem(); + } + if (!startItem) + return item; + + QQuickItem *firstFromItem = from; QQuickItem *current = item; qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem; qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem; diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index bb63bc1ee4..24941ab264 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -331,12 +331,26 @@ public: Q_DECLARE_FLAGS(ChangeTypes, ChangeType) struct ChangeListener { - ChangeListener(QQuickItemChangeListener *l = nullptr, QQuickItemPrivate::ChangeTypes t = nullptr) : listener(l), types(t), gTypes(QQuickGeometryChange::All) {} - ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt) : listener(l), types(Geometry), gTypes(gt) {} + using ChangeTypes = QQuickItemPrivate::ChangeTypes; + + ChangeListener(QQuickItemChangeListener *l = nullptr, ChangeTypes t = nullptr) + : listener(l) + , types(t) + , gTypes(QQuickGeometryChange::All) + {} + + ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt) + : listener(l) + , types(Geometry) + , gTypes(gt) + {} + + bool operator==(const ChangeListener &other) const + { return listener == other.listener && types == other.types; } + QQuickItemChangeListener *listener; - QQuickItemPrivate::ChangeTypes types; + ChangeTypes types; QQuickGeometryChange gTypes; //NOTE: not used for == - bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; } }; struct ExtraData { diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 3925fe95cf..8dce0149f6 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -59,7 +59,6 @@ Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle") #define Q_TABLEVIEW_ASSERT(cond, output) Q_ASSERT((cond) || [&](){ dumpTable(); qWarning() << "output:" << output; return false;}()) static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEdge, Qt::BottomEdge }; -static const int kBufferTimerInterval = 300; static QLine rectangleEdge(const QRect &rect, Qt::Edge tableEdge) { @@ -99,8 +98,6 @@ const QPoint QQuickTableViewPrivate::kDown = QPoint(0, 1); QQuickTableViewPrivate::QQuickTableViewPrivate() : QQuickFlickablePrivate() { - cacheBufferDelayTimer.setSingleShot(true); - QObject::connect(&cacheBufferDelayTimer, &QTimer::timeout, [=]{ loadBuffer(); }); } QQuickTableViewPrivate::~QQuickTableViewPrivate() @@ -463,19 +460,19 @@ bool QQuickTableViewPrivate::canUnloadTableEdge(Qt::Edge tableEdge, const QRectF case Qt::LeftEdge: if (loadedTable.width() <= 1) return false; - return loadedTableInnerRect.left() < fillRect.left(); + return loadedTableInnerRect.left() <= fillRect.left(); case Qt::RightEdge: if (loadedTable.width() <= 1) return false; - return loadedTableInnerRect.right() > fillRect.right(); + return loadedTableInnerRect.right() >= fillRect.right(); case Qt::TopEdge: if (loadedTable.height() <= 1) return false; - return loadedTableInnerRect.top() < fillRect.top(); + return loadedTableInnerRect.top() <= fillRect.top(); case Qt::BottomEdge: if (loadedTable.height() <= 1) return false; - return loadedTableInnerRect.bottom() > fillRect.bottom(); + return loadedTableInnerRect.bottom() >= fillRect.bottom(); } Q_TABLEVIEW_UNREACHABLE(tableEdge); return false; @@ -808,7 +805,7 @@ void QQuickTableViewPrivate::cancelLoadRequest() loadRequest.markAsDone(); model->cancel(modelIndexAtCell(loadRequest.currentCell())); - if (tableInvalid) { + if (rebuildState == RebuildState::NotStarted) { // No reason to rollback already loaded edge items // since we anyway are about to reload all items. return; @@ -860,13 +857,77 @@ void QQuickTableViewPrivate::processLoadRequest() qCDebug(lcTableViewDelegateLifecycle()) << "request completed! Table:" << tableLayoutToString(); } +void QQuickTableViewPrivate::processRebuildTable() +{ + moveToNextRebuildState(); + + if (rebuildState == RebuildState::LoadInitalTable) { + beginRebuildTable(); + if (!moveToNextRebuildState()) + return; + } + + if (rebuildState == RebuildState::VerifyTable) { + if (loadedItems.isEmpty()) { + qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model or no delegate"; + rebuildState = RebuildState::Done; + return; + } + if (!moveToNextRebuildState()) + return; + } + + if (rebuildState == RebuildState::LayoutTable) { + layoutAfterLoadingInitialTable(); + if (!moveToNextRebuildState()) + return; + } + + if (rebuildState == RebuildState::LoadAndUnloadAfterLayout) { + loadAndUnloadVisibleEdges(); + if (!moveToNextRebuildState()) + return; + } + + if (rebuildState == RebuildState::PreloadColumns) { + if (loadedTable.right() < tableSize.width() - 1) + loadEdge(Qt::RightEdge, QQmlIncubator::AsynchronousIfNested); + if (!moveToNextRebuildState()) + return; + } + + if (rebuildState == RebuildState::PreloadRows) { + if (loadedTable.bottom() < tableSize.height() - 1) + loadEdge(Qt::BottomEdge, QQmlIncubator::AsynchronousIfNested); + if (!moveToNextRebuildState()) + return; + } + + if (rebuildState == RebuildState::MovePreloadedItemsToPool) { + while (Qt::Edge edge = nextEdgeToUnload(viewportRect)) + unloadEdge(edge); + if (!moveToNextRebuildState()) + return; + } + + Q_TABLEVIEW_ASSERT(rebuildState == RebuildState::Done, int(rebuildState)); +} + +bool QQuickTableViewPrivate::moveToNextRebuildState() +{ + if (loadRequest.isActive()) { + // Items are still loading async, which means + // that the current state is not yet done. + return false; + } + rebuildState = RebuildState(int(rebuildState) + 1); + qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState); + return true; +} + void QQuickTableViewPrivate::beginRebuildTable() { Q_Q(QQuickTableView); - qCDebug(lcTableViewDelegateLifecycle()); - - tableInvalid = false; - tableRebuilding = true; releaseLoadedItems(); loadedTable = QRect(); @@ -882,21 +943,16 @@ void QQuickTableViewPrivate::beginRebuildTable() loadAndUnloadVisibleEdges(); } -void QQuickTableViewPrivate::endRebuildTable() +void QQuickTableViewPrivate::layoutAfterLoadingInitialTable() { - tableRebuilding = false; - - if (rowHeightProvider.isNull() && columnWidthProvider.isNull()) { - // Since we have no size providers, we need to calculate the size - // of each row and column based on the size of the delegate items. + if (rowHeightProvider.isNull() || columnWidthProvider.isNull()) { + // Since we don't have both size providers, we need to calculate the + // size of each row and column based on the size of the delegate items. // This couldn't be done while we were loading the initial rows and // columns, since during the process, we didn't have all the items - // available yet for the calculation. So we mark that it needs to be - // done now, from within updatePolish(). - columnRowPositionsInvalid = true; + // available yet for the calculation. So we do it now. + relayoutTable(); } - - qCDebug(lcTableViewDelegateLifecycle()) << tableLayoutToString(); } void QQuickTableViewPrivate::loadInitialTopLeftItem() @@ -958,13 +1014,12 @@ void QQuickTableViewPrivate::loadAndUnloadVisibleEdges() return; } - const QRectF unloadRect = hasBufferedItems ? bufferRect() : viewportRect; bool tableModified; do { tableModified = false; - if (Qt::Edge edge = nextEdgeToUnload(unloadRect)) { + if (Qt::Edge edge = nextEdgeToUnload(viewportRect)) { tableModified = true; unloadEdge(edge); } @@ -1023,46 +1078,8 @@ void QQuickTableViewPrivate::drainReusePoolAfterLoadRequest() tableModel->drainReusableItemsPool(maxTime); } -void QQuickTableViewPrivate::loadBuffer() -{ - // Rather than making sure to stop the timer from all locations that can - // violate the "buffering allowed" state, we just check that we're in the - // right state here before we start buffering. - if (cacheBuffer <= 0 || loadRequest.isActive() || loadedItems.isEmpty()) - return; - - qCDebug(lcTableViewDelegateLifecycle()); - const QRectF loadRect = bufferRect(); - while (Qt::Edge edge = nextEdgeToLoad(loadRect)) { - loadEdge(edge, QQmlIncubator::Asynchronous); - if (loadRequest.isActive()) - break; - } - - hasBufferedItems = true; -} - -void QQuickTableViewPrivate::unloadBuffer() -{ - if (!hasBufferedItems) - return; - - qCDebug(lcTableViewDelegateLifecycle()); - hasBufferedItems = false; - cacheBufferDelayTimer.stop(); - if (loadRequest.isActive()) - cancelLoadRequest(); - while (Qt::Edge edge = nextEdgeToUnload(viewportRect)) - unloadEdge(edge); -} - -QRectF QQuickTableViewPrivate::bufferRect() -{ - return viewportRect.adjusted(-cacheBuffer, -cacheBuffer, cacheBuffer, cacheBuffer); -} - void QQuickTableViewPrivate::invalidateTable() { - tableInvalid = true; + rebuildState = RebuildState::NotStarted; if (loadRequest.isActive()) cancelLoadRequest(); q_func()->polish(); @@ -1098,41 +1115,18 @@ void QQuickTableViewPrivate::updatePolish() if (!viewportRect.isValid()) return; - if (tableInvalid) { - beginRebuildTable(); - if (loadRequest.isActive()) - return; + if (rebuildState != RebuildState::Done) { + processRebuildTable(); + return; } - if (tableRebuilding) - endRebuildTable(); - - if (loadedItems.isEmpty()) { - qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model or no delegate"; + if (loadedItems.isEmpty()) return; - } if (columnRowPositionsInvalid) relayoutTable(); - if (hasBufferedItems && nextEdgeToLoad(viewportRect)) { - // We are about to load more edges, so trim down the table as much - // as possible to avoid loading cells that are outside the viewport. - unloadBuffer(); - } - loadAndUnloadVisibleEdges(); - - if (loadRequest.isActive()) - return; - - if (cacheBuffer > 0) { - // When polish hasn't been called for a while (which means that the viewport - // rect hasn't changed), we start buffering items. We delay this operation by - // using a timer to increase performance (by not loading hidden items) while - // the user is flicking. - cacheBufferDelayTimer.start(kBufferTimerInterval); - } } void QQuickTableViewPrivate::createWrapperModel() @@ -1558,7 +1552,8 @@ qreal QQuickTableView::explicitContentWidth() const { Q_D(const QQuickTableView); - if (d->tableInvalid && d->explicitContentWidth.isNull) { + if (d->rebuildState == QQuickTableViewPrivate::RebuildState::NotStarted + && d->explicitContentWidth.isNull) { // The table is pending to be rebuilt. Since we don't // know the contentWidth before this is done, we do the // rebuild now, instead of waiting for the polish event. @@ -1582,7 +1577,8 @@ qreal QQuickTableView::explicitContentHeight() const { Q_D(const QQuickTableView); - if (d->tableInvalid && d->explicitContentHeight.isNull) { + if (d->rebuildState == QQuickTableViewPrivate::RebuildState::NotStarted + && d->explicitContentHeight.isNull) { // The table is pending to be rebuilt. Since we don't // know the contentHeight before this is done, we do the // rebuild now, instead of waiting for the polish event. diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index 5fb5a7a9fd..53fd936195 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -66,7 +66,6 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle) -static const int kDefaultCacheBuffer = 300; static const qreal kDefaultRowHeight = 50; static const qreal kDefaultColumnWidth = 50; @@ -164,6 +163,18 @@ public: } }; + enum class RebuildState { + NotStarted = 0, + LoadInitalTable, + VerifyTable, + LayoutTable, + LoadAndUnloadAfterLayout, + PreloadColumns, + PreloadRows, + MovePreloadedItemsToPool, + Done + }; + public: QQuickTableViewPrivate(); ~QQuickTableViewPrivate() override; @@ -197,21 +208,16 @@ public: QSize tableSize; + RebuildState rebuildState = RebuildState::NotStarted; TableEdgeLoadRequest loadRequest; QPoint contentSizeBenchMarkPoint = QPoint(-1, -1); QSizeF cellSpacing = QSizeF(0, 0); QMarginsF tableMargins; - int cacheBuffer = kDefaultCacheBuffer; - QTimer cacheBufferDelayTimer; - bool hasBufferedItems = false; - QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable; bool blockItemCreatedCallback = false; - bool tableInvalid = false; - bool tableRebuilding = false; bool columnRowPositionsInvalid = false; bool layoutWarningIssued = false; bool polishing = false; @@ -294,12 +300,11 @@ public: void drainReusePoolAfterLoadRequest(); void cancelLoadRequest(); void processLoadRequest(); - void beginRebuildTable(); - void endRebuildTable(); - void loadBuffer(); - void unloadBuffer(); - QRectF bufferRect(); + void processRebuildTable(); + bool moveToNextRebuildState(); + void beginRebuildTable(); + void layoutAfterLoadingInitialTable(); void invalidateTable(); void invalidateColumnRowPositions(); diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 46af0f28f0..c99e149aa5 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -82,19 +82,26 @@ QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache() { } +QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::emptyData(glyph_t glyph) +{ + GlyphData gd; + gd.texture = &s_emptyTexture; + QHash<glyph_t, GlyphData>::iterator it = m_glyphsData.insert(glyph, gd); + return it.value(); +} + QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::glyphData(glyph_t glyph) { QHash<glyph_t, GlyphData>::iterator data = m_glyphsData.find(glyph); if (data == m_glyphsData.end()) { - GlyphData gd; - gd.texture = &s_emptyTexture; + GlyphData &gd = emptyData(glyph); gd.path = m_referenceFont.pathForGlyph(glyph); // need bounding rect in base font size scale qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution); QTransform scaleDown; scaleDown.scale(scaleFactor, scaleFactor); gd.boundingRect = scaleDown.mapRect(gd.path.boundingRect()); - data = m_glyphsData.insert(glyph, gd); + return gd; } return data.value(); } diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 5a465831cb..ba5c4353b2 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -83,6 +83,7 @@ class QSGGlyphNode; class QSGRootNode; class QSGSpriteNode; class QSGRenderNode; +class QSGRenderContext; class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx { @@ -507,6 +508,7 @@ protected: uint textureIdForGlyph(glyph_t glyph) const; GlyphData &glyphData(glyph_t glyph); + GlyphData &emptyData(glyph_t glyph); #if defined(QSG_DISTANCEFIELD_CACHE_DEBUG) void saveTexture(GLuint textureId, int width, int height) const; @@ -514,11 +516,14 @@ protected: inline bool isCoreProfile() const { return m_coreProfile; } -private: + bool m_doubleGlyphResolution; + +protected: QRawFont m_referenceFont; + +private: int m_glyphCount; - bool m_doubleGlyphResolution; bool m_coreProfile; QList<Texture> m_textures; diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index ef189ba461..ccc57b0b86 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -39,12 +39,18 @@ #include "qsgdefaultdistancefieldglyphcache_p.h" +#include <QtCore/qelapsedtimer.h> +#include <QtCore/qbuffer.h> +#include <QtQml/qqmlfile.h> + #include <QtGui/private/qdistancefield_p.h> #include <QtGui/private/qopenglcontext_p.h> #include <QtQml/private/qqmlglobal_p.h> #include <qopenglfunctions.h> #include <qopenglframebufferobject.h> #include <qmath.h> +#include "qsgcontext_p.h" + #if !defined(QT_OPENGL_ES_2) #include <QtGui/qopenglfunctions_3_2_core.h> @@ -59,10 +65,12 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI # define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2 #endif -QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) +QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, + const QRawFont &font) : QSGDistanceFieldGlyphCache(c, font) , m_maxTextureSize(0) , m_maxTextureCount(3) + , m_areaAllocator(nullptr) , m_blitProgram(nullptr) , m_blitBuffer(QOpenGLBuffer::VertexBuffer) , m_fboGuard(nullptr) @@ -81,7 +89,8 @@ QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLCont qWarning("Buffer creation failed"); } - m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize())); + // Load a pregenerated cache if the font contains one + loadPregeneratedCache(font); } QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache() @@ -101,6 +110,9 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph QList<GlyphPosition> glyphPositions; QVector<glyph_t> glyphsToRender; + if (m_areaAllocator == nullptr) + m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize())); + for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) { glyph_t glyphIndex = *it; @@ -237,10 +249,23 @@ void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyph m_unusedGlyphs += glyphs; } -void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int width, int height) +void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, + int width, + int height) +{ + QByteArray zeroBuf(width * height, 0); + createTexture(texInfo, width, height, zeroBuf.constData()); +} + +void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, + int width, + int height, + const void *pixels) { - if (useTextureResizeWorkaround() && texInfo->image.isNull()) + if (useTextureResizeWorkaround() && texInfo->image.isNull()) { texInfo->image = QDistanceField(width, height); + memcpy(texInfo->image.bits(), pixels, width * height); + } while (m_funcs->glGetError() != GL_NO_ERROR) { } @@ -261,8 +286,7 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int const GLenum format = GL_ALPHA; #endif - QByteArray zeroBuf(width * height, 0); - m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, zeroBuf.constData()); + m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, pixels); texInfo->size = QSize(width, height); @@ -520,4 +544,261 @@ int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const return m_maxTextureSize; } +namespace { + struct Qtdf { + // We need these structs to be tightly packed, but some compilers we use do not + // support #pragma pack(1), so we need to hardcode the offsets/sizes in the + // file format + enum TableSize { + HeaderSize = 14, + GlyphRecordSize = 46, + TextureRecordSize = 17 + }; + + enum Offset { + // Header + majorVersion = 0, + minorVersion = 1, + pixelSize = 2, + textureSize = 4, + flags = 8, + headerPadding = 9, + numGlyphs = 10, + + // Glyph record + glyphIndex = 0, + textureOffsetX = 4, + textureOffsetY = 8, + textureWidth = 12, + textureHeight = 16, + xMargin = 20, + yMargin = 24, + boundingRectX = 28, + boundingRectY = 32, + boundingRectWidth = 36, + boundingRectHeight = 40, + textureIndex = 44, + + // Texture record + allocatedX = 0, + allocatedY = 4, + allocatedWidth = 8, + allocatedHeight = 12, + texturePadding = 16 + + }; + + template <typename T> + static inline T fetch(const char *data, Offset offset) + { + return qFromBigEndian<T>(data + int(offset)); + } + }; +} + +bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font) +{ + // The pregenerated data must be loaded first, otherwise the area allocator + // will be wrong + if (m_areaAllocator != nullptr) { + qWarning("Font cache must be loaded before cache is used"); + return false; + } + + static QElapsedTimer timer; + + bool profile = QSG_LOG_TIME_GLYPH().isDebugEnabled(); + if (profile) + timer.start(); + + QByteArray qtdfTable = font.fontTable("qtdf"); + if (qtdfTable.isEmpty()) + return false; + + typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash; + + GlyphTextureHash glyphTextures; + + if (uint(qtdfTable.size()) < Qtdf::HeaderSize) { + qWarning("Invalid qtdf table in font '%s'", + qPrintable(font.familyName())); + return false; + } + + const char *qtdfTableStart = qtdfTable.constData(); + const char *qtdfTableEnd = qtdfTableStart + qtdfTable.size(); + + int padding = 0; + int textureCount = 0; + { + quint8 majorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::majorVersion); + quint8 minorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::minorVersion); + if (majorVersion != 5 || minorVersion != 12) { + qWarning("Invalid version of qtdf table %d.%d in font '%s'", + majorVersion, + minorVersion, + qPrintable(font.familyName())); + return false; + } + + qreal pixelSize = qreal(Qtdf::fetch<quint16>(qtdfTableStart, Qtdf::pixelSize)); + m_maxTextureSize = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize); + m_doubleGlyphResolution = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::flags) == 1; + padding = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::headerPadding); + + if (pixelSize <= 0.0) { + qWarning("Invalid pixel size in '%s'", qPrintable(font.familyName())); + return false; + } + + if (m_maxTextureSize <= 0) { + qWarning("Invalid texture size in '%s'", qPrintable(font.familyName())); + return false; + } + + int systemMaxTextureSize; + m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &systemMaxTextureSize); + + if (m_maxTextureSize > systemMaxTextureSize) { + qWarning("System maximum texture size is %d. This is lower than the value in '%s', which is %d", + systemMaxTextureSize, + qPrintable(font.familyName()), + m_maxTextureSize); + } + + if (padding != QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING) { + qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.", + qPrintable(font.familyName()), + padding, + QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING); + } + + m_referenceFont.setPixelSize(pixelSize); + + quint32 glyphCount = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::numGlyphs); + m_unusedGlyphs.reserve(glyphCount); + + const char *allocatorData = qtdfTableStart + Qtdf::HeaderSize; + { + m_areaAllocator = new QSGAreaAllocator(QSize(0, 0)); + allocatorData = m_areaAllocator->deserialize(allocatorData, qtdfTableEnd - allocatorData); + if (allocatorData == nullptr) + return false; + } + + if (m_areaAllocator->size().height() % m_maxTextureSize != 0) { + qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName())); + return false; + } + + textureCount = m_areaAllocator->size().height() / m_maxTextureSize; + m_maxTextureCount = qMax(m_maxTextureCount, textureCount); + + const char *textureRecord = allocatorData; + for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) { + if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) { + qWarning("qtdf table too small in font '%s'.", + qPrintable(font.familyName())); + return false; + } + + TextureInfo *tex = textureInfo(i); + tex->allocatedArea.setX(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedX)); + tex->allocatedArea.setY(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedY)); + tex->allocatedArea.setWidth(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedWidth)); + tex->allocatedArea.setHeight(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedHeight)); + tex->padding = Qtdf::fetch<quint8>(textureRecord, Qtdf::texturePadding); + } + + const char *glyphRecord = textureRecord; + for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) { + if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) { + qWarning("qtdf table too small in font '%s'.", + qPrintable(font.familyName())); + return false; + } + + glyph_t glyph = Qtdf::fetch<quint32>(glyphRecord, Qtdf::glyphIndex); + m_unusedGlyphs.insert(glyph); + + GlyphData &glyphData = emptyData(glyph); + +#define FROM_FIXED_POINT(value) \ +(((qreal)value)/(qreal)65536) + + glyphData.texCoord.x = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetX)); + glyphData.texCoord.y = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetY)); + glyphData.texCoord.width = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureWidth)); + glyphData.texCoord.height = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureHeight)); + glyphData.texCoord.xMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::xMargin)); + glyphData.texCoord.yMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::yMargin)); + glyphData.boundingRect.setX(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectX))); + glyphData.boundingRect.setY(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectY))); + glyphData.boundingRect.setWidth(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectWidth))); + glyphData.boundingRect.setHeight(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectHeight))); + +#undef FROM_FIXED_POINT + + int textureIndex = Qtdf::fetch<quint16>(glyphRecord, Qtdf::textureIndex); + if (textureIndex < 0 || textureIndex >= textureCount) { + qWarning("Invalid texture index %d (texture count == %d) in '%s'", + textureIndex, + textureCount, + qPrintable(font.familyName())); + return false; + } + + + TextureInfo *texInfo = textureInfo(textureIndex); + m_glyphsTexture.insert(glyph, texInfo); + + glyphTextures[texInfo].append(glyph); + } + + GLint alignment = 4; // default value + m_funcs->glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment); + + m_funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + const uchar *textureData = reinterpret_cast<const uchar *>(glyphRecord); + for (int i = 0; i < textureCount; ++i) { + + TextureInfo *texInfo = textureInfo(i); + + int width = texInfo->allocatedArea.width(); + int height = texInfo->allocatedArea.height(); + qint64 size = width * height; + if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) { + qWarning("qtdf table too small in font '%s'.", + qPrintable(font.familyName())); + return false; + } + + createTexture(texInfo, width, height, textureData); + + QVector<glyph_t> glyphs = glyphTextures.value(texInfo); + + Texture t; + t.textureId = texInfo->texture; + t.size = texInfo->size; + + setGlyphsTexture(glyphs, t); + + textureData += size; + } + + m_funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); + } + + if (profile) { + quint64 now = timer.elapsed(); + qCDebug(QSG_LOG_TIME_GLYPH, + "distancefield: %d pre-generated glyphs loaded in %dms", + m_unusedGlyphs.size(), + (int) now); + } + + return true; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h index 76c0d20647..a0e4387af9 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h @@ -85,7 +85,10 @@ public: void setMaxTextureCount(int max) { m_maxTextureCount = max; } int maxTextureCount() const { return m_maxTextureCount; } + private: + bool loadPregeneratedCache(const QRawFont &font); + struct TextureInfo { GLuint texture; QSize size; @@ -96,6 +99,7 @@ private: TextureInfo(const QRect &preallocRect = QRect()) : texture(0), allocatedArea(preallocRect) { } }; + void createTexture(TextureInfo * texInfo, int width, int height, const void *pixels); void createTexture(TextureInfo * texInfo, int width, int height); void resizeTexture(TextureInfo * texInfo, int width, int height); diff --git a/src/quick/scenegraph/util/qsgareaallocator.cpp b/src/quick/scenegraph/util/qsgareaallocator.cpp index cd270a1d63..9a8c8e333b 100644 --- a/src/quick/scenegraph/util/qsgareaallocator.cpp +++ b/src/quick/scenegraph/util/qsgareaallocator.cpp @@ -42,6 +42,9 @@ #include <QtCore/qglobal.h> #include <QtCore/qrect.h> #include <QtCore/qpoint.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qstack.h> +#include <QtCore/qendian.h> QT_BEGIN_NAMESPACE @@ -285,4 +288,145 @@ void QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node) } // end while(!done) } +namespace { + struct AreaAllocatorTable + { + enum TableSize { + HeaderSize = 10, + NodeSize = 9 + }; + + enum Offset { + // Header + majorVersion = 0, + minorVersion = 1, + width = 2, + height = 6, + + // Node + split = 0, + splitType = 4, + flags = 8 + }; + + enum Flags { + IsOccupied = 1, + HasLeft = 2, + HasRight = 4 + }; + + template <typename T> + static inline T fetch(const char *data, Offset offset) + { + return qFromBigEndian<T>(data + int(offset)); + } + + template <typename T> + static inline void put(char *data, Offset offset, T value) + { + qToBigEndian(value, data + int(offset)); + } + }; +} + +QByteArray QSGAreaAllocator::serialize() +{ + QVarLengthArray<QSGAreaAllocatorNode *> nodesToProcess; + + QStack<QSGAreaAllocatorNode *> nodes; + nodes.push(m_root); + while (!nodes.isEmpty()) { + QSGAreaAllocatorNode *node = nodes.pop(); + + nodesToProcess.append(node); + if (node->left != nullptr) + nodes.push(node->left); + if (node->right != nullptr) + nodes.push(node->right); + } + + QByteArray ret; + ret.resize(AreaAllocatorTable::HeaderSize + AreaAllocatorTable::NodeSize * nodesToProcess.size()); + + char *data = ret.data(); + AreaAllocatorTable::put(data, AreaAllocatorTable::majorVersion, quint8(5)); + AreaAllocatorTable::put(data, AreaAllocatorTable::minorVersion, quint8(12)); + AreaAllocatorTable::put(data, AreaAllocatorTable::width, quint32(m_size.width())); + AreaAllocatorTable::put(data, AreaAllocatorTable::height, quint32(m_size.height())); + + data += AreaAllocatorTable::HeaderSize; + for (QSGAreaAllocatorNode *node : nodesToProcess) { + AreaAllocatorTable::put(data, AreaAllocatorTable::split, qint32(node->split)); + AreaAllocatorTable::put(data, AreaAllocatorTable::splitType, quint32(node->splitType)); + + quint8 flags = + (node->isOccupied ? AreaAllocatorTable::IsOccupied : 0) + | (node->left != nullptr ? AreaAllocatorTable::HasLeft : 0) + | (node->right != nullptr ? AreaAllocatorTable::HasRight : 0); + AreaAllocatorTable::put(data, AreaAllocatorTable::flags, flags); + data += AreaAllocatorTable::NodeSize; + } + + return ret; +} + +const char *QSGAreaAllocator::deserialize(const char *data, int size) +{ + if (uint(size) < AreaAllocatorTable::HeaderSize) { + qWarning("QSGAreaAllocator::deserialize: Data not long enough to fit header"); + return nullptr; + } + + const char *end = data + size; + + quint8 majorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::majorVersion); + quint8 minorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::minorVersion); + if (majorVersion != 5 || minorVersion != 12) { + qWarning("Unrecognized version %d.%d of QSGAreaAllocator", + majorVersion, + minorVersion); + return nullptr; + } + + m_size = QSize(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::width), + AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::height)); + + Q_ASSERT(m_root != nullptr); + Q_ASSERT(m_root->left == nullptr); + Q_ASSERT(m_root->right == nullptr); + + QStack<QSGAreaAllocatorNode *> nodes; + nodes.push(m_root); + + data += AreaAllocatorTable::HeaderSize; + while (!nodes.isEmpty()) { + if (data + AreaAllocatorTable::NodeSize > end) { + qWarning("QSGAreaAllocator::deseriable: Data not long enough for nodes"); + return nullptr; + } + + QSGAreaAllocatorNode *node = nodes.pop(); + + node->split = AreaAllocatorTable::fetch<qint32>(data, AreaAllocatorTable::split); + node->splitType = SplitType(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::splitType)); + + quint8 flags = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::flags); + node->isOccupied = flags & AreaAllocatorTable::IsOccupied; + + if (flags & AreaAllocatorTable::HasLeft) { + node->left = new QSGAreaAllocatorNode(node); + nodes.push(node->left); + } + + if (flags & AreaAllocatorTable::HasRight) { + node->right = new QSGAreaAllocatorNode(node); + nodes.push(node->right); + } + + data += AreaAllocatorTable::NodeSize; + } + + return data; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgareaallocator_p.h b/src/quick/scenegraph/util/qsgareaallocator_p.h index 8bc05a5a5b..300a8128c0 100644 --- a/src/quick/scenegraph/util/qsgareaallocator_p.h +++ b/src/quick/scenegraph/util/qsgareaallocator_p.h @@ -69,6 +69,10 @@ public: bool deallocate(const QRect &rect); bool isEmpty() const { return m_root == nullptr; } QSize size() const { return m_size; } + + QByteArray serialize(); + const char *deserialize(const char *data, int size); + private: bool allocateInNode(const QSize &size, QPoint &result, const QRect ¤tRect, QSGAreaAllocatorNode *node); bool deallocateInNode(const QPoint &pos, QSGAreaAllocatorNode *node); diff --git a/src/quick/util/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp index a1367249c6..e51de1a02a 100644 --- a/src/quick/util/qquicktransitionmanager.cpp +++ b/src/quick/util/qquicktransitionmanager.cpp @@ -91,10 +91,10 @@ void QQuickTransitionManager::complete() { d->applyBindings(); - for (int ii = 0; ii < d->completeList.count(); ++ii) { - const QQmlProperty &prop = d->completeList.at(ii).property(); - prop.write(d->completeList.at(ii).value()); - } + // Explicitly take a copy in case the write action triggers a script that modifies the list. + QQuickTransitionManagerPrivate::SimpleActionList completeListCopy = d->completeList; + for (const QQuickSimpleAction &action : qAsConst(completeListCopy)) + action.property().write(action.value()); d->completeList.clear(); @@ -131,7 +131,9 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list, { cancel(); + // The copy below is ON PURPOSE, because firing actions might involve scripts that modify the list. QQuickStateOperation::ActionList applyList = list; + // Determine which actions are binding changes and disable any current bindings for (const QQuickStateAction &action : qAsConst(applyList)) { if (action.toBinding) @@ -156,8 +158,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list, if (transition && !d->bindingsList.isEmpty()) { // Apply all the property and binding changes - for (int ii = 0; ii < applyList.size(); ++ii) { - const QQuickStateAction &action = applyList.at(ii); + for (const QQuickStateAction &action : qAsConst(applyList)) { if (action.toBinding) { QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); } else if (!action.event) { @@ -170,17 +171,15 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list, } } - // Read all the end values for binding changes - for (int ii = 0; ii < applyList.size(); ++ii) { - QQuickStateAction *action = &applyList[ii]; - if (action->event) { - action->event->saveTargetValues(); + // Read all the end values for binding changes. + for (auto it = applyList.begin(), eit = applyList.end(); it != eit; ++it) { + if (it->event) { + it->event->saveTargetValues(); continue; } - const QQmlProperty &prop = action->property; - if (action->toBinding || !action->toValue.isValid()) { - action->toValue = prop.read(); - } + const QQmlProperty &prop = it->property; + if (it->toBinding || !it->toValue.isValid()) + it->toValue = prop.read(); } // Revert back to the original values @@ -210,29 +209,20 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list, delete oldInstance; // Modify the action list to remove actions handled in the transition - for (int ii = 0; ii < applyList.count(); ++ii) { - const QQuickStateAction &action = applyList.at(ii); - + auto isHandledInTransition = [this, touched](const QQuickStateAction &action) { if (action.event) { - - if (action.actionDone) { - applyList.removeAt(ii); - --ii; - } - + return action.actionDone; } else { - if (touched.contains(action.property)) { if (action.toValue != action.fromValue) - d->completeList << - QQuickSimpleAction(action, QQuickSimpleAction::EndState); - - applyList.removeAt(ii); - --ii; + d->completeList << QQuickSimpleAction(action, QQuickSimpleAction::EndState); + return true; } - } - } + return false; + }; + auto newEnd = std::remove_if(applyList.begin(), applyList.end(), isHandledInTransition); + applyList.erase(newEnd, applyList.end()); } // Any actions remaining have not been handled by the transition and should @@ -270,12 +260,9 @@ void QQuickTransitionManager::cancel() if (d->transitionInstance && d->transitionInstance->isRunning()) d->transitionInstance->stop(); - for(int i = 0; i < d->bindingsList.count(); ++i) { - QQuickStateAction action = d->bindingsList[i]; + for (const QQuickStateAction &action : qAsConst(d->bindingsList)) { if (action.toBinding && action.deletableToBinding) { QQmlPropertyPrivate::removeBinding(action.property); - action.toBinding = nullptr; - action.deletableToBinding = false; } else if (action.event) { //### what do we do here? } |