diff options
132 files changed, 3506 insertions, 2145 deletions
diff --git a/.gitignore b/.gitignore index f33da3c8b3..e0af30c299 100644 --- a/.gitignore +++ b/.gitignore @@ -284,3 +284,7 @@ src/qml/udis86_itab.h # Generated HLSL bytecode headers *.hlslh + +# Compiled QML/JS code +*.qmlc +*.jsc diff --git a/examples/quick/demos/demos.pro b/examples/quick/demos/demos.pro index e6937683ab..0644b81a22 100644 --- a/examples/quick/demos/demos.pro +++ b/examples/quick/demos/demos.pro @@ -5,8 +5,7 @@ SUBDIRS = samegame \ tweetsearch \ maroon \ photosurface \ - photoviewer \ stocqt -qtHaveModule(xmlpatterns): SUBDIRS += rssnews +qtHaveModule(xmlpatterns): SUBDIRS += rssnews photoviewer diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri index b5480babc6..fa0d3d3c55 100644 --- a/src/3rdparty/masm/masm-defs.pri +++ b/src/3rdparty/masm/masm-defs.pri @@ -24,7 +24,8 @@ INCLUDEPATH += $$PWD disassembler { if(isEqual(QT_ARCH, "i386")|isEqual(QT_ARCH, "x86_64")): DEFINES += WTF_USE_UDIS86=1 - if(isEqual(QT_ARCH, "arm")): DEFINES += WTF_USE_ARMV7_DISASSEMBLER=1 WTF_USE_ARM64_DISASSEMBLER=1 + if(isEqual(QT_ARCH, "arm")): DEFINES += WTF_USE_ARMV7_DISASSEMBLER=1 + if(isEqual(QT_ARCH, "arm64")): DEFINES += WTF_USE_ARM64_DISASSEMBLER=1 if(isEqual(QT_ARCH, "mips")): DEFINES += WTF_USE_MIPS32_DISASSEMBLER=1 } else { DEFINES += WTF_USE_UDIS86=0 diff --git a/src/3rdparty/masm/wtf/StdLibExtras.h b/src/3rdparty/masm/wtf/StdLibExtras.h index 605f98ec82..f0d792ed52 100644 --- a/src/3rdparty/masm/wtf/StdLibExtras.h +++ b/src/3rdparty/masm/wtf/StdLibExtras.h @@ -166,7 +166,7 @@ template<typename T> char (&ArrayLengthHelperFunction(T (&)[0]))[0]; // Efficient implementation that takes advantage of powers of two. inline size_t roundUpToMultipleOf(size_t divisor, size_t x) { - ASSERT(divisor && !(divisor & (divisor - 1))); + Q_ASSERT(divisor && !(divisor & (divisor - 1))); size_t remainderMask = divisor - 1; return (x + remainderMask) & ~remainderMask; } diff --git a/src/particles/qquickparticleemitter_p.h b/src/particles/qquickparticleemitter_p.h index dd55fdc7a6..b3f96ae3e6 100644 --- a/src/particles/qquickparticleemitter_p.h +++ b/src/particles/qquickparticleemitter_p.h @@ -62,7 +62,7 @@ #include <QPointF> QT_BEGIN_NAMESPACE -class QQuickParticleEmitter : public QQuickItem +class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleEmitter : public QQuickItem { Q_OBJECT Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index 12ed987ca0..f72b8a51f9 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -753,7 +753,7 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth if (!prop || !prop->isVMEFunction()) return false; - QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex); + QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex()); QList<QByteArray> paramNames = metaMethod.parameterNames(); QString paramStr; @@ -772,12 +772,12 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth QV4::Scope scope(v4); int lineNumber = 0; - QV4::ScopedFunctionObject oldMethod(scope, vmeMetaObject->vmeMethod(prop->coreIndex)); + QV4::ScopedFunctionObject oldMethod(scope, vmeMetaObject->vmeMethod(prop->coreIndex())); if (oldMethod && oldMethod->d()->function) { lineNumber = oldMethod->d()->function->compiledFunction->location.line; } QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber)); - vmeMetaObject->setVmeMethod(prop->coreIndex, v); + vmeMetaObject->setVmeMethod(prop->coreIndex(), v); return true; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index e32ecdc138..87215d5b54 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -75,7 +75,7 @@ DECLARE_DEBUG_VAR(buffer) DECLARE_DEBUG_VAR(texture) // Except for system info on startup. -Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.general") +Q_LOGGING_CATEGORY(QSG_LOG_INFO_GENERAL, "qt.scenegraph.general") // Any changes to the defaults below must be reflected in adaptations.qdoc as @@ -197,7 +197,7 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte DXGI_ADAPTER_DESC1 desc; adapter->GetDesc1(&desc); const QString name = QString::fromUtf16((char16_t *) desc.Description); - qCDebug(QSG_LOG_INFO, "Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags); + qCDebug(QSG_LOG_INFO_GENERAL, "Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags); } if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX")) { @@ -207,7 +207,7 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte const QString name = QString::fromUtf16((char16_t *) desc.Description); HRESULT hr = D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr); if (SUCCEEDED(hr)) { - qCDebug(QSG_LOG_INFO, "Using requested adapter '%s'", qPrintable(name)); + qCDebug(QSG_LOG_INFO_GENERAL, "Using requested adapter '%s'", qPrintable(name)); *outAdapter = adapter.Detach(); return; } else { @@ -223,7 +223,7 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) { const QString name = QString::fromUtf16((char16_t *) desc.Description); - qCDebug(QSG_LOG_INFO, "Using adapter '%s'", qPrintable(name)); + qCDebug(QSG_LOG_INFO_GENERAL, "Using adapter '%s'", qPrintable(name)); break; } } @@ -287,7 +287,7 @@ void QSGD3D12DeviceManager::ensureCreated() } if (warp) { - qCDebug(QSG_LOG_INFO, "Using WARP"); + qCDebug(QSG_LOG_INFO_GENERAL, "Using WARP"); m_factory->EnumWarpAdapter(IID_PPV_ARGS(&adapter)); HRESULT hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)); if (FAILED(hr)) { @@ -300,12 +300,12 @@ void QSGD3D12DeviceManager::ensureCreated() if (SUCCEEDED(adapter.As(&adapter3))) { DXGI_QUERY_VIDEO_MEMORY_INFO vidMemInfo; if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &vidMemInfo))) { - qCDebug(QSG_LOG_INFO, "Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", + qCDebug(QSG_LOG_INFO_GENERAL, "Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); } if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &vidMemInfo))) { - qCDebug(QSG_LOG_INFO, "Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", + qCDebug(QSG_LOG_INFO_GENERAL, "Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); } @@ -726,16 +726,16 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int waitableSwapChainMaxLatency = qBound(0, qEnvironmentVariableIntValue(latReqEnvVar), 16); if (qEnvironmentVariableIsSet("QSG_INFO")) - const_cast<QLoggingCategory &>(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true); + const_cast<QLoggingCategory &>(QSG_LOG_INFO_GENERAL()).setEnabled(QtDebugMsg, true); - qCDebug(QSG_LOG_INFO, "d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d", + qCDebug(QSG_LOG_INFO_GENERAL, "d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d", swapChainBufferCount, frameInFlightCount); if (waitableSwapChainMaxLatency) - qCDebug(QSG_LOG_INFO, "Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency); + qCDebug(QSG_LOG_INFO_GENERAL, "Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency); const bool debugLayer = qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0; if (debugLayer) { - qCDebug(QSG_LOG_INFO, "Enabling debug layer"); + qCDebug(QSG_LOG_INFO_GENERAL, "Enabling debug layer"); ComPtr<ID3D12Debug> debugController; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) debugController->EnableDebugLayer(); diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index ad73c26b15..e49f5c40a5 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -28,7 +28,8 @@ HEADERS += \ $$PWD/qv4isel_moth_p.h \ $$PWD/qv4instr_moth_p.h \ $$PWD/qqmlpropertycachecreator_p.h \ - $$PWD/qqmlpropertyvalidator_p.h + $$PWD/qqmlpropertyvalidator_p.h \ + $$PWD/qv4compilationunitmapper_p.h SOURCES += \ @@ -36,6 +37,10 @@ SOURCES += \ $$PWD/qv4instr_moth.cpp \ $$PWD/qv4isel_moth.cpp \ $$PWD/qqmlpropertycachecreator.cpp \ - $$PWD/qqmlpropertyvalidator.cpp + $$PWD/qqmlpropertyvalidator.cpp \ + $$PWD/qv4compilationunitmapper.cpp + +unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp +else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp } diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index ed72054e1e..ef8ffa8620 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1725,7 +1725,11 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, member->kind = QV4::IR::Member::MemberOfSingletonObject; return newResolver->resolveMember(qmlEngine, newResolver, member); } - } else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) { + } +#if 0 + else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) { + // Right now the attached property IDs are not stable and cannot be embedded in the + // code that is cached on disk. QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta); auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); newResolver->owner = resolver->owner; @@ -1733,6 +1737,7 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, member->setAttachedPropertiesId(type->attachedPropertiesId(qmlEngine)); return newResolver->resolveMember(qmlEngine, newResolver, member); } +#endif return result; } @@ -1840,20 +1845,20 @@ static QV4::IR::DiscoveredType resolveMetaObjectProperty( if (property->isEnum()) return QV4::IR::VarType; - switch (property->propType) { + switch (property->propType()) { case QMetaType::Bool: result = QV4::IR::BoolType; break; case QMetaType::Int: result = QV4::IR::SInt32Type; break; case QMetaType::Double: result = QV4::IR::DoubleType; break; case QMetaType::QString: result = QV4::IR::StringType; break; default: if (property->isQObject()) { - if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType)) { + if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType())) { auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); newResolver->owner = resolver->owner; initMetaObjectResolver(newResolver, cache); return QV4::IR::DiscoveredType(newResolver); } - } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType)) { + } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType())) { if (QQmlPropertyCache *cache = qmlEngine->cache(valueTypeMetaObject)) { auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); newResolver->owner = resolver->owner; @@ -2048,7 +2053,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis d = property(propName, notInRevision); if (d) - return cache->signal(d->notifyIndex); + return cache->signal(d->notifyIndex()); } return 0; diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index a1b0a14890..10bcd1dbc1 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -124,7 +124,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje // group properties and value type group properties. For the former the base type is derived from // the property that references us, for the latter we only need a meta-object on the referencing object // because interceptors can't go to the shared value type instances. - if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType)) { + if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType())) { if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex); auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex); @@ -180,8 +180,8 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac { if (context.instantiatingProperty) { if (context.instantiatingProperty->isQObject()) { - return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType); - } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType)) { + return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType()); + } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType())) { return enginePrivate->cache(vtmo); } } else if (obj->inheritedTypeNameIndex != 0) { @@ -664,7 +664,7 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias QQmlPropertyData *targetProperty = targetCache->property(coreIndex); Q_ASSERT(targetProperty); - *type = targetProperty->propType; + *type = targetProperty->propType(); writable = targetProperty->isWritable(); resettable = targetProperty->isResettable(); @@ -680,7 +680,7 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias *type = QVariant::Int; } else { // Copy type flags - propertyFlags->copyPropertyTypeFlags(targetProperty->getFlags()); + propertyFlags->copyPropertyTypeFlags(targetProperty->flags()); if (targetProperty->isVarProperty()) propertyFlags->type = QQmlPropertyData::Flags::QVariantType; diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index c644330e97..45379d5155 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -206,7 +206,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, } if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { - const QVector<QQmlCompileError> subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)); + const QVector<QQmlCompileError> subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())); if (!subObjectValidatorErrors.isEmpty()) return subObjectValidatorErrors; } @@ -240,7 +240,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { QString error; - if (pd->propType == qMetaTypeId<QQmlScriptString>()) + if (pd->propType() == qMetaTypeId<QQmlScriptString>()) error = tr( "Cannot assign multiple values to a script property"); else error = tr( "Cannot assign multiple values to a singular property"); @@ -255,7 +255,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, if (loc < (*assignedGroupProperty)->valueLocation) loc = (*assignedGroupProperty)->valueLocation; - if (pd && QQmlValueTypeFactory::isValueType(pd->propType)) + if (pd && QQmlValueTypeFactory::isValueType(pd->propType())) return recordError(loc, tr("Property has already been assigned a value")); return recordError(loc, tr("Cannot assign a value directly to a grouped property")); } @@ -269,8 +269,8 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, if (bindingError.isSet()) return recordError(bindingError); } else if (binding->isGroupProperty()) { - if (QQmlValueTypeFactory::isValueType(pd->propType)) { - if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)) { + if (QQmlValueTypeFactory::isValueType(pd->propType())) { + if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())) { if (!pd->isWritable()) { return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); } @@ -278,7 +278,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, return recordError(binding->location, tr("Invalid grouped property access")); } } else { - if (!enginePrivate->propertyCacheForType(pd->propType)) { + if (!enginePrivate->propertyCacheForType(pd->propType())) { return recordError(binding->location, tr("Invalid grouped property access")); } } @@ -334,7 +334,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache return noError; QString value = binding->valueAsString(qmlUnit); - QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex); + QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex()); bool ok; if (p.isFlagType()) { p.enumerator().keysToValue(value.toUtf8().constData(), &ok); @@ -347,7 +347,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache return noError; } - switch (property->propType) { + switch (property->propType()) { case QMetaType::QVariant: break; case QVariant::String: { @@ -541,12 +541,12 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); default: { // generate single literal value assignment to a list property if required - if (property->propType == qMetaTypeId<QList<qreal> >()) { + if (property->propType() == qMetaTypeId<QList<qreal> >()) { if (binding->type != QV4::CompiledData::Binding::Type_Number) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected")); } break; - } else if (property->propType == qMetaTypeId<QList<int> >()) { + } else if (property->propType() == qMetaTypeId<QList<int> >()) { bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); if (ok) { double n = binding->valueAsNumber(); @@ -556,31 +556,31 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache if (!ok) return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected")); break; - } else if (property->propType == qMetaTypeId<QList<bool> >()) { + } else if (property->propType() == qMetaTypeId<QList<bool> >()) { if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected")); } break; - } else if (property->propType == qMetaTypeId<QList<QUrl> >()) { + } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { if (binding->type != QV4::CompiledData::Binding::Type_String) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected")); } break; - } else if (property->propType == qMetaTypeId<QList<QString> >()) { + } else if (property->propType() == qMetaTypeId<QList<QString> >()) { if (!binding->evaluatesToString()) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected")); } break; - } else if (property->propType == qMetaTypeId<QJSValue>()) { + } else if (property->propType() == qMetaTypeId<QJSValue>()) { break; - } else if (property->propType == qMetaTypeId<QQmlScriptString>()) { + } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { break; } // otherwise, try a custom type assignment - QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); if (!converter) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType())))); } } break; @@ -652,15 +652,15 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * return noError; } - if (QQmlMetaType::isInterface(property->propType)) { + if (QQmlMetaType::isInterface(property->propType())) { // Can only check at instantiation time if the created sub-object successfully casts to the // target interface. return noError; - } else if (property->propType == QMetaType::QVariant) { + } else if (property->propType() == QMetaType::QVariant) { // We can convert everything to QVariant :) return noError; } else if (property->isQList()) { - const int listType = enginePrivate->listType(property->propType); + const int listType = enginePrivate->listType(property->propType()); if (!QQmlMetaType::isInterface(listType)) { QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); if (!canCoerce(listType, source)) { @@ -672,15 +672,15 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * return noError; } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { return noError; - } else if (QQmlValueTypeFactory::isValueType(property->propType)) { + } else if (QQmlValueTypeFactory::isValueType(property->propType())) { return QQmlCompileError(binding->location, tr("Unexpected object assignment")); - } else if (property->propType == qMetaTypeId<QQmlScriptString>()) { + } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); } else { // We want to raw metaObject here as the raw metaobject is the // actual property type before we applied any extensions that might // effect the properties on the type, but don't effect assignability - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType); + QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType()); // Will be true if the assgned type inherits propertyMetaObject bool isAssignable = false; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index e1166286c2..4b33363d09 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -390,7 +390,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio bool notInRevision = false; QQmlPropertyData *signal = resolver.signal(propertyName, ¬InRevision); if (signal) { - int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex); + int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex()); sigIndex = propertyCache->originalClone(sigIndex); bool unnamedParameter = false; @@ -547,7 +547,7 @@ bool QQmlEnumTypeResolver::resolveEnumBindings() if (!pd) continue; - if (!pd->isEnum() && pd->propType != QMetaType::Int) + if (!pd->isEnum() && pd->propType() != QMetaType::Int) continue; if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding)) @@ -577,7 +577,7 @@ bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QS bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding) { - bool isIntProp = (prop->propType == QMetaType::Int) && !prop->isEnum(); + bool isIntProp = (prop->propType() == QMetaType::Int) && !prop->isEnum(); if (!prop->isEnum() && !isIntProp) return true; @@ -621,7 +621,7 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, auto *tr = resolvedTypes->value(obj->inheritedTypeNameIndex); if (type && tr && tr->type == type) { - QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex); + QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex()); // When these two match, we can short cut the search if (mprop.isFlagType()) { @@ -758,7 +758,7 @@ void QQmlScriptStringScanner::scan() continue; bool notInRevision = false; QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; - if (!pd || pd->propType != scriptStringMetaType) + if (!pd || pd->propType() != scriptStringMetaType) continue; QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex); @@ -815,7 +815,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI if (!pd || !pd->isQObject()) continue; - QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType); + QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType()); const QMetaObject *mo = pc->firstCppMetaObject(); while (mo) { if (mo == &QQmlComponent::staticMetaObject) @@ -1116,15 +1116,15 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv } } - if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) { + if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) { *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); break; } - propIdx = QQmlPropertyIndex(targetProperty->coreIndex); + propIdx = QQmlPropertyIndex(targetProperty->coreIndex()); if (!subProperty.isEmpty()) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType); + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType()); if (!valueTypeMetaObject) { *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); break; diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp new file mode 100644 index 0000000000..b53b7cf784 --- /dev/null +++ b/src/qml/compiler/qv4compilationunitmapper.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qv4compilationunitmapper_p.h" + +#include "qv4compileddata_p.h" +#include <QFileInfo> +#include <QDateTime> + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +CompilationUnitMapper::CompilationUnitMapper() + : dataPtr(nullptr) +{ + +} + +CompilationUnitMapper::~CompilationUnitMapper() +{ + close(); +} + +bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const QString &sourcePath, QString *errorString) +{ + if (strncmp(header->magic, CompiledData::magic_str, sizeof(header->magic))) { + *errorString = QStringLiteral("Magic bytes in the header do not match"); + return false; + } + + if (header->version != quint32(QV4_DATA_STRUCTURE_VERSION)) { + *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); + return false; + } + + if (header->qtVersion != quint32(QT_VERSION)) { + *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header->qtVersion, 0, 16).arg(QT_VERSION, 0, 16); + return false; + } + + { + QFileInfo sourceCode(sourcePath); + if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header->sourceTimeStamp) { + *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + return false; + } + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h new file mode 100644 index 0000000000..5b6939f1cf --- /dev/null +++ b/src/qml/compiler/qv4compilationunitmapper_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 QV4COMPILATIONUNITMAPPER_H +#define QV4COMPILATIONUNITMAPPER_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/qv4global_p.h> +#include <QFile> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace CompiledData { +struct Unit; +} + +class CompilationUnitMapper +{ +public: + CompilationUnitMapper(); + ~CompilationUnitMapper(); + + CompiledData::Unit *open(const QString &cacheFilePath, const QString &sourcePath, QString *errorString); + void close(); + +private: + static bool verifyHeader(const QV4::CompiledData::Unit *header, const QString &sourcePath, QString *errorString); + +#if defined(Q_OS_UNIX) + size_t length; +#endif + void *dataPtr; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4COMPILATIONUNITMAPPER_H diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp new file mode 100644 index 0000000000..1aa3e05f5f --- /dev/null +++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qv4compilationunitmapper_p.h" + +#include <sys/mman.h> +#include <functional> +#include <private/qcore_unix_p.h> +#include <private/qdeferredcleanup_p.h> + +#include "qv4compileddata_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString) +{ + close(); + + int fd = qt_safe_open(QFile::encodeName(cacheFileName).constData(), O_RDONLY); + if (fd == -1) { + *errorString = qt_error_string(errno); + return nullptr; + } + + QDeferredCleanup cleanup([fd]{ + qt_safe_close(fd) ; + }); + + CompiledData::Unit header; + qint64 bytesRead = qt_safe_read(fd, reinterpret_cast<char *>(&header), sizeof(header)); + + if (bytesRead != sizeof(header)) { + *errorString = QStringLiteral("File too small for the header fields"); + return nullptr; + } + + if (!verifyHeader(&header, sourcePath, errorString)) + return nullptr; + + // Data structure and qt version matched, so now we can access the rest of the file safely. + + length = static_cast<size_t>(lseek(fd, 0, SEEK_END)); + + void *ptr = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, /*offset*/0); + if (ptr == MAP_FAILED) { + *errorString = qt_error_string(errno); + return nullptr; + } + dataPtr = ptr; + + return reinterpret_cast<CompiledData::Unit*>(dataPtr); +} + +void CompilationUnitMapper::close() +{ + if (dataPtr != nullptr) + munmap(dataPtr, length); + dataPtr = nullptr; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp new file mode 100644 index 0000000000..7e62cbfe8b --- /dev/null +++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qv4compilationunitmapper_p.h" + +#include "qv4compileddata_p.h" +#include <private/qdeferredcleanup_p.h> +#include <QFileInfo> +#include <QDateTime> +#include <qt_windows.h> + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString) +{ + close(); + + // ### TODO: fix up file encoding/normalization/unc handling once QFileSystemEntry + // is exported from QtCore. + HANDLE handle = +#if defined(Q_OS_WINRT) + CreateFile2(reinterpret_cast<const wchar_t*>(cacheFileName.constData()), + GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, + OPEN_EXISTING, nullptr); +#else + CreateFile(reinterpret_cast<const wchar_t*>(cacheFileName.constData()), + GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + nullptr); +#endif + if (handle == INVALID_HANDLE_VALUE) { + *errorString = qt_error_string(GetLastError()); + return nullptr; + } + + QDeferredCleanup fileHandleCleanup([handle]{ + CloseHandle(handle); + }); + +#if defined(Q_OS_WINRT) + *errorString = QStringLiteral("Compilation unit mapping not supported on WinRT yet"); + return nullptr; +#else + CompiledData::Unit header; + DWORD bytesRead; + if (!ReadFile(handle, reinterpret_cast<char *>(&header), sizeof(header), &bytesRead, nullptr)) { + *errorString = qt_error_string(GetLastError()); + return false; + } + + if (bytesRead != sizeof(header)) { + *errorString = QStringLiteral("File too small for the header fields"); + return nullptr; + } + + if (!verifyHeader(&header, sourcePath, errorString)) + return nullptr; + + // Data structure and qt version matched, so now we can access the rest of the file safely. + + HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_EXECUTE_READ, 0, 0, 0); + if (!fileMappingHandle) { + *errorString = qt_error_string(GetLastError()); + return false; + } + + QDeferredCleanup mappingCleanup([fileMappingHandle]{ + CloseHandle(fileMappingHandle); + }); + + dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ | FILE_MAP_EXECUTE, 0, 0, 0); + if (!dataPtr) { + *errorString = qt_error_string(GetLastError()); + return nullptr; + } + + return reinterpret_cast<CompiledData::Unit*>(dataPtr); +#endif +} + +void CompilationUnitMapper::close() +{ +#if !defined(Q_OS_WINRT) + if (dataPtr != nullptr) + UnmapViewOfFile(dataPtr); +#endif + dataPtr = nullptr; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 98eb7188cc..35f61b4f69 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -49,12 +49,15 @@ #include <private/qqmlpropertycache_p.h> #include <private/qqmltypeloader_p.h> #include <private/qqmlengine_p.h> +#include "qv4compilationunitmapper_p.h" #include <QQmlPropertyMap> #include <QDateTime> #include <QSaveFile> #include <QFile> #include <QFileInfo> #include <QScopedValueRollback> +#include <QStandardPaths> +#include <QDir> #endif #include <private/qqmlirbuilder_p.h> #include <QCoreApplication> @@ -320,6 +323,19 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine, sizeof(data->dependencyMD5Checksum)) == 0; } +static QString cacheFilePath(const QUrl &url) +{ + const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); + const QString localCachePath = localSourcePath + QLatin1Char('c'); + if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) + return localCachePath; + QCryptographicHash fileNameHash(QCryptographicHash::Sha1); + fileNameHash.addData(localSourcePath.toUtf8()); + QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); + QDir::root().mkpath(directory); + return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix(); +} + bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) { errorString->clear(); @@ -329,13 +345,13 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return false; } - if (!unitUrl.isLocalFile()) { + if (!QQmlFile::isLocalFile(unitUrl)) { *errorString = QStringLiteral("File has to be a local file."); return false; } // Foo.qml -> Foo.qmlc - QSaveFile cacheFile(unitUrl.toLocalFile() + QLatin1Char('c')); + QSaveFile cacheFile(cacheFilePath(unitUrl)); if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { *errorString = cacheFile.errorString(); return false; @@ -370,62 +386,20 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) { - if (!url.isLocalFile()) { + if (!QQmlFile::isLocalFile(url)) { *errorString = QStringLiteral("File has to be a local file."); return false; } const QString sourcePath = url.toLocalFile(); - QScopedPointer<QFile> cacheFile(new QFile(sourcePath + QLatin1Char('c'))); + QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); - if (!cacheFile->open(QIODevice::ReadOnly)) { - *errorString = cacheFile->errorString(); + CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString); + if (!mappedUnit) return false; - } - - { - CompiledData::Unit header; - qint64 bytesRead = cacheFile->read(reinterpret_cast<char *>(&header), sizeof(header)); - - if (bytesRead != sizeof(header)) { - *errorString = QStringLiteral("File too small for the header fields"); - return false; - } - - if (strncmp(header.magic, CompiledData::magic_str, sizeof(header.magic))) { - *errorString = QStringLiteral("Magic bytes in the header do not match"); - return false; - } - - if (header.version != quint32(QV4_DATA_STRUCTURE_VERSION)) { - *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header.version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); - return false; - } - - if (header.qtVersion != quint32(QT_VERSION)) { - *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header.qtVersion, 0, 16).arg(QT_VERSION, 0, 16); - return false; - } - - { - QFileInfo sourceCode(sourcePath); - if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header.sourceTimeStamp) { - *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); - return false; - } - } - - } - // Data structure and qt version matched, so now we can access the rest of the file safely. - - uchar *cacheData = cacheFile->map(/*offset*/0, cacheFile->size()); - if (!cacheData) { - *errorString = cacheFile->errorString(); - return false; - } const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; - QScopedValueRollback<const Unit *> dataPtrChange(data, reinterpret_cast<const Unit *>(cacheData)); + QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); { const QString foundArchitecture = stringAt(data->architectureIndex); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 639c79f79b..a6ca1594a4 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x01 +#define QV4_DATA_STRUCTURE_VERSION 0x02 class QIODevice; class QQmlPropertyCache; @@ -92,6 +92,7 @@ struct Function; struct Function; class EvalISelFactory; +class CompilationUnitMapper; namespace CompiledData { @@ -874,7 +875,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount int listMetaTypeId; bool isRegisteredWithEngine; - QScopedPointer<QIODevice> backingFile; + QScopedPointer<CompilationUnitMapper> backingFile; // --- interface for QQmlPropertyCacheCreator typedef Object CompiledObject; diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index d97eec5e1d..72e6c276a9 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -149,18 +149,18 @@ void IRDecoder::visitMove(IR::Move *s) if (_function && attachedPropertiesId == 0 && !m->property->isConstant()) { if (m->kind == IR::Member::MemberOfQmlContextObject) { - _function->contextObjectPropertyDependencies.insert(m->property->coreIndex, m->property->notifyIndex); + _function->contextObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex()); captureRequired = false; } else if (m->kind == IR::Member::MemberOfQmlScopeObject) { - _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex, m->property->notifyIndex); + _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex()); captureRequired = false; } } if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { - getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex, s->target); + getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex(), s->target); return; } - getQObjectProperty(m->base, m->property->coreIndex, captureRequired, isSingletonProperty, attachedPropertiesId, s->target); + getQObjectProperty(m->base, m->property->coreIndex(), captureRequired, isSingletonProperty, attachedPropertiesId, s->target); #endif // V4_BOOTSTRAP return; } else if (m->kind == IR::Member::MemberOfIdObjectsArray) { @@ -187,7 +187,7 @@ void IRDecoder::visitMove(IR::Move *s) #ifndef V4_BOOTSTRAP Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { - callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex, c->args, s->target); + callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, s->target); return; } #endif @@ -216,10 +216,10 @@ void IRDecoder::visitMove(IR::Move *s) Q_UNIMPLEMENTED(); #else if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { - setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex); + setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex()); return; } - setQObjectProperty(s->source, m->base, m->property->coreIndex); + setQObjectProperty(s->source, m->base, m->property->coreIndex()); #endif return; } else { @@ -263,7 +263,7 @@ void IRDecoder::visitExp(IR::Exp *s) #ifndef V4_BOOTSTRAP Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { - callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex, c->args, 0); + callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, 0); return; } #endif @@ -295,7 +295,7 @@ void IRDecoder::callBuiltin(IR::Call *call, Expr *result) if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { callBuiltinTypeofQmlContextProperty(member->base, IR::Member::MemberKind(member->kind), - member->property->coreIndex, result); + member->property->coreIndex(), result); return; } #endif diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index b6c5226894..5687834b00 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -911,8 +911,8 @@ void IRPrinter::visitMember(Member *e) *out << '.' << *e->name; #ifndef V4_BOOTSTRAP if (e->property) - *out << " (meta-property " << e->property->coreIndex - << " <" << QMetaType::typeName(e->property->propType) + *out << " (meta-property " << e->property->coreIndex() + << " <" << QMetaType::typeName(e->property->propType()) << ">)"; else if (e->kind == Member::MemberOfIdObjectsArray) *out << "(id object " << e->idIndex << ")"; diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index 7bb4d701e2..cc2fe90483 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -249,6 +249,18 @@ parameter, the value can be created as a JavaScript \c Date object in QML, and is automatically converted to a QDateTime value when it is passed to C++. +\section2 QTime to JavaScript Date + +The QML engine provides automatic type conversion from QTime values to +JavaScript \c Date objects. The date component of the resulting Date +object should not be relied upon, as it is operating system dependent. +Specifically, the year (and month and day) are set to zero. Conversion +from a JavaScript \c Date object to QTime is done by converting to a +QDateTime, and then relying on QVariant to convert it to a QTime. The end +effect is that the date part of the \c Date object is ignored, but the +local timezone will be used ignoring any DST complications it may have. + + \section2 Sequence Type to JavaScript Array Certain C++ sequence types are supported transparently in QML as JavaScript diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index e613c4fcfb..de8b967d72 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -110,7 +110,7 @@ console.log("Result: " + a); \endcode Any attempt to modify the global object - either implicitly or explicitly - will -cause an exception. If uncaught, this will result in an warning being printed, +cause an exception. If uncaught, this will result in a warning being printed, that includes the file and line number of the offending code. \li Global code is run in a reduced scope. @@ -120,7 +120,7 @@ code, it is executed in a scope that contains only the external file itself and the global object. That is, it will not have access to the QML objects and properties it \l {Scope and Naming Resolution}{normally would}. -Global code that only accesses script local variable is permitted. This is an +Global code that only accesses script local variables is permitted. This is an example of valid global code. \code diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index d76a21c74c..2ef0db78c0 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -84,7 +84,7 @@ struct CompilationUnit : public QV4::CompiledData::CompilationUnit void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; - bool memoryMapCode(QString *errorString); + bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; // Coderef + execution engine @@ -108,36 +108,6 @@ struct RuntimeCall { bool isValid() const { return addr.offset >= 0; } }; -template <typename T> -struct ExceptionCheck { - enum { NeedsCheck = 1 }; -}; -// push_catch and pop context methods shouldn't check for exceptions -template <> -struct ExceptionCheck<void (*)(QV4::ExecutionEngine *)> { - enum { NeedsCheck = 0 }; -}; -template <typename A> -struct ExceptionCheck<void (*)(A, QV4::NoThrowEngine)> { - enum { NeedsCheck = 0 }; -}; -template <> -struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *)> { - enum { NeedsCheck = 0 }; -}; -template <typename A> -struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A)> { - enum { NeedsCheck = 0 }; -}; -template <typename A, typename B> -struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A, B)> { - enum { NeedsCheck = 0 }; -}; -template <typename A, typename B, typename C> -struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { - enum { NeedsCheck = 0 }; -}; - class Assembler : public JSC::MacroAssembler, public TargetPlatform { Q_DISABLE_COPY(Assembler) @@ -809,7 +779,7 @@ public: }; template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size + SizeOnStack<1, Arg2>::Size @@ -852,7 +822,7 @@ public: if (stackSpaceNeeded) addPtr(TrustedImm32(stackSpaceNeeded), StackPointerRegister); - if (ExceptionCheck<Callable>::NeedsCheck) { + if (needsExceptionCheck) { checkException(); } @@ -861,33 +831,33 @@ public: } template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); + generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); } template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4> - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType(), VoidType()); + generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, VoidType(), VoidType()); } template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3> - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType(), VoidType()); + generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType(), VoidType()); } template <typename ArgRet, typename Callable, typename Arg1, typename Arg2> - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) { - generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType(), VoidType()); + generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType(), VoidType()); } template <typename ArgRet, typename Callable, typename Arg1> - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1) { - generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType(), VoidType()); + generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType(), VoidType()); } Pointer toAddress(RegisterID tmpReg, IR::Expr *e, int offset) diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index 45cc9259c3..9c535bb0bb 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -45,17 +45,17 @@ using namespace QV4; using namespace JIT; #define OP(op) \ - { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, 0, 0 } + { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define OPCONTEXT(op) \ - { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), 0, 0 } + { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define INLINE_OP(op, memOp, immOp) \ - { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, memOp, immOp } + { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define INLINE_OPCONTEXT(op, memOp, immOp) \ - { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), memOp, immOp } + { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define NULL_OP \ - { 0, 0, 0, 0, 0 } + { 0, 0, 0, 0, 0, false } const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { NULL_OP, // OpInvalid @@ -128,11 +128,11 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) RuntimeCall fallBack(info.fallbackImplementation); RuntimeCall context(info.contextImplementation); if (fallBack.isValid()) { - as->generateFunctionCallImp(target, info.name, fallBack, + as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, fallBack, Assembler::PointerToValue(lhs), Assembler::PointerToValue(rhs)); } else if (context.isValid()) { - as->generateFunctionCallImp(target, info.name, context, + as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, context, Assembler::EngineRegister, Assembler::PointerToValue(lhs), Assembler::PointerToValue(rhs)); diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h index c246ee43b0..37601f54ba 100644 --- a/src/qml/jit/qv4binop_p.h +++ b/src/qml/jit/qv4binop_p.h @@ -81,6 +81,7 @@ struct Binop { int contextImplementation; // offsetOf(Runtime,...) MemRegOp inlineMemRegOp; ImmRegOp inlineImmRegOp; + bool needsExceptionCheck; }; static const OpInfo operations[IR::LastAluOp + 1]; diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index da28df817d..c1c42f876c 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -970,9 +970,15 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) } #define setOp(op, opName, operation) \ - do { op = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); } while (0) + do { \ + op = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ + } while (0) #define setOpContext(op, opName, operation) \ - do { opContext = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); } while (0) + do { \ + opContext = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ + } while (0) void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) { @@ -1446,18 +1452,19 @@ void InstructionSelection::visitCJump(IR::CJump *s) RuntimeCall op; RuntimeCall opContext; const char *opName = 0; + bool needsExceptionCheck; switch (b->op) { default: Q_UNREACHABLE(); Q_ASSERT(!"todo"); break; - case IR::OpGt: setOp(op, opName, Runtime::compareGreaterThan); break; - case IR::OpLt: setOp(op, opName, Runtime::compareLessThan); break; - case IR::OpGe: setOp(op, opName, Runtime::compareGreaterEqual); break; - case IR::OpLe: setOp(op, opName, Runtime::compareLessEqual); break; - case IR::OpEqual: setOp(op, opName, Runtime::compareEqual); break; - case IR::OpNotEqual: setOp(op, opName, Runtime::compareNotEqual); break; - case IR::OpStrictEqual: setOp(op, opName, Runtime::compareStrictEqual); break; - case IR::OpStrictNotEqual: setOp(op, opName, Runtime::compareStrictNotEqual); break; - case IR::OpInstanceof: setOpContext(op, opName, Runtime::compareInstanceof); break; - case IR::OpIn: setOpContext(op, opName, Runtime::compareIn); break; + case IR::OpGt: setOp(op, opName, compareGreaterThan); break; + case IR::OpLt: setOp(op, opName, compareLessThan); break; + case IR::OpGe: setOp(op, opName, compareGreaterEqual); break; + case IR::OpLe: setOp(op, opName, compareLessEqual); break; + case IR::OpEqual: setOp(op, opName, compareEqual); break; + case IR::OpNotEqual: setOp(op, opName, compareNotEqual); break; + case IR::OpStrictEqual: setOp(op, opName, compareStrictEqual); break; + case IR::OpStrictNotEqual: setOp(op, opName, compareStrictNotEqual); break; + case IR::OpInstanceof: setOpContext(op, opName, compareInstanceof); break; + case IR::OpIn: setOpContext(op, opName, compareIn); break; } // switch // TODO: in SSA optimization, do constant expression evaluation. @@ -1466,12 +1473,14 @@ void InstructionSelection::visitCJump(IR::CJump *s) // Of course, after folding the CJUMP to a JUMP, dead-code (dead-basic-block) // elimination (which isn't there either) would remove the whole else block. if (opContext.isValid()) - _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, opContext, + _as->generateFunctionCallImp(needsExceptionCheck, + Assembler::ReturnValueRegister, opName, opContext, Assembler::EngineRegister, Assembler::PointerToValue(b->left), Assembler::PointerToValue(b->right)); else - _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, + _as->generateFunctionCallImp(needsExceptionCheck, + Assembler::ReturnValueRegister, opName, op, Assembler::PointerToValue(b->left), Assembler::PointerToValue(b->right)); diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 93453f71be..5bca879a77 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -244,7 +244,7 @@ private: #define isel_stringIfy(s) isel_stringIfyx(s) #define generateRuntimeCall(t, function, ...) \ - _as->generateFunctionCallImp(t, "Runtime::" isel_stringIfy(function), RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) + _as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) int prepareVariableArguments(IR::ExprList* args); int prepareCallData(IR::ExprList* args, IR::Expr *thisObject); @@ -260,7 +260,7 @@ private: // address. Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup)); - _as->generateFunctionCallImp(retval, "lookup getter/setter", + _as->generateFunctionCallImp(true, retval, "lookup getter/setter", LookupCall(lookupAddr, getterSetterOffset), lookupAddr, arg1, arg2, arg3); } diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp index 6a32069ac4..799103849b 100644 --- a/src/qml/jit/qv4unop.cpp +++ b/src/qml/jit/qv4unop.cpp @@ -47,10 +47,14 @@ using namespace JIT; #define stringIfyx(s) #s #define stringIfy(s) stringIfyx(s) #define setOp(operation) \ - do { call = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); } while (0) + do { \ + call = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); \ + needsExceptionCheck = Runtime::Method_##operation##_NeedsExceptionCheck; \ + } while (0) void Unop::generate(IR::Expr *source, IR::Expr *target) { + bool needsExceptionCheck; RuntimeCall call; const char *name = 0; switch (op) { @@ -71,7 +75,7 @@ void Unop::generate(IR::Expr *source, IR::Expr *target) } // switch Q_ASSERT(call.isValid()); - _as->generateFunctionCallImp(target, name, call, Assembler::PointerToValue(source)); + _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, Assembler::PointerToValue(source)); } void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index e5c1dcdb80..a4a96a96a7 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -178,7 +178,7 @@ QJSValue::QJSValue(SpecialValue value) : d(0) { if (value == NullValue) - QJSValuePrivate::setVariant(this, QVariant(QMetaType::VoidStar, (void *)0)); + QJSValuePrivate::setVariant(this, QVariant::fromValue(nullptr)); } /*! @@ -293,7 +293,10 @@ bool QJSValue::isNull() const if (val) return val->isNull(); QVariant *variant = QJSValuePrivate::getVariant(this); - return variant && variant->userType() == QMetaType::VoidStar; + if (!variant) + return false; + const int type = variant->userType(); + return type == QMetaType::Nullptr || type == QMetaType::VoidStar; } /*! @@ -582,7 +585,7 @@ quint32 QJSValue::toUInt() const \table \header \li Input Type \li Result \row \li Undefined \li An invalid QVariant. - \row \li Null \li A QVariant containing a null pointer (QMetaType::VoidStar). + \row \li Null \li A QVariant containing a null pointer (QMetaType::Nullptr). \row \li Boolean \li A QVariant containing the value of the boolean. \row \li Number \li A QVariant containing the value of the number. \row \li String \li A QVariant containing the value of the string. @@ -619,7 +622,7 @@ QVariant QJSValue::toVariant() const return QVariant(val->asDouble()); } if (val->isNull()) - return QVariant(QMetaType::VoidStar, 0); + return QVariant(QMetaType::Nullptr, 0); Q_ASSERT(val->isUndefined()); return QVariant(); } diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index 25afd9275c..c4761ad6ea 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -132,6 +132,7 @@ public: case QMetaType::Void: *v = QV4::Encode::undefined(); break; + case QMetaType::Nullptr: case QMetaType::VoidStar: *v = QV4::Encode::null(); break; diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index df648ba9ee..04358fe3b5 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -639,6 +639,28 @@ Heap::DateObject::DateObject(const QDateTime &date) this->date = date.isValid() ? date.toMSecsSinceEpoch() : qt_qnan(); } +Heap::DateObject::DateObject(const QTime &time) +{ + if (!time.isValid()) { + date = qt_qnan(); + return; + } + + /* All programmers know that stuff starts at 0. Whatever that may mean in this context (and + * local timezone), it's before the epoch, so there is defenitely no DST problem. Specifically: + * you can't start with a date before the epoch, add some[*] hours, and end up with a date + * after. That's a problem for timezones where new year happens during DST, like + * Australia/Hobart, because we have to ignore DST before the epoch (but honor it after the + * epoch). + * + * [*] Well, when "some" is in the range 0-24. If you add something like 1M then this might + * still happen. + */ + static const double d = MakeDay(0, 0, 0); + double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec()); + date = TimeClip(UTC(MakeDate(d, t))); +} + QDateTime DateObject::toQDateTime() const { return ToDateTime(date(), Qt::LocalTime); diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 13e9e04040..c67acdcfa2 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -74,6 +74,8 @@ struct DateObject : Object { } DateObject(const QDateTime &date); double date; + + DateObject(const QTime &time); }; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index f5bae4b258..27397fe3d8 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -621,6 +621,13 @@ Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt) return object->d(); } +Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) +{ + Scope scope(this); + Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(t)); + return object->d(); +} + Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { bool global = (flags & IR::RegExp::RegExp_Global); @@ -1130,7 +1137,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (value.isUndefined()) return QVariant(); if (value.isNull()) - return QVariant(QMetaType::VoidStar, (void *)0); + return QVariant::fromValue(nullptr); if (value.isBoolean()) return value.booleanValue(); if (value.isInteger()) @@ -1255,6 +1262,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) case QMetaType::UnknownType: case QMetaType::Void: return QV4::Encode::undefined(); + case QMetaType::Nullptr: case QMetaType::VoidStar: return QV4::Encode::null(); case QMetaType::Bool: @@ -1290,7 +1298,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) case QMetaType::QDate: return QV4::Encode(newDateObject(QDateTime(*reinterpret_cast<const QDate *>(ptr)))); case QMetaType::QTime: - return QV4::Encode(newDateObject(QDateTime(QDate(1970,1,1), *reinterpret_cast<const QTime *>(ptr)))); + return QV4::Encode(newDateObjectFromTime(*reinterpret_cast<const QTime *>(ptr))); case QMetaType::QRegExp: return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr))); case QMetaType::QObjectStar: @@ -1426,6 +1434,7 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) case QMetaType::UnknownType: case QMetaType::Void: return QV4::Encode::undefined(); + case QMetaType::Nullptr: case QMetaType::VoidStar: return QV4::Encode::null(); case QMetaType::Bool: diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index f42f727295..843a6f4d94 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -419,6 +419,7 @@ public: Heap::DateObject *newDateObject(const Value &value); Heap::DateObject *newDateObject(const QDateTime &dt); + Heap::DateObject *newDateObjectFromTime(const QTime &t); Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags); Heap::RegExpObject *newRegExpObject(RegExp *re, bool global); diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 276a069a77..b08ad24fbe 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -166,14 +166,14 @@ ReturnedValue FunctionObject::name() const return get(scope()->engine->id_name()); } -ReturnedValue FunctionObject::construct(const Managed *that, CallData *) +void FunctionObject::construct(const Managed *that, Scope &scope, CallData *) { - return static_cast<const FunctionObject *>(that)->engine()->throwTypeError(); + scope.result = static_cast<const FunctionObject *>(that)->engine()->throwTypeError(); } -ReturnedValue FunctionObject::call(const Managed *, CallData *) +void FunctionObject::call(const Managed *, Scope &scope, CallData *) { - return Encode::undefined(); + scope.result = Encode::undefined(); } void FunctionObject::markObjects(Heap::Base *that, ExecutionEngine *e) @@ -548,8 +548,11 @@ void SimpleScriptFunction::construct(const Managed *that, Scope &scope, CallData if (f->function()->compiledFunction->hasQmlDependencies()) QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope); - if (!scope.result.isManaged() || !scope.result.managed()) + if (v4->hasException) { + scope.result = Encode::undefined(); + } else if (!scope.result.isObject()) { scope.result = callData->thisObject; + } } void SimpleScriptFunction::call(const Managed *that, Scope &scope, CallData *callData) diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index be80b87873..182b762606 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -147,8 +147,8 @@ struct Q_QML_EXPORT FunctionObject: Object { using Object::construct; using Object::call; - static ReturnedValue construct(const Managed *that, CallData *); - static ReturnedValue call(const Managed *that, CallData *d); + static void construct(const Managed *that, Scope &scope, CallData *); + static void call(const Managed *that, Scope &scope, CallData *d); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function, bool createProto = true); static Heap::FunctionObject *createQmlFunction(QQmlContextData *qmlContext, QObject *scopeObject, QV4::Function *runtimeFunction, diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 074d3bf866..624c37a694 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -124,7 +124,7 @@ struct ReadAccessor { Q_UNUSED(n); void *args[] = { output, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); + QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex(), args); } static inline void Direct(QObject *object, const QQmlPropertyData &property, @@ -134,16 +134,16 @@ struct ReadAccessor { Q_UNUSED(n); void *args[] = { output, 0 }; - object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); + object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex(), args); } static inline void Accessor(QObject *object, const QQmlPropertyData &property, void *output, QQmlNotifier **n) { - Q_ASSERT(property.accessors); + Q_ASSERT(property.accessors()); - property.accessors->read(object, output); - if (n) property.accessors->notifier(object, n); + property.accessors()->read(object, output); + if (n) property.accessors()->notifier(object, n); } }; @@ -162,32 +162,32 @@ static QV4::ReturnedValue LoadProperty(QV4::ExecutionEngine *v4, QObject *object ReadFunction(object, property, &rv, notifier); return QV4::QObjectWrapper::wrap(v4, rv); } else if (property.isQList()) { - return QmlListWrapper::create(v4, object, property.coreIndex, property.propType); - } else if (property.propType == QMetaType::QReal) { + return QmlListWrapper::create(v4, object, property.coreIndex(), property.propType()); + } else if (property.propType() == QMetaType::QReal) { qreal v = 0; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); - } else if (property.propType == QMetaType::Int || property.isEnum()) { + } else if (property.propType() == QMetaType::Int || property.isEnum()) { int v = 0; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); - } else if (property.propType == QMetaType::Bool) { + } else if (property.propType() == QMetaType::Bool) { bool v = false; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); - } else if (property.propType == QMetaType::QString) { + } else if (property.propType() == QMetaType::QString) { QString v; ReadFunction(object, property, &v, notifier); return v4->newString(v)->asReturnedValue(); - } else if (property.propType == QMetaType::UInt) { + } else if (property.propType() == QMetaType::UInt) { uint v = 0; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); - } else if (property.propType == QMetaType::Float) { + } else if (property.propType() == QMetaType::Float) { float v = 0; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); - } else if (property.propType == QMetaType::Double) { + } else if (property.propType() == QMetaType::Double) { double v = 0; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); @@ -195,7 +195,7 @@ static QV4::ReturnedValue LoadProperty(QV4::ExecutionEngine *v4, QObject *object QQmlV4Handle handle; ReadFunction(object, property, &handle, notifier); return handle; - } else if (property.propType == qMetaTypeId<QJSValue>()) { + } else if (property.propType() == qMetaTypeId<QJSValue>()) { QJSValue v; ReadFunction(object, property, &v, notifier); return QJSValuePrivate::convertedToValue(v4, v); @@ -205,32 +205,32 @@ static QV4::ReturnedValue LoadProperty(QV4::ExecutionEngine *v4, QObject *object if (QQmlValueTypeFactory::isValueType(v.userType())) { if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(v.userType())) - return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex, valueTypeMetaObject, v.userType()); // VariantReference value-type. + return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, v.userType()); // VariantReference value-type. } return scope.engine->fromVariant(v); - } else if (QQmlValueTypeFactory::isValueType(property.propType)) { + } else if (QQmlValueTypeFactory::isValueType(property.propType())) { Q_ASSERT(notifier == 0); - if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType)) - return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex, valueTypeMetaObject, property.propType); + if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType())) + return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType()); } else { Q_ASSERT(notifier == 0); // see if it's a sequence type bool succeeded = false; - QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType, object, property.coreIndex, &succeeded)); + QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), &succeeded)); if (succeeded) return retn->asReturnedValue(); } - if (property.propType == QMetaType::UnknownType) { - QMetaProperty p = object->metaObject()->property(property.coreIndex); + if (property.propType() == QMetaType::UnknownType) { + QMetaProperty p = object->metaObject()->property(property.coreIndex()); qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property " "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name()); return QV4::Encode::undefined(); } else { - QVariant v(property.propType, (void *)0); + QVariant v(property.propType(), (void *)0); ReadFunction(object, property, v.data(), notifier); return scope.engine->fromVariant(v); } @@ -328,25 +328,25 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired) { - QQmlData::flushPendingBinding(object, property->encodedIndex()); + QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex())); if (property->isFunction() && !property->isVarProperty()) { if (property->isVMEFunction()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); - return vmemo->vmeMethod(property->coreIndex); + return vmemo->vmeMethod(property->coreIndex()); } else if (property->isV4Function()) { Scope scope(engine); ScopedContext global(scope, engine->qmlContext()); if (!global) global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, property->coreIndex); + return QV4::QObjectMethod::create(global, object, property->coreIndex()); } else if (property->isSignalHandler()) { QmlSignalHandler::initProto(engine); - return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex)->asReturnedValue(); + return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue(); } else { ExecutionContext *global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, property->coreIndex); + return QV4::QObjectMethod::create(global, object, property->coreIndex()); } } @@ -356,19 +356,19 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje QQmlNotifier *n = 0; QQmlNotifier **nptr = 0; - if (ep && ep->propertyCapture && property->accessors->notifier) + if (ep && ep->propertyCapture && property->accessors()->notifier) nptr = &n; Scope scope(engine); QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(engine, object, *property, nptr)); if (captureRequired && !property->isConstant()) { - if (property->accessors->notifier) { + if (property->accessors()->notifier) { if (n && ep->propertyCapture) ep->propertyCapture->captureProperty(n); } else { if (ep->propertyCapture) - ep->propertyCapture->captureProperty(object, property->coreIndex, property->notifyIndex); + ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex()); } } @@ -376,12 +376,12 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje } if (captureRequired && ep && ep->propertyCapture && !property->isConstant()) - ep->propertyCapture->captureProperty(object, property->coreIndex, property->notifyIndex); + ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex()); if (property->isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); - return vmemo->vmeProperty(property->coreIndex); + return vmemo->vmeProperty(property->coreIndex()); } else if (property->isDirect()) { return LoadProperty<ReadAccessor::Direct>(engine, object, *property, 0); } else { @@ -448,13 +448,13 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP QV4::ScopedFunctionObject f(scope, value); if (f) { if (!f->isBinding()) { - if (!property->isVarProperty() && property->propType != qMetaTypeId<QJSValue>()) { + if (!property->isVarProperty() && property->propType() != qMetaTypeId<QJSValue>()) { // assigning a JS function to a non var or QJSValue property or is not allowed. QString error = QLatin1String("Cannot assign JavaScript function to "); - if (!QMetaType::typeName(property->propType)) + if (!QMetaType::typeName(property->propType())) error += QLatin1String("[unknown property type]"); else - error += QLatin1String(QMetaType::typeName(property->propType)); + error += QLatin1String(QMetaType::typeName(property->propType())); scope.engine->throwError(error); return; } @@ -466,20 +466,20 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP bindingFunction->initBindingLocation(); newBinding = QQmlBinding::create(property, value, object, callingQmlContext); - newBinding->setTarget(object, *property); + newBinding->setTarget(object, *property, nullptr); } } if (newBinding) QQmlPropertyPrivate::setBinding(newBinding); else - QQmlPropertyPrivate::removeBinding(object, property->encodedIndex()); + QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex())); if (!newBinding && property->isVarProperty()) { // allow assignment of "special" values (null, undefined, function) to var properties QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); - vmemo->setVMEProperty(property->coreIndex, value); + vmemo->setVMEProperty(property->coreIndex(), value); return; } @@ -488,44 +488,44 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP int status = -1; \ int flags = 0; \ void *argv[] = { &o, 0, &status, &flags }; \ - QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv); + QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv); if (value.isNull() && property->isQObject()) { PROPERTY_STORE(QObject*, 0); } else if (value.isUndefined() && property->isResettable()) { void *a[] = { 0 }; - QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a); - } else if (value.isUndefined() && property->propType == qMetaTypeId<QVariant>()) { + QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex(), a); + } else if (value.isUndefined() && property->propType() == qMetaTypeId<QVariant>()) { PROPERTY_STORE(QVariant, QVariant()); - } else if (value.isUndefined() && property->propType == QMetaType::QJsonValue) { + } else if (value.isUndefined() && property->propType() == QMetaType::QJsonValue) { PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); - } else if (!newBinding && property->propType == qMetaTypeId<QJSValue>()) { + } else if (!newBinding && property->propType() == qMetaTypeId<QJSValue>()) { PROPERTY_STORE(QJSValue, QJSValue(scope.engine, value.asReturnedValue())); - } else if (value.isUndefined() && property->propType != qMetaTypeId<QQmlScriptString>()) { + } else if (value.isUndefined() && property->propType() != qMetaTypeId<QQmlScriptString>()) { QString error = QLatin1String("Cannot assign [undefined] to "); - if (!QMetaType::typeName(property->propType)) + if (!QMetaType::typeName(property->propType())) error += QLatin1String("[unknown property type]"); else - error += QLatin1String(QMetaType::typeName(property->propType)); + error += QLatin1String(QMetaType::typeName(property->propType())); scope.engine->throwError(error); return; } else if (value.as<FunctionObject>()) { // this is handled by the binding creation above - } else if (property->propType == QMetaType::Int && value.isNumber()) { + } else if (property->propType() == QMetaType::Int && value.isNumber()) { PROPERTY_STORE(int, value.asDouble()); - } else if (property->propType == QMetaType::QReal && value.isNumber()) { + } else if (property->propType() == QMetaType::QReal && value.isNumber()) { PROPERTY_STORE(qreal, qreal(value.asDouble())); - } else if (property->propType == QMetaType::Float && value.isNumber()) { + } else if (property->propType() == QMetaType::Float && value.isNumber()) { PROPERTY_STORE(float, float(value.asDouble())); - } else if (property->propType == QMetaType::Double && value.isNumber()) { + } else if (property->propType() == QMetaType::Double && value.isNumber()) { PROPERTY_STORE(double, double(value.asDouble())); - } else if (property->propType == QMetaType::QString && value.isString()) { + } else if (property->propType() == QMetaType::QString && value.isString()) { PROPERTY_STORE(QString, value.toQStringNoThrow()); } else if (property->isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); - vmemo->setVMEProperty(property->coreIndex, value); - } else if (property->propType == qMetaTypeId<QQmlScriptString>() && (value.isUndefined() || value.isPrimitive())) { + vmemo->setVMEProperty(property->coreIndex(), value); + } else if (property->propType() == qMetaTypeId<QQmlScriptString>() && (value.isUndefined() || value.isPrimitive())) { QQmlScriptString ss(value.toQStringNoThrow(), 0 /* context */, object); if (value.isNumber()) { ss.d->numberValue = value.toNumber(); @@ -540,7 +540,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP if (property->isQList()) v = scope.engine->toVariant(value, qMetaTypeId<QList<QObject *> >()); else - v = scope.engine->toVariant(value, property->propType); + v = scope.engine->toVariant(value, property->propType()); QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) { @@ -548,7 +548,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP if (v.userType() == QVariant::Invalid) valueType = "null"; else valueType = QMetaType::typeName(v.userType()); - const char *targetTypeName = QMetaType::typeName(property->propType); + const char *targetTypeName = QMetaType::typeName(property->propType()); if (!targetTypeName) targetTypeName = "an unregistered type"; @@ -648,6 +648,9 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, con void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value) { + Q_ASSERT(propertyIndex < 0xffff); + Q_ASSERT(propertyIndex >= 0); + if (QQmlData::wasDeleted(object)) return; QQmlData *ddata = QQmlData::get(object, /*create*/false); @@ -1247,6 +1250,7 @@ static int MatchScore(const QV4::Value &actual, int conversionType) } } else if (actual.isNull()) { switch (conversionType) { + case QMetaType::Nullptr: case QMetaType::VoidStar: case QMetaType::QObjectStar: case QMetaType::QJsonValue: @@ -1319,34 +1323,34 @@ static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object, if (!current->isOverload()) return 0; - Q_ASSERT(!current->overrideIndexIsProperty); + Q_ASSERT(!current->overrideIndexIsProperty()); if (propertyCache) { - return propertyCache->method(current->overrideIndex); + return propertyCache->method(current->overrideIndex()); } else { const QMetaObject *mo = object.metaObject(); int methodOffset = mo->methodCount() - QMetaObject_methods(mo); - while (methodOffset > current->overrideIndex) { + while (methodOffset > current->overrideIndex()) { mo = mo->superClass(); methodOffset -= QMetaObject_methods(mo); } // If we've been called before with the same override index, then // we can't go any further... - if (&dummy == current && dummy.coreIndex == current->overrideIndex) + if (&dummy == current && dummy.coreIndex() == current->overrideIndex()) return 0; - QMetaMethod method = mo->method(current->overrideIndex); + QMetaMethod method = mo->method(current->overrideIndex()); dummy.load(method); // Look for overloaded methods QByteArray methodName = method.name(); - for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) { + for (int ii = current->overrideIndex() - 1; ii >= methodOffset; --ii) { if (methodName == mo->method(ii).name()) { dummy.setOverload(true); - dummy.overrideIndexIsProperty = 0; - dummy.overrideIndex = ii; + dummy.setOverrideIndexIsProperty(0); + dummy.setOverrideIndex(ii); return &dummy; } } @@ -1376,9 +1380,9 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ if (data.isConstructor()) args = static_cast<const QQmlStaticMetaObject&>(object).constructorParameterTypes( - data.coreIndex, &storage, &unknownTypeError); + data.coreIndex(), &storage, &unknownTypeError); else - args = object.methodParameterTypes(data.coreIndex, &storage, &unknownTypeError); + args = object.methodParameterTypes(data.coreIndex(), &storage, &unknownTypeError); if (!args) { QString typeName = QString::fromLatin1(unknownTypeError); @@ -1391,11 +1395,11 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ return engine->throwError(error); } - return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs, callType); + return CallMethod(object, data.coreIndex(), returnType, args[0], args + 1, engine, callArgs, callType); } else { - return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs, callType); + return CallMethod(object, data.coreIndex(), returnType, 0, 0, engine, callArgs, callType); } } @@ -1434,7 +1438,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const int methodArgumentCount = 0; int *methodArgTypes = 0; if (attempt->hasArguments()) { - int *args = object.methodParameterTypes(attempt->coreIndex, &storage, 0); + int *args = object.methodParameterTypes(attempt->coreIndex(), &storage, 0); if (!args) // Must be an unknown argument continue; @@ -1471,7 +1475,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const const QQmlPropertyData *candidate = &data; while (candidate) { error += QLatin1String("\n ") + - QString::fromUtf8(object.metaObject()->method(candidate->coreIndex) + QString::fromUtf8(object.metaObject()->method(candidate->coreIndex()) .methodSignature()); candidate = RelatedMethod(object, candidate, dummy, propertyCache); } @@ -1855,7 +1859,7 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const const QMetaMethod moMethod = mo->method(d()->index); method.load(moMethod); - if (method.coreIndex == -1) { + if (method.coreIndex() == -1) { scope.result = QV4::Encode::undefined(); return; } @@ -1866,8 +1870,8 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const for (int ii = d()->index - 1; ii >= methodOffset; --ii) { if (methodName == mo->method(ii).name()) { method.setOverload(true); - method.overrideIndexIsProperty = 0; - method.overrideIndex = ii; + method.setOverrideIndexIsProperty(0); + method.setOverrideIndex(ii); break; } } @@ -1879,7 +1883,7 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const QQmlV4Function *funcptr = &func; void *args[] = { 0, &funcptr }; - object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex, args); + object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex(), args); return; } @@ -1919,7 +1923,7 @@ void Heap::QMetaObjectWrapper::ensureConstructorsCache() { QMetaMethod method = metaObject->constructor(i); QQmlPropertyData d; d.load(method); - d.coreIndex = i; + d.setCoreIndex(i); constructors << d; } } @@ -2006,7 +2010,7 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine int *methodArgTypes = 0; if (attempt.hasArguments()) { QQmlMetaObject::ArgTypeStorage storage; - int *args = object.constructorParameterTypes(attempt.coreIndex, &storage, 0); + int *args = object.constructorParameterTypes(attempt.coreIndex(), &storage, 0); if (!args) // Must be an unknown argument continue; @@ -2042,7 +2046,7 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine for (int i = 0; i < numberOfConstructors; i++) { const QQmlPropertyData & candidate = d()->constructors.at(i); error += QLatin1String("\n ") + - QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex) + QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex()) .methodSignature()); } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index a69874cacb..c95e767bb0 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -259,7 +259,7 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->append(QString(decpt - result->length(), zero)); } - if (sign) + if (sign && num) result->prepend(QLatin1Char('-')); return; @@ -1716,6 +1716,8 @@ ReturnedValue Runtime::method_ushr(const Value &left, const Value &right) return Encode(res); } +#endif // V4_BOOTSTRAP + ReturnedValue Runtime::method_greaterThan(const Value &left, const Value &right) { TRACE2(left, right); @@ -1824,8 +1826,6 @@ Bool Runtime::method_toBoolean(const Value &value) return value.toBoolean(); } -#endif // V4_BOOTSTRAP - } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index cbc7a2ddc1..582cdcf4e9 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -58,8 +58,41 @@ namespace QV4 { struct NoThrowEngine; +namespace { +template <typename T> +struct ExceptionCheck { + enum { NeedsCheck = 1 }; +}; +// push_catch and pop context methods shouldn't check for exceptions +template <> +struct ExceptionCheck<void (*)(QV4::ExecutionEngine *)> { + enum { NeedsCheck = 0 }; +}; +template <typename A> +struct ExceptionCheck<void (*)(A, QV4::NoThrowEngine)> { + enum { NeedsCheck = 0 }; +}; +template <> +struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *)> { + enum { NeedsCheck = 0 }; +}; +template <typename A> +struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A)> { + enum { NeedsCheck = 0 }; +}; +template <typename A, typename B> +struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A, B)> { + enum { NeedsCheck = 0 }; +}; +template <typename A, typename B, typename C> +struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { + enum { NeedsCheck = 0 }; +}; +} // anonymous namespace + #define RUNTIME_METHOD(returnvalue, name, args) \ typedef returnvalue (*Method_##name)args; \ + enum { Method_##name##_NeedsExceptionCheck = ExceptionCheck<Method_##name>::NeedsCheck }; \ static returnvalue method_##name args; \ const Method_##name name diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index a6d7c3b1ed..53b9ebfe2a 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -48,6 +48,7 @@ #include <QTime> #include <QMap> +#include <QScopedValueRollback> #include <iostream> #include <cstdlib> @@ -574,6 +575,8 @@ void MemoryManager::runGC() return; } + QScopedValueRollback<bool> gcBlocker(m_d->gcBlocked, true); + if (!m_d->gcStats) { mark(); sweep(); diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri index addf1d9ff8..2d4a82e2f4 100644 --- a/src/qml/qml/ftw/ftw.pri +++ b/src/qml/qml/ftw/ftw.pri @@ -13,6 +13,7 @@ HEADERS += \ $$PWD/qflagpointer_p.h \ $$PWD/qlazilyallocated_p.h \ $$PWD/qqmlnullablevalue_p.h \ + $$PWD/qdeferredcleanup_p.h \ SOURCES += \ $$PWD/qintrusivelist.cpp \ diff --git a/src/qml/qml/ftw/qdeferredcleanup_p.h b/src/qml/qml/ftw/qdeferredcleanup_p.h new file mode 100644 index 0000000000..6b59f04a77 --- /dev/null +++ b/src/qml/qml/ftw/qdeferredcleanup_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 QDEFERREDCLEANUP_P_H +#define QDEFERREDCLEANUP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#include <functional> + +QT_BEGIN_NAMESPACE + +struct QDeferredCleanup +{ + std::function<void()> callback; + template <typename Callback> + QDeferredCleanup(Callback &&cb) + : callback(cb) + {} + ~QDeferredCleanup() { callback(); } + QDeferredCleanup(const QDeferredCleanup &) = delete; + QDeferredCleanup &operator=(const QDeferredCleanup &) = delete; +}; + +QT_END_NAMESPACE + +#endif // QDEFERREDCLEANUP_P_H diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 8c342e0592..fef2da753b 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -69,6 +69,7 @@ void QQmlApplicationEnginePrivate::init() q->connect(&statusMapper, SIGNAL(mapped(QObject*)), q, SLOT(_q_finishLoad(QObject*))); q->connect(q, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); + q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit); #ifndef QT_NO_TRANSLATION QTranslator* qtTranslator = new QTranslator; if (qtTranslator->load(QLatin1String("qt_") + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) @@ -134,7 +135,7 @@ void QQmlApplicationEnginePrivate::_q_finishLoad(QObject *o) break; case QQmlComponent::Ready: objects << c->create(); - q->objectCreated(objects.last(), c->url()); + q->objectCreated(objects.constLast(), c->url()); break; case QQmlComponent::Loading: case QQmlComponent::Null: diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index d4a8b87aaa..8ed7641610 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -170,7 +170,11 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) // Check for a binding update loop if (Q_UNLIKELY(updatingFlag())) { - QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), getPropertyData(), 0); + QQmlPropertyData *d = nullptr; + QQmlPropertyData vtd; + getPropertyData(&d, &vtd); + Q_ASSERT(d); + QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), *d, &vtd, 0); QQmlAbstractBinding::printBindingLoopError(p); return; } @@ -205,8 +209,10 @@ protected: QQmlPropertyData::WriteFlags flags, QV4::Scope &, const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL { - QQmlPropertyData pd = getPropertyData(); - pd.writeProperty(*m_target, &binding, flags); + Q_ASSERT(!m_targetIndex.hasValueTypeIndex()); + QQmlPropertyData *pd = nullptr; + getPropertyData(&pd, nullptr); + pd->writeProperty(*m_target, &binding, flags); } }; @@ -260,13 +266,18 @@ protected: Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL { - QQmlPropertyData pd = getPropertyData(); + Q_ASSERT(targetObject()); + + QQmlPropertyData *pd; + QQmlPropertyData vpd; + getPropertyData(&pd, &vpd); + Q_ASSERT(pd); + int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded. if (propertyType == QMetaType::UnknownType) - propertyType = pd.propType; - Q_ASSERT(targetObject()); + propertyType = pd->propType(); - if (Q_LIKELY(!isUndefined && !pd.isValueTypeVirtual())) { + if (Q_LIKELY(!isUndefined && !vpd.isValid())) { switch (propertyType) { case QMetaType::Bool: if (result.isBoolean()) @@ -293,32 +304,34 @@ protected: break; default: if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) { - if (vtw->d()->valueType->typeId == pd.propType) { - return vtw->write(m_target.data(), pd.coreIndex); + if (vtw->d()->valueType->typeId == pd->propType()) { + return vtw->write(m_target.data(), pd->coreIndex()); } } break; } } - return slowWrite(pd, result, isUndefined, flags); + return slowWrite(*pd, vpd, result, isUndefined, flags); } template <typename T> - Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData &pd, QQmlPropertyData::WriteFlags flags) const + Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData *pd, QQmlPropertyData::WriteFlags flags) const { void *o = &value; - return pd.writeProperty(targetObject(), o, flags); + return pd->writeProperty(targetObject(), o, flags); } }; -Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const QV4::Value &result, +Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, + const QQmlPropertyData &valueTypeData, + const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) { QQmlEngine *engine = context()->engine; QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); - int type = core.isValueTypeVirtual() ? core.valueTypePropType : core.propType; + int type = valueTypeData.isValid() ? valueTypeData.propType() : core.propType(); QQmlJavaScriptExpression::DeleteWatcher watcher(this); @@ -330,7 +343,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const Q value = QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QObject *> >()); } else if (result.isNull() && core.isQObject()) { value = QVariant::fromValue((QObject *)0); - } else if (core.propType == qMetaTypeId<QList<QUrl> >()) { + } else if (core.propType() == qMetaTypeId<QList<QUrl> >()) { value = QQmlPropertyPrivate::resolvedUrlSequence(QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QUrl> >()), context()); } else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) { value = QV8Engine::getV4(v8engine)->toVariant(result, type); @@ -349,28 +362,27 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const Q QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_target.data()); Q_ASSERT(vmemo); - vmemo->setVMEProperty(core.coreIndex, result); + vmemo->setVMEProperty(core.coreIndex(), result); } else if (isUndefined && core.isResettable()) { void *args[] = { 0 }; - QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex, args); + QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex(), args); } else if (isUndefined && type == qMetaTypeId<QVariant>()) { - QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, QVariant(), context(), flags); + QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant(), context(), flags); } else if (type == qMetaTypeId<QJSValue>()) { const QV4::FunctionObject *f = result.as<QV4::FunctionObject>(); if (f && f->isBinding()) { delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); return false; } - QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, QVariant::fromValue( + QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant::fromValue( QJSValue(QV8Engine::getV4(v8engine), result.asReturnedValue())), context(), flags); } else if (isUndefined) { - QString errorStr = QLatin1String("Unable to assign [undefined] to "); - if (!QMetaType::typeName(type)) - errorStr += QLatin1String("[unknown property type]"); - else - errorStr += QLatin1String(QMetaType::typeName(type)); - delayedError()->setErrorDescription(errorStr); + const QLatin1String typeName(QMetaType::typeName(type) + ? QMetaType::typeName(type) + : "[unknown property type]"); + delayedError()->setErrorDescription(QLatin1String("Unable to assign [undefined] to ") + + typeName); return false; } else if (const QV4::FunctionObject *f = result.as<QV4::FunctionObject>()) { if (f->isBinding()) @@ -378,7 +390,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const Q else delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var.")); return false; - } else if (!QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, value, context(), flags)) { + } else if (!QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, value, context(), flags)) { if (watcher.wasDeleted()) return true; @@ -386,7 +398,8 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const Q const char *valueType = 0; const char *propertyType = 0; - if (value.userType() == QMetaType::QObjectStar) { + const int userType = value.userType(); + if (userType == QMetaType::QObjectStar) { if (QObject *o = *(QObject *const *)value.constData()) { valueType = o->metaObject()->className(); @@ -394,11 +407,11 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const Q if (!propertyMetaObject.isNull()) propertyType = propertyMetaObject.className(); } - } else if (value.userType() != QVariant::Invalid) { - if (value.userType() == QMetaType::VoidStar) + } else if (userType != QVariant::Invalid) { + if (userType == QMetaType::Nullptr || userType == QMetaType::VoidStar) valueType = "null"; else - valueType = QMetaType::typeName(value.userType()); + valueType = QMetaType::typeName(userType); } if (!valueType) @@ -444,8 +457,7 @@ QString QQmlBinding::expressionIdentifier() QString url = function->sourceFile(); quint16 lineNumber = function->compiledFunction->location.line; quint16 columnNumber = function->compiledFunction->location.column; - - return url + QLatin1Char(':') + QString::number(lineNumber) + QLatin1Char(':') + QString::number(columnNumber); + return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber)); } void QQmlBinding::expressionChanged() @@ -482,10 +494,11 @@ QString QQmlBinding::expression() const void QQmlBinding::setTarget(const QQmlProperty &prop) { - setTarget(prop.object(), QQmlPropertyPrivate::get(prop)->core); + auto pd = QQmlPropertyPrivate::get(prop); + setTarget(prop.object(), pd->core, &pd->valueTypeData); } -void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) +void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) { m_target = object; @@ -494,11 +507,9 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) return; } - QQmlPropertyData pd = core; - - while (pd.isAlias()) { - int coreIndex = pd.coreIndex; - int valueTypeIndex = pd.getValueTypeCoreIndex(); + int coreIndex = core.coreIndex(); + int valueTypeIndex = valueType ? valueType->coreIndex() : -1; + for (bool isAlias = core.isAlias(); isAlias; ) { QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); int aValueTypeIndex; @@ -520,18 +531,10 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) Q_ASSERT(propertyData); m_target = object; - pd = *propertyData; - if (valueTypeIndex != -1) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(pd.propType); - Q_ASSERT(valueTypeMetaObject); - QMetaProperty vtProp = valueTypeMetaObject->property(valueTypeIndex); - pd.setAsValueTypeVirtual(); - pd.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); - pd.valueTypePropType = vtProp.userType(); - pd.valueTypeCoreIndex = valueTypeIndex; - } + isAlias = propertyData->isAlias(); + coreIndex = propertyData->coreIndex(); } - m_targetIndex = pd.encodedIndex(); + m_targetIndex = QQmlPropertyIndex(coreIndex, valueTypeIndex); QQmlData *data = QQmlData::get(*m_target, true); if (!data->propertyCache) { @@ -540,25 +543,24 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) } } -QQmlPropertyData QQmlBinding::getPropertyData() const +void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const { + Q_ASSERT(propertyData); + QQmlData *data = QQmlData::get(*m_target, false); Q_ASSERT(data && data->propertyCache); - QQmlPropertyData *propertyData = data->propertyCache->property(m_targetIndex.coreIndex()); - Q_ASSERT(propertyData); + *propertyData = data->propertyCache->property(m_targetIndex.coreIndex()); + Q_ASSERT(*propertyData); - QQmlPropertyData d = *propertyData; - if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex())) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d.propType); + if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType((*propertyData)->propType()); Q_ASSERT(valueTypeMetaObject); QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex()); - d.setAsValueTypeVirtual(); - d.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); - d.valueTypePropType = vtProp.userType(); - d.valueTypeCoreIndex = m_targetIndex.valueTypeIndex(); + valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp)); + valueTypeData->setPropType(vtProp.userType()); + valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex()); } - return d; } class QObjectPointerBinding: public QQmlNonbindingBinding @@ -574,20 +576,22 @@ protected: Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL { - QQmlPropertyData pd = getPropertyData(); - if (Q_UNLIKELY(isUndefined || pd.isValueTypeVirtual())) - return slowWrite(pd, result, isUndefined, flags); + QQmlPropertyData *pd; + QQmlPropertyData vtpd; + getPropertyData(&pd, &vtpd); + if (Q_UNLIKELY(isUndefined || vtpd.isValid())) + return slowWrite(*pd, vtpd, result, isUndefined, flags); // Check if the result is a QObject: QObject *resultObject = nullptr; QQmlMetaObject resultMo; if (result.isNull()) { // Special case: we can always write a nullptr. Don't bother checking anything else. - return pd.writeProperty(targetObject(), &resultObject, flags); + return pd->writeProperty(targetObject(), &resultObject, flags); } else if (auto wrapper = result.as<QV4::QObjectWrapper>()) { resultObject = wrapper->object(); if (!resultObject) - return pd.writeProperty(targetObject(), &resultObject, flags); + return pd->writeProperty(targetObject(), &resultObject, flags); if (QQmlData *ddata = QQmlData::get(resultObject, false)) resultMo = ddata->propertyCache; if (resultMo.isNull()) { @@ -598,22 +602,22 @@ protected: QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()); resultMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, value.userType()); if (resultMo.isNull()) - return slowWrite(pd, result, isUndefined, flags); + return slowWrite(*pd, vtpd, result, isUndefined, flags); resultObject = *static_cast<QObject *const *>(value.constData()); } else { - return slowWrite(pd, result, isUndefined, flags); + return slowWrite(*pd, vtpd, result, isUndefined, flags); } // Compare & set: if (QQmlMetaObject::canConvert(resultMo, targetMetaObject)) { - return pd.writeProperty(targetObject(), &resultObject, flags); + return pd->writeProperty(targetObject(), &resultObject, flags); } else if (!resultObject && QQmlMetaObject::canConvert(targetMetaObject, resultMo)) { // In the case of a null QObject, we assign the null if there is // any change that the null variant type could be up or down cast to // the property type. - return pd.writeProperty(targetObject(), &resultObject, flags); + return pd->writeProperty(targetObject(), &resultObject, flags); } else { - return slowWrite(pd, result, isUndefined, flags); + return slowWrite(*pd, vtpd, result, isUndefined, flags); } } }; @@ -621,9 +625,9 @@ protected: QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property) { if (property && property->isQObject()) - return new QObjectPointerBinding(engine, property->propType); + return new QObjectPointerBinding(engine, property->propType()); - const int type = (property && property->isFullyResolved()) ? property->propType : QMetaType::UnknownType; + const int type = (property && property->isFullyResolved()) ? property->propType() : QMetaType::UnknownType; if (type == qMetaTypeId<QQmlBinding *>()) { return new QQmlBindingBinding; diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 94fc0ccfc0..1801c3040c 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -81,7 +81,7 @@ public: ~QQmlBinding(); void setTarget(const QQmlProperty &); - void setTarget(QObject *, const QQmlPropertyData &); + void setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); void setNotifyOnValueChanged(bool); @@ -106,11 +106,11 @@ protected: QQmlPropertyData::WriteFlags flags, QV4::Scope &scope, const QV4::ScopedFunctionObject &f) = 0; - QQmlPropertyData getPropertyData() const; + void getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const; int getPropertyType() const; - bool slowWrite(const QQmlPropertyData &core, const QV4::Value &result, bool isUndefined, - QQmlPropertyData::WriteFlags flags); + bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, + const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags); private: inline bool updatingFlag() const; diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index ef837183db..4e63790290 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -80,8 +80,8 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, // Add some leading whitespace to account for the binding's column offset. // It's 2 off because a, we start counting at 1 and b, the '(' below is not counted. - function.fill(QChar(QChar::Space), qMax(column, (quint16)2) - 2); - function += QLatin1String("(function ") + handlerName + QLatin1Char('('); + function += QString(qMax(column, (quint16)2) - 2, QChar(QChar::Space)) + + QLatin1String("(function ") + handlerName + QLatin1Char('('); if (parameterString.isEmpty()) { QString error; diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 9c7b4fe1c0..5a440bdaf4 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -61,6 +61,7 @@ #include <private/qv4objectiterator_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <QDir> #include <QStack> #include <QStringList> #include <QThreadStorage> @@ -549,7 +550,8 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, : QQmlComponent(engine, parent) { Q_D(QQmlComponent); - d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)), mode); + const QUrl url = QDir::isAbsolutePath(fileName) ? QUrl::fromLocalFile(fileName) : d->engine->baseUrl().resolved(QUrl(fileName)); + d->loadUrl(url, mode); } /*! diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 6621f48e20..018841b9b1 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -383,7 +383,7 @@ QVariant QQmlContext::contextProperty(const QString &name) const QQmlPropertyData *property = QQmlPropertyCache::property(data->engine, obj, name, data, local); - if (property) value = obj->metaObject()->property(property->coreIndex).read(obj); + if (property) value = obj->metaObject()->property(property->coreIndex()).read(obj); } if (!value.isValid() && parentContext()) value = parentContext()->contextProperty(name); diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index d9a69a9ca3..dce75c51aa 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -129,14 +129,16 @@ public: quint32 parentFrozen:1; quint32 dummy:21; - // When bindingBitsSize < 32, we store the binding bit flags inside - // bindingBitsValue. When we need more than 32 bits, we allocated + // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside + // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated // sufficient space and use bindingBits to point to it. int bindingBitsSize; + typedef quintptr BindingBitsType; union { - quint32 *bindingBits; - quint32 bindingBitsValue; + BindingBitsType *bindingBits; + BindingBitsType bindingBitsValue; }; + enum { MaxInlineBits = sizeof(BindingBitsType) * 8 }; struct NotifyList { quint64 connectionMask; @@ -236,6 +238,17 @@ private: mutable QQmlDataExtended *extendedData; void flushPendingBindingImpl(QQmlPropertyIndex index); + + Q_ALWAYS_INLINE bool hasBitSet(int bit) const + { + if (bindingBitsSize <= bit) + return false; + + if (bindingBitsSize == MaxInlineBits) + return bindingBitsValue & (BindingBitsType(1) << bit); + else + return bindingBits[bit / MaxInlineBits] & (BindingBitsType(1) << (bit % MaxInlineBits)); + } }; bool QQmlData::wasDeleted(QObject *object) @@ -281,20 +294,18 @@ inline bool QQmlData::signalHasEndpoint(int index) const bool QQmlData::hasBindingBit(int coreIndex) const { - int bit = coreIndex * 2; + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); - return bindingBitsSize > bit && - ((bindingBitsSize == 32) ? (bindingBitsValue & (1 << bit)) : - (bindingBits[bit / 32] & (1 << (bit % 32)))); + return hasBitSet(coreIndex * 2); } -bool QQmlData::hasPendingBindingBit(int index) const +bool QQmlData::hasPendingBindingBit(int coreIndex) const { - int bit = index * 2 + 1; + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); - return bindingBitsSize > bit && - ((bindingBitsSize == 32) ? (bindingBitsValue & (1 << bit)) : - (bindingBits[bit / 32] & (1 << (bit % 32)))); + return hasBitSet(coreIndex * 2 + 1); } void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 4acc74ee43..c939af93ef 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -106,6 +106,9 @@ Q_DECLARE_METATYPE(QQmlProperty) QT_BEGIN_NAMESPACE +typedef QQmlData::BindingBitsType BindingBitsType; +enum { MaxInlineBits = QQmlData::MaxInlineBits }; + void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor) { QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor); @@ -679,7 +682,8 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) QQmlData::QQmlData() : ownedByQml1(false), ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false), hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), - hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(0), notifyList(0), context(0), outerContext(0), + hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), + bindingBitsSize(MaxInlineBits), bindingBitsValue(0), notifyList(0), context(0), outerContext(0), bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0), deferredData(0), propertyCache(0), guards(0), extendedData(0) @@ -980,8 +984,19 @@ QQmlEngine::~QQmlEngine() /*! \fn void QQmlEngine::quit() This signal is emitted when the QML loaded by the engine would like to quit. + + \sa exit() + */ + +/*! \fn void QQmlEngine::exit(int retCode) + This signal is emitted when the QML loaded by the engine would like to exit + from the event loop with the specified return code. + + \since 5.8 + \sa quit() */ + /*! \fn void QQmlEngine::warnings(const QList<QQmlError> &warnings) This signal is emitted when \a warnings messages are generated by QML. */ @@ -1692,7 +1707,7 @@ void QQmlData::destroyed(QObject *object) signalHandler = next; } - if (bindingBitsSize > 32) + if (bindingBitsSize > MaxInlineBits) free(bindingBits); if (propertyCache) @@ -1742,31 +1757,27 @@ void QQmlData::parentChanged(QObject *object, QObject *parent) static void QQmlData_setBit(QQmlData *data, QObject *obj, int bit) { - if (data->bindingBitsSize == 0 && bit < 32) { - data->bindingBitsSize = 32; - } - - if (data->bindingBitsSize <= bit) { + if (Q_UNLIKELY(data->bindingBitsSize <= bit)) { int props = QQmlMetaObject(obj).propertyCount(); Q_ASSERT(bit < 2 * props); - int arraySize = (2 * props + 31) / 32; + int arraySize = (2 * props + MaxInlineBits - 1) / MaxInlineBits; Q_ASSERT(arraySize > 1); // special handling for 32 here is to make sure we wipe the first byte // when going from bindingBitsValue to bindingBits, and preserve the old // set bits so we can restore them after the allocation - int oldArraySize = data->bindingBitsSize > 32 ? data->bindingBitsSize / 32 : 0; - quint32 oldValue = data->bindingBitsSize == 32 ? data->bindingBitsValue : 0; + int oldArraySize = data->bindingBitsSize > MaxInlineBits ? data->bindingBitsSize / MaxInlineBits : 0; + quintptr oldValue = data->bindingBitsSize == MaxInlineBits ? data->bindingBitsValue : 0; - data->bindingBits = (quint32 *)realloc((data->bindingBitsSize == 32) ? 0 : data->bindingBits, - arraySize * sizeof(quint32)); + data->bindingBits = static_cast<BindingBitsType *>(realloc((data->bindingBitsSize == MaxInlineBits) ? 0 : data->bindingBits, + arraySize * sizeof(BindingBitsType))); memset(data->bindingBits + oldArraySize, 0x00, - sizeof(quint32) * (arraySize - oldArraySize)); + sizeof(BindingBitsType) * (arraySize - oldArraySize)); - data->bindingBitsSize = arraySize * 32; + data->bindingBitsSize = arraySize * MaxInlineBits; // reinstate bindingBitsValue after we dropped it if (oldValue) { @@ -1774,19 +1785,19 @@ static void QQmlData_setBit(QQmlData *data, QObject *obj, int bit) } } - if (data->bindingBitsSize == 32) - data->bindingBitsValue |= (1 << (bit % 32)); + if (data->bindingBitsSize == MaxInlineBits) + data->bindingBitsValue |= BindingBitsType(1) << bit; else - data->bindingBits[bit / 32] |= (1 << (bit % 32)); + data->bindingBits[bit / MaxInlineBits] |= (BindingBitsType(1) << (bit % MaxInlineBits)); } static void QQmlData_clearBit(QQmlData *data, int bit) { if (data->bindingBitsSize > bit) { - if (data->bindingBitsSize == 32) - data->bindingBitsValue &= ~(1 << (bit % 32)); + if (data->bindingBitsSize == MaxInlineBits) + data->bindingBitsValue &= ~(BindingBitsType(1) << (bit % MaxInlineBits)); else - data->bindingBits[bit / 32] &= ~(1 << (bit % 32)); + data->bindingBits[bit / MaxInlineBits] &= ~(BindingBitsType(1) << (bit % MaxInlineBits)); } } @@ -1839,6 +1850,14 @@ void QQmlEnginePrivate::sendQuit() } } +void QQmlEnginePrivate::sendExit(int retCode) +{ + Q_Q(QQmlEngine); + if (q->receivers(SIGNAL(exit(int))) == 0) + qWarning("Signal QQmlEngine::exit() emitted, but no receivers connected to handle it."); + emit q->exit(retCode); +} + static void dumpwarning(const QQmlError &error) { QMessageLogger(error.url().toString().toLatin1().constData(), diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 132af78f80..bbb9c36ce9 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -155,6 +155,7 @@ protected: Q_SIGNALS: void quit(); + void exit(int retCode); void warnings(const QList<QQmlError> &warnings); private: diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 949060f395..713b03dbf3 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -227,6 +227,7 @@ public: bool isScriptLoaded(const QUrl &url) const; void sendQuit(); + void sendExit(int retCode = 0); void warning(const QQmlError &); void warning(const QList<QQmlError> &); void warning(QQmlDelayedError *); diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp index 74ceeabeb4..b309550ca8 100644 --- a/src/qml/qml/qqmlerror.cpp +++ b/src/qml/qml/qqmlerror.cpp @@ -249,9 +249,9 @@ QString QQmlError::toString() const int l(line()); if (u.isEmpty() || (u.isLocalFile() && u.path().isEmpty())) - rv = QLatin1String("<Unknown File>"); + rv += QLatin1String("<Unknown File>"); else - rv = u.toString(); + rv += u.toString(); if (l != -1) { rv += QLatin1Char(':') + QString::number(l); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 9393f74a8d..63e8da3aa8 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -399,9 +399,7 @@ bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QSt if (baseUrl.startsWith(importUrl)) { - QString typeUrl(importUrl); - typeUrl.append(fileName); - if (typeUrl == baseUrl) + if (fileName == baseUrl.midRef(importUrl.size())) return false; } @@ -540,10 +538,10 @@ QString QQmlImports::versionString(int vmaj, int vmin, ImportVersion version) { if (version == QQmlImports::FullyVersioned) { // extension with fully encoded version number (eg. MyModule.3.2) - return QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin); + return QString::asprintf(".%d.%d", vmaj, vmin); } else if (version == QQmlImports::PartiallyVersioned) { // extension with encoded version major (eg. MyModule.3) - return QString(QLatin1String(".%1")).arg(vmaj); + return QString::asprintf(".%d", vmaj); } // else extension without version number (eg. MyModule) return QString(); } @@ -1395,15 +1393,9 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix // The uri for this import. For library imports this is the same as uri // specified by the user, but it may be different in the case of file imports. QString importUri = uri; - - QString qmldirPath = importUri; - if (importUri.endsWith(Slash)) - qmldirPath += String_qmldir; - else - qmldirPath += Slash_qmldir; - - QString qmldirUrl = resolveLocalUrl(base, qmldirPath); - + QString qmldirUrl = resolveLocalUrl(base, importUri + (importUri.endsWith(Slash) + ? String_qmldir + : Slash_qmldir)); QString qmldirIdentifier; if (QQmlFile::isLocalFile(qmldirUrl)) { @@ -1701,10 +1693,9 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, if (!resolvedPath.endsWith(Slash)) resolvedPath += Slash; + resolvedPath += prefix + baseName; foreach (const QString &suffix, suffixes) { - QString pluginFileName = prefix + baseName + suffix; - - QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName); + const QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + suffix); if (!absolutePath.isEmpty()) return absolutePath; } @@ -1957,7 +1948,7 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba #ifndef QT_NO_LIBRARY // Dynamic plugins are differentiated by their filepath. For static plugins we // don't have that information so we use their address as key instead. - QString uniquePluginID = QString().sprintf("%p", instance); + const QString uniquePluginID = QString::asprintf("%p", instance); StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); QMutexLocker lock(&plugins->mutex); diff --git a/src/qml/qml/qqmllist.cpp b/src/qml/qml/qqmllist.cpp index d0b7fb4853..a719956483 100644 --- a/src/qml/qml/qqmllist.cpp +++ b/src/qml/qml/qqmllist.cpp @@ -143,16 +143,16 @@ QQmlListReference::QQmlListReference(QObject *object, const char *property, QQml QQmlEnginePrivate *p = engine?QQmlEnginePrivate::get(engine):0; - int listType = p?p->listType(data->propType):QQmlMetaType::listType(data->propType); + int listType = p?p->listType(data->propType()):QQmlMetaType::listType(data->propType()); if (listType == -1) return; d = new QQmlListReferencePrivate; d->object = object; d->elementType = p?p->rawMetaObjectForType(listType):QQmlMetaType::qmlType(listType)->baseMetaObject(); - d->propertyType = data->propType; + d->propertyType = data->propType(); void *args[] = { &d->property, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, data->coreIndex, args); + QMetaObject::metacall(object, QMetaObject::ReadProperty, data->coreIndex(), args); } /*! \internal */ diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 271b4f1b31..ce0f4b798a 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -634,7 +634,7 @@ void QQmlTypePrivate::init() const QMetaObject *mmo = builder.toMetaObject(); mmo->d.superdata = baseMetaObject; if (!metaObjects.isEmpty()) - metaObjects.last().metaObject->d.superdata = mmo; + metaObjects.constLast().metaObject->d.superdata = mmo; QQmlProxyMetaObject::ProxyData data = { mmo, t->d->extraData.cd->extFunc, 0, 0 }; metaObjects << data; } @@ -656,7 +656,7 @@ void QQmlTypePrivate::init() const if (metaObjects.isEmpty()) mo = baseMetaObject; else - mo = metaObjects.first().metaObject; + mo = metaObjects.constFirst().metaObject; for (int ii = 0; !containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) { if (isPropertyRevisioned(mo, ii)) @@ -851,7 +851,7 @@ const QMetaObject *QQmlType::metaObject() const if (d->metaObjects.isEmpty()) return d->baseMetaObject; else - return d->metaObjects.first().metaObject; + return d->metaObjects.constFirst().metaObject; } @@ -1861,7 +1861,7 @@ QQmlType *QQmlMetaType::qmlTypeFromIndex(int idx) if (idx < 0 || idx >= data->types.count()) return 0; - return data->types[idx]; + return data->types.at(idx); } /*! diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 1e1c238b19..f9794ec26a 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -281,7 +281,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | QQmlPropertyData::RemoveBindingOnAliasWrite; QV4::Scope scope(v4); - int propertyType = property->propType; + int propertyType = property->propType(); if (property->isEnum()) { if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) { @@ -302,7 +302,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const double n = binding->valueAsNumber(); if (double(int(n)) == n) { if (property->isVarProperty()) { - _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromInt32(int(n))); + _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromInt32(int(n))); } else { int i = int(n); QVariant value(i); @@ -310,7 +310,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } } else { if (property->isVarProperty()) { - _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromDouble(n)); + _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromDouble(n)); } else { QVariant value(n); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -318,7 +318,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { if (property->isVarProperty()) { - _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromBoolean(binding->valueAsBoolean())); + _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromBoolean(binding->valueAsBoolean())); } else { QVariant value(binding->valueAsBoolean()); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -327,7 +327,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QString stringValue = binding->valueAsString(qmlUnit); if (property->isVarProperty()) { QV4::ScopedString s(scope, v4->newString(stringValue)); - _vmeMetaObject->setVMEProperty(property->coreIndex, s); + _vmeMetaObject->setVMEProperty(property->coreIndex(), s); } else { QVariant value = QQmlStringConverters::variantFromString(stringValue); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -398,7 +398,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); struct { void *data[4]; } buffer; - if (QQml_valueTypeProvider()->storeValueType(property->propType, &colorValue, &buffer, sizeof(buffer))) { + if (QQml_valueTypeProvider()->storeValueType(property->propType(), &colorValue, &buffer, sizeof(buffer))) { property->writeProperty(_qobject, &buffer, propertyWriteFlags); } } @@ -534,26 +534,26 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; default: { // generate single literal value assignment to a list property if required - if (property->propType == qMetaTypeId<QList<qreal> >()) { + if (property->propType() == qMetaTypeId<QList<qreal> >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); QList<qreal> value; value.append(binding->valueAsNumber()); property->writeProperty(_qobject, &value, propertyWriteFlags); break; - } else if (property->propType == qMetaTypeId<QList<int> >()) { + } else if (property->propType() == qMetaTypeId<QList<int> >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double n = binding->valueAsNumber(); QList<int> value; value.append(int(n)); property->writeProperty(_qobject, &value, propertyWriteFlags); break; - } else if (property->propType == qMetaTypeId<QList<bool> >()) { + } else if (property->propType() == qMetaTypeId<QList<bool> >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean); QList<bool> value; value.append(binding->valueAsBoolean()); property->writeProperty(_qobject, &value, propertyWriteFlags); break; - } else if (property->propType == qMetaTypeId<QList<QUrl> >()) { + } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); QString urlString = binding->valueAsString(qmlUnit); QUrl u = urlString.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(urlString)); @@ -561,13 +561,13 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const value.append(u); property->writeProperty(_qobject, &value, propertyWriteFlags); break; - } else if (property->propType == qMetaTypeId<QList<QString> >()) { + } else if (property->propType() == qMetaTypeId<QList<QString> >()) { Q_ASSERT(binding->evaluatesToString()); QList<QString> value; value.append(binding->valueAsString(qmlUnit)); property->writeProperty(_qobject, &value, propertyWriteFlags); break; - } else if (property->propType == qMetaTypeId<QJSValue>()) { + } else if (property->propType() == qMetaTypeId<QJSValue>()) { QJSValue value; if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { value = QJSValue(binding->valueAsBoolean()); @@ -586,12 +586,12 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const // otherwise, try a custom type assignment QString stringValue = binding->valueAsString(qmlUnit); - QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); Q_ASSERT(converter); QVariant value = (*converter)(stringValue); - QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex); - if (value.isNull() || ((int)metaProperty.type() != property->propType && metaProperty.userType() != property->propType)) { + QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex()); + if (value.isNull() || ((int)metaProperty.type() != property->propType() && metaProperty.userType() != property->propType())) { recordError(binding->location, tr("Cannot assign value %1 to property %2").arg(stringValue).arg(QString::fromUtf8(metaProperty.name()))); break; } @@ -623,7 +623,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) if (_compiledObject->idNameIndex) { const QQmlPropertyData *idProperty = propertyData.last(); Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QLatin1String("id")); - if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType == QMetaType::QString) { + if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType() == QMetaType::QString) { QV4::CompiledData::Binding idBinding; idBinding.propertyNameIndex = 0; // Not used idBinding.flags = 0; @@ -636,10 +636,10 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) // ### this is best done through type-compile-time binding skip lists. if (_valueTypeProperty) { - QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex)); + QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex())); if (binding && !binding->isValueTypeProxy()) { - QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex)); + QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex())); } else if (binding) { QQmlValueTypeProxyBinding *proxy = static_cast<QQmlValueTypeProxyBinding *>(binding); @@ -652,7 +652,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { QQmlPropertyData *property = binding->propertyNameIndex != 0 ? _propertyCache->property(stringAt(binding->propertyNameIndex), _qobject, context) : defaultProperty; if (property) - bindingSkipList |= (1 << property->coreIndex); + bindingSkipList |= (1 << property->coreIndex()); } proxy->removeBindings(bindingSkipList); @@ -678,10 +678,10 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) const QQmlPropertyData *property = propertyData.at(i); if (property && property->isQList()) { - if (property->coreIndex != currentListPropertyIndex) { + if (property->coreIndex() != currentListPropertyIndex) { void *argv[1] = { (void*)&_currentList }; - QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex, argv); - currentListPropertyIndex = property->coreIndex; + QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv); + currentListPropertyIndex = property->coreIndex(); } } else if (_currentList.object) { _currentList = QQmlListProperty<void>(); @@ -717,7 +717,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } // ### resolve this at compile time - if (property && property->propType == qMetaTypeId<QQmlScriptString>()) { + if (property && property->propType() == qMetaTypeId<QQmlScriptString>()) { QQmlScriptString ss(binding->valueAsScriptString(qmlUnit), context->asQQmlContext(), _scopeObject); ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line; @@ -730,7 +730,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyData::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; void *argv[] = { &ss, 0, &propertyWriteStatus, &propertyWriteFlags }; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); return true; } @@ -753,20 +753,20 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con const QQmlPropertyData *valueTypeProperty = 0; QObject *bindingTarget = _bindingTarget; - if (QQmlValueTypeFactory::isValueType(property->propType)) { - valueType = QQmlValueTypeFactory::valueType(property->propType); + if (QQmlValueTypeFactory::isValueType(property->propType())) { + valueType = QQmlValueTypeFactory::valueType(property->propType()); if (!valueType) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; } - valueType->read(_qobject, property->coreIndex); + valueType->read(_qobject, property->coreIndex()); groupObject = valueType; valueTypeProperty = property; } else { void *argv[1] = { &groupObject }; - QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex, argv); + QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv); if (!groupObject) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; @@ -779,16 +779,16 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; if (valueType) - valueType->write(_qobject, property->coreIndex, QQmlPropertyData::BypassInterceptor); + valueType->write(_qobject, property->coreIndex(), QQmlPropertyData::BypassInterceptor); return true; } } - if (_ddata->hasBindingBit(property->coreIndex) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + if (_ddata->hasBindingBit(property->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) && !_valueTypeProperty) - QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex)); + QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex())); if (binding->type == QV4::CompiledData::Binding::Type_Script) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; @@ -798,7 +798,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction, /*createProto*/ false)); if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { - int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex); + int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, context, _scopeObject, function); @@ -810,16 +810,18 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // the point property (_qobjectForBindings) and after evaluating the expression, // the result is written to a value type virtual property, that contains the sub-index // of the "x" property. - QQmlPropertyData targetCorePropertyData = *property; - if (_valueTypeProperty) - targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); + QQmlBinding *qmlBinding; + if (_valueTypeProperty) { + qmlBinding = QQmlBinding::create(_valueTypeProperty, function, _scopeObject, context); + qmlBinding->setTarget(_bindingTarget, *_valueTypeProperty, property); + } else { + qmlBinding = QQmlBinding::create(property, function, _scopeObject, context); + qmlBinding->setTarget(_bindingTarget, *property, nullptr); + } - QQmlBinding *qmlBinding = QQmlBinding::create(&targetCorePropertyData, function, _scopeObject, context); sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); - qmlBinding->setTarget(_bindingTarget, targetCorePropertyData); - - if (targetCorePropertyData.isAlias()) { + if (property->isAlias()) { QQmlPropertyPrivate::setBinding(qmlBinding, QQmlPropertyPrivate::DontEnable); } else { qmlBinding->addToObject(); @@ -827,7 +829,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con if (!_valueTypeProperty) { QQmlData *targetDeclarativeData = QQmlData::get(_bindingTarget); Q_ASSERT(targetDeclarativeData); - targetDeclarativeData->setPendingBindingBit(_bindingTarget, property->coreIndex); + targetDeclarativeData->setPendingBindingBit(_bindingTarget, property->coreIndex()); } } } @@ -840,15 +842,16 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlType *type = qmlTypeForObject(createdSubObject); Q_ASSERT(type); - QQmlPropertyData targetCorePropertyData = *property; - if (_valueTypeProperty) - targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); - int valueSourceCast = type->propertyValueSourceCast(); if (valueSourceCast != -1) { QQmlPropertyValueSource *vs = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>(createdSubObject) + valueSourceCast); QObject *target = createdSubObject->parent(); - vs->setTarget(QQmlPropertyPrivate::restore(target, targetCorePropertyData, context)); + QQmlProperty prop; + if (_valueTypeProperty) + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + else + prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + vs->setTarget(prop); return true; } int valueInterceptorCast = type->propertyValueInterceptorCast(); @@ -856,9 +859,11 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(createdSubObject) + valueInterceptorCast); QObject *target = createdSubObject->parent(); - if (targetCorePropertyData.isAlias()) { + QQmlPropertyIndex propertyIndex; + if (property->isAlias()) { + QQmlPropertyIndex originalIndex(property->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); QQmlPropertyIndex propIndex; - QQmlPropertyPrivate::findAliasTarget(target, QQmlPropertyIndex(targetCorePropertyData.coreIndex), &target, &propIndex); + QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); QQmlData *data = QQmlData::get(target); if (!data || !data->propertyCache) { qWarning() << "can't resolve property alias for 'on' assignment"; @@ -866,17 +871,24 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } // we can't have aliasses on subproperties of value types, so: - targetCorePropertyData = *data->propertyCache->property(propIndex.coreIndex()); + QQmlPropertyData targetPropertyData = *data->propertyCache->property(propIndex.coreIndex()); + auto prop = QQmlPropertyPrivate::restore(target, targetPropertyData, nullptr, context); + vi->setTarget(prop); + propertyIndex = QQmlPropertyPrivate::propertyIndex(prop); + } else { + QQmlProperty prop; + if (_valueTypeProperty) + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + else + prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + vi->setTarget(prop); + propertyIndex = QQmlPropertyPrivate::propertyIndex(prop); } - QQmlProperty prop = - QQmlPropertyPrivate::restore(target, targetCorePropertyData, context); - - vi->setTarget(prop); QQmlInterceptorMetaObject *mo = QQmlInterceptorMetaObject::get(target); if (!mo) mo = new QQmlInterceptorMetaObject(target, QQmlData::get(target)->propertyCache); - mo->registerInterceptor(QQmlPropertyPrivate::propertyIndex(prop), vi); + mo->registerInterceptor(propertyIndex, vi); return true; } return false; @@ -894,7 +906,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex); + QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex()); if (!QMetaObject::checkConnectArgs(signalMethod, method)) { recordError(binding->valueLocation, tr("Cannot connect mismatched signal/slot %1 %vs. %2") @@ -903,7 +915,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QQmlPropertyPrivate::connect(_qobject, property->coreIndex, createdSubObject, method.methodIndex()); + QQmlPropertyPrivate::connect(_qobject, property->coreIndex(), createdSubObject, method.methodIndex()); return true; } @@ -912,24 +924,24 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con int propertyWriteStatus = -1; void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags }; - if (const char *iid = QQmlMetaType::interfaceIId(property->propType)) { + if (const char *iid = QQmlMetaType::interfaceIId(property->propType())) { void *ptr = createdSubObject->qt_metacast(iid); if (ptr) { argv[0] = &ptr; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); } else { recordError(binding->location, tr("Cannot assign object to interface property")); return false; } - } else if (property->propType == QMetaType::QVariant) { + } else if (property->propType() == QMetaType::QVariant) { if (property->isVarProperty()) { QV4::Scope scope(v4); QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(QV8Engine::getV4(engine), createdSubObject)); - _vmeMetaObject->setVMEProperty(property->coreIndex, wrappedObject); + _vmeMetaObject->setVMEProperty(property->coreIndex(), wrappedObject); } else { QVariant value = QVariant::fromValue(createdSubObject); argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); } } else if (property->isQList()) { Q_ASSERT(_currentList.object); @@ -937,7 +949,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con void *itemToAdd = createdSubObject; const char *iid = 0; - int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType); + int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType()); if (listItemType != -1) iid = QQmlMetaType::interfaceIId(listItemType); if (iid) @@ -953,7 +965,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else { // pointer compatibility was tested in QQmlPropertyValidator at type compile time argv[0] = &createdSubObject; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); } return true; } @@ -983,7 +995,7 @@ void QQmlObjectCreator::setupFunctions() continue; function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction); - _vmeMetaObject->setVmeMethod(property->coreIndex, function); + _vmeMetaObject->setVmeMethod(property->coreIndex(), function); } } diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp index 0fd9e63bde..49f02476a2 100644 --- a/src/qml/qml/qqmlopenmetaobject.cpp +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -278,7 +278,7 @@ int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void * propertyRead(propId); *reinterpret_cast<QVariant *>(a[0]) = d->getData(propId); } else if (c == QMetaObject::WriteProperty) { - if (propId >= d->data.count() || d->data[propId].first != *reinterpret_cast<QVariant *>(a[0])) { + if (propId >= d->data.count() || d->data.at(propId).first != *reinterpret_cast<QVariant *>(a[0])) { propertyWrite(propId); QPair<QVariant, bool> &prop = d->getDataRef(propId); prop.first = propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0])); diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 33f3b96389..7b1e2ec4f6 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -291,9 +291,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) if (property->isFunction()) return; // Not an object property - if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType)) { + if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType())) { // We're now at a value type property - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType); + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType()); if (!valueTypeMetaObject) return; // Not a value type int idx = valueTypeMetaObject->indexOfProperty(path.last().toUtf8().constData()); @@ -306,10 +306,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) object = currentObject; core = *property; - core.setAsValueTypeVirtual(); - core.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); - core.valueTypePropType = vtProp.userType(); - core.valueTypeCoreIndex = idx; + valueTypeData.setFlags(QQmlPropertyData::flagsForProperty(vtProp)); + valueTypeData.setPropType(vtProp.userType()); + valueTypeData.setCoreIndex(idx); return; } else { @@ -356,9 +355,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) while (d && d->isFunction()) d = ddata->propertyCache->overrideData(d); - if (d && d->notifyIndex != -1) { + if (d && d->notifyIndex() != -1) { object = currentObject; - core = *ddata->propertyCache->signal(d->notifyIndex); + core = *ddata->propertyCache->signal(d->notifyIndex()); return; } } @@ -395,7 +394,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) int QQmlPropertyPrivate::signalIndex() const { Q_ASSERT(type() == QQmlProperty::SignalProperty); - QMetaMethod m = object->metaObject()->method(core.coreIndex); + QMetaMethod m = object->metaObject()->method(core.coreIndex()); return QMetaObjectPrivate::signalIndex(m); } @@ -471,11 +470,11 @@ const char *QQmlProperty::propertyTypeName() const if (!d) return 0; if (d->isValueType()) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType); + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType()); Q_ASSERT(valueTypeMetaObject); - return valueTypeMetaObject->property(d->core.valueTypeCoreIndex).typeName(); + return valueTypeMetaObject->property(d->valueTypeData.coreIndex()).typeName(); } else if (d->object && type() & Property && d->core.isValid()) { - return d->object->metaObject()->property(d->core.coreIndex).typeName(); + return d->object->metaObject()->property(d->core.coreIndex()).typeName(); } else { return 0; } @@ -492,11 +491,8 @@ bool QQmlProperty::operator==(const QQmlProperty &other) const // category is intentially omitted here as it is generated // from the other members return d->object == other.d->object && - d->core.coreIndex == other.d->core.coreIndex && - d->core.isValueTypeVirtual() == other.d->core.isValueTypeVirtual() && - (!d->core.isValueTypeVirtual() || - (d->core.valueTypeCoreIndex == other.d->core.valueTypeCoreIndex && - d->core.valueTypePropType == other.d->core.valueTypePropType)); + d->core.coreIndex() == other.d->core.coreIndex() && + d->valueTypeData.coreIndex() == other.d->valueTypeData.coreIndex(); } /*! @@ -510,16 +506,16 @@ int QQmlProperty::propertyType() const bool QQmlPropertyPrivate::isValueType() const { - return core.isValueTypeVirtual(); + return valueTypeData.isValid(); } int QQmlPropertyPrivate::propertyType() const { uint type = this->type(); if (isValueType()) { - return core.valueTypePropType; + return valueTypeData.propType(); } else if (type & QQmlProperty::Property) { - return core.propType; + return core.propType(); } else { return QVariant::Invalid; } @@ -608,7 +604,7 @@ bool QQmlProperty::isDesignable() const if (!d) return false; if (type() & Property && d->core.isValid() && d->object) - return d->object->metaObject()->property(d->core.coreIndex).isDesignable(); + return d->object->metaObject()->property(d->core.coreIndex()).isDesignable(); else return false; } @@ -648,15 +644,11 @@ QString QQmlProperty::name() const // ### if (!d->object) { } else if (d->isValueType()) { - QString rv = d->core.name(d->object) + QLatin1Char('.'); - - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType); + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType()); Q_ASSERT(valueTypeMetaObject); - const char *vtName = valueTypeMetaObject->property(d->core.valueTypeCoreIndex).name(); - rv += QString::fromUtf8(vtName); - - d->nameCache = rv; + const char *vtName = valueTypeMetaObject->property(d->valueTypeData.coreIndex()).name(); + d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(vtName); } else if (type() & SignalProperty) { QString name = QLatin1String("on") + d->core.name(d->object); name[2] = name.at(2).toUpper(); @@ -679,7 +671,7 @@ QMetaProperty QQmlProperty::property() const if (!d) return QMetaProperty(); if (type() & Property && d->core.isValid() && d->object) - return d->object->metaObject()->property(d->core.coreIndex); + return d->object->metaObject()->property(d->core.coreIndex()); else return QMetaProperty(); } @@ -693,7 +685,7 @@ QMetaMethod QQmlProperty::method() const if (!d) return QMetaMethod(); if (type() & SignalProperty && d->object) - return d->object->metaObject()->method(d->core.coreIndex); + return d->object->metaObject()->method(d->core.coreIndex()); else return QMetaMethod(); } @@ -708,7 +700,8 @@ QQmlPropertyPrivate::binding(const QQmlProperty &that) if (!that.d || !that.isProperty() || !that.d->object) return 0; - return binding(that.d->object, that.d->core.encodedIndex()); + QQmlPropertyIndex thatIndex(that.d->core.coreIndex(), that.d->valueTypeData.coreIndex()); + return binding(that.d->object, thatIndex); } /*! @@ -791,7 +784,7 @@ void QQmlPropertyPrivate::removeBinding(const QQmlProperty &that) if (!that.d || !that.isProperty() || !that.d->object) return; - removeBinding(that.d->object, that.d->core.encodedIndex()); + removeBinding(that.d->object, that.d->encodedIndex()); } QQmlAbstractBinding * @@ -1027,16 +1020,16 @@ QVariant QQmlPropertyPrivate::readValueProperty() { if (isValueType()) { - QQmlValueType *valueType = QQmlValueTypeFactory::valueType(core.propType); + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(core.propType()); Q_ASSERT(valueType); - valueType->read(object, core.coreIndex); - return valueType->metaObject()->property(core.valueTypeCoreIndex).read(valueType); + valueType->read(object, core.coreIndex()); + return valueType->metaObject()->property(valueTypeData.coreIndex()).read(valueType); } else if (core.isQList()) { QQmlListProperty<QObject> prop; core.readProperty(object, &prop); - return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType, engine)); + return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType(), engine)); } else if (core.isQObject()) { @@ -1046,21 +1039,21 @@ QVariant QQmlPropertyPrivate::readValueProperty() } else { - if (!core.propType) // Unregistered type - return object->metaObject()->property(core.coreIndex).read(object); + if (!core.propType()) // Unregistered type + return object->metaObject()->property(core.coreIndex()).read(object); QVariant value; int status = -1; void *args[] = { 0, &value, &status }; - if (core.propType == QMetaType::QVariant) { + if (core.propType() == QMetaType::QVariant) { args[0] = &value; } else { - value = QVariant(core.propType, (void*)0); + value = QVariant(core.propType(), (void*)0); args[0] = value.data(); } core.readPropertyWithArgs(object, args); - if (core.propType != QMetaType::QVariant && args[0] != value.data()) - return QVariant((QVariant::Type)core.propType, args[0]); + if (core.propType() != QMetaType::QVariant && args[0] != value.data()) + return QVariant((QVariant::Type)core.propType(), args[0]); return value; } @@ -1148,38 +1141,28 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, QQmlPropertyData::WriteFlags flags) { - return writeValueProperty(object, core, value, effectiveContext(), flags); + return writeValueProperty(object, core, valueTypeData, value, effectiveContext(), flags); } bool QQmlPropertyPrivate::writeValueProperty(QObject *object, const QQmlPropertyData &core, + const QQmlPropertyData &valueTypeData, const QVariant &value, QQmlContextData *context,QQmlPropertyData::WriteFlags flags) { // Remove any existing bindings on this property if (!(flags & QQmlPropertyData::DontRemoveBinding) && object) - removeBinding(object, core.encodedIndex()); + removeBinding(object, encodedIndex(core, valueTypeData)); bool rv = false; - if (core.isValueTypeVirtual()) { - - QQmlValueType *writeBack = QQmlValueTypeFactory::valueType(core.propType); - writeBack->read(object, core.coreIndex); - - QQmlPropertyData data = core; - data.setFlags(core.valueTypeFlags); - data.coreIndex = core.valueTypeCoreIndex; - data.propType = core.valueTypePropType; - - rv = write(writeBack, data, value, context, flags); - - writeBack->write(object, core.coreIndex, flags); - + if (valueTypeData.isValid()) { + QQmlValueType *writeBack = QQmlValueTypeFactory::valueType(core.propType()); + writeBack->read(object, core.coreIndex()); + rv = write(writeBack, valueTypeData, value, context, flags); + writeBack->write(object, core.coreIndex(), flags); } else { - rv = write(object, core, value, context, flags); - } return rv; @@ -1190,11 +1173,11 @@ bool QQmlPropertyPrivate::write(QObject *object, const QVariant &value, QQmlContextData *context, QQmlPropertyData::WriteFlags flags) { - const int propertyType = property.propType; + const int propertyType = property.propType(); const int variantType = value.userType(); if (property.isEnum()) { - QMetaProperty prop = object->metaObject()->property(property.coreIndex); + QMetaProperty prop = object->metaObject()->property(property.coreIndex()); QVariant v = value; // Enum values come through the script engine as doubles if (variantType == QVariant::Double) { @@ -1203,7 +1186,7 @@ bool QQmlPropertyPrivate::write(QObject *object, if (qFuzzyIsNull(fractional)) v.convert(QVariant::Int); } - return writeEnumProperty(prop, property.coreIndex, object, v, flags); + return writeEnumProperty(prop, property.coreIndex(), object, v, flags); } QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context); @@ -1291,9 +1274,9 @@ bool QQmlPropertyPrivate::write(QObject *object, QQmlMetaObject listType; if (enginePriv) { - listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType)); + listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType())); } else { - QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType)); + QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType())); if (!type) return false; listType = type->baseMetaObject(); @@ -1508,7 +1491,7 @@ bool QQmlProperty::reset() const { if (isResettable()) { void *args[] = { 0 }; - QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex, args); + QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex(), args); return true; } else { return false; @@ -1533,7 +1516,7 @@ bool QQmlPropertyPrivate::write(const QQmlProperty &that, bool QQmlProperty::hasNotifySignal() const { if (type() & Property && d->object) { - return d->object->metaObject()->property(d->core.coreIndex).hasNotifySignal(); + return d->object->metaObject()->property(d->core.coreIndex()).hasNotifySignal(); } return false; } @@ -1563,7 +1546,7 @@ bool QQmlProperty::connectNotifySignal(QObject *dest, int method) const if (!(type() & Property) || !d->object) return false; - QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); + QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex()); if (prop.hasNotifySignal()) { return QQmlPropertyPrivate::connect(d->object, prop.notifySignalIndex(), dest, method, Qt::DirectConnection); } else { @@ -1584,7 +1567,7 @@ bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const if (!(type() & Property) || !d->object) return false; - QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); + QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex()); if (prop.hasNotifySignal()) { QByteArray signal('2' + prop.notifySignal().methodSignature()); return QObject::connect(d->object, signal.constData(), dest, slot); @@ -1598,61 +1581,28 @@ bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const */ int QQmlProperty::index() const { - return d ? d->core.coreIndex : -1; + return d ? d->core.coreIndex() : -1; } QQmlPropertyIndex QQmlPropertyPrivate::propertyIndex(const QQmlProperty &that) { - return that.d ? that.d->core.encodedIndex() : QQmlPropertyIndex(); -} - -/*! - Returns the "property index" for use in bindings. The top 16 bits are the value type - offset, and 0 otherwise. The bottom 16 bits are the regular property index. -*/ -int QQmlPropertyPrivate::bindingIndex(const QQmlProperty &that) -{ - if (!that.d) - return -1; - return bindingIndex(that.d->core); -} - -int QQmlPropertyPrivate::bindingIndex(const QQmlPropertyData &that) -{ - int rv = that.coreIndex; - if (rv != -1 && that.isValueTypeVirtual()) - rv = rv | (that.valueTypeCoreIndex << 16); - return rv; -} - -QQmlPropertyData -QQmlPropertyPrivate::saveValueType(const QQmlPropertyData &base, - const QMetaObject *subObject, int subIndex, - QQmlEngine *) -{ - QMetaProperty subProp = subObject->property(subIndex); - - QQmlPropertyData core = base; - core.setAsValueTypeVirtual(); - core.valueTypeFlags = QQmlPropertyData::flagsForProperty(subProp); - core.valueTypeCoreIndex = subIndex; - core.valueTypePropType = subProp.userType(); - - return core; + return that.d ? that.d->encodedIndex() : QQmlPropertyIndex(); } QQmlProperty QQmlPropertyPrivate::restore(QObject *object, const QQmlPropertyData &data, - QQmlContextData *ctxt) + const QQmlPropertyData *valueTypeData, QQmlContextData *ctxt) { QQmlProperty prop; prop.d = new QQmlPropertyPrivate; prop.d->object = object; prop.d->context = ctxt; - prop.d->engine = ctxt?ctxt->engine:0; + prop.d->engine = ctxt ? ctxt->engine : nullptr; prop.d->core = data; + if (valueTypeData) + prop.d->valueTypeData = *valueTypeData; return prop; } diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index f23426f45c..2565ec0ce6 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -73,12 +73,18 @@ public: QPointer<QObject> object; QQmlPropertyData core; + QQmlPropertyData valueTypeData; bool isNameCached:1; QString nameCache; QQmlPropertyPrivate(); + QQmlPropertyIndex encodedIndex() const + { return encodedIndex(core, valueTypeData); } + static QQmlPropertyIndex encodedIndex(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData) + { return QQmlPropertyIndex(core.coreIndex(), valueTypeData.coreIndex()); } + inline QQmlContextData *effectiveContext() const; void initProperty(QObject *obj, const QString &name); @@ -96,7 +102,7 @@ public: static bool writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, const QVariant &value, int flags); static bool writeValueProperty(QObject *, - const QQmlPropertyData &, + const QQmlPropertyData &, const QQmlPropertyData &valueTypeData, const QVariant &, QQmlContextData *, QQmlPropertyData::WriteFlags flags = 0); static bool write(QObject *, const QQmlPropertyData &, const QVariant &, @@ -116,12 +122,8 @@ public: static void removeBinding(QQmlAbstractBinding *b); static QQmlAbstractBinding *binding(QObject *, QQmlPropertyIndex index); - static QQmlPropertyData saveValueType(const QQmlPropertyData &, - const QMetaObject *, int, - QQmlEngine *); - static QQmlProperty restore(QObject *, - const QQmlPropertyData &, - QQmlContextData *); + static QQmlProperty restore(QObject *, const QQmlPropertyData &, const QQmlPropertyData *, + QQmlContextData *); int signalIndex() const; @@ -139,8 +141,6 @@ public: QQmlBoundSignalExpression *); static bool write(const QQmlProperty &that, const QVariant &, QQmlPropertyData::WriteFlags); static QQmlPropertyIndex propertyIndex(const QQmlProperty &that); - static int bindingIndex(const QQmlProperty &that); - static int bindingIndex(const QQmlPropertyData &that); static QMetaMethod findSignalByName(const QMetaObject *mo, const QByteArray &); static bool connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 9ac137a315..af9b8cc045 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -145,102 +145,102 @@ QQmlPropertyData::flagsForProperty(const QMetaProperty &p, QQmlEngine *engine) void QQmlPropertyData::lazyLoad(const QMetaProperty &p) { - coreIndex = p.propertyIndex(); - notifyIndex = QMetaObjectPrivate::signalIndex(p.notifySignal()); + setCoreIndex(p.propertyIndex()); + setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); Q_ASSERT(p.revision() <= Q_INT16_MAX); - revision = p.revision(); + setRevision(p.revision()); - flags = fastFlagsForProperty(p); + _flags = fastFlagsForProperty(p); int type = static_cast<int>(p.type()); if (type == QMetaType::QObjectStar) { - propType = type; - flags.type = Flags::QObjectDerivedType; + setPropType(type); + _flags.type = Flags::QObjectDerivedType; } else if (type == QMetaType::QVariant) { - propType = type; - flags.type = Flags::QVariantType; + setPropType(type); + _flags.type = Flags::QVariantType; } else if (type == QVariant::UserType || type == -1) { - propTypeName = p.typeName(); - flags.notFullyResolved = true; + _flags.notFullyResolved = true; + setPropTypeName(p.typeName()); } else { - propType = type; + setPropType(type); } } void QQmlPropertyData::load(const QMetaProperty &p, QQmlEngine *engine) { - propType = p.userType(); - coreIndex = p.propertyIndex(); - notifyIndex = QMetaObjectPrivate::signalIndex(p.notifySignal()); - flags = fastFlagsForProperty(p); - flagsForPropertyType(propType, engine, flags); + setPropType(p.userType()); + setCoreIndex(p.propertyIndex()); + setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); + _flags = fastFlagsForProperty(p); + flagsForPropertyType(propType(), engine, _flags); Q_ASSERT(p.revision() <= Q_INT16_MAX); - revision = p.revision(); + setRevision(p.revision()); } void QQmlPropertyData::load(const QMetaMethod &m) { - coreIndex = m.methodIndex(); - arguments = 0; + setCoreIndex(m.methodIndex()); + setArguments(nullptr); - propType = m.returnType(); + setPropType(m.returnType()); - flags.type = Flags::FunctionType; + _flags.type = Flags::FunctionType; if (m.methodType() == QMetaMethod::Signal) - flags.isSignal = true; + _flags.isSignal = true; else if (m.methodType() == QMetaMethod::Constructor) { - flags.isConstructor = true; - propType = QMetaType::QObjectStar; + _flags.isConstructor = true; + setPropType(QMetaType::QObjectStar); } if (m.parameterCount()) { - flags.hasArguments = true; + _flags.hasArguments = true; if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { - flags.isV4Function = true; + _flags.isV4Function = true; } } if (m.attributes() & QMetaMethod::Cloned) - flags.isCloned = true; + _flags.isCloned = true; Q_ASSERT(m.revision() <= Q_INT16_MAX); - revision = m.revision(); + setRevision(m.revision()); } void QQmlPropertyData::lazyLoad(const QMetaMethod &m) { - coreIndex = m.methodIndex(); - propType = QMetaType::Void; - arguments = 0; - flags.type = Flags::FunctionType; + setCoreIndex(m.methodIndex()); + setPropType(QMetaType::Void); + setArguments(nullptr); + _flags.type = Flags::FunctionType; if (m.methodType() == QMetaMethod::Signal) - flags.isSignal = true; + _flags.isSignal = true; else if (m.methodType() == QMetaMethod::Constructor) { - flags.isConstructor = true; - propType = QMetaType::QObjectStar; + _flags.isConstructor = true; + setPropType(QMetaType::QObjectStar); } const char *returnType = m.typeName(); if (!returnType) returnType = "\0"; if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) { - propTypeName = returnType; - flags.notFullyResolved = true; + setPropTypeName(returnType); + _flags.notFullyResolved = true; } const int paramCount = m.parameterCount(); if (paramCount) { - flags.hasArguments = true; + _flags.hasArguments = true; if ((paramCount == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { - flags.isV4Function = true; + _flags.isV4Function = true; } } if (m.attributes() & QMetaMethod::Cloned) - flags.isCloned = true; + _flags.isCloned = true; Q_ASSERT(m.revision() <= Q_INT16_MAX); - revision = m.revision(); + setRevision(m.revision()); } /*! @@ -343,10 +343,10 @@ void QQmlPropertyCache::appendProperty(const QString &name, QQmlPropertyData::Fl int coreIndex, int propType, int notifyIndex) { QQmlPropertyData data; - data.propType = propType; - data.coreIndex = coreIndex; - data.notifyIndex = notifyIndex; - data.flags = flags; + data.setPropType(propType); + data.setCoreIndex(coreIndex); + data.setNotifyIndex(notifyIndex); + data._flags = flags; QQmlPropertyData *old = findNamedProperty(name); if (old) @@ -363,20 +363,20 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag const QList<QByteArray> &names) { QQmlPropertyData data; - data.propType = QVariant::Invalid; - data.coreIndex = coreIndex; - data.flags = flags; - data.arguments = 0; + data.setPropType(QVariant::Invalid); + data.setCoreIndex(coreIndex); + data._flags = flags; + data.setArguments(nullptr); QQmlPropertyData handler = data; - handler.flags.isSignalHandler = true; + handler._flags.isSignalHandler = true; if (types) { int argumentCount = *types; QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names); ::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int)); args->argumentsValid = true; - data.arguments = args; + data.setArguments(args); } QQmlPropertyData *old = findNamedProperty(name); @@ -390,7 +390,7 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag signalHandlerIndexCache.append(handler); QString handlerName = QLatin1String("on") + name; - handlerName[2] = handlerName[2].toUpper(); + handlerName[2] = handlerName.at(2).toUpper(); setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0)); setNamedProperty(handlerName, signalHandlerIndex + signalOffset(), signalHandlerIndexCache.data() + signalHandlerIndex, (old != 0)); @@ -402,16 +402,16 @@ void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flag int argumentCount = names.count(); QQmlPropertyData data; - data.propType = QMetaType::QVariant; - data.coreIndex = coreIndex; + data.setPropType(QMetaType::QVariant); + data.setCoreIndex(coreIndex); QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names); for (int ii = 0; ii < argumentCount; ++ii) args->arguments[ii + 1] = QMetaType::QVariant; args->argumentsValid = true; - data.arguments = args; + data.setArguments(args); - data.flags = flags; + data._flags = flags; QQmlPropertyData *old = findNamedProperty(name); if (old) @@ -570,20 +570,20 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QQmlPropertyData *sigdata = 0; if (m.methodType() == QMetaMethod::Signal) - data->flags = signalFlags; + data->_flags = signalFlags; else - data->flags = methodFlags; + data->_flags = methodFlags; data->lazyLoad(m); - data->flags.isDirect = !dynamicMetaObject; + data->_flags.isDirect = !dynamicMetaObject; Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); - data->metaObjectOffset = allowedRevisionCache.count() - 1; + data->setMetaObjectOffset(allowedRevisionCache.count() - 1); if (data->isSignal()) { sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart]; *sigdata = *data; - sigdata->flags.isSignalHandler = true; + sigdata->_flags.isSignalHandler = true; } QQmlPropertyData *old = 0; @@ -624,8 +624,8 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, if (old) { // We only overload methods in the same class, exactly like C++ - if (old->isFunction() && old->coreIndex >= methodOffset) - data->flags.isOverload = true; + if (old->isFunction() && old->coreIndex() >= methodOffset) + data->_flags.isOverload = true; data->markAsOverrideOf(old); } @@ -652,13 +652,13 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart]; - data->flags = propertyFlags; + data->_flags = propertyFlags; data->lazyLoad(p); - data->flags.isDirect = !dynamicMetaObject; + data->_flags.isDirect = !dynamicMetaObject; Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); - data->metaObjectOffset = allowedRevisionCache.count() - 1; + data->setMetaObjectOffset(allowedRevisionCache.count() - 1); QQmlPropertyData *old = 0; @@ -677,11 +677,11 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QQmlAccessorProperties::Property *accessorProperty = accessorProperties.property(str); // Fast properties may not be overrides or revisioned - Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision == 0)); + Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision() == 0)); if (accessorProperty) { - data->flags.hasAccessors = true; - data->accessors = accessorProperty->accessors; + data->_flags.hasAccessors = true; + data->setAccessors(accessorProperty->accessors); } else if (old) { data->markAsOverrideOf(old); } @@ -692,10 +692,11 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const { Q_ASSERT(data->notFullyResolved()); - data->propType = QMetaType::type(data->propTypeName); + data->setPropType(QMetaType::type(data->propTypeName())); + data->_flags.notFullyResolved = false; if (!data->isFunction()) { - if (data->propType == QMetaType::UnknownType) { + if (data->propType() == QMetaType::UnknownType) { const QMetaObject *mo = _metaObject; QQmlPropertyCache *p = _parent; while (p && (!mo || _ownMetaObject)) { @@ -704,22 +705,20 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const } int propOffset = mo->propertyOffset(); - if (mo && data->coreIndex < propOffset + mo->propertyCount()) { - while (data->coreIndex < propOffset) { + if (mo && data->coreIndex() < propOffset + mo->propertyCount()) { + while (data->coreIndex() < propOffset) { mo = mo->superClass(); propOffset = mo->propertyOffset(); } int registerResult = -1; void *argv[] = { ®isterResult }; - mo->static_metacall(QMetaObject::RegisterPropertyMetaType, data->coreIndex - propOffset, argv); - data->propType = registerResult == -1 ? QMetaType::UnknownType : registerResult; + mo->static_metacall(QMetaObject::RegisterPropertyMetaType, data->coreIndex() - propOffset, argv); + data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult); } } - flagsForPropertyType(data->propType, engine->qmlEngine(), data->flags); + flagsForPropertyType(data->propType(), engine->qmlEngine(), data->_flags); } - - data->flags.notFullyResolved = false; } void QQmlPropertyCache::updateRecur(const QMetaObject *metaObject) @@ -873,33 +872,25 @@ QString QQmlPropertyData::name(QObject *object) const QString QQmlPropertyData::name(const QMetaObject *metaObject) const { - if (!metaObject || coreIndex == -1) + if (!metaObject || coreIndex() == -1) return QString(); if (isFunction()) { - QMetaMethod m = metaObject->method(coreIndex); + QMetaMethod m = metaObject->method(coreIndex()); return QString::fromUtf8(m.name().constData()); } else { - QMetaProperty p = metaObject->property(coreIndex); + QMetaProperty p = metaObject->property(coreIndex()); return QString::fromUtf8(p.name()); } } void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor) { - overrideIndexIsProperty = !predecessor->isFunction(); - overrideIndex = predecessor->coreIndex; - - predecessor->flags.isOverridden = true; -} + setOverrideIndexIsProperty(!predecessor->isFunction()); + setOverrideIndex(predecessor->coreIndex()); -QStringList QQmlPropertyCache::propertyNames() const -{ - QStringList keys; - for (StringCache::ConstIterator iter = stringCache.begin(), cend = stringCache.end(); iter != cend; ++iter) - keys.append(iter.key()); - return keys; + predecessor->_flags.isOverridden = true; } struct StaticQtMetaObject : public QObject @@ -950,7 +941,6 @@ QString QQmlPropertyCache::signalParameterStringForJS(QV4::ExecutionEngine *engi { bool unnamedParameter = false; const QSet<QString> &illegalNames = engine->v8Engine->illegalNames(); - QString error; QString parameters; for (int i = 0; i < parameterNameList.count(); ++i) { @@ -1133,7 +1123,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) { struct Sort { static bool lt(const QPair<QString, QQmlPropertyData *> &lhs, const QPair<QString, QQmlPropertyData *> &rhs) { - return lhs.second->coreIndex < rhs.second->coreIndex; + return lhs.second->coreIndex() < rhs.second->coreIndex(); } }; struct Insert { static void in(QQmlPropertyCache *This, @@ -1144,7 +1134,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) return; if (data->isFunction()) { - if (data->coreIndex < This->methodIndexCacheStart) + if (data->coreIndex() < This->methodIndexCacheStart) return; QPair<QString, QQmlPropertyData *> entry = qMakePair((QString)iter.key(), data); @@ -1154,7 +1144,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) data = This->overrideData(data); if (data && !data->isFunction()) Insert::in(This, properties, methods, iter, data); } else { - if (data->coreIndex < This->propertyIndexCacheStart) + if (data->coreIndex() < This->propertyIndexCacheStart) return; QPair<QString, QQmlPropertyData *> entry = qMakePair((QString)iter.key(), data); @@ -1185,11 +1175,11 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) QQmlPropertyData *data = properties.at(ii).second; int notifierId = -1; - if (data->notifyIndex != -1) - notifierId = data->notifyIndex - signalHandlerIndexCacheStart; + if (data->notifyIndex() != -1) + notifierId = data->notifyIndex() - signalHandlerIndexCacheStart; QMetaPropertyBuilder property = builder.addProperty(properties.at(ii).first.toUtf8(), - QMetaType::typeName(data->propType), + QMetaType::typeName(data->propType()), notifierId); property.setReadable(true); @@ -1201,14 +1191,16 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) QQmlPropertyData *data = methods.at(ii).second; QByteArray returnType; - if (data->propType != 0) - returnType = QMetaType::typeName(data->propType); + if (data->propType() != 0) + returnType = QMetaType::typeName(data->propType()); - QByteArray signature = methods.at(ii).first.toUtf8() + '('; + QByteArray signature; + // '+=' reserves extra capacity. Follow-up appending will be probably free. + signature += methods.at(ii).first.toUtf8() + '('; QQmlPropertyCacheMethodArguments *arguments = 0; if (data->hasArguments()) { - arguments = (QQmlPropertyCacheMethodArguments *)data->arguments; + arguments = (QQmlPropertyCacheMethodArguments *)data->arguments(); Q_ASSERT(arguments->argumentsValid); for (int ii = 0; ii < arguments->arguments[0]; ++ii) { if (ii != 0) signature.append(','); @@ -1235,7 +1227,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) if (!_defaultPropertyName.isEmpty()) { QQmlPropertyData *dp = property(_defaultPropertyName, 0, 0); - if (dp && dp->coreIndex >= propertyIndexCacheStart) { + if (dp && dp->coreIndex() >= propertyIndexCacheStart) { Q_ASSERT(!dp->isFunction()); builder.addClassInfo("DefaultProperty", _defaultPropertyName.toUtf8()); } @@ -1459,7 +1451,7 @@ QList<QByteArray> QQmlPropertyCache::signalParameterNames(int index) const { QQmlPropertyData *signalData = signal(index); if (signalData && signalData->hasArguments()) { - QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments; + QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments(); if (args && args->names) return *args->names; const QMetaMethod &method = QMetaObjectPrivate::signal(firstCppMetaObject(), index); @@ -1561,9 +1553,9 @@ QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const { - Q_ASSERT(!_m.isNull() && data.coreIndex >= 0); + Q_ASSERT(!_m.isNull() && data.coreIndex() >= 0); - int type = data.propType; + int type = data.propType(); const char *propTypeName = 0; @@ -1573,16 +1565,16 @@ int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *u if (_m.isT1()) { QQmlPropertyCache *c = _m.asT1(); - Q_ASSERT(data.coreIndex < c->methodIndexCacheStart + c->methodIndexCache.count()); + Q_ASSERT(data.coreIndex() < c->methodIndexCacheStart + c->methodIndexCache.count()); - while (data.coreIndex < c->methodIndexCacheStart) + while (data.coreIndex() < c->methodIndexCacheStart) c = c->_parent; const QMetaObject *metaObject = c->createMetaObject(); Q_ASSERT(metaObject); - m = metaObject->method(data.coreIndex); + m = metaObject->method(data.coreIndex()); } else { - m = _m.asT2()->method(data.coreIndex); + m = _m.asT2()->method(data.coreIndex()); } type = m.returnType(); @@ -1622,19 +1614,19 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart)); - if (rv->arguments && static_cast<A *>(rv->arguments)->argumentsValid) - return static_cast<A *>(rv->arguments)->arguments; + if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid) + return static_cast<A *>(rv->arguments())->arguments; const QMetaObject *metaObject = c->createMetaObject(); Q_ASSERT(metaObject); QMetaMethod m = metaObject->method(index); int argc = m.parameterCount(); - if (!rv->arguments) { + if (!rv->arguments()) { A *args = c->createArgumentsObject(argc, m.parameterNames()); - rv->arguments = args; + rv->setArguments(args); } - A *args = static_cast<A *>(rv->arguments); + A *args = static_cast<A *>(rv->arguments()); QList<QByteArray> argTypeNames; // Only loaded if needed @@ -1658,7 +1650,7 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, args->arguments[ii + 1] = type; } args->argumentsValid = true; - return static_cast<A *>(rv->arguments)->arguments; + return static_cast<A *>(rv->arguments())->arguments; } else { QMetaMethod m = _m.asT2()->method(index); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index bc2e338348..6d3c4a8a7e 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -64,6 +64,8 @@ #include <private/qv4value_p.h> #include <private/qqmlaccessors_p.h> +#include <limits> + QT_BEGIN_NAMESPACE class QCryptographicHash; @@ -95,8 +97,7 @@ public: QJSValueType = 6, // Property type is a QScriptValue V4HandleType = 7, // Property type is a QQmlV4Handle VarPropertyType = 8, // Property type is a "var" property of VMEMO - ValueTypeVirtualType = 9, // Property is a value type "virtual" property - QVariantType = 10 // Property is a QVariant + QVariantType = 9 // Property is a QVariant }; // Can apply to all properties, except IsFunction @@ -124,106 +125,129 @@ public: // Internal QQmlPropertyCache flags unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved + unsigned overrideIndexIsProperty: 1; - unsigned _padding : 10; // align to 32 bits + unsigned _padding : 9; // align to 32 bits inline Flags(); inline bool operator==(const Flags &other) const; inline void copyPropertyTypeFlags(Flags from); }; - Flags getFlags() const { return flags; } - void setFlags(Flags f) { flags = f; } - - bool isValid() const { return coreIndex != -1; } - - bool isConstant() const { return flags.isConstant; } - bool isWritable() const { return flags.isWritable; } - void setWritable(bool onoff) { flags.isWritable = onoff; } - bool isResettable() const { return flags.isResettable; } - bool isAlias() const { return flags.isAlias; } - bool isFinal() const { return flags.isFinal; } - bool isOverridden() const { return flags.isOverridden; } - bool isDirect() const { return flags.isDirect; } - bool hasAccessors() const { return flags.hasAccessors; } - bool isFunction() const { return flags.type == Flags::FunctionType; } - bool isQObject() const { return flags.type == Flags::QObjectDerivedType; } - bool isEnum() const { return flags.type == Flags::EnumType; } - bool isQList() const { return flags.type == Flags::QListType; } - bool isQmlBinding() const { return flags.type == Flags::QmlBindingType; } - bool isQJSValue() const { return flags.type == Flags::QJSValueType; } - bool isV4Handle() const { return flags.type == Flags::V4HandleType; } - bool isVarProperty() const { return flags.type == Flags::VarPropertyType; } - bool isValueTypeVirtual() const { return flags.type == Flags::ValueTypeVirtualType; } - void setAsValueTypeVirtual() { flags.type = Flags::ValueTypeVirtualType; } - bool isQVariant() const { return flags.type == Flags::QVariantType; } - bool isVMEFunction() const { return flags.isVMEFunction; } - bool hasArguments() const { return flags.hasArguments; } - bool isSignal() const { return flags.isSignal; } - bool isVMESignal() const { return flags.isVMESignal; } - bool isV4Function() const { return flags.isV4Function; } - bool isSignalHandler() const { return flags.isSignalHandler; } - bool isOverload() const { return flags.isOverload; } - void setOverload(bool onoff) { flags.isOverload = onoff; } - bool isCloned() const { return flags.isCloned; } - bool isConstructor() const { return flags.isConstructor; } - - bool hasOverride() const { return flags.type != Flags::ValueTypeVirtualType && - !(flags.hasAccessors) && - overrideIndex >= 0; } - bool hasRevision() const { return !(flags.hasAccessors) && revision != 0; } - - bool isFullyResolved() const { return !flags.notFullyResolved; } - - // Returns -1 if not a value type virtual property - inline int getValueTypeCoreIndex() const; - - // Returns the "encoded" index for use with bindings. Encoding is: - // coreIndex | ((valueTypeCoreIndex + 1) << 16) - inline QQmlPropertyIndex encodedIndex() const; + Flags flags() const { return _flags; } + void setFlags(Flags f) { _flags = f; } + + bool isValid() const { return coreIndex() != -1; } + + bool isConstant() const { return _flags.isConstant; } + bool isWritable() const { return _flags.isWritable; } + void setWritable(bool onoff) { _flags.isWritable = onoff; } + bool isResettable() const { return _flags.isResettable; } + bool isAlias() const { return _flags.isAlias; } + bool isFinal() const { return _flags.isFinal; } + bool isOverridden() const { return _flags.isOverridden; } + bool isDirect() const { return _flags.isDirect; } + bool hasAccessors() const { return _flags.hasAccessors; } + bool isFunction() const { return _flags.type == Flags::FunctionType; } + bool isQObject() const { return _flags.type == Flags::QObjectDerivedType; } + bool isEnum() const { return _flags.type == Flags::EnumType; } + bool isQList() const { return _flags.type == Flags::QListType; } + bool isQmlBinding() const { return _flags.type == Flags::QmlBindingType; } + bool isQJSValue() const { return _flags.type == Flags::QJSValueType; } + bool isV4Handle() const { return _flags.type == Flags::V4HandleType; } + bool isVarProperty() const { return _flags.type == Flags::VarPropertyType; } + bool isQVariant() const { return _flags.type == Flags::QVariantType; } + bool isVMEFunction() const { return _flags.isVMEFunction; } + bool hasArguments() const { return _flags.hasArguments; } + bool isSignal() const { return _flags.isSignal; } + bool isVMESignal() const { return _flags.isVMESignal; } + bool isV4Function() const { return _flags.isV4Function; } + bool isSignalHandler() const { return _flags.isSignalHandler; } + bool isOverload() const { return _flags.isOverload; } + void setOverload(bool onoff) { _flags.isOverload = onoff; } + bool isCloned() const { return _flags.isCloned; } + bool isConstructor() const { return _flags.isConstructor; } + + bool hasOverride() const { return !(_flags.hasAccessors) && + overrideIndex() >= 0; } + bool hasRevision() const { return !(_flags.hasAccessors) && revision() != 0; } + + bool isFullyResolved() const { return !_flags.notFullyResolved; } + + int propType() const { Q_ASSERT(isFullyResolved()); return _propType; } + void setPropType(int pt) { _propType = pt; } + + const char *propTypeName() const { Q_ASSERT(!isFullyResolved()); return _propTypeName; } + void setPropTypeName(const char *ptn) { _propTypeName = ptn; } + + int notifyIndex() const { return _notifyIndex; } + void setNotifyIndex(int idx) { _notifyIndex = idx; } + + QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; } + void setArguments(QQmlPropertyCacheMethodArguments *args) { _arguments = args; } + + int revision() const { return _revision; } + void setRevision(int rev) + { + Q_ASSERT(rev >= std::numeric_limits<qint16>::min()); + Q_ASSERT(rev <= std::numeric_limits<qint16>::max()); + _revision = qint16(rev); + } + + int metaObjectOffset() const { return _metaObjectOffset; } + void setMetaObjectOffset(int off) + { + Q_ASSERT(off >= std::numeric_limits<qint16>::min()); + Q_ASSERT(off <= std::numeric_limits<qint16>::max()); + _metaObjectOffset = qint16(off); + } + + bool overrideIndexIsProperty() const { return _flags.overrideIndexIsProperty; } + void setOverrideIndexIsProperty(bool onoff) { _flags.overrideIndexIsProperty = onoff; } + + int overrideIndex() const { return _overrideIndex; } + void setOverrideIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); + Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); + _overrideIndex = idx; + } + + QQmlAccessors *accessors() const { return _accessors; } + void setAccessors(QQmlAccessors *acc) { _accessors = acc; } + int coreIndex() const { return _coreIndex; } + void setCoreIndex(int idx) { _coreIndex = idx; } + +private: union { - int propType; // When !NotFullyResolved - const char *propTypeName; // When NotFullyResolved + int _propType; // When !NotFullyResolved + const char *_propTypeName; // When NotFullyResolved }; union { // The notify index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). - int notifyIndex; // When !IsFunction - void *arguments; // When IsFunction && HasArguments + int _notifyIndex; // When !IsFunction + QQmlPropertyCacheMethodArguments *_arguments; // When IsFunction && HasArguments }; union { struct { // When !HasAccessors - qint16 revision; - qint16 metaObjectOffset; - - union { - struct { // When IsValueTypeVirtual - quint16 valueTypePropType; // The QVariant::Type of access property on the value - // type proxy object - quint16 valueTypeCoreIndex; // The prop index of the access property on the value - // type proxy object - }; - - struct { // When !IsValueTypeVirtual - uint overrideIndexIsProperty : 1; - signed int overrideIndex : 31; - }; - }; + qint16 _revision; + qint16 _metaObjectOffset; + + signed int _overrideIndex; // When !IsValueTypeVirtual }; struct { // When HasAccessors - QQmlAccessors *accessors; + QQmlAccessors *_accessors; }; }; - int coreIndex; - Flags valueTypeFlags; // flags of the access property on the value type proxy - // object -private: + int _coreIndex; + Flags _flags; + friend class QQmlPropertyData; friend class QQmlPropertyCache; - Flags flags; }; class QQmlPropertyData : public QQmlPropertyRawData @@ -258,20 +282,20 @@ public: inline void readPropertyWithArgs(QObject *target, void *args[]) const { if (hasAccessors()) { - accessors->read(target, args[0]); + accessors()->read(target, args[0]); } else { - QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, args); + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args); } } bool writeProperty(QObject *target, void *value, WriteFlags flags) const { - if (flags.testFlag(BypassInterceptor) && hasAccessors() && accessors->write) { - accessors->write(target, value); + if (flags.testFlag(BypassInterceptor) && hasAccessors() && accessors()->write) { + accessors()->write(target, value); } else { int status = -1; void *argv[] = { value, 0, &status, &flags }; - QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex, argv); + QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv); } return true; } @@ -297,7 +321,7 @@ private: friend class QQmlPropertyCache; void lazyLoad(const QMetaProperty &); void lazyLoad(const QMetaMethod &); - bool notFullyResolved() const { return flags.notFullyResolved; } + bool notFullyResolved() const { return _flags.notFullyResolved; } }; class QQmlPropertyCacheMethodArguments; @@ -347,7 +371,6 @@ public: QQmlPropertyData *method(int) const; QQmlPropertyData *signal(int index) const; int methodIndexToSignalIndex(int) const; - QStringList propertyNames() const; QString defaultPropertyName() const; QQmlPropertyData *defaultProperty() const; @@ -559,6 +582,7 @@ QQmlPropertyRawData::Flags::Flags() , isCloned(false) , isConstructor(false) , notFullyResolved(false) + , overrideIndexIsProperty(false) , _padding(0) {} @@ -581,7 +605,8 @@ bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &ot isOverload == other.isOverload && isCloned == other.isCloned && isConstructor == other.isConstructor && - notFullyResolved == other.notFullyResolved; + notFullyResolved == other.notFullyResolved && + overrideIndexIsProperty == other.overrideIndexIsProperty; } void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flags from) @@ -600,13 +625,12 @@ void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flag QQmlPropertyData::QQmlPropertyData() { - propType = 0; - coreIndex = -1; - notifyIndex = -1; - overrideIndexIsProperty = false; - overrideIndex = -1; - revision = 0; - metaObjectOffset = -1; + setPropType(0); + setNotifyIndex(-1); + setOverrideIndex(-1); + setRevision(0); + setMetaObjectOffset(-1); + setCoreIndex(-1); } QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d) @@ -616,25 +640,11 @@ QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d) bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other) { - return flags == other.flags && - propType == other.propType && - coreIndex == other.coreIndex && - notifyIndex == other.notifyIndex && - revision == other.revision && - (!isValueTypeVirtual() || - (valueTypeCoreIndex == other.valueTypeCoreIndex && - valueTypePropType == other.valueTypePropType)); -} - -int QQmlPropertyRawData::getValueTypeCoreIndex() const -{ - return isValueTypeVirtual()?valueTypeCoreIndex:-1; -} - -QQmlPropertyIndex QQmlPropertyRawData::encodedIndex() const -{ - return isValueTypeVirtual() ? QQmlPropertyIndex(coreIndex, valueTypeCoreIndex) - : QQmlPropertyIndex(coreIndex); + return _flags == other._flags && + propType() == other.propType() && + coreIndex() == other.coreIndex() && + notifyIndex() == other.notifyIndex() && + revision() == other.revision(); } inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const @@ -697,7 +707,7 @@ inline QQmlPropertyData *QQmlPropertyCache::signal(int index) const return _parent->signal(index); QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - signalHandlerIndexCacheStart)); - Q_ASSERT(rv->isSignal() || rv->coreIndex == -1); + Q_ASSERT(rv->isSignal() || rv->coreIndex() == -1); return ensureResolved(rv); } @@ -729,16 +739,16 @@ QQmlPropertyCache::overrideData(QQmlPropertyData *data) const if (!data->hasOverride()) return 0; - if (data->overrideIndexIsProperty) - return property(data->overrideIndex); + if (data->overrideIndexIsProperty()) + return property(data->overrideIndex()); else - return method(data->overrideIndex); + return method(data->overrideIndex()); } bool QQmlPropertyCache::isAllowedInRevision(QQmlPropertyData *data) const { - return (data->hasAccessors() || (data->metaObjectOffset == -1 && data->revision == 0)) || - (allowedRevisionCache[data->metaObjectOffset] >= data->revision); + return (data->hasAccessors() || (data->metaObjectOffset() == -1 && data->revision() == 0)) || + (allowedRevisionCache[data->metaObjectOffset()] >= data->revision()); } int QQmlPropertyCache::propertyCount() const diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp index bbaab1e155..27e3c13ff8 100644 --- a/src/qml/qml/qqmlproxymetaobject.cpp +++ b/src/qml/qml/qqmlproxymetaobject.cpp @@ -45,7 +45,7 @@ QT_BEGIN_NAMESPACE QQmlProxyMetaObject::QQmlProxyMetaObject(QObject *obj, QList<ProxyData> *mList) : metaObjects(mList), proxies(0), parent(0), object(obj) { - *static_cast<QMetaObject *>(this) = *metaObjects->first().metaObject; + *static_cast<QMetaObject *>(this) = *metaObjects->constFirst().metaObject; QObjectPrivate *op = QObjectPrivate::get(obj); if (op->metaObject) @@ -71,7 +71,7 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void if ((c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) && - id >= metaObjects->last().propertyOffset) { + id >= metaObjects->constLast().propertyOffset) { for (int ii = 0; ii < metaObjects->count(); ++ii) { const ProxyData &data = metaObjects->at(ii); @@ -107,7 +107,7 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void } } } else if (c == QMetaObject::InvokeMetaMethod && - id >= metaObjects->last().methodOffset) { + id >= metaObjects->constLast().methodOffset) { QMetaMethod m = object->metaObject()->method(id); if (m.methodType() == QMetaMethod::Signal) { QMetaObject::activate(object, id, a); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 40a2c596e3..f7846f333b 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -51,6 +51,7 @@ #include <private/qqmltypecompiler_p.h> #include <private/qqmlpropertyvalidator_p.h> #include <private/qqmlpropertycachecreator_p.h> +#include <private/qdeferredcleanup_p.h> #include <QtCore/qdir.h> #include <QtCore/qfile.h> @@ -103,22 +104,12 @@ #endif DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); -DEFINE_BOOL_CONFIG_OPTION(_disableDiskCache, QML_DISABLE_DISK_CACHE); +DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE); DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE); Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache") -static bool disableDiskCache() -{ - return _disableDiskCache() - // ### FIXME: Fix crashes on Windows with mmap'ed code. -#if defined(Q_OS_WIN) - || true -#endif - ; -} - QT_BEGIN_NAMESPACE namespace { @@ -130,18 +121,6 @@ namespace { LockHolder(LockType *l) : lock(*l) { lock.lock(); } ~LockHolder() { lock.unlock(); } }; - - struct Defer - { - std::function<void()> callback; - template <typename Callback> - Defer(Callback &&cb) - : callback(cb) - {} - ~Defer() { callback(); } - Defer(const Defer &) = delete; - Defer &operator=(const Defer &) = delete; - }; } #ifndef QT_NO_NETWORK @@ -1473,13 +1452,10 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL bool incomplete = false; - QUrl qmldirUrl; - if (importQualifier.isEmpty()) { - qmldirUrl = finalUrl().resolved(QUrl(importUri + QLatin1String("/qmldir"))); - if (!QQmlImports::isLocal(qmldirUrl)) { - // This is a remote file; the import is currently incomplete - incomplete = true; - } + QUrl qmldirUrl = finalUrl().resolved(QUrl(importUri + QLatin1String("/qmldir"))); + if (!QQmlImports::isLocal(qmldirUrl)) { + // This is a remote file; the import is currently incomplete + incomplete = true; } if (!m_importCache.addFileImport(importDatabase, importUri, importQualifier, import->majorVersion, @@ -2002,8 +1978,7 @@ void QQmlTypeLoader::trimCache() break; while (!unneededTypes.isEmpty()) { - TypeCache::Iterator iter = unneededTypes.last(); - unneededTypes.removeLast(); + TypeCache::Iterator iter = unneededTypes.takeLast(); iter.value()->release(); m_typeCache.erase(iter); @@ -2173,7 +2148,7 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeName void QQmlTypeData::done() { - Defer cleanup([this]{ + QDeferredCleanup cleanup([this]{ m_document.reset(); m_typeReferences.clear(); if (isError()) @@ -2546,13 +2521,13 @@ void QQmlTypeData::resolveTypes() ScriptReference ref; //ref.location = ... - ref.qualifier = script.nameSpace; if (!script.qualifier.isEmpty()) { - ref.qualifier.prepend(script.qualifier + QLatin1Char('.')); - + ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace; // Add a reference to the enclosing namespace m_namespaces.insert(script.qualifier); + } else { + ref.qualifier = script.nameSpace; } ref.script = blob; @@ -2562,12 +2537,13 @@ void QQmlTypeData::resolveTypes() // Lets handle resolved composite singleton types foreach (const QQmlImports::CompositeSingletonReference &csRef, m_importCache.resolvedCompositeSingletons()) { TypeReference ref; - QString typeName = csRef.typeName; - + QString typeName; if (!csRef.prefix.isEmpty()) { - typeName.prepend(csRef.prefix + QLatin1Char('.')); + typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName; // Add a reference to the enclosing namespace m_namespaces.insert(csRef.prefix); + } else { + typeName = csRef.typeName; } int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1; diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 8e87ec7f63..bcefad0ee3 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -88,6 +88,7 @@ bool QQmlValueTypeFactoryImpl::isValueType(int idx) && idx != QVariant::StringList && idx != QMetaType::QObjectStar && idx != QMetaType::VoidStar + && idx != QMetaType::Nullptr && idx != QMetaType::QVariant && idx != QMetaType::QLocale) { return true; @@ -259,7 +260,7 @@ int QQmlValueType::metaCall(QObject *, QMetaObject::Call type, int _id, void **a QString QQmlPointFValueType::toString() const { - return QString(QLatin1String("QPointF(%1, %2)")).arg(v.x()).arg(v.y()); + return QString::asprintf("QPointF(%g, %g)", v.x(), v.y()); } qreal QQmlPointFValueType::x() const @@ -306,7 +307,7 @@ void QQmlPointValueType::setY(int y) QString QQmlSizeFValueType::toString() const { - return QString(QLatin1String("QSizeF(%1, %2)")).arg(v.width()).arg(v.height()); + return QString::asprintf("QSizeF(%g, %g)", v.width(), v.height()); } qreal QQmlSizeFValueType::width() const @@ -352,7 +353,7 @@ void QQmlSizeValueType::setHeight(int h) QString QQmlRectFValueType::toString() const { - return QString(QLatin1String("QRectF(%1, %2, %3, %4)")).arg(v.x()).arg(v.y()).arg(v.width()).arg(v.height()); + return QString::asprintf("QRectF(%g, %g, %g, %g)", v.x(), v.y(), v.width(), v.height()); } qreal QQmlRectFValueType::x() const diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index a1bf692f19..2566ab06b5 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -332,7 +332,8 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(CallContext *ctx) if (QMetaType::convert(w->d()->gadgetPtr, w->d()->valueType->typeId, &convertResult, QMetaType::QString)) { result = convertResult; } else { - result = QString::fromUtf8(QMetaType::typeName(w->d()->valueType->typeId)) + QLatin1Char('('); + result += QString::fromUtf8(QMetaType::typeName(w->d()->valueType->typeId)) + + QLatin1Char('('); const QMetaObject *mo = w->d()->propertyCache->metaObject(); const int propCount = mo->propertyCount(); for (int i = 0; i < propCount; ++i) { @@ -369,10 +370,10 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha if (result->isFunction()) // calling a Q_INVOKABLE function of a value type - return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex); + return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex()); #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ - if (result->propType == metatype) { \ + if (result->propType() == metatype) { \ cpptype v; \ void *args[] = { &v, 0 }; \ metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args); \ @@ -381,7 +382,7 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha const QMetaObject *metaObject = r->d()->propertyCache->metaObject(); - int index = result->coreIndex; + int index = result->coreIndex(); QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index); void *gadget = r->d()->gadgetPtr; @@ -394,10 +395,10 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha QVariant v; void *args[] = { Q_NULLPTR, Q_NULLPTR }; - if (result->propType == QMetaType::QVariant) { + if (result->propType() == QMetaType::QVariant) { args[0] = &v; } else { - v = QVariant(result->propType, static_cast<void *>(Q_NULLPTR)); + v = QVariant(result->propType(), static_cast<void *>(Q_NULLPTR)); args[0] = v.data(); } metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args); @@ -431,8 +432,6 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) const QQmlPropertyData *pd = r->d()->propertyCache->property(name, 0, 0); if (!pd) return; - QMetaProperty property = metaObject->property(pd->coreIndex); - Q_ASSERT(property.isValid()); if (reference) { QV4::ScopedFunctionObject f(scope, value); @@ -449,25 +448,23 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QQmlPropertyData cacheData; cacheData.setWritable(true); - cacheData.setAsValueTypeVirtual(); - cacheData.propType = writeBackPropertyType; - cacheData.coreIndex = reference->d()->property; - cacheData.valueTypeCoreIndex = pd->coreIndex; - cacheData.valueTypePropType = property.userType(); + cacheData.setPropType(writeBackPropertyType); + cacheData.setCoreIndex(reference->d()->property); QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); bindingFunction->initBindingLocation(); QQmlBinding *newBinding = QQmlBinding::create(&cacheData, value, reference->d()->object, context); - newBinding->setTarget(reference->d()->object, cacheData); + newBinding->setTarget(reference->d()->object, cacheData, pd); QQmlPropertyPrivate::setBinding(newBinding); return; } else { - QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex)); - + QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex())); } } + QMetaProperty property = metaObject->property(pd->coreIndex()); + Q_ASSERT(property.isValid()); QVariant v = v4->toVariant(value, property.userType()); diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 81bc6846e6..c8281f02c0 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -164,8 +164,8 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() if (!pd) return; - if (pd->notifyIndex != -1) - connect(target, pd->notifyIndex, ctxt->engine); + if (pd->notifyIndex() != -1) + connect(target, pd->notifyIndex(), ctxt->engine); } metaObject.setFlag(); @@ -225,7 +225,7 @@ bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) continue; const int valueIndex = vi->m_propertyIndex.valueTypeIndex(); - int type = QQmlData::get(object)->propertyCache->property(id)->propType; + int type = QQmlData::get(object)->propertyCache->property(id)->propType(); if (type != QVariant::Invalid) { if (valueIndex != -1) { @@ -883,7 +883,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * return -1; const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); // Value type property - QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType); + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType()); Q_ASSERT(valueType); valueType->read(target, coreIndex); @@ -932,10 +932,10 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * // are not rewritten correctly but this bug is deemed out-of-scope to fix for // performance reasons; see QTBUG-24064) and thus compilation will have failed. QQmlError e; - e.setDescription(QString::fromLatin1("Exception occurred during compilation of " - "function: %1") - .arg(QString::fromUtf8(QMetaObject::method(_id) - .methodSignature()))); + e.setDescription(QLatin1String("Exception occurred during compilation of " + "function: ") + + QString::fromUtf8(QMetaObject::method(_id) + .methodSignature())); ep->warning(e); return -1; // The dynamic method with that id is not available. } diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 201d634411..85e17525a5 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -502,7 +502,7 @@ ReturnedValue NodePrototype::method_get_firstChild(CallContext *ctx) if (r->d()->d->children.isEmpty()) return Encode::null(); else - return Node::create(scope.engine, r->d()->d->children.first()); + return Node::create(scope.engine, r->d()->d->children.constFirst()); } ReturnedValue NodePrototype::method_get_lastChild(CallContext *ctx) @@ -515,7 +515,7 @@ ReturnedValue NodePrototype::method_get_lastChild(CallContext *ctx) if (r->d()->d->children.isEmpty()) return Encode::null(); else - return Node::create(scope.engine, r->d()->d->children.last()); + return Node::create(scope.engine, r->d()->d->children.constLast()); } ReturnedValue NodePrototype::method_get_previousSibling(CallContext *ctx) @@ -1016,8 +1016,8 @@ public: ReturnedValue abort(Object *thisObject, QQmlContextData *context); void addHeader(const QString &, const QString &); - QString header(const QString &name); - QString headers(); + QString header(const QString &name) const; + QString headers() const; QString responseBody(); const QByteArray & rawResponseBody() const; @@ -1144,26 +1144,27 @@ void QQmlXMLHttpRequest::addHeader(const QString &name, const QString &value) } } -QString QQmlXMLHttpRequest::header(const QString &name) +QString QQmlXMLHttpRequest::header(const QString &name) const { - QByteArray utfname = name.toLower().toUtf8(); - - foreach (const HeaderPair &header, m_headersList) { - if (header.first == utfname) - return QString::fromUtf8(header.second); + if (!m_headersList.isEmpty()) { + const QByteArray utfname = name.toLower().toUtf8(); + for (const HeaderPair &header : m_headersList) { + if (header.first == utfname) + return QString::fromUtf8(header.second); + } } return QString(); } -QString QQmlXMLHttpRequest::headers() +QString QQmlXMLHttpRequest::headers() const { QString ret; - foreach (const HeaderPair &header, m_headersList) { + for (const HeaderPair &header : m_headersList) { if (ret.length()) ret.append(QLatin1String("\r\n")); - ret = ret % QString::fromUtf8(header.first) % QLatin1String(": ") - % QString::fromUtf8(header.second); + ret += QString::fromUtf8(header.first) + QLatin1String(": ") + + QString::fromUtf8(header.second); } return ret; } diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index edc83b3bf1..5d70b17ece 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -140,6 +140,7 @@ Heap::QtObject::QtObject(QQmlEngine *qmlEngine) o->defineDefaultProperty(QStringLiteral("darker"), QV4::QtObject::method_darker); o->defineDefaultProperty(QStringLiteral("tint"), QV4::QtObject::method_tint); o->defineDefaultProperty(QStringLiteral("quit"), QV4::QtObject::method_quit); + o->defineDefaultProperty(QStringLiteral("exit"), QV4::QtObject::method_exit); o->defineDefaultProperty(QStringLiteral("createQmlObject"), QV4::QtObject::method_createQmlObject); o->defineDefaultProperty(QStringLiteral("createComponent"), QV4::QtObject::method_createComponent); } @@ -995,6 +996,8 @@ This function causes the QQmlEngine::quit() signal to be emitted. Within the \l {Prototyping with qmlscene}, this causes the launcher application to exit; to quit a C++ application when this method is called, connect the QQmlEngine::quit() signal to the QCoreApplication::quit() slot. + +\sa exit() */ ReturnedValue QtObject::method_quit(CallContext *ctx) { @@ -1003,6 +1006,28 @@ ReturnedValue QtObject::method_quit(CallContext *ctx) } /*! + \qmlmethod Qt::exit(int retCode) + + This function causes the QQmlEngine::exit(int) signal to be emitted. + Within the \l {Prototyping with qmlscene}, this causes the launcher application to exit + the specified return code. To exit from the event loop with a specified return code when this + method is called, a C++ application can connect the QQmlEngine::exit(int) signal + to the QCoreApplication::exit(int) slot. + + \sa quit() +*/ +ReturnedValue QtObject::method_exit(CallContext *ctx) +{ + if (ctx->argc() != 1) + V4THROW_ERROR("Qt.exit(): Invalid arguments"); + + int retCode = ctx->args()[0].toNumber(); + + QQmlEnginePrivate::get(ctx->engine()->qmlEngine())->sendExit(retCode); + return QV4::Encode::undefined(); +} + +/*! \qmlmethod object Qt::createQmlObject(string qml, object parent, string filepath) Returns a new object created from the given \a string of QML which will have the specified \a parent, @@ -1035,7 +1060,9 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) struct Error { static ReturnedValue create(QV4::ExecutionEngine *v4, const QList<QQmlError> &errors) { Scope scope(v4); - QString errorstr = QLatin1String("Qt.createQmlObject(): failed to create object: "); + QString errorstr; + // '+=' reserves extra capacity. Follow-up appending will be probably free. + errorstr += QLatin1String("Qt.createQmlObject(): failed to create object: "); QV4::ScopedArrayObject qmlerrors(scope, v4->newArrayObject()); QV4::ScopedObject qmlerror(scope); @@ -1490,15 +1517,13 @@ static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *c result.append(QLatin1Char(' ')); if (ctx->args()[i].as<ArrayObject>()) - result.append(QLatin1Char('[') + ctx->args()[i].toQStringNoThrow() + QLatin1Char(']')); + result += QLatin1Char('[') + ctx->args()[i].toQStringNoThrow() + QLatin1Char(']'); else result.append(ctx->args()[i].toQStringNoThrow()); } - if (printStack) { - result.append(QLatin1Char('\n')); - result.append(jsStack(v4)); - } + if (printStack) + result += QLatin1Char('\n') + jsStack(v4); static QLoggingCategory qmlLoggingCategory("qml"); static QLoggingCategory jsLoggingCategory("js"); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index d29983c476..8c0759679a 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -122,6 +122,7 @@ struct QtObject : Object static ReturnedValue method_btoa(CallContext *ctx); static ReturnedValue method_atob(CallContext *ctx); static ReturnedValue method_quit(CallContext *ctx); + static ReturnedValue method_exit(CallContext *ctx); static ReturnedValue method_resolvedUrl(CallContext *ctx); static ReturnedValue method_createQmlObject(CallContext *ctx); static ReturnedValue method_createComponent(CallContext *ctx); diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro index ebbcbcd1eb..9852861334 100644 --- a/src/qmltest/qmltest.pro +++ b/src/qmltest/qmltest.pro @@ -1,6 +1,6 @@ TARGET = QtQuickTest -DEFINES += QT_NO_URL_CAST_FROM_STRING +DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_FOREACH QT = core testlib-private QT_PRIVATE = quick qml-private gui core-private diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index bc26a19033..0e348eee11 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -144,7 +144,7 @@ void handleCompileErrors(const QFileInfo &fi, QQuickView *view) QTextStream str(&message); str << "\n " << QDir::toNativeSeparators(fi.absoluteFilePath()) << " produced " << errors.size() << " error(s):\n"; - foreach (const QQmlError &e, errors) { + for (const QQmlError &e : errors) { str << " "; if (e.url().isLocalFile()) { str << QDir::toNativeSeparators(e.url().toLocalFile()); @@ -158,11 +158,12 @@ void handleCompileErrors(const QFileInfo &fi, QQuickView *view) str << " Working directory: " << QDir::toNativeSeparators(QDir::current().absolutePath()) << '\n'; if (QQmlEngine *engine = view->engine()) { str << " View: " << view->metaObject()->className() << ", import paths:\n"; - foreach (const QString &i, engine->importPathList()) + const auto importPaths = engine->importPathList(); + for (const QString &i : importPaths) str << " '" << QDir::toNativeSeparators(i) << "'\n"; const QStringList pluginPaths = engine->pluginPathList(); str << " Plugin paths:\n"; - foreach (const QString &p, pluginPaths) + for (const QString &p : pluginPaths) str << " '" << QDir::toNativeSeparators(p) << "'\n"; } qWarning("%s", qPrintable(message)); @@ -338,11 +339,11 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD &eventLoop, SLOT(quit())); view->rootContext()->setContextProperty (QLatin1String("qtest"), QTestRootObject::instance()); // Deprecated. Use QTestRootObject from Qt.test.qtestroot instead - foreach (const QString &path, imports) + for (const QString &path : qAsConst(imports)) view->engine()->addImportPath(path); - foreach (const QString &path, pluginPaths) + for (const QString &path : qAsConst(pluginPaths)) view->engine()->addPluginPath(path); - foreach (const QString &file, files) { + for (const QString &file : qAsConst(files)) { const QFileInfo fi(file); if (!fi.exists()) continue; diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index b924701f2b..c26f641754 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -794,7 +794,7 @@ static QPainter::CompositionMode qt_composite_mode_from_string(const QString &co } else if (compositeOperator == QLatin1String("destination-over")) { return QPainter::CompositionMode_DestinationOver; } else if (compositeOperator == QLatin1String("lighter")) { - return QPainter::CompositionMode_Lighten; + return QPainter::CompositionMode_Plus; } else if (compositeOperator == QLatin1String("copy")) { return QPainter::CompositionMode_Source; } else if (compositeOperator == QLatin1String("xor")) { @@ -857,7 +857,7 @@ static QString qt_composite_mode_to_string(QPainter::CompositionMode op) case QPainter::CompositionMode_Xor: return QStringLiteral("xor"); case QPainter::CompositionMode_Plus: - return QStringLiteral("plus"); + return QStringLiteral("lighter"); case QPainter::CompositionMode_Multiply: return QStringLiteral("qt-multiply"); case QPainter::CompositionMode_Screen: diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h index a82b88f36f..f281cec2d5 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h @@ -70,7 +70,7 @@ public: inline int size() {return commands.size();} inline bool isEmpty() const {return commands.isEmpty(); } inline bool hasNext() const {return cmdIdx < commands.size(); } - inline QQuickContext2D::PaintCommand takeNextCommand() { return commands[cmdIdx++]; } + inline QQuickContext2D::PaintCommand takeNextCommand() { return commands.at(cmdIdx++); } inline qreal takeGlobalAlpha() { return takeReal(); } inline QPainter::CompositionMode takeGlobalCompositeOperation(){ return static_cast<QPainter::CompositionMode>(takeInt()); } @@ -227,20 +227,20 @@ public: colors << color; } - inline QTransform takeMatrix() { return matrixes[matrixIdx++]; } + inline QTransform takeMatrix() { return matrixes.at(matrixIdx++); } - inline QRectF takeRect() { return rects[rectIdx++]; } + inline QRectF takeRect() { return rects.at(rectIdx++); } - inline QPainterPath takePath() { return pathes[pathIdx++]; } + inline QPainterPath takePath() { return pathes.at(pathIdx++); } - inline const QImage& takeImage() { return images[imageIdx++]; } - inline QQmlRefPointer<QQuickCanvasPixmap> takePixmap() { return pixmaps[pixmapIdx++]; } + inline const QImage& takeImage() { return images.at(imageIdx++); } + inline QQmlRefPointer<QQuickCanvasPixmap> takePixmap() { return pixmaps.at(pixmapIdx++); } - inline int takeInt() { return ints[intIdx++]; } - inline bool takeBool() {return bools[boolIdx++]; } - inline qreal takeReal() { return reals[realIdx++]; } - inline QColor takeColor() { return colors[colorIdx++]; } - inline QBrush takeBrush() { return brushes[brushIdx++]; } + inline int takeInt() { return ints.at(intIdx++); } + inline bool takeBool() {return bools.at(boolIdx++); } + inline qreal takeReal() { return reals.at(realIdx++); } + inline QColor takeColor() { return colors.at(colorIdx++); } + inline QBrush takeBrush() { return brushes.at(brushIdx++); } void replay(QPainter* painter, QQuickContext2D::State& state, const QVector2D &scaleFactor); diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 14c0adf393..59faafa023 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -38,9 +38,15 @@ ****************************************************************************/ #include "qquickevents_p_p.h" +#include <QtGui/private/qguiapplication_p.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickwindow_p.h> +#include <private/qdebug_p.h> QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcPointerEvents, "qt.quick.pointer.events") + /*! \qmltype KeyEvent \instantiates QQuickKeyEvent @@ -437,4 +443,500 @@ Item { \l inverted always returns false. */ +typedef QHash<QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash; +Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices) + +Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice, + (QQuickPointerDevice::Mouse, + QQuickPointerDevice::GenericPointer, + QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover, + 1, 3, QLatin1String("core pointer"), 0)) + +typedef QHash<qint64, QQuickPointerDevice *> PointerDeviceForDeviceIdHash; +Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices) + +QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d) +{ + if (g_touchDevices->contains(d)) + return g_touchDevices->value(d); + + QQuickPointerDevice::DeviceType type = QQuickPointerDevice::TouchScreen; + QString name; + int maximumTouchPoints = 10; + QQuickPointerDevice::Capabilities caps = QQuickPointerDevice::Capabilities(QTouchDevice::Position); + if (d) { + QQuickPointerDevice::Capabilities caps = + static_cast<QQuickPointerDevice::Capabilities>(static_cast<int>(d->capabilities()) & 0x0F); + if (d->type() == QTouchDevice::TouchPad) { + type = QQuickPointerDevice::TouchPad; + caps |= QQuickPointerDevice::Scroll; + } + name = d->name(); + maximumTouchPoints = d->maximumTouchPoints(); + } else { + qWarning() << "QQuickWindowPrivate::touchDevice: creating touch device from nullptr device in QTouchEvent"; + } + + QQuickPointerDevice *dev = new QQuickPointerDevice(type, QQuickPointerDevice::Finger, + caps, maximumTouchPoints, 0, name, 0); + g_touchDevices->insert(d, dev); + return dev; +} + +QList<QQuickPointerDevice*> QQuickPointerDevice::touchDevices() +{ + return g_touchDevices->values(); +} + +QQuickPointerDevice *QQuickPointerDevice::genericMouseDevice() +{ + return g_genericMouseDevice; +} + +QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) +{ + auto it = g_tabletDevices->find(id); + if (it != g_tabletDevices->end()) + return it.value(); + + // ### Figure out how to populate the tablet devices + return nullptr; +} + +void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp) +{ + m_scenePos = scenePos; + m_pointId = pointId; + m_valid = true; + m_accept = false; + m_state = static_cast<QQuickEventPoint::State>(state); + m_timestamp = timestamp; + if (state == Qt::TouchPointPressed) + m_pressTimestamp = timestamp; + // TODO calculate velocity +} + +QQuickItem *QQuickEventPoint::grabber() const +{ + return m_grabber.data(); +} + +void QQuickEventPoint::setGrabber(QQuickItem *grabber) +{ + m_grabber = QPointer<QQuickItem>(grabber); +} + +void QQuickEventPoint::setAccepted(bool accepted) +{ + if (m_accept != accepted) { + qCDebug(lcPointerEvents) << this << m_accept << "->" << accepted; + m_accept = accepted; + } +} + +QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) + : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) +{} + +void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong timestamp) +{ + QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp); + m_rotation = tp.rotation(); + m_pressure = tp.pressure(); + m_uniqueId = tp.uniqueId(); +} + +/*! + \internal + \class QQuickPointerEvent + + QQuickPointerEvent is used as a long-lived object to store data related to + an event from a pointing device, such as a mouse, touch or tablet event, + during event delivery. It also provides properties which may be used later + to expose the event to QML, the same as is done with QQuickMouseEvent, + QQuickTouchPoint, QQuickKeyEvent, etc. Since only one event can be + delivered at a time, this class is effectively a singleton. We don't worry + about the QObject overhead because the instances are long-lived: we don't + dynamically create and destroy objects of this type for each event. +*/ + +QQuickPointerEvent::~QQuickPointerEvent() +{} + +QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) +{ + auto ev = static_cast<QMouseEvent*>(event); + m_event = ev; + if (!event) + return this; + + m_device = QQuickPointerDevice::genericMouseDevice(); + m_button = ev->button(); + m_pressedButtons = ev->buttons(); + Qt::TouchPointState state = Qt::TouchPointStationary; + switch (ev->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + state = Qt::TouchPointPressed; + break; + case QEvent::MouseButtonRelease: + state = Qt::TouchPointReleased; + break; + case QEvent::MouseMove: + state = Qt::TouchPointMoved; + break; + default: + break; + } + m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0 + return this; +} + +QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) +{ + auto ev = static_cast<QTouchEvent*>(event); + m_event = ev; + if (!event) + return this; + + m_device = QQuickPointerDevice::touchDevice(ev->device()); + m_button = Qt::NoButton; + m_pressedButtons = Qt::NoButton; + + const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints(); + int newPointCount = tps.count(); + m_touchPoints.reserve(newPointCount); + + for (int i = m_touchPoints.size(); i < newPointCount; ++i) + m_touchPoints.insert(i, new QQuickEventTouchPoint(this)); + + // Make sure the grabbers are right from one event to the next + QVector<QQuickItem*> grabbers; + // Copy all grabbers, because the order of points might have changed in the event. + // The ID is all that we can rely on (release might remove the first point etc). + for (int i = 0; i < newPointCount; ++i) { + QQuickItem *grabber = nullptr; + if (auto point = pointById(tps.at(i).id())) + grabber = point->grabber(); + grabbers.append(grabber); + } + + for (int i = 0; i < newPointCount; ++i) { + auto point = m_touchPoints.at(i); + point->reset(tps.at(i), ev->timestamp()); + if (point->state() == QQuickEventPoint::Pressed) { + if (grabbers.at(i)) + qWarning() << "TouchPointPressed without previous release event" << point; + point->setGrabber(nullptr); + } else { + point->setGrabber(grabbers.at(i)); + } + } + m_pointCount = newPointCount; + return this; +} + +QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const { + if (i == 0) + return m_mousePoint; + return nullptr; +} + +QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { + if (i >= 0 && i < m_pointCount) + return m_touchPoints.at(i); + return nullptr; +} + +QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent) + : QObject(parent), m_pointId(0), m_grabber(nullptr), m_timestamp(0), m_pressTimestamp(0), + m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false) +{ + Q_UNUSED(m_reserved); +} + +QQuickPointerEvent *QQuickEventPoint::pointerEvent() const +{ + return static_cast<QQuickPointerEvent *>(parent()); +} + +bool QQuickPointerMouseEvent::allPointsAccepted() const { + return m_mousePoint->isAccepted(); +} + +QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const +{ + auto event = static_cast<QMouseEvent *>(m_event); + event->setLocalPos(localPos); + return event; +} + +QVector<QQuickItem *> QQuickPointerMouseEvent::grabbers() const +{ + QVector<QQuickItem *> result; + if (QQuickItem *grabber = m_mousePoint->grabber()) + result << grabber; + return result; +} + +void QQuickPointerMouseEvent::clearGrabbers() const { + m_mousePoint->setGrabber(nullptr); +} + +bool QQuickPointerMouseEvent::isPressEvent() const +{ + auto me = static_cast<QMouseEvent*>(m_event); + return ((me->type() == QEvent::MouseButtonPress || me->type() == QEvent::MouseButtonDblClick) && + (me->buttons() & me->button()) == me->buttons()); +} + +bool QQuickPointerTouchEvent::allPointsAccepted() const { + for (int i = 0; i < m_pointCount; ++i) { + if (!m_touchPoints.at(i)->isAccepted()) + return false; + } + return true; +} + +QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const +{ + QVector<QQuickItem *> result; + for (int i = 0; i < m_pointCount; ++i) { + auto point = m_touchPoints.at(i); + if (QQuickItem *grabber = point->grabber()) { + if (!result.contains(grabber)) + result << grabber; + } + } + return result; +} + +void QQuickPointerTouchEvent::clearGrabbers() const { + for (auto point: m_touchPoints) + point->setGrabber(nullptr); +} + +bool QQuickPointerTouchEvent::isPressEvent() const +{ + return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointPressed; +} + +QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const +{ + QVector<QPointF> points; + for (int i = 0; i < pointCount(); ++i) { + if (!point(i)->isAccepted() && point(i)->state() == QQuickEventPoint::Pressed) + points << point(i)->scenePos(); + } + return points; +} + +/*! + \internal + Populate the reusable synth-mouse event from one touchpoint. + It's required that isTouchEvent() be true when this is called. + If the touchpoint cannot be found, this returns nullptr. + Ownership of the event is NOT transferred to the caller. +*/ +QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const { + const QTouchEvent::TouchPoint *p = touchPointById(pointID); + if (!p) + return nullptr; + QEvent::Type type; + Qt::MouseButton buttons = Qt::LeftButton; + switch (p->state()) { + case Qt::TouchPointPressed: + type = QEvent::MouseButtonPress; + break; + case Qt::TouchPointMoved: + case Qt::TouchPointStationary: + type = QEvent::MouseMove; + break; + case Qt::TouchPointReleased: + type = QEvent::MouseButtonRelease; + buttons = Qt::NoButton; + break; + default: + Q_ASSERT(false); + return nullptr; + } + m_synthMouseEvent = QMouseEvent(type, relativeTo->mapFromScene(p->scenePos()), + p->scenePos(), p->screenPos(), Qt::LeftButton, buttons, m_event->modifiers()); + m_synthMouseEvent.setAccepted(true); + m_synthMouseEvent.setTimestamp(m_event->timestamp()); + // In the future we will try to always have valid velocity in every QQuickEventPoint. + // QQuickFlickablePrivate::handleMouseMoveEvent() checks for QTouchDevice::Velocity + // and if it is set, then it does not need to do its own velocity calculations. + // That's probably the only usecase for this, so far. Some day Flickable should handle + // pointer events, and then passing touchpoint velocity via QMouseEvent will be obsolete. + // Conveniently (by design), QTouchDevice::Velocity == QQuickPointerDevice.Velocity + // so that we don't need to convert m_device->capabilities(). + if (m_device) + QGuiApplicationPrivate::setMouseEventCapsAndVelocity(&m_synthMouseEvent, m_device->capabilities(), p->velocity()); + QGuiApplicationPrivate::setMouseEventSource(&m_synthMouseEvent, Qt::MouseEventSynthesizedByQt); + return &m_synthMouseEvent; +} + +/*! + \internal + Returns a pointer to the QQuickEventPoint which has the \a pointId as + \l {QQuickEventPoint::pointId}{pointId}. + Returns nullptr if there is no point with that ID. + + \fn QQuickPointerEvent::pointById(quint64 pointId) const +*/ +QQuickEventPoint *QQuickPointerMouseEvent::pointById(quint64 pointId) const { + if (m_mousePoint && pointId == m_mousePoint->pointId()) + return m_mousePoint; + return nullptr; +} + +QQuickEventPoint *QQuickPointerTouchEvent::pointById(quint64 pointId) const { + auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(), + [pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } ); + if (it != m_touchPoints.constEnd()) + return *it; + return nullptr; +} + + +/*! + \internal + Returns a pointer to the original TouchPoint which has the same + \l {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a + QTouchEvent, and if that point is found. Otherwise, returns nullptr. +*/ +const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const { + const QTouchEvent *ev = asTouchEvent(); + if (!ev) + return nullptr; + const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints(); + auto it = std::find_if(tps.constBegin(), tps.constEnd(), + [pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } ); + // return the pointer to the actual TP in QTouchEvent::_touchPoints + return (it == tps.constEnd() ? nullptr : it.operator->()); +} + +/*! + \internal + Make a new QTouchEvent, giving it a subset of the original touch points. + + Returns a nullptr if all points are stationary or there are no points inside the item. +*/ +QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const +{ + QList<QTouchEvent::TouchPoint> touchPoints; + Qt::TouchPointStates eventStates; + // TODO maybe add QQuickItem::mapVector2DFromScene(QVector2D) to avoid needing QQuickItemPrivate here + // Or else just document that velocity is always scene-relative and is not scaled and rotated with the item + // but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity + + QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform()); + for (int i = 0; i < m_pointCount; ++i) { + auto p = m_touchPoints.at(i); + if (p->isAccepted()) + continue; + bool isGrabber = p->grabber() == item; + bool isPressInside = p->state() == QQuickEventPoint::Pressed && item->contains(item->mapFromScene(p->scenePos())); + if (!(isGrabber || isPressInside || isFiltering)) + continue; + + const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId()); + if (tp) { + eventStates |= tp->state(); + QTouchEvent::TouchPoint tpCopy = *tp; + tpCopy.setPos(item->mapFromScene(tpCopy.scenePos())); + tpCopy.setLastPos(item->mapFromScene(tpCopy.lastScenePos())); + tpCopy.setStartPos(item->mapFromScene(tpCopy.startScenePos())); + tpCopy.setRect(item->mapRectFromScene(tpCopy.sceneRect())); + tpCopy.setVelocity(transformMatrix.mapVector(tpCopy.velocity()).toVector2D()); + touchPoints << tpCopy; + } + } + + if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty()) + return nullptr; + + // if all points have the same state, set the event type accordingly + const QTouchEvent &event = *asTouchEvent(); + QEvent::Type eventType = event.type(); + switch (eventStates) { + case Qt::TouchPointPressed: + eventType = QEvent::TouchBegin; + break; + case Qt::TouchPointReleased: + eventType = QEvent::TouchEnd; + break; + default: + eventType = QEvent::TouchUpdate; + break; + } + + QTouchEvent *touchEvent = new QTouchEvent(eventType); + touchEvent->setWindow(event.window()); + touchEvent->setTarget(item); + touchEvent->setDevice(event.device()); + touchEvent->setModifiers(event.modifiers()); + touchEvent->setTouchPoints(touchPoints); + touchEvent->setTouchPointStates(eventStates); + touchEvent->setTimestamp(event.timestamp()); + touchEvent->accept(); + return touchEvent; +} + +QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const +{ + return static_cast<QTouchEvent *>(m_event); +} + +#ifndef QT_NO_DEBUG_STREAM + +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) { + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QQuickPointerDevice("<< dev->name() << ' '; + QtDebugUtils::formatQEnum(dbg, dev->type()); + dbg << ' '; + QtDebugUtils::formatQEnum(dbg, dev->pointerType()); + dbg << " caps:"; + QtDebugUtils::formatQFlags(dbg, dev->capabilities()); + if (dev->type() == QQuickPointerDevice::TouchScreen || + dev->type() == QQuickPointerDevice::TouchPad) + dbg << " maxTouchPoints:" << dev->maximumTouchPoints(); + else + dbg << " buttonCount:" << dev->buttonCount(); + dbg << ')'; + return dbg; +} + +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) { + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QQuickPointerEvent(dev:"; + QtDebugUtils::formatQEnum(dbg, event->device()->type()); + if (event->buttons() != Qt::NoButton) { + dbg << " buttons:"; + QtDebugUtils::formatQEnum(dbg, event->buttons()); + } + dbg << " ["; + int c = event->pointCount(); + for (int i = 0; i < c; ++i) + dbg << event->point(i) << ' '; + dbg << "])"; + return dbg; +} + +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) { + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QQuickEventPoint(valid:" << event->isValid() << " accepted:" << event->isAccepted() + << " state:"; + QtDebugUtils::formatQEnum(dbg, event->state()); + dbg << " scenePos:" << event->scenePos() << " id:" << event->pointId() + << " timeHeld:" << event->timeHeld() << ')'; + return dbg; +} + +#endif + QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 065e025152..61bbb4ecda 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -55,12 +55,20 @@ #include <qqml.h> #include <QtCore/qobject.h> +#include <QtCore/qpointer.h> #include <QtGui/qvector2d.h> #include <QtGui/qevent.h> #include <QtGui/qkeysequence.h> +#include <QtQuick/qquickitem.h> QT_BEGIN_NAMESPACE +class QQuickPointerDevice; +class QQuickPointerEvent; +class QQuickPointerMouseEvent; +class QQuickPointerTabletEvent; +class QQuickPointerTouchEvent; + class QQuickKeyEvent : public QObject { Q_OBJECT @@ -238,11 +246,315 @@ private: bool _accepted; }; +class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject +{ + Q_OBJECT + Q_PROPERTY(QPointF scenePos READ scenePos) + Q_PROPERTY(State state READ state) + Q_PROPERTY(quint64 pointId READ pointId) + Q_PROPERTY(qreal timeHeld READ timeHeld) + Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + Q_PROPERTY(QQuickItem *grabber READ grabber WRITE setGrabber) + +public: + enum State { + Pressed = Qt::TouchPointPressed, + Updated = Qt::TouchPointMoved, + Stationary = Qt::TouchPointStationary, + Released = Qt::TouchPointReleased + // Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe + }; + Q_ENUM(State) + + QQuickEventPoint(QQuickPointerEvent *parent); + + void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp); + + void invalidate() { m_valid = false; } + + QQuickPointerEvent *pointerEvent() const; + QPointF scenePos() const { return m_scenePos; } + State state() const { return m_state; } + quint64 pointId() const { return m_pointId; } + bool isValid() const { return m_valid; } + qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } + bool isAccepted() const { return m_accept; } + void setAccepted(bool accepted = true); + QQuickItem *grabber() const; + void setGrabber(QQuickItem *grabber); + +private: + QPointF m_scenePos; + quint64 m_pointId; + QPointer<QQuickItem> m_grabber; + ulong m_timestamp; + ulong m_pressTimestamp; + State m_state; + bool m_valid : 1; + bool m_accept : 1; + int m_reserved : 30; + + Q_DISABLE_COPY(QQuickEventPoint) +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint +{ + Q_OBJECT + Q_PROPERTY(qreal rotation READ rotation) + Q_PROPERTY(qreal pressure READ pressure) + Q_PROPERTY(QPointerUniqueId uniqueId READ uniqueId) + +public: + QQuickEventTouchPoint(QQuickPointerTouchEvent *parent); + + void reset(const QTouchEvent::TouchPoint &tp, ulong timestamp); + + qreal rotation() const { return m_rotation; } + qreal pressure() const { return m_pressure; } + QPointerUniqueId uniqueId() const { return m_uniqueId; } + +private: + qreal m_rotation; + qreal m_pressure; + QPointerUniqueId m_uniqueId; + + Q_DISABLE_COPY(QQuickEventTouchPoint) +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(const QQuickPointerDevice *device READ device) + Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers) + Q_PROPERTY(Qt::MouseButtons button READ button) + Q_PROPERTY(Qt::MouseButtons buttons READ buttons) + +public: + QQuickPointerEvent(QObject *parent = nullptr) + : QObject(parent) + , m_device(nullptr) + , m_event(nullptr) + , m_button(Qt::NoButton) + , m_pressedButtons(Qt::NoButton) + { } + + virtual ~QQuickPointerEvent(); + +public: // property accessors + QQuickPointerDevice *device() const { return m_device; } + Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; } + Qt::MouseButton button() const { return m_button; } + Qt::MouseButtons buttons() const { return m_pressedButtons; } + +public: // helpers for C++ only (during event delivery) + virtual QQuickPointerEvent *reset(QEvent *ev) = 0; + + virtual bool isPressEvent() const = 0; + virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } + virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; } + virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; } + virtual const QQuickPointerMouseEvent *asPointerMouseEvent() const { return nullptr; } + virtual const QQuickPointerTouchEvent *asPointerTouchEvent() const { return nullptr; } + virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } + bool isValid() const { return m_event != nullptr; } + virtual bool allPointsAccepted() const = 0; + bool isAccepted() { return m_event->isAccepted(); } + void setAccepted(bool accepted) { m_event->setAccepted(accepted); } + QVector<QPointF> unacceptedPressedPointScenePositions() const; + + virtual int pointCount() const = 0; + virtual QQuickEventPoint *point(int i) const = 0; + virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; + virtual QVector<QQuickItem *> grabbers() const = 0; + virtual void clearGrabbers() const = 0; + + ulong timestamp() const { return m_event->timestamp(); } + +protected: + QQuickPointerDevice *m_device; + QInputEvent *m_event; // original event as received by QQuickWindow + Qt::MouseButton m_button; + Qt::MouseButtons m_pressedButtons; + + Q_DISABLE_COPY(QQuickPointerEvent) +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent +{ + Q_OBJECT +public: + QQuickPointerMouseEvent(QObject *parent = nullptr) + : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) { } + + QQuickPointerEvent *reset(QEvent *) override; + bool isPressEvent() const override; + QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; } + const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; } + int pointCount() const override { return 1; } + QQuickEventPoint *point(int i) const override; + QQuickEventPoint *pointById(quint64 pointId) const override; + bool allPointsAccepted() const override; + QVector<QQuickItem *> grabbers() const override; + void clearGrabbers() const override; + + QMouseEvent *asMouseEvent(const QPointF& localPos) const; + +private: + QQuickEventPoint *m_mousePoint; + + Q_DISABLE_COPY(QQuickPointerMouseEvent) +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent +{ + Q_OBJECT +public: + QQuickPointerTouchEvent(QObject *parent = nullptr) + : QQuickPointerEvent(parent) + , m_pointCount(0) + , m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier) + { } + + QQuickPointerEvent *reset(QEvent *) override; + bool isPressEvent() const override; + QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; } + const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; } + int pointCount() const override { return m_pointCount; } + QQuickEventPoint *point(int i) const override; + QQuickEventPoint *pointById(quint64 pointId) const override; + const QTouchEvent::TouchPoint *touchPointById(int pointId) const; + bool allPointsAccepted() const override; + QVector<QQuickItem *> grabbers() const override; + void clearGrabbers() const override; + + QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; + QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const; + + QTouchEvent *asTouchEvent() const; + +private: + int m_pointCount; + QVector<QQuickEventTouchPoint *> m_touchPoints; + mutable QMouseEvent m_synthMouseEvent; + + Q_DISABLE_COPY(QQuickPointerTouchEvent) +}; + +// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent +class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject +{ + Q_OBJECT + Q_PROPERTY(DeviceType type READ type CONSTANT) + Q_PROPERTY(PointerType pointerType READ pointerType CONSTANT) + Q_PROPERTY(Capabilities capabilities READ capabilities CONSTANT) + Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints CONSTANT) + Q_PROPERTY(int buttonCount READ buttonCount CONSTANT) + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(qint64 uniqueId READ uniqueId CONSTANT) + +public: + enum DeviceType { + UnknownDevice = 0x0000, + Mouse = 0x0001, + TouchScreen = 0x0002, + TouchPad = 0x0004, + Puck = 0x0008, + Stylus = 0x0010, + Airbrush = 0x0020, + AllDevices = 0x003F + }; + Q_DECLARE_FLAGS(DeviceTypes, DeviceType) + Q_ENUM(DeviceType) + Q_FLAG(DeviceTypes) + + enum PointerType { + GenericPointer = 0x0001, + Finger = 0x0002, + Pen = 0x0004, + Eraser = 0x0008, + Cursor = 0x0010, + AllPointerTypes = 0x001F + }; + Q_DECLARE_FLAGS(PointerTypes, PointerType) + Q_ENUM(PointerType) + Q_FLAG(PointerTypes) + + enum CapabilityFlag { + Position = QTouchDevice::Position, + Area = QTouchDevice::Area, + Pressure = QTouchDevice::Pressure, + Velocity = QTouchDevice::Velocity, + // some bits reserved in case we need more of QTouchDevice::Capabilities + Scroll = 0x0100, // mouse has a wheel, or there is OS-level scroll gesture recognition (dubious?) + Hover = 0x0200, + Rotation = 0x0400, + XTilt = 0x0800, + YTilt = 0x1000 + }; + Q_DECLARE_FLAGS(Capabilities, CapabilityFlag) + Q_ENUM(CapabilityFlag) + Q_FLAG(Capabilities) + + QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) + : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) + , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId), m_event(nullptr) + { + if (m_deviceType == Mouse) { + m_event = new QQuickPointerMouseEvent; + } else if (m_deviceType == TouchScreen || m_deviceType == TouchPad) { + m_event = new QQuickPointerTouchEvent; + } else { + Q_ASSERT(false); + } + } + + ~QQuickPointerDevice() { delete m_event; } + DeviceType type() const { return m_deviceType; } + PointerType pointerType() const { return m_pointerType; } + Capabilities capabilities() const { return m_capabilities; } + bool hasCapability(CapabilityFlag cap) { return m_capabilities & cap; } + int maximumTouchPoints() const { return m_maximumTouchPoints; } + int buttonCount() const { return m_buttonCount; } + QString name() const { return m_name; } + qint64 uniqueId() const { return m_uniqueId; } + QQuickPointerEvent *pointerEvent() const { return m_event; } + + static QQuickPointerDevice *touchDevice(QTouchDevice *d); + static QList<QQuickPointerDevice *> touchDevices(); + static QQuickPointerDevice *genericMouseDevice(); + static QQuickPointerDevice *tabletDevice(qint64); + +private: + DeviceType m_deviceType; + PointerType m_pointerType; + Capabilities m_capabilities; + int m_maximumTouchPoints; + int m_buttonCount; + QString m_name; + qint64 m_uniqueId; + // the device-specific event instance which is reused during event delivery + QQuickPointerEvent *m_event; + + Q_DISABLE_COPY(QQuickPointerDevice) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::PointerTypes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::Capabilities) + +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickPointerDevice *); +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickPointerEvent *); +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickEventPoint *); +//Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickEventTouchPoint *); TODO maybe + QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickKeyEvent) QML_DECLARE_TYPE(QQuickMouseEvent) QML_DECLARE_TYPE(QQuickWheelEvent) QML_DECLARE_TYPE(QQuickCloseEvent) +QML_DECLARE_TYPE(QQuickPointerDevice) +QML_DECLARE_TYPE(QPointerUniqueId) +QML_DECLARE_TYPE(QQuickPointerEvent) #endif // QQUICKEVENTS_P_P_H diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index cb2611199e..b5d5e43f7a 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -478,7 +478,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal qreal colPos = colPosAt(visibleIndex); qreal rowPos = rowPosAt(visibleIndex); if (visibleItems.count()) { - FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last()); + FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.constLast()); rowPos = lastItem->rowPos(); int colNum = qFloor((lastItem->colPos()+colSize()/2) / colSize()); if (++colNum >= columns) { @@ -536,7 +536,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal // Find first column if (visibleItems.count()) { - FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first()); + FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.constFirst()); rowPos = firstItem->rowPos(); colNum = qFloor((firstItem->colPos()+colSize()/2) / colSize()); if (--colNum < 0) { @@ -586,7 +586,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer bool changed = false; while (visibleItems.count() > 1 - && (item = static_cast<FxGridItemSG*>(visibleItems.first())) + && (item = static_cast<FxGridItemSG*>(visibleItems.constFirst())) && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) { if (item->attached->delayRemove()) break; @@ -598,7 +598,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer changed = true; } while (visibleItems.count() > 1 - && (item = static_cast<FxGridItemSG*>(visibleItems.last())) + && (item = static_cast<FxGridItemSG*>(visibleItems.constLast())) && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) { if (item->attached->delayRemove()) break; @@ -623,7 +623,7 @@ void QQuickGridViewPrivate::layoutVisibleItems(int fromModelIndex) const qreal from = isContentFlowReversed() ? -position()-displayMarginBeginning-size() : position()-displayMarginBeginning; const qreal to = isContentFlowReversed() ? -position()+displayMarginEnd : position()+size()+displayMarginEnd; - FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first()); + FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.constFirst()); qreal rowPos = firstItem->rowPos(); qreal colPos = firstItem->colPos(); int col = visibleIndex % columns; @@ -679,7 +679,7 @@ void QQuickGridViewPrivate::repositionPackageItemAt(QQuickItem *item, int index) void QQuickGridViewPrivate::resetFirstItemPosition(qreal pos) { - FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.first()); + FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.constFirst()); item->setPosition(0, pos); } @@ -692,7 +692,7 @@ void QQuickGridViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int if (moveCount == 0 && changeBeforeVisible != 0) moveCount += (changeBeforeVisible % columns) - (columns - 1); - FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.first()); + FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.constFirst()); gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize())); } @@ -2517,7 +2517,7 @@ void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex int markerItemIndex = -1; for (int i=0; i<visibleItems.count(); i++) { - if (visibleItems[i]->index == afterModelIndex) { + if (visibleItems.at(i)->index == afterModelIndex) { markerItemIndex = i; break; } @@ -2536,7 +2536,7 @@ void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex countItemsRemoved -= removalResult.countChangeAfterVisibleItems; for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) { - FxGridItemSG *gridItem = static_cast<FxGridItemSG *>(visibleItems[i]); + FxGridItemSG *gridItem = static_cast<FxGridItemSG *>(visibleItems.at(i)); if (!gridItem->transitionScheduledOrRunning()) { qreal origRowPos = gridItem->colPos(); qreal origColPos = gridItem->rowPos(); diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp index 60e31631c0..a2b99b6395 100644 --- a/src/quick/items/qquickimagebase.cpp +++ b/src/quick/items/qquickimagebase.cpp @@ -355,7 +355,7 @@ void QQuickImageBase::resolve2xLocalFile(const QUrl &url, qreal targetDevicePixe if (disable2xImageLoading) return; - QString localFile = QQmlFile::urlToLocalFileOrQrc(url); + const QString localFile = QQmlFile::urlToLocalFileOrQrc(url); // Non-local file path: @2x loading is not supported. if (localFile.isEmpty()) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 8905faf973..84f9b0f169 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1426,7 +1426,7 @@ void QQuickKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post) d->inIM = true; for (QQuickItem *targetItem : qAsConst(d->targets)) { if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) { - d->item->window()->sendEvent(targetItem, event); + QCoreApplication::sendEvent(targetItem, event); if (event->isAccepted()) { d->imeItem = targetItem; d->inIM = false; diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp index 027c07ec07..5c0caf5ca2 100644 --- a/src/quick/items/qquickitemanimation.cpp +++ b/src/quick/items/qquickitemanimation.cpp @@ -339,9 +339,9 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act qreal w = target->width(); qreal h = target->height(); if (pc->widthIsSet() && i < actions.size() - 1) - w = actions[++i].toValue.toReal(); + w = actions.at(++i).toValue.toReal(); if (pc->heightIsSet() && i < actions.size() - 1) - h = actions[++i].toValue.toReal(); + h = actions.at(++i).toValue.toReal(); const QPointF &transformOrigin = d->computeTransformOrigin(target->transformOrigin(), w,h); qreal tempxt = transformOrigin.x(); diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 3c1a5a6ebe..d3f045f35c 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -1199,9 +1199,9 @@ void QQuickItemViewPrivate::showVisibleItems() const { qDebug() << "Visible items:"; for (int i = 0; i < visibleItems.count(); ++i) { - qDebug() << "\t" << visibleItems[i]->index - << visibleItems[i]->item->objectName() - << visibleItems[i]->position(); + qDebug() << "\t" << visibleItems.at(i)->index + << visibleItems.at(i)->item->objectName() + << visibleItems.at(i)->position(); } } @@ -1998,7 +1998,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult prevViewPos = prevFirstVisible->position(); prevFirstVisibleIndex = prevFirstVisible->index; } - qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0; + qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.constFirst()->position() : 0.0; totalInsertionResult->visiblePos = prevViewPos; totalRemovalResult->visiblePos = prevViewPos; @@ -2077,13 +2077,13 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult // can transition it from this "original" position to its new position in the view if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) { for (int i=0; i<movingIntoView.count(); i++) { - int fromIndex = findMoveKeyIndex(movingIntoView[i].moveKey, removals); + int fromIndex = findMoveKeyIndex(movingIntoView.at(i).moveKey, removals); if (fromIndex >= 0) { if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex) - repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos); + repositionItemAt(movingIntoView.at(i).item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos); else - repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos); - movingIntoView[i].item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true); + repositionItemAt(movingIntoView.at(i).item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos); + movingIntoView.at(i).item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true); } } } @@ -2129,11 +2129,11 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &remo Q_Q(QQuickItemView); bool visibleAffected = false; - if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) { - if (removal.index > visibleItems.last()->index) + if (visibleItems.count() && removal.index + removal.count > visibleItems.constLast()->index) { + if (removal.index > visibleItems.constLast()->index) removeResult->countChangeAfterVisibleItems += removal.count; else - removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index); + removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.constLast()->index); } QList<FxViewItem*>::Iterator it = visibleItems.begin(); @@ -2223,10 +2223,11 @@ void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirs qreal moveBackwardsBy = 0; // shift visibleItems.first() relative to the number of added/removed items - if (visibleItems.first()->position() > prevViewPos) { + const auto pos = visibleItems.constFirst()->position(); + if (pos > prevViewPos) { moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos; moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos; - } else if (visibleItems.first()->position() < prevViewPos) { + } else if (pos < prevViewPos) { moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos; moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos; } @@ -2304,7 +2305,7 @@ bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, co void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item) { for (int i=0; i<releasePendingTransition.count(); i++) { - if (releasePendingTransition[i]->transitionableItem == item) { + if (releasePendingTransition.at(i)->transitionableItem == item) { releaseItem(releasePendingTransition.takeAt(i)); return; } @@ -2324,8 +2325,8 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous) return 0; for (int i=0; i<releasePendingTransition.count(); i++) { - if (releasePendingTransition[i]->index == modelIndex - && !releasePendingTransition[i]->isPendingRemoval()) { + if (releasePendingTransition.at(i)->index == modelIndex + && !releasePendingTransition.at(i)->isPendingRemoval()) { releasePendingTransition[i]->releaseAfterTransition = false; return releasePendingTransition.takeAt(i); } diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 7bbc6dc9ba..50d75d3dab 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -400,7 +400,7 @@ FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const ++idx; } if (lastIndex == modelIndex-1) - return visibleItems.last(); + return visibleItems.constLast(); return 0; } @@ -752,7 +752,7 @@ bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer } } - while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) { + while (visibleItems.count() > 1 && (item = visibleItems.constLast()) && item->position() > bufferTo) { if (item->attached->delayRemove()) break; qCDebug(lcItemViewDelegateLifecycle) << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position() << (QObject *)(item->item); @@ -839,7 +839,7 @@ void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index) void QQuickListViewPrivate::resetFirstItemPosition(qreal pos) { - FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first()); + FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.constFirst()); item->setPosition(pos); } @@ -848,12 +848,12 @@ void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int if (!visibleItems.count()) return; qreal diff = forwards - backwards; - static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff); + static_cast<FxListItemSG*>(visibleItems.constFirst())->setPosition(visibleItems.constFirst()->position() + diff); } void QQuickListViewPrivate::updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult) { - if (item != visibleItems.first()) + if (item != visibleItems.constFirst()) QQuickItemViewPrivate::updateSizeChangesBeforeVisiblePos(item, removeResult); } @@ -1270,7 +1270,7 @@ void QQuickListViewPrivate::initializeCurrentItem() if (!actualItem) { if (currentIndex == visibleIndex - 1 && visibleItems.count()) { // We can calculate exact postion in this case - listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing); + listItem->setPosition(visibleItems.constFirst()->position() - currentItem->size() - spacing); } else { // Create current item now and position as best we can. // Its position will be corrected when it becomes visible. @@ -1424,8 +1424,8 @@ void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometry // if visibleItems.first() has resized, adjust its pos since it is used to // position all subsequent items - if (visibleItems.count() && item == visibleItems.first()->item) { - FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first()); + if (visibleItems.count() && item == visibleItems.constFirst()->item) { + FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.constFirst()); const QRectF oldGeometry(item->x() - diff.x(), item->y() - diff.y(), item->width() - diff.width(), @@ -3111,7 +3111,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch int i = visibleItems.count() - 1; while (i > 0 && visibleItems.at(i)->index == -1) --i; - if (i == 0 && visibleItems.first()->index == -1) { + if (i == 0 && visibleItems.constFirst()->index == -1) { // there are no visible items except items marked for removal index = visibleItems.count(); } else if (visibleItems.at(i)->index + 1 == modelIndex @@ -3136,7 +3136,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch qreal pos = 0; if (visibleItems.count()) { pos = index < visibleItems.count() ? visibleItems.at(index)->position() - : visibleItems.last()->endPosition()+spacing; + : visibleItems.constLast()->endPosition() + spacing; } // Update the indexes of the following visible items. @@ -3271,7 +3271,7 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex int markerItemIndex = -1; for (int i=0; i<visibleItems.count(); i++) { - if (visibleItems[i]->index == afterModelIndex) { + if (visibleItems.at(i)->index == afterModelIndex) { markerItemIndex = i; break; } @@ -3284,7 +3284,7 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing)); for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) { - FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]); + FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems.at(i)); if (!listItem->transitionScheduledOrRunning()) { qreal pos = listItem->position(); listItem->setPosition(pos - sizeRemoved); diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 33cc6c9a63..0118d882af 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -405,8 +405,7 @@ bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *i /*! \qmlsignal QtQuick::MouseArea::canceled() - This signal is emitted when mouse events have been canceled, either because an event was not accepted, or - because another item stole the mouse event handling. + This signal is emitted when mouse events have been canceled, because another item stole the mouse event handling. This signal is for advanced use: it is useful when there is more than one MouseArea that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter @@ -1198,6 +1197,11 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS emit mouseXChanged(&me); me.setPosition(d->lastPos); emit mouseYChanged(&me); + + if (!me.isAccepted()) { + d->pressed = Qt::NoButton; + } + if (!oldPressed) { emit pressedChanged(); emit containsPressChanged(); diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index ac5598767a..d31807de7f 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -448,20 +448,12 @@ void QQuickMultiPointTouchArea::touchEvent(QTouchEvent *event) } } updateTouchData(event); - if (event->type() == QEvent::TouchEnd) { - //TODO: move to window - _stealMouse = false; - setKeepMouseGrab(false); - setKeepTouchGrab(false); - ungrabTouchPoints(); - } + if (event->type() == QEvent::TouchEnd) + ungrab(); break; } case QEvent::TouchCancel: - _stealMouse = false; - setKeepMouseGrab(false); - setKeepTouchGrab(false); - ungrabTouchPoints(); + ungrab(); break; default: QQuickItem::touchEvent(event); @@ -557,13 +549,13 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) addTouchPoint(&p); started = true; } else if (touchPointState & Qt::TouchPointMoved) { - QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints[id]); + QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id)); Q_ASSERT(dtp); _movedTouchPoints.append(dtp); updateTouchPoint(dtp,&p); moved = true; } else { - QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints[id]); + QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id)); Q_ASSERT(dtp); updateTouchPoint(dtp,&p); } @@ -784,13 +776,12 @@ void QQuickMultiPointTouchArea::mouseReleaseEvent(QMouseEvent *event) void QQuickMultiPointTouchArea::ungrab() { + _stealMouse = false; + setKeepMouseGrab(false); + setKeepTouchGrab(false); + ungrabTouchPoints(); + if (_touchPoints.count()) { - QQuickWindow *c = window(); - if (c && c->mouseGrabberItem() == this) { - _stealMouse = false; - setKeepMouseGrab(false); - } - setKeepTouchGrab(false); foreach (QObject *obj, _touchPoints) static_cast<QQuickTouchPoint*>(obj)->setPressed(false); emit canceled(_touchPoints.values()); @@ -881,11 +872,7 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *i, QEvent *eve if (!shouldFilter(event)) return false; updateTouchData(event); - //TODO: verify this behavior - _stealMouse = false; - setKeepMouseGrab(false); - setKeepTouchGrab(false); - ungrabTouchPoints(); + ungrab(); } break; default: diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h index 52913f53f6..b9ef7816e0 100644 --- a/src/quick/items/qquickmultipointtoucharea_p.h +++ b/src/quick/items/qquickmultipointtoucharea_p.h @@ -231,7 +231,7 @@ public: static QQuickTouchPoint* touchPoint_at(QQmlListProperty<QQuickTouchPoint> *list, int index) { QQuickMultiPointTouchArea *q = static_cast<QQuickMultiPointTouchArea*>(list->object); - return q->_touchPrototypes[index]; + return q->_touchPrototypes.value(index); } Q_SIGNALS: diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index 9d24a6c511..b974641cca 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -252,11 +252,11 @@ void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item, const UniformData &d = uniformData[shaderType].at(i); QQmlPropertyData *pd = propCache->property(QString::fromUtf8(d.name), nullptr, nullptr); if (pd && !pd->isFunction()) { - if (pd->notifyIndex == -1) { + if (pd->notifyIndex() == -1) { qWarning("QQuickOpenGLShaderEffect: property '%s' does not have notification method!", d.name.constData()); } else { auto *mapper = signalMappers[shaderType].at(i); - mapper->setSignalIndex(pd->notifyIndex); + mapper->setSignalIndex(pd->notifyIndex()); Q_ASSERT(item->metaObject() == itemMetaObject); QObjectPrivate::connectImpl(item, mapper->signalIndex(), item, nullptr, mapper, Qt::AutoConnection, nullptr, itemMetaObject); @@ -347,7 +347,7 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, } else { if (QQmlPropertyData *pd = propCache->property(QString::fromUtf8(d.name), nullptr, nullptr)) { if (!pd->isFunction()) - d.propertyIndex = pd->coreIndex; + d.propertyIndex = pd->coreIndex(); } const int mappedId = uniformData[shaderType].size() | (shaderType << 16); mapper = new QtPrivate::MappedSlotObject([this, mappedId](){ diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index c7e476c4b5..1de20ed5b9 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -245,7 +245,7 @@ void QQuickPathViewPrivate::clear() currentItem = 0; } for (int i=0; i<items.count(); i++){ - QQuickItem *p = items[i]; + QQuickItem *p = items.at(i); releaseItem(p); } if (requestedIndex >= 0) { @@ -1993,8 +1993,8 @@ void QQuickPathView::refill() endPos = -1.0; startPos = 2.0; - for (int i = 0; i < d->items.count(); i++) { - int idx = d->model->indexOf(d->items[i], 0); + for (QQuickItem * item : qAsConst(d->items)) { + int idx = d->model->indexOf(item, 0); qreal curPos = d->positionOfIndex(idx); if (curPos > endPos) { endPos = curPos; @@ -2084,7 +2084,7 @@ void QQuickPathView::refill() if (!waiting && d->items.count() < count+d->cacheSize) { qCDebug(lcItemViewDelegateLifecycle) << "Checking for pathview middle inserts, items count was" << d->items.count(); idx = startIdx; - QQuickItem *lastItem = d->items[0]; + QQuickItem *lastItem = d->items.at(0); while (idx != endIdx) { //This gets the reference from the delegate model, and will not re-create QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0); @@ -2300,8 +2300,8 @@ void QQuickPathViewPrivate::createCurrentItem() return; bool inItems = false; - for (int i = 0; i < items.count(); i++) { - if (model->indexOf(items[i], 0) == currentIndex) { + for (QQuickItem *item : qAsConst(items)) { + if (model->indexOf(item, 0) == currentIndex) { inItems = true; break; } diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index 083e02fe3f..db04a83afc 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -94,7 +94,7 @@ QQuickStochasticEngine::QQuickStochasticEngine(QObject *parent) : setCount(1); } -QQuickStochasticEngine::QQuickStochasticEngine(QList<QQuickStochasticState*> states, QObject *parent) : +QQuickStochasticEngine::QQuickStochasticEngine(const QList<QQuickStochasticState *> &states, QObject *parent) : QObject(parent), m_states(states), m_timeOffset(0), m_addAdvance(false) { //Default size 1 @@ -122,7 +122,7 @@ QQuickSpriteEngine::~QQuickSpriteEngine() } -int QQuickSpriteEngine::maxFrames() +int QQuickSpriteEngine::maxFrames() const { return m_maxFrames; } @@ -140,7 +140,7 @@ TODO: Above idea needs to have the varying duration offset added to it m_startTimes will be set in advance/restart to 0->(m_framesPerRow-1) and can be used directly as extra. This makes it 'frame' instead, but is more memory efficient than two arrays and less hideous than a vector of unions. */ -int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDuration) +int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDuration) const { int myRowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow / m_sprites[state]->m_frames; if (rowDuration) @@ -153,7 +153,7 @@ int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDura return (m_timeOffset - m_startTimes[sprite]) / myRowDuration; } -int QQuickSpriteEngine::spriteState(int sprite) +int QQuickSpriteEngine::spriteState(int sprite) const { if (!m_loaded) return 0; @@ -174,7 +174,7 @@ int QQuickSpriteEngine::spriteState(int sprite) return state + extra; } -int QQuickSpriteEngine::spriteStart(int sprite) +int QQuickSpriteEngine::spriteStart(int sprite) const { if (!m_duration[sprite] || !m_loaded) return m_timeOffset; @@ -188,7 +188,7 @@ int QQuickSpriteEngine::spriteStart(int sprite) return m_startTimes[sprite] + extra*rowDuration; } -int QQuickSpriteEngine::spriteFrames(int sprite) +int QQuickSpriteEngine::spriteFrames(int sprite) const { if (!m_loaded) return 1; @@ -215,7 +215,7 @@ int QQuickSpriteEngine::spriteFrames(int sprite) return m_sprites[state]->m_framesPerRow; } -int QQuickSpriteEngine::spriteDuration(int sprite)//Full duration, not per frame +int QQuickSpriteEngine::spriteDuration(int sprite) const //Full duration, not per frame { if (!m_duration[sprite] || !m_loaded) return m_duration[sprite]; @@ -235,7 +235,7 @@ int QQuickSpriteEngine::spriteDuration(int sprite)//Full duration, not per frame return rowDuration; } -int QQuickSpriteEngine::spriteY(int sprite) +int QQuickSpriteEngine::spriteY(int sprite) const { if (!m_loaded) return 0; @@ -257,7 +257,7 @@ int QQuickSpriteEngine::spriteY(int sprite) return m_sprites[state]->m_rowY + m_sprites[state]->m_frameHeight * extra; } -int QQuickSpriteEngine::spriteX(int sprite) +int QQuickSpriteEngine::spriteX(int sprite) const { if (!m_loaded) return 0; @@ -280,24 +280,24 @@ int QQuickSpriteEngine::spriteX(int sprite) return m_sprites[state]->m_rowStartX; } -QQuickSprite* QQuickSpriteEngine::sprite(int sprite) +QQuickSprite* QQuickSpriteEngine::sprite(int sprite) const { return m_sprites[m_things[sprite]]; } -int QQuickSpriteEngine::spriteWidth(int sprite) +int QQuickSpriteEngine::spriteWidth(int sprite) const { int state = m_things[sprite]; return m_sprites[state]->m_frameWidth; } -int QQuickSpriteEngine::spriteHeight(int sprite) +int QQuickSpriteEngine::spriteHeight(int sprite) const { int state = m_things[sprite]; return m_sprites[state]->m_frameHeight; } -int QQuickSpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together +int QQuickSpriteEngine::spriteCount() const //TODO: Actually image state count, need to rename these things to make sense together { return m_imageStateCount; } @@ -312,14 +312,14 @@ void QQuickStochasticEngine::setGoal(int state, int sprite, bool jump) return; } - if (m_things[sprite] == state) + if (m_things.at(sprite) == state) return;//Already there m_things[sprite] = state; - m_duration[sprite] = m_states[state]->variedDuration(); + m_duration[sprite] = m_states.at(state)->variedDuration(); m_goals[sprite] = -1; restart(sprite); emit stateChanged(sprite); - emit m_states[state]->entered(); + emit m_states.at(state)->entered(); return; } @@ -516,8 +516,8 @@ void QQuickStochasticEngine::start(int index, int state) if (index >= m_things.count()) return; m_things[index] = state; - m_duration[index] = m_states[state]->variedDuration(); - if (m_states[state]->randomStart()) + m_duration[index] = m_states.at(state)->variedDuration(); + if (m_states.at(state)->randomStart()) m_startTimes[index] = NINF; else m_startTimes[index] = 0; @@ -538,33 +538,33 @@ void QQuickStochasticEngine::stop(int index) void QQuickStochasticEngine::restart(int index) { - bool randomStart = (m_startTimes[index] == NINF); + bool randomStart = (m_startTimes.at(index) == NINF); m_startTimes[index] = m_timeOffset; if (m_addAdvance) m_startTimes[index] += m_advanceTime.elapsed(); if (randomStart) - m_startTimes[index] -= qrand() % m_duration[index]; - int time = m_duration[index] + m_startTimes[index]; + m_startTimes[index] -= qrand() % m_duration.at(index); + int time = m_duration.at(index) + m_startTimes.at(index); for (int i=0; i<m_stateUpdates.count(); i++) m_stateUpdates[i].second.removeAll(index); - if (m_duration[index] >= 0) + if (m_duration.at(index) >= 0) addToUpdateList(time, index); } void QQuickSpriteEngine::restart(int index) //Reimplemented to recognize and handle pseudostates { - bool randomStart = (m_startTimes[index] == NINF); - if (m_loaded && m_sprites[m_things[index]]->frameSync()) {//Manually advanced + bool randomStart = (m_startTimes.at(index) == NINF); + if (m_loaded && m_sprites.at(m_things.at(index))->frameSync()) {//Manually advanced m_startTimes[index] = 0; - if (randomStart && m_sprites[m_things[index]]->m_generatedCount) - m_startTimes[index] += qrand() % m_sprites[m_things[index]]->m_generatedCount; + if (randomStart && m_sprites.at(m_things.at(index))->m_generatedCount) + m_startTimes[index] += qrand() % m_sprites.at(m_things.at(index))->m_generatedCount; } else { m_startTimes[index] = m_timeOffset; if (m_addAdvance) m_startTimes[index] += m_advanceTime.elapsed(); if (randomStart) - m_startTimes[index] -= qrand() % m_duration[index]; - int time = spriteDuration(index) + m_startTimes[index]; + m_startTimes[index] -= qrand() % m_duration.at(index); + int time = spriteDuration(index) + m_startTimes.at(index); if (randomStart) { int curTime = m_timeOffset + (m_addAdvance ? m_advanceTime.elapsed() : 0); while (time < curTime) //Fast forward through psuedostates as needed @@ -581,11 +581,11 @@ void QQuickStochasticEngine::advance(int idx) { if (idx >= m_things.count()) return;//TODO: Proper fix(because this has happened and I just ignored it) - int nextIdx = nextState(m_things[idx],idx); + int nextIdx = nextState(m_things.at(idx), idx); m_things[idx] = nextIdx; - m_duration[idx] = m_states[nextIdx]->variedDuration(); + m_duration[idx] = m_states.at(nextIdx)->variedDuration(); restart(idx); - emit m_states[nextIdx]->entered(); + emit m_states.at(nextIdx)->entered(); emit stateChanged(idx); } @@ -598,29 +598,29 @@ void QQuickSpriteEngine::advance(int idx) //Reimplemented to recognize and handl if (idx >= m_things.count()) return;//TODO: Proper fix(because this has happened and I just ignored it) - if (m_duration[idx] == 0) { - if (m_sprites[m_things[idx]]->frameSync()) { + if (m_duration.at(idx) == 0) { + if (m_sprites.at(m_things.at(idx))->frameSync()) { //Manually called, advance inner substate count m_startTimes[idx]++; - if (m_startTimes[idx] < m_sprites[m_things[idx]]->m_generatedCount) { + if (m_startTimes.at(idx) < m_sprites.at(m_things.at(idx))->m_generatedCount) { //only a pseudostate ended emit stateChanged(idx); return; } } //just go past the pseudostate logic - } else if (m_startTimes[idx] + m_duration[idx] + } else if (m_startTimes.at(idx) + m_duration.at(idx) > int(m_timeOffset + (m_addAdvance ? m_advanceTime.elapsed() : 0))) { //only a pseduostate ended emit stateChanged(idx); addToUpdateList(spriteStart(idx) + spriteDuration(idx) + (m_addAdvance ? m_advanceTime.elapsed() : 0), idx); return; } - int nextIdx = nextState(m_things[idx],idx); + int nextIdx = nextState(m_things.at(idx), idx); m_things[idx] = nextIdx; - m_duration[idx] = m_states[nextIdx]->variedDuration(); + m_duration[idx] = m_states.at(nextIdx)->variedDuration(); restart(idx); - emit m_states[nextIdx]->entered(); + emit m_states.at(nextIdx)->entered(); emit stateChanged(idx); } @@ -631,16 +631,16 @@ int QQuickStochasticEngine::nextState(int curState, int curThing) if (goalPath == -1){//Random qreal r =(qreal) qrand() / (qreal) RAND_MAX; qreal total = 0.0; - for (QVariantMap::const_iterator iter=m_states[curState]->m_to.constBegin(); - iter!=m_states[curState]->m_to.constEnd(); ++iter) + for (QVariantMap::const_iterator iter=m_states.at(curState)->m_to.constBegin(); + iter!=m_states.at(curState)->m_to.constEnd(); ++iter) total += (*iter).toReal(); r*=total; - for (QVariantMap::const_iterator iter= m_states[curState]->m_to.constBegin(); - iter!=m_states[curState]->m_to.constEnd(); ++iter){ + for (QVariantMap::const_iterator iter= m_states.at(curState)->m_to.constBegin(); + iter!=m_states.at(curState)->m_to.constEnd(); ++iter){ if (r < (*iter).toReal()){ bool superBreak = false; for (int i=0; i<m_states.count(); i++){ - if (m_states[i]->name() == iter.key()){ + if (m_states.at(i)->name() == iter.key()){ nextIdx = i; superBreak = true; break; @@ -664,8 +664,8 @@ uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a lis //Sprite State Update; m_timeOffset = time; m_addAdvance = false; - while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){ - foreach (int idx, m_stateUpdates.first().second) + while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.constFirst().first){ + foreach (int idx, m_stateUpdates.constFirst().second) advance(idx); m_stateUpdates.pop_front(); } @@ -674,14 +674,14 @@ uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a lis m_addAdvance = true; if (m_stateUpdates.isEmpty()) return uint(-1); - return m_stateUpdates.first().first; + return m_stateUpdates.constFirst().first; } int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) { QString goalName; - if (m_goals[spriteIdx] != -1) - goalName = m_states[m_goals[spriteIdx]]->name(); + if (m_goals.at(spriteIdx) != -1) + goalName = m_states.at(m_goals.at(spriteIdx))->name(); else goalName = m_globalGoal; if (goalName.isEmpty()) @@ -689,16 +689,16 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) //TODO: caching instead of excessively redoing iterative deepening (which was chosen arbitrarily anyways) // Paraphrased - implement in an *efficient* manner for (int i=0; i<m_states.count(); i++) - if (m_states[curIdx]->name() == goalName) + if (m_states.at(curIdx)->name() == goalName) return curIdx; if (dist < 0) dist = m_states.count(); - QQuickStochasticState* curState = m_states[curIdx]; + QQuickStochasticState* curState = m_states.at(curIdx); for (QVariantMap::const_iterator iter = curState->m_to.constBegin(); iter!=curState->m_to.constEnd(); ++iter){ if (iter.key() == goalName) for (int i=0; i<m_states.count(); i++) - if (m_states[i]->name() == goalName) + if (m_states.at(i)->name() == goalName) return i; } QSet<int> options; @@ -707,7 +707,7 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) iter!=curState->m_to.constEnd(); ++iter){ int option = -1; for (int j=0; j<m_states.count(); j++)//One place that could be a lot more efficient... - if (m_states[j]->name() == iter.key()) + if (m_states.at(j)->name() == iter.key()) if (goalSeek(j, spriteIdx, i) != -1) option = j; if (option != -1) @@ -721,13 +721,13 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) qreal total = 0; for (QSet<int>::const_iterator iter=options.constBegin(); iter!=options.constEnd(); ++iter) - total += curState->m_to.value(m_states[(*iter)]->name()).toReal(); + total += curState->m_to.value(m_states.at((*iter))->name()).toReal(); r *= total; for (QVariantMap::const_iterator iter = curState->m_to.constBegin(); iter!=curState->m_to.constEnd(); ++iter){ bool superContinue = true; for (int j=0; j<m_states.count(); j++) - if (m_states[j]->name() == iter.key()) + if (m_states.at(j)->name() == iter.key()) if (options.contains(j)) superContinue = false; if (superContinue) @@ -735,7 +735,7 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) if (r < (*iter).toReal()){ bool superBreak = false; for (int j=0; j<m_states.count(); j++){ - if (m_states[j]->name() == iter.key()){ + if (m_states.at(j)->name() == iter.key()){ option = j; superBreak = true; break; @@ -755,10 +755,10 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) void QQuickStochasticEngine::addToUpdateList(uint t, int idx) { for (int i=0; i<m_stateUpdates.count(); i++){ - if (m_stateUpdates[i].first==t){ + if (m_stateUpdates.at(i).first == t){ m_stateUpdates[i].second << idx; return; - }else if (m_stateUpdates[i].first > t){ + } else if (m_stateUpdates.at(i).first > t) { QList<int> tmpList; tmpList << idx; m_stateUpdates.insert(i, qMakePair(t, tmpList)); diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h index 1bdcc9a741..424fa18a54 100644 --- a/src/quick/items/qquickspriteengine_p.h +++ b/src/quick/items/qquickspriteengine_p.h @@ -189,7 +189,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickStochasticEngine : public QObject Q_PROPERTY(QQmlListProperty<QQuickStochasticState> states READ states) public: explicit QQuickStochasticEngine(QObject *parent = 0); - QQuickStochasticEngine(QList<QQuickStochasticState*> states, QObject *parent=0); + QQuickStochasticEngine(const QList<QQuickStochasticState*> &states, QObject *parent = 0); ~QQuickStochasticEngine(); QQmlListProperty<QQuickStochasticState> states() @@ -210,11 +210,11 @@ public: virtual void restart(int index=0); virtual void advance(int index=0);//Sends state to the next chosen state, unlike goal. void stop(int index=0); - int curState(int index=0) {return m_things[index];} + int curState(int index=0) const {return m_things[index];} - QQuickStochasticState* state(int idx){return m_states[idx];} - int stateIndex(QQuickStochasticState* s){return m_states.indexOf(s);} - int stateIndex(const QString& s) { + QQuickStochasticState* state(int idx) const {return m_states[idx];} + int stateIndex(QQuickStochasticState* s) const {return m_states.indexOf(s);} + int stateIndex(const QString& s) const { for (int i=0; i<m_states.count(); i++) if (m_states[i]->name() == s) return i; @@ -273,17 +273,17 @@ public: return QQmlListProperty<QQuickSprite>(this, m_sprites); } - QQuickSprite* sprite(int sprite=0); - int spriteState(int sprite=0); - int spriteStart(int sprite=0); - int spriteFrames(int sprite=0); - int spriteDuration(int sprite=0); - int spriteX(int sprite=0); - int spriteY(int sprite=0); - int spriteWidth(int sprite=0); - int spriteHeight(int sprite=0); - int spriteCount();//Like state count - int maxFrames(); + QQuickSprite* sprite(int sprite = 0) const; + int spriteState(int sprite = 0) const; + int spriteStart(int sprite = 0) const; + int spriteFrames(int sprite = 0) const; + int spriteDuration(int sprite = 0) const; + int spriteX(int sprite = 0) const; + int spriteY(int sprite = 0) const; + int spriteWidth(int sprite = 0) const; + int spriteHeight(int sprite = 0) const; + int spriteCount() const;//Like state count + int maxFrames() const; void restart(int index=0) Q_DECL_OVERRIDE; void advance(int index=0) Q_DECL_OVERRIDE; @@ -298,7 +298,7 @@ public: QImage assembledImage(int maxSize = 2048); private: - int pseudospriteProgress(int,int,int*rd=0); + int pseudospriteProgress(int, int, int *rd = 0) const; QList<QQuickSprite*> m_sprites; bool m_startedImageAssembly; bool m_loaded; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index a2c82c30a0..f958d1a087 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -471,7 +471,6 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size) QQuickWindowPrivate::QQuickWindowPrivate() : contentItem(0) , activeFocusItem(0) - , mouseGrabberItem(0) #ifndef QT_NO_CURSOR , cursorItem(0) #endif @@ -479,6 +478,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , dragGrabber(0) #endif , touchMouseId(-1) + , touchMouseDevice(nullptr) , touchMousePressTimestamp(0) , dirtyItemList(0) , devicePixelRatio(0) @@ -486,7 +486,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , renderer(0) , windowManager(0) , renderControl(0) - , touchRecursionGuard(0) + , pointerEventRecursionGuard(0) , customRenderStage(0) , clearColor(Qt::white) , clearBeforeRendering(true) @@ -622,8 +622,15 @@ bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp) return doubleClicked; } -bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event) +bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent) { + Q_Q(QQuickWindow); + auto device = pointerEvent->device(); + + // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here. + Q_ASSERT(pointerEvent->asPointerTouchEvent()); + QTouchEvent *event = pointerEvent->asPointerTouchEvent()->touchEventForItem(item); + // For each point, check if it is accepted, if not, try the next point. // Any of the fingers can become the mouse one. // This can happen because a mouse area might not accept an event at some point but another. @@ -637,65 +644,45 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e if (!item->contains(pos)) break; - // Store the id already here and restore it to -1 if the event does not get - // accepted. Cannot defer setting the new value because otherwise if the event - // handler spins the event loop all subsequent moves and releases get lost. - touchMouseId = p.id(); - itemForTouchPointId[touchMouseId] = item; qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item; QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); // Send a single press and see if that's accepted - if (!mouseGrabberItem) - item->grabMouse(); - item->grabTouchPoints(QVector<int>() << touchMouseId); - QCoreApplication::sendEvent(item, mousePress.data()); event->setAccepted(mousePress->isAccepted()); - if (!mousePress->isAccepted()) { - touchMouseId = -1; - if (itemForTouchPointId.value(p.id()) == item) { - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "disassociated"; - itemForTouchPointId.remove(p.id()); + if (mousePress->isAccepted()) { + touchMouseDevice = device; + touchMouseId = p.id(); + if (!q->mouseGrabberItem()) + item->grabMouse(); + auto pointerEventPoint = pointerEvent->pointById(p.id()); + pointerEventPoint->setGrabber(item); + + if (checkIfDoubleClicked(event->timestamp())) { + QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); + QCoreApplication::sendEvent(item, mouseDoubleClick.data()); + event->setAccepted(mouseDoubleClick->isAccepted()); + if (!mouseDoubleClick->isAccepted()) { + touchMouseId = -1; + touchMouseDevice = nullptr; + } } - if (mouseGrabberItem == item) - item->ungrabMouse(); - } - - if (mousePress->isAccepted() && checkIfDoubleClicked(event->timestamp())) { - QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); - QCoreApplication::sendEvent(item, mouseDoubleClick.data()); - event->setAccepted(mouseDoubleClick->isAccepted()); - if (mouseDoubleClick->isAccepted()) { - touchMouseIdCandidates.clear(); - return true; - } else { - touchMouseId = -1; - } - } - // The event was accepted, we are done. - if (mousePress->isAccepted()) { - touchMouseIdCandidates.clear(); return true; } - // The event was not accepted but touchMouseId was set. - if (touchMouseId != -1) - return false; // try the next point // Touch point was there before and moved - } else if (p.id() == touchMouseId) { + } else if (touchMouseDevice == device && p.id() == touchMouseId) { if (p.state() & Qt::TouchPointMoved) { - if (mouseGrabberItem) { + if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); event->setAccepted(me->isAccepted()); if (me->isAccepted()) { qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem; - itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent() - return true; } + return event->isAccepted(); } else { // no grabber, check if we care about mouse hover // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now. @@ -718,7 +705,8 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e } else if (p.state() & Qt::TouchPointReleased) { // currently handled point was released touchMouseId = -1; - if (mouseGrabberItem) { + touchMouseDevice = nullptr; + if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); @@ -730,8 +718,8 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e Qt::NoButton, Qt::NoButton, event->modifiers()); QCoreApplication::sendEvent(item, &mm); } - if (mouseGrabberItem) // might have ungrabbed due to event - mouseGrabberItem->ungrabMouse(); + if (q->mouseGrabberItem()) // might have ungrabbed due to event + q->mouseGrabberItem()->ungrabMouse(); return me->isAccepted(); } } @@ -744,25 +732,29 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) { Q_Q(QQuickWindow); - if (mouseGrabberItem == grabber) + if (q->mouseGrabberItem() == grabber) return; - qCDebug(DBG_MOUSE_TARGET) << "grabber" << mouseGrabberItem << "->" << grabber; - QQuickItem *oldGrabber = mouseGrabberItem; - mouseGrabberItem = grabber; + qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber; + QQuickItem *oldGrabber = q->mouseGrabberItem(); + + QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); + Q_ASSERT(event->pointCount() == 1); + event->point(0)->setGrabber(grabber); - if (touchMouseId != -1) { + if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber - itemForTouchPointId.remove(touchMouseId); - if (grabber) { - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << mouseGrabberItem; - itemForTouchPointId[touchMouseId] = grabber; - } + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); + auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); + if (point) + point->setGrabber(grabber); } if (oldGrabber) { - QEvent ev(QEvent::UngrabMouse); - q->sendEvent(oldGrabber, &ev); + QEvent e(QEvent::UngrabMouse); + QSet<QQuickItem *> hasFiltered; + if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) + oldGrabber->mouseUngrabEvent(); } } @@ -771,20 +763,24 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int Q_Q(QQuickWindow); QSet<QQuickItem*> ungrab; for (int i = 0; i < ids.count(); ++i) { - QQuickItem *oldGrabber = itemForTouchPointId.value(ids.at(i)); - if (oldGrabber == grabber) - continue; + // FIXME: deprecate this function, we need a device + for (auto device: QQuickPointerDevice::touchDevices()) { + auto point = device->pointerEvent()->pointById(ids.at(i)); + if (!point) + continue; + QQuickItem *oldGrabber = point->grabber(); + if (oldGrabber == grabber) + continue; - itemForTouchPointId[ids.at(i)] = grabber; - if (oldGrabber) - ungrab.insert(oldGrabber); + point->setGrabber(grabber); + if (oldGrabber) + ungrab.insert(oldGrabber); + } - QQuickItem *originalMouseGrabberItem = mouseGrabberItem; + QQuickItem *mouseGrabberItem = q->mouseGrabberItem(); if (touchMouseId == ids.at(i) && mouseGrabberItem && mouseGrabberItem != grabber) { qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: grabber" << mouseGrabberItem << "-> null"; - mouseGrabberItem = 0; - QEvent ev(QEvent::UngrabMouse); - q->sendEvent(originalMouseGrabberItem, &ev); + setMouseGrabber(nullptr); } } foreach (QQuickItem *oldGrabber, ungrab) @@ -795,35 +791,23 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to { Q_Q(QQuickWindow); if (Q_LIKELY(touch)) { - QMutableHashIterator<int, QQuickItem *> itemTouchMapIt(itemForTouchPointId); - while (itemTouchMapIt.hasNext()) { - if (itemTouchMapIt.next().value() == grabber) { - itemTouchMapIt.remove(); - grabber->touchUngrabEvent(); + for (auto device: QQuickPointerDevice::touchDevices()) { + auto pointerEvent = device->pointerEvent(); + for (int i = 0; i < pointerEvent->pointCount(); ++i) { + if (pointerEvent->point(i)->grabber() == grabber) { + pointerEvent->point(i)->setGrabber(nullptr); + // FIXME send ungrab event only once + grabber->touchUngrabEvent(); + } } } } - if (Q_LIKELY(mouse) && mouseGrabberItem == grabber) { - qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << mouseGrabberItem << "-> null"; - mouseGrabberItem = 0; - QEvent ev(QEvent::UngrabMouse); - q->sendEvent(grabber, &ev); - } -} - -void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform) -{ - QMatrix4x4 transformMatrix(transform); - for (int i=0; i<touchPoints.count(); i++) { - QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; - touchPoint.setRect(transform.mapRect(touchPoint.sceneRect())); - touchPoint.setStartPos(transform.map(touchPoint.startScenePos())); - touchPoint.setLastPos(transform.map(touchPoint.lastScenePos())); - touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D()); + if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) { + qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << q->mouseGrabberItem() << "-> null"; + setMouseGrabber(nullptr); } } - /*! Translates the data in \a touchEvent to this window. This method leaves the item local positions in \a touchEvent untouched (these are filled in later). @@ -837,9 +821,6 @@ void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent) touchPoint.setSceneRect(touchPoint.rect()); touchPoint.setStartScenePos(touchPoint.startPos()); touchPoint.setLastScenePos(touchPoint.lastPos()); - - if (i == 0) - lastMousePosition = touchPoint.pos().toPoint(); } touchEvent->setTouchPoints(touchPoints); } @@ -950,17 +931,16 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q } // Now that all the state is changed, emit signals & events - // We must do this last, as this process may result in further changes to - // focus. + // We must do this last, as this process may result in further changes to focus. if (oldActiveFocusItem) { QFocusEvent event(QEvent::FocusOut, reason); - q->sendEvent(oldActiveFocusItem, &event); + QCoreApplication::sendEvent(oldActiveFocusItem, &event); } // Make sure that the FocusOut didn't result in another focus change. if (sendFocusIn && activeFocusItem == newActiveFocusItem) { QFocusEvent event(QEvent::FocusIn, reason); - q->sendEvent(newActiveFocusItem, &event); + QCoreApplication::sendEvent(newActiveFocusItem, &event); } if (activeFocusItem != currentActiveFocusItem) @@ -1047,13 +1027,13 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, // focus. if (oldActiveFocusItem) { QFocusEvent event(QEvent::FocusOut, reason); - q->sendEvent(oldActiveFocusItem, &event); + QCoreApplication::sendEvent(oldActiveFocusItem, &event); } // Make sure that the FocusOut didn't result in another focus change. if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) { QFocusEvent event(QEvent::FocusIn, reason); - q->sendEvent(newActiveFocusItem, &event); + QCoreApplication::sendEvent(newActiveFocusItem, &event); } if (activeFocusItem != currentActiveFocusItem) @@ -1482,9 +1462,9 @@ QObject *QQuickWindow::focusObject() const */ QQuickItem *QQuickWindow::mouseGrabberItem() const { - Q_D(const QQuickWindow); - - return d->mouseGrabberItem; + QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); + Q_ASSERT(event->pointCount()); + return event->point(0)->grabber(); } @@ -1529,7 +1509,7 @@ bool QQuickWindow::event(QEvent *e) break; case QEvent::Leave: d->clearHover(); - d->lastMousePosition = QPoint(); + d->lastMousePosition = QPointF(); break; #ifndef QT_NO_DRAGANDDROP case QEvent::DragEnter: @@ -1555,8 +1535,8 @@ bool QQuickWindow::event(QEvent *e) if (d->activeFocusItem) qGuiApp->inputMethod()->commit(); #endif - if (d->mouseGrabberItem) - d->mouseGrabberItem->ungrabMouse(); + if (mouseGrabberItem()) + mouseGrabberItem()->ungrabMouse(); break; case QEvent::UpdateRequest: { if (d->windowManager) @@ -1570,7 +1550,7 @@ bool QQuickWindow::event(QEvent *e) #endif case QEvent::ShortcutOverride: if (d->activeFocusItem) - sendEvent(d->activeFocusItem, static_cast<QKeyEvent *>(e)); + QCoreApplication::sendEvent(d->activeFocusItem, e); return true; default: break; @@ -1622,78 +1602,44 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t return me; } -bool QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event) +void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { Q_Q(QQuickWindow); - - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - - if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - QPointF p = item->mapFromScene(event->windowPos()); - if (!item->contains(p)) - return false; - } - - QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QQuickItem *child = children.at(ii); - if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled) - continue; - if (deliverInitialMousePressEvent(child, event)) - return true; - } - - if (itemPrivate->acceptedMouseButtons() & event->button()) { - QPointF localPos = item->mapFromScene(event->windowPos()); - if (item->contains(localPos)) { - QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos)); - me->accept(); - item->grabMouse(); - q->sendEvent(item, me.data()); - event->setAccepted(me->isAccepted()); - if (me->isAccepted()) - return true; - if (mouseGrabberItem) - mouseGrabberItem->ungrabMouse(); + auto point = pointerEvent->point(0); + lastMousePosition = point->scenePos(); + QQuickItem *grabber = point->grabber(); + if (grabber) { + // if the update consists of changing button state, then don't accept it + // unless the button is one in which the item is interested + if (pointerEvent->button() != Qt::NoButton + && grabber->acceptedMouseButtons() + && !(grabber->acceptedMouseButtons() & pointerEvent->button())) { + pointerEvent->setAccepted(false); + return; } - } - return false; -} - -bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) -{ - Q_Q(QQuickWindow); - - lastMousePosition = event->windowPos(); - - if (!mouseGrabberItem && - event->type() == QEvent::MouseButtonPress && - (event->buttons() & event->button()) == event->buttons()) { - if (deliverInitialMousePressEvent(contentItem, event)) - event->accept(); - else - event->ignore(); - return event->isAccepted(); - } + // send update + QPointF localPos = grabber->mapFromScene(lastMousePosition); + auto me = pointerEvent->asMouseEvent(localPos); + me->accept(); + q->sendEvent(grabber, me); + point->setAccepted(me->isAccepted()); - if (mouseGrabberItem) { - if (event->button() != Qt::NoButton - && mouseGrabberItem->acceptedMouseButtons() - && !(mouseGrabberItem->acceptedMouseButtons() & event->button())) { - event->ignore(); - return false; + // release event, make sure to ungrab if there still is a grabber + if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem()) + q->mouseGrabberItem()->ungrabMouse(); + } else { + // send initial press + bool delivered = false; + if (pointerEvent->isPressEvent()) { + QSet<QQuickItem*> hasFiltered; + delivered = deliverPressEvent(pointerEvent, &hasFiltered); } - QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos()); - QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos)); - me->accept(); - q->sendEvent(mouseGrabberItem, me.data()); - event->setAccepted(me->isAccepted()); - if (me->isAccepted()) - return true; - } - return false; + if (!delivered) + // make sure not to accept unhandled events + pointerEvent->setAccepted(false); + } } bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, @@ -1701,7 +1647,6 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted) { - Q_Q(QQuickWindow); const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform(); //create copy of event @@ -1714,7 +1659,7 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, return true; } - q->sendEvent(item, &hoverEvent); + QCoreApplication::sendEvent(item, &hoverEvent); return hoverEvent.isAccepted(); } @@ -1746,7 +1691,7 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce if (itemPrivate->hoverEnabled) { QPointF p = item->mapFromScene(scenePos); if (item->contains(p)) { - if (!hoverItems.isEmpty() && hoverItems[0] == item) { + if (!hoverItems.isEmpty() && hoverItems.at(0) == item) { //move accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted); } else { @@ -1757,24 +1702,24 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce itemsToHover << parent; // Leaving from previous hovered items until we reach the item or one of its ancestors. - while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) { + while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems.at(0))) { QQuickItem *hoverLeaveItem = hoverItems.takeFirst(); sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted); } - if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item + if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {//Not entering a new Item // ### Shouldn't we send moves for the parent items as well? accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted); } else { // Enter items that are not entered yet. int startIdx = -1; if (!hoverItems.isEmpty()) - startIdx = itemsToHover.indexOf(hoverItems[0]) - 1; + startIdx = itemsToHover.indexOf(hoverItems.at(0)) - 1; if (startIdx == -1) startIdx = itemsToHover.count() - 1; for (int i = startIdx; i >= 0; i--) { - QQuickItem *itemToHover = itemsToHover[i]; + QQuickItem *itemToHover = itemsToHover.at(i); QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(itemToHover); // The item may be about to be deleted or reparented to another window // due to another hover event delivered in this function. If that is the @@ -1798,7 +1743,6 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce #ifndef QT_NO_WHEELEVENT bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event) { - Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { @@ -1822,7 +1766,7 @@ bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(), event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted()); wheel.accept(); - q->sendEvent(item, &wheel); + QCoreApplication::sendEvent(item, &wheel); if (wheel.isAccepted()) { event->accept(); return true; @@ -1889,20 +1833,22 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) { qCDebug(DBG_TOUCH) << event; Q_Q(QQuickWindow); + // A TouchCancel event will typically not contain any points. // Deliver it to all items that have active touches. - QSet<QQuickItem *> cancelDelivered; - foreach (QQuickItem *item, itemForTouchPointId) { - if (cancelDelivered.contains(item)) - continue; - cancelDelivered.insert(item); - q->sendEvent(item, event); + QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent(); + QVector<QQuickItem *> grabbers = pointerEvent->grabbers(); + + for (QQuickItem *grabber: qAsConst(grabbers)) { + q->sendEvent(grabber, event); } touchMouseId = -1; - if (mouseGrabberItem) - mouseGrabberItem->ungrabMouse(); + touchMouseDevice = nullptr; + if (q->mouseGrabberItem()) + q->mouseGrabberItem()->ungrabMouse(); + // The next touch event can only be a TouchBegin so clean up. - itemForTouchPointId.clear(); + pointerEvent->clearGrabbers(); return true; } @@ -1912,7 +1858,7 @@ void QQuickWindowPrivate::deliverDelayedTouchEvent() // Set delayedTouch to 0 before delivery to avoid redelivery in case of // event loop recursions (e.g if it the touch starts a dnd session). QScopedPointer<QTouchEvent> e(delayedTouch.take()); - deliverTouchEvent(e.data()); + deliverPointerEvent(pointerEventInstance(e.data())); } static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION"); @@ -1988,17 +1934,20 @@ bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event) void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) { translateTouchEvent(event); + if (event->touchPoints().size()) + lastMousePosition = event->touchPoints().at(0).pos(); + qCDebug(DBG_TOUCH) << event; - if (qquickwindow_no_touch_compression || touchRecursionGuard) { - deliverTouchEvent(event); + if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) { + deliverPointerEvent(pointerEventInstance(event)); return; } if (!compressTouchEvent(event)) { if (delayedTouch) deliverDelayedTouchEvent(); - deliverTouchEvent(event); + deliverPointerEvent(pointerEventInstance(event)); } } @@ -2029,6 +1978,8 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) { + Q_Q(QQuickWindow); + if (event->source() == Qt::MouseEventSynthesizedBySystem) { event->accept(); return; @@ -2039,33 +1990,17 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonPress: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(), event->buttons()); - deliverMouseEvent(event); + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseButtonRelease: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(), event->buttons()); - if (!mouseGrabberItem) { - event->ignore(); - return; - } - - deliverMouseEvent(event); - if (mouseGrabberItem && !event->buttons()) - mouseGrabberItem->ungrabMouse(); + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseButtonDblClick: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, event->button(), event->buttons()); - - if (!mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) { - if (deliverInitialMousePressEvent(contentItem, event)) - event->accept(); - else - event->ignore(); - return; - } - - deliverMouseEvent(event); + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseMove: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, @@ -2077,10 +2012,8 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) updateCursor(event->windowPos()); #endif - if (!mouseGrabberItem) { - if (lastMousePosition.isNull()) - lastMousePosition = event->windowPos(); - QPointF last = lastMousePosition; + if (!q->mouseGrabberItem()) { + QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition; lastMousePosition = event->windowPos(); bool accepted = event->isAccepted(); @@ -2092,7 +2025,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) event->setAccepted(accepted); return; } - deliverMouseEvent(event); + deliverPointerEvent(pointerEventInstance(event)); break; default: Q_ASSERT(false); @@ -2102,6 +2035,8 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) void QQuickWindowPrivate::flushFrameSynchronousEvents() { + Q_Q(QQuickWindow); + if (delayedTouch) { deliverDelayedTouchEvent(); @@ -2116,7 +2051,7 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() // For instance, during animation (including the case of a ListView // whose delegates contain MouseAreas), a MouseArea needs to know // whether it has moved into a position where it is now under the cursor. - if (!mouseGrabberItem && !lastMousePosition.isNull()) { + if (!q->mouseGrabberItem() && !lastMousePosition.isNull()) { bool accepted = false; bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0, accepted); if (!delivered) @@ -2124,167 +2059,211 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() } } -void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) -{ - qCDebug(DBG_TOUCH) << " - delivering" << event; +/*! + \internal + Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event. - // If users spin the eventloop as a result of touch delivery, we disable - // touch compression and send events directly. This is because we consider - // the usecase a bit evil, but we at least don't want to lose events. - ++touchRecursionGuard; - - // List of all items that received an event before - // When we have TouchBegin this is and will stay empty - QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints; - - // Figure out who accepted a touch point last and put it in updatedPoints - // Add additional item to newPoints - const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints(); - QList<QTouchEvent::TouchPoint> newPoints; - for (int i=0; i<touchPoints.count(); i++) { - const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i); - if (touchPoint.state() == Qt::TouchPointPressed) { - newPoints << touchPoint; - } else { - // TouchPointStationary is relevant only to items which - // are also receiving touch points with some other state. - // But we have not yet decided which points go to which item, - // so for now we must include all non-new points in updatedPoints. - if (itemForTouchPointId.contains(touchPoint.id())) { - QQuickItem *item = itemForTouchPointId.value(touchPoint.id()); - if (item) - updatedPoints[item].append(touchPoint); - } - } + There is a unique instance per QQuickPointerDevice, which is determined + from \a event's device. +*/ +QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) +{ + QQuickPointerDevice *dev = nullptr; + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + dev = QQuickPointerDevice::genericMouseDevice(); + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + case QEvent::TouchCancel: + dev = QQuickPointerDevice::touchDevice(static_cast<QTouchEvent *>(event)->device()); + break; + // TODO tablet event types + default: + break; } + Q_ASSERT(dev); + return dev->pointerEvent()->reset(event); +} - // Deliver the event, but only if there is at least one new point - // or some item accepted a point and should receive an update - if (newPoints.count() > 0 || updatedPoints.count() > 0) { - QSet<int> acceptedNewPoints; - QSet<QQuickItem *> hasFiltered; - event->setAccepted(deliverTouchPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints, &hasFiltered)); - } else - event->ignore(); +void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) +{ + // If users spin the eventloop as a result of event delivery, we disable + // event compression and send events directly. This is because we consider + // the usecase a bit evil, but we at least don't want to lose events. + ++pointerEventRecursionGuard; - // Remove released points from itemForTouchPointId - if (event->touchPointStates() & Qt::TouchPointReleased) { - for (int i=0; i<touchPoints.count(); i++) { - if (touchPoints[i].state() == Qt::TouchPointReleased) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << touchPoints[i].id() << "released"; - itemForTouchPointId.remove(touchPoints[i].id()); - if (touchPoints[i].id() == touchMouseId) - touchMouseId = -1; - touchMouseIdCandidates.remove(touchPoints[i].id()); - } - } + if (event->asPointerMouseEvent()) { + deliverMouseEvent(event->asPointerMouseEvent()); + } else if (event->asPointerTouchEvent()) { + deliverTouchEvent(event->asPointerTouchEvent()); + } else { + Q_ASSERT(false); } - if (event->type() == QEvent::TouchEnd) { - if (!itemForTouchPointId.isEmpty()) { - qWarning() << "No release received for" << itemForTouchPointId.size() - << "touch points over" << itemForTouchPointId.begin().value() - << "on touch end."; - itemForTouchPointId.clear(); - } - } + event->reset(nullptr); - --touchRecursionGuard; + --pointerEventRecursionGuard; } -// This function recurses and sends the events to the individual items -bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, - QSet<int> *acceptedNewPoints, QHash<QQuickItem *, - QList<QTouchEvent::TouchPoint> > *updatedPoints, QSet<QQuickItem *> *hasFiltered) +// check if item or any of its child items contain the point +// FIXME: should this be iterative instead of recursive? +QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons) const { - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - + QVector<QQuickItem *> targets; + auto itemPrivate = QQuickItemPrivate::get(item); + QPointF itemPos = item->mapFromScene(scenePos); + // if the item clips, we can potentially return early if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - for (int i=0; i<newPoints.count(); i++) { - QPointF p = item->mapFromScene(newPoints[i].scenePos()); - if (!item->contains(p)) - return false; - } + if (!item->contains(itemPos)) + return targets; } - // Check if our children want the event (or parts of it) - // This is the only point where touch event delivery recurses! + // recurse for children QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); for (int ii = children.count() - 1; ii >= 0; --ii) { QQuickItem *child = children.at(ii); - if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled) + auto childPrivate = QQuickItemPrivate::get(child); + if (!child->isVisible() || !child->isEnabled() || childPrivate->culled) continue; - if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints, hasFiltered)) - return true; + targets << pointerTargets(child, scenePos, false); } - // None of the children accepted the event, so check the given item itself. - // First, construct matchingPoints as a list of TouchPoints which the - // given item might be interested in. Any newly-pressed point which is - // inside the item's bounds will be interesting, and also any updated point - // which was already accepted by that item when it was first pressed. - // (A point which was already accepted is effectively "grabbed" by the item.) - - // set of IDs of "interesting" new points - QSet<int> matchingNewPoints; - // set of points which this item has previously accepted, for starters - QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item]; - // now add the new points which are inside this item's bounds - if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) { - for (int i = 0; i < newPoints.count(); i++) { - if (acceptedNewPoints->contains(newPoints[i].id())) - continue; - QPointF p = item->mapFromScene(newPoints[i].scenePos()); - if (item->contains(p)) { - matchingNewPoints.insert(newPoints[i].id()); - matchingPoints << newPoints[i]; - } + if (item->contains(itemPos) && (!checkMouseButtons || itemPrivate->acceptedMouseButtons())) { + // add this item last - children take precedence + targets << item; + } + return targets; +} + +// return the joined lists +// list1 has priority, common items come last +QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const +{ + QVector<QQuickItem *> targets = list1; + // start at the end of list2 + // if item not in list, append it + // if item found, move to next one, inserting before the last found one + int insertPosition = targets.length(); + for (int i = list2.length() - 1; i >= 0; --i) { + int newInsertPosition = targets.lastIndexOf(list2.at(i), insertPosition); + if (newInsertPosition >= 0) { + Q_ASSERT(newInsertPosition <= insertPosition); + insertPosition = newInsertPosition; } + // check for duplicates, only insert if the item isn't there already + if (insertPosition == targets.size() || list2.at(i) != targets.at(insertPosition)) + targets.insert(insertPosition, list2.at(i)); } - // If there are no matching new points, and the existing points are all stationary, - // there's no need to send an event to this item. This is required by a test in - // tst_qquickwindow::touchEvent_basic: - // a single stationary press on an item shouldn't cause an event - if (matchingNewPoints.isEmpty()) { - bool stationaryOnly = true; - - foreach (const QTouchEvent::TouchPoint &tp, matchingPoints) { - if (tp.state() != Qt::TouchPointStationary) { - stationaryOnly = false; - break; + return targets; +} + +void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) +{ + qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); + + QSet<QQuickItem *> hasFiltered; + if (event->isPressEvent()) + deliverPressEvent(event, &hasFiltered); + if (!event->allPointsAccepted()) + deliverUpdatedTouchPoints(event, &hasFiltered); + + // Remove released points from itemForTouchPointId + bool allReleased = true; + int pointCount = event->pointCount(); + for (int i = 0; i < pointCount; ++i) { + QQuickEventPoint *point = event->point(i); + if (point->state() == QQuickEventPoint::Released) { + int id = point->pointId(); + qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released"; + point->setGrabber(nullptr); + if (id == touchMouseId) { + touchMouseId = -1; + touchMouseDevice = nullptr; } + } else { + allReleased = false; } + } - if (stationaryOnly) - matchingPoints.clear(); + if (allReleased && !event->grabbers().isEmpty()) { + qWarning() << "No release received for some grabbers" << event->grabbers(); + event->clearGrabbers(); } +} - if (!matchingPoints.isEmpty()) { - // Now we know this item might be interested in the event. Copy and send it, but - // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints. - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform()); - deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints, hasFiltered); +// Deliver touch points to existing grabbers +bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered) +{ + for (auto grabber: event->grabbers()) { + deliverMatchingPointsToItem(grabber, event, hasFiltered); + } + + return false; +} + +// Deliver newly pressed touch points +bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQuickItem *> *hasFiltered) +{ + const QVector<QPointF> points = event->unacceptedPressedPointScenePositions(); + QVector<QQuickItem *> targetItems; + for (QPointF point: points) { + QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false); + if (targetItems.count()) { + targetItems = mergePointerTargets(targetItems, targetItemsForPoint); + } else { + targetItems = targetItemsForPoint; + } } - // record the fact that this item has been visited already - updatedPoints->remove(item); + for (QQuickItem *item: targetItems) { + deliverMatchingPointsToItem(item, event, hasFiltered); + if (event->allPointsAccepted()) + break; + } - // recursion is done only if ALL touch points have been delivered - return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty()); + return event->allPointsAccepted(); } -// touchEventForItem has no means to generate a touch event that contains -// only the points that are relevant for this item. Thus the need for -// matchingPoints to already be that set of interesting points. -// They are all pre-transformed, too. -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem *> *hasFiltered) +bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem *> *hasFiltered) { - QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints)); - touchEvent.data()->setTarget(item); - bool touchEventAccepted = false; + Q_Q(QQuickWindow); + + // TODO: unite this mouse point delivery with the synthetic mouse event below + if (auto event = pointerEvent->asPointerMouseEvent()) { + if (item->acceptedMouseButtons() & event->button()) { + auto point = event->point(0); + if (point->isAccepted()) + return false; + QPointF localPos = item->mapFromScene(point->scenePos()); + Q_ASSERT(item->contains(localPos)); // transform is checked already + QMouseEvent *me = event->asMouseEvent(localPos); + me->accept(); + q->sendEvent(item, me); + if (me->isAccepted()) { + if (!q->mouseGrabberItem()) + item->grabMouse(); + point->setAccepted(true); + } + return me->isAccepted(); + } + return false; + } + + QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent(); + if (!event) + return false; + + QScopedPointer<QTouchEvent> touchEvent(event->touchEventForItem(item)); + if (!touchEvent) + return false; qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item; + bool eventAccepted = false; // First check whether the parent wants to be a filter, // and if the parent accepts the event we are done. @@ -2292,114 +2271,53 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; - foreach (int id, matchingNewPoints) - acceptedNewPoints->insert(id); + for (auto point: qAsConst(touchEvent->touchPoints())) { + event->pointById(point.id())->setAccepted(); + } return true; } - // Since it can change in sendEvent, update itemForTouchPointId now - foreach (int id, matchingNewPoints) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "->" << item; - itemForTouchPointId[id] = item; - } - // Deliver the touch event to the given item qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; QCoreApplication::sendEvent(item, touchEvent.data()); - touchEventAccepted = touchEvent->isAccepted(); + eventAccepted = touchEvent->isAccepted(); // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it. QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { + if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - event->setAccepted(translateTouchToMouse(item, touchEvent.data())); - if (event->isAccepted()) { - touchEventAccepted = true; - } + if (deliverTouchAsMouse(item, event)) + eventAccepted = true; } - if (touchEventAccepted) { + if (eventAccepted) { // If the touch was accepted (regardless by whom or in what form), - // update acceptedNewPoints. - foreach (int id, matchingNewPoints) - acceptedNewPoints->insert(id); + // update accepted new points. + for (auto point: qAsConst(touchEvent->touchPoints())) { + auto pointerEventPoint = event->pointById(point.id()); + pointerEventPoint->setAccepted(); + if (point.state() == Qt::TouchPointPressed) + pointerEventPoint->setGrabber(item); + } } else { // But if the event was not accepted then we know this item // will not be interested in further updates for those touchpoint IDs either. - foreach (int id, matchingNewPoints) - if (itemForTouchPointId[id] == item) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "disassociated"; - itemForTouchPointId.remove(id); - } - } - - return touchEventAccepted; -} - -// create touch event containing only points inside the target item -QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds) -{ - const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints(); - QList<QTouchEvent::TouchPoint> pointsInBounds; - // if all points are stationary, the list of points should be empty to signal a no-op - if (originalEvent.touchPointStates() != Qt::TouchPointStationary) { - for (int i = 0; i < touchPoints.count(); ++i) { - const QTouchEvent::TouchPoint &tp = touchPoints.at(i); - // Touch presses are relevant to the target item only if they occur inside its bounds. - // Touch updates and releases are relevant if they occur inside, or if we want to - // finish the sequence because the press occurred inside. - if (tp.state() == Qt::TouchPointPressed || alwaysCheckBounds) { - QPointF p = target->mapFromScene(tp.scenePos()); - if (target->contains(p)) - pointsInBounds.append(tp); - } else { - pointsInBounds.append(tp); + for (auto point: qAsConst(touchEvent->touchPoints())) { + if (point.state() == Qt::TouchPointPressed) { + if (event->pointById(point.id())->grabber() == item) { + qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated"; + event->pointById(point.id())->setGrabber(nullptr); + } } } - transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->windowToItemTransform()); - } - - QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds); - touchEvent->setTarget(target); - return touchEvent; -} - -// copy a touch event's basic properties but give it new touch points -QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints) -{ - Qt::TouchPointStates eventStates; - for (int i=0; i<newPoints.count(); i++) - eventStates |= newPoints[i].state(); - // if all points have the same state, set the event type accordingly - QEvent::Type eventType = event.type(); - switch (eventStates) { - case Qt::TouchPointPressed: - eventType = QEvent::TouchBegin; - break; - case Qt::TouchPointReleased: - eventType = QEvent::TouchEnd; - break; - default: - eventType = QEvent::TouchUpdate; - break; } - QTouchEvent *touchEvent = new QTouchEvent(eventType); - touchEvent->setWindow(event.window()); - touchEvent->setTarget(event.target()); - touchEvent->setDevice(event.device()); - touchEvent->setModifiers(event.modifiers()); - touchEvent->setTouchPoints(newPoints); - touchEvent->setTouchPointStates(eventStates); - touchEvent->setTimestamp(event.timestamp()); - touchEvent->accept(); - return touchEvent; + return eventAccepted; } #ifndef QT_NO_DRAGANDDROP void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event) { - Q_Q(QQuickWindow); grabber->resetTarget(); QQuickDragGrabber::iterator grabItem = grabber->begin(); if (grabItem != grabber->end()) { @@ -2415,7 +2333,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e e->mouseButtons(), e->keyboardModifiers()); QQuickDropEventEx::copyActions(&translatedEvent, *e); - q->sendEvent(**grabItem, &translatedEvent); + QCoreApplication::sendEvent(**grabItem, &translatedEvent); e->setAccepted(translatedEvent.isAccepted()); e->setDropAction(translatedEvent.dropAction()); grabber->setTarget(**grabItem); @@ -2424,7 +2342,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave. QDragLeaveEvent leaveEvent; for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) - q->sendEvent(**grabItem, &leaveEvent); + QCoreApplication::sendEvent(**grabItem, &leaveEvent); return; } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event); @@ -2440,18 +2358,18 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e moveEvent->mouseButtons(), moveEvent->keyboardModifiers()); QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent); - q->sendEvent(**grabItem, &translatedEvent); + QCoreApplication::sendEvent(**grabItem, &translatedEvent); ++grabItem; } else { QDragLeaveEvent leaveEvent; - q->sendEvent(**grabItem, &leaveEvent); + QCoreApplication::sendEvent(**grabItem, &leaveEvent); grabItem = grabber->release(grabItem); } } return; } else { QDragLeaveEvent leaveEvent; - q->sendEvent(**grabItem, &leaveEvent); + QCoreApplication::sendEvent(**grabItem, &leaveEvent); } } } @@ -2470,7 +2388,6 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event) { - Q_Q(QQuickWindow); bool accepted = false; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled) @@ -2505,7 +2422,7 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte event->keyboardModifiers(), event->type()); QQuickDropEventEx::copyActions(&translatedEvent, *event); - q->sendEvent(item, &translatedEvent); + QCoreApplication::sendEvent(item, &translatedEvent); if (event->type() == QEvent::DragEnter) { if (translatedEvent.isAccepted()) { grabber->grab(item); @@ -2568,8 +2485,10 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF } #endif -bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem *> *hasFiltered) +bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered) { + Q_Q(QQuickWindow); + if (!target) return false; @@ -2578,8 +2497,8 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { hasFiltered->insert(target); - QScopedPointer<QTouchEvent> targetEvent(touchEventForItem(target, *event)); - if (!targetEvent->touchPoints().isEmpty()) { + QScopedPointer<QTouchEvent> targetEvent(event->touchEventForItem(target, true)); + if (targetEvent) { if (target->childMouseEventFilter(item, targetEvent.data())) { qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target; QVector<int> touchIds; @@ -2588,9 +2507,10 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem for (int i = 0; i < touchPointCount; ++i) touchIds.append(targetEvent->touchPoints().at(i).id()); target->grabTouchPoints(touchIds); - if (mouseGrabberItem) { - mouseGrabberItem->ungrabMouse(); + if (q->mouseGrabberItem()) { + q->mouseGrabberItem()->ungrabMouse(); touchMouseId = -1; + touchMouseDevice = nullptr; } filtered = true; } @@ -2602,12 +2522,6 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem switch (tp.state()) { case Qt::TouchPointPressed: t = QEvent::MouseButtonPress; - if (touchMouseId == -1) { - // We don't want to later filter touches as a mouse event if they were pressed - // while a touchMouseId was already active. - // Remember this touch as a potential to become the touchMouseId. - touchMouseIdCandidates.insert(tp.id()); - } break; case Qt::TouchPointReleased: t = QEvent::MouseButtonRelease; @@ -2620,18 +2534,20 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem } // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId - if ((touchMouseIdCandidates.contains(tp.id()) && touchMouseId == -1) || touchMouseId == tp.id()) { + if (touchMouseId == -1 || touchMouseId == tp.id()) { // targetEvent is already transformed wrt local position, velocity, etc. - QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event, item, false)); + + // FIXME: remove asTouchEvent!!! + QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false)); if (target->childMouseEventFilter(item, mouseEvent.data())) { qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target; if (t != QEvent::MouseButtonRelease) { qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; - itemForTouchPointId[tp.id()] = target; touchMouseId = tp.id(); + touchMouseDevice = event->device(); + touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target); target->grabMouse(); } - touchMouseIdCandidates.clear(); filtered = true; } // Only one event can be filtered as a mouse event. @@ -2785,8 +2701,13 @@ void QQuickWindowPrivate::contextCreationFailureMessage(const QSurfaceFormat &fo /*! Propagates an event \a e to a QQuickItem \a item on the window. + Use \l QCoreApplication::sendEvent() directly instead. + The return value is currently not used. + + \deprecated */ +// ### Qt6: remove bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) { Q_D(QQuickWindow); @@ -2808,9 +2729,6 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) QCoreApplication::sendEvent(item, e); } break; - case QEvent::ShortcutOverride: - QCoreApplication::sendEvent(item, e); - break; case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: @@ -2824,41 +2742,8 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) } } break; - case QEvent::UngrabMouse: { - QSet<QQuickItem *> hasFiltered; - if (!d->sendFilteredMouseEvent(item->parentItem(), item, e, &hasFiltered)) { - e->accept(); - item->mouseUngrabEvent(); - } - } - break; -#ifndef QT_NO_WHEELEVENT - case QEvent::Wheel: -#endif -#ifndef QT_NO_DRAGANDDROP - case QEvent::DragEnter: - case QEvent::DragMove: - case QEvent::DragLeave: - case QEvent::Drop: -#endif - case QEvent::FocusIn: - case QEvent::FocusOut: - case QEvent::HoverEnter: - case QEvent::HoverLeave: - case QEvent::HoverMove: - case QEvent::TouchCancel: - QCoreApplication::sendEvent(item, e); - break; - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: { - QSet<QQuickItem*> hasFiltered; - QTouchEvent *ev = static_cast<QTouchEvent *>(e); - qCDebug(DBG_TOUCH) << " - sendEvent for " << ev << " to " << item->parentItem() << " and " << item; - d->sendFilteredTouchEvent(item->parentItem(), item, ev, &hasFiltered); - } - break; default: + QCoreApplication::sendEvent(item, e); break; } @@ -3219,7 +3104,7 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) << itemPriv->paintNode; nodes.removeAll(0); - Q_ASSERT(nodes.first() == itemPriv->itemNodeInstance); + Q_ASSERT(nodes.constFirst() == itemPriv->itemNodeInstance); for (int i=1; i<nodes.size(); ++i) { QSGNode *n = nodes.at(i); // Failing this means we messed up reparenting diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 40a361a897..3328eb65c4 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -53,6 +53,7 @@ #include "qquickitem.h" #include "qquickwindow.h" +#include "qquickevents_p_p.h" #include <QtQuick/private/qsgcontext_p.h> @@ -68,13 +69,19 @@ QT_BEGIN_NAMESPACE -//Make it easy to identify and customize the root item if needed - +class QOpenGLVertexArrayObjectHelper; class QQuickAnimatorController; -class QSGRenderLoop; -class QQuickRenderControl; class QQuickDragGrabber; +class QQuickItemPrivate; +class QQuickPointerDevice; +class QQuickRenderControl; +class QQuickWindowIncubationController; +class QQuickWindowPrivate; +class QQuickWindowRenderLoop; +class QSGRenderLoop; +class QTouchEvent; +//Make it easy to identify and customize the root item if needed class QQuickRootItem : public QQuickItem { Q_OBJECT @@ -85,15 +92,6 @@ public Q_SLOTS: void setHeight(int h) {QQuickItem::setHeight(qreal(h));} }; -class QQuickItemPrivate; -class QQuickWindowPrivate; - -class QTouchEvent; -class QQuickWindowRenderLoop; -class QQuickWindowIncubationController; - -class QOpenGLVertexArrayObjectHelper; - class Q_QUICK_PRIVATE_EXPORT QQuickCustomRenderStage { public: @@ -127,7 +125,6 @@ public: void deliverKeyEvent(QKeyEvent *e); // Keeps track of the item currently receiving mouse events - QQuickItem *mouseGrabberItem; #ifndef QT_NO_CURSOR QQuickItem *cursorItem; #endif @@ -135,20 +132,19 @@ public: QQuickDragGrabber *dragGrabber; #endif int touchMouseId; + QQuickPointerDevice *touchMouseDevice; bool checkIfDoubleClicked(ulong newPressEventTimestamp); ulong touchMousePressTimestamp; // Mouse positions are saved in widget coordinates QPointF lastMousePosition; - bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event); + bool deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent); void translateTouchEvent(QTouchEvent *touchEvent); void setMouseGrabber(QQuickItem *grabber); void grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); - static void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); - bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *); - bool deliverMouseEvent(QMouseEvent *); + void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet<QQuickItem *> *); #ifndef QT_NO_WHEELEVENT bool deliverWheelEvent(QQuickItem *, QWheelEvent *); @@ -156,23 +152,33 @@ public: #ifndef QT_NO_GESTURES bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *); #endif - bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *, - QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *, QSet<QQuickItem*> *filtered); + + // entry point of events to the window void handleTouchEvent(QTouchEvent *); void handleMouseEvent(QMouseEvent *); - void deliverTouchEvent(QTouchEvent *); bool compressTouchEvent(QTouchEvent *); - bool deliverTouchCancelEvent(QTouchEvent *); - void deliverDelayedTouchEvent(); void flushFrameSynchronousEvents(); + void deliverDelayedTouchEvent(); + + // delivery of pointer events: + QQuickPointerEvent *pointerEventInstance(QEvent *ev); + void deliverPointerEvent(QQuickPointerEvent *); + void deliverTouchEvent(QQuickPointerTouchEvent *); + bool deliverTouchCancelEvent(QTouchEvent *); + bool deliverPressEvent(QQuickPointerEvent *, QSet<QQuickItem *> *); + bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered); + bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem*> *filtered); + bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem*> *filtered); + + QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; + QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const; + + // hover delivery bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted); - bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem*> *filtered); - static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); - static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints); - bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem*> *filtered); bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted); bool clearHover(ulong timestamp = 0); + #ifndef QT_NO_DRAGANDDROP void deliverDragEvent(QQuickDragGrabber *, QEvent *); bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *); @@ -237,7 +243,8 @@ public: QQuickRenderControl *renderControl; QQuickAnimatorController *animationController; QScopedPointer<QTouchEvent> delayedTouch; - int touchRecursionGuard; + + int pointerEventRecursionGuard; QQuickCustomRenderStage *customRenderStage; QColor clearColor; @@ -258,10 +265,6 @@ public: QOpenGLVertexArrayObjectHelper *vaoHelper; - // Keeps track of which touch point (int) was last accepted by which item - QHash<int, QQuickItem *> itemForTouchPointId; - QSet<int> touchMouseIdCandidates; - mutable QQuickWindowIncubationController *incubationController; static bool defaultAlphaBuffer; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index d900688173..7b9e749532 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -218,11 +218,11 @@ void QSGSoftwareRenderableNode::update() struct RenderNodeState : public QSGRenderNode::RenderState { const QMatrix4x4 *projectionMatrix() const override { return &ident; } - QRect scissorRect() const { return QRect(); } - bool scissorEnabled() const { return false; } - int stencilValue() const { return 0; } - bool stencilEnabled() const { return false; } - const QRegion *clipRegion() const { return &cr; } + QRect scissorRect() const override { return QRect(); } + bool scissorEnabled() const override { return false; } + int stencilValue() const override { return 0; } + bool stencilEnabled() const override { return false; } + const QRegion *clipRegion() const override { return &cr; } QMatrix4x4 ident; QRegion cr; }; diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index c0967bcb09..2211f88973 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -1268,7 +1268,7 @@ QSGRootNode::QSGRootNode() QSGRootNode::~QSGRootNode() { while (!m_renderers.isEmpty()) - m_renderers.last()->setRootNode(0); + m_renderers.constLast()->setRootNode(0); destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode. } diff --git a/src/quick/util/qquickanimatorcontroller.cpp b/src/quick/util/qquickanimatorcontroller.cpp index 3fc7d87840..eb902b2972 100644 --- a/src/quick/util/qquickanimatorcontroller.cpp +++ b/src/quick/util/qquickanimatorcontroller.cpp @@ -265,7 +265,7 @@ void QQuickAnimatorController::animationFinished(QAbstractAnimationJob *job) * needed in any case. */ if (!m_deleting.contains(job)) { - QQuickAnimatorProxyJob *proxy = m_animatorRoots[job]; + QQuickAnimatorProxyJob *proxy = m_animatorRoots.value(job); if (proxy) { m_window->update(); m_proxiesToStop << proxy; diff --git a/src/quick/util/qquickfontloader.cpp b/src/quick/util/qquickfontloader.cpp index b7367f3934..6e23d99f7e 100644 --- a/src/quick/util/qquickfontloader.cpp +++ b/src/quick/util/qquickfontloader.cpp @@ -264,7 +264,7 @@ void QQuickFontLoader::setSource(const QUrl &url) updateFontInfo(QString(), Error); } } else { - updateFontInfo(QFontDatabase::applicationFontFamilies(fontLoaderFonts()->map[d->url]->id).at(0), Ready); + updateFontInfo(QFontDatabase::applicationFontFamilies(fontLoaderFonts()->map.value(d->url)->id).at(0), Ready); } } else { if (!fontLoaderFonts()->map.contains(d->url)) { @@ -280,7 +280,7 @@ void QQuickFontLoader::setSource(const QUrl &url) // Silently fail if compiled with no_network #endif } else { - QQuickFontObject *fo = fontLoaderFonts()->map[d->url]; + QQuickFontObject *fo = fontLoaderFonts()->map.value(d->url); if (fo->id == -1) { #ifndef QT_NO_NETWORK d->status = Loading; diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 6b491a433c..dfed4c1885 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -382,7 +382,7 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en } // Fixup end points - const AttributePoint &last = attributePoints.last(); + const AttributePoint &last = attributePoints.constLast(); for (int ii = 0; ii < attributes.count(); ++ii) { if (!last.values.contains(attributes.at(ii))) endpoint(attributePoints, attributes.at(ii)); @@ -407,11 +407,11 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en } attributePoints[ii].origpercent /= length; attributePoints[ii].percent = point.values.value(percentString); - prevorigpercent = attributePoints[ii].origpercent; - prevpercent = attributePoints[ii].percent; + prevorigpercent = attributePoints.at(ii).origpercent; + prevpercent = attributePoints.at(ii).percent; } else { attributePoints[ii].origpercent /= length; - attributePoints[ii].percent = attributePoints[ii].origpercent; + attributePoints[ii].percent = attributePoints.at(ii).origpercent; } } diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index dc4b27d738..49956de822 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -629,7 +629,7 @@ void QQuickPixmapReader::processJobs() // Find a job we can use bool usableJob = false; for (int i = jobs.count() - 1; !usableJob && i >= 0; i--) { - QQuickPixmapReply *job = jobs[i]; + QQuickPixmapReply *job = jobs.at(i); const QUrl url = job->url; QString localFile; QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid; diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index c4be68cd31..555533a44e 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -287,7 +287,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler; handler->property = prop; handler->expression.take(new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(prop)->signalIndex(), - QQmlContextData::get(qmlContext(q)), object, compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex])); + QQmlContextData::get(qmlContext(q)), object, compilationUnit->runtimeFunctions.at(binding->value.compiledScriptIndex))); signalReplacements << handler; return; } @@ -455,7 +455,7 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions() QQmlBinding *newBinding = 0; if (e.id != QQmlBinding::Invalid) { QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this))); - QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, object(), d->compilationUnit->runtimeFunctions[e.id])); + QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, object(), d->compilationUnit->runtimeFunctions.at(e.id))); newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, function, object(), context); } diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp index a992589040..e7beee8ed3 100644 --- a/src/quick/util/qquicksmoothedanimation.cpp +++ b/src/quick/util/qquicksmoothedanimation.cpp @@ -409,7 +409,7 @@ QAbstractAnimationJob* QQuickSmoothedAnimation::transition(QQuickStateActions &a Q_UNUSED(direction); Q_D(QQuickSmoothedAnimation); - QQuickStateActions dataActions = QQuickPropertyAnimation::createTransitionActions(actions, modified, defaultTarget); + const QQuickStateActions dataActions = QQuickPropertyAnimation::createTransitionActions(actions, modified, defaultTarget); QContinuingAnimationGroupJob *wrapperGroup = new QContinuingAnimationGroupJob(); diff --git a/src/quick/util/qquicktimeline.cpp b/src/quick/util/qquicktimeline.cpp index 74baa3bfda..b3f0caa2b3 100644 --- a/src/quick/util/qquicktimeline.cpp +++ b/src/quick/util/qquicktimeline.cpp @@ -154,7 +154,7 @@ void QQuickTimeLinePrivate::add(QQuickTimeLineObject &g, const Op &o) } if (!iter->ops.isEmpty() && o.type == Op::Pause && - iter->ops.last().type == Op::Pause) { + iter->ops.constLast().type == Op::Pause) { iter->ops.last().length += o.length; iter->length += o.length; } else { diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index ad04c76783..b8c5b900c9 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -575,8 +575,8 @@ void QQuickWidget::setContent(const QUrl& url, QQmlComponent *component, QObject d->component = component; if (d->component && d->component->isError()) { - QList<QQmlError> errorList = d->component->errors(); - foreach (const QQmlError &error, errorList) { + const QList<QQmlError> errorList = d->component->errors(); + for (const QQmlError &error : errorList) { QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning() << error; } @@ -1018,8 +1018,8 @@ void QQuickWidget::continueExecute() disconnect(d->component, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(continueExecute())); if (d->component->isError()) { - QList<QQmlError> errorList = d->component->errors(); - foreach (const QQmlError &error, errorList) { + const QList<QQmlError> errorList = d->component->errors(); + for (const QQmlError &error : errorList) { QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning() << error; } @@ -1030,8 +1030,8 @@ void QQuickWidget::continueExecute() QObject *obj = d->component->create(); if (d->component->isError()) { - QList<QQmlError> errorList = d->component->errors(); - foreach (const QQmlError &error, errorList) { + const QList<QQmlError> errorList = d->component->errors(); + for (const QQmlError &error : errorList) { QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning() << error; } diff --git a/src/quickwidgets/quickwidgets.pro b/src/quickwidgets/quickwidgets.pro index 87409e31c5..2438e577ae 100644 --- a/src/quickwidgets/quickwidgets.pro +++ b/src/quickwidgets/quickwidgets.pro @@ -2,7 +2,7 @@ TARGET = QtQuickWidgets QT = core-private gui-private qml-private quick-private widgets-private -DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES +DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FOREACH HEADERS += \ qquickwidget.h \ diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 3ceeb97718..cd55ae191d 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -1444,6 +1444,7 @@ void tst_QJSEngine::valueConversion_QVariant() QCOMPARE(qjsvalue_cast<QVariant>(QJSValue(123)), QVariant(123)); QVERIFY(eng.toScriptValue(QVariant(QMetaType::VoidStar, 0)).isNull()); + QVERIFY(eng.toScriptValue(QVariant::fromValue(nullptr)).isNull()); { QVariantMap map; diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index 859caf72c7..28b9adea38 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -978,8 +978,8 @@ void tst_QJSValue::toVariant() QCOMPARE(qjsvalue_cast<QVariant>(undefined), QVariant()); QJSValue null = eng.evaluate("null"); - QCOMPARE(null.toVariant(), QVariant(QMetaType::VoidStar, 0)); - QCOMPARE(qjsvalue_cast<QVariant>(null), QVariant(QMetaType::VoidStar, 0)); + QCOMPARE(null.toVariant(), QVariant::fromValue(nullptr)); + QCOMPARE(qjsvalue_cast<QVariant>(null), QVariant::fromValue(nullptr)); { QJSValue number = eng.toScriptValue(123.0); @@ -1055,8 +1055,8 @@ void tst_QJSValue::toVariant() QCOMPARE(qjsvalue_cast<QVariant>(undef), QVariant()); QJSValue nil = QJSValue(QJSValue::NullValue); - QCOMPARE(nil.toVariant(), QVariant(QMetaType::VoidStar, 0)); - QCOMPARE(qjsvalue_cast<QVariant>(nil), QVariant(QMetaType::VoidStar, 0)); + QCOMPARE(nil.toVariant(), QVariant::fromValue(nullptr)); + QCOMPARE(qjsvalue_cast<QVariant>(nil), QVariant::fromValue(nullptr)); } // array diff --git a/tests/auto/qml/qmldiskcache/qmldiskcache.pro b/tests/auto/qml/qmldiskcache/qmldiskcache.pro index f2d1a04780..f98a157b6a 100644 --- a/tests/auto/qml/qmldiskcache/qmldiskcache.pro +++ b/tests/auto/qml/qmldiskcache/qmldiskcache.pro @@ -4,4 +4,6 @@ osx:CONFIG -= app_bundle SOURCES += tst_qmldiskcache.cpp +RESOURCES += test.qml + QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmldiskcache/test.qml b/tests/auto/qml/qmldiskcache/test.qml new file mode 100644 index 0000000000..d632422616 --- /dev/null +++ b/tests/auto/qml/qmldiskcache/test.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int value: 20 +} diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index dd23dfca07..8ccf4714ba 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -38,6 +38,9 @@ #include <QQmlEngine> #include <QQmlFileSelector> #include <QThread> +#include <QCryptographicHash> +#include <QStandardPaths> +#include <QDirIterator> class tst_qmldiskcache: public QObject { @@ -52,6 +55,7 @@ private slots: void recompileAfterChange(); void fileSelectors(); void localAliases(); + void cacheResources(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -529,6 +533,30 @@ void tst_qmldiskcache::localAliases() } } +void tst_qmldiskcache::cacheResources() +{ + const QString cacheDirectory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + QVERIFY(QDir::root().mkpath(cacheDirectory)); + + const QString qmlCacheDirectory = cacheDirectory + QLatin1String("/qmlcache/"); + QVERIFY(QDir(qmlCacheDirectory).removeRecursively()); + QVERIFY(QDir::root().mkpath(qmlCacheDirectory)); + QVERIFY(QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot).isEmpty()); + + + QQmlEngine engine; + + { + CleanlyLoadingComponent component(&engine, QUrl("qrc:/test.qml")); + qDebug() << component.errorString(); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 20); + } + + QCOMPARE(QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot | QDir::Files).count(), 1); +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index de3ead917f..30ecfa7d96 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -5814,7 +5814,7 @@ void tst_qqmlecmascript::variants() QVERIFY(object != 0); QCOMPARE(object->property("undefinedVariant").type(), QVariant::Invalid); - QCOMPARE(int(object->property("nullVariant").type()), int(QMetaType::VoidStar)); + QCOMPARE(int(object->property("nullVariant").type()), int(QMetaType::Nullptr)); QCOMPARE(object->property("intVariant").type(), QVariant::Int); QCOMPARE(object->property("doubleVariant").type(), QVariant::Double); @@ -7470,8 +7470,11 @@ void tst_qqmlecmascript::negativeYear() QVariant q; QMetaObject::invokeMethod(object, "check_negative_tostring", Q_RETURN_ARG(QVariant, q)); - // Strip the timezone. It should be irrelevant as the date was created with the same one. - QCOMPARE(q.toString().left(32), QStringLiteral("result: Sat Jan 1 00:00:00 -2001")); + + // Only check for the year. We hope that every language writes the year in arabic numerals and + // in relation to a specific dude's date of birth. We also hope that no language adds a "-2001" + // junk string somewhere in the middle. + QVERIFY(q.toString().indexOf(QStringLiteral("-2001")) != -1); QMetaObject::invokeMethod(object, "check_negative_toisostring", Q_RETURN_ARG(QVariant, q)); QCOMPARE(q.toString().left(16), QStringLiteral("result: -002000-")); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index f32051a26a..ad06946b0b 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2601,7 +2601,7 @@ void tst_qqmllanguage::basicRemote_data() QTest::newRow("no need for qmldir") << QUrl(serverdir+"Test.qml") << "" << ""; QTest::newRow("absent qmldir") << QUrl(serverdir+"/noqmldir/Test.qml") << "" << ""; - QTest::newRow("need qmldir") << QUrl(serverdir+"TestLocal.qml") << "" << ""; + QTest::newRow("need qmldir") << QUrl(serverdir+"TestNamed.qml") << "" << ""; } void tst_qqmllanguage::basicRemote() @@ -2641,6 +2641,8 @@ void tst_qqmllanguage::importsRemote_data() << ""; QTest::newRow("remote import with local") << "import \""+serverdir+"\"\nTestLocal {}" << "QQuickImage" << ""; + QTest::newRow("remote import with qualifier") << "import \""+serverdir+"\" as NS\nNS.NamedLocal {}" << "QQuickImage" + << ""; QTest::newRow("wrong remote import with undeclared local") << "import \""+serverdir+"\"\nWrongTestLocal {}" << "" << "WrongTestLocal is not a type"; QTest::newRow("wrong remote import of internal local") << "import \""+serverdir+"\"\nLocalInternal {}" << "" @@ -4126,7 +4128,7 @@ void tst_qqmllanguage::preservePropertyCacheOnGroupObjects() QVERIFY(subCache); QQmlPropertyData *pd = subCache->property(QStringLiteral("newProperty"), /*object*/0, /*context*/0); QVERIFY(pd); - QCOMPARE(pd->propType, qMetaTypeId<int>()); + QCOMPARE(pd->propType(), qMetaTypeId<int>()); } void tst_qqmllanguage::propertyCacheInSync() diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index 2916d8455c..824fe445c0 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -111,16 +111,16 @@ void tst_qqmlpropertycache::properties() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "propertyA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyA")); QVERIFY((data = cacheProperty(cache, "propertyB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyB")); QVERIFY((data = cacheProperty(cache, "propertyC"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyC")); QVERIFY((data = cacheProperty(cache, "propertyD"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyD")); } void tst_qqmlpropertycache::propertiesDerived() @@ -135,16 +135,16 @@ void tst_qqmlpropertycache::propertiesDerived() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "propertyA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyA")); QVERIFY((data = cacheProperty(cache, "propertyB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyB")); QVERIFY((data = cacheProperty(cache, "propertyC"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyC")); QVERIFY((data = cacheProperty(cache, "propertyD"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyD")); } void tst_qqmlpropertycache::methods() @@ -158,28 +158,28 @@ void tst_qqmlpropertycache::methods() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "slotA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotA()")); QVERIFY((data = cacheProperty(cache, "slotB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotB()")); QVERIFY((data = cacheProperty(cache, "signalA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); QVERIFY((data = cacheProperty(cache, "signalB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); QVERIFY((data = cacheProperty(cache, "propertyAChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); QVERIFY((data = cacheProperty(cache, "propertyBChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); QVERIFY((data = cacheProperty(cache, "propertyCChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); QVERIFY((data = cacheProperty(cache, "propertyDChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } void tst_qqmlpropertycache::methodsDerived() @@ -194,28 +194,28 @@ void tst_qqmlpropertycache::methodsDerived() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "slotA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotA()")); QVERIFY((data = cacheProperty(cache, "slotB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotB()")); QVERIFY((data = cacheProperty(cache, "signalA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); QVERIFY((data = cacheProperty(cache, "signalB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); QVERIFY((data = cacheProperty(cache, "propertyAChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); QVERIFY((data = cacheProperty(cache, "propertyBChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); QVERIFY((data = cacheProperty(cache, "propertyCChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); QVERIFY((data = cacheProperty(cache, "propertyDChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } void tst_qqmlpropertycache::signalHandlers() @@ -229,22 +229,22 @@ void tst_qqmlpropertycache::signalHandlers() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "onSignalA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); QVERIFY((data = cacheProperty(cache, "onSignalB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); QVERIFY((data = cacheProperty(cache, "onPropertyAChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyBChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyCChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyDChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } void tst_qqmlpropertycache::signalHandlersDerived() @@ -259,22 +259,22 @@ void tst_qqmlpropertycache::signalHandlersDerived() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "onSignalA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); QVERIFY((data = cacheProperty(cache, "onSignalB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); QVERIFY((data = cacheProperty(cache, "onPropertyAChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyBChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyCChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyDChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } class TestClass : public QObject diff --git a/tests/auto/qml/qqmlqt/data/exit.qml b/tests/auto/qml/qqmlqt/data/exit.qml new file mode 100644 index 0000000000..12727d9f04 --- /dev/null +++ b/tests/auto/qml/qqmlqt/data/exit.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +QtObject { + property int returnCode: -1 + Component.onCompleted: Qt.exit(returnCode) +} + diff --git a/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml new file mode 100644 index 0000000000..9d73640c87 --- /dev/null +++ b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +QtObject { + Component.onCompleted: { + var t = tp.time; + tp.time = t; + } +} diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index 8150241e4a..0576650d01 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -46,6 +46,15 @@ #include <QFont> #include "../../shared/util.h" +// Copied from tst_qdatetime.cpp +#ifdef Q_OS_WIN +# include <qt_windows.h> +# include <time.h> +# if defined(Q_OS_WINRT) +# define tzset() +# endif +#endif + class tst_qqmlqt : public QQmlDataTest { Q_OBJECT @@ -87,11 +96,15 @@ private slots: void atob(); void fontFamilies(); void quit(); + void exit(); void resolvedUrl(); void later_data(); void later(); void qtObjectContents(); + void timeRoundtrip_data(); + void timeRoundtrip(); + private: QQmlEngine engine; }; @@ -872,8 +885,6 @@ void tst_qqmlqt::dateTimeFormattingVariants_data() QTime time(11, 16, 39, 755); temporary = QDateTime(QDate(1970,1,1), time); - QTest::newRow("formatDate, qtime") << "formatDate" << QVariant::fromValue(time) << (QStringList() << temporary.date().toString(Qt::DefaultLocaleShortDate) << temporary.date().toString(Qt::DefaultLocaleLongDate) << temporary.date().toString("ddd MMMM d yy")); - QTest::newRow("formatDateTime, qtime") << "formatDateTime" << QVariant::fromValue(time) << (QStringList() << temporary.toString(Qt::DefaultLocaleShortDate) << temporary.toString(Qt::DefaultLocaleLongDate) << temporary.toString("M/d/yy H:m:s a")); QTest::newRow("formatTime, qtime") << "formatTime" << QVariant::fromValue(time) << (QStringList() << temporary.time().toString(Qt::DefaultLocaleShortDate) << temporary.time().toString(Qt::DefaultLocaleLongDate) << temporary.time().toString("H:m:s a") << temporary.time().toString("hh:mm:ss.zzz")); QDate date(2011,5,31); @@ -980,6 +991,20 @@ void tst_qqmlqt::quit() delete object; } +void tst_qqmlqt::exit() +{ + QQmlComponent component(&engine, testFileUrl("exit.qml")); + + QSignalSpy spy(&engine, &QQmlEngine::exit); + QObject *object = component.create(); + QVERIFY(object != Q_NULLPTR); + QCOMPARE(spy.count(), 1); + QList<QVariant> arguments = spy.takeFirst(); + QVERIFY(arguments.at(0).toInt() == object->property("returnCode").toInt()); + + delete object; +} + void tst_qqmlqt::resolvedUrl() { QQmlComponent component(&engine, testFileUrl("resolvedUrl.qml")); @@ -1139,6 +1164,104 @@ void tst_qqmlqt::qtObjectContents() delete object; } +class TimeProvider: public QObject +{ + Q_OBJECT + Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged) + +public: + TimeProvider(const QTime &t) + : m_getTime(t) + {} + + QTime time() const { return m_getTime; } + void setTime(const QTime &t) { m_putTime = t; emit timeChanged(); } + +signals: + void timeChanged(); + +public: + QTime m_getTime, m_putTime; +}; + +class TimeZoneSwitch +{ +public: + TimeZoneSwitch(const char *newZone) + : doChangeZone(qstrcmp(newZone, "localtime") == 0) + { + if (!doChangeZone) + return; + + hadOldZone = qEnvironmentVariableIsSet("TZ"); + if (hadOldZone) { + oldZone = qgetenv("TZ"); + } + qputenv("TZ", newZone); + tzset(); + } + + ~TimeZoneSwitch() + { + if (!doChangeZone) + return; + + if (hadOldZone) + qputenv("TZ", oldZone); + else + qunsetenv("TZ"); + tzset(); + } + +private: + bool doChangeZone; + bool hadOldZone; + QByteArray oldZone; +}; + +void tst_qqmlqt::timeRoundtrip_data() +{ + QTest::addColumn<QTime>("time"); + + // Local timezone: + QTest::newRow("localtime") << QTime(0, 0, 0); + + // No DST: + QTest::newRow("UTC") << QTime(0, 0, 0); + QTest::newRow("Europe/Amsterdam") << QTime(1, 0, 0); + QTest::newRow("Asia/Jakarta") << QTime(7, 0, 0); + + // DST: + QTest::newRow("Namibia/Windhoek") << QTime(1, 0, 0); + QTest::newRow("Australia/Adelaide") << QTime(10, 0, 0); + QTest::newRow("Australia/Hobart") << QTime(10, 0, 0); + QTest::newRow("Pacific/Auckland") << QTime(12, 0, 0); + QTest::newRow("Pacific/Samoa") << QTime(13, 0, 0); +} + +void tst_qqmlqt::timeRoundtrip() +{ +#ifdef Q_OS_WIN + QSKIP("On Windows, the DateObject doesn't handle DST transitions correctly when the timezone is not localtime."); // I.e.: for this test. +#endif + + TimeZoneSwitch tzs(QTest::currentDataTag()); + QFETCH(QTime, time); + + TimeProvider tp(time); + + QQmlEngine eng; + eng.rootContext()->setContextProperty(QLatin1String("tp"), &tp); + QQmlComponent component(&eng, testFileUrl("timeRoundtrip.qml")); + QObject *obj = component.create(); + QVERIFY(obj != 0); + + // QML reads m_getTime and saves the result as m_putTime; this should come out the same, without + // any perturbation (e.g. by DST effects) from converting from QTime to V4's Date and back + // again. + QCOMPARE(tp.m_getTime, tp.m_putTime); +} + QTEST_MAIN(tst_qqmlqt) #include "tst_qqmlqt.moc" diff --git a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp index ea988bb50d..d4922599be 100644 --- a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp +++ b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp @@ -180,7 +180,7 @@ void tst_QQuickFramebufferObject::testThatStuffWorks() qmlRegisterType<FBOItem>("FBOItem", 1, 0, "FBOItem"); QQuickView view; - view.setSource(QUrl::fromLocalFile("data/testStuff.qml")); + view.setSource(testFileUrl("testStuff.qml")); FBOItem *item = view.rootObject()->findChild<FBOItem *>("fbo"); @@ -224,7 +224,7 @@ void tst_QQuickFramebufferObject::testInvalidate() qmlRegisterType<FBOItem>("FBOItem", 1, 0, "FBOItem"); QQuickView view; - view.setSource(QUrl::fromLocalFile("data/testStuff.qml")); + view.setSource(testFileUrl("testStuff.qml")); FBOItem *item = view.rootObject()->findChild<FBOItem *>("fbo"); item->setTextureFollowsItemSize(false); diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index bf9df7850d..b0d903908f 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -250,6 +250,7 @@ private slots: void QTBUG_50105(); void keyNavigationEnabled(); void QTBUG_50097_stickyHeader_positionViewAtIndex(); + void itemFiltered(); private: template <class T> void items(const QUrl &source); @@ -8343,6 +8344,37 @@ void tst_QQuickListView::QTBUG_50097_stickyHeader_positionViewAtIndex() QTRY_COMPARE(listview->contentY(), -100.0); // back to the same position: header visible, items not under the header. } +void tst_QQuickListView::itemFiltered() +{ + QStringListModel model(QStringList() << "one" << "two" << "three" << "four" << "five" << "six"); + QSortFilterProxyModel proxy1; + proxy1.setSourceModel(&model); + proxy1.setSortRole(Qt::DisplayRole); + proxy1.setDynamicSortFilter(true); + proxy1.sort(0); + + QSortFilterProxyModel proxy2; + proxy2.setSourceModel(&proxy1); + proxy2.setFilterRole(Qt::DisplayRole); + proxy2.setFilterRegExp("^[^ ]*$"); + proxy2.setDynamicSortFilter(true); + + QScopedPointer<QQuickView> window(createView()); + window->engine()->rootContext()->setContextProperty("_model", &proxy2); + QQmlComponent component(window->engine()); + component.setData("import QtQuick 2.4; ListView { " + "anchors.fill: parent; model: _model; delegate: Text { width: parent.width;" + "text: model.display; } }", + QUrl()); + window->setContent(QUrl(), &component, component.create()); + + window->show(); + QTest::qWaitForWindowExposed(window.data()); + + // this should not crash + model.setData(model.index(2), QStringLiteral("modified three"), Qt::DisplayRole); +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" diff --git a/tests/auto/quick/qquickloader/data/qmldir b/tests/auto/quick/qquickloader/data/qmldir deleted file mode 100644 index bf42b507c0..0000000000 --- a/tests/auto/quick/qquickloader/data/qmldir +++ /dev/null @@ -1 +0,0 @@ -# For tst_QDeclarativeLoader::networkRequestUrl; no types needed though. diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 9c627ad69c..adf0d282c5 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -769,7 +769,7 @@ void tst_QQuickMouseArea::onMousePressRejected() QVERIFY(!window.rootObject()->property("mr1_canceled").toBool()); QVERIFY(window.rootObject()->property("mr2_pressed").toBool()); QVERIFY(!window.rootObject()->property("mr2_released").toBool()); - QVERIFY(window.rootObject()->property("mr2_canceled").toBool()); + QVERIFY(!window.rootObject()->property("mr2_canceled").toBool()); QTest::qWait(200); @@ -1904,9 +1904,9 @@ void tst_QQuickMouseArea::ignoreBySource() // MouseArea should grab the press because it's interested in non-synthesized mouse events QPoint p = QPoint(80, 80); QTest::mousePress(&window, Qt::LeftButton, 0, p); - QVERIFY(window.mouseGrabberItem() == mouseArea); + QCOMPARE(window.mouseGrabberItem(), mouseArea); // That was a real mouse event - QVERIFY(root->property("lastEventSource").toInt() == Qt::MouseEventNotSynthesized); + QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventNotSynthesized)); // Flickable content should not move p -= QPoint(startDragDistance() + 1, startDragDistance() + 1); @@ -1919,12 +1919,14 @@ void tst_QQuickMouseArea::ignoreBySource() QCOMPARE(flickable->contentY(), 0.); QTest::mouseRelease(&window, Qt::LeftButton, 0, p); + QCOMPARE(window.mouseGrabberItem(), nullptr); // Now try touch events and confirm that MouseArea ignores them, while Flickable does its thing p = QPoint(80, 80); QTest::touchEvent(&window, device).press(0, p, &window); QQuickTouchUtils::flush(&window); - QVERIFY(window.mouseGrabberItem() != mouseArea); + QCOMPARE(window.mouseGrabberItem(), flickable); + // That was a fake mouse event QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventSynthesizedByQt)); p -= QPoint(startDragDistance() + 1, startDragDistance() + 1); diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index c3981c466f..2872556a94 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -915,13 +915,13 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() // Touch both, release one, manipulate other touchpoint with mouse QTest::touchEvent(window.data(), device).press(1, touch1); QQuickTouchUtils::flush(window.data()); - QTest::touchEvent(window.data(), device).press(2, touch2); + QTest::touchEvent(window.data(), device).move(1, touch1).press(2, touch2); QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch2.x()); QCOMPARE(touch2rect->property("y").toInt(), touch2.y()); - QTest::touchEvent(window.data(), device).release(1, touch1); + QTest::touchEvent(window.data(), device).release(1, touch1).move(2, touch2); touch1.setY(20); QTest::mousePress(window.data(), Qt::LeftButton, 0, touch1); QQuickTouchUtils::flush(window.data()); diff --git a/tests/auto/quick/qquickopenglinfo/tst_qquickopenglinfo.cpp b/tests/auto/quick/qquickopenglinfo/tst_qquickopenglinfo.cpp index 355301878d..3bf61e8f17 100644 --- a/tests/auto/quick/qquickopenglinfo/tst_qquickopenglinfo.cpp +++ b/tests/auto/quick/qquickopenglinfo/tst_qquickopenglinfo.cpp @@ -48,7 +48,7 @@ private slots: void tst_QQuickOpenGLInfo::testProperties() { QQuickView view; - view.setSource(QUrl::fromLocalFile("data/basic.qml")); + view.setSource(testFileUrl("basic.qml")); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp index 1d7273f6df..c1a51fd659 100644 --- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp +++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp @@ -511,6 +511,7 @@ void tst_QQuickPinchArea::cancel() QCOMPARE(blackRect->scale(), 1.5); QTouchEvent cancelEvent(QEvent::TouchCancel); + cancelEvent.setDevice(device); QCoreApplication::sendEvent(window, &cancelEvent); QQuickTouchUtils::flush(window); diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index d9bdf47041..acccac8eca 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -157,6 +157,7 @@ public: lastVelocity = lastVelocityFromMouseMove = QVector2D(); lastMousePos = QPointF(); lastMouseCapabilityFlags = 0; + touchEventCount = 0; } static void clearMousePressCounter() @@ -302,6 +303,9 @@ private slots: void touchEvent_reentrant(); void touchEvent_velocity(); + void mergeTouchPointLists_data(); + void mergeTouchPointLists(); + void mouseFromTouch_basic(); void clearWindow(); @@ -362,6 +366,9 @@ private slots: void testHoverChildMouseEventFilter(); void testHoverTimestamp(); + + void pointerEventTypeAndPointCount(); + private: QTouchDevice *touchDevice; QTouchDevice *touchDeviceWithVelocity; @@ -521,9 +528,8 @@ void tst_qquickwindow::touchEvent_basic() // press single point QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window); - QTest::qWait(50); - - QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); + QQuickTouchUtils::flush(window); + QTRY_COMPARE(topItem->lastEvent.touchPoints.count(), 1); QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); @@ -531,9 +537,10 @@ void tst_qquickwindow::touchEvent_basic() // would put the decorated window at that position rather than the window itself. COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos))); topItem->reset(); + QTest::touchEvent(window, touchDevice).release(0, topItem->mapToScene(pos).toPoint(), window); // press multiple points - QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window) + QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window) .press(1, bottomItem->mapToScene(pos).toPoint(), window); QQuickTouchUtils::flush(window); QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); @@ -543,6 +550,7 @@ void tst_qquickwindow::touchEvent_basic() COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos))); topItem->reset(); bottomItem->reset(); + QTest::touchEvent(window, touchDevice).release(0, topItem->mapToScene(pos).toPoint(), window).release(1, bottomItem->mapToScene(pos).toPoint(), window); // touch point on top item moves to bottom item, but top item should still receive the event QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window); @@ -553,6 +561,7 @@ void tst_qquickwindow::touchEvent_basic() COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved, makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos))); topItem->reset(); + QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), window); // touch point on bottom item moves to top item, but bottom item should still receive the event QTest::touchEvent(window, touchDevice).press(0, bottomItem->mapToScene(pos).toPoint(), window); @@ -563,6 +572,7 @@ void tst_qquickwindow::touchEvent_basic() COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved, makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos))); bottomItem->reset(); + QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), window); // a single stationary press on an item shouldn't cause an event QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window); @@ -667,6 +677,7 @@ void tst_qquickwindow::touchEvent_propagation() QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos)))); + QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window); // touch top and middle items, middle item should get both events QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window) @@ -678,6 +689,8 @@ void tst_qquickwindow::touchEvent_propagation() COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos)) << makeTouchPoint(middleItem, pos) ))); + QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window) + .release(1, pointInMiddleItem, window); middleItem->reset(); // disable middleItem as well @@ -702,6 +715,8 @@ void tst_qquickwindow::touchEvent_propagation() bottomItem->acceptTouchEvents = acceptTouchEvents; bottomItem->setEnabled(enableItem); bottomItem->setVisible(showItem); + QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window) + .release(1, pointInMiddleItem, window); // no events should be received QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window) @@ -711,7 +726,9 @@ void tst_qquickwindow::touchEvent_propagation() QVERIFY(topItem->lastEvent.touchPoints.isEmpty()); QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); - + QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window) + .release(1, pointInMiddleItem, window) + .release(2, pointInBottomItem, window); topItem->reset(); middleItem->reset(); bottomItem->reset(); @@ -737,6 +754,7 @@ void tst_qquickwindow::touchEvent_propagation() COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos))); } + QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window); delete topItem; delete middleItem; @@ -855,6 +873,8 @@ void tst_qquickwindow::touchEvent_velocity() QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); QGuiApplication::processEvents(); QQuickTouchUtils::flush(window); + QCOMPARE(item->touchEventCount, 1); + points[0].state = Qt::TouchPointMoved; points[0].area.adjust(5, 5, 5, 5); QVector2D velocity(1.5, 2.5); @@ -887,6 +907,67 @@ void tst_qquickwindow::touchEvent_velocity() delete item; } +void tst_qquickwindow::mergeTouchPointLists_data() +{ + QTest::addColumn<QVector<QQuickItem*>>("list1"); + QTest::addColumn<QVector<QQuickItem*>>("list2"); + QTest::addColumn<QVector<QQuickItem*>>("expected"); + QTest::addColumn<bool>("showItem"); + + // FIXME: do not leak all these items + auto item1 = new QQuickItem(); + auto item2 = new QQuickItem(); + auto item3 = new QQuickItem(); + auto item4 = new QQuickItem(); + auto item5 = new QQuickItem(); + + QTest::newRow("empty") << QVector<QQuickItem*>() << QVector<QQuickItem*>() << QVector<QQuickItem*>(); + QTest::newRow("single list left") + << (QVector<QQuickItem*>() << item1 << item2 << item3) + << QVector<QQuickItem*>() + << (QVector<QQuickItem*>() << item1 << item2 << item3); + QTest::newRow("single list right") + << QVector<QQuickItem*>() + << (QVector<QQuickItem*>() << item1 << item2 << item3) + << (QVector<QQuickItem*>() << item1 << item2 << item3); + QTest::newRow("two lists identical") + << (QVector<QQuickItem*>() << item1 << item2 << item3) + << (QVector<QQuickItem*>() << item1 << item2 << item3) + << (QVector<QQuickItem*>() << item1 << item2 << item3); + QTest::newRow("two lists 1") + << (QVector<QQuickItem*>() << item1 << item2 << item5) + << (QVector<QQuickItem*>() << item3 << item4 << item5) + << (QVector<QQuickItem*>() << item1 << item2 << item3 << item4 << item5); + QTest::newRow("two lists 2") + << (QVector<QQuickItem*>() << item1 << item2 << item5) + << (QVector<QQuickItem*>() << item3 << item4 << item5) + << (QVector<QQuickItem*>() << item1 << item2 << item3 << item4 << item5); + QTest::newRow("two lists 3") + << (QVector<QQuickItem*>() << item1 << item2 << item3) + << (QVector<QQuickItem*>() << item1 << item4 << item5) + << (QVector<QQuickItem*>() << item1 << item2 << item3 << item4 << item5); + QTest::newRow("two lists 4") + << (QVector<QQuickItem*>() << item1 << item3 << item4) + << (QVector<QQuickItem*>() << item2 << item3 << item5) + << (QVector<QQuickItem*>() << item1 << item2 << item3 << item4 << item5); + QTest::newRow("two lists 5") + << (QVector<QQuickItem*>() << item1 << item2 << item4) + << (QVector<QQuickItem*>() << item1 << item3 << item4) + << (QVector<QQuickItem*>() << item1 << item2 << item3 << item4); +} + +void tst_qquickwindow::mergeTouchPointLists() +{ + QFETCH(QVector<QQuickItem*>, list1); + QFETCH(QVector<QQuickItem*>, list2); + QFETCH(QVector<QQuickItem*>, expected); + + QQuickWindow win; + auto windowPrivate = QQuickWindowPrivate::get(&win); + auto targetList = windowPrivate->mergePointerTargets(list1, list2); + QCOMPARE(targetList, expected); +} + void tst_qquickwindow::mouseFromTouch_basic() { // Turn off accepting touch events with acceptTouchEvents. This @@ -2402,6 +2483,58 @@ void tst_qquickwindow::testHoverTimestamp() QCOMPARE(hoverConsumer->hoverTimestamps.last(), 5UL); } +void tst_qquickwindow::pointerEventTypeAndPointCount() +{ + QPointF localPosition(33, 66); + QPointF scenePosition(133, 166); + QPointF screenPosition(333, 366); + QMouseEvent me(QEvent::MouseButtonPress, localPosition, scenePosition, screenPosition, + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QTouchEvent te(QEvent::TouchBegin, touchDevice, Qt::NoModifier, Qt::TouchPointPressed, + QList<QTouchEvent::TouchPoint>() << QTouchEvent::TouchPoint(1)); + + + QQuickPointerMouseEvent pme; + pme.reset(&me); + QVERIFY(pme.isValid()); + QCOMPARE(pme.asMouseEvent(localPosition), &me); + QVERIFY(pme.asPointerMouseEvent()); + QVERIFY(!pme.asPointerTouchEvent()); + QVERIFY(!pme.asPointerTabletEvent()); +// QVERIFY(!pe->asTabletEvent()); // TODO + QCOMPARE(pme.pointCount(), 1); + QCOMPARE(pme.point(0)->scenePos(), scenePosition); + QCOMPARE(pme.asMouseEvent(localPosition)->localPos(), localPosition); + QCOMPARE(pme.asMouseEvent(localPosition)->screenPos(), screenPosition); + + QQuickPointerTouchEvent pte; + pte.reset(&te); + QVERIFY(pte.isValid()); + QCOMPARE(pte.asTouchEvent(), &te); + QVERIFY(!pte.asPointerMouseEvent()); + QVERIFY(pte.asPointerTouchEvent()); + QVERIFY(!pte.asPointerTabletEvent()); + QVERIFY(pte.asTouchEvent()); +// QVERIFY(!pte.asTabletEvent()); // TODO + QCOMPARE(pte.pointCount(), 1); + QCOMPARE(pte.touchPointById(1)->id(), 1); + QVERIFY(!pte.touchPointById(0)); + + te.setTouchPoints(QList<QTouchEvent::TouchPoint>() << QTouchEvent::TouchPoint(1) << QTouchEvent::TouchPoint(2)); + pte.reset(&te); + QCOMPARE(pte.pointCount(), 2); + QCOMPARE(pte.touchPointById(1)->id(), 1); + QCOMPARE(pte.touchPointById(2)->id(), 2); + QVERIFY(!pte.touchPointById(0)); + + te.setTouchPoints(QList<QTouchEvent::TouchPoint>() << QTouchEvent::TouchPoint(2)); + pte.reset(&te); + QCOMPARE(pte.pointCount(), 1); + QCOMPARE(pte.touchPointById(2)->id(), 2); + QVERIFY(!pte.touchPointById(1)); + QVERIFY(!pte.touchPointById(0)); +} + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp index 791bcb215a..f6d624d871 100644 --- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp +++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp @@ -44,6 +44,7 @@ #include <private/qsgcontext_p.h> #include <private/qsgrenderloop_p.h> +#include "../../shared/util.h" #include "../shared/visualtestutil.h" using namespace QQuickVisualTestUtil; @@ -92,7 +93,7 @@ private: QColor m_color; }; -class tst_SceneGraph : public QObject +class tst_SceneGraph : public QQmlDataTest { Q_OBJECT @@ -112,6 +113,7 @@ private slots: private: bool m_brokenMipmapSupport; + QQuickView *createView(const QString &file, QWindow *parent = 0, int x = -1, int y = -1, int w = -1, int h = -1); }; template <typename T> class ScopedList : public QList<T> { @@ -123,6 +125,8 @@ void tst_SceneGraph::initTestCase() { qmlRegisterType<PerPixelRect>("SceneGraphTest", 1, 0, "PerPixelRect"); + QQmlDataTest::initTestCase(); + QSGRenderLoop *loop = QSGRenderLoop::instance(); qDebug() << "RenderLoop: " << loop; @@ -162,26 +166,16 @@ void tst_SceneGraph::initTestCase() #endif } -QQuickView *createView(const QString &file, QWindow *parent = 0, int x = -1, int y = -1, int w = -1, int h = -1) +QQuickView *tst_SceneGraph::createView(const QString &file, QWindow *parent, int x, int y, int w, int h) { QQuickView *view = new QQuickView(parent); - view->setSource(QUrl::fromLocalFile("data/" + file)); + view->setSource(testFileUrl(file)); if (x >= 0 && y >= 0) view->setPosition(x, y); if (w >= 0 && h >= 0) view->resize(w, h); view->show(); return view; } -QImage showAndGrab(const QString &file, int w, int h) -{ - QQuickView view; - view.setSource(QUrl::fromLocalFile(QStringLiteral("data/") + file)); - if (w >= 0 && h >= 0) - view.resize(w, h); - view.create(); - return view.grabWindow(); -} - // Assumes the images are opaque white... bool containsSomethingOtherThanWhite(const QImage &image) { @@ -384,17 +378,17 @@ void tst_SceneGraph::render_data() QTest::addColumn<QList<Sample> >("finalStage"); QList<QString> files; - files << "data/render_DrawSets.qml" - << "data/render_Overlap.qml" - << "data/render_MovingOverlap.qml" - << "data/render_BreakOpacityBatch.qml" - << "data/render_OutOfFloatRange.qml" - << "data/render_StackingOrder.qml" - << "data/render_ImageFiltering.qml" - << "data/render_bug37422.qml" - << "data/render_OpacityThroughBatchRoot.qml"; + files << "render_DrawSets.qml" + << "render_Overlap.qml" + << "render_MovingOverlap.qml" + << "render_BreakOpacityBatch.qml" + << "render_OutOfFloatRange.qml" + << "render_StackingOrder.qml" + << "render_ImageFiltering.qml" + << "render_bug37422.qml" + << "render_OpacityThroughBatchRoot.qml"; if (!m_brokenMipmapSupport) - files << "data/render_Mipmap.qml"; + files << "render_Mipmap.qml"; QRegExp sampleCount("#samples: *(\\d+)"); // X:int Y:int R:float G:float B:float Error:float @@ -402,7 +396,7 @@ void tst_SceneGraph::render_data() QRegExp finalSamples("#final: *(\\d+) *(\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+)"); foreach (QString fileName, files) { - QFile file(fileName); + QFile file(testFile(fileName)); if (!file.open(QFile::ReadOnly)) { qFatal("render_data: QFile::open failed! file=%s, error=%s", qPrintable(fileName), qPrintable(file.errorString())); @@ -452,7 +446,7 @@ void tst_SceneGraph::render() QQuickView view; view.rootContext()->setContextProperty("suite", &suite); - view.setSource(QUrl::fromLocalFile(file)); + view.setSource(testFileUrl(file)); view.setResizeMode(QQuickView::SizeViewToRootObject); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); @@ -501,7 +495,7 @@ void tst_SceneGraph::hideWithOtherContext() { QQuickView view; - view.setSource(QUrl::fromLocalFile("data/simple.qml")); + view.setSource(testFileUrl("simple.qml")); view.setResizeMode(QQuickView::SizeViewToRootObject); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); diff --git a/tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml b/tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml new file mode 100644 index 0000000000..9f67c226a0 --- /dev/null +++ b/tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml @@ -0,0 +1,39 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Rectangle { + id: root + + width: 600 + height: 400 + + EventItem { + objectName: "background" + width: 600 + height: 400 + Rectangle { anchors.fill: parent; color: "lightsteelblue" } + + EventItem { + objectName: "left" + width: 300 + height: 300 + Rectangle { anchors.fill: parent; color: "yellow" } + } + + EventItem { + objectName: "right" + x: 300 + width: 300 + height: 300 + Rectangle { anchors.fill: parent; color: "green" } + } + + EventItem { + objectName: "middle" + x: 150 + width: 300 + height: 300 + Rectangle { anchors.fill: parent; color: "blue" } + } + } +} diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index dc70081f09..1ec24e35d5 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -33,12 +33,12 @@ #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquickevents_p_p.h> #include <QtQuick/private/qquickmousearea_p.h> #include <QtQuick/private/qquickmultipointtoucharea_p.h> #include <QtQuick/private/qquickpincharea_p.h> #include <QtQuick/private/qquickflickable_p.h> - -#include <private/qquickwindow_p.h> +#include <QtQuick/private/qquickwindow_p.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlproperty.h> @@ -65,15 +65,22 @@ struct Event class EventItem : public QQuickItem { Q_OBJECT + +Q_SIGNALS: + void onTouchEvent(QQuickItem *receiver); + public: EventItem(QQuickItem *parent = 0) : QQuickItem(parent), acceptMouse(false), acceptTouch(false), filterTouch(false) - {} + { + setAcceptedMouseButtons(Qt::LeftButton); + } void touchEvent(QTouchEvent *event) { eventList.append(Event(event->type(), event->touchPoints())); event->setAccepted(acceptTouch); + emit onTouchEvent(this); } void mousePressEvent(QMouseEvent *event) { @@ -154,6 +161,7 @@ private slots: void tapOnDismissiveTopMouseAreaClicksBottomOne(); void touchGrabCausesMouseUngrab(); + void touchPointDeliveryOrder(); void hoverEnabled(); @@ -211,15 +219,17 @@ void tst_TouchMouse::simpleTouchEvent() p1 = QPoint(20, 20); QTest::touchEvent(window, device).press(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 1); + // Get a touch and then mouse event offered + QCOMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); p1 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 1); + // Not accepted, no updates + QCOMPARE(eventItem1->eventList.size(), 2); QTest::touchEvent(window, device).release(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 1); + QCOMPARE(eventItem1->eventList.size(), 2); eventItem1->eventList.clear(); // Accept touch @@ -285,17 +295,16 @@ void tst_TouchMouse::simpleTouchEvent() p1 = QPoint(20, 20); QTest::touchEvent(window, device).press(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); - QCOMPARE(eventItem1->eventList.at(2).type, QEvent::UngrabMouse); p1 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 2); QTest::touchEvent(window, device).release(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 2); eventItem1->eventList.clear(); // wait to avoid getting a double click event @@ -572,7 +581,8 @@ void tst_TouchMouse::buttonOnFlickable() QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); QCOMPARE(windowPriv->touchMouseId, 0); - QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1); + auto pointerEvent = QQuickPointerDevice::touchDevices().at(0)->pointerEvent(); + QCOMPARE(pointerEvent->point(0)->grabber(), eventItem1); QCOMPARE(window->mouseGrabberItem(), eventItem1); p1 += QPoint(0, -10); @@ -593,7 +603,7 @@ void tst_TouchMouse::buttonOnFlickable() QCOMPARE(window->mouseGrabberItem(), flickable); QCOMPARE(windowPriv->touchMouseId, 0); - QCOMPARE(windowPriv->itemForTouchPointId[0], flickable); + QCOMPARE(pointerEvent->point(0)->grabber(), flickable); QVERIFY(flickable->isMovingVertically()); QTest::touchEvent(window, device).release(0, p3, window); @@ -653,11 +663,12 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress); QCOMPARE(filteredEventList.count(), 1); - // eventItem1 should have the mouse grab, and have moved the itemForTouchPointId + // eventItem1 should have the mouse grab, and have moved the grab // for the touchMouseId to the new grabber. QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); QCOMPARE(windowPriv->touchMouseId, 0); - QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1); + auto pointerEvent = QQuickPointerDevice::touchDevices().at(0)->pointerEvent(); + QCOMPARE(pointerEvent->point(0)->grabber(), eventItem1); QCOMPARE(window->mouseGrabberItem(), eventItem1); p1 += QPoint(0, -10); @@ -676,7 +687,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() // for the touchMouseId to the new grabber. QCOMPARE(window->mouseGrabberItem(), flickable); QCOMPARE(windowPriv->touchMouseId, 0); - QCOMPARE(windowPriv->itemForTouchPointId[0], flickable); + QCOMPARE(pointerEvent->point(0)->grabber(), flickable); QTest::touchEvent(window, device).release(0, p3, window); QQuickTouchUtils::flush(window); @@ -1090,8 +1101,6 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() pinchSequence.move(0, p, window).commit(); QQuickTouchUtils::flush(window); - QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); - qDebug() << "Mouse Grabber: " << window->mouseGrabberItem() << " itemForTouchPointId: " << windowPriv->itemForTouchPointId; QCOMPARE(window->mouseGrabberItem(), flickable); // Add a second finger, this should lead to stealing @@ -1222,6 +1231,105 @@ void tst_TouchMouse::touchGrabCausesMouseUngrab() delete window; } +void tst_TouchMouse::touchPointDeliveryOrder() +{ + // Touch points should be first delivered to the item under the primary finger + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("touchpointdeliveryorder.qml")); + /* + The items are positioned from left to right: + | background | + | left | + | | right | + | middle | + 0 150 300 450 600 + */ + QPoint pLeft = QPoint(100, 100); + QPoint pRight = QPoint(500, 100); + QPoint pLeftMiddle = QPoint(200, 100); + QPoint pRightMiddle = QPoint(350, 100); + + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QVector<QQuickItem*> events; + EventItem *background = window->rootObject()->findChild<EventItem*>("background"); + EventItem *left = window->rootObject()->findChild<EventItem*>("left"); + EventItem *middle = window->rootObject()->findChild<EventItem*>("middle"); + EventItem *right = window->rootObject()->findChild<EventItem*>("right"); + QVERIFY(background); + QVERIFY(left); + QVERIFY(middle); + QVERIFY(right); + connect(background, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); }); + connect(left, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); }); + connect(middle, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); }); + connect(right, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); }); + + QTest::touchEvent(window.data(), device).press(0, pLeft, window.data()); + QQuickTouchUtils::flush(window.data()); + + // Touch on left, then background + QCOMPARE(events.size(), 2); + QCOMPARE(events.at(0), left); + QCOMPARE(events.at(1), background); + events.clear(); + + // New press events are deliverd first, the stationary point was not accepted, thus it doesn't get delivered + QTest::touchEvent(window.data(), device).stationary(0).press(1, pRightMiddle, window.data()); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(events.size(), 3); + QCOMPARE(events.at(0), middle); + QCOMPARE(events.at(1), right); + QCOMPARE(events.at(2), background); + events.clear(); + + QTest::touchEvent(window.data(), device).release(0, pLeft, window.data()).release(1, pRightMiddle, window.data()); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(events.size(), 0); // no accepted events + + // Two presses, the first point should come first + QTest::touchEvent(window.data(), device).press(0, pLeft, window.data()).press(1, pRight, window.data()); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(events.size(), 3); + QCOMPARE(events.at(0), left); + QCOMPARE(events.at(1), right); + QCOMPARE(events.at(2), background); + QTest::touchEvent(window.data(), device).release(0, pLeft, window.data()).release(1, pRight, window.data()); + events.clear(); + + // Again, pressing right first + QTest::touchEvent(window.data(), device).press(0, pRight, window.data()).press(1, pLeft, window.data()); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(events.size(), 3); + QCOMPARE(events.at(0), right); + QCOMPARE(events.at(1), left); + QCOMPARE(events.at(2), background); + QTest::touchEvent(window.data(), device).release(0, pRight, window.data()).release(1, pLeft, window.data()); + events.clear(); + + // Two presses, both hitting the middle item on top, then branching left and right, then bottom + // Each target should be offered the events exactly once, middle first, left must come before right (id 0) + QTest::touchEvent(window.data(), device).press(0, pLeftMiddle, window.data()).press(1, pRightMiddle, window.data()); + QCOMPARE(events.size(), 4); + QCOMPARE(events.at(0), middle); + QCOMPARE(events.at(1), left); + QCOMPARE(events.at(2), right); + QCOMPARE(events.at(3), background); + QTest::touchEvent(window.data(), device).release(0, pLeftMiddle, window.data()).release(1, pRightMiddle, window.data()); + events.clear(); + + QTest::touchEvent(window.data(), device).press(0, pRightMiddle, window.data()).press(1, pLeftMiddle, window.data()); + qDebug() << events; + QCOMPARE(events.size(), 4); + QCOMPARE(events.at(0), middle); + QCOMPARE(events.at(1), right); + QCOMPARE(events.at(2), left); + QCOMPARE(events.at(3), background); + QTest::touchEvent(window.data(), device).release(0, pRightMiddle, window.data()).release(1, pLeftMiddle, window.data()); +} + void tst_TouchMouse::hoverEnabled() { // QTouchDevice *device = new QTouchDevice; diff --git a/tools/fdegen/fdegen.pro b/tools/fdegen/fdegen.pro deleted file mode 100644 index a52533280e..0000000000 --- a/tools/fdegen/fdegen.pro +++ /dev/null @@ -1,8 +0,0 @@ -TEMPLATE = app -TARGET = fdegen -INCLUDEPATH += . - -# Input -SOURCES += main.cpp - -LIBS += -ldwarf -lelf diff --git a/tools/fdegen/main.cpp b/tools/fdegen/main.cpp deleted file mode 100644 index 53ee9dec2a..0000000000 --- a/tools/fdegen/main.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <libdwarf.h> -#include <dwarf.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> - -#define DEBUG - -#ifdef DEBUG -#include <libelf.h> -#endif - -#include <qglobal.h> - -enum DwarfRegs { -#if defined(Q_PROCESSOR_X86_64) - // X86-64 - RAX = 0, - RDX = 1, - RCX = 2, - RBX = 3, - RSI = 4, - RDI = 5, - RBP = 6, - RSP = 7, - R8 = 8, - R9 = 9, - R10 = 10, - R11 = 11, - R12 = 12, - R13 = 13, - R14 = 14, - R15 = 15, - RIP = 16, - - InstructionPointerRegister = RIP, - StackPointerRegister = RSP, - StackFrameRegister = RBP -#elif defined(Q_PROCESSOR_X86) - // x86 - EAX = 0, - EDX = 1, - ECX = 2, - EBX = 3, - ESP = 4, - EBP = 5, - ESI = 6, - EDI = 7, - EIP = 8, - - InstructionPointerRegister = EIP, - StackPointerRegister = ESP, - StackFrameRegister = EBP -#else -#error Not ported yet -#endif -}; - -static const DwarfRegs calleeSavedRegisters[] = { -#if defined(Q_PROCESSOR_X86_64) - R12, - R14 -#elif defined(Q_PROCESSOR_X86) - ESI, - EDI -#endif -}; -static const int calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); - -#if QT_POINTER_SIZE == 8 -#define Elf_Ehdr Elf64_Ehdr -#define elf_newehdr elf64_newehdr -#define Elf_Shdr Elf64_Shdr -#define elf_getshdr elf64_getshdr -#else -#define Elf_Ehdr Elf32_Ehdr -#define elf_newehdr elf32_newehdr -#define Elf_Shdr Elf32_Shdr -#define elf_getshdr elf32_getshdr -#endif - -static void die(const char *msg) -{ - fprintf(stderr, "error: %s\n", msg); - exit(1); -} - -static int createSectionCallback( - char *name, - int size, - Dwarf_Unsigned /*type*/, - Dwarf_Unsigned /*flags*/, - Dwarf_Unsigned /*link*/, - Dwarf_Unsigned /*info*/, - Dwarf_Unsigned* /*sect_name_index*/, - void * /*user_data*/, - int* /*error*/) -{ - if (strcmp(name, ".debug_frame")) - return 0; - fprintf(stderr, "createsection called with %s and size %d\n", name, size); - return 1; -} - -static unsigned char cie_init_instructions[] = { - DW_CFA_def_cfa, StackPointerRegister, /*offset in bytes */sizeof(void*), - DW_CFA_offset | InstructionPointerRegister, 1, -}; - -int main() -{ - Dwarf_Error error = 0; - Dwarf_P_Debug dw = dwarf_producer_init_c(DW_DLC_WRITE | DW_DLC_SIZE_64, - createSectionCallback, - /* error handler */0, - /* error arg */0, - /* user data */0, - &error); - if (error != 0) - die("dwarf_producer_init_c failed"); - - Dwarf_Unsigned cie = dwarf_add_frame_cie(dw, - "", - /* code alignment factor */sizeof(void*), - /* data alignment factor */-sizeof(void*), - /* return address reg*/InstructionPointerRegister, - cie_init_instructions, - sizeof(cie_init_instructions), - &error); - if (error != 0) - die("dwarf_add_frame_cie failed"); - - Dwarf_P_Fde fde = dwarf_new_fde(dw, &error); - if (error != 0) - die("dwarf_new_fde failed"); - - /* New entry in state machine for code offset 1 after push rbp instruction */ - dwarf_add_fde_inst(fde, - DW_CFA_advance_loc, - /*offset in code alignment units*/ 1, - /* unused*/ 0, - &error); - - /* After "push rbp" the offset to the CFA is now 2 instead of 1 */ - dwarf_add_fde_inst(fde, - DW_CFA_def_cfa_offset_sf, - /*offset in code alignment units*/ -2, - /* unused*/ 0, - &error); - - /* After "push rbp" the value of rbp is now stored at offset 1 from CFA */ - dwarf_add_fde_inst(fde, - DW_CFA_offset, - StackFrameRegister, - 2, - &error); - - /* New entry in state machine for code offset 3 for mov rbp, rsp instruction */ - dwarf_add_fde_inst(fde, - DW_CFA_advance_loc, - /*offset in code alignment units*/ 3, - /* unused */ 0, - &error); - - /* After "mov rbp, rsp" the cfa is reachable via rbp */ - dwarf_add_fde_inst(fde, - DW_CFA_def_cfa_register, - StackFrameRegister, - /* unused */0, - &error); - - /* Callee saved registers */ - for (int i = 0; i < calleeSavedRegisterCount; ++i) { - dwarf_add_fde_inst(fde, - DW_CFA_offset, - calleeSavedRegisters[i], - i + 3, - &error); - } - - dwarf_add_frame_fde(dw, fde, - /* die */0, - cie, - /*virt addr */0, - /* length of code */0, - /* symbol index */0, - &error); - if (error != 0) - die("dwarf_add_frame_fde failed"); - - dwarf_transform_to_disk_form(dw, &error); - if (error != 0) - die("dwarf_transform_to_disk_form failed"); - - Dwarf_Unsigned len = 0; - Dwarf_Signed elfIdx = 0; - unsigned char *bytes = (unsigned char *)dwarf_get_section_bytes(dw, /* section */1, - &elfIdx, &len, &error); - if (error != 0) - die("dwarf_get_section_bytes failed"); - - // libdwarf doesn't add a terminating FDE with zero length, so let's add one - // ourselves. - unsigned char *newBytes = (unsigned char *)alloca(len + 4); - memcpy(newBytes, bytes, len); - newBytes[len] = 0; - newBytes[len + 1] = 0; - newBytes[len + 2] = 0; - newBytes[len + 3] = 0; - newBytes[len + 4] = 0; - bytes = newBytes; - len += 4; - - // Reset CIE-ID back to 0 as expected for .eh_frames - bytes[4] = 0; - bytes[5] = 0; - bytes[6] = 0; - bytes[7] = 0; - - unsigned fde_offset = bytes[0] + 4; - - bytes[fde_offset + 4] = fde_offset + 4; - - printf("static const unsigned char cie_fde_data[] = {\n"); - int i = 0; - while (i < len) { - printf(" "); - for (int j = 0; i < len && j < 8; ++j, ++i) - printf("0x%x, ", bytes[i]); - printf("\n"); - } - printf("};\n"); - - printf("static const int fde_offset = %d;\n", fde_offset); - printf("static const int initial_location_offset = %d;\n", fde_offset + 8); - printf("static const int address_range_offset = %d;\n", fde_offset + 8 + sizeof(void*)); - -#ifdef DEBUG - { - if (elf_version(EV_CURRENT) == EV_NONE) - die("wrong elf version"); - int fd = open("debug.o", O_WRONLY | O_CREAT, 0777); - if (fd < 0) - die("cannot create debug.o"); - - Elf *e = elf_begin(fd, ELF_C_WRITE, 0); - if (!e) - die("elf_begin failed"); - - Elf_Ehdr *ehdr = elf_newehdr(e); - if (!ehdr) - die(elf_errmsg(-1)); - - ehdr->e_ident[EI_DATA] = ELFDATA2LSB; -#if defined(Q_PROCESSOR_X86_64) - ehdr->e_machine = EM_X86_64; -#elif defined(Q_PROCESSOR_X86) - ehdr->e_machine = EM_386; -#else -#error port me :) -#endif - ehdr->e_type = ET_EXEC; - ehdr->e_version = EV_CURRENT; - - Elf_Scn *section = elf_newscn(e); - if (!section) - die("elf_newscn failed"); - - Elf_Data *data = elf_newdata(section); - if (!data) - die(elf_errmsg(-1)); - data->d_align = 4; - data->d_off = 0; - data->d_buf = bytes; - data->d_size = len; - data->d_type = ELF_T_BYTE; - data->d_version = EV_CURRENT; - - Elf_Shdr *shdr = elf_getshdr(section); - if (!shdr) - die(elf_errmsg(-1)); - - shdr->sh_name = 1; - shdr->sh_type = SHT_PROGBITS; - shdr->sh_entsize = 0; - - char stringTable[] = { - 0, - '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0, - '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0 - }; - - section = elf_newscn(e); - if (!section) - die("elf_newscn failed"); - - data = elf_newdata(section); - if (!data) - die(elf_errmsg(-1)); - data->d_align = 1; - data->d_off = 0; - data->d_buf = stringTable; - data->d_size = sizeof(stringTable); - data->d_type = ELF_T_BYTE; - data->d_version = EV_CURRENT; - - shdr = elf_getshdr(section); - if (!shdr) - die(elf_errmsg(-1)); - - shdr->sh_name = 11; - shdr->sh_type = SHT_STRTAB; - shdr->sh_flags = SHF_STRINGS | SHF_ALLOC; - shdr->sh_entsize = 0; - - ehdr->e_shstrndx = elf_ndxscn(section); - - if (elf_update(e, ELF_C_WRITE) < 0) - die(elf_errmsg(-1)); - - elf_end(e); - close(fd); - } -#endif - - dwarf_producer_finish(dw, &error); - if (error != 0) - die("dwarf_producer_finish failed"); - return 0; -} diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp index a795144984..d718067616 100644 --- a/tools/qml/main.cpp +++ b/tools/qml/main.cpp @@ -160,18 +160,23 @@ public: LoadWatcher(QQmlApplicationEngine *e, int expected) : QObject(e) , earlyExit(false) + , returnCode(0) , expect(expected) , haveOne(false) { connect(e, SIGNAL(objectCreated(QObject*,QUrl)), this, SLOT(checkFinished(QObject*))); // QQmlApplicationEngine also connects quit() to QCoreApplication::quit - // but if called before exec() then QCoreApplication::quit does nothing + // and exit() to QCoreApplication::exit but if called before exec() + // then QCoreApplication::quit or QCoreApplication::exit does nothing connect(e, SIGNAL(quit()), this, SLOT(quit())); + connect(e, &QQmlEngine::exit, + this, &LoadWatcher::exit); } bool earlyExit; + int returnCode; private: void contain(QObject *o, const QUrl &containPath); @@ -196,14 +201,20 @@ public Q_SLOTS: if (! --expect) { printf("qml: Did not load any objects, exiting.\n"); - exit(2);//Different return code from qFatal + std::exit(2);//Different return code from qFatal } } void quit() { //Will be checked before calling exec() earlyExit = true; + returnCode = 0; } + void exit(int retCode) { + earlyExit = true; + returnCode = retCode; + } + #if defined(QT_GUI_LIB) && !defined(QT_NO_OPENGL) void onOpenGlContextCreated(QOpenGLContext *context); #endif @@ -582,7 +593,7 @@ int main(int argc, char *argv[]) } if (lw->earlyExit) - return 0; + return lw->returnCode; return app->exec(); } diff --git a/tools/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp index 78ed9606db..ee55931a46 100644 --- a/tools/qmleasing/splineeditor.cpp +++ b/tools/qmleasing/splineeditor.cpp @@ -34,6 +34,7 @@ #include <QContextMenuEvent> #include <QDebug> #include <QApplication> +#include <QVector> const int canvasWidth = 640; const int canvasHeight = 320; @@ -672,25 +673,23 @@ void SplineEditor::setEasingCurve(const QString &code) if (m_block) return; if (code.startsWith(QLatin1Char('[')) && code.endsWith(QLatin1Char(']'))) { - QString cleanCode = code; - cleanCode.remove(0, 1); - cleanCode.chop(1); - const QStringList stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts); + const QStringRef cleanCode(&code, 1, code.size() - 2); + const auto stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts); if (stringList.count() >= 6 && (stringList.count() % 6 == 0)) { - QList<qreal> realList; + QVector<qreal> realList; realList.reserve(stringList.count()); - foreach (const QString &string, stringList) { + for (const QStringRef &string : stringList) { bool ok; realList.append(string.toDouble(&ok)); if (!ok) return; } - QList<QPointF> points; + QVector<QPointF> points; const int count = realList.count() / 2; points.reserve(count); for (int i = 0; i < count; ++i) points.append(QPointF(realList.at(i * 2), realList.at(i * 2 + 1))); - if (points.last() == QPointF(1.0, 1.0)) { + if (points.constLast() == QPointF(1.0, 1.0)) { QEasingCurve easingCurve(QEasingCurve::BezierSpline); for (int i = 0; i < points.count() / 3; ++i) { diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 2569d78c63..2371057878 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -224,11 +224,11 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) if (plugininfo.contains(QStringLiteral("dependencies"))) { QStringList dependencies = plugininfo.value(QStringLiteral("dependencies")).toStringList(); foreach (const QString &line, dependencies) { - QList<QString> dep = line.split(QLatin1Char(' ')); + const auto dep = line.splitRef(QLatin1Char(' ')); QVariantMap depImport; depImport[QStringLiteral("type")] = QStringLiteral("module"); - depImport[QStringLiteral("name")] = dep[0]; - depImport[QStringLiteral("version")] = dep[1]; + depImport[QStringLiteral("name")] = dep[0].toString(); + depImport[QStringLiteral("version")] = dep[1].toString(); importsCopy.append(depImport); } } diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp index 063e5e2961..45c32487a2 100644 --- a/tools/qmlprofiler/qmlprofilerapplication.cpp +++ b/tools/qmlprofiler/qmlprofilerapplication.cpp @@ -347,7 +347,7 @@ bool QmlProfilerApplication::checkOutputFile(PendingRequest pending) void QmlProfilerApplication::userCommand(const QString &command) { - QStringList args = command.split(QChar::Space, QString::SkipEmptyParts); + auto args = command.splitRef(QChar::Space, QString::SkipEmptyParts); if (args.isEmpty()) { prompt(); return; @@ -401,7 +401,7 @@ void QmlProfilerApplication::userCommand(const QString &command) } else if (m_profilerData.isEmpty()) { prompt(tr("No data was recorded so far.")); } else { - m_interactiveOutputFile = args.length() > 0 ? args[0] : m_outputFile; + m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile; if (checkOutputFile(REQUEST_OUTPUT_FILE)) output(); } @@ -418,7 +418,7 @@ void QmlProfilerApplication::userCommand(const QString &command) if (!m_recording && m_profilerData.isEmpty()) { prompt(tr("No data was recorded so far.")); } else { - m_interactiveOutputFile = args.length() > 0 ? args[0] : m_outputFile; + m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile; if (checkOutputFile(REQUEST_FLUSH_FILE)) flush(); } diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp index 74fa44c1d6..b651b2724f 100644 --- a/tools/qmlprofiler/qmlprofilerdata.cpp +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -248,7 +248,7 @@ void QmlProfilerData::addQmlEvent(QQmlProfilerDefinitions::RangeType type, eventHashStr = getHashStringForQmlEvent(eventLocation, type); } else { const QString filePath = QUrl(eventLocation.filename).path(); - displayName = filePath.mid( + displayName = filePath.midRef( filePath.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') + QString::number(eventLocation.line); eventHashStr = getHashStringForQmlEvent(eventLocation, type); @@ -327,8 +327,8 @@ void QmlProfilerData::addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventTy QString filePath = QUrl(location).path(); - QString eventHashStr = filePath.mid(filePath.lastIndexOf(QLatin1Char('/')) + 1) + - QStringLiteral(":") + QString::number(type); + const QString eventHashStr = filePath.midRef(filePath.lastIndexOf(QLatin1Char('/')) + 1) + + QLatin1Char(':') + QString::number(type); QmlRangeEventData *newEvent; if (d->eventDescriptions.contains(eventHashStr)) { newEvent = d->eventDescriptions[eventHashStr]; diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp index 1185a8e7ae..0e542ab0c8 100644 --- a/tools/qmlscene/main.cpp +++ b/tools/qmlscene/main.cpp @@ -564,6 +564,7 @@ int main(int argc, char ** argv) loadDummyDataFiles(engine, fi.path()); } QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); + QObject::connect(&engine, &QQmlEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit); component->loadUrl(options.url); while (component->isLoading()) QCoreApplication::processEvents(); |