aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarja Sundqvist <tarja.sundqvist@qt.io>2022-08-16 20:37:22 +0300
committerTarja Sundqvist <tarja.sundqvist@qt.io>2022-08-16 20:37:22 +0300
commitf274d775774b78f6217c9770ae87045d969acbe1 (patch)
treed14162d5a1badabc71f6034bd4013b521d8f52e9
parentae66ecf0f95c79d730190b92e641c0410d5d6896 (diff)
parenteaf011c4ae440bbf7a02ceb7a33be5dfa5bcf907 (diff)
Merge remote-tracking branch 'origin/tqtc/lts-5.15.6' into tqtc/lts-5.15-opensourcev5.15.6-lts-lgpl
-rw-r--r--.qmake.conf2
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp8
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp20
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h5
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp38
-rw-r--r--src/qml/jsruntime/qv4generatorobject_p.h1
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp11
-rw-r--r--src/qml/qml/qqmlbinding.cpp9
-rw-r--r--src/qml/qml/qqmlmetaobject.cpp23
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp118
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h10
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h4
-rw-r--r--src/qml/qml/qqmlpropertycachemethodarguments_p.h2
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h105
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp13
-rw-r--r--src/quick/accessible/qaccessiblequickitem.cpp3
-rw-r--r--src/quick/designer/qquickdesignersupportproperties.cpp35
-rw-r--r--src/quick/designer/qquickdesignersupportproperties_p.h7
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml73
-rw-r--r--src/quick/doc/snippets/qml/externaldrag.qml20
-rw-r--r--src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc64
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc67
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp16
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp27
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp13
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp5
-rw-r--r--src/quick/items/qquickdroparea.cpp1
-rw-r--r--src/quick/items/qquickimage.cpp6
-rw-r--r--src/quick/items/qquickimagebase.cpp8
-rw-r--r--src/quick/items/qquickitem.cpp54
-rw-r--r--src/quick/items/qquickstateoperations.cpp2
-rw-r--r--src/quick/items/qquicktextinput.cpp8
-rw-r--r--src/quick/items/qquicktextinput_p.h2
-rw-r--r--src/quick/items/qquickwindow.cpp12
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp15
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp13
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop.cpp3
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml21
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml31
-rw-r--r--tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp22
-rw-r--r--tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml13
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp10
-rw-r--r--tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/qtbug_89822.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp7
-rw-r--r--tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml54
-rw-r--r--tests/auto/quick/nodes/tst_nodestest.cpp4
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml34
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp54
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp4
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_line.qml31
-rw-r--r--tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml8
-rw-r--r--tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml13
-rw-r--r--tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp45
-rw-r--r--tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml51
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp31
-rw-r--r--tests/auto/quick/qquicktextinput/data/focusReason.qml39
-rw-r--r--tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp80
-rw-r--r--tests/manual/pointer/pinchHandler.qml8
59 files changed, 1177 insertions, 215 deletions
diff --git a/.qmake.conf b/.qmake.conf
index a72382f252..e46219506c 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -4,4 +4,4 @@ CONFIG += warning_clean
DEFINES += QT_NO_LINKED_LIST
DEFINES += QT_NO_JAVA_STYLE_ITERATORS
-MODULE_VERSION = 5.15.5
+MODULE_VERSION = 5.15.6
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
index 5e78539155..e11b8c9776 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
@@ -398,6 +398,14 @@ bool QQmlPreviewFileEngine::supportsExtension(Extension extension) const
void QQmlPreviewFileEngine::load() const
{
+ // We can get here from different threads on different instances of QQmlPreviewFileEngine.
+ // However, there is only one loader per QQmlPreviewFileEngineHandler and it is not thread-safe.
+ // Its content mutex doesn't help us here because we explicitly wait on it in load(), which
+ // causes it to be released. Therefore, lock the load mutex first.
+ // This doesn't cause any deadlocks because the only thread that wakes the loader on the content
+ // mutex never calls load(). It's the QML debug server thread that handles the debug protocol.
+ QMutexLocker loadLocker(m_loader->loadMutex());
+
m_result = m_loader->load(m_absolute);
switch (m_result) {
case QQmlPreviewFileLoader::File:
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
index bb43f75c63..8d8a8f18d2 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
@@ -101,7 +101,7 @@ QQmlPreviewFileLoader::~QQmlPreviewFileLoader() {
QQmlPreviewFileLoader::Result QQmlPreviewFileLoader::load(const QString &path)
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_path = path;
auto fileIterator = m_fileCache.constFind(path);
@@ -124,19 +124,19 @@ QQmlPreviewFileLoader::Result QQmlPreviewFileLoader::load(const QString &path)
m_entries.clear();
m_contents.clear();
emit request(path);
- m_waitCondition.wait(&m_mutex);
+ m_waitCondition.wait(&m_contentMutex);
return m_result;
}
QByteArray QQmlPreviewFileLoader::contents()
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
return m_contents;
}
QStringList QQmlPreviewFileLoader::entries()
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
return m_entries;
}
@@ -144,20 +144,20 @@ void QQmlPreviewFileLoader::whitelist(const QUrl &url)
{
const QString path = QQmlFile::urlToLocalFileOrQrc(url);
if (!path.isEmpty()) {
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_blacklist.whitelist(path);
}
}
bool QQmlPreviewFileLoader::isBlacklisted(const QString &path)
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
return m_blacklist.isBlacklisted(path);
}
void QQmlPreviewFileLoader::file(const QString &path, const QByteArray &contents)
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_blacklist.whitelist(path);
m_fileCache[path] = contents;
if (path == m_path) {
@@ -169,7 +169,7 @@ void QQmlPreviewFileLoader::file(const QString &path, const QByteArray &contents
void QQmlPreviewFileLoader::directory(const QString &path, const QStringList &entries)
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_blacklist.whitelist(path);
m_directoryCache[path] = entries;
if (path == m_path) {
@@ -181,7 +181,7 @@ void QQmlPreviewFileLoader::directory(const QString &path, const QStringList &en
void QQmlPreviewFileLoader::error(const QString &path)
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_blacklist.blacklist(path);
if (path == m_path) {
m_result = Fallback;
@@ -191,7 +191,7 @@ void QQmlPreviewFileLoader::error(const QString &path)
void QQmlPreviewFileLoader::clearCache()
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_fileCache.clear();
m_directoryCache.clear();
}
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h
index 0c55c48c4a..ffda9c0dbf 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h
@@ -79,7 +79,9 @@ public:
QQmlPreviewFileLoader(QQmlPreviewServiceImpl *service);
~QQmlPreviewFileLoader();
+ QMutex *loadMutex() { return &m_loadMutex; }
Result load(const QString &file);
+
QByteArray contents();
QStringList entries();
@@ -90,7 +92,8 @@ signals:
void request(const QString &file);
private:
- QMutex m_mutex;
+ QMutex m_loadMutex;
+ QMutex m_contentMutex;
QWaitCondition m_waitCondition;
QThread m_thread;
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index 4e445a73fa..a14c7d9257 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -78,10 +78,21 @@
QTBUG-75585 for an explanation and possible workarounds.
*/
#define USE_QTZ_SYSTEM_ZONE
+#elif defined(Q_OS_WASM)
+/*
+ TODO: evaluate using this version of the code more generally, rather than
+ the #else branches of the various USE_QTZ_SYSTEM_ZONE choices. It might even
+ work better than the timezone variant; experiments needed.
+*/
+// Kludge around the lack of time-zone info using QDateTime.
+// It uses localtime() and friends to determine offsets from UTC.
+#define USE_QDT_LOCAL_TIME
#endif
#ifdef USE_QTZ_SYSTEM_ZONE
#include <QtCore/QTimeZone>
+#elif defined(USE_QDT_LOCAL_TIME)
+// QDateTime already included above
#else
# ifdef Q_OS_WIN
# include <windows.h>
@@ -356,6 +367,7 @@ static inline double MakeDate(double day, double time)
mean a whole day of DST offset for some zones, that have crossed the
international date line. This shall confuse client code.) The bug report
against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725
+ and they've now changed the spec so that the following conforms to it ;^>
*/
static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time
@@ -363,6 +375,12 @@ static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC t
return QTimeZone::systemTimeZone().offsetFromUtc(
QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA;
}
+#elif defined(USE_QDT_LOCAL_TIME)
+static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time
+{
+ return QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC
+ ).toLocalTime().offsetFromUtc() * 1e3 - localTZA;
+}
#else
// This implementation fails to take account of past changes in standard offset.
static inline double DaylightSavingTA(double t, double /*localTZA*/)
@@ -721,6 +739,26 @@ static double getLocalTZA()
// TODO: QTimeZone::resetSystemTimeZone(), see QTBUG-56899 and comment above.
// Standard offset, with no daylight-savings adjustment, in ms:
return QTimeZone::systemTimeZone().standardTimeOffset(QDateTime::currentDateTime()) * 1e3;
+#elif defined(USE_QDT_LOCAL_TIME)
+ QDate today = QDate::currentDate();
+ QDateTime near = today.startOfDay(Qt::LocalTime);
+ // Early out if we're in standard time anyway:
+ if (!near.isDaylightTime())
+ return near.offsetFromUtc() * 1000;
+ int year, month;
+ today.getDate(&year, &month, nullptr);
+ // One of the solstices is probably in standard time:
+ QDate summer(year, 6, 21), winter(year - (month < 7 ? 1 : 0), 12, 21);
+ // But check the one closest to the present by preference, in case there's a
+ // standard time offset change between them:
+ QDateTime far = summer.startOfDay(Qt::LocalTime);
+ near = winter.startOfDay(Qt::LocalTime);
+ if (month > 3 && month < 10)
+ near.swap(far);
+ bool isDst = near.isDaylightTime();
+ if (isDst && far.isDaylightTime()) // Permanent DST, probably an hour west:
+ return (qMin(near.offsetFromUtc(), far.offsetFromUtc()) - 3600) * 1000;
+ return (isDst ? far : near).offsetFromUtc() * 1000;
#else
# ifdef Q_OS_WIN
TIME_ZONE_INFORMATION tzInfo;
diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h
index 10eea5e46b..14368f5416 100644
--- a/src/qml/jsruntime/qv4generatorobject_p.h
+++ b/src/qml/jsruntime/qv4generatorobject_p.h
@@ -87,7 +87,6 @@ struct GeneratorPrototype : FunctionObject {
#define GeneratorObjectMembers(class, Member) \
Member(class, Pointer, ExecutionContext *, context) \
- Member(class, Pointer, GeneratorFunction *, function) \
Member(class, NoMark, GeneratorState, state) \
Member(class, NoMark, CppStackFrame, cppFrame) \
Member(class, Pointer, ArrayObject *, values) \
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index aff8844bb0..f84718b48f 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -720,13 +720,20 @@ DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealList);
}
#define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType<SequenceType>(#SequenceType);
-void SequencePrototype::init()
+static bool registerAllSequenceTypes()
{
FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE)
+ return true;
+}
+#undef REGISTER_QML_SEQUENCE_METATYPE
+
+void SequencePrototype::init()
+{
+ static const bool registered = registerAllSequenceTypes();
+ Q_UNUSED(registered);
defineDefaultProperty(QStringLiteral("sort"), method_sort, 1);
defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0);
}
-#undef REGISTER_QML_SEQUENCE_METATYPE
ReturnedValue SequencePrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int)
{
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index b9566d5862..194f7b4cf7 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -645,7 +645,10 @@ void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyD
Q_ASSERT(valueTypeMetaObject);
QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex());
valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp));
+
+ // valueTypeData is expected to be local here. It must not be shared with other threads.
valueTypeData->setPropType(vtProp.userType());
+
valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex());
}
}
@@ -748,7 +751,11 @@ QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlProper
if (property && property->isQObject())
return new QObjectPointerBinding(engine, property->propType());
- const int type = (property && property->isFullyResolved()) ? property->propType() : QMetaType::UnknownType;
+ // If the property is not resolved at this point, you get a binding of unknown type.
+ // This has been the case for a long time and we keep it like this in Qt5 to be bug-compatible.
+ const int type = (property && property->isResolved())
+ ? property->propType()
+ : QMetaType::UnknownType;
if (type == qMetaTypeId<QQmlBinding *>()) {
return new QQmlBindingBinding;
diff --git a/src/qml/qml/qqmlmetaobject.cpp b/src/qml/qml/qqmlmetaobject.cpp
index a967f46b12..d273849ccb 100644
--- a/src/qml/qml/qqmlmetaobject.cpp
+++ b/src/qml/qml/qqmlmetaobject.cpp
@@ -232,8 +232,6 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
Q_ASSERT(!_m.isNull() && index >= 0);
if (_m.isT1()) {
- typedef QQmlPropertyCacheMethodArguments A;
-
QQmlPropertyCache *c = _m.asT1();
Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
@@ -242,19 +240,16 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
- if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid)
- return static_cast<A *>(rv->arguments())->arguments;
+ if (QQmlPropertyCacheMethodArguments *args = rv->arguments())
+ return args->arguments;
const QMetaObject *metaObject = c->createMetaObject();
Q_ASSERT(metaObject);
QMetaMethod m = metaObject->method(index);
int argc = m.parameterCount();
- if (!rv->arguments()) {
- A *args = c->createArgumentsObject(argc, m.parameterNames());
- rv->setArguments(args);
- }
- A *args = static_cast<A *>(rv->arguments());
+
+ QQmlPropertyCacheMethodArguments *args = c->createArgumentsObject(argc, m.parameterNames());
QList<QByteArray> argTypeNames; // Only loaded if needed
@@ -280,8 +275,14 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
}
args->arguments[ii + 1] = type;
}
- args->argumentsValid = true;
- return static_cast<A *>(rv->arguments())->arguments;
+
+ // If we cannot set it, then another thread has set it in the mean time.
+ // Just return that one, then. We don't have to delete the arguments object
+ // we've created as it's tracked in a linked list.
+ if (rv->setArguments(args))
+ return args->arguments;
+ else
+ return rv->arguments()->arguments;
} else {
QMetaMethod m = _m.asT2()->method(index);
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 0911c06cd0..6b68a2a288 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -138,17 +138,17 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p)
{
populate(this, p);
int type = static_cast<int>(p.userType());
- if (type == QMetaType::QObjectStar) {
- setPropType(type);
+
+ if (type >= QMetaType::User || type == 0)
+ return; // Resolve later
+
+ if (type == QMetaType::QObjectStar)
m_flags.type = Flags::QObjectDerivedType;
- } else if (type == QMetaType::QVariant) {
- setPropType(type);
+ else if (type == QMetaType::QVariant)
m_flags.type = Flags::QVariantType;
- } else if (type >= QMetaType::User || type == 0) {
- m_flags.notFullyResolved = true;
- } else {
- setPropType(type);
- }
+
+ // This is OK because lazyLoad is done before exposing the property data.
+ setPropType(type);
}
void QQmlPropertyData::load(const QMetaProperty &p)
@@ -158,45 +158,48 @@ void QQmlPropertyData::load(const QMetaProperty &p)
flagsForPropertyType(propType(), m_flags);
}
-void QQmlPropertyData::load(const QMetaMethod &m)
+static void populate(QQmlPropertyData *data, const QMetaMethod &m)
{
- setCoreIndex(m.methodIndex());
- setArguments(nullptr);
+ data->setCoreIndex(m.methodIndex());
- setPropType(m.returnType());
-
- m_flags.type = Flags::FunctionType;
- if (m.methodType() == QMetaMethod::Signal) {
- m_flags.setIsSignal(true);
- } else if (m.methodType() == QMetaMethod::Constructor) {
- m_flags.setIsConstructor(true);
- setPropType(QMetaType::QObjectStar);
- }
+ QQmlPropertyData::Flags flags = data->flags();
+ flags.type = QQmlPropertyData::Flags::FunctionType;
+ if (m.methodType() == QMetaMethod::Signal)
+ flags.setIsSignal(true);
+ else if (m.methodType() == QMetaMethod::Constructor)
+ flags.setIsConstructor(true);
const int paramCount = m.parameterCount();
if (paramCount) {
- m_flags.setHasArguments(true);
+ flags.setHasArguments(true);
if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*"))
- m_flags.setIsV4Function(true);
+ flags.setIsV4Function(true);
}
if (m.attributes() & QMetaMethod::Cloned)
- m_flags.setIsCloned(true);
+ flags.setIsCloned(true);
+
+ data->setFlags(flags);
Q_ASSERT(m.revision() <= Q_INT16_MAX);
- setRevision(m.revision());
+ data->setRevision(m.revision());
}
-void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
+void QQmlPropertyData::load(const QMetaMethod &m)
{
- load(m);
+ populate(this, m);
+ setPropType(m.methodType() == QMetaMethod::Constructor
+ ? QMetaType::QObjectStar
+ : m.returnType());
+}
+void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
+{
const char *returnType = m.typeName();
- if (!returnType)
- returnType = "\0";
- if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) {
- m_flags.notFullyResolved = true;
- }
+ if (!returnType || *returnType != 'v' || qstrcmp(returnType + 1, "oid") != 0)
+ populate(this, m);
+ else
+ load(m); // If it's void, resolve it right away
}
/*!
@@ -318,7 +321,6 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag
data.setPropType(QMetaType::UnknownType);
data.setCoreIndex(coreIndex);
data.setFlags(flags);
- data.setArguments(nullptr);
QQmlPropertyData handler = data;
handler.m_flags.setIsSignalHandler(true);
@@ -327,7 +329,6 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag
int argumentCount = *types;
QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int));
- args->argumentsValid = true;
data.setArguments(args);
}
@@ -361,7 +362,6 @@ void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flag
QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
for (int ii = 0; ii < argumentCount; ++ii)
args->arguments[ii + 1] = parameterTypes.at(ii);
- args->argumentsValid = true;
data.setArguments(args);
data.setFlags(flags);
@@ -650,25 +650,23 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
}
}
-void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
+int QQmlPropertyCache::findPropType(const QQmlPropertyData *data) const
{
- Q_ASSERT(data->notFullyResolved());
- data->m_flags.notFullyResolved = false;
-
+ int type = QMetaType::UnknownType;
const QMetaObject *mo = firstCppMetaObject();
if (data->isFunction()) {
auto metaMethod = mo->method(data->coreIndex());
const char *retTy = metaMethod.typeName();
if (!retTy)
retTy = "\0";
- data->setPropType(QMetaType::type(retTy));
+ type = QMetaType::type(retTy);
} else {
auto metaProperty = mo->property(data->coreIndex());
- data->setPropType(QMetaType::type(metaProperty.typeName()));
+ type = QMetaType::type(metaProperty.typeName());
}
if (!data->isFunction()) {
- if (data->propType() == QMetaType::UnknownType) {
+ if (type == QMetaType::UnknownType) {
QQmlPropertyCache *p = _parent;
while (p && (!mo || _ownMetaObject)) {
mo = p->_metaObject;
@@ -684,11 +682,33 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
int registerResult = -1;
void *argv[] = { &registerResult };
- mo->static_metacall(QMetaObject::RegisterPropertyMetaType, data->coreIndex() - propOffset, argv);
- data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult);
+ mo->static_metacall(QMetaObject::RegisterPropertyMetaType,
+ data->coreIndex() - propOffset, argv);
+ type = registerResult == -1 ? QMetaType::UnknownType : registerResult;
}
}
- flagsForPropertyType(data->propType(), data->m_flags);
+ }
+
+ return type;
+}
+
+void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
+{
+ const int type = findPropType(data);
+
+ // Setting the flags unsynchronized is somewhat dirty but unlikely to cause trouble
+ // in practice. We have to do this before setting the property type because otherwise
+ // a consumer of the flags might see outdated flags even after the property type has
+ // become valid. The flags should only depend on the property type and the property
+ // type should be the same across different invocations. So, setting this concurrently
+ // should be a noop.
+ if (!data->isFunction())
+ flagsForPropertyType(type, data->m_flags);
+
+ // This is the one place where we can update the property type after exposing the data.
+ if (!data->m_propTypeAndRelativePropIndex.testAndSetOrdered(
+ 0, type > 0 ? quint32(type) : quint32(QQmlPropertyData::PropTypeUnknown))) {
+ return; // Someone else is resolving it already
}
}
@@ -873,12 +893,11 @@ QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int a
typedef QQmlPropertyCacheMethodArguments A;
A *args = static_cast<A *>(malloc(sizeof(A) + (argc) * sizeof(int)));
args->arguments[0] = argc;
- args->argumentsValid = false;
args->signalParameterStringForJS = nullptr;
- args->parameterError = false;
args->names = argc ? new QList<QByteArray>(names) : nullptr;
- args->next = argumentsCache;
- argumentsCache = args;
+ do {
+ args->next = argumentsCache;
+ } while (!argumentsCache.testAndSetRelease(args->next, args));
return args;
}
@@ -1172,7 +1191,6 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
QQmlPropertyCacheMethodArguments *arguments = nullptr;
if (data->hasArguments()) {
arguments = (QQmlPropertyCacheMethodArguments *)data->arguments();
- Q_ASSERT(arguments->argumentsValid);
for (int ii = 0; ii < arguments->arguments[0]; ++ii) {
if (ii != 0) signature.append(',');
signature.append(QMetaType::typeName(arguments->arguments[1 + ii]));
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index a5340cec37..1563bc0a41 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -220,6 +220,8 @@ private:
_hasPropertyOverrides |= isOverride;
}
+ int findPropType(const QQmlPropertyData *data) const;
+
private:
QQmlPropertyCache *_parent;
int propertyIndexCacheStart;
@@ -239,14 +241,18 @@ private:
QByteArray _dynamicClassName;
QByteArray _dynamicStringData;
QString _defaultPropertyName;
- QQmlPropertyCacheMethodArguments *argumentsCache;
+ QAtomicPointer<QQmlPropertyCacheMethodArguments> argumentsCache;
int _jsFactoryMethodIndex;
QByteArray _checksum;
};
inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const
{
- if (p && Q_UNLIKELY(p->notFullyResolved()))
+ // Avoid resolve() in the common case where it's already initialized and we don't
+ // run into a data race. resolve() checks again, with an atomic operation.
+ // If there is no coreIndex, there is no point in trying to resolve anything. In that
+ // case it's a default-constructed instance that never got load()'ed or lazyLoad()'ed.
+ if (p && p->coreIndex() != -1 && Q_UNLIKELY(p->m_propTypeAndRelativePropIndex == 0))
resolve(p);
return p;
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index 77e3763a49..3fd6c8b4da 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -865,6 +865,10 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
Q_ASSERT(targetCache);
targetProperty = targetCache->property(valueTypeIndex);
+ if (targetProperty == nullptr) {
+ return qQmlCompileError(alias.referenceLocation,
+ QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ }
*type = targetProperty->propType();
writable = targetProperty->isWritable();
diff --git a/src/qml/qml/qqmlpropertycachemethodarguments_p.h b/src/qml/qml/qqmlpropertycachemethodarguments_p.h
index 62f09bdfff..32affe6d9c 100644
--- a/src/qml/qml/qqmlpropertycachemethodarguments_p.h
+++ b/src/qml/qml/qqmlpropertycachemethodarguments_p.h
@@ -64,8 +64,6 @@ public:
//for signal handler rewrites
QString *signalParameterStringForJS;
- int parameterError:1;
- int argumentsValid:1;
QList<QByteArray> *names;
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
index d9855797cd..2f1b6f62f1 100644
--- a/src/qml/qml/qqmlpropertydata_p.h
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -84,14 +84,6 @@ public:
QVariantType = 9 // Property is a QVariant
};
- // The _otherBits (which "pad" the Flags struct to align it nicely) are used
- // to store the relative property index. It will only get used when said index fits. See
- // trySetStaticMetaCallFunction for details.
- // (Note: this padding is done here, because certain compilers have surprising behavior
- // when an enum is declared in-between two bit fields.)
- enum { BitsLeftInFlags = 15 };
- unsigned otherBits : BitsLeftInFlags; // align to 32 bits
-
// Members of the form aORb can only be a when type is not FunctionType, and only be
// b when type equals FunctionType. For that reason, the semantic meaning of the bit is
// overloaded, and the accessor functions are used to get the correct value
@@ -102,25 +94,24 @@ public:
//
// Lastly, isDirect and isOverridden apply to both functions and non-functions
private:
- unsigned isConstantORisVMEFunction : 1; // Has CONST flag OR Function was added by QML
- unsigned isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments
- unsigned isResettableORisSignal : 1; // Has RESET function OR Function is a signal
- unsigned isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML
- unsigned isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args
- unsigned isSignalHandler : 1; // Function is a signal handler
- unsigned isOverload : 1; // Function is an overload of another function
- unsigned isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned
- unsigned isConstructor : 1; // The function was marked is a constructor
- unsigned isDirect : 1; // Exists on a C++ QMetaObject
- unsigned isOverridden : 1; // Is overridden by a extension property
+ quint16 isConstantORisVMEFunction : 1; // Has CONST flag OR Function was added by QML
+ quint16 isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments
+ quint16 isResettableORisSignal : 1; // Has RESET function OR Function is a signal
+ quint16 isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML
+ quint16 isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args
+ quint16 isSignalHandler : 1; // Function is a signal handler
+ quint16 isOverload : 1; // Function is an overload of another function
+ quint16 isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned
+ quint16 isConstructor : 1; // The function was marked is a constructor
+ quint16 isDirect : 1; // Exists on a C++ QMetaObject
+ quint16 isOverridden : 1; // Is overridden by a extension property
public:
- unsigned type : 4; // stores an entry of Types
+ quint16 type : 4; // stores an entry of Types
// Apply only to IsFunctions
// Internal QQmlPropertyCache flags
- unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved
- unsigned overrideIndexIsProperty: 1;
+ quint16 overrideIndexIsProperty: 1;
inline Flags();
inline bool operator==(const Flags &other) const;
@@ -208,16 +199,12 @@ public:
};
+ Q_STATIC_ASSERT(sizeof(Flags) == sizeof(quint16));
inline bool operator==(const QQmlPropertyData &) const;
Flags flags() const { return m_flags; }
- void setFlags(Flags f)
- {
- unsigned otherBits = m_flags.otherBits;
- m_flags = f;
- m_flags.otherBits = otherBits;
- }
+ void setFlags(Flags f) { m_flags = f; }
bool isValid() const { return coreIndex() != -1; }
@@ -253,14 +240,26 @@ public:
bool hasOverride() const { return overrideIndex() >= 0; }
bool hasRevision() const { return revision() != 0; }
- bool isFullyResolved() const { return !m_flags.notFullyResolved; }
+ // This is unsafe in the general case. The property might be in the process of getting
+ // resolved. Only use it if this case has been taken into account.
+ bool isResolved() const { return m_propTypeAndRelativePropIndex != 0; }
+
+ int propType() const
+ {
+ const quint32 type = m_propTypeAndRelativePropIndex & PropTypeMask;
+ Q_ASSERT(type > 0); // Property has to be fully resolved.
+ return type == PropTypeUnknown ? 0 : type;
+ }
- int propType() const { Q_ASSERT(isFullyResolved()); return m_propType; }
void setPropType(int pt)
{
+ // You can only directly set the property type if you own the QQmlPropertyData.
+ // It must not be exposed to other threads before setting the type!
Q_ASSERT(pt >= 0);
- Q_ASSERT(pt <= std::numeric_limits<qint16>::max());
- m_propType = quint16(pt);
+ Q_ASSERT(uint(pt) < PropTypeUnknown);
+ m_propTypeAndRelativePropIndex
+ = (m_propTypeAndRelativePropIndex & RelativePropIndexMask)
+ | (pt == 0 ? PropTypeUnknown : quint32(pt));
}
int notifyIndex() const { return m_notifyIndex; }
@@ -323,7 +322,10 @@ public:
}
QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; }
- void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; }
+ bool setArguments(QQmlPropertyCacheMethodArguments *args)
+ {
+ return m_arguments.testAndSetRelease(nullptr, args);
+ }
int metaObjectOffset() const { return m_metaObjectOffset; }
void setMetaObjectOffset(int off)
@@ -336,12 +338,26 @@ public:
StaticMetaCallFunction staticMetaCallFunction() const { return m_staticMetaCallFunction; }
void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex)
{
- if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) {
- m_flags.otherBits = relativePropertyIndex;
+ if (relativePropertyIndex > std::numeric_limits<quint16>::max())
+ return;
+
+ const quint16 propType = m_propTypeAndRelativePropIndex & PropTypeMask;
+ if (propType > 0) {
+ // We can do this because we know that resolve() has run at this point
+ // and we don't need to synchronize anymore. If we get a 0, that means it hasn't
+ // run or is currently in progress. We don't want to interfer and just go through
+ // the meta object.
+ m_propTypeAndRelativePropIndex
+ = propType | (relativePropertyIndex << RelativePropIndexShift);
m_staticMetaCallFunction = f;
}
}
- quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return m_flags.otherBits; }
+
+ quint16 relativePropertyIndex() const
+ {
+ Q_ASSERT(hasStaticMetaCallFunction());
+ return m_propTypeAndRelativePropIndex >> 16;
+ }
static Flags flagsForProperty(const QMetaProperty &);
void load(const QMetaProperty &);
@@ -401,11 +417,17 @@ private:
friend class QQmlPropertyCache;
void lazyLoad(const QMetaProperty &);
void lazyLoad(const QMetaMethod &);
- bool notFullyResolved() const { return m_flags.notFullyResolved; }
+
+ enum {
+ PropTypeMask = 0x0000ffff,
+ RelativePropIndexMask = 0xffff0000,
+ RelativePropIndexShift = 16,
+ PropTypeUnknown = std::numeric_limits<quint16>::max(),
+ };
+ QAtomicInteger<quint32> m_propTypeAndRelativePropIndex;
Flags m_flags;
qint16 m_coreIndex = -1;
- quint16 m_propType = 0;
// The notify index is in the range returned by QObjectPrivate::signalIndex().
// This is different from QMetaMethod::methodIndex().
@@ -416,7 +438,7 @@ private:
quint8 m_typeMinorVersion = 0;
qint16 m_metaObjectOffset = -1;
- QQmlPropertyCacheMethodArguments *m_arguments = nullptr;
+ QAtomicPointer<QQmlPropertyCacheMethodArguments> m_arguments;
StaticMetaCallFunction m_staticMetaCallFunction = nullptr;
};
@@ -436,8 +458,7 @@ bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const
}
QQmlPropertyData::Flags::Flags()
- : otherBits(0)
- , isConstantORisVMEFunction(false)
+ : isConstantORisVMEFunction(false)
, isWritableORhasArguments(false)
, isResettableORisSignal(false)
, isAliasORisVMESignal(false)
@@ -449,7 +470,6 @@ QQmlPropertyData::Flags::Flags()
, isDirect(false)
, isOverridden(false)
, type(OtherType)
- , notFullyResolved(false)
, overrideIndexIsProperty(false)
{}
@@ -465,7 +485,6 @@ bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) c
isRequiredORisCloned == other.isRequiredORisCloned &&
type == other.type &&
isConstructor == other.isConstructor &&
- notFullyResolved == other.notFullyResolved &&
overrideIndexIsProperty == other.overrideIndexIsProperty;
}
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
index 312a621029..2079a8ed04 100644
--- a/src/qmlmodels/qqmldelegatemodel.cpp
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
@@ -968,6 +968,17 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod
contextData->extraObject = modelItemToIncubate;
}
+ // If we have required properties, we clear the context object
+ // so that the model role names are not polluting the context
+ if (incubating) {
+ Q_ASSERT(incubating->contextData);
+ incubating->contextData->contextObject = nullptr;
+ }
+
+ if (proxyContext) {
+ proxyContext->contextObject = nullptr;
+ }
+
if (incubatorPriv->requiredProperties().empty())
return;
RequiredProperties &requiredProperties = incubatorPriv->requiredProperties();
@@ -1279,6 +1290,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
QQmlContextData *ctxt = new QQmlContextData;
ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context.data()));
+ ctxt->contextObject = cacheItem;
cacheItem->contextData = ctxt;
if (m_adaptorModel.hasProxyObject()) {
@@ -1289,6 +1301,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
QObject *proxied = proxy->proxiedObject();
cacheItem->incubationTask->proxiedObject = proxied;
cacheItem->incubationTask->proxyContext = ctxt;
+ ctxt->contextObject = cacheItem;
// We don't own the proxied object. We need to clear it if it goes away.
QObject::connect(proxied, &QObject::destroyed,
cacheItem, &QQmlDelegateModelItem::childContextObjectDestroyed);
diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp
index 5e1ae25c38..85719fdc80 100644
--- a/src/quick/accessible/qaccessiblequickitem.cpp
+++ b/src/quick/accessible/qaccessiblequickitem.cpp
@@ -216,6 +216,8 @@ QAccessible::Role QAccessibleQuickItem::role() const
if (role == QAccessible::NoRole) {
if (qobject_cast<QQuickText*>(const_cast<QQuickItem *>(item())))
role = QAccessible::StaticText;
+ else if (qobject_cast<QQuickTextInput*>(const_cast<QQuickItem *>(item())))
+ role = QAccessible::EditableText;
else
role = QAccessible::Client;
}
@@ -232,6 +234,7 @@ QStringList QAccessibleQuickItem::actionNames() const
{
QStringList actions;
switch (role()) {
+ case QAccessible::Link:
case QAccessible::PushButton:
actions << QAccessibleActionInterface::pressAction();
break;
diff --git a/src/quick/designer/qquickdesignersupportproperties.cpp b/src/quick/designer/qquickdesignersupportproperties.cpp
index fb6a5fb324..479e77bf68 100644
--- a/src/quick/designer/qquickdesignersupportproperties.cpp
+++ b/src/quick/designer/qquickdesignersupportproperties.cpp
@@ -126,16 +126,15 @@ void QQuickDesignerSupportProperties::getPropertyCache(QObject *object, QQmlEngi
QQmlEnginePrivate::get(engine)->cache(object->metaObject());
}
-QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::propertyNameListForWritableProperties(QObject *object,
+static QQuickDesignerSupport::PropertyNameList propertyNameListForWritableProperties(QObject *object,
const QQuickDesignerSupport::PropertyName &baseName,
- QObjectList *inspectedObjects)
+ QObjectList *inspectedObjects,
+ int depth = 0)
{
QQuickDesignerSupport::PropertyNameList propertyNameList;
- QObjectList localObjectList;
-
- if (inspectedObjects == nullptr)
- inspectedObjects = &localObjectList;
+ if (depth > 2)
+ return propertyNameList;
if (!inspectedObjects->contains(object))
inspectedObjects->append(object);
@@ -150,14 +149,16 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::propert
if (childObject)
propertyNameList.append(propertyNameListForWritableProperties(childObject,
baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())
- + '.', inspectedObjects));
+ + '.', inspectedObjects,
+ depth + 1));
}
} else if (QQmlGadgetPtrWrapper *valueType
= QQmlGadgetPtrWrapper::instance(qmlEngine(object), metaProperty.userType())) {
valueType->setValue(metaProperty.read(object));
propertyNameList.append(propertyNameListForWritableProperties(valueType,
baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())
- + '.', inspectedObjects));
+ + '.', inspectedObjects,
+ depth + 1));
}
if (metaProperty.isReadable() && metaProperty.isWritable()) {
@@ -169,6 +170,12 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::propert
return propertyNameList;
}
+QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::propertyNameListForWritableProperties(QObject *object)
+{
+ QObjectList localObjectList;
+ return ::propertyNameListForWritableProperties(object, {}, &localObjectList);
+}
+
bool QQuickDesignerSupportProperties::isPropertyBlackListed(const QQuickDesignerSupport::PropertyName &propertyName)
{
if (propertyName.contains(".") && propertyName.contains("__"))
@@ -182,7 +189,8 @@ bool QQuickDesignerSupportProperties::isPropertyBlackListed(const QQuickDesigner
QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::allPropertyNames(QObject *object,
const QQuickDesignerSupport::PropertyName &baseName,
- QObjectList *inspectedObjects)
+ QObjectList *inspectedObjects,
+ int depth)
{
QQuickDesignerSupport::PropertyNameList propertyNameList;
@@ -191,6 +199,9 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::allProp
if (inspectedObjects == nullptr)
inspectedObjects = &localObjectList;
+ if (depth > 2)
+ return propertyNameList;
+
if (!inspectedObjects->contains(object))
inspectedObjects->append(object);
@@ -214,7 +225,8 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::allProp
propertyNameList.append(allPropertyNames(childObject,
baseName
+ QQuickDesignerSupport::PropertyName(metaProperty.name())
- + '.', inspectedObjects));
+ + '.', inspectedObjects,
+ depth + 1));
}
} else if (QQmlGadgetPtrWrapper *valueType
= QQmlGadgetPtrWrapper::instance(qmlEngine(object), metaProperty.userType())) {
@@ -223,7 +235,8 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::allProp
propertyNameList.append(allPropertyNames(valueType,
baseName
+ QQuickDesignerSupport::PropertyName(metaProperty.name())
- + '.', inspectedObjects));
+ + '.', inspectedObjects,
+ depth + 1));
} else {
addToPropertyNameListIfNotBlackListed(&propertyNameList,
baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()));
diff --git a/src/quick/designer/qquickdesignersupportproperties_p.h b/src/quick/designer/qquickdesignersupportproperties_p.h
index 02e75ea886..5970eca9f1 100644
--- a/src/quick/designer/qquickdesignersupportproperties_p.h
+++ b/src/quick/designer/qquickdesignersupportproperties_p.h
@@ -90,12 +90,11 @@ public:
static void getPropertyCache(QObject *object, QQmlEngine *engine);
static bool isPropertyBlackListed(const QQuickDesignerSupport::PropertyName &propertyName);
- static QQuickDesignerSupport::PropertyNameList propertyNameListForWritableProperties(QObject *object,
- const QQuickDesignerSupport::PropertyName &baseName = QQuickDesignerSupport::PropertyName(),
- QObjectList *inspectedObjects = nullptr);
+ static QQuickDesignerSupport::PropertyNameList propertyNameListForWritableProperties(QObject *object);
static QQuickDesignerSupport::PropertyNameList allPropertyNames(QObject *object,
const QQuickDesignerSupport::PropertyName &baseName = QQuickDesignerSupport::PropertyName(),
- QObjectList *inspectedObjects = nullptr);
+ QObjectList *inspectedObjects = nullptr,
+ int depth = 0);
static bool hasFullImplementedListInterface(const QQmlListReference &list);
};
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml b/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml
new file mode 100644
index 0000000000..1564aa16b5
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.12
+
+Rectangle {
+ id: button
+ signal clicked
+
+ width: 150; height: 50; radius: 3
+ color: tapHandler.pressed ? "goldenrod" : hoverHandler.hovered ? "wheat" : "beige"
+ border.color: activeFocus ? "brown" : "transparent"
+ focus: true
+
+ HoverHandler {
+ id: hoverHandler
+ }
+
+ TapHandler {
+ id: tapHandler
+ onTapped: button.clicked()
+ }
+
+ Keys.onEnterPressed: button.clicked()
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/externaldrag.qml b/src/quick/doc/snippets/qml/externaldrag.qml
index 97a23293ca..5a88be2d4b 100644
--- a/src/quick/doc/snippets/qml/externaldrag.qml
+++ b/src/quick/doc/snippets/qml/externaldrag.qml
@@ -48,7 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.8
+import QtQuick 2.12
Item {
width: 200; height: 200
@@ -59,7 +59,7 @@ Item {
color: "green"
radius: 5
- Drag.active: dragArea.drag.active
+ Drag.active: dragHandler.active
Drag.dragType: Drag.Automatic
Drag.supportedActions: Qt.CopyAction
Drag.mimeData: {
@@ -72,14 +72,14 @@ Item {
text: "Drag me"
}
- MouseArea {
- id: dragArea
- anchors.fill: parent
-
- drag.target: parent
- onPressed: parent.grabToImage(function(result) {
- parent.Drag.imageSource = result.url
- })
+ DragHandler {
+ id: dragHandler
+ onActiveChanged:
+ if (active) {
+ parent.grabToImage(function(result) {
+ parent.Drag.imageSource = result.url;
+ })
+ }
}
}
}
diff --git a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
index 2ac9860e6f..bf889a9066 100644
--- a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
+++ b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -30,20 +30,21 @@
\title Qt Quick Input Handlers
\brief A module with a set of QML elements that handle events from input devices in a user interface.
- Qt Quick Input Handlers are a set of QML types used to handle events from
- keyboard, touch, mouse, and stylus devices in a UI. In contrast to event-handling
- items, such as \l MouseArea and \l Flickable, input handlers are explicitly non-visual,
- require less memory and are intended to be used in greater numbers: one
- handler instance per aspect of interaction. Each input handler instance
- handles certain events on behalf of its \c parent Item. Thus the visual and
+ Qt Quick Input Handlers are a set of QML types used to handle
+ \l {QInputEvent}{events} from keyboard, touch, mouse, and stylus
+ \l {QInputDevice}{devices} in a UI. In contrast to event-handling
+ items, such as \l MouseArea and \l Flickable, input handlers are explicitly
+ non-visual, require less memory and are intended to be used in greater
+ numbers: one handler instance per aspect of interaction. Each input handler
+ instance handles certain events on behalf of its
+ \l {QQuickPointerHandler::parent()}{parent} Item. Thus the visual and
behavioral concerns are better separated, and the behavior is built up by
finer-grained composition.
- In Qt 5.10, these handlers were introduced in a separate Qt.labs.handlers module.
- Now they are included with Qt Quick since 5.12. The pre-existing
- \l Keys attached property is similar in concept, so we refer to the
- pointing-device-oriented handlers plus \c Keys together as the set of Input Handlers.
- We expect to offer more attached-property use cases in future versions of Qt.
+ The pre-existing \l Keys attached property is similar in concept, so we
+ refer to the pointing-device-oriented handlers plus \c Keys together as the
+ set of Input Handlers. We expect to offer more attached-property use cases
+ in future versions of Qt.
\section1 Input Handlers
@@ -60,7 +61,44 @@
\li Each Item can have unlimited Handlers
\endlist
- \omit TODO actual overview with snippets and stuff \endomit
+ \section1 Handlers Manipulating Items
+
+ Some Handlers add interactivity simply by being declared inside an Item:
+
+ \snippet pointerHandlers/dragHandler.qml 0
+
+ \section1 Handler Properties and Signals
+
+ All Handlers have properties that can be used in bindings, and signals that
+ can be handled to react to input:
+
+ \snippet pointerHandlers/hoverTapKeyButton.qml 0
+
+ \section1 Pointer Grab
+
+ An important concept with Pointer Handlers is the type of grabs that they
+ perform. The only kind of grab an Item can take is the exclusive grab: for
+ example if you call \l QPointerEvent::setExclusiveGrabber(), the following
+ mouse moves and mouse release event will be sent only to that object. (As a
+ workaround to this exclusivity, see \l QQuickItem::setFiltersChildMouseEvents()
+ and \l QQuickItem::childMouseEventFilter().) However Pointer Handlers have
+ an additional mechanism available: the
+ \l {QPointerEvent::addPassiveGrabber()} {passive grab}. Mouse and touch
+ \l {QEventPoint::state()}{press} events are delivered by visiting all the
+ Items in top-down Z order: first each Item's child Handlers, and then the
+ \l {QQuickItem::event()}{Item} itself. At the time a press event is
+ delivered, a Handler can take either a passive or an exclusive grab
+ depending on its needs. If it takes a passive grab, it is guaranteed to
+ receive the updates and the release, even if other Items or Handlers in the
+ scene take any kind of grab, passive or exclusve. Some Handlers (such as
+ PointHandler) can work only with passive grabs; others require exclusive
+ grabs; and others can "lurk" with passive grabs until they detect that a
+ gesture is being performed, and then make the transition from passive to
+ exclusive grab.
+
+ When a grab transition is requested, \l PointerHandler::grabPermissions,
+ \l QQuickItem::keepMouseGrab() and \l QQuickItem::keepTouchGrab() control
+ whether the transition will be allowed.
\section1 Related Information
diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index 528444cad3..65de1284a4 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -740,6 +740,73 @@ console.log(c + " " + d); // false true
\li Example
\row
+ \li translate(vector3d vector)
+ \li Multiplies \c this matrix4x4 by another that translates coordinates by the components
+ of \c vector
+ \li \code
+var m = Qt.matrix4x4();
+m.translate(Qt.vector3d(1,2,3));
+console.log(m.toString());
+// QMatrix4x4(1, 0, 0, 1, 0, 1, 0, 2, 0, 0, 1, 3, 0, 0, 0, 1)
+ \endcode
+
+ \row
+ \li rotate(real angle, vector3d axis)
+ \li Multiples \c this matrix4x4 by another that rotates coordinates through
+ \c angle degrees about \c axis
+ \li \code
+var m = Qt.matrix4x4();
+m.rotate(180,vector3d(1,0,0));
+console.log(m.toString());
+// QMatrix4x4(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)
+ \endcode
+
+ \row
+ \li scale(real factor)
+ \li Multiplies \c this matrix4x4 by another that scales coordinates by the given \c factor
+ \li \code
+var m = Qt.matrix4x4();
+m.scale(2);
+console.log(m.toString());
+// QMatrix4x4(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1)
+ \endcode
+
+ \row
+ \li scale(real x, real y, real z)
+ \li Multiplies \c this matrix4x4 by another that scales coordinates by the components
+ \c x, \c y, and \c z
+ \li \code
+var m = Qt.matrix4x4();
+m.scale(1,2,3);
+console.log(m.toString());
+// QMatrix4x4(1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1)
+ \endcode
+
+ \row
+ \li scale(vector3d vector)
+ \li Multiplies \c this matrix4x4 by another that scales coordinates by the components
+ of \c vector
+ \li \code
+var m = Qt.matrix4x4();
+m.scale(Qt.vector3d(1,2,3));
+console.log(m.toString());
+// QMatrix4x4(1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1)
+ \endcode
+
+ \row
+ \li lookAt(vector3d eye, vector3d center, vector3d up)
+ \li Multiplies \c this matrix4x4 by a viewing matrix derived from an \c eye point.
+ The \c center vector3d indicates the center of the view that the \c eye is looking at.
+ The \c up vector3d indicates which direction should be considered up with respect to
+ the \c eye.
+ \li \code
+var m = Qt.matrix4x4();
+m.lookAt(Qt.vector3d(1,2,3),Qt.vector3d(1,2,0),Qt.vector3d(0,1,0));
+console.log(m.toString());
+// QMatrix4x4(1, 0, 0, -1, 0, 1, 0, -2, 0, 0, 1, -3, 0, 0, 0, 1)
+ \endcode
+
+ \row
\li matrix4x4 times(matrix4x4 other)
\li Returns the matrix4x4 result of multiplying \c this matrix4x4 with
the \c other matrix4x4
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
index f404788de4..2a9ecd341c 100644
--- a/src/quick/handlers/qquickmultipointhandler.cpp
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -146,7 +146,7 @@ void QQuickMultiPointHandler::onActiveChanged()
}
}
-void QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *)
+void QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
{
Q_D(QQuickMultiPointHandler);
// If another handler or item takes over this set of points, assume it has
@@ -155,6 +155,20 @@ void QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *, QQuickEventP
// (e.g. between DragHandler and PinchHandler).
if (transition == QQuickEventPoint::UngrabExclusive || transition == QQuickEventPoint::CancelGrabExclusive)
d->currentPoints.clear();
+ if (grabber != this)
+ return;
+ switch (transition) {
+ case QQuickEventPoint::GrabExclusive:
+ case QQuickEventPoint::GrabPassive:
+ case QQuickEventPoint::UngrabPassive:
+ case QQuickEventPoint::UngrabExclusive:
+ case QQuickEventPoint::CancelGrabPassive:
+ case QQuickEventPoint::CancelGrabExclusive:
+ QQuickPointerHandler::onGrabChanged(grabber, transition, point);
+ break;
+ case QQuickEventPoint::OverrideGrabPassive:
+ return; // don't emit
+ }
}
QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointerEvent *event)
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index 03f8d8918c..a6c18feafe 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -346,10 +346,16 @@ bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObjec
existingPhGrabber->metaObject()->className() == metaObject()->className())
allowed = true;
} else if ((d->grabPermissions & CanTakeOverFromItems)) {
+ allowed = true;
QQuickItem * existingItemGrabber = point->grabberItem();
- if (existingItemGrabber && !((existingItemGrabber->keepMouseGrab() && point->pointerEvent()->asPointerMouseEvent()) ||
- (existingItemGrabber->keepTouchGrab() && point->pointerEvent()->asPointerTouchEvent()))) {
- allowed = true;
+ QQuickWindowPrivate *winPriv = QQuickWindowPrivate::get(parentItem()->window());
+ const bool isMouse = point->pointerEvent()->asPointerMouseEvent();
+ const bool isTouch = point->pointerEvent()->asPointerTouchEvent();
+ if (existingItemGrabber &&
+ ((existingItemGrabber->keepMouseGrab() &&
+ (isMouse || winPriv->isDeliveringTouchAsMouse())) ||
+ (existingItemGrabber->keepTouchGrab() && isTouch))) {
+ allowed = false;
// If the handler wants to steal the exclusive grab from an Item, the Item can usually veto
// by having its keepMouseGrab flag set. But an exception is if that Item is a parent that
// normally filters events (such as a Flickable): it needs to be possible for e.g. a
@@ -358,14 +364,19 @@ bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObjec
// at first and then expects to be able to steal the grab later on. It cannot respect
// Flickable's wishes in that case, because then it would never have a chance.
if (existingItemGrabber->keepMouseGrab() &&
- !(existingItemGrabber->filtersChildMouseEvents() && existingItemGrabber->isAncestorOf(parentItem()))) {
- QQuickWindowPrivate *winPriv = QQuickWindowPrivate::get(parentItem()->window());
+ existingItemGrabber->filtersChildMouseEvents() && existingItemGrabber->isAncestorOf(parentItem())) {
if (winPriv->isDeliveringTouchAsMouse() && point->pointId() == winPriv->touchMouseId) {
- qCDebug(lcPointerHandlerGrab) << this << "wants to grab touchpoint" << point->pointId()
- << "but declines to steal grab from touch-mouse grabber with keepMouseGrab=true" << existingItemGrabber;
- allowed = false;
+ qCDebug(lcPointerHandlerGrab) << this << "steals touchpoint" << point->pointId()
+ << "despite parent touch-mouse grabber with keepMouseGrab=true" << existingItemGrabber;
+ allowed = true;
}
}
+ if (!allowed) {
+ qCDebug(lcPointerHandlerGrab) << this << "wants to grab point" << point->pointId()
+ << "but declines to steal from grabber" << existingItemGrabber
+ << "with keepMouseGrab=" << existingItemGrabber->keepMouseGrab()
+ << "keepTouchGrab=" << existingItemGrabber->keepTouchGrab();
+ }
}
}
}
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index f3674d6fa9..2284750f15 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -209,6 +209,8 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
If the spatial constraint is violated, \l pressed transitions immediately
from true to false, regardless of the time held.
+ The \c gesturePolicy also affects grab behavior as described below.
+
\value TapHandler.DragThreshold
(the default value) The event point must not move significantly.
If the mouse, finger or stylus moves past the system-wide drag
@@ -217,11 +219,13 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
can be useful whenever TapHandler needs to cooperate with other
input handlers (for example \l DragHandler) or event-handling Items
(for example QtQuick Controls), because in this case TapHandler
- will not take the exclusive grab, but merely a passive grab.
+ will not take the exclusive grab, but merely a
+ \l {QPointerEvent::addPassiveGrabber()}{passive grab}.
\value TapHandler.WithinBounds
If the event point leaves the bounds of the \c parent Item, the tap
- gesture is canceled. The TapHandler will take the exclusive grab on
+ gesture is canceled. The TapHandler will take the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on
press, but will release the grab as soon as the boundary constraint
is no longer satisfied.
@@ -232,8 +236,9 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
typical behavior for button widgets: you can cancel a click by
dragging outside the button, and you can also change your mind by
dragging back inside the button before release. Note that it's
- necessary for TapHandler take the exclusive grab on press and retain
- it until release in order to detect this gesture.
+ necessary for TapHandler to take the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on press
+ and retain it until release in order to detect this gesture.
*/
void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
{
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
index 55ebbe907c..1d047e3c2f 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
@@ -375,7 +375,10 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s
}
state.lineDash = pattern;
QPen nPen = p->pen();
- nPen.setDashPattern(pattern);
+ if (count > 0)
+ nPen.setDashPattern(pattern);
+ else
+ nPen.setStyle(Qt::SolidLine);
p->setPen(nPen);
break;
}
diff --git a/src/quick/items/qquickdroparea.cpp b/src/quick/items/qquickdroparea.cpp
index d90ee209d6..e503cd7815 100644
--- a/src/quick/items/qquickdroparea.cpp
+++ b/src/quick/items/qquickdroparea.cpp
@@ -91,6 +91,7 @@ QQuickDropAreaPrivate::~QQuickDropAreaPrivate()
/*!
\qmltype DropArea
\instantiates QQuickDropArea
+ \inherits Item
\inqmlmodule QtQuick
\ingroup qtquick-input
\brief For specifying drag and drop handling in an area.
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index 1882ec8997..04c5da6167 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -343,9 +343,9 @@ void QQuickImage::setFillMode(FillMode mode)
}
/*!
-
\qmlproperty real QtQuick::Image::paintedWidth
\qmlproperty real QtQuick::Image::paintedHeight
+ \readonly
These properties hold the size of the image that is actually painted.
In most cases it is the same as \c width and \c height, but when using an
@@ -367,6 +367,7 @@ qreal QQuickImage::paintedHeight() const
/*!
\qmlproperty enumeration QtQuick::Image::status
+ \readonly
This property holds the status of image loading. It can be one of:
\list
@@ -404,6 +405,7 @@ qreal QQuickImage::paintedHeight() const
/*!
\qmlproperty real QtQuick::Image::progress
+ \readonly
This property holds the progress of image loading, from 0.0 (nothing loaded)
to 1.0 (finished).
@@ -425,7 +427,7 @@ qreal QQuickImage::paintedHeight() const
*/
/*!
- \qmlproperty QSize QtQuick::Image::sourceSize
+ \qmlproperty size QtQuick::Image::sourceSize
This property holds the scaled width and height of the full-frame image.
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index 8849c2005c..d7b5709bb0 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -56,8 +56,8 @@ QT_BEGIN_NAMESPACE
bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio)
{
// QQuickImageProvider and SVG and PDF can generate a high resolution image when
- // sourceSize is set (this function is only called if it's set).
- // If sourceSize is not set then the provider default size will be used, as usual.
+ // sourceSize is set. If sourceSize is not set then the provider default size will
+ // be used, as usual.
bool setDevicePixelRatio = false;
if (url.scheme() == QLatin1String("image")) {
setDevicePixelRatio = true;
@@ -418,10 +418,14 @@ void QQuickImageBase::itemChange(ItemChange change, const ItemChangeData &value)
Q_D(QQuickImageBase);
// If the screen DPI changed, reload image.
if (change == ItemDevicePixelRatioHasChanged && value.realValue != d->devicePixelRatio) {
+ const auto oldDpr = d->devicePixelRatio;
// ### how can we get here with !qmlEngine(this)? that implies
// itemChange() on an item pending deletion, which seems strange.
if (qmlEngine(this) && isComponentComplete() && d->url.isValid()) {
load();
+ // not changed when loading (sourceSize might not be set)
+ if (d->devicePixelRatio == oldDpr)
+ d->updateDevicePixelRatio(value.realValue);
}
}
QQuickItem::itemChange(change, value);
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 3df899d63d..64123c82c4 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -4790,14 +4790,24 @@ void QQuickItem::forceActiveFocus()
void QQuickItem::forceActiveFocus(Qt::FocusReason reason)
{
+ Q_D(QQuickItem);
setFocus(true, reason);
QQuickItem *parent = parentItem();
+ QQuickItem *scope = nullptr;
while (parent) {
if (parent->flags() & QQuickItem::ItemIsFocusScope) {
parent->setFocus(true, reason);
+ if (!scope)
+ scope = parent;
}
parent = parent->parentItem();
}
+ // In certain reparenting scenarios, d->focus might be true and the scope
+ // might also have focus, so that setFocus() returns early without actually
+ // acquiring active focus, because it thinks it already has it. In that
+ // case, try to set the DeliveryAgent's active focus. (QTBUG-89736).
+ if (scope && !d->activeFocus && d->window)
+ QQuickWindowPrivate::get(d->window)->setFocusInScope(scope, this, Qt::OtherFocusReason);
}
/*!
@@ -7858,22 +7868,48 @@ bool QQuickItem::contains(const QPointF &point) const
\qmlproperty QObject* QtQuick::Item::containmentMask
\since 5.11
This property holds an optional mask for the Item to be used in the
- QtQuick::Item::contains method.
- QtQuick::Item::contains main use is currently to determine whether
- an input event has landed into the item or not.
+ QtQuick::Item::contains() method. Its main use is currently to determine
+ whether a \l {QPointerEvent}{pointer event} has landed into the item or not.
By default the \l contains method will return true for any point
- within the Item's bounding box. \c containmentMask allows for a
- more fine-grained control. For example, the developer could
- define and use an AnotherItem element as containmentMask,
- which has a specialized contains method, like:
+ within the Item's bounding box. \c containmentMask allows for
+ more fine-grained control. For example, if a custom C++
+ QQuickItem subclass with a specialized contains() method
+ is used as containmentMask:
\code
Item { id: item; containmentMask: AnotherItem { id: anotherItem } }
\endcode
- \e{item}'s contains method would then return true only if
- \e{anotherItem}'s contains implementation returns true.
+ \e{item}'s contains method would then return \c true only if
+ \e{anotherItem}'s contains() implementation returns \c true.
+
+ A \l Shape can be used in this way, to make an item react to
+ \l {QPointerEvent}{pointer events} only within a non-rectangular region,
+ as illustrated in the \l {Qt Quick Examples - Shapes}{Shapes example}
+ (see \c tapableTriangle.qml).
+*/
+/*!
+ \property QQuickItem::containmentMask
+ \since 5.11
+ This property holds an optional mask to be used in the contains() method,
+ which is mainly used for hit-testing each \l QPointerEvent.
+
+ By default, \l contains() will return \c true for any point
+ within the Item's bounding box. But any QQuickItem, or any QObject
+ that implements a function of the form
+ \code
+ Q_INVOKABLE bool contains(const QPointF &point) const;
+ \endcode
+ can be used as a mask, to defer hit-testing to that object.
+
+ \note contains() is called frequently during event delivery.
+ Deferring hit-testing to another object slows it down somewhat.
+ containmentMask() can cause performance problems if that object's
+ contains() method is not efficient. If you implement a custom
+ QQuickItem subclass, you can alternatively override contains().
+
+ \sa contains()
*/
QObject *QQuickItem::containmentMask() const
{
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index ddaa1979b6..a832f53e39 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -573,7 +573,7 @@ void QQuickParentChange::rewind()
The AnchorChanges type is used to modify the anchors of an item in a \l State.
AnchorChanges cannot be used to modify the margins on an item. For this, use
- PropertyChanges intead.
+ PropertyChanges instead.
In the following example we change the top and bottom anchors of an item
using AnchorChanges, and the top and bottom anchor margins using
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 0e7f52e816..b4b64d59cc 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -1585,7 +1585,7 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
d->moveCursor(cursor, mark);
if (d->focusOnPress && !qGuiApp->styleHints()->setFocusOnTouchRelease())
- ensureActiveFocus();
+ ensureActiveFocus(Qt::MouseFocusReason);
event->setAccepted(true);
}
@@ -1637,7 +1637,7 @@ void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
#endif
if (d->focusOnPress && qGuiApp->styleHints()->setFocusOnTouchRelease())
- ensureActiveFocus();
+ ensureActiveFocus(Qt::MouseFocusReason);
if (!event->isAccepted())
QQuickImplicitSizeItem::mouseReleaseEvent(event);
@@ -1872,10 +1872,10 @@ void QQuickTextInput::invalidateFontCaches()
d->m_textLayout.engine()->resetFontEngineCache();
}
-void QQuickTextInput::ensureActiveFocus()
+void QQuickTextInput::ensureActiveFocus(Qt::FocusReason reason)
{
bool hadActiveFocus = hasActiveFocus();
- forceActiveFocus();
+ forceActiveFocus(reason);
#if QT_CONFIG(im)
Q_D(QQuickTextInput);
// re-open input panel on press if already focused
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index 9f7b82b168..8e97393d10 100644
--- a/src/quick/items/qquicktextinput_p.h
+++ b/src/quick/items/qquicktextinput_p.h
@@ -361,7 +361,7 @@ Q_SIGNALS:
private:
void invalidateFontCaches();
- void ensureActiveFocus();
+ void ensureActiveFocus(Qt::FocusReason reason);
protected:
QQuickTextInput(QQuickTextInputPrivate &dd, QQuickItem *parent = nullptr);
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index c956c85091..2b9810ed57 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -450,12 +450,13 @@ void QQuickWindow::physicalDpiChanged()
void QQuickWindow::handleScreenChanged(QScreen *screen)
{
Q_D(QQuickWindow);
+ // we connected to the initial screen in QQuickWindowPrivate::init, but the screen changed
disconnect(d->physicalDpiChangedConnection);
if (screen) {
physicalDpiChanged();
// When physical DPI changes on the same screen, either the resolution or the device pixel
// ratio changed. We must check what it is. Device pixel ratio does not have its own
- // ...Changed() signal.
+ // ...Changed() signal. Reconnect, same as in QQuickWindowPrivate::init.
d->physicalDpiChangedConnection = connect(screen, &QScreen::physicalDotsPerInchChanged,
this, &QQuickWindow::physicalDpiChanged);
}
@@ -707,8 +708,13 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
Q_ASSERT(windowManager || renderControl);
- if (QScreen *screen = q->screen())
- devicePixelRatio = screen->devicePixelRatio();
+ if (QScreen *screen = q->screen()) {
+ devicePixelRatio = screen->devicePixelRatio();
+ // if the screen changes, then QQuickWindow::handleScreenChanged disconnects
+ // and connects to the new screen
+ physicalDpiChangedConnection = QObject::connect(screen, &QScreen::physicalDotsPerInchChanged,
+ q, &QQuickWindow::physicalDpiChanged);
+ }
QSGContext *sg;
if (renderControl) {
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index c96129e660..ee01211545 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -413,13 +413,6 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
qCDebug(QSG_LOG_RENDERLOOP, "cleanup without an OpenGL context");
}
-#if QT_CONFIG(quick_shadereffect)
- QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
-#if QT_CONFIG(opengl)
- QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
-#endif
-#endif
-
if (d->swapchain) {
if (window->handle()) {
// We get here when exiting via QCoreApplication::quit() instead of
@@ -432,6 +425,14 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
}
d->cleanupNodesOnShutdown();
+
+#if QT_CONFIG(quick_shadereffect)
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
+#if QT_CONFIG(opengl)
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
+#endif
+
if (m_windows.size() == 0) {
rc->invalidate();
d->rhi = nullptr;
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 1c72c4dba6..d47b0d72a5 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -547,17 +547,16 @@ void QSGRenderThread::invalidateGraphics(QQuickWindow *window, bool inDestructor
QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
+ // The canvas nodes must be cleaned up regardless if we are in the destructor..
+ if (wipeSG) {
+ dd->cleanupNodesOnShutdown();
#if QT_CONFIG(quick_shadereffect)
- QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
#if QT_CONFIG(opengl)
- if (current)
- QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+ if (current)
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
#endif
#endif
-
- // The canvas nodes must be cleaned up regardless if we are in the destructor..
- if (wipeSG) {
- dd->cleanupNodesOnShutdown();
} else {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- persistent SG, avoiding cleanup");
if (current && gl)
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
index 20d7c4557f..20e127c49f 100644
--- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp
+++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
@@ -256,12 +256,13 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window)
if (Q_UNLIKELY(!current))
RLDEBUG("cleanup without an OpenGL context");
+ d->cleanupNodesOnShutdown();
+
#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
if (current)
QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
#endif
- d->cleanupNodesOnShutdown();
if (m_windows.size() == 0) {
d->context->invalidate();
delete m_gl;
diff --git a/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml b/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml
new file mode 100644
index 0000000000..fa154b25f3
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.0
+
+Item {
+ property var isSelected: null
+ property string source
+ implicitWidth: 16
+ implicitHeight: 16
+
+ onSourceChanged: {
+ updateImageSource()
+ }
+
+ onIsSelectedChanged: {
+ updateImageSource()
+ }
+
+ function updateImageSource() {
+ let result = isSelected ? source + "_selected_dark.png" : source + "_active_dark.png"
+ }
+
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml b/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml
new file mode 100644
index 0000000000..46d7524527
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ width: 640
+ height: 480
+ property bool works: myView.currentItem.okay
+
+ ListView {
+ id: myView
+ model: myModel
+ anchors.fill: parent
+ delegate: Row {
+ property alias okay: image.isSelected
+ ImageToggle {
+ id: image
+ source: "glyph_16_arrow_patch"
+ isSelected: model.age < 6
+ }
+ Text {
+ text: "age:" + model.age + " selected:" + image.isSelected
+ }
+ }
+ }
+
+ ListModel {
+ id: myModel
+ ListElement { type: "Cat"; age: 3; }
+ ListElement { type: "Dog"; age: 2; }
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
index 9bc359d243..35f1e2c94d 100644
--- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
+++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
@@ -46,6 +46,7 @@ private slots:
void valueWithoutCallingObjectFirst();
void filterOnGroup_removeWhenCompleted();
void qtbug_86017();
+ void contextAccessedByHandler();
};
class AbstractItemModel : public QAbstractItemModel
@@ -164,6 +165,27 @@ void tst_QQmlDelegateModel::qtbug_86017()
QCOMPARE(model->filterGroup(), "selected");
}
+void tst_QQmlDelegateModel::filterOnGroup_removeWhenCompleted()
+{
+ QQuickView view(testFileUrl("removeFromGroup.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QQuickItem *root = view.rootObject();
+ QVERIFY(root);
+ QQmlDelegateModel *model = root->findChild<QQmlDelegateModel*>();
+ QVERIFY(model);
+ QVERIFY(QTest::qWaitFor([=]{ return model->count() == 2; }));
+}
+
+void tst_QQmlDelegateModel::contextAccessedByHandler()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("contextAccessedByHandler.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY2(root, qPrintable(component.errorString()));
+ QVERIFY(root->property("works").toBool());
+}
+
QTEST_MAIN(tst_QQmlDelegateModel)
#include "tst_qqmldelegatemodel.moc"
diff --git a/tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml b/tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml
new file mode 100644
index 0000000000..7fe366cac8
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml
@@ -0,0 +1,13 @@
+import QtQml 2.15
+
+QtObject {
+ function test_generator_gc() {
+ ((function*() { gc() })()).next();
+ ((function*() { gc() })()).next();
+ ((function*() { gc() })()).next();
+ ((function*() { gc() })()).next();
+ }
+
+ Component.onCompleted: () => test_generator_gc()
+
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 3c3a2a7a99..7da1b2c500 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -240,6 +240,7 @@ private slots:
void function();
void topLevelGeneratorFunction();
void generatorCrashNewProperty();
+ void generatorCallsGC();
void qtbug_10696();
void qtbug_11606();
void qtbug_11600();
@@ -6505,6 +6506,15 @@ void tst_qqmlecmascript::generatorCrashNewProperty()
QCOMPARE(o->property("c").toInt(), 42);
}
+void tst_qqmlecmascript::generatorCallsGC()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("generatorCallsGC.qml"));
+
+ QScopedPointer<QObject> o(component.create()); // should not crash
+ QVERIFY2(o != nullptr, qPrintable(component.errorString()));
+}
+
// Test the "Qt.include" method
void tst_qqmlecmascript::include()
{
diff --git a/tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt b/tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt
new file mode 100644
index 0000000000..d69122ef8b
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt
@@ -0,0 +1 @@
+6:40:Invalid alias target
diff --git a/tests/auto/qml/qqmllanguage/data/qtbug_89822.qml b/tests/auto/qml/qqmllanguage/data/qtbug_89822.qml
new file mode 100644
index 0000000000..17602ca4b9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/qtbug_89822.qml
@@ -0,0 +1,8 @@
+import QtQml 2.0
+
+QtObject {
+ id: root
+ readonly property QtObject test: QtObject { property int subproperty: 3}
+ readonly property alias testAlias: root.test.subproperty
+}
+
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 94ecd6862a..31bf30c57c 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -146,6 +146,7 @@ private slots:
void aliasProperties();
void aliasPropertiesAndSignals();
void aliasPropertyChangeSignals();
+ void qtbug_89822();
void componentCompositeType();
void i18n();
void i18n_data();
@@ -2232,6 +2233,12 @@ void tst_qqmllanguage::aliasPropertiesAndSignals()
QCOMPARE(o->property("test").toBool(), true);
}
+void tst_qqmllanguage::qtbug_89822()
+{
+ QQmlComponent component(&engine, testFileUrl("qtbug_89822.qml"));
+ VERIFY_ERRORS("qtbug_89822.errors.txt");
+}
+
// Test that the root element in a composite type can be a Component
void tst_qqmllanguage::componentCompositeType()
{
diff --git a/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml b/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml
index aa26956922..09f1d472b7 100644
--- a/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml
+++ b/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml
@@ -10,6 +10,59 @@ Item {
property variant v2: Qt.vector3d(1,2,3)
property real factor: 2.23
+ function testTransformation() {
+ let m = Qt.matrix4x4();
+
+ m.scale(1, 2, 4);
+ if (m !== Qt.matrix4x4(1, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 4, 0,
+ 0, 0, 0, 1))
+ return false;
+ m.scale(Qt.vector3d(-8, -4, -2));
+ if (m !== Qt.matrix4x4(-8, 0, 0, 0,
+ 0,-8, 0, 0,
+ 0, 0, -8, 0,
+ 0, 0, 0, 1))
+ return false;
+ m.scale(-1 / 8);
+ if (m !== Qt.matrix4x4())
+ return false;
+
+ m.rotate(180, Qt.vector3d(1, 0, 0));
+ if (m !== Qt.matrix4x4(1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, -1, 0,
+ 0, 0, 0, 1))
+ return false;
+ m.rotate(180, Qt.vector3d(0, 1, 0));
+ if (m !== Qt.matrix4x4(-1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1))
+ return false;
+ m.rotate(180, Qt.vector3d(0, 0, 1));
+ if (m !== Qt.matrix4x4())
+ return false;
+
+ m.translate(Qt.vector3d(1, 2, 4));
+ if (m !== Qt.matrix4x4(1, 0, 0, 1,
+ 0, 1, 0, 2,
+ 0, 0, 1, 4,
+ 0, 0, 0, 1))
+ return false;
+
+ m = Qt.matrix4x4();
+ m.lookAt(Qt.vector3d(1, 2, 4), Qt.vector3d(1, 2, 0), Qt.vector3d(0, 1, 0));
+ if (m !== Qt.matrix4x4(1, 0, 0, -1,
+ 0, 1, 0, -2,
+ 0, 0, 1, -4,
+ 0, 0, 0, 1))
+ return false;
+
+ return true;
+ }
+
Component.onCompleted: {
success = true;
if (m1.times(m2) != Qt.matrix4x4(26, 26, 26, 26, 52, 52, 52, 52, 78, 78, 78, 78, 104, 104, 104, 104)) success = false;
@@ -27,5 +80,6 @@ Item {
if (m1.transposed() != Qt.matrix4x4(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4)) success = false;
if (m1.fuzzyEquals(m2)) success = false;
if (!m1.fuzzyEquals(m2, 10)) success = false;
+ if (!testTransformation()) success = false;
}
}
diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp
index 249ecd5aa5..a4f138c8df 100644
--- a/tests/auto/quick/nodes/tst_nodestest.cpp
+++ b/tests/auto/quick/nodes/tst_nodestest.cpp
@@ -125,12 +125,12 @@ public:
setRootNode(root);
}
- void render() {
+ void render() override {
++renderCount;
renderingOrder = ++globalRendereringOrder;
}
- void nodeChanged(QSGNode *node, QSGNode::DirtyState state) {
+ void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override {
changedNode = node;
changedState = state;
QSGBatchRenderer::Renderer::nodeChanged(node, state);
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml
new file mode 100644
index 0000000000..b24812c914
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml
@@ -0,0 +1,34 @@
+import QtQuick 2.15
+
+import Test 1.0
+
+Item {
+ width: 640
+ height: 480
+
+ Rectangle {
+ anchors.fill: parent
+ color: "grey"
+
+ Rectangle {
+ x: 200
+ y: 200
+ width: 100
+ height: 100
+ color: "orange"
+ DragHandler {
+ grabPermissions: DragHandler.CanTakeOverFromAnything // but not anything with keepMouseGrab!
+ }
+ }
+ }
+
+ ModalLayer {
+ anchors.fill: parent
+
+ Rectangle {
+ anchors.fill: parent
+ color: "red"
+ opacity: 0.4
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
index 4d6866041e..f71febbaf9 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
@@ -68,6 +68,7 @@ private slots:
void touchPassiveGrabbers_data();
void touchPassiveGrabbers();
void touchPinchAndMouseMove();
+ void underModalLayer();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
@@ -811,6 +812,59 @@ void tst_DragHandler::touchPinchAndMouseMove()
}
}
+class ModalLayer : public QQuickItem {
+public:
+ explicit ModalLayer(QQuickItem* parent = nullptr) : QQuickItem(parent) {
+ this->setAcceptedMouseButtons(Qt::AllButtons);
+ this->setAcceptTouchEvents(true);
+ this->setKeepMouseGrab(true);
+ this->setKeepTouchGrab(true);
+ }
+
+ bool event(QEvent* event) override {
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ case QEvent::MouseMove:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseTrackingChange:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::Wheel:
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchCancel:
+ case QEvent::TouchEnd: {
+ qCDebug(lcPointerTests) << "BLOCK!" << event->type();
+ return true;
+ }
+ default: break;
+ }
+ return QQuickItem::event(event);
+ }
+};
+
+void tst_DragHandler::underModalLayer() // QTBUG-78258
+{
+ qmlRegisterType<ModalLayer>("Test", 1, 0, "ModalLayer");
+
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "dragHandlerUnderModalLayer.qml");
+ QQuickView * window = windowPtr.data();
+ QPointer<QQuickDragHandler> dragHandler = window->rootObject()->findChild<QQuickDragHandler*>();
+ QVERIFY(dragHandler);
+
+ QPoint p1(250, 250);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ p1 += QPoint(dragThreshold, dragThreshold);
+ QTest::mouseMove(window, p1);
+ QVERIFY(!dragHandler->active());
+ p1 += QPoint(dragThreshold, dragThreshold);
+ QTest::mouseMove(window, p1);
+ QVERIFY(!dragHandler->active());
+ QTest::mouseRelease(window, Qt::LeftButton);
+}
+
QTEST_MAIN(tst_DragHandler)
#include "tst_qquickdraghandler.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
index 19fdae3b44..2b8ddd197d 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
@@ -217,6 +217,7 @@ void tst_QQuickPinchHandler::scale()
QQuickPinchHandler *pinchHandler = window->rootObject()->findChild<QQuickPinchHandler*>("pinchHandler");
QVERIFY(pinchHandler != nullptr);
+ QSignalSpy grabChangedSpy(pinchHandler, SIGNAL(grabChanged(QQuickEventPoint::GrabTransition, QQuickEventPoint*)));
QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
QVERIFY(root != nullptr);
@@ -238,6 +239,7 @@ void tst_QQuickPinchHandler::scale()
// it is outside its bounds.
pinchSequence.stationary(0).press(1, p1, window).commit();
QQuickTouchUtils::flush(window);
+ QTRY_COMPARE(grabChangedSpy.count(), 1); // passive grab
QPoint pd(10, 10);
// move one point until PinchHandler activates
@@ -247,6 +249,8 @@ void tst_QQuickPinchHandler::scale()
QQuickTouchUtils::flush(window);
}
QCOMPARE(pinchHandler->active(), true);
+ // first point got a passive grab; both points got exclusive grabs
+ QCOMPARE(grabChangedSpy.count(), 3);
QLineF line(p0, p1);
const qreal startLength = line.length();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_line.qml b/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
index dc960a24d0..fb4db5ae54 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
@@ -894,6 +894,37 @@ CanvasTestCase {
comparePixel(ctx, 39,0, 0,0,0,0);
}
+ function test_lineDashReset(row) {
+ var canvas = createCanvasObject(row);
+ var ctx = canvas.getContext('2d');
+ ctx.reset();
+ ctx.strokeStyle = "#ff0000";
+ ctx.lineWidth = 2;
+ var pattern = [2, 3, 5, 1, 6, 3]
+ ctx.setLineDash(pattern)
+
+ compare(ctx.getLineDash(), pattern);
+
+ pattern = []
+ ctx.setLineDash(pattern)
+ compare(ctx.getLineDash(), pattern);
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(40, 0);
+ ctx.stroke();
+
+ comparePixel(ctx, 0,0, 255,0,0,255);
+ comparePixel(ctx, 4,0, 255,0,0,255);
+ comparePixel(ctx, 5,0, 255,0,0,255);
+ comparePixel(ctx, 14,0, 255,0,0,255);
+ comparePixel(ctx, 20,0, 255,0,0,255);
+ comparePixel(ctx, 21,0, 255,0,0,255);
+ comparePixel(ctx, 22,0, 255,0,0,255);
+ comparePixel(ctx, 34,0, 255,0,0,255);
+ comparePixel(ctx, 35,0, 255,0,0,255);
+ }
+
function test_lineDashOffset(row) {
var canvas = createCanvasObject(row);
var ctx = canvas.getContext('2d');
diff --git a/tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml b/tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml
new file mode 100644
index 0000000000..ec419f8935
--- /dev/null
+++ b/tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+Item {
+ id: myObject
+ readonly property int testProperty: 0
+ readonly property QtObject myproperty: myObject
+}
+
diff --git a/tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml b/tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml
new file mode 100644
index 0000000000..88fd8509cc
--- /dev/null
+++ b/tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.11
+
+Rectangle {
+ objectName: "rootItem"
+ color: "white"
+ width: 800
+ height: 600
+
+ RecursiveProperty {
+ objectName: "recursiveProperty"
+
+ }
+}
diff --git a/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp b/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp
index b44977bd5a..0471619049 100644
--- a/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp
+++ b/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp
@@ -62,6 +62,7 @@ private slots:
void testNotifyPropertyChangeCallBack();
void testFixResourcePathsForObjectCallBack();
void testComponentOnCompleteSignal();
+ void testPropertyNames();
};
void tst_qquickdesignersupport::customData()
@@ -586,6 +587,50 @@ void tst_qquickdesignersupport::testComponentOnCompleteSignal()
}
}
+void tst_qquickdesignersupport::testPropertyNames()
+{
+#ifdef Q_CC_MINGW
+ QSKIP("QQuickDesignerSupportProperties::registerCustomData segfaults on mingw. QTBUG-90869");
+#endif
+
+ QScopedPointer<QQuickView> view(new QQuickView);
+ view->engine()->setOutputWarningsToStandardError(false);
+ view->setSource(testFileUrl("propertyNameTest.qml"));
+
+ QVERIFY(view->errors().isEmpty());
+ QQuickItem *rootItem = view->rootObject();
+ QVERIFY(rootItem);
+
+ QQuickDesignerSupport::PropertyNameList names = QQuickDesignerSupportProperties::allPropertyNames(rootItem);
+ QVERIFY(!names.isEmpty());
+ QVERIFY(names.contains("width"));
+ QVERIFY(names.contains("height"));
+ QVERIFY(names.contains("clip"));
+ QVERIFY(names.contains("opacity"));
+ QVERIFY(names.contains("childrenRect"));
+ QVERIFY(names.contains("activeFocus"));
+ QVERIFY(names.contains("border.width"));
+ names = QQuickDesignerSupportProperties::propertyNameListForWritableProperties(rootItem);
+ QVERIFY(!names.isEmpty());
+ QVERIFY(names.contains("width"));
+ QVERIFY(names.contains("height"));
+ QVERIFY(names.contains("opacity"));
+ QVERIFY(names.contains("clip"));
+ QVERIFY(!names.contains("childrenRect"));
+ QVERIFY(!names.contains("activeFocus"));
+ QVERIFY(names.contains("border.width"));
+
+ QQuickItem *recursiveProperty = findItem<QQuickItem>(rootItem, QLatin1String("recursiveProperty"));
+ QVERIFY(recursiveProperty);
+ names = QQuickDesignerSupportProperties::allPropertyNames(recursiveProperty);
+ QVERIFY(!names.isEmpty());
+ QVERIFY(names.contains("testProperty"));
+ QVERIFY(names.contains("myproperty.testProperty"));
+
+ names = QQuickDesignerSupportProperties::propertyNameListForWritableProperties(recursiveProperty);
+ QVERIFY(!names.isEmpty());
+ QVERIFY(!names.contains("testProperty"));
+}
QTEST_MAIN(tst_qquickdesignersupport)
diff --git a/tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml b/tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml
new file mode 100644
index 0000000000..a690c4243b
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml
@@ -0,0 +1,51 @@
+import QtQuick 2.12
+
+Item {
+ width: 240; height: 240
+ Loader {
+ id: loader
+ sourceComponent: surfaceParent
+ anchors.fill: parent
+
+ onStatusChanged: {
+ if (status === Loader.Ready) {
+ holder.create()
+ holder.item.parent = item
+ } else if (status === Loader.Null){
+ holder.item.parent = null
+ }
+ }
+ }
+
+ property var holder: QtObject {
+ property bool created: false
+ function create()
+ {
+ if (!created)
+ surfaceComponent.createObject(item)
+ created = true
+ }
+
+ property Item item: Item {
+ anchors.fill: parent
+ Component {
+ id: surfaceComponent
+ Item {
+ anchors.fill: parent
+ TextInput {
+ width: parent.width
+ font.pixelSize: 40
+ text: "focus me"
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: surfaceParent
+ Rectangle {
+ anchors.fill: parent
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index f65650cf9c..c8f251dbe1 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -33,6 +33,7 @@
#include <QtQuick/qquickitemgrabresult.h>
#include <QtQuick/qquickview.h>
#include <QtGui/private/qinputmethod_p.h>
+#include <QtQuick/private/qquickloader_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
@@ -75,6 +76,7 @@ private slots:
void qtbug_50516();
void qtbug_50516_2_data();
void qtbug_50516_2();
+ void focusableItemReparentedToLoadedComponent();
void keys();
#if QT_CONFIG(shortcut)
@@ -1312,6 +1314,35 @@ void tst_QQuickItem::qtbug_50516_2()
delete window;
}
+void tst_QQuickItem::focusableItemReparentedToLoadedComponent() // QTBUG-89736
+{
+ QQuickView window;
+ window.setSource(testFileUrl("focusableItemReparentedToLoadedComponent.qml"));
+ window.show();
+ QVERIFY(QTest::qWaitForWindowActive(&window));
+ QCOMPARE(QGuiApplication::focusWindow(), &window);
+ QQuickLoader *loader = window.rootObject()->findChild<QQuickLoader *>();
+ QVERIFY(loader);
+ QTRY_VERIFY(loader->status() == QQuickLoader::Ready);
+ QQuickTextInput *textInput = window.rootObject()->findChild<QQuickTextInput *>();
+ QVERIFY(textInput);
+
+ // click to focus
+ QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10});
+ QTRY_VERIFY(textInput->hasActiveFocus());
+
+ // unload and reload
+ auto component = loader->sourceComponent();
+ loader->resetSourceComponent();
+ QTRY_VERIFY(loader->status() == QQuickLoader::Null);
+ loader->setSourceComponent(component);
+ QTRY_VERIFY(loader->status() == QQuickLoader::Ready);
+
+ // click to focus again
+ QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10});
+ QTRY_VERIFY(textInput->hasActiveFocus());
+}
+
void tst_QQuickItem::keys()
{
QQuickView *window = new QQuickView(nullptr);
diff --git a/tests/auto/quick/qquicktextinput/data/focusReason.qml b/tests/auto/quick/qquicktextinput/data/focusReason.qml
new file mode 100644
index 0000000000..7ac913d363
--- /dev/null
+++ b/tests/auto/quick/qquicktextinput/data/focusReason.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.2
+
+Rectangle {
+ width: 400
+ height: 400
+
+ Column {
+ spacing: 5
+ TextInput {
+ id: first
+ objectName: "first"
+ width: 100
+ Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 }
+ KeyNavigation.backtab: third
+ KeyNavigation.tab: second
+ KeyNavigation.down: second
+ }
+ TextInput {
+ id: second
+ objectName: "second"
+ width: 100
+ Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 }
+ KeyNavigation.up: first
+ KeyNavigation.backtab: first
+ KeyNavigation.tab: third
+ }
+ TextInput {
+ objectName: "third"
+ id: third
+ width: 100
+ Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 }
+ KeyNavigation.backtab: second
+ KeyNavigation.tab: first
+ }
+ Component.onCompleted: {
+ first.focus = true
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
index ac502bcb28..7c5c09055d 100644
--- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
+++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
@@ -237,6 +237,8 @@ private slots:
void QTBUG_77814_InsertRemoveNoSelection();
void checkCursorDelegateWhenPaddingChanged();
+
+ void focusReason();
private:
void simulateKey(QWindow *, int key);
@@ -7084,6 +7086,84 @@ void tst_qquicktextinput::checkCursorDelegateWhenPaddingChanged()
QCOMPARE(cursorDelegate->y(), textInput->topPadding());
}
+/*!
+ Verifies that TextInput items get focus in/out events with the
+ correct focus reason set.
+
+ Up and Down keys translates to Backtab and Tab focus reasons.
+
+ See QTBUG-75862.
+*/
+void tst_qquicktextinput::focusReason()
+{
+ QQuickView view;
+ view.setSource(testFileUrl("focusReason.qml"));
+
+ QQuickTextInput *first = view.rootObject()->findChild<QQuickTextInput *>("first");
+ QQuickTextInput *second = view.rootObject()->findChild<QQuickTextInput *>("second");
+ QQuickTextInput *third = view.rootObject()->findChild<QQuickTextInput *>("third");
+ QVERIFY(first && second && third);
+
+ class FocusEventFilter : public QObject
+ {
+ public:
+ using QObject::QObject;
+
+ QHash<QObject*, Qt::FocusReason> lastFocusReason;
+ protected:
+ bool eventFilter(QObject *o, QEvent *e)
+ {
+ if (e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut) {
+ QFocusEvent *fe = static_cast<QFocusEvent*>(e);
+ lastFocusReason[o] = fe->reason();
+ }
+ return QObject::eventFilter(o, e);
+ }
+ } eventFilter;
+ first->installEventFilter(&eventFilter);
+ second->installEventFilter(&eventFilter);
+ third->installEventFilter(&eventFilter);
+
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QCOMPARE(qApp->focusObject(), first);
+ // on some platforms we don't get ActiveWindowFocusReason; tolerate this,
+ // it's not what we are testing in this test
+ if (eventFilter.lastFocusReason[first] != Qt::ActiveWindowFocusReason) {
+ QEXPECT_FAIL("", qPrintable(QString("No window activation event on the %1 platform")
+ .arg(QGuiApplication::platformName())),
+ Continue);
+ }
+ QCOMPARE(eventFilter.lastFocusReason[first], Qt::ActiveWindowFocusReason);
+
+ QTest::mouseClick(&view, Qt::LeftButton, {},
+ (second->boundingRect().center() + second->position()).toPoint());
+ QTRY_COMPARE(qApp->focusObject(), second);
+ QCOMPARE(eventFilter.lastFocusReason[first], Qt::MouseFocusReason);
+ QCOMPARE(eventFilter.lastFocusReason[second], Qt::MouseFocusReason);
+
+ QTest::keyClick(&view, Qt::Key_Tab);
+ QCOMPARE(qApp->focusObject(), third);
+ QCOMPARE(eventFilter.lastFocusReason[second], Qt::TabFocusReason);
+ QCOMPARE(eventFilter.lastFocusReason[third], Qt::TabFocusReason);
+
+ QTest::keyClick(&view, Qt::Key_Backtab);
+ QCOMPARE(qApp->focusObject(), second);
+ QCOMPARE(eventFilter.lastFocusReason[third], Qt::BacktabFocusReason);
+ QCOMPARE(eventFilter.lastFocusReason[second], Qt::BacktabFocusReason);
+
+ QTest::keyClick(&view, Qt::Key_Up);
+ QCOMPARE(qApp->focusObject(), first);
+ QCOMPARE(eventFilter.lastFocusReason[second], Qt::BacktabFocusReason);
+ QCOMPARE(eventFilter.lastFocusReason[first], Qt::BacktabFocusReason);
+
+ QTest::keyClick(&view, Qt::Key_Down);
+ QCOMPARE(qApp->focusObject(), second);
+ QCOMPARE(eventFilter.lastFocusReason[second], Qt::TabFocusReason);
+ QCOMPARE(eventFilter.lastFocusReason[first], Qt::TabFocusReason);
+}
+
QTEST_MAIN(tst_qquicktextinput)
#include "tst_qquicktextinput.moc"
diff --git a/tests/manual/pointer/pinchHandler.qml b/tests/manual/pointer/pinchHandler.qml
index 46ab91c2ed..93169da60a 100644
--- a/tests/manual/pointer/pinchHandler.qml
+++ b/tests/manual/pointer/pinchHandler.qml
@@ -154,6 +154,14 @@ Rectangle {
if (!active)
anim.restart(centroid.velocity)
}
+ onGrabChanged: function (transition, point) {
+ if (transition === 0x10) { // GrabExclusive
+ console.log(point.id, "grabbed @", point.position)
+ Qt.createQmlObject("import QtQuick 2.0; Rectangle { opacity: 0.5; border.color: 'red'; radius: 8; width: radius * 2; height: radius * 2; " +
+ "x: " + (point.position.x - 8) + "; y: " + (point.position.y - 8) + "}",
+ rect3, "touchpoint" + point.id);
+ }
+ }
}
TapHandler { gesturePolicy: TapHandler.DragThreshold; onTapped: rect3.z = rect2.z + 1 }
MomentumAnimation { id: anim; target: rect3 }