aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTarja Sundqvist <tarja.sundqvist@qt.io>2025-12-15 16:14:22 +0200
committerTarja Sundqvist <tarja.sundqvist@qt.io>2025-12-15 16:14:22 +0200
commitb58ec3b086518da5aa573f99426235854c23e35f (patch)
tree861a9935d8f1cdba2fdca546836a351736dbddbf /src
parent4826f86e274f1b29bd769e6790824f9e62a40f62 (diff)
parent22032227d16c39211e2ebceef97d21f4d89c7c87 (diff)
Merge tag 'v6.5.8-lts-lgpl' into 6.56.5
Qt 6.5.8-lts-lgpl release
Diffstat (limited to 'src')
-rw-r--r--src/imports/tooling/Component.qml1
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp9
-rw-r--r--src/qml/doc/snippets/qml/qsTrId.1.qml17
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_module.qdoc4
-rw-r--r--src/qml/jsruntime/qv4engine.cpp11
-rw-r--r--src/qml/jsruntime/qv4engine_p.h2
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp2
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp10
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp2
-rw-r--r--src/qml/qml/ftw/qqmlthread.cpp3
-rw-r--r--src/qml/qml/qqml.cpp8
-rw-r--r--src/qml/qml/qqml.h5
-rw-r--r--src/qml/qml/qqmlbuiltinfunctions.cpp7
-rw-r--r--src/qml/qml/qqmlcomponent.cpp48
-rw-r--r--src/qml/qml/qqmlcomponent_p.h43
-rw-r--r--src/qml/qml/qqmlcustomparser.cpp2
-rw-r--r--src/qml/qml/qqmlimport_p.h20
-rw-r--r--src/qml/qml/qqmllocale.cpp1
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp13
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h30
-rw-r--r--src/qml/qml/qqmlproperty.cpp12
-rw-r--r--src/qml/qml/qqmltypeloader.cpp7
-rw-r--r--src/qml/qml/qqmltypeloader_p.h1
-rw-r--r--src/qml/qml/qqmltypeloaderthread.cpp11
-rw-r--r--src/qml/qml/qqmltypeloaderthread_p.h2
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp10
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp6
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp16
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h6
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp7
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp3
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp81
-rw-r--r--src/qmlmodels/qqmldelegatemodel_p_p.h2
-rw-r--r--src/qmltyperegistrar/qqmltypesclassdescription.cpp37
-rw-r--r--src/qmltyperegistrar/qqmltypesclassdescription_p.h3
-rw-r--r--src/qmltyperegistrar/qqmltypescreator.cpp26
-rw-r--r--src/qmltyperegistrar/qqmltypescreator_p.h5
-rw-r--r--src/quick/accessible/qaccessiblequickview.cpp7
-rw-r--r--src/quick/doc/snippets/qml/listview/hideDelegate.qml27
-rw-r--r--src/quick/doc/snippets/qml/listview/listview.qml9
-rw-r--r--src/quick/doc/snippets/qml/listview/stateInDelegate.qml20
-rw-r--r--src/quick/doc/snippets/qml/listview/stateInModel.qml26
-rw-r--r--src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc6
-rw-r--r--src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc17
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp12
-rw-r--r--src/quick/items/qquickflickable.cpp4
-rw-r--r--src/quick/items/qquickitem.cpp60
-rw-r--r--src/quick/items/qquickitemview.cpp4
-rw-r--r--src/quick/items/qquicklistview.cpp46
-rw-r--r--src/quick/items/qquickmousearea.cpp9
-rw-r--r--src/quick/items/qquickscreen.cpp2
-rw-r--r--src/quick/items/qquickwindow.cpp29
-rw-r--r--src/quick/items/qquickwindow_p.h1
-rw-r--r--src/quick/util/qquickpixmapcache.cpp23
-rw-r--r--src/quickcontrols/basic/impl/qquickbasicbusyindicator.cpp12
-rw-r--r--src/quickcontrols/basic/impl/qquickbasicbusyindicator_p.h1
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-combobox-custom.qml2
-rw-r--r--src/quickcontrols/doc/src/includes/customize-button-background.qdocinc2
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc4
-rw-r--r--src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp12
-rw-r--r--src/quickcontrols/fusion/impl/qquickfusionbusyindicator_p.h1
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialbusyindicator.cpp14
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialbusyindicator_p.h1
-rw-r--r--src/quickdialogs/quickdialogs/qquickfiledialog.cpp12
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/MessageDialog.qml4
-rw-r--r--src/quicktemplates/doc/src/qtquicktemplates-qmltypes.qdoc13
-rw-r--r--src/quicktemplates/qquickcombobox.cpp11
-rw-r--r--src/quicktemplates/qquickdeferredexecute.cpp25
-rw-r--r--src/quicktemplates/qquickmenu.cpp8
-rw-r--r--src/quicktemplates/qquickmenu_p_p.h1
-rw-r--r--src/quicktemplates/qquickoverlay.cpp33
-rw-r--r--src/quicktemplates/qquickpopup.cpp15
-rw-r--r--src/quicktestutils/quick/viewtestutils.cpp109
-rw-r--r--src/quicktestutils/quick/viewtestutils_p.h22
-rw-r--r--src/quicktestutils/quick/visualtestutils.cpp4
-rw-r--r--src/quickwidgets/qquickwidget.cpp1
76 files changed, 785 insertions, 297 deletions
diff --git a/src/imports/tooling/Component.qml b/src/imports/tooling/Component.qml
index 773fcfecda..c7c7607870 100644
--- a/src/imports/tooling/Component.qml
+++ b/src/imports/tooling/Component.qml
@@ -22,6 +22,7 @@ QtObject {
property bool isComposite: false
property bool hasCustomParser: false
property bool extensionIsNamespace: false
+ property bool enforcesScopedEnums: false
property string accessSemantics: "reference"
property string defaultProperty
property string parentProperty
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
index e1e98537da..0686b9e14a 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
@@ -120,8 +120,13 @@ void QQmlPreviewServiceImpl::engineAboutToBeRemoved(QJSEngine *engine)
void QQmlPreviewServiceImpl::stateChanged(QQmlDebugService::State state)
{
- m_fileEngine.reset(state == Enabled ? new QQmlPreviewFileEngineHandler(m_loader.data())
- : nullptr);
+ if (state == Enabled) {
+ QV4::ExecutionEngine::setPreviewing(true);
+ m_fileEngine.reset(new QQmlPreviewFileEngineHandler(m_loader.data()));
+ } else {
+ QV4::ExecutionEngine::setPreviewing(false);
+ m_fileEngine.reset();
+ }
}
void QQmlPreviewServiceImpl::forwardRequest(const QString &file)
diff --git a/src/qml/doc/snippets/qml/qsTrId.1.qml b/src/qml/doc/snippets/qml/qsTrId.1.qml
index 4989adad25..6776620c77 100644
--- a/src/qml/doc/snippets/qml/qsTrId.1.qml
+++ b/src/qml/doc/snippets/qml/qsTrId.1.qml
@@ -3,9 +3,18 @@
import QtQuick
+Item {
//![0]
-Text {
- //% "hello"
- text: qsTrId("hello_id")
-}
+ Text {
+ //% "hello"
+ text: qsTrId("hello_id")
+ }
//![0]
+
+//![1]
+ Text {
+ /*% "hello" */
+ text: qsTrId("hello_id")
+ }
+//![1]
+}
diff --git a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
index 79c3cd8082..353505c78f 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
@@ -520,8 +520,8 @@ class MyItem: public QQuickItem { ... };
\endcode
then one has to make sure that the module containing \c QQuickItem, called
-\c Quick, is declared as a dependency via the \c DEPENDENCIES option. Not doing
-so might result in errors during type compilation with
+\c QtQuick, is declared as a dependency via the \c DEPENDENCIES option. Not
+doing so might result in errors during type compilation with
\l{QML type compiler}{qmltc} or during binding and function compilation to C++
with \l{qmlcachegen-auto}{qmlcachegen}.
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 99fad7e3c3..a807cc42df 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -105,7 +105,8 @@ using namespace QV4;
// odd while the statics are being initialized, and stays even afterwards.
// Any further engines created while the statics are being initialized busy-wait until engineSerial
// is even.
-static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
+Q_CONSTINIT static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
+Q_CONSTINIT static QBasicAtomicInt hasPreview = Q_BASIC_ATOMIC_INITIALIZER(0);
int ExecutionEngine::s_maxCallDepth = -1;
int ExecutionEngine::s_jitCallCountThreshold = 3;
int ExecutionEngine::s_maxJSStackSize = 4 * 1024 * 1024;
@@ -889,6 +890,12 @@ void ExecutionEngine::setProfiler(Profiling::Profiler *profiler)
Q_ASSERT(!m_profiler);
m_profiler.reset(profiler);
}
+
+void ExecutionEngine::setPreviewing(bool enabled)
+{
+ hasPreview.storeRelease(enabled);
+}
+
#endif // QT_CONFIG(qml_debug)
void ExecutionEngine::initRootContext()
@@ -2212,7 +2219,7 @@ QV4::Value *ExecutionEngine::registerNativeModule(const QUrl &url, const QV4::Va
bool ExecutionEngine::diskCacheEnabled() const
{
- return (!disableDiskCache() && !debugger()) || forceDiskCache();
+ return (!disableDiskCache() && !debugger() && !hasPreview.loadAcquire()) || forceDiskCache();
}
void ExecutionEngine::callInContext(QV4::Function *function, QObject *self,
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 75c8efd67e..99b9e526f6 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -514,12 +514,14 @@ public:
void setDebugger(Debugging::Debugger *) {}
void setProfiler(Profiling::Profiler *) {}
+ static void setPreviewing(bool) {}
#else
QV4::Debugging::Debugger *debugger() const { return m_debugger.data(); }
QV4::Profiling::Profiler *profiler() const { return m_profiler.data(); }
void setDebugger(Debugging::Debugger *debugger);
void setProfiler(Profiling::Profiler *profiler);
+ static void setPreviewing(bool enabled);
#endif // QT_CONFIG(qml_debug)
ExecutionContext *currentContext() const { return currentStackFrame->context(); }
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index 90eb326d65..eac2aca059 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -94,6 +94,8 @@ void Heap::ForInIteratorObject::markObjects(Heap::Base *that, MarkStack *markSta
o->object->mark(markStack);
if (o->current)
o->current->mark(markStack);
+ if (o->target)
+ o->target->mark(markStack);
o->workArea[0].mark(markStack);
o->workArea[1].mark(markStack);
Object::markObjects(that, markStack);
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 50389e06ec..ecd947ccc3 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -1021,6 +1021,16 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
ReturnedValue QObjectWrapper::lookupAttached(
Lookup *l, ExecutionEngine *engine, const Value &object)
{
+ if (&QObjectWrapper::lookupAttached == &Lookup::getterGeneric) {
+ // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
+ // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
+ // decides how to retrieve the property.
+ qFatal("Your C++ compiler is broken.");
+ }
+
+ // This getter marks the presence of a lookup for an attached object.
+ // It falls back to the generic lookup when run through the interpreter, but AOT-compiled
+ // code can get clever with it.
return Lookup::getterGeneric(l, engine, object);
}
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 98bb6347d3..2613cff99a 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -777,7 +777,7 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val
nMatchOffsets += re->captureCount() * 2;
if (!regExp->global())
break;
- offset = qMax(offset + 1, matchOffsets[oldSize + 1]);
+ offset = qMax(offset, matchOffsets[oldSize + 1]) + 1;
}
if (regExp->global()) {
regExp->setLastIndex(0);
diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp
index d91dfb95db..7ffdffad7b 100644
--- a/src/qml/qml/ftw/qqmlthread.cpp
+++ b/src/qml/qml/ftw/qqmlthread.cpp
@@ -195,6 +195,9 @@ void QQmlThread::shutdown()
d->m_shutdown = true;
+ if (d->mainSync)
+ d->wakeOne();
+
if (QCoreApplication::closingDown())
d->quit();
else
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 94a91629f3..44e8955b7a 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -1602,7 +1602,13 @@ static void initTypeWrapperLookup(
QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
scope, QV4::QQmlTypeWrapper::create(
scope.engine, nullptr, context->qmlContext->imports(), importRef));
- wrapper = l->qmlContextPropertyGetter(l, context->engine->handle(), wrapper);
+
+ // This is not a contextGetter since we actually load from the namespace.
+ wrapper = l->getter(l, context->engine->handle(), wrapper);
+
+ // In theory, the getter may have populated the lookup's property cache.
+ l->releasePropertyCache();
+
l->qmlContextPropertyGetter = qmlContextPropertyGetter;
if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton)
l->qmlContextSingletonLookup.singletonObject = wrapper->heapObject();
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 0650cee167..73e8a09601 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -630,6 +630,11 @@ QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true)
// super types should be registered as CppType (or not at all). We only need the object and its
// QML engine to resolve composite types. Therefore, the function is actually a static property
// of the C++ type system and we can cache it here for improved performance on further lookups.
+ if (const auto func = QQmlPrivate::attachedPropertiesFunc<T>())
+ return qmlAttachedPropertiesObject(const_cast<QObject *>(obj), func, create);
+
+ // Usually the above func should not be nullptr. However, to be safe, keep this fallback
+ // via the metaobject.
static const auto func = qmlAttachedPropertiesFunction(nullptr, &T::staticMetaObject);
return qmlAttachedPropertiesObject(const_cast<QObject *>(obj), func, create);
}
diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp
index fdc28d727d..0251a72049 100644
--- a/src/qml/qml/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/qqmlbuiltinfunctions.cpp
@@ -2307,12 +2307,13 @@ ReturnedValue GlobalExtensions::method_qsTrNoOp(const FunctionObject *, const Va
\tt{//% <string>}
+ \snippet qml/qsTrId.1.qml 0
+
or
- \tt{\\begincomment% <string> \\endcomment}
+ \tt{\begincomment% <string> \endcomment}
- Example:
- \snippet qml/qsTrId.1.qml 0
+ \snippet qml/qsTrId.1.qml 1
Creating binary translation (QM) files suitable for use with this function requires passing
the \c -idbased option to the \c lrelease tool.
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 5a6fd4c8d3..7b2107b858 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -455,6 +455,10 @@ QQmlComponent::~QQmlComponent()
if (d->typeData) {
d->typeData->unregisterCallback(d);
+ if (d->engine) {
+ QQmlEnginePrivate::get(d->engine)->typeLoader.drop(
+ QQmlDataBlob::Ptr(d->typeData.data()));
+ }
d->typeData.reset();
}
}
@@ -1013,6 +1017,14 @@ QObject *QQmlComponent::beginCreate(QQmlContext *context)
return d->beginCreate(QQmlContextData::get(context));
}
+static QQmlParserStatus *parserStatusCast(const QQmlType &type, QObject *rv)
+{
+ const int parserStatusCast = type.parserStatusCast();
+ return parserStatusCast == -1
+ ? nullptr
+ : reinterpret_cast<QQmlParserStatus *>(reinterpret_cast<char *>(rv) + parserStatusCast);
+}
+
QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> context)
{
Q_Q(QQmlComponent);
@@ -1074,7 +1086,7 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
if (!loadedType.isValid()) {
enginePriv->referenceScarceResources();
- state.initCreator(std::move(context), compilationUnit, creationContext);
+ state.initCreator(context, compilationUnit, creationContext);
rv = state.creator()->create(start, nullptr, nullptr, isInlineComponent ? QQmlObjectCreator::InlineComponent : QQmlObjectCreator::NormalObject);
if (!rv)
state.appendCreatorErrors();
@@ -1083,26 +1095,21 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
// TODO: extract into function
rv = loadedType.createWithQQmlData();
QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(rv);
- QQmlParserStatus *parserStatus = nullptr;
- const int parserStatusCast = loadedType.parserStatusCast();
- if (parserStatusCast != -1) {
- parserStatus = reinterpret_cast<QQmlParserStatus*>(reinterpret_cast<char *>(rv) + parserStatusCast);
+ if (QQmlParserStatus *parserStatus = parserStatusCast(loadedType, rv)) {
parserStatus->classBegin();
+ state.ensureRequiredPropertyStorage(rv);
+ } else if (loadedType.finalizerCast() != -1) {
+ state.ensureRequiredPropertyStorage(rv);
}
+
for (int i = 0, propertyCount = propertyCache->propertyCount(); i < propertyCount; ++i) {
if (const QQmlPropertyData *propertyData = propertyCache->property(i); propertyData->isRequired()) {
- state.ensureRequiredPropertyStorage();
+ state.ensureRequiredPropertyStorage(rv);
RequiredPropertyInfo info;
info.propertyName = propertyData->name(rv);
state.addPendingRequiredProperty(rv, propertyData, info);
}
}
- if (parserStatus)
- parserStatus->componentComplete();
- if (const int finalizerCast = loadedType.finalizerCast(); finalizerCast != -1) {
- auto* hook = reinterpret_cast<QQmlFinalizerHook *>(reinterpret_cast<char *>(rv) + finalizerCast);
- hook->componentFinalized();
- }
}
if (rv) {
@@ -1113,6 +1120,12 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
ddata->indestructible = true;
ddata->explicitIndestructibleSet = true;
ddata->rootObjectInCreation = false;
+
+ // Assign parent context to the object if we haven't created one.
+ if (!ddata->outerContext)
+ ddata->outerContext = context.data();
+ if (!ddata->context)
+ ddata->context = context.data();
}
return rv;
@@ -1249,7 +1262,18 @@ void QQmlComponentPrivate::completeCreate()
state.errors.push_back(QQmlComponentPrivate::AnnotatedQmlError { error, true });
}
}
+
if (loadedType.isValid()) {
+ QObject *rv = state.target();
+ if (QQmlParserStatus *parserStatus = parserStatusCast(loadedType, rv))
+ parserStatus->componentComplete();
+
+ if (const int finalizerCast = loadedType.finalizerCast(); finalizerCast != -1) {
+ auto *hook = reinterpret_cast<QQmlFinalizerHook *>(
+ reinterpret_cast<char *>(rv) + finalizerCast);
+ hook->componentFinalized();
+ }
+
/*
We can directly set completePending to false, as finalize is only concerned
with setting up pending bindings, but that cannot happen here, as we're
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index df6ccc58ca..35e4a3b219 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -111,7 +111,7 @@ public:
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QQmlComponentPrivate::ConstructionState);
- inline void ensureRequiredPropertyStorage();
+ inline void ensureRequiredPropertyStorage(QObject *target);
inline RequiredProperties *requiredProperties();
inline void addPendingRequiredProperty(
const QObject *object, const QQmlPropertyData *propData,
@@ -126,16 +126,31 @@ public:
inline const QQmlObjectCreator *creator() const;
inline void clear();
inline bool hasCreator() const;
- inline QQmlObjectCreator *initCreator(QQmlRefPointer<QQmlContextData> parentContext,
- const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
- const QQmlRefPointer<QQmlContextData> &creationContext);
+ inline QQmlObjectCreator *initCreator(
+ const QQmlRefPointer<QQmlContextData> &parentContext,
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QQmlRefPointer<QQmlContextData> &creationContext);
QList<AnnotatedQmlError> errors;
inline bool isCompletePending() const;
inline void setCompletePending(bool isPending);
- private:
- QBiPointer<QQmlObjectCreator, RequiredProperties> m_creatorOrRequiredProperties;
+ QObject *target() const
+ {
+ if (m_creatorOrRequiredProperties.isNull())
+ return nullptr;
+
+ if (m_creatorOrRequiredProperties.isT1()) {
+ const auto &objects = m_creatorOrRequiredProperties.asT1()->allCreatedObjects();
+ return objects.isEmpty() ? nullptr : objects.at(0);
+ }
+
+ Q_ASSERT(m_creatorOrRequiredProperties.isT2());
+ return m_creatorOrRequiredProperties.asT2()->target;
+ }
+
+ private:
+ QBiPointer<QQmlObjectCreator, RequiredPropertiesAndTarget> m_creatorOrRequiredProperties;
};
ConstructionState state;
@@ -267,15 +282,17 @@ inline void QQmlComponentPrivate::ConstructionState::clear()
}
}
-inline QQmlObjectCreator *QQmlComponentPrivate::ConstructionState::initCreator(QQmlRefPointer<QQmlContextData> parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QQmlRefPointer<QQmlContextData> &creationContext)
+inline QQmlObjectCreator *QQmlComponentPrivate::ConstructionState::initCreator(
+ const QQmlRefPointer<QQmlContextData> &parentContext,
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QQmlRefPointer<QQmlContextData> &creationContext)
{
if (m_creatorOrRequiredProperties.isT1())
delete m_creatorOrRequiredProperties.asT1();
else
delete m_creatorOrRequiredProperties.asT2();
m_creatorOrRequiredProperties = new QQmlObjectCreator(
- std::move(parentContext), compilationUnit,
- creationContext);
+ parentContext, compilationUnit, creationContext);
return m_creatorOrRequiredProperties.asT1();
}
@@ -293,13 +310,15 @@ inline void QQmlComponentPrivate::ConstructionState::setCompletePending(bool isP
\internal
This is meant to be used in the context of QQmlComponent::loadFromModule,
when dealing with a C++ type. In that case, we do not have a creator,
- and need a separate storage for required properties.
+ and need a separate storage for required properties and the target object.
*/
-inline void QQmlComponentPrivate::ConstructionState::ensureRequiredPropertyStorage()
+inline void QQmlComponentPrivate::ConstructionState::ensureRequiredPropertyStorage(QObject *target)
{
Q_ASSERT(m_creatorOrRequiredProperties.isT2() || m_creatorOrRequiredProperties.isNull());
if (m_creatorOrRequiredProperties.isNull())
- m_creatorOrRequiredProperties = new RequiredProperties;
+ m_creatorOrRequiredProperties = new RequiredPropertiesAndTarget(target);
+ else
+ m_creatorOrRequiredProperties.asT2()->target = target;
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp
index d3f43cb8c4..d68e778ce2 100644
--- a/src/qml/qml/qqmlcustomparser.cpp
+++ b/src/qml/qml/qqmlcustomparser.cpp
@@ -132,7 +132,7 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
// Allow recursion so that we can find enums from the same document.
const QQmlTypeNameCache::Result result
= imports.asT2()->query<QQmlImport::AllowRecursion>(scope);
- if (result.isValid()) {
+ if (result.type.isValid()) {
type = result.type;
} else if (result.importNamespace) {
dot = nextDot(dot);
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index ae1bd87035..fe8ae5295c 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -297,6 +297,18 @@ public:
void addPluginPath(const QString& path);
+ static void sanitizeUNCPath(QString *path)
+ {
+ // This handles the UNC path case as when the path is retrieved from the QUrl it
+ // will convert the host name from upper case to lower case. So the absoluteFilePath
+ // is changed at this point to make sure it will match later on in that case.
+ if (path->startsWith(QStringLiteral("//"))) {
+ // toLocalFile() since that faithfully restores all the things you can do to a
+ // path but not a URL, in particular weird characters like '%'.
+ *path = QUrl::fromLocalFile(*path).toLocalFile();
+ }
+ }
+
template<typename Callback>
LocalQmldirResult locateLocalQmldir(
const QString &uri, QTypeRevision version, LocalQmldirSearchLocation location,
@@ -397,13 +409,7 @@ QQmlImportDatabase::LocalQmldirResult QQmlImportDatabase::locateLocalQmldir(
url = QStringLiteral("qrc") + absolutePath;
} else {
url = QUrl::fromLocalFile(absolutePath).toString();
- // This handles the UNC path case as when the path is retrieved from the QUrl it
- // will convert the host name from upper case to lower case. So the absoluteFilePath
- // is changed at this point to make sure it will match later on in that case.
- if (qmldirAbsoluteFilePath.startsWith(QStringLiteral("//"))) {
- qmldirAbsoluteFilePath = QUrl::fromLocalFile(qmldirAbsoluteFilePath)
- .toString(QUrl::RemoveScheme);
- }
+ sanitizeUNCPath(&qmldirAbsoluteFilePath);
}
QmldirCache *cache = new QmldirCache;
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index d0025324f4..64e105c74c 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -8,7 +8,6 @@
#include <QtCore/qtimezone.h>
#include <private/qlocale_p.h>
-#include <private/qlocale_data_p.h>
#include <private/qv4dateobject_p.h>
#include <private/qv4numberobject_p.h>
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 80db703e73..119053828f 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -52,7 +52,7 @@ Q_TRACE_POINT(qtqml, QQmlObjectCreator_createInstance_entry, const QV4::Compiled
Q_TRACE_POINT(qtqml, QQmlObjectCreator_createInstance_exit, const QString &typeName)
QQmlObjectCreator::QQmlObjectCreator(
- QQmlRefPointer<QQmlContextData> parentContext,
+ const QQmlRefPointer<QQmlContextData> &parentContext,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
const QQmlRefPointer<QQmlContextData> &creationContext,
QQmlIncubatorPrivate *incubator)
@@ -64,7 +64,7 @@ QQmlObjectCreator::QQmlObjectCreator(
, isContextObject(true)
, incubator(incubator)
{
- init(std::move(parentContext));
+ init(parentContext);
sharedState->componentAttached = nullptr;
sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount());
@@ -83,7 +83,8 @@ QQmlObjectCreator::QQmlObjectCreator(
}
}
-QQmlObjectCreator::QQmlObjectCreator(QQmlRefPointer<QQmlContextData> parentContext,
+QQmlObjectCreator::QQmlObjectCreator(
+ const QQmlRefPointer<QQmlContextData> &parentContext,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
QQmlObjectCreatorSharedState *inheritedSharedState, bool isContextObject)
: phase(Startup)
@@ -94,12 +95,12 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlRefPointer<QQmlContextData> parentConte
, isContextObject(isContextObject)
, incubator(nullptr)
{
- init(std::move(parentContext));
+ init(parentContext);
}
-void QQmlObjectCreator::init(QQmlRefPointer<QQmlContextData> providedParentContext)
+void QQmlObjectCreator::init(const QQmlRefPointer<QQmlContextData> &providedParentContext)
{
- parentContext = std::move(providedParentContext);
+ parentContext = providedParentContext;
engine = parentContext->engine();
v4 = engine->handle();
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index 36249b45bc..07d8da213d 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -78,6 +78,17 @@ private:
class RequiredProperties : public QHash<RequiredPropertyKey, RequiredPropertyInfo> {};
+class RequiredPropertiesAndTarget : public RequiredProperties
+{
+public:
+ RequiredPropertiesAndTarget(QObject *target) : target(target) {}
+ RequiredPropertiesAndTarget(const RequiredPropertiesAndTarget &) = default;
+ RequiredPropertiesAndTarget(RequiredPropertiesAndTarget &&) = default;
+ RequiredPropertiesAndTarget &operator=(const RequiredPropertiesAndTarget &) = default;
+ RequiredPropertiesAndTarget &operator=(RequiredPropertiesAndTarget &&) = default;
+ QObject *target = nullptr;
+};
+
struct DeferredQPropertyBinding {
QObject *target = nullptr;
int properyIndex = -1;
@@ -105,10 +116,11 @@ class Q_QML_PRIVATE_EXPORT QQmlObjectCreator
{
Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator)
public:
- QQmlObjectCreator(QQmlRefPointer<QQmlContextData> parentContext,
- const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
- const QQmlRefPointer<QQmlContextData> &creationContext,
- QQmlIncubatorPrivate *incubator = nullptr);
+ QQmlObjectCreator(
+ const QQmlRefPointer<QQmlContextData> &parentContext,
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QQmlRefPointer<QQmlContextData> &creationContext,
+ QQmlIncubatorPrivate *incubator = nullptr);
~QQmlObjectCreator();
enum CreationFlags { NormalObject = 1, InlineComponent = 2 };
@@ -159,12 +171,12 @@ public:
}
private:
- QQmlObjectCreator(QQmlRefPointer<QQmlContextData> contextData,
- const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
- QQmlObjectCreatorSharedState *inheritedSharedState,
- bool isContextObject);
+ QQmlObjectCreator(
+ const QQmlRefPointer<QQmlContextData> &contextData,
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ QQmlObjectCreatorSharedState *inheritedSharedState, bool isContextObject);
- void init(QQmlRefPointer<QQmlContextData> parentContext);
+ void init(const QQmlRefPointer<QQmlContextData> &parentContext);
QObject *createInstance(int index, QObject *parent = nullptr, bool isContextObject = false);
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index 1430c20099..e8d5b0c8cb 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -872,18 +872,18 @@ static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPrope
oldBinding = oldBinding->nextBinding();
}
- if (valueTypeIndex != -1
- && oldBinding
- && oldBinding->kind() == QQmlAbstractBinding::ValueTypeProxy) {
- oldBinding = static_cast<QQmlValueTypeProxyBinding *>(oldBinding.data())->binding(index);
- }
-
if (!oldBinding) {
// Clear the binding bit so that the binding doesn't appear later for any reason
data->clearBindingBit(coreIndex);
return;
}
+ if (valueTypeIndex != -1 && oldBinding->kind() == QQmlAbstractBinding::ValueTypeProxy) {
+ oldBinding = static_cast<QQmlValueTypeProxyBinding *>(oldBinding.data())->binding(index);
+ if (!oldBinding)
+ return;
+ }
+
if (!(flags & QQmlPropertyPrivate::DontEnable))
oldBinding->setEnabled(false, {});
oldBinding->removeFromObject();
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 5c9916193d..72ad77b41c 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -213,6 +213,13 @@ void QQmlTypeLoader::loadWithCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::C
doLoad(CachedLoader(unit), blob, mode);
}
+void QQmlTypeLoader::drop(const QQmlDataBlob::Ptr &blob)
+{
+ // We must not destroy a QQmlDataBlob from the main thread
+ // since it will shuffle its dependencies around.
+ m_thread->drop(blob);
+}
+
void QQmlTypeLoader::loadWithStaticDataThread(const QQmlDataBlob::Ptr &blob, const QByteArray &data)
{
ASSERT_LOADTHREAD();
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 1e1a03c607..854778125a 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -151,6 +151,7 @@ public:
void load(QQmlDataBlob *, Mode = PreferSynchronous);
void loadWithStaticData(QQmlDataBlob *, const QByteArray &, Mode = PreferSynchronous);
void loadWithCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit, Mode mode = PreferSynchronous);
+ void drop(const QQmlDataBlob::Ptr &blob);
QQmlEngine *engine() const;
void initializeEngine(QQmlEngineExtensionInterface *, const char *);
diff --git a/src/qml/qml/qqmltypeloaderthread.cpp b/src/qml/qml/qqmltypeloaderthread.cpp
index 3d35962c08..66e1300684 100644
--- a/src/qml/qml/qqmltypeloaderthread.cpp
+++ b/src/qml/qml/qqmltypeloaderthread.cpp
@@ -102,6 +102,11 @@ void QQmlTypeLoaderThread::initializeEngine(QQmlEngineExtensionInterface *iface,
callMethodInMain(&This::initializeEngineExtensionMain, iface, uri);
}
+void QQmlTypeLoaderThread::drop(const QQmlDataBlob::Ptr &b)
+{
+ postMethodToThread(&This::dropThread, b);
+}
+
void QQmlTypeLoaderThread::loadThread(const QQmlDataBlob::Ptr &b)
{
m_loader->loadThread(b);
@@ -148,4 +153,10 @@ void QQmlTypeLoaderThread::initializeEngineExtensionMain(QQmlEngineExtensionInte
iface->initializeEngine(m_loader->engine(), uri);
}
+void QQmlTypeLoaderThread::dropThread(const QQmlDataBlob::Ptr &b)
+{
+ // Simply drop the reference to b
+ Q_UNUSED(b);
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypeloaderthread_p.h b/src/qml/qml/qqmltypeloaderthread_p.h
index 4f65fc0cbd..e9d2f2da36 100644
--- a/src/qml/qml/qqmltypeloaderthread_p.h
+++ b/src/qml/qml/qqmltypeloaderthread_p.h
@@ -56,6 +56,7 @@ public:
void callDownloadProgressChanged(const QQmlDataBlob::Ptr &b, qreal p);
void initializeEngine(QQmlExtensionInterface *, const char *);
void initializeEngine(QQmlEngineExtensionInterface *, const char *);
+ void drop(const QQmlDataBlob::Ptr &b);
private:
void loadThread(const QQmlDataBlob::Ptr &b);
@@ -65,6 +66,7 @@ private:
void callDownloadProgressChangedMain(const QQmlDataBlob::Ptr &b, qreal p);
void initializeExtensionMain(QQmlExtensionInterface *iface, const char *uri);
void initializeEngineExtensionMain(QQmlEngineExtensionInterface *iface, const char *uri);
+ void dropThread(const QQmlDataBlob::Ptr &b);
QQmlTypeLoader *m_loader;
#if QT_CONFIG(qml_network)
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 07ba1bf9ff..6bd26f65dd 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -706,6 +706,16 @@ ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine
bool QQmlValueTypeWrapper::lookupSetter(
Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
+ if (&QQmlValueTypeWrapper::lookupSetter == &QV4::Lookup::setterFallback) {
+ // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
+ // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
+ // decides how to retrieve the property.
+ qFatal("Your C++ compiler is broken.");
+ }
+
+ // This setter marks the presence of a value type setter lookup.
+ // It falls back to the fallback lookup when run through the interpreter, but AOT-compiled
+ // code can get clever with it.
return QV4::Lookup::setterFallback(l, engine, object, value);
}
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 7db2821a5d..653cc5581e 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -2139,6 +2139,12 @@ void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
const auto propertyName = getScopeName(m_currentScope, QQmlJSScope::QMLScope);
leaveEnvironment();
+ if (m_currentScope->isInCustomParserParent()) {
+ // These warnings do not apply for custom parsers and their children and need to be handled
+ // on a case by case basis
+ return;
+ }
+
qsizetype i = 0;
for (auto element = arrayBinding->members; element; element = element->next, ++i) {
const auto &type = children[i];
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index e039c7b386..fcb6fab857 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -1201,6 +1201,22 @@ QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineCompo
}
/*!
+ \internal
+
+ Returns true if this type or any base type of it has the "EnforcesScopedEnums" flag.
+ The rationale is that you can turn on enforcement of scoped enums, but you cannot turn
+ it off explicitly.
+ */
+bool QQmlJSScope::enforcesScopedEnums() const
+{
+ for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
+ if (scope->hasEnforcesScopedEnumsFlag())
+ return true;
+ }
+ return false;
+}
+
+/*!
\internal
Returns true if the current type is creatable by checking all the required base classes.
"Uncreatability" is only inherited from base types for composite types (in qml) and not for non-composite types (c++).
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 60c84825de..8dfbebbd06 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -82,6 +82,7 @@ public:
HasBaseTypeError = 0x100,
HasExtensionNamespace = 0x200,
IsListProperty = 0x400,
+ EnforcesScopedEnums = 0x2000,
};
Q_DECLARE_FLAGS(Flags, Flag)
Q_FLAGS(Flags);
@@ -547,6 +548,10 @@ QT_WARNING_POP
bool isInlineComponent() const { return m_flags & InlineComponent; }
bool isWrappedInImplicitComponent() const { return m_flags & WrappedInImplicitComponent; }
bool extensionIsNamespace() const { return m_flags & HasExtensionNamespace; }
+
+ bool enforcesScopedEnums() const;
+ void setEnforcesScopedEnumsFlag(bool v) { m_flags.setFlag(EnforcesScopedEnums, v); }
+
void setIsSingleton(bool v) { m_flags.setFlag(Singleton, v); }
void setCreatableFlag(bool v) { m_flags.setFlag(Creatable, v); }
void setIsComposite(bool v) { m_flags.setFlag(Composite, v); }
@@ -725,6 +730,7 @@ private:
void addOwnPropertyBindingInQmlIROrder(const QQmlJSMetaPropertyBinding &binding,
BindingTargetSpecifier specifier);
+ bool hasEnforcesScopedEnumsFlag() const { return m_flags & EnforcesScopedEnums; }
QHash<QString, JavaScriptIdentifier> m_jsIdentifiers;
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
index 381a500b67..ec40f8b14d 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp
+++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
@@ -216,6 +216,8 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
scope->setIsComposite(readBoolBinding(script));
} else if (name == QLatin1String("hasCustomParser")) {
scope->setHasCustomParser(readBoolBinding(script));
+ } else if (name == QLatin1String("enforcesScopedEnums")) {
+ scope->setEnforcesScopedEnumsFlag(readBoolBinding(script));
} else if (name == QLatin1String("accessSemantics")) {
const QString semantics = readStringBinding(script);
if (semantics == QLatin1String("reference")) {
@@ -242,8 +244,9 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
addWarning(script->firstSourceLocation(),
tr("Expected only name, prototype, defaultProperty, attachedType, "
"valueType, exports, interfaces, isSingleton, isCreatable, "
- "isComposite, hasCustomParser, exportMetaObjectRevisions, "
- "deferredNames, and immediateNames in script bindings, not \"%1\".")
+ "isComposite, hasCustomParser, enforcesScopedEnums, "
+ "exportMetaObjectRevisions, deferredNames, and immediateNames in "
+ "script bindings, not \"%1\".")
.arg(name));
}
} else {
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index c7479cff82..3c62009472 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -916,7 +916,8 @@ bool QQmlJSTypeResolver::checkEnums(const QQmlJSScope::ConstPtr &scope, const QS
return true;
}
- if (!enumeration.isScoped() && enumeration.hasKey(name)) {
+ if ((!enumeration.isScoped() || enumeration.isQml() || !scope->enforcesScopedEnums())
+ && enumeration.hasKey(name)) {
*result = QQmlJSRegisterContent::create(
storedType(intType()), enumeration, name,
inExtension ? QQmlJSRegisterContent::ExtensionObjectEnum
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
index 13965d316b..97d1ce60cb 100644
--- a/src/qmlmodels/qqmldelegatemodel.cpp
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
@@ -3,6 +3,8 @@
#include "qqmldelegatemodel_p_p.h"
+#include <QtCore/private/qabstractitemmodel_p.h>
+
#include <QtQml/qqmlinfo.h>
#include <private/qqmlabstractdelegatecomponent_p.h>
@@ -167,7 +169,6 @@ QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt)
, m_transaction(false)
, m_incubatorCleanupScheduled(false)
, m_waitingToFetchMore(false)
- , m_maybeResetRoleNames(false)
, m_cacheItems(nullptr)
, m_items(nullptr)
, m_persistedItems(nullptr)
@@ -372,8 +373,6 @@ void QQmlDelegateModelPrivate::connectToAbstractItemModel()
QObject::connect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset);
qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
- QObject::connect(aim, &QAbstractItemModel::modelReset, q, &QQmlDelegateModel::handleModelReset);
- QObject::connect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged);
}
void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel()
@@ -403,8 +402,6 @@ void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel()
QObject::disconnect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset);
QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
- QObject::disconnect(aim, &QAbstractItemModel::modelReset, q, &QQmlDelegateModel::handleModelReset);
- QObject::disconnect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged);
}
void QQmlDelegateModel::setModel(const QVariant &model)
@@ -428,6 +425,21 @@ void QQmlDelegateModel::setModel(const QVariant &model)
_q_itemsInserted(0, d->adaptorModelCount());
d->requestMoreIfNecessary();
}
+
+ // Since 837c2f18cd223707e7cedb213257b0158ea07146, we connect to modelAboutToBeReset
+ // rather than modelReset so that we can handle role name changes. _q_modelAboutToBeReset
+ // now connects modelReset to handleModelReset with a single shot connection instead.
+ // However, it's possible for user code to begin the reset before connectToAbstractItemModel is called
+ // (QTBUG-125053), in which case we connect to modelReset too late and handleModelReset is never called,
+ // resulting in delegates not being created in certain cases.
+ // So, we check at the earliest point we can if the model is in the process of being reset,
+ // and if so, connect modelReset to handleModelReset.
+ if (d->m_adaptorModel.adaptsAim()) {
+ auto *aim = d->m_adaptorModel.aim();
+ auto *aimPrivate = QAbstractItemModelPrivate::get(aim);
+ if (aimPrivate->resetting)
+ QObject::connect(aim, &QAbstractItemModel::modelReset, this, &QQmlDelegateModel::handleModelReset, Qt::SingleShotConnection);
+ }
}
/*!
@@ -1856,28 +1868,28 @@ void QQmlDelegateModelPrivate::emitChanges()
void QQmlDelegateModel::_q_modelAboutToBeReset()
{
- /*
- roleNames are generally guaranteed to be stable (given that QAIM has no
- change signal for them), except that resetting the model is allowed to
- invalidate them (QTBUG-32132). DelegateModel must take this into account by
- snapshotting the current roleNames before the model is reset.
- Afterwards, if we detect that roleNames has changed, we throw the
- current model set up away and rebuild everything from scratch – it is
- unlikely that a more efficient implementation would be worth it.
-
- If we detect no changes, we simply use the existing logic to handle the
- model reset.
-
- This (role name resetting) logic relies on the fact that
- modelAboutToBeReset must be followed by a modelReset signal before any
- further modelAboutToBeReset can occur. However, it's possible for user
- code to begin the reset before connectToAbstractItemModel is called
- (QTBUG-125053), in which case we don't attempt to reset the role names.
- */
Q_D(QQmlDelegateModel);
- Q_ASSERT(!d->m_maybeResetRoleNames);
- d->m_maybeResetRoleNames = true;
- d->m_roleNamesBeforeReset = d->m_adaptorModel.aim()->roleNames();
+ if (!d->m_adaptorModel.adaptsAim())
+ return;
+ auto aim = d->m_adaptorModel.aim();
+ auto oldRoleNames = aim->roleNames();
+ // this relies on the fact that modelAboutToBeReset must be followed
+ // by a modelReset signal before any further modelAboutToBeReset can occur
+ QObject::connect(aim, &QAbstractItemModel::modelReset, this, [this, d, oldRoleNames, aim](){
+ if (!d->m_adaptorModel.adaptsAim() || d->m_adaptorModel.aim() != aim)
+ return;
+ if (oldRoleNames == aim->roleNames()) {
+ // if the rolenames stayed the same (most common case), then we don't have
+ // to throw away all the setup that we did
+ handleModelReset();
+ } else {
+ // If they did change, we give up and just start from scratch via setMode
+ setModel(QVariant::fromValue(model()));
+ // but we still have to call handleModelReset, otherwise views will
+ // not refresh
+ handleModelReset();
+ }
+ }, Qt::SingleShotConnection);
}
void QQmlDelegateModel::handleModelReset()
@@ -1887,23 +1899,6 @@ void QQmlDelegateModel::handleModelReset()
return;
int oldCount = d->m_count;
-
- if (d->m_maybeResetRoleNames) {
- auto aim = d->m_adaptorModel.aim();
- if (!d->m_adaptorModel.adaptsAim() || d->m_adaptorModel.aim() != aim)
- return;
-
- // If the role names stayed the same (most common case), then we don't have
- // to throw away all the setup that we did.
- // If they did change, we give up and just start from scratch via setModel.
- // We do this before handling the reset to ensure that views refresh.
- if (aim->roleNames() != d->m_roleNamesBeforeReset)
- setModel(QVariant::fromValue(model()));
-
- d->m_maybeResetRoleNames = false;
- d->m_roleNamesBeforeReset.clear();
- }
-
d->m_adaptorModel.rootIndex = QModelIndex();
if (d->m_complete) {
diff --git a/src/qmlmodels/qqmldelegatemodel_p_p.h b/src/qmlmodels/qqmldelegatemodel_p_p.h
index 2fab7b35eb..a7d22dfaeb 100644
--- a/src/qmlmodels/qqmldelegatemodel_p_p.h
+++ b/src/qmlmodels/qqmldelegatemodel_p_p.h
@@ -332,7 +332,6 @@ public:
QQmlReusableDelegateModelItemsPool m_reusableItemsPool;
QList<QQDMIncubationTask *> m_finishedIncubating;
QList<QByteArray> m_watchedRoles;
- QHash<int, QByteArray> m_roleNamesBeforeReset;
QString m_filterGroup;
@@ -346,7 +345,6 @@ public:
bool m_transaction : 1;
bool m_incubatorCleanupScheduled : 1;
bool m_waitingToFetchMore : 1;
- bool m_maybeResetRoleNames : 1;
union {
struct {
diff --git a/src/qmltyperegistrar/qqmltypesclassdescription.cpp b/src/qmltyperegistrar/qqmltypesclassdescription.cpp
index e25b7998de..1de1d4ce8a 100644
--- a/src/qmltyperegistrar/qqmltypesclassdescription.cpp
+++ b/src/qmltyperegistrar/qqmltypesclassdescription.cpp
@@ -70,6 +70,21 @@ void QmlTypesClassDescription::collectInterfaces(const QJsonObject *classDef)
}
}
+void QmlTypesClassDescription::handleRegisterEnumClassesUnscoped(
+ const QString &fileName, const QString &value)
+{
+ if (value == QStringLiteral("false")) {
+ enforcesScopedEnums = true;
+ return;
+ }
+
+ auto warning = qWarning().nospace().noquote() << "Warning: " << fileName << ":: ";
+ if (value == QStringLiteral("true"))
+ warning << "Setting RegisterEnumClassesUnscoped to true has no effect.";
+ else
+ warning << "Unrecognized value for RegisterEnumClassesUnscoped: " << value;
+}
+
void QmlTypesClassDescription::collectLocalAnonymous(
const QJsonObject *classDef, const QVector<QJsonObject> &types,
const QVector<QJsonObject> &foreign, QTypeRevision defaultRevision)
@@ -91,14 +106,12 @@ void QmlTypesClassDescription::collectLocalAnonymous(
const QJsonObject obj = classInfo.toObject();
const QString name = obj[QStringLiteral("name")].toString();
const auto value = [&]() { return obj[QStringLiteral("value")].toString(); };
- if (name == QStringLiteral("DefaultProperty")) {
+ if (name == QStringLiteral("DefaultProperty"))
defaultProp = value();
- } else if (name == QStringLiteral("ParentProperty")) {
+ else if (name == QStringLiteral("ParentProperty"))
parentProp = value();
- } else if (name == QStringLiteral("RegisterEnumClassesUnscoped")
- && value() == QStringLiteral("false")) {
- registerEnumClassesScoped = true;
- }
+ else if (name == QStringLiteral("RegisterEnumClassesUnscoped"))
+ handleRegisterEnumClassesUnscoped(file, value());
}
collectInterfaces(classDef);
@@ -128,8 +141,10 @@ void QmlTypesClassDescription::collect(
if (mode != RelatedType && parentProp.isEmpty())
parentProp = value;
} else if (name == QLatin1String("RegisterEnumClassesUnscoped")) {
- if (mode != RelatedType && value == QLatin1String("false"))
- registerEnumClassesScoped = true;
+ if (mode != RelatedType) {
+ handleRegisterEnumClassesUnscoped(
+ classDef->value(QLatin1String("inputFile")).toString(), value);
+ }
} else if (name == QLatin1String("QML.AddedInVersion")) {
const QTypeRevision revision = QTypeRevision::fromEncodedVersion(value.toInt());
if (mode == TopLevel) {
@@ -202,7 +217,7 @@ void QmlTypesClassDescription::collect(
// Default properties and enum classes are always local.
defaultProp.clear();
- registerEnumClassesScoped = false;
+ enforcesScopedEnums = false;
// Foreign type can have a default property or an attached types
// or RegisterEnumClassesUnscoped classinfo.
@@ -216,8 +231,8 @@ void QmlTypesClassDescription::collect(
} else if (parentProp.isEmpty() && foreignName == QLatin1String("ParentProperty")) {
parentProp = foreignValue;
} else if (foreignName == QLatin1String("RegisterEnumClassesUnscoped")) {
- if (foreignValue == QLatin1String("false"))
- registerEnumClassesScoped = true;
+ handleRegisterEnumClassesUnscoped(
+ classDef->value(QLatin1String("inputFile")).toString(), foreignValue);
} else if (foreignName == QLatin1String("QML.Attached")) {
attachedType = foreignValue;
collectRelated(foreignValue, types, foreign, defaultRevision);
diff --git a/src/qmltyperegistrar/qqmltypesclassdescription_p.h b/src/qmltyperegistrar/qqmltypesclassdescription_p.h
index 7931611e75..10cd7b931a 100644
--- a/src/qmltyperegistrar/qqmltypesclassdescription_p.h
+++ b/src/qmltyperegistrar/qqmltypesclassdescription_p.h
@@ -45,6 +45,7 @@ struct QmlTypesClassDescription
bool omitFromQmlTypes = false;
bool extensionIsNamespace = false;
bool registerEnumClassesScoped = false;
+ bool enforcesScopedEnums = false;
QStringList implementsInterfaces;
QStringList deferredNames;
QStringList immediateNames;
@@ -72,6 +73,8 @@ private:
const QJsonObject *classDef, const QVector<QJsonObject> &types,
const QVector<QJsonObject> &foreign, CollectMode mode, QTypeRevision defaultRevision);
void collectInterfaces(const QJsonObject *classDef);
+
+ void handleRegisterEnumClassesUnscoped(const QString &fileName, const QString &value);
};
QT_END_NAMESPACE
diff --git a/src/qmltyperegistrar/qqmltypescreator.cpp b/src/qmltyperegistrar/qqmltypescreator.cpp
index 28f8c658cd..031d047e5f 100644
--- a/src/qmltyperegistrar/qqmltypescreator.cpp
+++ b/src/qmltyperegistrar/qqmltypescreator.cpp
@@ -127,6 +127,9 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
if (collector.hasCustomParser)
m_qml.writeScriptBinding(QLatin1String("hasCustomParser"), QLatin1String("true"));
+ if (collector.enforcesScopedEnums)
+ m_qml.writeScriptBinding(QLatin1String("enforcesScopedEnums"), QLatin1String("true"));
+
m_qml.writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjects);
if (!collector.attachedType.isEmpty())
@@ -312,8 +315,7 @@ void QmlTypesCreator::writeMethods(const QJsonArray &methods, const QString &typ
}
}
-void QmlTypesCreator::writeEnums(
- const QJsonArray &enums, QmlTypesCreator::EnumClassesMode enumClassesMode)
+void QmlTypesCreator::writeEnums(const QJsonArray &enums)
{
for (const QJsonValue item : enums) {
const QJsonObject obj = item.toObject();
@@ -333,11 +335,9 @@ void QmlTypesCreator::writeEnums(
if (isFlag != obj.end() && isFlag->toBool())
m_qml.writeBooleanBinding(isFlag.key(), true);
- if (enumClassesMode == EnumClassesMode::Scoped) {
- const auto isClass = obj.find(QLatin1String("isClass"));
- if (isClass != obj.end() && isClass->toBool())
- m_qml.writeBooleanBinding(QLatin1String("isScoped"), true);
- }
+ const auto isClass = obj.find(QLatin1String("isClass"));
+ if (isClass != obj.end() && isClass->toBool())
+ m_qml.writeBooleanBinding(QLatin1String("isScoped"), true);
m_qml.writeArrayBinding(QLatin1String("values"), valueList);
m_qml.writeEndObject();
@@ -395,11 +395,7 @@ void QmlTypesCreator::writeComponents()
writeClassProperties(collector);
if (const QJsonObject *classDef = collector.resolvedClass) {
- writeEnums(
- members(classDef, enumsKey, m_version),
- collector.registerEnumClassesScoped
- ? EnumClassesMode::Scoped
- : EnumClassesMode::Unscoped);
+ writeEnums(members(classDef, enumsKey, m_version));
writeProperties(members(classDef, propertiesKey, m_version));
@@ -424,11 +420,7 @@ void QmlTypesCreator::writeComponents()
collector.collectLocalAnonymous(&component, m_ownTypes, m_foreignTypes, m_version);
writeClassProperties(collector);
- writeEnums(
- members(&component, enumsKey, m_version),
- collector.registerEnumClassesScoped
- ? EnumClassesMode::Scoped
- : EnumClassesMode::Unscoped);
+ writeEnums(members(&component, enumsKey, m_version));
writeProperties(members(&component, propertiesKey, m_version));
diff --git a/src/qmltyperegistrar/qqmltypescreator_p.h b/src/qmltyperegistrar/qqmltypescreator_p.h
index 6d3a498f2e..eeab19a71b 100644
--- a/src/qmltyperegistrar/qqmltypescreator_p.h
+++ b/src/qmltyperegistrar/qqmltypescreator_p.h
@@ -41,10 +41,7 @@ private:
void writeType(const QJsonObject &property, const QString &key);
void writeProperties(const QJsonArray &properties);
void writeMethods(const QJsonArray &methods, const QString &type);
-
- enum class EnumClassesMode { Scoped, Unscoped };
- void writeEnums(const QJsonArray &enums, EnumClassesMode enumClassesMode);
-
+ void writeEnums(const QJsonArray &enums);
void writeComponents();
QByteArray m_output;
diff --git a/src/quick/accessible/qaccessiblequickview.cpp b/src/quick/accessible/qaccessiblequickview.cpp
index 08f5889070..7b9bd18582 100644
--- a/src/quick/accessible/qaccessiblequickview.cpp
+++ b/src/quick/accessible/qaccessiblequickview.cpp
@@ -87,10 +87,11 @@ QString QAccessibleQuickWindow::text(QAccessible::Text text) const
if (text == QAccessible::DebugDescription) {
return QString::fromLatin1(object()->metaObject()->className()) ;
}
-#else
- Q_UNUSED(text);
#endif
- return window()->title();
+ if (text == QAccessible::Name)
+ return window()->title();
+ else
+ return {};
}
QAccessibleInterface *QAccessibleQuickWindow::childAt(int x, int y) const
diff --git a/src/quick/doc/snippets/qml/listview/hideDelegate.qml b/src/quick/doc/snippets/qml/listview/hideDelegate.qml
new file mode 100644
index 0000000000..7c019a2c92
--- /dev/null
+++ b/src/quick/doc/snippets/qml/listview/hideDelegate.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Basic
+
+//! [ListView]
+ListView {
+ anchors.fill: parent
+ model: ListModel {
+ ListElement { hidden: false }
+ ListElement { hidden: false }
+ ListElement { hidden: false }
+ // ...
+ }
+ delegate: ItemDelegate {
+ text: qsTr("Item %1").arg(index)
+ visible: !model.hidden
+ height: visible ? implicitHeight : 0
+
+ required property int index
+ required property var model
+
+ onClicked: model.hidden = true
+ }
+}
+//! [ListView]
diff --git a/src/quick/doc/snippets/qml/listview/listview.qml b/src/quick/doc/snippets/qml/listview/listview.qml
index c8df8e727b..611f590542 100644
--- a/src/quick/doc/snippets/qml/listview/listview.qml
+++ b/src/quick/doc/snippets/qml/listview/listview.qml
@@ -13,6 +13,8 @@ ListView {
model: ContactModel {}
delegate: Text {
+ required property string name
+ required property string number
text: name + ": " + number
}
}
@@ -25,10 +27,13 @@ Rectangle {
Component {
id: contactDelegate
Item {
+ id: myItem
+ required property string name
+ required property string number
width: 180; height: 40
Column {
- Text { text: '<b>Name:</b> ' + name }
- Text { text: '<b>Number:</b> ' + number }
+ Text { text: '<b>Name:</b> ' + myItem.name }
+ Text { text: '<b>Number:</b> ' + myItem.number }
}
}
}
diff --git a/src/quick/doc/snippets/qml/listview/stateInDelegate.qml b/src/quick/doc/snippets/qml/listview/stateInDelegate.qml
new file mode 100644
index 0000000000..29d8d3e7c2
--- /dev/null
+++ b/src/quick/doc/snippets/qml/listview/stateInDelegate.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Basic
+
+//! [ListView]
+ListView {
+ anchors.fill: parent
+ model: 3
+ delegate: CheckDelegate {
+ text: qsTr("Channel %1").arg(index + 1)
+
+ required property int index
+ property bool channelActivated
+
+ onClicked: channelActivated = checked
+ }
+}
+//! [ListView]
diff --git a/src/quick/doc/snippets/qml/listview/stateInModel.qml b/src/quick/doc/snippets/qml/listview/stateInModel.qml
new file mode 100644
index 0000000000..56b2792140
--- /dev/null
+++ b/src/quick/doc/snippets/qml/listview/stateInModel.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Basic
+
+//! [ListView]
+ListView {
+ anchors.fill: parent
+ model: ListModel {
+ ListElement {
+ channelActivated: true
+ }
+ // ...
+ }
+ delegate: CheckDelegate {
+ text: qsTr("Channel %1").arg(index + 1)
+ checked: model.channelActivated
+
+ required property int index
+ required property var model
+
+ onClicked: model.channelActivated = checked
+ }
+}
+//! [ListView]
diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
index 1934be6f26..2b1c282e64 100644
--- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
@@ -181,6 +181,12 @@ to a \e {.qml} file.
\li \l{Qt Design Studio: UI Files}
\endlist
+\section1 Using Qt Quick Views
+
+\section2 Store State in Models
+
+See \l {Avoid Storing State in Delegates}.
+
\section1 Using Qt Quick Layouts
Qt offers Qt Quick Layouts to arrange Qt Quick items visually in a layout.
diff --git a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
index 87397e43b4..f614c98ccc 100644
--- a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
@@ -91,24 +91,9 @@ flag for doing so by passing \c{--help} on the command line.
\section1 qmlformat
-\e qmlformat is a tool that automatically formats QML files in accordance
+\l [QtQml] {qmlformat} is a tool that automatically formats QML files in accordance
with the \l{QML Coding Conventions}.
-If you pass the \c{-n} or \c{--normalize} flag, \e qmlformat groups all
-properties, functions, and signals together, instead of retaining the order you
-specified.
-
-By default, qmlformat writes the formatted version of the file to stdout.
-If you wish to have your file updated in-place specify the \c{-i} flag.
-
-You may also change tab widths and line ending types among other settings,
-either via command line options or by using a settings file called
-\c{.qmlformat.ini}. A default settings file can be obtained by passing the
-\c{--write-defaults} flag.
-
-As with all tools, the \c{-h} or \c{--help} flag will print some information on
-all the available options.
-
\section1 Qt Quick Compiler
The Qt Quick Compiler consist of two components:
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index 6078cfd45e..971be5eb17 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -697,9 +697,17 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
m_xAxis.updateValue(activeTranslation.x(), m_xAxis.persistentValue() + delta.x(), delta.x());
m_yAxis.updateValue(activeTranslation.y(), m_yAxis.persistentValue() + delta.y(), delta.y());
emit translationChanged(delta);
+ // xAxis or yAxis may be disabled; nevertheless, we use setPosition() to compensate for
+ // other aspects of the transform. So it should not be skipped. Above, we've already
+ // subtracted activeTranslation if necessary.
t->setPosition(pos);
- t->setRotation(m_rotationAxis.persistentValue());
- t->setScale(m_scaleAxis.persistentValue());
+ // Set rotation and scale properties only if the respective axes are enabled.
+ // We've already checked above, so we don't expect activeScale or activeRotation to change
+ // if the axis is disabled; but then don't call the setter at all, to avoid breaking bindings.
+ if (m_rotationAxis.enabled())
+ t->setRotation(m_rotationAxis.persistentValue());
+ if (m_scaleAxis.enabled())
+ t->setScale(m_scaleAxis.persistentValue());
} else {
auto activeTranslation = centroid().scenePosition() - centroid().scenePressPosition();
auto accumulated = QPointF(m_xAxis.m_startValue, m_yAxis.m_startValue) + activeTranslation;
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 77a4bef646..29b2bafa0e 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -1726,7 +1726,9 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
|| (yflick() && qAbs(d->accumulatedWheelPixelDelta.y()) > qAbs(d->accumulatedWheelPixelDelta.x() * 2))) {
d->drag(currentTimestamp, event->type(), event->position(), d->accumulatedWheelPixelDelta,
true, !d->scrollingPhase, true, velocity);
- event->accept();
+ d->updateBeginningEnd();
+ if ((xflick() && !isAtXBeginning() && !isAtXEnd()) || (yflick() && !isAtYBeginning() && !isAtYEnd()))
+ event->accept();
} else {
qCDebug(lcWheel) << "not dragging: accumulated deltas" << d->accumulatedWheelPixelDelta <<
"moving?" << isMoving() << "can flick horizontally?" << xflick() << "vertically?" << yflick();
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 1e4af6838d..82587a178a 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -2375,6 +2375,20 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
if (item == item->window()->contentItem())
return true;
+ const auto tabFocus = QGuiApplication::styleHints()->tabFocusBehavior();
+ if (tabFocus == Qt::NoTabFocus)
+ return false;
+ if (tabFocus == Qt::TabFocusAllControls)
+ return true;
+
+ QVariant editable = item->property("editable");
+ if (editable.isValid())
+ return editable.toBool();
+
+ QVariant readonly = item->property("readOnly");
+ if (readonly.isValid())
+ return !readonly.toBool() && item->property("text").isValid();
+
#if QT_CONFIG(accessibility)
QAccessible::Role role = QQuickItemPrivate::get(item)->effectiveAccessibleRole();
if (role == QAccessible::EditableText || role == QAccessible::Table || role == QAccessible::List) {
@@ -2385,14 +2399,6 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
}
#endif
- QVariant editable = item->property("editable");
- if (editable.isValid())
- return editable.toBool();
-
- QVariant readonly = item->property("readOnly");
- if (readonly.isValid() && !readonly.toBool() && item->property("text").isValid())
- return true;
-
return false;
}
@@ -2473,8 +2479,6 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
if (!contentItem)
return item;
- bool all = QGuiApplication::styleHints()->tabFocusBehavior() == Qt::TabFocusAllControls;
-
QQuickItem *from = nullptr;
bool isTabFence = item->d_func()->isTabFence;
if (forward) {
@@ -2594,7 +2598,7 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
}
}
} while (skip || !current->activeFocusOnTab() || !current->isEnabled() || !current->isVisible()
- || !(all || QQuickItemPrivate::canAcceptTabFocus(current)));
+ || !(QQuickItemPrivate::canAcceptTabFocus(current)));
return current;
}
@@ -2666,9 +2670,10 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
while (!scopeItem->isFocusScope() && scopeItem->parentItem())
scopeItem = scopeItem->parentItem();
if (d->window) {
- d->deliveryAgentPrivate()->
- clearFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
+ if (QQuickDeliveryAgentPrivate *da = d->deliveryAgentPrivate()) {
+ da->clearFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
QQuickDeliveryAgentPrivate::DontChangeFocusProperty);
+ }
if (scopeFocusedItem != this)
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, true);
} else {
@@ -2742,9 +2747,10 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
emit scopeFocusedItem->focusChanged(false);
} else {
if (d->window) {
- d->deliveryAgentPrivate()->
- setFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
+ if (QQuickDeliveryAgentPrivate *da = d->deliveryAgentPrivate()) {
+ da->setFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
QQuickDeliveryAgentPrivate::DontChangeFocusProperty);
+ }
} else {
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, true);
}
@@ -6499,9 +6505,10 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
dirty(Visible);
if (parentItem)
QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged);
- if (window)
- if (auto agent = deliveryAgentPrivate(); agent)
+ if (window) {
+ if (auto agent = deliveryAgentPrivate())
agent->removeGrabber(q, true, true, true);
+ }
bool childVisibilityChanged = false;
for (int ii = 0; ii < childItems.size(); ++ii)
@@ -6839,12 +6846,26 @@ void QQuickItem::setSmooth(bool smooth)
key events used by Keys or KeyNavigation have precedence over
focus chain behavior; ignore the events in other key handlers
to allow it to propagate.
+
+ \note {QStyleHints::tabFocusBehavior}{tabFocusBehavior} can further limit focus
+ to only specific types of controls, such as only text or list controls. This is
+ the case on macOS, where focus to particular controls may be restricted based on
+ system settings.
+
+ \sa QStyleHints::tabFocusBehavior, focusPolicy
*/
/*!
\property QQuickItem::activeFocusOnTab
This property holds whether the item wants to be in the tab focus
chain. By default, this is set to \c false.
+
+ \note {QStyleHints::tabFocusBehavior}{tabFocusBehavior} can further limit focus
+ to only specific types of controls, such as only text or list controls. This is
+ the case on macOS, where focus to particular controls may be restricted based on
+ system settings.
+
+ \sa QStyleHints::tabFocusBehavior, focusPolicy
*/
bool QQuickItem::activeFocusOnTab() const
{
@@ -8381,7 +8402,8 @@ void QQuickItem::ungrabTouchPoints()
Q_D(QQuickItem);
if (!d->window)
return;
- d->deliveryAgentPrivate()->removeGrabber(this, false, true);
+ if (QQuickDeliveryAgentPrivate *da = d->deliveryAgentPrivate())
+ da->removeGrabber(this, false, true);
}
/*!
@@ -9877,7 +9899,7 @@ QRectF QQuickItem::mapFromItem(const QQuickItem *item, qreal x, qreal y, qreal w
//! \internal
QPointF QQuickItem::mapToItem(const QQuickItem *item, qreal x, qreal y)
-{ return mapToItem(item, QPoint(x, y)); }
+{ return mapToItem(item, QPointF(x, y)); }
//! \internal
QRectF QQuickItem::mapToItem(const QQuickItem *item, const QRectF &rect) const
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index 210759585b..2da1f172f1 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -2497,7 +2497,9 @@ bool QQuickItemViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::Reu
// One case where this can happen is moving an item out of one ObjectModel and into another.
QQuickItemPrivate::get(item->item)->setCulled(true);
}
- if (!isClearing)
+ // If deleteLater was called, the item isn't long for this world and so we shouldn't store references to it.
+ // This can happen when a Repeater is used to populate items in SwipeView's ListView contentItem.
+ if (!isClearing && !QObjectPrivate::get(item->item)->deleteLaterCalled)
unrequestedItems.insert(item->item, model->indexOf(item->item, q));
} else if (flags & QQmlInstanceModel::Destroyed) {
item->item->setParentItem(nullptr);
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 92b66ca5ed..063c48260a 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -2077,7 +2077,7 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
The list view itself is a focus scope (see \l{Keyboard Focus in Qt Quick} for more details).
Delegates are instantiated as needed and may be destroyed at any time.
- As such, state should \e never be stored in a delegate.
+ As such, \l {Avoid Storing State in Delegates}{state should \e never be stored in a delegate}.
Delegates are usually parented to ListView's \l {Flickable::contentItem}{contentItem}, but
typically depending on whether it's visible in the view or not, the \e parent
can change, and sometimes be \c null. Because of that, binding to
@@ -2215,8 +2215,9 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
item is reused. This includes \c index and \c row, but also
any model roles.
- \note Avoid storing any state inside a delegate. If you do, reset it
- manually on receiving the \l ListView::reused signal.
+ \note \l {Avoid Storing State in Delegates}{Avoid storing any state inside
+ a delegate}. If you do, reset it manually on receiving the
+ \l ListView::reused signal.
If an item has timers or animations, consider pausing them on receiving
the \l ListView::pooled signal. That way you avoid using the CPU resources
@@ -2248,6 +2249,45 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
items, at the expense of additional memory usage. \l{ListView::section}{Sections}
have the same effect because they attach and elongate the section label to the
first item within the section.
+
+ \section1 Avoid Storing State in Delegates
+
+ ListView's delegates are instantiated as needed and may be destroyed when
+ out of view. For an illustration of this, run the following example:
+
+ \snippet qml/listview/stateInDelegate.qml ListView
+
+ When an item is clicked, \c channelActivated is set to \c true. However,
+ because delegates can be \l {Reusing Items}{reused} and destroyed, all
+ state is lost when the view is moved far enough. When the delegate becomes
+ visible again, it will have its default, unmodified state (or, in the case
+ of an item that was reused, old state from a previous item).
+
+ To avoid this, state should be stored in the model:
+
+ \snippet qml/listview/stateInModel.qml ListView
+
+ \section1 Hiding Delegates
+
+ Setting a delegate's \l {Item::}{visible} property to \c false will hide
+ that item, but the space it occupied in the view will remain. It is
+ possible to set the item's \l {Item::}{height} to \c 0 (for a \l
+ {ListView::orientation}{vertical} ListView):
+
+ \snippet qml/listview/hideDelegate.qml ListView
+
+ Note that the hidden state is stored in the model, following the advice of
+ the \l {Avoid Storing State in Delegates} section.
+
+ However, if \l spacing is non-zero, there will be uneven gaps between
+ delegates.
+
+ A better option is to filter your model so that items that should not be
+ visible are not loaded by the view at all. This can be achieved with
+ \l QSortFilterProxyModel.
+
+ Another option is to \l {Item::enabled}{disable} the delegate instead of
+ hiding it.
*/
QQuickListView::QQuickListView(QQuickItem *parent)
: QQuickItemView(*(new QQuickListViewPrivate), parent)
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 44cbec55b8..b24a733501 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -825,6 +825,7 @@ void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event)
me.setPosition(d->lastPos);
emit mouseYChanged(&me);
me.setPosition(d->lastPos);
+ emit positionChanged(&me);
}
// A MouseArea should not block hover events
@@ -1224,6 +1225,10 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
{
Q_D(QQuickMouseArea);
+ // Don't allow entering pressed state while invisible
+ if (p && !d->effectiveVisible)
+ return false;
+
#if QT_CONFIG(quick_draganddrop)
bool dragged = d->drag && d->drag->active();
#else
@@ -1320,6 +1325,10 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
\endcode
The default value is \c Qt.ArrowCursor.
+
+ \note If the \c cursorShape property is set to \c undefined, the \c MouseArea will
+ not change the existing shape when entering it.
+
\sa Qt::CursorShape
*/
diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp
index c8405ca9ad..cdcab39773 100644
--- a/src/quick/items/qquickscreen.cpp
+++ b/src/quick/items/qquickscreen.cpp
@@ -197,8 +197,8 @@ QT_BEGIN_NAMESPACE
QQuickScreenInfo::QQuickScreenInfo(QObject *parent, QScreen *wrappedScreen)
: QObject(parent)
- , m_screen(wrappedScreen)
{
+ setWrappedScreen(wrappedScreen);
}
QString QQuickScreenInfo::name() const
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 9de90f8d82..d5d5454f22 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -826,13 +826,17 @@ void QQuickWindow::handleApplicationStateChanged(Qt::ApplicationState state)
QQmlListProperty<QObject> QQuickWindowPrivate::data()
{
- return QQmlListProperty<QObject>(q_func(), nullptr,
- QQuickWindowPrivate::data_append,
- QQuickWindowPrivate::data_count,
- QQuickWindowPrivate::data_at,
- QQuickWindowPrivate::data_clear,
- QQuickWindowPrivate::data_replace,
- QQuickWindowPrivate::data_removeLast);
+ QQmlListProperty<QObject> ret;
+
+ ret.object = q_func();
+ ret.append = QQuickWindowPrivate::data_append;
+ ret.count = QQuickWindowPrivate::data_count;
+ ret.at = QQuickWindowPrivate::data_at;
+ ret.clear = QQuickWindowPrivate::data_clear;
+ // replace is not supported by QQuickItem. Don't synthesize it.
+ ret.removeLast = QQuickWindowPrivate::data_removeLast;
+
+ return ret;
}
void QQuickWindowPrivate::dirtyItem(QQuickItem *)
@@ -1840,13 +1844,6 @@ void QQuickWindowPrivate::data_clear(QQmlListProperty<QObject> *property)
itemProperty.clear(&itemProperty);
}
-void QQuickWindowPrivate::data_replace(QQmlListProperty<QObject> *property, qsizetype i, QObject *o)
-{
- QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
- QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(win->contentItem())->data();
- itemProperty.replace(&itemProperty, i, o);
-}
-
void QQuickWindowPrivate::data_removeLast(QQmlListProperty<QObject> *property)
{
QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
@@ -4166,8 +4163,8 @@ void QQuickWindow::setGraphicsConfiguration(const QQuickGraphicsConfiguration &c
}
/*!
- \return the QQuickGraphicsDevice passed to setGraphicsDevice(), or a
- default constructed one otherwise
+ \return the QQuickGraphicsConfiguration passed to
+ setGraphicsConfiguration(), or a default constructed one otherwise.
\since 6.0
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 558c7d8fc2..6393e4a0cb 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -231,7 +231,6 @@ public:
static qsizetype data_count(QQmlListProperty<QObject> *);
static QObject *data_at(QQmlListProperty<QObject> *, qsizetype);
static void data_clear(QQmlListProperty<QObject> *);
- static void data_replace(QQmlListProperty<QObject> *, qsizetype, QObject *);
static void data_removeLast(QQmlListProperty<QObject> *);
static void rhiCreationFailureMessage(const QString &backendName,
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index 0cf24bfba2..ef27f22f87 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -228,7 +228,7 @@ class QQuickPixmapData
public:
QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QRect &r, const QSize &rs,
const QQuickImageProviderOptions &po, const QString &e)
- : refCount(1), frameCount(1), frame(0), inCache(false), pixmapStatus(QQuickPixmap::Error),
+ : refCount(1), frameCount(1), frame(0), inCache(false), fromSpecialDevice(false), pixmapStatus(QQuickPixmap::Error),
url(u), errorString(e), requestRegion(r), requestSize(rs),
providerOptions(po), appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform),
textureFactory(nullptr), reply(nullptr), prevUnreferenced(nullptr),
@@ -242,7 +242,7 @@ public:
QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QRect &r, const QSize &s, const QQuickImageProviderOptions &po,
QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1)
- : refCount(1), frameCount(frameCount), frame(frame), inCache(false), pixmapStatus(QQuickPixmap::Loading),
+ : refCount(1), frameCount(frameCount), frame(frame), inCache(false), fromSpecialDevice(false), pixmapStatus(QQuickPixmap::Loading),
url(u), requestRegion(r), requestSize(s),
providerOptions(po), appliedTransform(aTransform),
textureFactory(nullptr), reply(nullptr), prevUnreferenced(nullptr), prevUnreferencedPtr(nullptr),
@@ -257,7 +257,7 @@ public:
QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, QQuickTextureFactory *texture,
const QSize &s, const QRect &r, const QSize &rs, const QQuickImageProviderOptions &po,
QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1)
- : refCount(1), frameCount(frameCount), frame(frame), inCache(false), pixmapStatus(QQuickPixmap::Ready),
+ : refCount(1), frameCount(frameCount), frame(frame), inCache(false), fromSpecialDevice(false), pixmapStatus(QQuickPixmap::Ready),
url(u), implicitSize(s), requestRegion(r), requestSize(rs),
providerOptions(po), appliedTransform(aTransform),
textureFactory(texture), reply(nullptr), prevUnreferenced(nullptr),
@@ -270,7 +270,7 @@ public:
}
QQuickPixmapData(QQuickPixmap *pixmap, QQuickTextureFactory *texture)
- : refCount(1), frameCount(1), frame(0), inCache(false), pixmapStatus(QQuickPixmap::Ready),
+ : refCount(1), frameCount(1), frame(0), inCache(false), fromSpecialDevice(false), pixmapStatus(QQuickPixmap::Ready),
appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform),
textureFactory(texture), reply(nullptr), prevUnreferenced(nullptr),
prevUnreferencedPtr(nullptr), nextUnreferenced(nullptr)
@@ -304,6 +304,7 @@ public:
int frame;
bool inCache:1;
+ bool fromSpecialDevice:1;
QQuickPixmap::Status pixmapStatus;
QUrl url;
@@ -315,7 +316,9 @@ public:
QQuickImageProviderOptions::AutoTransform appliedTransform;
QColorSpace targetColorSpace;
- QIODevice *specialDevice = nullptr;
+ QPointer<QIODevice> specialDevice;
+
+ // actual image data, after loading
QQuickTextureFactory *textureFactory;
QIntrusiveList<QQuickPixmap, &QQuickPixmap::dataListNode> declarativePixmaps;
@@ -926,9 +929,14 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
QString errorStr;
QSize readSize;
- if (runningJob->data && runningJob->data->specialDevice) {
+ if (runningJob->data && runningJob->data->fromSpecialDevice) {
+ auto specialDevice = runningJob->data->specialDevice;
+ if (specialDevice.isNull() || QObjectPrivate::get(specialDevice.data())->deleteLaterCalled) {
+ qCDebug(lcImg) << "readImage job aborted" << url;
+ return;
+ }
int frameCount;
- if (!readImage(url, runningJob->data->specialDevice, &image, &errorStr, &readSize, &frameCount,
+ if (!readImage(url, specialDevice.data(), &image, &errorStr, &readSize, &frameCount,
runningJob->requestRegion, runningJob->requestSize,
runningJob->providerOptions, nullptr, runningJob->data->frame)) {
errorCode = QQuickPixmapReply::Loading;
@@ -1831,6 +1839,7 @@ void QQuickPixmap::loadImageFromDevice(QQmlEngine *engine, QIODevice *device, co
d = new QQuickPixmapData(this, url, requestRegion, requestSize, providerOptions,
QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
d->specialDevice = device;
+ d->fromSpecialDevice = true;
d->addToCache();
QQuickPixmapReader::readerMutex.lock();
diff --git a/src/quickcontrols/basic/impl/qquickbasicbusyindicator.cpp b/src/quickcontrols/basic/impl/qquickbasicbusyindicator.cpp
index d6cceb2a56..f885c35c59 100644
--- a/src/quickcontrols/basic/impl/qquickbasicbusyindicator.cpp
+++ b/src/quickcontrols/basic/impl/qquickbasicbusyindicator.cpp
@@ -145,8 +145,11 @@ bool QQuickBasicBusyIndicator::isRunning() const
void QQuickBasicBusyIndicator::setRunning(bool running)
{
- if (running)
+ m_running = running;
+
+ if (m_running)
setVisible(true);
+ // Don't set visible to false if not running, because we use an opacity animation (in QML) to hide ourselves.
}
int QQuickBasicBusyIndicator::elapsed() const
@@ -159,7 +162,12 @@ void QQuickBasicBusyIndicator::itemChange(QQuickItem::ItemChange change, const Q
QQuickItem::itemChange(change, data);
switch (change) {
case ItemOpacityHasChanged:
- if (qFuzzyIsNull(data.realValue))
+ // If running is set to false and then true within a short period (QTBUG-85860), our
+ // OpacityAnimator cancels the 1 => 0 animation (which was for running being set to false),
+ // setting opacity to 0 and hence visible to false. This happens _after_ setRunning(true)
+ // was called, because the properties were set synchronously but the animation is
+ // asynchronous. To account for this situation, we only hide ourselves if we're not running.
+ if (qFuzzyIsNull(data.realValue) && !m_running)
setVisible(false);
break;
case ItemVisibleHasChanged:
diff --git a/src/quickcontrols/basic/impl/qquickbasicbusyindicator_p.h b/src/quickcontrols/basic/impl/qquickbasicbusyindicator_p.h
index a39b61cde4..42df13243f 100644
--- a/src/quickcontrols/basic/impl/qquickbasicbusyindicator_p.h
+++ b/src/quickcontrols/basic/impl/qquickbasicbusyindicator_p.h
@@ -49,6 +49,7 @@ protected:
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override;
private:
+ bool m_running = false;
int m_elapsed = 0;
QColor m_pen;
QColor m_fill;
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-custom.qml
index 960600eec8..d4d5d04f5a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-custom.qml
@@ -69,7 +69,7 @@ ComboBox {
popup: Popup {
y: control.height - 1
width: control.width
- implicitHeight: contentItem.implicitHeight
+ height: Math.min(contentItem.implicitHeight, control.Window.height - topMargin - bottomMargin)
padding: 1
contentItem: ListView {
diff --git a/src/quickcontrols/doc/src/includes/customize-button-background.qdocinc b/src/quickcontrols/doc/src/includes/customize-button-background.qdocinc
index 0acad02d38..e8c88058b6 100644
--- a/src/quickcontrols/doc/src/includes/customize-button-background.qdocinc
+++ b/src/quickcontrols/doc/src/includes/customize-button-background.qdocinc
@@ -1,7 +1,7 @@
//! [file]
\qml
import QtQuick
-import QtQuick.Controls
+import QtQuick.Controls.Basic
ApplicationWindow {
width: 400
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc
index 59a97baa0e..3a06d670bf 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc
@@ -52,7 +52,7 @@
your application, refer to it by its filename:
\qml
- import QtQuick.Controls
+ import QtQuick.Controls.Basic
ApplicationWindow {
MyButton {
@@ -68,7 +68,7 @@
control, first import the folder into a namespace:
\qml
- import QtQuick.Controls
+ import QtQuick.Controls.Basic
import "controls" as MyControls
ApplicationWindow {
diff --git a/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp b/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp
index 8e58b09ed3..ddf26140ce 100644
--- a/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp
+++ b/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp
@@ -33,10 +33,13 @@ bool QQuickFusionBusyIndicator::isRunning() const
void QQuickFusionBusyIndicator::setRunning(bool running)
{
- if (running) {
+ m_running = running;
+
+ if (m_running) {
setVisible(true);
update();
}
+ // Don't set visible to false if not running, because we use an opacity animation (in QML) to hide ourselves.
}
void QQuickFusionBusyIndicator::paint(QPainter *painter)
@@ -73,7 +76,12 @@ void QQuickFusionBusyIndicator::itemChange(ItemChange change, const ItemChangeDa
switch (change) {
case ItemOpacityHasChanged:
- if (qFuzzyIsNull(data.realValue))
+ // If running is set to false and then true within a short period (QTBUG-85860), our
+ // OpacityAnimator cancels the 1 => 0 animation (which was for running being set to false),
+ // setting opacity to 0 and hence visible to false. This happens _after_ setRunning(true)
+ // was called, because the properties were set synchronously but the animation is
+ // asynchronous. To account for this situation, we only hide ourselves if we're not running.
+ if (qFuzzyIsNull(data.realValue) && !m_running)
setVisible(false);
break;
case ItemVisibleHasChanged:
diff --git a/src/quickcontrols/fusion/impl/qquickfusionbusyindicator_p.h b/src/quickcontrols/fusion/impl/qquickfusionbusyindicator_p.h
index 22ff2af788..8581042539 100644
--- a/src/quickcontrols/fusion/impl/qquickfusionbusyindicator_p.h
+++ b/src/quickcontrols/fusion/impl/qquickfusionbusyindicator_p.h
@@ -44,6 +44,7 @@ protected:
void itemChange(ItemChange change, const ItemChangeData &data) override;
private:
+ bool m_running = false;
QColor m_color;
};
diff --git a/src/quickcontrols/material/impl/qquickmaterialbusyindicator.cpp b/src/quickcontrols/material/impl/qquickmaterialbusyindicator.cpp
index 4d5b4e13e7..d99140b113 100644
--- a/src/quickcontrols/material/impl/qquickmaterialbusyindicator.cpp
+++ b/src/quickcontrols/material/impl/qquickmaterialbusyindicator.cpp
@@ -162,13 +162,16 @@ void QQuickMaterialBusyIndicator::setColor(const QColor &color)
bool QQuickMaterialBusyIndicator::isRunning() const
{
- return isVisible();
+ return m_running;
}
void QQuickMaterialBusyIndicator::setRunning(bool running)
{
- if (running)
+ m_running = running;
+
+ if (m_running)
setVisible(true);
+ // Don't set visible to false if not running, because we use an opacity animation (in QML) to hide ourselves.
}
int QQuickMaterialBusyIndicator::elapsed() const
@@ -181,7 +184,12 @@ void QQuickMaterialBusyIndicator::itemChange(QQuickItem::ItemChange change, cons
QQuickItem::itemChange(change, data);
switch (change) {
case ItemOpacityHasChanged:
- if (qFuzzyIsNull(data.realValue))
+ // If running is set to false and then true within a short period (QTBUG-85860), our
+ // OpacityAnimator cancels the 1 => 0 animation (which was for running being set to false),
+ // setting opacity to 0 and hence visible to false. This happens _after_ setRunning(true)
+ // was called, because the properties were set synchronously but the animation is
+ // asynchronous. To account for this situation, we only hide ourselves if we're not running.
+ if (qFuzzyIsNull(data.realValue) && !m_running)
setVisible(false);
break;
case ItemVisibleHasChanged:
diff --git a/src/quickcontrols/material/impl/qquickmaterialbusyindicator_p.h b/src/quickcontrols/material/impl/qquickmaterialbusyindicator_p.h
index b6bbd925c7..ab70432d61 100644
--- a/src/quickcontrols/material/impl/qquickmaterialbusyindicator_p.h
+++ b/src/quickcontrols/material/impl/qquickmaterialbusyindicator_p.h
@@ -45,6 +45,7 @@ protected:
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override;
private:
+ bool m_running = false;
int m_elapsed = 0;
QColor m_color = Qt::black;
};
diff --git a/src/quickdialogs/quickdialogs/qquickfiledialog.cpp b/src/quickdialogs/quickdialogs/qquickfiledialog.cpp
index 88b4047e40..a1f2f633eb 100644
--- a/src/quickdialogs/quickdialogs/qquickfiledialog.cpp
+++ b/src/quickdialogs/quickdialogs/qquickfiledialog.cpp
@@ -29,12 +29,12 @@ Q_LOGGING_CATEGORY(lcFileDialog, "qt.quick.dialogs.filedialog")
\image qtquickdialogs-filedialog-gtk.png
- To show a file dialog, construct an instance of FileDialog, set the
- desired properties, and call \l {Dialog::}{open()}. The \l currentFile
- or \l currentFiles properties can be used to determine the currently
- selected file(s) in the dialog. The \l selectedFile and \l selectedFiles
- properties are updated only after the final selection has been made by
- accepting the dialog.
+ To show a file dialog, construct an instance of FileDialog, set the desired
+ properties, and call \l {Dialog::}{open()}. The \l currentFolder property
+ can be used to determine the folder in which the dialog opens. The
+ \l selectedFile and \l selectedFiles properties can be used to determine
+ which file(s) are selected when the dialog opens, and are also updated
+ when the user selects a file in the dialog and when the dialog is accepted.
\snippet qtquickdialogs-filedialog.qml file
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/MessageDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/MessageDialog.qml
index cb3eb1cc64..5e2b511a43 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/MessageDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/MessageDialog.qml
@@ -117,9 +117,9 @@ MessageDialogImpl {
Layout.bottomMargin: 12
background: Rectangle {
- color: Qt.rgba(1,1,1,1)
+ color: detailedTextArea.palette.base
radius: 3
- border.color: Qt.darker(control.palette.light)
+ border.color: detailedTextArea.activeFocus ? Fusion.highlightedOutline(detailedTextArea.palette) : Fusion.outline(detailedTextArea.palette)
border.width: 1
}
}
diff --git a/src/quicktemplates/doc/src/qtquicktemplates-qmltypes.qdoc b/src/quicktemplates/doc/src/qtquicktemplates-qmltypes.qdoc
index c2c183ae72..10bdc2af58 100644
--- a/src/quicktemplates/doc/src/qtquicktemplates-qmltypes.qdoc
+++ b/src/quicktemplates/doc/src/qtquicktemplates-qmltypes.qdoc
@@ -10,6 +10,11 @@
The \l {Qt Quick Templates 2} module provides a set of non-visual templates
that can be used to build user interface controls in QML using \l {Qt Quick}.
+ These types should be used when you want to implement a control from
+ scratch, rather than \l {Customizing a Control}{customizing an existing
+ control}. This is most commonly the case when \l {Creating a Custom
+ Style}{creating a custom style}.
+
The QML types can be imported using the following import statement in your
\c .qml file:
@@ -17,10 +22,10 @@
import QtQuick.Templates as T
\endqml
- For the sake of clarity, there is a one-to-one mapping between the types
- provided by the \c QtQuick.Templates and \c QtQuick.Controls imports. For
- every type available in the \c QtQuick.Controls import, a non-visual template
- type by the same name exists in the \c QtQuick.Templates import.
+ There is a one-to-one mapping between the types provided by the \c
+ QtQuick.Templates and \c QtQuick.Controls imports. For every type available
+ in the \c QtQuick.Controls import, a non-visual template type by the same
+ name exists in the \c QtQuick.Templates import.
\note It is recommended to use a namespace for the templates import to avoid
overlap with the types provided by the \c QtQuick.Controls import.
diff --git a/src/quicktemplates/qquickcombobox.cpp b/src/quicktemplates/qquickcombobox.cpp
index 6e08019000..d9832aaf94 100644
--- a/src/quicktemplates/qquickcombobox.cpp
+++ b/src/quicktemplates/qquickcombobox.cpp
@@ -1037,10 +1037,15 @@ void QQuickComboBox::setModel(const QVariant& m)
}
/*!
- \internal
+ \readonly
\qmlproperty model QtQuick.Controls::ComboBox::delegateModel
- This property holds the model providing delegate instances for the combo box.
+ This property holds the model that provides delegate instances for the combo box.
+
+ It is typically assigned to a \l ListView in the \l {Popup::}{contentItem}
+ of the \l popup.
+
+ \sa {Customizing ComboBox}
*/
QQmlInstanceModel *QQuickComboBox::delegateModel() const
{
@@ -1869,7 +1874,7 @@ QString QQuickComboBox::textAt(int index) const
\value Qt.MatchWildcard The search term matches using wildcards.
\value Qt.MatchFixedString The search term matches as a fixed string.
\value Qt.MatchStartsWith The search term matches the start of the item.
- \value Qt.MatchEndsWidth The search term matches the end of the item.
+ \value Qt.MatchEndsWith The search term matches the end of the item.
\value Qt.MatchContains The search term is contained in the item.
\value Qt.MatchCaseSensitive The search is case sensitive.
diff --git a/src/quicktemplates/qquickdeferredexecute.cpp b/src/quicktemplates/qquickdeferredexecute.cpp
index 239b0fa6a6..8e63ae2188 100644
--- a/src/quicktemplates/qquickdeferredexecute.cpp
+++ b/src/quicktemplates/qquickdeferredexecute.cpp
@@ -13,6 +13,31 @@
QT_BEGIN_NAMESPACE
+/*!
+ \internal
+
+ For the history behind why these functions were introduced, see
+ the comments of QTBUG-50992, specifically
+ \l {https://bugreports.qt.io/browse/QTBUG-50992?focusedId=325677&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-325677}{this}.
+
+ The first commit to the QML engine to support deferred properties seems to
+ be 59b8d719d6122d86a4cc650911788cc4d778ce29.
+
+ The first commit to add support for it in Controls was
+ 458eb65f730178bc93ba7b18f0e4dd2a13efad2e.
+
+ In short, deferred execution solved two issues:
+
+ \list 1
+ \li Incubation issues when using asynchronous loaders, AKA QTBUG-50992.
+ \li Performance issues from creating two items unnecessarily when a
+ styled control was customized, which is explained in more detail
+ in the commit message of 458eb65f730178bc93ba7b18f0e4dd2a13efad2e.
+ \endlist
+
+ \sa qmlExecuteDeferred
+*/
+
namespace QtQuickPrivate {
static void cancelDeferred(QQmlData *ddata, int propertyIndex)
diff --git a/src/quicktemplates/qquickmenu.cpp b/src/quicktemplates/qquickmenu.cpp
index d46a2a862f..f53e9a9091 100644
--- a/src/quicktemplates/qquickmenu.cpp
+++ b/src/quicktemplates/qquickmenu.cpp
@@ -471,6 +471,14 @@ bool QQuickMenuPrivate::blockInput(QQuickItem *item, const QPointF &point) const
return (cascade && parentMenu && contains(point)) || QQuickPopupPrivate::blockInput(item, point);
}
+bool QQuickMenuPrivate::handlePress(QQuickItem *item, const QPointF &point, ulong timestamp)
+{
+ // Don't propagate mouse event as it can cause underlying item to receive
+ // events
+ return QQuickPopupPrivate::handlePress(item, point, timestamp)
+ || (popupItem == item);
+}
+
void QQuickMenuPrivate::onItemHovered()
{
Q_Q(QQuickMenu);
diff --git a/src/quicktemplates/qquickmenu_p_p.h b/src/quicktemplates/qquickmenu_p_p.h
index 509614d2d1..82f66abff0 100644
--- a/src/quicktemplates/qquickmenu_p_p.h
+++ b/src/quicktemplates/qquickmenu_p_p.h
@@ -66,6 +66,7 @@ public:
bool prepareEnterTransition() override;
bool prepareExitTransition() override;
bool blockInput(QQuickItem *item, const QPointF &point) const override;
+ bool handlePress(QQuickItem *item, const QPointF &point, ulong timestamp) override;
void onItemHovered();
void onItemTriggered();
diff --git a/src/quicktemplates/qquickoverlay.cpp b/src/quicktemplates/qquickoverlay.cpp
index c9d3cf9892..091b2f8fb3 100644
--- a/src/quicktemplates/qquickoverlay.cpp
+++ b/src/quicktemplates/qquickoverlay.cpp
@@ -97,9 +97,32 @@ bool QQuickOverlayPrivate::startDrag(QEvent *event, const QPointF &pos)
return false;
}
+static QQuickItem *findRootOfOverlaySubtree(QQuickItem *source, const QQuickOverlay *overlay)
+{
+ QQuickItem *sourceAncestor = source;
+ while (sourceAncestor) {
+ QQuickItem *parentItem = sourceAncestor->parentItem();
+ if (parentItem == overlay)
+ return sourceAncestor;
+ sourceAncestor = parentItem;
+ }
+ // Not an ancestor of the overlay.
+ return nullptr;
+}
+
bool QQuickOverlayPrivate::handlePress(QQuickItem *source, QEvent *event, QQuickPopup *target)
{
+ Q_Q(const QQuickOverlay);
if (target) {
+ // childMouseEventFilter will cause this function to get called for each active popup.
+ // If any of those active popups block inputs, the delivery agent won't send the press event to source.
+ // A popup will block input, if it's modal, and the item isn't an ancestor of the popup's popup item.
+ // If source doesn't belong to a popup, but exists in an overlay subtree, it makes sense to not filter the event.
+ const QList<QQuickItem *> childItems = paintOrderChildItems();
+ if (childItems.indexOf(findRootOfOverlaySubtree(source, q))
+ > childItems.indexOf(QQuickPopupPrivate::get(target)->popupItem))
+ return false;
+
if (target->overlayEvent(source, event)) {
setMouseGrabberPopup(target);
return true;
@@ -143,7 +166,17 @@ bool QQuickOverlayPrivate::handleMove(QQuickItem *source, QEvent *event, QQuickP
bool QQuickOverlayPrivate::handleRelease(QQuickItem *source, QEvent *event, QQuickPopup *target)
{
+ Q_Q(const QQuickOverlay);
if (target) {
+ // childMouseEventFilter will cause this function to get called for each active popup.
+ // If any of those active popups block inputs, the delivery agent won't send the press event to source.
+ // A popup will block input, if it's modal, and the item isn't an ancestor of the popup's popup item.
+ // If source doesn't belong to a popup, but exists in an overlay subtree, it makes sense to not filter the event.
+ const QList<QQuickItem *> childItems = paintOrderChildItems();
+ if (childItems.indexOf(findRootOfOverlaySubtree(source, q))
+ > childItems.indexOf(QQuickPopupPrivate::get(target)->popupItem))
+ return false;
+
setMouseGrabberPopup(nullptr);
if (target->overlayEvent(source, event)) {
setMouseGrabberPopup(nullptr);
diff --git a/src/quicktemplates/qquickpopup.cpp b/src/quicktemplates/qquickpopup.cpp
index e48f944936..bc573a3313 100644
--- a/src/quicktemplates/qquickpopup.cpp
+++ b/src/quicktemplates/qquickpopup.cpp
@@ -467,7 +467,8 @@ bool QQuickPopupPrivate::blockInput(QQuickItem *item, const QPointF &point) cons
// don't block presses and releases
// a) outside a non-modal popup,
// b) to popup children/content, or
- // b) outside a modal popups's background dimming
+ // c) outside a modal popups's background dimming
+
return modal && ((popupItem != item) && !popupItem->isAncestorOf(item)) && (!dimmer || dimmer->contains(dimmer->mapFromScene(point)));
}
@@ -2797,6 +2798,18 @@ void QQuickPopup::mouseUngrabEvent()
d->handleUngrab();
}
+/*!
+ \internal
+
+ Called whenever the window receives a Wheel/Hover/Mouse/Touch event,
+ and has an active popup (with popupType: Popup.Item) in its scene.
+
+ The purpose is to close popups when the press/release event happened outside of it,
+ and the closePolicy allows for it to happen.
+
+ If the function is called from childMouseEventFilter, then the return value of this
+ function will determine whether the event will be filtered, or delivered to \a item.
+*/
bool QQuickPopup::overlayEvent(QQuickItem *item, QEvent *event)
{
Q_D(QQuickPopup);
diff --git a/src/quicktestutils/quick/viewtestutils.cpp b/src/quicktestutils/quick/viewtestutils.cpp
index 79c34168e5..1263d7667c 100644
--- a/src/quicktestutils/quick/viewtestutils.cpp
+++ b/src/quicktestutils/quick/viewtestutils.cpp
@@ -8,6 +8,7 @@
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickView>
#include <QtGui/QScreen>
+#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtTest/QTest>
@@ -52,33 +53,6 @@ void QQuickViewTestUtils::moveMouseAway(QQuickView *window)
#endif
}
-void QQuickViewTestUtils::moveAndRelease(QQuickView *window, const QPoint &position)
-{
- QTest::mouseMove(window, position);
- QTest::mouseRelease(window, Qt::LeftButton, {}, position);
-}
-
-void QQuickViewTestUtils::moveAndPress(QQuickView *window, const QPoint &position)
-{
- QTest::mouseMove(window, position);
- QTest::mousePress(window, Qt::LeftButton, {}, position);
-}
-
-void QQuickViewTestUtils::flick(QQuickView *window, const QPoint &from, const QPoint &to, int duration)
-{
- const int pointCount = 5;
- QPoint diff = to - from;
-
- // send press, five equally spaced moves, and release.
- moveAndPress(window, from);
-
- for (int i = 0; i < pointCount; ++i)
- QTest::mouseMove(window, from + (i+1)*diff/pointCount, duration / pointCount);
-
- moveAndRelease(window, to);
- QTest::qWait(50);
-}
-
QList<int> QQuickViewTestUtils::adjustIndexesForAddDisplaced(const QList<int> &indexes, int index, int count)
{
QList<int> result;
@@ -524,50 +498,63 @@ namespace QQuickTest {
static Qt::KeyboardModifiers pressedTabletModifiers = Qt::NoModifier;
void pointerPress(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p,
- Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
+ Qt::MouseButton button, Qt::KeyboardModifiers modifiers, int delay)
{
+ const auto defaultDelay = QTest::defaultMouseDelay();
switch (dev->type()) {
case QPointingDevice::DeviceType::Mouse:
case QPointingDevice::DeviceType::TouchPad:
- QTest::mousePress(window, button, modifiers, p);
+ QTest::mousePress(window, button, modifiers, p, delay >= 0 ? delay : defaultDelay ? defaultDelay : 1);
break;
case QPointingDevice::DeviceType::TouchScreen:
+ // TODO apply delay when QTBUG-95421 is fixed
QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).press(pointId, p, window);
QQuickTouchUtils::flush(window);
break;
case QPointingDevice::DeviceType::Puck:
case QPointingDevice::DeviceType::Stylus:
- case QPointingDevice::DeviceType::Airbrush:
- QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
+ case QPointingDevice::DeviceType::Airbrush:{
+ const QPointF nativeLocal = QHighDpi::toNativeLocalPosition(p, window);
+ const QPointF nativeGlobal = QHighDpi::toNativeGlobalPosition(window->mapToGlobal(p), window);
+ QTest::lastMouseTimestamp += delay >= 0 ? delay : defaultDelay ? defaultDelay : 1;
pressedTabletButton = button;
pressedTabletModifiers = modifiers;
- QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
+ QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, nativeLocal, nativeGlobal,
button, 0.8, 0, 0, 0, 0, 0, modifiers);
break;
+ }
default:
qWarning() << "can't send a press event from" << dev;
break;
}
}
- void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p)
+ void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p, int delay)
{
+ const auto defaultDelay = QTest::defaultMouseDelay();
switch (dev->type()) {
case QPointingDevice::DeviceType::Mouse:
case QPointingDevice::DeviceType::TouchPad:
- QTest::mouseMove(window, p);
+ QTest::mouseMove(window, p, delay >= 0 ? delay : defaultDelay ? defaultDelay : 1);
break;
case QPointingDevice::DeviceType::TouchScreen:
+ // TODO apply delay when QTBUG-95421 is fixed
QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).move(pointId, p, window);
QQuickTouchUtils::flush(window);
break;
case QPointingDevice::DeviceType::Puck:
case QPointingDevice::DeviceType::Stylus:
- case QPointingDevice::DeviceType::Airbrush:
- QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
- QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
- pressedTabletButton, 0, 0, 0, 0, 0, 0, pressedTabletModifiers);
+ case QPointingDevice::DeviceType::Airbrush: {
+ const QPointF nativeLocal = QHighDpi::toNativeLocalPosition(p, window);
+ const QPointF nativeGlobal = QHighDpi::toNativeGlobalPosition(window->mapToGlobal(p), window);
+ const auto delay = QTest::defaultMouseDelay();
+ // often QTest::defaultMouseDelay() == 0; but avoid infinite velocity
+ QTest::lastMouseTimestamp += delay >= 0 ? delay : defaultDelay ? defaultDelay : 1;
+ QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, nativeLocal, nativeGlobal,
+ pressedTabletButton, pressedTabletButton == Qt::NoButton ? 0 : 0.75,
+ 0, 0, 0, 0, 0, pressedTabletModifiers);
break;
+ }
default:
qWarning() << "can't send a move event from" << dev;
break;
@@ -575,30 +562,66 @@ namespace QQuickTest {
}
void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p,
- Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
+ Qt::MouseButton button, Qt::KeyboardModifiers modifiers, int delay)
{
+ const auto defaultDelay = QTest::defaultMouseDelay();
switch (dev->type()) {
case QPointingDevice::DeviceType::Mouse:
case QPointingDevice::DeviceType::TouchPad:
- QTest::mouseRelease(window, button, modifiers, p);
+ QTest::mouseRelease(window, button, modifiers, p, delay >= 0 ? delay : defaultDelay ? defaultDelay : 1);
break;
case QPointingDevice::DeviceType::TouchScreen:
+ // TODO apply delay when QTBUG-95421 is fixed
QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).release(pointId, p, window);
QQuickTouchUtils::flush(window);
break;
case QPointingDevice::DeviceType::Puck:
case QPointingDevice::DeviceType::Stylus:
- case QPointingDevice::DeviceType::Airbrush:
- QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
- QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
+ case QPointingDevice::DeviceType::Airbrush: {
+ const QPointF nativeLocal = QHighDpi::toNativeLocalPosition(p, window);
+ const QPointF nativeGlobal = QHighDpi::toNativeGlobalPosition(window->mapToGlobal(p), window);
+ QTest::lastMouseTimestamp += delay >= 0 ? delay : defaultDelay ? defaultDelay : 1;
+ QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, nativeLocal, nativeGlobal,
Qt::NoButton, 0, 0, 0, 0, 0, 0, modifiers);
break;
+ }
default:
qWarning() << "can't send a press event from" << dev;
break;
}
}
+ void pointerMoveAndPress(const QPointingDevice *dev, QQuickWindow *window,
+ int pointId, const QPoint &p, Qt::MouseButton button,
+ Qt::KeyboardModifiers modifiers, int delay)
+ {
+ pointerMove(dev, window, pointId, p, delay);
+ pointerPress(dev, window, pointId, p, button, modifiers);
+ }
+
+ void pointerMoveAndRelease(const QPointingDevice *dev, QQuickWindow *window,
+ int pointId, const QPoint &p, Qt::MouseButton button,
+ Qt::KeyboardModifiers modifiers, int delay)
+ {
+ pointerMove(dev, window, pointId, p, delay);
+ pointerRelease(dev, window, pointId, p, button, modifiers);
+ }
+
+ void pointerFlick(const QPointingDevice *dev, QQuickWindow *window,
+ int pointId, const QPoint &from, const QPoint &to, int duration,
+ Qt::MouseButton button, Qt::KeyboardModifiers modifiers, int delay)
+ {
+ const int pointCount = 5;
+ const QPoint diff = to - from;
+
+ // send press, five equally spaced moves, and release.
+ pointerMoveAndPress(dev, window, pointId, from, button, modifiers, delay);
+
+ for (int i = 0; i < pointCount; ++i)
+ pointerMove(dev, window, pointId, from + (i + 1) * diff / pointCount, duration / pointCount);
+
+ pointerMoveAndRelease(dev, window, pointId, to, button, modifiers);
+ }
}
QT_END_NAMESPACE
diff --git a/src/quicktestutils/quick/viewtestutils_p.h b/src/quicktestutils/quick/viewtestutils_p.h
index 28666786f0..aec1b5063a 100644
--- a/src/quicktestutils/quick/viewtestutils_p.h
+++ b/src/quicktestutils/quick/viewtestutils_p.h
@@ -31,12 +31,9 @@ namespace QQuickViewTestUtils
{
QQuickView *createView();
- void flick(QQuickView *window, const QPoint &from, const QPoint &to, int duration);
void centerOnScreen(QQuickView *window, const QSize &size);
void centerOnScreen(QQuickView *window);
void moveMouseAway(QQuickView *window);
- void moveAndPress(QQuickView *window, const QPoint &position);
- void moveAndRelease(QQuickView *window, const QPoint &position);
QList<int> adjustIndexesForAddDisplaced(const QList<int> &indexes, int index, int count);
QList<int> adjustIndexesForMove(const QList<int> &indexes, int from, int to, int count);
@@ -185,14 +182,27 @@ namespace QQuickTest {
void pointerPress(const QPointingDevice *dev, QQuickWindow *window,
int pointId, const QPoint &p, Qt::MouseButton button = Qt::LeftButton,
- Qt::KeyboardModifiers modifiers = Qt::NoModifier);
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier, int delay = -1);
void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId,
- const QPoint &p);
+ const QPoint &p, int delay = -1);
void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId,
const QPoint &p, Qt::MouseButton button = Qt::LeftButton,
- Qt::KeyboardModifiers modifiers = Qt::NoModifier);
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier, int delay = -1);
+
+ void pointerMoveAndPress(const QPointingDevice *dev, QQuickWindow *window,
+ int pointId, const QPoint &p, Qt::MouseButton button = Qt::LeftButton,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier, int delay = -1);
+
+ void pointerMoveAndRelease(const QPointingDevice *dev, QQuickWindow *window,
+ int pointId, const QPoint &p, Qt::MouseButton button = Qt::LeftButton,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier, int delay = -1);
+
+ void pointerFlick(const QPointingDevice *dev, QQuickWindow *window,
+ int pointId, const QPoint &from, const QPoint &to, int duration,
+ Qt::MouseButton button = Qt::LeftButton,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier, int delay = -1);
}
QT_END_NAMESPACE
diff --git a/src/quicktestutils/quick/visualtestutils.cpp b/src/quicktestutils/quick/visualtestutils.cpp
index c5e41f33f1..5a593ba9d0 100644
--- a/src/quicktestutils/quick/visualtestutils.cpp
+++ b/src/quicktestutils/quick/visualtestutils.cpp
@@ -145,6 +145,10 @@ bool QQuickVisualTestUtils::compareImages(const QImage &ia, const QImage &ib, QS
QDebug(errorMessage) << "Images are of different formats:" << ia.format() << ib.format();
return false;
}
+ if (ia.depth() != 32) {
+ QDebug(errorMessage) << "This function only supports bit depths of 32 - depth of images is:" << ia.depth();
+ return false;
+ }
int w = ia.width();
int h = ia.height();
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index c64eff8aba..7d7e39d839 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -913,6 +913,7 @@ void QQuickWidgetPrivate::updateSize()
} else if (offscreenWindow->size().isEmpty()) {
// QQuickDeliveryAgentPrivate::deliverHoverEvent() ignores events that
// occur outside of QQuickRootItem's geometry, so we need it to match root's size.
+ offscreenWindow->resize(newSize);
offscreenWindow->contentItem()->setSize(newSize);
}
}