diff options
| author | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2025-08-22 09:33:17 +0300 |
|---|---|---|
| committer | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2025-08-22 09:33:17 +0300 |
| commit | c6fdadd916a7568c1d71b750e054ca9aa2fd5dfc (patch) | |
| tree | bbd5690b38a5e0df66dd070a9e9f42b0d1b2b673 /src | |
| parent | a7c766a9863605eb81e8f0cdb4d2b93e087b5bde (diff) | |
| parent | e436dad999060b92965291b45c0e95a3b93f5866 (diff) | |
Merge tag 'v6.2.13-lts' into tqtc/lts-6.2-opensourcev6.2.13-lts-lgpl6.2
Qt 6.2.13-lts release
Conflicts solved:
dependencies.yaml
Change-Id: I3cbe1ce4293179888e236dd1a3a299cd2c66c950
Diffstat (limited to 'src')
38 files changed, 732 insertions, 151 deletions
diff --git a/src/imports/tooling/Enum.qml b/src/imports/tooling/Enum.qml index cd5904b6b6..0d0e90d621 100644 --- a/src/imports/tooling/Enum.qml +++ b/src/imports/tooling/Enum.qml @@ -42,5 +42,6 @@ import QML Member { property string alias property bool isFlag: false + property bool isScoped: false property var values: [] } diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp index 5744c61849..0bb71d1d70 100644 --- a/src/particles/qquickparticlesystem.cpp +++ b/src/particles/qquickparticlesystem.cpp @@ -240,8 +240,7 @@ void QQuickParticleDataHeap::insertTimed(QQuickParticleData* data, int time) int QQuickParticleDataHeap::top() { - if (m_end == 0) - return 1 << 30; + Q_ASSERT(!isEmpty()); return m_data[0].time; } @@ -391,7 +390,7 @@ bool QQuickParticleGroupData::recycle() { m_latestAliveParticles.clear(); - while (dataHeap.top() <= m_system->timeInt) { + while (!dataHeap.isEmpty() && dataHeap.top() <= m_system->timeInt) { for (QQuickParticleData *datum : dataHeap.pop()) { if (!datum->stillAlive(m_system)) { freeList.free(datum->index); diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h index 0ae2a5f345..ab5cc9cd7f 100644 --- a/src/particles/qquickparticlesystem_p.h +++ b/src/particles/qquickparticlesystem_p.h @@ -117,6 +117,8 @@ public: int top(); + bool isEmpty() const { return m_end == 0; } + QSet<QQuickParticleData*> pop(); void clear(); diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 1452027dcb..3d7fc92a30 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -1334,14 +1334,14 @@ struct TypeReferenceMap : QHash<int, TypeReference> if (!formal->type.indexIsBuiltinType()) { TypeReference &r = this->add(formal->type.typeNameIndexOrBuiltinType(), it->location); - r.errorWhenNotFound = true; + r.errorWhenNotFound = false; } } if (!it->returnType.indexIsBuiltinType()) { TypeReference &r = this->add(it->returnType.typeNameIndexOrBuiltinType(), it->location); - r.errorWhenNotFound = true; + r.errorWhenNotFound = false; } } } diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index b303dd497a..964bb78d3a 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -2497,16 +2497,15 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi return true; } - if (metaType == QMetaType::fromType<QQmlListReference>()) { - if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) { + if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) { + if (metaType == QMetaType::fromType<QQmlListReference>()) { *reinterpret_cast<QQmlListReference *>(data) = wrapper->toListReference(); return true; } - } - if (metaType == QMetaType::fromType<QQmlListProperty<QObject>>()) { - if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) { - *reinterpret_cast<QQmlListProperty<QObject> *>(data) = wrapper->d()->property(); + const auto wrapperPrivate = wrapper->d(); + if (QMetaType(wrapperPrivate->propertyType) == metaType) { + *reinterpret_cast<QQmlListProperty<QObject> *>(data) = wrapperPrivate->property(); return true; } } diff --git a/src/qml/jsruntime/qv4estable.cpp b/src/qml/jsruntime/qv4estable.cpp index 99f6bf6aa0..2e504a70cb 100644 --- a/src/qml/jsruntime/qv4estable.cpp +++ b/src/qml/jsruntime/qv4estable.cpp @@ -147,21 +147,18 @@ ReturnedValue ESTable::get(const Value &key, bool *hasValue) const // Removes the given \a key from the table bool ESTable::remove(const Value &key) { - bool found = false; - uint idx = 0; - for (; idx < m_size; ++idx) { - if (m_keys[idx].sameValueZero(key)) { - found = true; - break; + for (uint index = 0; index < m_size; ++index) { + if (m_keys[index].sameValueZero(key)) { + // Remove the element at |index| by moving all elements to the right + // of |index| one place to the left. + size_t count = (m_size - (index + 1)) * sizeof(Value); + memmove(m_keys + index, m_keys + index + 1, count); + memmove(m_values + index, m_values + index + 1, count); + m_size--; + return true; } } - - if (found == true) { - memmove(m_keys + idx, m_keys + idx + 1, (m_size - idx)*sizeof(Value)); - memmove(m_values + idx, m_values + idx + 1, (m_size - idx)*sizeof(Value)); - m_size--; - } - return found; + return false; } // Returns the size of the table. Note that the size may not match the underlying allocation. diff --git a/src/qml/jsruntime/qv4estable_p.h b/src/qml/jsruntime/qv4estable_p.h index f54fc37a7b..e63dda7a4d 100644 --- a/src/qml/jsruntime/qv4estable_p.h +++ b/src/qml/jsruntime/qv4estable_p.h @@ -53,12 +53,13 @@ #include "qv4value_p.h" +class tst_qv4estable; + QT_BEGIN_NAMESPACE -namespace QV4 -{ +namespace QV4 { -class ESTable +class Q_AUTOTEST_EXPORT ESTable { public: ESTable(); @@ -76,13 +77,15 @@ public: void removeUnmarkedKeys(); private: + friend class ::tst_qv4estable; + Value *m_keys = nullptr; Value *m_values = nullptr; uint m_size = 0; uint m_capacity = 0; }; -} +} // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h index 248fcdb02f..daa21baf06 100644 --- a/src/qml/jsruntime/qv4sparsearray_p.h +++ b/src/qml/jsruntime/qv4sparsearray_p.h @@ -146,8 +146,8 @@ struct Q_QML_EXPORT SparseArray { SparseArray(); ~SparseArray() { - if (root()) - freeTree(header.left, alignof(SparseArrayNode)); + if (SparseArrayNode *n = root()) + freeTree(n, alignof(SparseArrayNode)); } SparseArray(const SparseArray &other); @@ -323,37 +323,45 @@ inline QList<int> SparseArray::keys() const inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const { - const SparseArrayNode *lb = root()->lowerBound(akey); - if (!lb) - lb = end(); - return lb; + if (SparseArrayNode *n = root()) { + if (const SparseArrayNode *lb = n->lowerBound(akey)) + return lb; + } + + return end(); } inline SparseArrayNode *SparseArray::lowerBound(uint akey) { - SparseArrayNode *lb = root()->lowerBound(akey); - if (!lb) - lb = end(); - return lb; + if (SparseArrayNode *n = root()) { + if (SparseArrayNode *lb = n->lowerBound(akey)) + return lb; + } + + return end(); } inline const SparseArrayNode *SparseArray::upperBound(uint akey) const { - const SparseArrayNode *ub = root()->upperBound(akey); - if (!ub) - ub = end(); - return ub; + if (SparseArrayNode *n = root()) { + if (const SparseArrayNode *ub = n->upperBound(akey)) + return ub; + } + + return end(); } inline SparseArrayNode *SparseArray::upperBound(uint akey) { - SparseArrayNode *ub = root()->upperBound(akey); - if (!ub) - ub = end(); - return ub; + if (SparseArrayNode *n = root()) { + if (SparseArrayNode *ub = n->upperBound(akey)) + return ub; + } + + return end(); } } diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 5fb14332ab..8989efe701 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -251,7 +251,7 @@ struct QV4QPointer { private: QtSharedPointer::ExternalRefCountData *d; - QObject *qObject; + T *qObject; }; Q_STATIC_ASSERT(std::is_trivial< QV4QPointer<QObject> >::value); #endif diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index 127fdf7ebb..ec596c0506 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -697,7 +697,13 @@ void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>( QQmlEngine *AOTCompiledContext::qmlEngine() const { - return qmlContext ? qmlContext->engine() : nullptr; + return engine->handle()->qmlEngine(); +} + +static QQmlPropertyCapture *propertyCapture(const AOTCompiledContext *aotContext) +{ + QQmlEngine *engine = aotContext->qmlEngine(); + return engine ? QQmlEnginePrivate::get(aotContext->qmlEngine())->propertyCapture : nullptr; } QJSValue AOTCompiledContext::jsMetaType(int index) const @@ -722,31 +728,23 @@ void AOTCompiledContext::setReturnValueUndefined() const static void captureFallbackProperty( QObject *object, int coreIndex, int notifyIndex, bool isConstant, - QQmlContextData *qmlContext) + const AOTCompiledContext *aotContext) { - if (!qmlContext || isConstant) + if (isConstant) return; - QQmlEngine *engine = qmlContext->engine(); - Q_ASSERT(engine); - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); - Q_ASSERT(ep); - if (QQmlPropertyCapture *capture = ep->propertyCapture) + if (QQmlPropertyCapture *capture = propertyCapture(aotContext)) capture->captureProperty(object, coreIndex, notifyIndex); } static void captureObjectProperty( QObject *object, const QQmlPropertyCache *propertyCache, - const QQmlPropertyData *property, QQmlContextData *qmlContext) + const QQmlPropertyData *property, const AOTCompiledContext *aotContext) { - if (!qmlContext || property->isConstant()) + if (property->isConstant()) return; - QQmlEngine *engine = qmlContext->engine(); - Q_ASSERT(engine); - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); - Q_ASSERT(ep); - if (QQmlPropertyCapture *capture = ep->propertyCapture) + if (QQmlPropertyCapture *capture = propertyCapture(aotContext)) capture->captureProperty(object, propertyCache, property); } @@ -762,7 +760,7 @@ static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCach enum class ObjectPropertyResult { OK, NeedsInit, Deleted }; static ObjectPropertyResult loadObjectProperty( - QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext) + QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext) { QQmlData *qmlData = QQmlData::get(object); if (!qmlData) @@ -779,13 +777,13 @@ static ObjectPropertyResult loadObjectProperty( if (qmlData->hasPendingBindingBit(coreIndex)) qmlData->flushPendingBinding(coreIndex); - captureObjectProperty(object, propertyCache, property, qmlContext); + captureObjectProperty(object, propertyCache, property, aotContext); property->readProperty(object, target); return ObjectPropertyResult::OK; } static ObjectPropertyResult loadFallbackProperty( - QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext) + QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext) { QQmlData *qmlData = QQmlData::get(object); if (qmlData && qmlData->isQueuedForDeletion) @@ -803,7 +801,7 @@ static ObjectPropertyResult loadFallbackProperty( qmlData->flushPendingBinding(coreIndex); captureFallbackProperty(object, coreIndex, l->qobjectFallbackLookup.notifyIndex, - l->qobjectFallbackLookup.isConstant, qmlContext); + l->qobjectFallbackLookup.isConstant, aotContext); void *a[] = { target, nullptr }; metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a); @@ -984,7 +982,7 @@ bool AOTCompiledContext::captureLookup(uint index, QObject *object) const || l->getter == QV4::Lookup::getterQObject) { const QQmlPropertyData *property = l->qobjectLookup.propertyData; QQmlData::flushPendingBinding(object, property->coreIndex()); - captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext); + captureObjectProperty(object, l->qobjectLookup.propertyCache, property, this); return true; } @@ -993,7 +991,7 @@ bool AOTCompiledContext::captureLookup(uint index, QObject *object) const QQmlData::flushPendingBinding(object, coreIndex); captureFallbackProperty( object, coreIndex, l->qobjectFallbackLookup.notifyIndex, - l->qobjectFallbackLookup.isConstant, qmlContext); + l->qobjectFallbackLookup.isConstant, this); return true; } @@ -1007,7 +1005,7 @@ bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const && l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty) { const QQmlPropertyData *property = l->qobjectLookup.propertyData; QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex()); - captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, qmlContext); + captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, this); return true; } @@ -1015,7 +1013,7 @@ bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const const int coreIndex = l->qobjectFallbackLookup.coreIndex; QQmlData::flushPendingBinding(qmlScopeObject, coreIndex); captureFallbackProperty(qmlScopeObject, coreIndex, l->qobjectFallbackLookup.notifyIndex, - l->qobjectFallbackLookup.isConstant, qmlContext); + l->qobjectFallbackLookup.isConstant, this); return true; } @@ -1267,9 +1265,9 @@ bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target) ObjectPropertyResult result = ObjectPropertyResult::NeedsInit; if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty) - result = loadObjectProperty(l, qmlScopeObject, target, qmlContext); + result = loadObjectProperty(l, qmlScopeObject, target, this); else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) - result = loadFallbackProperty(l, qmlScopeObject, target, qmlContext); + result = loadFallbackProperty(l, qmlScopeObject, target, this); else return false; @@ -1405,9 +1403,9 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit; if (l->getter == QV4::Lookup::getterQObject) - result = loadObjectProperty(l, object, target, qmlContext); + result = loadObjectProperty(l, object, target, this); else if (l->getter == QV4::Lookup::getterFallback) - result = loadFallbackProperty(l, object, target, qmlContext); + result = loadFallbackProperty(l, object, target, this); else return false; diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 372103b94f..d4a6ba1dd3 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -812,17 +812,21 @@ static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPrope oldBinding = data->bindings; while (oldBinding && (oldBinding->targetPropertyIndex().coreIndex() != coreIndex || - oldBinding->targetPropertyIndex().hasValueTypeIndex())) + oldBinding->targetPropertyIndex().hasValueTypeIndex())) { oldBinding = oldBinding->nextBinding(); + } - if (!oldBinding) - return; - - if (valueTypeIndex != -1 && oldBinding->isValueTypeProxy()) + if (valueTypeIndex != -1 + && oldBinding + && oldBinding->isValueTypeProxy()) { oldBinding = static_cast<QQmlValueTypeProxyBinding *>(oldBinding.data())->binding(index); + } - if (!oldBinding) + if (!oldBinding) { + // Clear the binding bit so that the binding doesn't appear later for any reason + data->clearBindingBit(coreIndex); return; + } if (!(flags & QQmlPropertyPrivate::DontEnable)) oldBinding->setEnabled(false, {}); diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp index a244bdd0ba..2d2f37261c 100644 --- a/src/qml/qml/qqmltypedata.cpp +++ b/src/qml/qml/qqmltypedata.cpp @@ -345,7 +345,9 @@ void QQmlTypeData::done() ++it) { const TypeReference &type = *it; Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError() || type.type.isInlineComponentType()); - if (type.type.isInlineComponentType() && !type.type.pendingResolutionName().isEmpty()) { + if (type.errorWhenNotFound + && type.type.isInlineComponentType() + && !type.type.pendingResolutionName().isEmpty()) { auto containingType = type.type.containingType(); auto objectId = containingType.lookupInlineComponentIdByName(type.type.pendingResolutionName()); if (objectId < 0) { // can be any negative number if we tentatively resolved it in QQmlImport but it actually was not an inline component @@ -365,7 +367,7 @@ void QQmlTypeData::done() type.type.setInlineComponentObjectId(objectId); } } - if (type.typeData && type.typeData->isError()) { + if (type.errorWhenNotFound && type.typeData && type.typeData->isError()) { const QString typeName = stringAt(it.key()); QList<QQmlError> errors = type.typeData->errors(); @@ -887,6 +889,7 @@ void QQmlTypeData::resolveTypes() ref.version = version; ref.location = unresolvedRef->location; ref.needsCreation = unresolvedRef->needsCreation; + ref.errorWhenNotFound = unresolvedRef->errorWhenNotFound; m_resolvedTypes.insert(unresolvedRef.key(), ref); } @@ -930,8 +933,12 @@ QQmlError QQmlTypeData::buildTypeResolutionCaches( } else { objectId = resolvedType->type.inlineComponentId(); } - Q_ASSERT(objectId != -1); - ref->setTypePropertyCache(resolvedType->typeData->compilationUnit()->propertyCaches.at(objectId)); + + if (objectId >= 0) { + ref->setTypePropertyCache( + resolvedType->typeData->compilationUnit()->propertyCaches.at(objectId)); + } + ref->setType(qmlType); Q_ASSERT(ref->type().isInlineComponentType()); } diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h index 2c56ede9bd..e55e5ebae6 100644 --- a/src/qml/qml/qqmltypedata_p.h +++ b/src/qml/qml/qqmltypedata_p.h @@ -62,16 +62,16 @@ class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob public: struct TypeReference { - TypeReference() : version(QTypeRevision::zero()), needsCreation(true) {} - QV4::CompiledData::Location location; QQmlType type; - QTypeRevision version; + QTypeRevision version = QTypeRevision::zero(); QQmlRefPointer<QQmlTypeData> typeData; - bool selfReference = false; QString prefix; // used by CompositeSingleton types + bool selfReference = false; + bool needsCreation = true; + bool errorWhenNotFound = true; + QString qualifiedName() const; - bool needsCreation; }; struct ScriptReference diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index 733cb3b2c0..827d2c2cf5 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -1340,6 +1340,7 @@ void QQmlJSImportVisitor::endVisit(UiArrayBinding *) bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied) { QQmlJSMetaEnum qmlEnum(uied->name.toString()); + qmlEnum.setIsQml(true); for (const auto *member = uied->members; member; member = member->next) { qmlEnum.addKey(member->member.toString()); qmlEnum.addValue(int(member->value)); diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h index 513414d87f..c0b3144819 100644 --- a/src/qmlcompiler/qqmljsmetatypes_p.h +++ b/src/qmlcompiler/qqmljsmetatypes_p.h @@ -66,6 +66,8 @@ class QQmlJSMetaEnum QString m_alias; QSharedPointer<const QQmlJSScope> m_type; bool m_isFlag = false; + bool m_isScoped = false; + bool m_isQml = false; public: QQmlJSMetaEnum() = default; @@ -82,6 +84,12 @@ public: bool isFlag() const { return m_isFlag; } void setIsFlag(bool isFlag) { m_isFlag = isFlag; } + bool isScoped() const { return m_isScoped; } + void setIsScoped(bool v) { m_isScoped = v; } + + bool isQml() const { return m_isQml; } + void setIsQml(bool v) { m_isQml = v; } + void addKey(const QString &key) { m_keys.append(key); } QStringList keys() const { return m_keys; } @@ -102,7 +110,8 @@ public: && a.m_name == b.m_name && a.m_alias == b.m_alias && a.m_isFlag == b.m_isFlag - && a.m_type == b.m_type; + && a.m_type == b.m_type + && a.m_isScoped == b.m_isScoped; } friend bool operator!=(const QQmlJSMetaEnum &a, const QQmlJSMetaEnum &b) @@ -112,7 +121,8 @@ public: friend size_t qHash(const QQmlJSMetaEnum &e, size_t seed = 0) { - return qHashMulti(seed, e.m_keys, e.m_values, e.m_name, e.m_alias, e.m_isFlag, e.m_type); + return qHashMulti( + seed, e.m_keys, e.m_values, e.m_name, e.m_alias, e.m_isFlag, e.m_type, e.m_isScoped); } }; diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp index 04293fd14c..779e040a2d 100644 --- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp +++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp @@ -418,9 +418,11 @@ void QQmlJSTypeDescriptionReader::readEnum(UiObjectDefinition *ast, const QQmlJS metaEnum.setIsFlag(readBoolBinding(script)); } else if (name == QLatin1String("values")) { readEnumValues(script, &metaEnum); + } else if (name == QLatin1String("isScoped")) { + metaEnum.setIsScoped(readBoolBinding(script)); } else { addWarning(script->firstSourceLocation(), - tr("Expected only name and values script bindings.")); + tr("Expected only name, alias, isFlag, values, or isScoped.")); } } diff --git a/src/qmlmodels/qqmllistmodel.cpp b/src/qmlmodels/qqmllistmodel.cpp index c060aaec97..1663471ab5 100644 --- a/src/qmlmodels/qqmllistmodel.cpp +++ b/src/qmlmodels/qqmllistmodel.cpp @@ -1699,9 +1699,12 @@ bool ModelObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Val ExecutionEngine *eng = that->engine(); const int elementIndex = that->d()->elementIndex(); - int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng); - if (roleIndex != -1) - that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); + if (QQmlListModel *model = that->d()->m_model) { + const int roleIndex + = model->listModel()->setExistingProperty(elementIndex, propName, value, eng); + if (roleIndex != -1) + model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); + } ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object()); if (mo->initialized()) @@ -1717,7 +1720,11 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va const ModelObject *that = static_cast<const ModelObject*>(m); Scope scope(that); ScopedString name(scope, id.asStringOrSymbol()); - const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name); + QQmlListModel *model = that->d()->m_model; + if (!model) + return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); + + const ListLayout::Role *role = model->listModel()->getExistingRole(name); if (!role) return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); if (hasProperty) @@ -1730,7 +1737,7 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va } const int elementIndex = that->d()->elementIndex(); - QVariant value = that->d()->m_model->data(elementIndex, role->index); + QVariant value = model->data(elementIndex, role->index); return that->engine()->fromVariant(value); } @@ -1753,16 +1760,19 @@ PropertyKey ModelObjectOwnPropertyKeyIterator::next(const Object *o, Property *p const ModelObject *that = static_cast<const ModelObject *>(o); ExecutionEngine *v4 = that->engine(); - if (roleNameIndex < that->listModel()->roleCount()) { + + QQmlListModel *model = that->d()->m_model; + ListModel *listModel = model ? model->listModel() : nullptr; + if (listModel && roleNameIndex < listModel->roleCount()) { Scope scope(that->engine()); - const ListLayout::Role &role = that->listModel()->getExistingRole(roleNameIndex); + const ListLayout::Role &role = listModel->getExistingRole(roleNameIndex); ++roleNameIndex; ScopedString roleName(scope, v4->newString(role.name)); if (attrs) *attrs = QV4::Attr_Data; if (pd) { - QVariant value = that->d()->m_model->data(that->d()->elementIndex(), role.index); + QVariant value = model->data(that->d()->elementIndex(), role.index); if (auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) { auto size = recursiveListModel->count(); auto array = ScopedArrayObject{scope, v4->newArrayObject(size)}; diff --git a/src/qmlmodels/qqmllistmodel_p.h b/src/qmlmodels/qqmllistmodel_p.h index 7cd2b4662b..3ebdf209a4 100644 --- a/src/qmlmodels/qqmllistmodel_p.h +++ b/src/qmlmodels/qqmllistmodel_p.h @@ -115,6 +115,8 @@ public: bool dynamicRoles() const { return m_dynamicRoles; } void setDynamicRoles(bool enableDynamicRoles); + ListModel *listModel() const { return m_listModel; } + Q_SIGNALS: void countChanged(); diff --git a/src/qmlmodels/qqmllistmodel_p_p.h b/src/qmlmodels/qqmllistmodel_p_p.h index 19cba980f4..da04b0a5f5 100644 --- a/src/qmlmodels/qqmllistmodel_p_p.h +++ b/src/qmlmodels/qqmllistmodel_p_p.h @@ -161,13 +161,23 @@ struct ModelObject : public QObjectWrapper { { QObjectWrapper::init(object); m_model = model; - QObjectPrivate *op = QObjectPrivate::get(object); - m_nodeModelMetaObject = static_cast<ModelNodeMetaObject *>(op->metaObject); } - void destroy() { QObjectWrapper::destroy(); } - int elementIndex() const { return m_nodeModelMetaObject->m_elementIndex; } - QQmlListModel *m_model; - ModelNodeMetaObject *m_nodeModelMetaObject; + + void destroy() + { + m_model.destroy(); + QObjectWrapper::destroy(); + } + + int elementIndex() const { + if (const QObject *o = object()) { + const QObjectPrivate *op = QObjectPrivate::get(o); + return static_cast<ModelNodeMetaObject *>(op->metaObject)->m_elementIndex; + } + return -1; + } + + QV4QPointer<QQmlListModel> m_model; }; } @@ -177,8 +187,6 @@ struct ModelObject : public QObjectWrapper V4_OBJECT2(ModelObject, QObjectWrapper) V4_NEEDS_DESTROY - ListModel *listModel() const { return d()->m_model->m_listModel; } - protected: static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver); static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc index 5a3fdfeef4..48cfdf205f 100644 --- a/src/qmltest/doc/src/qtquicktest-index.qdoc +++ b/src/qmltest/doc/src/qtquicktest-index.qdoc @@ -118,15 +118,67 @@ \snippet src_qmltest_qquicktest_snippet.cpp 1 Where "example" is the identifier to use to uniquely identify - this set of tests. Finally, add \c{CONFIG += qmltestcase} to the project - file: + this set of tests. + + \if defined(onlinedocs) + \tab {run-qtquicktest}{tab-cmake}{CMake}{checked} + \tab {run-qtquicktest}{tab-qmake}{qmake}{} + \tabcontent {tab-cmake} + \else + \section1 Using CMake + \endif + Configure your CMakeLists.txt file and build your project using your + favorite generator. + \badcode + cmake_minimum_required(VERSION 3.2) + + project(tst_example LANGUAGES CXX) + + enable_testing() + + find_package(Qt6 REQUIRED COMPONENTS QuickTest Qml) + + #[[The test harness scans the specified source directory recursively + for "tst_*.qml" files. By default, it looks in the current directory, + which is usually where the executable is. This command makes it look + in the project's source directory instead.]] + add_definitions(-DQUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") + + qt_standard_project_setup(REQUIRES 6.6) + + add_executable(tst_example tst_example.cpp) + + add_test(NAME tst_example COMMAND tst_example) + + target_link_libraries(tst_example + PRIVATE + Qt6::QuickTest + Qt6::Qml + ) + \endcode + \if defined(onlinedocs) + \endtabcontent + \tabcontent {tab-qmake} + \else + \section1 Using qmake + \endif + Add \c{CONFIG += qmltestcase} to your project file: + \badcode + TEMPLATE = app + TARGET = tst_example + CONFIG += warn_on qmltestcase + SOURCES += tst_example.cpp + \endcode + + If \c IMPORTPATH is specified in your .pro file, each import path added to \c IMPORTPATH + will be passed as a command-line argument when the test is run using "make check": \badcode - TEMPLATE = app - TARGET = tst_example - CONFIG += warn_on qmltestcase - SOURCES += tst_example.cpp + IMPORTPATH += $$PWD/../imports/my_module1 $$PWD/../imports/my_module2 \endcode + \if defined(onlinedocs) + \endtabcontent + \endif The test harness scans the specified source directory recursively for "tst_*.qml" files. If \c{QUICK_TEST_SOURCE_DIR} is not defined, @@ -160,12 +212,6 @@ If your test case needs QML imports, then you can add them as \c{-import} options to the test program command-line. - If \c IMPORTPATH is specified in your .pro file, each import path added to \c IMPORTPATH - will be passed as a command-line argument when the test is run using "make check": - - \badcode - IMPORTPATH += $$PWD/../imports/my_module1 $$PWD/../imports/my_module2 - \endcode The \c{-functions} command-line option will return a list of the current tests functions. It is possible to run a single test function using the name diff --git a/src/qmltyperegistrar/qmltypesclassdescription.cpp b/src/qmltyperegistrar/qmltypesclassdescription.cpp index b58d4ac00a..ec58998ee7 100644 --- a/src/qmltyperegistrar/qmltypesclassdescription.cpp +++ b/src/qmltyperegistrar/qmltypesclassdescription.cpp @@ -112,10 +112,16 @@ void QmlTypesClassDescription::collectLocalAnonymous( const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray(); for (const QJsonValue classInfo : classInfos) { const QJsonObject obj = classInfo.toObject(); - if (obj[QStringLiteral("name")].toString() == QStringLiteral("DefaultProperty")) - defaultProp = obj[QStringLiteral("value")].toString(); - if (obj[QStringLiteral("name")].toString() == QStringLiteral("ParentProperty")) - parentProp = obj[QStringLiteral("value")].toString(); + const QString name = obj[QStringLiteral("name")].toString(); + const auto value = [&]() { return obj[QStringLiteral("value")].toString(); }; + if (name == QStringLiteral("DefaultProperty")) { + defaultProp = value(); + } else if (name == QStringLiteral("ParentProperty")) { + parentProp = value(); + } else if (name == QStringLiteral("RegisterEnumClassesUnscoped") + && value() == QStringLiteral("false")) { + registerEnumClassesScoped = true; + } } collectInterfaces(classDef); @@ -143,6 +149,9 @@ void QmlTypesClassDescription::collect( } else if (name == QLatin1String("ParentProperty")) { if (mode != RelatedType && parentProp.isEmpty()) parentProp = value; + } else if (name == QLatin1String("RegisterEnumClassesUnscoped")) { + if (mode != RelatedType && value == QLatin1String("false")) + registerEnumClassesScoped = true; } else if (name == QLatin1String("QML.AddedInVersion")) { const QTypeRevision revision = QTypeRevision::fromEncodedVersion(value.toInt()); if (mode == TopLevel) { @@ -192,10 +201,12 @@ void QmlTypesClassDescription::collect( if (const QJsonObject *other = findType(foreign, foreignTypeName)) { classDef = other; - // Default properties are always local. + // Default properties and enum classes are always local. defaultProp.clear(); + registerEnumClassesScoped = false; // Foreign type can have a default property or an attached types + // or RegisterEnumClassesUnscoped classinfo. const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray(); for (const QJsonValue classInfo : classInfos) { const QJsonObject obj = classInfo.toObject(); @@ -205,6 +216,9 @@ void QmlTypesClassDescription::collect( defaultProp = foreignValue; } else if (parentProp.isEmpty() && foreignName == QLatin1String("ParentProperty")) { parentProp = foreignValue; + } else if (foreignName == QLatin1String("RegisterEnumClassesUnscoped")) { + if (foreignValue == QLatin1String("false")) + registerEnumClassesScoped = true; } else if (foreignName == QLatin1String("QML.Attached")) { attachedType = foreignValue; collectRelated(foreignValue, types, foreign, defaultRevision); diff --git a/src/qmltyperegistrar/qmltypesclassdescription.h b/src/qmltyperegistrar/qmltypesclassdescription.h index 1e043fb925..ecc5eb1ebf 100644 --- a/src/qmltyperegistrar/qmltypesclassdescription.h +++ b/src/qmltyperegistrar/qmltypesclassdescription.h @@ -55,6 +55,7 @@ struct QmlTypesClassDescription bool isSingleton = false; bool isRootClass = false; bool hasCustomParser = false; + bool registerEnumClassesScoped = false; QStringList implementsInterfaces; enum CollectMode { diff --git a/src/qmltyperegistrar/qmltypescreator.cpp b/src/qmltyperegistrar/qmltypescreator.cpp index 15740579c0..dbc9721a37 100644 --- a/src/qmltyperegistrar/qmltypescreator.cpp +++ b/src/qmltyperegistrar/qmltypescreator.cpp @@ -244,7 +244,8 @@ void QmlTypesCreator::writeMethods(const QJsonArray &methods, const QString &typ } } -void QmlTypesCreator::writeEnums(const QJsonArray &enums) +void QmlTypesCreator::writeEnums( + const QJsonArray &enums, QmlTypesCreator::EnumClassesMode enumClassesMode) { for (const QJsonValue item : enums) { const QJsonObject obj = item.toObject(); @@ -263,6 +264,13 @@ void QmlTypesCreator::writeEnums(const QJsonArray &enums) auto isFlag = obj.find(QLatin1String("isFlag")); if (isFlag != obj.end() && isFlag->toBool()) m_qml.writeBooleanBinding(isFlag.key(), true); + + if (enumClassesMode == EnumClassesMode::Scoped) { + const auto isClass = obj.find(QLatin1String("isClass")); + if (isClass != obj.end() && isClass->toBool()) + m_qml.writeBooleanBinding(QLatin1String("isScoped"), true); + } + m_qml.writeArrayBinding(QLatin1String("values"), valueList); m_qml.writeEndObject(); } @@ -382,7 +390,11 @@ void QmlTypesCreator::writeComponents() writeClassProperties(collector); if (const QJsonObject *classDef = collector.resolvedClass) { - writeEnums(members(classDef, enumsKey, m_version)); + writeEnums( + members(classDef, enumsKey, m_version), + collector.registerEnumClassesScoped + ? EnumClassesMode::Scoped + : EnumClassesMode::Unscoped); writeProperties(members(classDef, propertiesKey, m_version)); @@ -411,7 +423,11 @@ void QmlTypesCreator::writeComponents() collector.collectLocalAnonymous(&component, m_ownTypes, m_foreignTypes, m_version); writeClassProperties(collector); - writeEnums(members(&component, enumsKey, m_version)); + writeEnums( + members(&component, enumsKey, m_version), + collector.registerEnumClassesScoped + ? EnumClassesMode::Scoped + : EnumClassesMode::Unscoped); writeProperties(members(&component, propertiesKey, m_version)); diff --git a/src/qmltyperegistrar/qmltypescreator.h b/src/qmltyperegistrar/qmltypescreator.h index 3e8fa480a2..aff41485cc 100644 --- a/src/qmltyperegistrar/qmltypescreator.h +++ b/src/qmltyperegistrar/qmltypescreator.h @@ -53,7 +53,10 @@ private: void writeType(const QJsonObject &property, const QString &key); void writeProperties(const QJsonArray &properties); void writeMethods(const QJsonArray &methods, const QString &type); - void writeEnums(const QJsonArray &enums); + + enum class EnumClassesMode { Scoped, Unscoped }; + void writeEnums(const QJsonArray &enums, EnumClassesMode enumClassesMode); + void writeComponents(); QByteArray m_output; diff --git a/src/quick/doc/snippets/pointerHandlers/dragHandlerMargin.qml b/src/quick/doc/snippets/pointerHandlers/dragHandlerMargin.qml new file mode 100644 index 0000000000..20a3a802cf --- /dev/null +++ b/src/quick/doc/snippets/pointerHandlers/dragHandlerMargin.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick + +//! [entire] +Item { + width: 320 + height: 240 + //![draggable] + Rectangle { + width: 24 + height: 24 + border.color: "steelblue" + Text { + text: "it's\ntiny" + font.pixelSize: 7 + rotation: -45 + anchors.centerIn: parent + } + + DragHandler { + margin: 12 + } + } + //![draggable] +} +//! [entire] diff --git a/src/quick/doc/snippets/pointerHandlers/draggableGridView.qml b/src/quick/doc/snippets/pointerHandlers/draggableGridView.qml new file mode 100644 index 0000000000..0b19274e0d --- /dev/null +++ b/src/quick/doc/snippets/pointerHandlers/draggableGridView.qml @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +pragma ComponentBehavior: Bound +import QtQml +import QtQuick +import QtQml.Models + +//! [entire] +GridView { + id: root + width: 320 + height: 480 + cellWidth: 80 + cellHeight: 80 + interactive: false + + displaced: Transition { + NumberAnimation { + properties: "x,y" + easing.type: Easing.OutQuad + } + } + + model: DelegateModel { + id: visualModel + model: 24 + property var dropTarget: undefined + property bool copy: false + delegate: DropArea { + id: delegateRoot + + width: 80 + height: 80 + + onEntered: drag => { + if (visualModel.copy) { + if (drag.source !== icon) + visualModel.dropTarget = icon + } else { + visualModel.items.move(drag.source.DelegateModel.itemsIndex, icon.DelegateModel.itemsIndex) + } + } + + Rectangle { + id: icon + objectName: DelegateModel.itemsIndex + + property string text + Component.onCompleted: { + color = Qt.rgba(0.2 + (48 - DelegateModel.itemsIndex) * Math.random() / 48, + 0.3 + DelegateModel.itemsIndex * Math.random() / 48, + 0.4 * Math.random(), + 1.0) + text = DelegateModel.itemsIndex + } + border.color: visualModel.dropTarget === this ? "black" : "transparent" + border.width: 2 + radius: 3 + width: 72 + height: 72 + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + + states: [ + State { + when: dragHandler.active || controlDragHandler.active + ParentChange { + target: icon + parent: root + } + + AnchorChanges { + target: icon + anchors { + horizontalCenter: undefined + verticalCenter: undefined + } + } + } + ] + + Text { + anchors.centerIn: parent + color: "white" + font.pointSize: 14 + text: controlDragHandler.active ? "+" : icon.text + } + + //! [draghandlers] + DragHandler { + id: dragHandler + acceptedModifiers: Qt.NoModifier + onActiveChanged: if (!active) visualModel.dropTarget = undefined + } + + DragHandler { + id: controlDragHandler + acceptedModifiers: Qt.ControlModifier + onActiveChanged: { + visualModel.copy = active + if (!active) { + visualModel.dropTarget.text = icon.text + visualModel.dropTarget.color = icon.color + visualModel.dropTarget = undefined + } + } + } + //! [draghandlers] + + Drag.active: dragHandler.active || controlDragHandler.active + Drag.source: icon + Drag.hotSpot.x: 36 + Drag.hotSpot.y: 36 + } + } + } +} +//! [entire] diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index 8c9464ee55..c3f57f9b02 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -86,7 +86,8 @@ Q_LOGGING_CATEGORY(lcDragHandler, "qt.quick.handler.drag") \c target is an Item, \c centroid is the point at which the drag begins and to which the \c target will be moved (subject to constraints). - At this time, drag-and-drop is not yet supported. + DragHandler can be used together with the \l Drag attached property to + implement drag-and-drop. \sa Drag, MouseArea, {Pointer Handlers Example} */ @@ -134,7 +135,7 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDe The snap mode configures snapping of the \l target item's center to the \l eventPoint. Possible values: - \value DragHandler.SnapNever Never snap + \value DragHandler.NoSnap Never snap \value DragHandler.SnapAuto The \l target snaps if the \l eventPoint was pressed outside of the \l target item \e and the \l target is a descendant of \l {PointerHandler::}{parent} item (default) \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the \l eventPoint was pressed outside of the \l target @@ -394,6 +395,93 @@ void QQuickDragHandler::setActiveTranslation(const QVector2D &trans) \c {0, 0} again. */ +/*! + \qmlproperty flags QtQuick::DragHandler::acceptedButtons + + The mouse buttons that can activate this DragHandler. + + By default, this property is set to + \l {QtQuick::MouseEvent::button} {Qt.LeftButton}. + It can be set to an OR combination of mouse buttons, and will ignore events + from other buttons. + + For example, if a component (such as TextEdit) already handles + left-button drags in its own way, it can be augmented with a + DragHandler that does something different when dragged via the + right button: + + \snippet pointerHandlers/dragHandlerAcceptedButtons.qml 0 +*/ + +/*! + \qmlproperty flags DragHandler::acceptedDevices + + The types of pointing devices that can activate this DragHandler. + + By default, this property is set to + \l{QInputDevice::DeviceType}{PointerDevice.AllDevices}. + If you set it to an OR combination of device types, it will ignore events + from non-matching devices. + + \note Not all platforms are yet able to distinguish mouse and touchpad; and + on those that do, you often want to make mouse and touchpad behavior the same. +*/ + +/*! + \qmlproperty flags DragHandler::acceptedModifiers + + If this property is set, it will require the given keyboard modifiers to + be pressed in order to react to pointer events, and otherwise ignore them. + + For example, two DragHandlers can perform two different drag-and-drop + operations, depending on whether the \c Control modifier is pressed: + + \snippet pointerHandlers/draggableGridView.qml entire + + If this property is set to \c Qt.KeyboardModifierMask (the default value), + then the DragHandler ignores the modifier keys. + + If you set \c acceptedModifiers to an OR combination of modifier keys, + it means \e all of those modifiers must be pressed to activate the handler. + + The available modifiers are as follows: + + \value NoModifier No modifier key is allowed. + \value ShiftModifier A Shift key on the keyboard must be pressed. + \value ControlModifier A Ctrl key on the keyboard must be pressed. + \value AltModifier An Alt key on the keyboard must be pressed. + \value MetaModifier A Meta key on the keyboard must be pressed. + \value KeypadModifier A keypad button must be pressed. + \value GroupSwitchModifier X11 only (unless activated on Windows by a command line argument). + A Mode_switch key on the keyboard must be pressed. + \value KeyboardModifierMask The handler does not care which modifiers are pressed. + + \sa Qt::KeyboardModifier +*/ + +/*! + \qmlproperty flags DragHandler::acceptedPointerTypes + + The types of pointing instruments (finger, stylus, eraser, etc.) + that can activate this DragHandler. + + By default, this property is set to + \l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}. + If you set it to an OR combination of device types, it will ignore events + from non-matching \l {PointerDevice}{devices}. +*/ + +/*! + \qmlproperty real DragHandler::margin + + The margin beyond the bounds of the \l {PointerHandler::parent}{parent} + item within which an \l eventPoint can activate this handler. For example, + you can make it easier to drag small items by allowing the user to drag + from a position nearby: + + \snippet pointerHandlers/dragHandlerMargin.qml draggable +*/ + QT_END_NAMESPACE #include "moc_qquickdraghandler_p.cpp" diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 916ae61cf5..27438bda14 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -1404,9 +1404,53 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight) /*! \qmlproperty int QtQuick::GridView::count - This property holds the number of items in the view. + This property holds the number of items in the model. */ +/*! + \qmlproperty bool QtQuick::GridView::reuseItems + + This property enables you to reuse items that are instantiated + from the \l delegate. If set to \c false, any currently + pooled items are destroyed. + + This property is \c false by default. + + \since 5.15 + + \sa {Reusing items}, pooled(), reused() +*/ + +/*! + \qmlattachedsignal QtQuick::GridView::pooled() + + This signal is emitted after an item has been added to the reuse + pool. You can use it to pause ongoing timers or animations inside + the item, or free up resources that cannot be reused. + + This signal is emitted only if the \l reuseItems property is \c true. + + \sa {Reusing items}, reuseItems, reused() +*/ + +/*! + \qmlattachedsignal QtQuick::GridView::reused() + + This signal is emitted after an item has been reused. At this point, the + item has been taken out of the pool and placed inside the content view, + and the model properties such as \c index and \c row have been updated. + + Other properties that are not provided by the model does not change when an + item is reused. You should avoid storing any state inside a delegate, but if + you do, manually reset that state on receiving this signal. + + This signal is emitted when the item is reused, and not the first time the + item is created. + + This signal is emitted only if the \l reuseItems property is \c true. + + \sa {Reusing items}, reuseItems, pooled() +*/ /*! \qmlproperty Component QtQuick::GridView::highlight diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 9e7a8f4803..6c822e62c1 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -1721,7 +1721,7 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte updateHighlight(); bottomItem = currentItem; } - qreal pos; + qreal pos = 0; bool isInBounds = -position() > maxExtent && -position() <= minExtent; if (header && !topItem && isInBounds) { @@ -1802,6 +1802,19 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte QQuickItemViewPrivate::fixup(data, minExtent, maxExtent); return; } + // If we have the CurrentLabelAtStart flag set, then we need to consider + // the section size while calculating the position + if (sectionCriteria + && (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart) + && currentSectionItem) { + auto sectionSize = (orient == QQuickListView::Vertical) ? currentSectionItem->height() + : currentSectionItem->width(); + if (isContentFlowReversed()) + pos += sectionSize; + else + pos -= sectionSize; + } + pos = qBound(-minExtent, pos, -maxExtent); qreal dist = qAbs(data.move + pos); @@ -2380,7 +2393,7 @@ QQuickListView::~QQuickListView() /*! \qmlproperty int QtQuick::ListView::count - This property holds the number of items in the view. + This property holds the number of items in the model. */ /*! diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 9b0334b851..490e89acad 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -3297,11 +3297,18 @@ void QQuickTableViewPrivate::syncSyncView() q->setColumnSpacing(syncView->columnSpacing()); updateContentWidth(); - if (syncView->leftColumn() != q->leftColumn()) { - // The left column is no longer the same as the left - // column in syncView. This requires a rebuild. - scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn; - scheduledRebuildOptions.setFlag(RebuildOption::ViewportOnly); + if (scheduledRebuildOptions & RebuildOption::LayoutOnly) { + if (syncView->leftColumn() != q->leftColumn() + || syncView->d_func()->loadedTableOuterRect.left() != loadedTableOuterRect.left()) { + // The left column is no longer the same, or at the same pos, as the left column in + // syncView. This can happen if syncView did a relayout that caused its left column + // to be resized so small that it ended up outside the viewport. It can also happen + // if the syncView loaded and unloaded columns after the relayout. We therefore need + // to sync our own left column and pos to be the same, which we do by rebuilding the + // whole viewport instead of just doing a plain LayoutOnly. + scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn; + scheduledRebuildOptions.setFlag(RebuildOption::ViewportOnly); + } } } @@ -3309,11 +3316,18 @@ void QQuickTableViewPrivate::syncSyncView() q->setRowSpacing(syncView->rowSpacing()); updateContentHeight(); - if (syncView->topRow() != q->topRow()) { - // The top row is no longer the same as the top - // row in syncView. This requires a rebuild. - scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow; - scheduledRebuildOptions.setFlag(RebuildOption::ViewportOnly); + if (scheduledRebuildOptions & RebuildOption::LayoutOnly) { + if (syncView->topRow() != q->topRow() + || syncView->d_func()->loadedTableOuterRect.top() != loadedTableOuterRect.top()) { + // The top row is no longer the same, or at the same pos, as the top row in + // syncView. This can happen if syncView did a relayout that caused its top row + // to be resized so small that it ended up outside the viewport. It can also happen + // if the syncView loaded and unloaded rows after the relayout. We therefore need + // to sync our own top row and pos to be the same, which we do by rebuilding the + // whole viewport instead of just doing a plain LayoutOnly. + scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow; + scheduledRebuildOptions.setFlag(RebuildOption::ViewportOnly); + } } } diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index cef0dd2900..cdbcf46380 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -2462,8 +2462,10 @@ void QQuickText::geometryChange(const QRectF &newGeometry, const QRectF &oldGeom } } } else if (!heightChanged && widthMaximum) { - if (!qFuzzyIsNull(oldGeometry.width())) { + if (oldGeometry.width() > 0) { // no change to height, width is adequate and wasn't 0 before + // (old width could also be negative if it was 0 and the margins + // were set) goto geomChangeDone; } } diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 9d18868c58..d3aa5ef7a8 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -190,7 +190,12 @@ void QSGDistanceFieldGlyphCache::update() distanceFields.reserve(pendingGlyphsSize); for (int i = 0; i < pendingGlyphsSize; ++i) { GlyphData &gd = glyphData(m_pendingGlyphs.at(i)); - distanceFields.append(QDistanceField(gd.path, + + QSize size = QSize(qCeil(gd.texCoord.width + gd.texCoord.xMargin * 2), + qCeil(gd.texCoord.height + gd.texCoord.yMargin * 2)); + + distanceFields.append(QDistanceField(size, + gd.path, m_pendingGlyphs.at(i), m_doubleGlyphResolution)); gd.path = QPainterPath(); // no longer needed, so release memory used by the painter path diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp index d3e0d5a7c5..fbc2227bae 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp @@ -83,11 +83,14 @@ void QSGDefaultGlyphNode::update() QRawFont font = m_glyphs.rawFont(); QMargins margins(0, 0, 0, 0); - if (m_style == QQuickText::Normal) { + const auto *fontEngine = QRawFontPrivate::get(font)->fontEngine; + const bool isColorFont = fontEngine->glyphFormat == QFontEngine::Format_ARGB; + + if (m_style == QQuickText::Normal || isColorFont) { QFontEngine::GlyphFormat glyphFormat; // Don't try to override glyph format of color fonts - if (QRawFontPrivate::get(font)->fontEngine->glyphFormat == QFontEngine::Format_ARGB) { + if (isColorFont) { glyphFormat = QFontEngine::Format_None; } else { switch (m_preferredAntialiasingMode) { diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp index af60f5b02b..a2003fdc37 100644 --- a/src/quick/util/qquickanimation.cpp +++ b/src/quick/util/qquickanimation.cpp @@ -310,6 +310,11 @@ void QQuickAbstractAnimation::setRunning(bool r) // Therefore, the state of d->running will in that case be different than r if we are back in // the root stack frame of the recursive calls to setRunning() emit runningChanged(d->running); + } else if (d->animationInstance) { + // If there was a recursive call, make sure the d->running is set correctly + d->running = d->animationInstance->isRunning(); + } else { + d->running = r; } } diff --git a/src/quicklayouts/qquicklinearlayout.cpp b/src/quicklayouts/qquicklinearlayout.cpp index dc55196c11..f7f7523c21 100644 --- a/src/quicklayouts/qquicklinearlayout.cpp +++ b/src/quicklayouts/qquicklinearlayout.cpp @@ -482,7 +482,7 @@ void QQuickGridLayoutBase::itemVisibilityChanged(QQuickItem *item) void QQuickGridLayoutBase::rearrange(const QSizeF &size) { Q_D(QQuickGridLayoutBase); - if (!isReady()) + if (!isReady() || !size.isValid()) return; qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::rearrange" << d->m_recurRearrangeCounter << this; diff --git a/src/quicktemplates2/qquickcontrol.cpp b/src/quicktemplates2/qquickcontrol.cpp index 8a46d040f2..f999aec9b7 100644 --- a/src/quicktemplates2/qquickcontrol.cpp +++ b/src/quicktemplates2/qquickcontrol.cpp @@ -385,12 +385,22 @@ void QQuickControlPrivate::resizeBackground() bool changeHeight = false; if (((!p->widthValid() || !extra.isAllocated() || !extra->hasBackgroundWidth) && qFuzzyIsNull(background->x())) || (extra.isAllocated() && (extra->hasLeftInset || extra->hasRightInset))) { - background->setX(getLeftInset()); + const auto leftInset = getLeftInset(); + if (!qt_is_nan(leftInset) && p->x.valueBypassingBindings() != leftInset) { + // We bypass the binding here to prevent it from being removed + p->x.setValueBypassingBindings(leftInset); + p->dirty(DirtyType::Position); + } changeWidth = !p->width.hasBinding(); } if (((!p->heightValid() || !extra.isAllocated() || !extra->hasBackgroundHeight) && qFuzzyIsNull(background->y())) || (extra.isAllocated() && (extra->hasTopInset || extra->hasBottomInset))) { - background->setY(getTopInset()); + const auto topInset = getTopInset(); + if (!qt_is_nan(topInset) && p->y.valueBypassingBindings() != topInset) { + // We bypass the binding here to prevent it from being removed + p->y.setValueBypassingBindings(topInset); + p->dirty(DirtyType::Position); + } changeHeight = !p->height.hasBinding(); } if (changeHeight || changeWidth) { diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp index 1829f9a30e..62abaae005 100644 --- a/src/quicktemplates2/qquickoverlay.cpp +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -498,6 +498,13 @@ bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event) case QEvent::HoverEnter: case QEvent::HoverMove: case QEvent::HoverLeave: + // If the control item has already been hovered, allow the hover leave event + // to be processed by the same item for resetting its internal hovered state + // instead of filtering it here. + if (auto *control = qobject_cast<QQuickControl *>(item)) { + if (control->isHovered() && event->type() == QEvent::HoverLeave) + return false; + } handled = d->handleHoverEvent(item, static_cast<QHoverEvent *>(event), popup); break; diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index c593ca2a81..058b959258 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -664,7 +664,24 @@ QQuickWidget::QQuickWidget(QWidget *parent) { setMouseTracking(true); setFocusPolicy(Qt::StrongFocus); +#ifndef Q_OS_MACOS + /* + Usually, a QTouchEvent comes from a touchscreen, and we want those + touch events in Qt Quick. But on macOS, there are no touchscreens, and + WA_AcceptTouchEvents has a different meaning: QApplication::notify() + calls the native-integration function registertouchwindow() to change + NSView::allowedTouchTypes to include NSTouchTypeMaskIndirect when the + trackpad cursor enters the window, and removes that mask when the + cursor exits. In other words, WA_AcceptTouchEvents enables getting + discrete touchpoints from the trackpad. We rather prefer to get mouse, + wheel and native gesture events from the trackpad (because those + provide more of a "native feel"). The only exception is for + MultiPointTouchArea, and it takes care of that for itself. So don't + automatically set WA_AcceptTouchEvents on macOS. The user can still do + it, but we don't recommend it. + */ setAttribute(Qt::WA_AcceptTouchEvents); +#endif d_func()->init(); } |
