aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarja Sundqvist <[email protected]>2025-08-22 09:33:17 +0300
committerTarja Sundqvist <[email protected]>2025-08-22 09:33:17 +0300
commitc6fdadd916a7568c1d71b750e054ca9aa2fd5dfc (patch)
treebbd5690b38a5e0df66dd070a9e9f42b0d1b2b673
parenta7c766a9863605eb81e8f0cdb4d2b93e087b5bde (diff)
parente436dad999060b92965291b45c0e95a3b93f5866 (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
-rw-r--r--.cmake.conf2
-rw-r--r--.qmake.conf2
-rw-r--r--dependencies.yaml8
-rw-r--r--examples/quick/scenegraph/graph/shaders/noisy.frag2
-rw-r--r--examples/quick/scenegraph/graph/shaders/noisy.frag.qsbbin1763 -> 1760 bytes
-rw-r--r--examples/quick/scenegraph/graph/shaders/noisy.vert4
-rw-r--r--examples/quick/scenegraph/graph/shaders/noisy.vert.qsbbin1650 -> 1646 bytes
-rw-r--r--src/imports/tooling/Enum.qml1
-rw-r--r--src/particles/qquickparticlesystem.cpp5
-rw-r--r--src/particles/qquickparticlesystem_p.h2
-rw-r--r--src/qml/common/qv4compileddata_p.h4
-rw-r--r--src/qml/jsruntime/qv4engine.cpp11
-rw-r--r--src/qml/jsruntime/qv4estable.cpp23
-rw-r--r--src/qml/jsruntime/qv4estable_p.h11
-rw-r--r--src/qml/jsruntime/qv4sparsearray_p.h44
-rw-r--r--src/qml/memory/qv4heap_p.h2
-rw-r--r--src/qml/qml/qqml.cpp52
-rw-r--r--src/qml/qml/qqmlproperty.cpp16
-rw-r--r--src/qml/qml/qqmltypedata.cpp15
-rw-r--r--src/qml/qml/qqmltypedata_p.h10
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp1
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h14
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp4
-rw-r--r--src/qmlmodels/qqmllistmodel.cpp26
-rw-r--r--src/qmlmodels/qqmllistmodel_p.h2
-rw-r--r--src/qmlmodels/qqmllistmodel_p_p.h24
-rw-r--r--src/qmltest/doc/src/qtquicktest-index.qdoc70
-rw-r--r--src/qmltyperegistrar/qmltypesclassdescription.cpp24
-rw-r--r--src/qmltyperegistrar/qmltypesclassdescription.h1
-rw-r--r--src/qmltyperegistrar/qmltypescreator.cpp22
-rw-r--r--src/qmltyperegistrar/qmltypescreator.h5
-rw-r--r--src/quick/doc/snippets/pointerHandlers/dragHandlerMargin.qml74
-rw-r--r--src/quick/doc/snippets/pointerHandlers/draggableGridView.qml168
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp92
-rw-r--r--src/quick/items/qquickgridview.cpp46
-rw-r--r--src/quick/items/qquicklistview.cpp17
-rw-r--r--src/quick/items/qquicktableview.cpp34
-rw-r--r--src/quick/items/qquicktext.cpp4
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp7
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode.cpp7
-rw-r--r--src/quick/util/qquickanimation.cpp5
-rw-r--r--src/quicklayouts/qquicklinearlayout.cpp2
-rw-r--r--src/quicktemplates2/qquickcontrol.cpp14
-rw-r--r--src/quicktemplates2/qquickoverlay.cpp7
-rw-r--r--src/quickwidgets/qquickwidget.cpp17
-rw-r--r--tests/auto/qml/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp21
-rw-r--r--tests/auto/qml/qqmllanguage/data/BindingOverrider.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/SimpleWidget.qml25
-rw-r--r--tests/auto/qml/qqmllanguage/data/badICAnnotation.qml26
-rw-r--r--tests/auto/qml/qqmllanguage/data/typedObjectList.qml13
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp51
-rw-r--r--tests/auto/qml/qqmllistmodel/data/deadModelData.qml45
-rw-r--r--tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp62
-rw-r--r--tests/auto/qml/qv4estable/CMakeLists.txt24
-rw-r--r--tests/auto/qml/qv4estable/tst_qv4estable.cpp40
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp36
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml47
-rw-r--r--tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml52
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp22
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/BLACKLIST2
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp81
-rw-r--r--tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml27
-rw-r--r--tests/auto/quick/qquicktext/tst_qquicktext.cpp12
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_control.qml30
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml28
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp35
-rw-r--r--tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp3
68 files changed, 1428 insertions, 161 deletions
diff --git a/.cmake.conf b/.cmake.conf
index 7733def214..0fcb9b1a08 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -1,2 +1,2 @@
-set(QT_REPO_MODULE_VERSION "6.2.12")
+set(QT_REPO_MODULE_VERSION "6.2.13")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "")
diff --git a/.qmake.conf b/.qmake.conf
index ac797c6928..d10b666e6a 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -5,4 +5,4 @@ DEFINES += QT_NO_JAVA_STYLE_ITERATORS
QQC2_SOURCE_TREE = $$PWD
-MODULE_VERSION = 6.2.12
+MODULE_VERSION = 6.2.13
diff --git a/dependencies.yaml b/dependencies.yaml
index c1eda40e5e..ec29efacb5 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,13 +1,13 @@
dependencies:
../tqtc-qtbase:
- ref: 3a82051eade32b34c2f4f6f652a9d8ef0db96c71
+ ref: a963a536e623499cc56b0231c35dc5790a4bbc29
required: true
../tqtc-qtimageformats:
- ref: 8dd102eea4bf0e6d683f9f8bdfe74c5d63a626c8
+ ref: 282852e3eff8909bfa157d80d1ff7c72fd1662d6
required: false
../tqtc-qtshadertools:
- ref: ebe45c38bffcf8c3487949045f9ecf9c8a5f7e30
+ ref: 4e8c28a3dc8d4dd6e237f7cc4235c078147385a2
required: false
../tqtc-qtsvg:
- ref: 2563e58640a7a85144ed64641f00c7e5edbabb68
+ ref: 0463e412eeadba6dcf1b84b9c655550f93656e7d
required: false
diff --git a/examples/quick/scenegraph/graph/shaders/noisy.frag b/examples/quick/scenegraph/graph/shaders/noisy.frag
index 0b7cb1306b..0f13b83fd4 100644
--- a/examples/quick/scenegraph/graph/shaders/noisy.frag
+++ b/examples/quick/scenegraph/graph/shaders/noisy.frag
@@ -11,7 +11,7 @@ layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
vec4 color;
- vec2 textureSize;
+ vec2 texCoordScale;
float qt_Opacity;
};
diff --git a/examples/quick/scenegraph/graph/shaders/noisy.frag.qsb b/examples/quick/scenegraph/graph/shaders/noisy.frag.qsb
index 1a49e93cf3..dd1739b1fe 100644
--- a/examples/quick/scenegraph/graph/shaders/noisy.frag.qsb
+++ b/examples/quick/scenegraph/graph/shaders/noisy.frag.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/graph/shaders/noisy.vert b/examples/quick/scenegraph/graph/shaders/noisy.vert
index 5728f2a02f..057f1c8d16 100644
--- a/examples/quick/scenegraph/graph/shaders/noisy.vert
+++ b/examples/quick/scenegraph/graph/shaders/noisy.vert
@@ -9,7 +9,7 @@ layout(location = 1) out vec2 vShadeCoord;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
vec4 color;
- vec2 textureSize;
+ vec2 texCoordScale;
float qt_Opacity;
};
@@ -17,6 +17,6 @@ out gl_PerVertex { vec4 gl_Position; };
void main() {
gl_Position = qt_Matrix * aVertex;
- vTexCoord = aVertex.xy * textureSize;
+ vTexCoord = aVertex.xy * texCoordScale;
vShadeCoord = aTexCoord;
}
diff --git a/examples/quick/scenegraph/graph/shaders/noisy.vert.qsb b/examples/quick/scenegraph/graph/shaders/noisy.vert.qsb
index ce2a828ead..9de384e3a6 100644
--- a/examples/quick/scenegraph/graph/shaders/noisy.vert.qsb
+++ b/examples/quick/scenegraph/graph/shaders/noisy.vert.qsb
Binary files differ
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();
}
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt
index 5fe0b0950f..4b2fb9c1e7 100644
--- a/tests/auto/qml/CMakeLists.txt
+++ b/tests/auto/qml/CMakeLists.txt
@@ -120,6 +120,7 @@ if(QT_FEATURE_private_tests)
add_subdirectory(qqmltablemodel)
add_subdirectory(qv4assembler)
add_subdirectory(qv4mm)
+ add_subdirectory(qv4estable)
add_subdirectory(qv4identifiertable)
add_subdirectory(qv4regexp)
if(QT_FEATURE_process AND NOT QNX)
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index c2c6f3f43f..8d2b5179f8 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -293,6 +293,7 @@ private slots:
void spreadNoOverflow();
void deleteDefineCycle();
+ void deleteFromSparseArray();
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
@@ -5837,6 +5838,26 @@ void tst_QJSEngine::deleteDefineCycle()
QVERIFY(stackTrace.isEmpty());
}
+void tst_QJSEngine::deleteFromSparseArray()
+{
+ QJSEngine engine;
+
+ // Should not crash
+ const QJSValue result = engine.evaluate(QLatin1String(R"((function() {
+ let o = [];
+ o[10000] = 10;
+ o[20000] = 20;
+ for (let k in o)
+ delete o[k];
+ return o;
+ })())"));
+
+ QVERIFY(result.isArray());
+ QCOMPARE(result.property("length").toNumber(), 20001);
+ QVERIFY(result.property(10000).isUndefined());
+ QVERIFY(result.property(20000).isUndefined());
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qqmllanguage/data/BindingOverrider.qml b/tests/auto/qml/qqmllanguage/data/BindingOverrider.qml
new file mode 100644
index 0000000000..de7e1e96a3
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/BindingOverrider.qml
@@ -0,0 +1,5 @@
+import QtQml 2.15
+
+SimpleWidget {
+ width: 20
+}
diff --git a/tests/auto/qml/qqmllanguage/data/SimpleWidget.qml b/tests/auto/qml/qqmllanguage/data/SimpleWidget.qml
new file mode 100644
index 0000000000..9150ebaa4e
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/SimpleWidget.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.15
+
+Item {
+ id: outer
+
+ property real innerWidth: 0
+
+ Item {
+ id: inner
+ width: style.width
+ onWidthChanged: outer.innerWidth = width
+ }
+
+ width: inner.width
+
+ onWidthChanged: {
+ if (width !== inner.width)
+ inner.width = width // overwrite binding
+ }
+
+ QtObject {
+ id: style
+ property int width: 50
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml b/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml
new file mode 100644
index 0000000000..78555ac7dc
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml
@@ -0,0 +1,26 @@
+
+import QtQml
+
+QtObject {
+ id: self
+
+ function doStuff(status: Binding.NotAnInlineComponent) : int {
+ return status
+ }
+
+ function doStuff2(status: InlineComponentBase.IC) : QtObject {
+ return status
+ }
+
+ function doStuff3(status: InlineComponentBase.NotIC) : QtObject {
+ return status
+ }
+
+ property InlineComponentBase.IC ic: InlineComponentBase.IC {}
+
+ property int a: doStuff(5)
+ property QtObject b: doStuff2(ic)
+ property QtObject c: doStuff3(ic)
+ property QtObject d: doStuff2(self)
+}
+
diff --git a/tests/auto/qml/qqmllanguage/data/typedObjectList.qml b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml
new file mode 100644
index 0000000000..89c66249cf
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml
@@ -0,0 +1,13 @@
+import QtQml
+
+QtObject {
+ property var b;
+ property Component c: QtObject {}
+
+ // In 6.5 and earlier we don't have heap-managed QQmlListProperty, yet.
+ property list<Component> ll;
+
+ function returnList(a: Component) : list<Component> { ll.push(a); return ll; }
+
+ Component.onCompleted: b = { b: returnList(c) }
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 3d1f023425..95e838476f 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -394,6 +394,11 @@ private slots:
void deepAliasOnICOrReadonly();
void writeNumberToEnumAlias();
+ void badInlineComponentAnnotation();
+
+ void typedObjectList();
+
+ void overrideInnerBinding();
private:
QQmlEngine engine;
@@ -6747,6 +6752,52 @@ void tst_qqmllanguage::writeNumberToEnumAlias()
QCOMPARE(o->property("strokeStyle").toInt(), 1);
}
+void tst_qqmllanguage::badInlineComponentAnnotation()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("badICAnnotation.qml");
+ QQmlComponent c(&engine, testFileUrl("badICAnnotation.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("a").toInt(), 5);
+
+ QObject *ic = o->property("ic").value<QObject *>();
+ QVERIFY(ic);
+
+ QCOMPARE(o->property("b").value<QObject *>(), ic);
+ QCOMPARE(o->property("c").value<QObject *>(), ic);
+}
+
+void tst_qqmllanguage::typedObjectList()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("typedObjectList.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QJSValue b = o->property("b").value<QJSValue>();
+ auto list = qjsvalue_cast<QQmlListProperty<QQmlComponent>>(b.property(QStringLiteral("b")));
+
+ QCOMPARE(list.count(&list), 1);
+ QVERIFY(list.at(&list, 0) != nullptr);
+}
+
+void tst_qqmllanguage::overrideInnerBinding()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("BindingOverrider.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("width").toReal(), 20.0);
+ QCOMPARE(o->property("innerWidth").toReal(), 20.0);
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmllistmodel/data/deadModelData.qml b/tests/auto/qml/qqmllistmodel/data/deadModelData.qml
new file mode 100644
index 0000000000..fc3dd6fa11
--- /dev/null
+++ b/tests/auto/qml/qqmllistmodel/data/deadModelData.qml
@@ -0,0 +1,45 @@
+import QtQml
+
+QtObject {
+ function swapCorpses() {
+ const lhsData = getModelData(lhsButtonListModel);
+ const rhsData = getModelData(rhsButtonListModel);
+
+ lhsButtonListModel.clear();
+ rhsButtonListModel.clear();
+
+ addToModel(lhsButtonListModel, rhsData);
+ addToModel(rhsButtonListModel, lhsData);
+ }
+
+ property ListModel l1: ListModel {
+ id: lhsButtonListModel
+ }
+
+ property ListModel l2: ListModel {
+ id: rhsButtonListModel
+ }
+
+ Component.onCompleted: {
+ lhsButtonListModel.append({ "ident": 1, "buttonText": "B 1"});
+ lhsButtonListModel.append({ "ident": 2, "buttonText": "B 2"});
+ lhsButtonListModel.append({ "ident": 3, "buttonText": "B 3"});
+
+ rhsButtonListModel.append({ "ident": 4, "buttonText": "B 4"});
+ rhsButtonListModel.append({ "ident": 5, "buttonText": "B 5"});
+ rhsButtonListModel.append({ "ident": 6, "buttonText": "B 6"});
+ }
+
+ function getModelData(model) {
+ var dataList = []
+ for (var i = 0; i < model.count; ++i)
+ dataList.push(model.get(i));
+
+ return dataList;
+ }
+
+ function addToModel(model, buttonData) {
+ for (var i = 0; i < buttonData.length; ++i)
+ model.append(buttonData[i]);
+ }
+}
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
index fbdf6d90f3..f440eab6b7 100644
--- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
+++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
@@ -140,6 +140,7 @@ private slots:
void destroyComponentObject();
void objectOwnershipFlip();
void protectQObjectFromGC();
+ void deadModelData();
};
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
@@ -1940,6 +1941,67 @@ void tst_qqmllistmodel::protectQObjectFromGC()
}
}
+void tst_qqmllistmodel::deadModelData()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("deadModelData.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+
+ QQmlListModel *l1 = o->property("l1").value<QQmlListModel *>();
+ QVERIFY(l1);
+ QQmlListModel *l2 = o->property("l2").value<QQmlListModel *>();
+ QVERIFY(l2);
+
+ QCOMPARE(l1->count(), 3);
+ QCOMPARE(l2->count(), 3);
+
+ for (int i = 0; i < 3; ++i) {
+ QObject *i1 = qjsvalue_cast<QObject *>(l1->get(i));
+ QVERIFY(i1);
+ QCOMPARE(i1->property("ident").value<double>(), i + 1);
+ QCOMPARE(i1->property("buttonText").value<QString>(),
+ QLatin1String("B %1").arg(QLatin1Char('0' + i + 1)));
+
+ QObject *i2 = qjsvalue_cast<QObject *>(l2->get(i));
+ QVERIFY(i2);
+ QCOMPARE(i2->property("ident").value<double>(), i + 4);
+ QCOMPARE(i2->property("buttonText").value<QString>(),
+ QLatin1String("B %1").arg(QLatin1Char('0' + i + 4)));
+ }
+
+ for (int i = 0; i < 6; ++i) {
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ QRegularExpression(".*: ident is undefined. Adding an object with a undefined "
+ "member does not create a role for it."));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ QRegularExpression(".*: buttonText is undefined. Adding an object with a undefined "
+ "member does not create a role for it."));
+ }
+
+ QMetaObject::invokeMethod(o.data(), "swapCorpses");
+
+ // We get default-created values for all the roles now.
+
+ QCOMPARE(l1->count(), 3);
+ QCOMPARE(l2->count(), 3);
+
+ for (int i = 0; i < 3; ++i) {
+ QObject *i1 = qjsvalue_cast<QObject *>(l1->get(i));
+ QVERIFY(i1);
+ QCOMPARE(i1->property("ident").value<double>(), double());
+ QCOMPARE(i1->property("buttonText").value<QString>(), QString());
+
+ QObject *i2 = qjsvalue_cast<QObject *>(l2->get(i));
+ QVERIFY(i2);
+ QCOMPARE(i2->property("ident").value<double>(), double());
+ QCOMPARE(i2->property("buttonText").value<QString>(), QString());
+ }
+}
+
QTEST_MAIN(tst_qqmllistmodel)
#include "tst_qqmllistmodel.moc"
diff --git a/tests/auto/qml/qv4estable/CMakeLists.txt b/tests/auto/qml/qv4estable/CMakeLists.txt
new file mode 100644
index 0000000000..01d2663a04
--- /dev/null
+++ b/tests/auto/qml/qv4estable/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qv4estable Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qv4estable LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qv4estable
+ SOURCES
+ tst_qv4estable.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::QmlPrivate
+)
+
+## Scopes:
+#####################################################################
diff --git a/tests/auto/qml/qv4estable/tst_qv4estable.cpp b/tests/auto/qml/qv4estable/tst_qv4estable.cpp
new file mode 100644
index 0000000000..eecb672bc6
--- /dev/null
+++ b/tests/auto/qml/qv4estable/tst_qv4estable.cpp
@@ -0,0 +1,40 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <qtest.h>
+#include <private/qv4estable_p.h>
+
+class tst_qv4estable : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void checkRemoveAvoidsHeapBufferOverflow();
+};
+
+// QTBUG-123999
+void tst_qv4estable::checkRemoveAvoidsHeapBufferOverflow()
+{
+ QV4::ESTable estable;
+
+ // Fill the ESTable with values so it is at max capacity.
+ QCOMPARE(estable.m_capacity, 8u);
+ for (uint i = 0; i < estable.m_capacity; ++i) {
+ estable.set(QV4::Value::fromUInt32(i), QV4::Value::fromUInt32(i));
+ }
+ // Our |m_keys| array should now contain eight values.
+ // > [v0, v1, v2, v3, v4, v5, v6, v7]
+ for (uint i = 0; i < estable.m_capacity; ++i) {
+ QVERIFY(estable.m_keys[i].sameValueZero(QV4::Value::fromUInt32(i)));
+ }
+ QCOMPARE(estable.m_capacity, 8u);
+ QCOMPARE(estable.m_size, 8u);
+
+ // Remove the first item from the set to verify that asan does not trip.
+ // Relies on the CI platform propagating asan flag to all tests.
+ estable.remove(QV4::Value::fromUInt32(0));
+}
+
+QTEST_MAIN(tst_qv4estable)
+
+#include "tst_qv4estable.moc"
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index 30b934eeec..579eeb7c0a 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -119,6 +119,7 @@ private slots:
void cleanupWhenRenderThreadStops();
void infiniteLoopsWithoutFrom();
void targetsDeletedNotRemoved();
+ void alwaysRunToEndSetFalseRestartBug();
};
#define QTIMED_COMPARE(lhs, rhs) do { \
for (int ii = 0; ii < 5; ++ii) { \
@@ -2081,6 +2082,41 @@ void tst_qquickanimations::targetsDeletedNotRemoved()
}
}
+//QTBUG-125224
+void tst_qquickanimations::alwaysRunToEndSetFalseRestartBug()
+{
+ QQuickRectangle rect;
+ QQuickSequentialAnimation sequential;
+ QQuickPropertyAnimation beginAnim;
+ QQuickPropertyAnimation endAnim;
+
+ beginAnim.setTargetObject(&rect);
+ beginAnim.setProperty("x");
+ beginAnim.setTo(200);
+ beginAnim.setDuration(1000);
+
+ endAnim.setTargetObject(&rect);
+ endAnim.setProperty("x");
+ endAnim.setFrom(200);
+ endAnim.setDuration(1000);
+
+ beginAnim.setGroup(&sequential);
+ endAnim.setGroup(&sequential);
+
+ sequential.setLoops(-1);
+ sequential.setAlwaysRunToEnd(true);
+
+ QCOMPARE(sequential.loops(), -1);
+ QVERIFY(sequential.alwaysRunToEnd());
+ sequential.start();
+ sequential.stop();
+ sequential.setAlwaysRunToEnd(false);
+ sequential.start();
+ QCOMPARE(sequential.isRunning(), true);
+ sequential.stop();
+ QCOMPARE(sequential.isRunning(), false);
+}
+
QTEST_MAIN(tst_qquickanimations)
#include "tst_qquickanimations.moc"
diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
index 5e8f6f9bcf..37166af119 100644
--- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
+++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
@@ -1308,6 +1308,53 @@ Item {
compare(rootRect.item1.width, 100)
}
+ //---------------------------
+ // Layout with negative size
+ Component {
+ id: negativeSize_Component
+ Item {
+ id: rootItem
+ width: 0
+ height: 0
+ // default width x height: (0 x 0)
+ RowLayout {
+ spacing: 0
+ anchors.fill: parent
+ anchors.leftMargin: 1 // since parent size == (0 x 0), it causes layout size
+ anchors.bottomMargin: 1 // to become (-1, -1)
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ }
+ }
+
+ function test_negativeSize() {
+ let rootItem = createTemporaryObject(negativeSize_Component, container)
+ let rowLayout = rootItem.children[0]
+ let item = rowLayout.children[0]
+
+ // 1 doesn't work in 6.2 because that will make the layout of size 0x0
+ // Setting the geometry of a layout to 0x0 doesn't work because
+ // QQuickLayout::geometryChange() early returns if !newGeometry.isValid()
+ // This check has been later removed.
+ const arr = [7, /*1,*/ 7, 0]
+ arr.forEach((n) => {
+ rootItem.width = n
+ rootItem.height = n
+
+ // n === 0 is special: It will cause the layout to have a
+ // negative size. In this case it will simply not rearrange its
+ // child (and leave it at its previous size, 6)
+ const expectedItemExtent = n === 0 ? 6 : n - 1
+
+ compare(item.width, expectedItemExtent)
+ compare(item.height, expectedItemExtent)
+ });
+ }
+
+
//---------------------------
Component {
id: rowlayoutWithTextItems_Component
diff --git a/tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml b/tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml
new file mode 100644
index 0000000000..0d4c233345
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml
@@ -0,0 +1,52 @@
+import QtQuick
+
+ListView {
+ id: listView
+ width: 240
+ height: 300
+
+ model: ListModel {
+ ListElement { section: "section 1" }
+ ListElement { section: "section 1" }
+ ListElement { section: "section 1" }
+ ListElement { section: "section 2" }
+ ListElement { section: "section 2" }
+ ListElement { section: "section 2" }
+ ListElement { section: "section 3" }
+ ListElement { section: "section 3" }
+ ListElement { section: "section 3" }
+ ListElement { section: "section 4" }
+ ListElement { section: "section 4" }
+ ListElement { section: "section 4" }
+ ListElement { section: "section 5" }
+ ListElement { section: "section 5" }
+ ListElement { section: "section 5" }
+ }
+
+ section.property: "section"
+ section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
+ section.delegate: Rectangle {
+ width: listView.width
+ height: 30
+ Text {
+ anchors.fill: parent
+ text: section
+ }
+ color: "lightblue"
+ }
+
+ snapMode: ListView.SnapToItem
+
+ delegate: Rectangle {
+ width: listView.width
+ height: 30
+ Text {
+ anchors.fill: parent
+ text: index
+ }
+ border {
+ width: 1
+ color: "black"
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index 24196e2ba1..8f3aa551e1 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -193,6 +193,7 @@ private slots:
void headerSnapToItem_data();
void headerSnapToItem();
void snapToItemWithSpacing_QTBUG_59852();
+ void snapToItemWithSectionAtStart();
void snapOneItemResize_QTBUG_43555();
void snapOneItem_data();
void snapOneItem();
@@ -5407,6 +5408,27 @@ void tst_QQuickListView::snapToItemWithSpacing_QTBUG_59852()
releaseView(window);
}
+void tst_QQuickListView::snapToItemWithSectionAtStart() // QTBUG-30768
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("snapToItemWithSectionAtStart.qml")));
+ QQuickListView *listView = qobject_cast<QQuickListView *>(window.rootObject());
+ QTRY_VERIFY(listView);
+
+ // Both sections and elements are 30px high. The list height is 300px, so
+ // it fits exactly 10 elements. We can do some random flicks, but the
+ // content position always MUST be divisible by 30.
+ for (int i = 0; i < 10; ++i) {
+ const bool even = (i % 2 == 0);
+ const QPoint start = even ? QPoint(20, 100 + i * 5) : QPoint(20, 20 + i * 3);
+ const QPoint end = even ? start - QPoint(0, 50 + i * 10) : start + QPoint(0, 50 + i * 5);
+
+ flick(&window, start, end, 180);
+ QTRY_COMPARE(listView->isMoving(), false); // wait until it stops
+ QCOMPARE(int(listView->contentY()) % 30, 0);
+ }
+}
+
static void drag_helper(QWindow *window, QPoint *startPos, const QPoint &delta)
{
QPoint pos = *startPos;
diff --git a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
index 2488eff270..46db6477f5 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
+++ b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
@@ -4,3 +4,5 @@ ubuntu-22.04
[nested]
ubuntu-20.04
ubuntu-22.04
+[inFlickable]
+* # QTBUG-127628
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index 5991621376..5330d8d01f 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -185,6 +185,8 @@ private slots:
void checkSyncView_pageFlicking();
void checkSyncView_emptyModel();
void checkSyncView_topLeftChanged();
+ void checkSyncView_dontRelayoutWhileFlicking();
+ void checkSyncView_detectTopLeftPositionChanged();
void delegateWithRequiredProperties();
void checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable();
void replaceModel();
@@ -3108,6 +3110,85 @@ void tst_QQuickTableView::checkSyncView_topLeftChanged()
QCOMPARE(tableViewV->topRow(), tableView->topRow());
}
+void tst_QQuickTableView::checkSyncView_dontRelayoutWhileFlicking()
+{
+ // Check that we don't do a full relayout in a sync child when
+ // a new row or column is flicked into the view. Normal load
+ // and unload of edges should suffice, equal to how the main
+ // TableView (syncView) does it.
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewHV);
+
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+ tableViewHV->setModel(model);
+
+ tableView->setColumnWidthProvider(QJSValue());
+ tableView->setRowHeightProvider(QJSValue());
+ view->rootObject()->setProperty("delegateWidth", 50);
+ view->rootObject()->setProperty("delegateHeight", 50);
+
+ WAIT_UNTIL_POLISHED;
+
+ // To check that we don't do a relayout when flicking horizontally, we use a "trick"
+ // where we check the rebuildOptions when we receive the rightColumnChanged
+ // signal. If this signal is emitted as a part of a relayout, rebuildOptions
+ // would still be different from RebuildOption::None at that point.
+ bool columnFlickedIn = false;
+ connect(tableViewHV, &QQuickTableView::rightColumnChanged, [&] {
+ columnFlickedIn = true;
+ QCOMPARE(tableViewHVPrivate->rebuildOptions, QQuickTableViewPrivate::RebuildOption::None);
+ });
+
+ // We do the same for vertical flicking
+ bool rowFlickedIn = false;
+ connect(tableViewHV, &QQuickTableView::bottomRowChanged, [&] {
+ rowFlickedIn = true;
+ QCOMPARE(tableViewHVPrivate->rebuildOptions, QQuickTableViewPrivate::RebuildOption::None);
+ });
+
+ // Move the main tableview so that a new column is flicked in
+ tableView->setContentX(60);
+ QTRY_VERIFY(columnFlickedIn);
+
+ // Move the main tableview so that a new row is flicked in
+ tableView->setContentY(60);
+ QTRY_VERIFY(rowFlickedIn);
+}
+
+void tst_QQuickTableView::checkSyncView_detectTopLeftPositionChanged()
+{
+ // It can happen that, during a resize of columns or rows from using a float-based
+ // slider, that the position of the top-left delegate item is shifted a bit left or
+ // right because of rounding issues. And this again can over time, as you flick, make
+ // the loadedTableOuterRect get slightly out of sync in the sync child compared to the
+ // sync view. TableView will detect if this happens (in syncSyncView), and correct for
+ // it. And this test will test that it works.
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewHV);
+
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+ tableViewHV->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Writing an auto test to trigger this rounding issue is very hard. So to keep it
+ // simple, we cheat by just moving the loadedTableOuterRect directly, and
+ // check that the syncView child detects it, and corrects it, upon doing a
+ // forceLayout()
+ tableViewPrivate->loadedTableOuterRect.moveLeft(20);
+ tableViewPrivate->loadedTableOuterRect.moveTop(30);
+ tableViewPrivate->relayoutTableItems();
+ tableViewHV->forceLayout();
+
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), 20);
+ QCOMPARE(tableViewHVPrivate->loadedTableOuterRect.left(), 20);
+
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), 30);
+ QCOMPARE(tableViewHVPrivate->loadedTableOuterRect.top(), 30);
+}
+
void tst_QQuickTableView::checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable()
{
LOAD_TABLEVIEW("plaintableview.qml");
diff --git a/tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml b/tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml
new file mode 100644
index 0000000000..bb269e6ad5
--- /dev/null
+++ b/tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml
@@ -0,0 +1,27 @@
+import QtQuick
+
+Item {
+ id: root
+ property bool ok: false
+ width: 640
+ height: 480
+
+ Text {
+ id: text
+ text: "This is a quite long text. Click me and i should remain visible!!! Sadly this doesn't happen"
+ elide: Text.ElideRight
+ anchors {
+ fill: parent
+ margins: 1
+ }
+ }
+
+ Component.onCompleted: {
+ text.width = 300;
+ text.height = 0;
+ text.width = 0;
+ text.height = 30;
+ text.width = 300;
+ root.ok = text.paintedWidth > 0 && text.paintedHeight > 0
+ }
+}
diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
index 476297918d..ea90fc74d6 100644
--- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp
+++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
@@ -69,6 +69,7 @@ private slots:
void wrap();
void elide();
void elideParentChanged();
+ void elideRelayoutAfterZeroWidth_data();
void elideRelayoutAfterZeroWidth();
void multilineElide_data();
void multilineElide();
@@ -611,10 +612,19 @@ void tst_qquicktext::elideParentChanged()
QCOMPARE(actualItemImageGrab, expectedItemImageGrab);
}
+void tst_qquicktext::elideRelayoutAfterZeroWidth_data()
+{
+ QTest::addColumn<QByteArray>("fileName");
+
+ QTest::newRow("no_margins") << QByteArray("elideZeroWidth.qml");
+ QTest::newRow("with_margins") << QByteArray("elideZeroWidthWithMargins.qml");
+}
+
void tst_qquicktext::elideRelayoutAfterZeroWidth()
{
+ QFETCH(const QByteArray, fileName);
QQmlEngine engine;
- QQmlComponent component(&engine, testFileUrl("elideZeroWidth.qml"));
+ QQmlComponent component(&engine, testFileUrl(fileName.constData()));
QScopedPointer<QObject> root(component.create());
QVERIFY2(root, qPrintable(component.errorString()));
QVERIFY(root->property("ok").toBool());
diff --git a/tests/auto/quickcontrols2/controls/data/tst_control.qml b/tests/auto/quickcontrols2/controls/data/tst_control.qml
index e12cbbf4ef..5103d9afd4 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_control.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_control.qml
@@ -503,6 +503,36 @@ TestCase {
}
Component {
+ id: backgroundTest2
+ Button {
+ id: btn
+ width: 100
+ height: 100
+ topInset: 0
+ objectName: ""
+
+ background: Rectangle {
+ id: bg
+ implicitHeight: 80
+ border.color: "red"
+ y: btn.objectName === "aaa" ? 20 : 0
+ }
+ }
+ }
+
+ // QTBUG-120033: Make sure that the binding for y on the tab button's background doesn't get removed
+ function test_background2() {
+ let button = createTemporaryObject(backgroundTest2, testCase)
+ verify(button)
+
+ verify(button.background.y === 0)
+ button.objectName = "aaa"
+ verify(button.background.y === 20)
+ button.objectName = ""
+ verify(button.background.y === 0)
+ }
+
+ Component {
id: component2
T.Control {
id: item2
diff --git a/tests/auto/quickcontrols2/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml b/tests/auto/quickcontrols2/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml
new file mode 100644
index 0000000000..a7e66718ee
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml
@@ -0,0 +1,28 @@
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: root
+ width: 100
+ height: 100
+ property alias controlsPopup: _controlsPopup
+ property alias blockInputPopup: _blockInputPopup
+ Popup {
+ id: _controlsPopup
+ width: parent.width
+ height: parent.height
+ modal: true
+ Control {
+ id: controls
+ anchors.fill: parent
+ hoverEnabled: true
+ contentItem: Text { text: "Test Control" }
+ }
+ }
+ Popup {
+ id: _blockInputPopup
+ width: parent.width
+ height: parent.height
+ modal: true
+ }
+}
diff --git a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
index a587c085a4..2895ac4a83 100644
--- a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
@@ -121,6 +121,7 @@ private slots:
void shrinkPopupThatWasLargerThanWindow();
void mirroredCombobox();
void rotatedCombobox();
+ void resetHoveredStateForItemsWithinPopup();
private:
static bool hasWindowActivation();
@@ -2038,6 +2039,40 @@ void tst_QQuickPopup::rotatedCombobox()
}
}
+void tst_QQuickPopup::resetHoveredStateForItemsWithinPopup()
+{
+ QQuickControlsApplicationHelper helper(this, "resetHoveredForItemsWithinOverlay.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ QQuickPopup *controlsPopup = window->property("controlsPopup").value<QQuickPopup*>();
+ QVERIFY(controlsPopup);
+
+ QQuickPopup *blockInputPopup = window->property("blockInputPopup").value<QQuickPopup*>();
+ QVERIFY(controlsPopup);
+
+ controlsPopup->open();
+ QTRY_VERIFY(controlsPopup->isOpened());
+
+ QTest::mouseMove(window, QPoint(window->width() / 2 + 2, window->height() / 2 + 2));
+ QTest::mouseMove(window, QPoint(window->width() / 2, window->height() / 2));
+
+ auto *controlItem = qobject_cast<QQuickControl *>(controlsPopup->contentItem()->childItems().at(0));
+ QVERIFY(controlItem);
+ // Check hover enabled for the control item within the popup
+ QTRY_VERIFY(controlItem->isHovered());
+
+ // Open the modal popup window over the existing control item
+ blockInputPopup->open();
+ QTRY_VERIFY(blockInputPopup->isOpened());
+
+ // Control item hovered shall be disabled once we open the modal popup
+ QTRY_VERIFY(!controlItem->isHovered());
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup)
#include "tst_qquickpopup.moc"
diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
index 54673b70ce..f0bfd3c79c 100644
--- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
+++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
@@ -711,6 +711,9 @@ void tst_qquickwidget::touchMultipleWidgets()
QWidget window;
QQuickWidget *leftQuick = new QQuickWidget;
leftQuick->setSource(testFileUrl("button.qml"));
+ if (!leftQuick->testAttribute(Qt::WA_AcceptTouchEvents))
+ QSKIP("irrelevant on non-touch platforms");
+
QQuickWidget *rightQuick = new QQuickWidget;
rightQuick->setSource(testFileUrl("button.qml"));