From 69ae7eb1abee9e8d212c7277076285fe15a47f08 Mon Sep 17 00:00:00 2001 From: Bartlomiej Moskal Date: Tue, 19 Jan 2021 10:41:48 +0100 Subject: qquicktextinput: Fix Undo history for IM event Do not set m_cursor (cursor position) before calling removeSelectedText() in processInputMethodEvent(QInputMethodEvent *) method. Before this change, DeleteSelection command was added to history with new cursor position. If this command will be later rolled back, cursor position will not be set correctly. It should be set to position before handling the event. Task-number: QTBUG-90239 Change-Id: Ib5e46d232e6b32f904e745da4f9e5bc03a58963f Reviewed-by: Qt CI Bot Reviewed-by: Rami Potinkara Reviewed-by: Assam Boudjelthia (cherry picked from commit 6ec8c62ca22c363fa00e085de10198a90e3d65dc) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktextinput.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index daa92d8d20..253ab9d3fb 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -3429,17 +3429,19 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) if (event->replacementStart() <= 0) c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength()); - m_cursor += event->replacementStart(); - if (m_cursor < 0) - m_cursor = 0; + int cursorInsertPos = m_cursor + event->replacementStart(); + if (cursorInsertPos < 0) + cursorInsertPos = 0; // insert commit string if (event->replacementLength()) { - m_selstart = m_cursor; + m_selstart = cursorInsertPos; m_selend = m_selstart + event->replacementLength(); m_selend = qMin(m_selend, m_text.length()); removeSelectedText(); } + m_cursor = cursorInsertPos; + if (!event->commitString().isEmpty()) { internalInsert(event->commitString()); cursorPositionChanged = true; -- cgit v1.2.3 From 689b323782eb24b8084ba9712897682932ff4908 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 16 Feb 2021 15:49:37 +0100 Subject: QQmlPropertyPrivate::signalExpression: handle object being null QQmlData::get expects a non-null pointer, therefore we need to check whether the object still exists. Note that while this fixes the crash in the referenced bug, PropertyChanges still does not support a dynamic target. Task-number: QTBUG-46350 Change-Id: Ifeecf5df83e87468a1d314ce2b120006124d6f4b Reviewed-by: Maximilian Goldstein Reviewed-by: Ulf Hermann (cherry picked from commit 1ff376e64bf5af6df7e0079700d2b9164037dc89) Reviewed-by: Qt Cherry-pick Bot --- src/qml/qml/qqmlproperty.cpp | 4 ++++ tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index f9c28ad54a..54e17cdf12 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -918,6 +918,8 @@ QQmlPropertyPrivate::signalExpression(const QQmlProperty &that) if (!(that.type() & QQmlProperty::SignalProperty)) return nullptr; + if (!that.d->object) + return nullptr; QQmlData *data = QQmlData::get(that.d->object); if (!data) return nullptr; @@ -957,6 +959,8 @@ void QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that, return; } + if (!that.d->object) + return; QQmlData *data = QQmlData::get(that.d->object, nullptr != expr); if (!data) return; diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 8a96fc52c5..5363cc8fcb 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -164,6 +164,8 @@ private slots: void nestedQQmlPropertyMap(); void underscorePropertyChangeHandler(); + + void signalExpressionWithoutObject(); private: QQmlEngine engine; }; @@ -2210,6 +2212,14 @@ void tst_qqmlproperty::underscorePropertyChangeHandler() QVERIFY(changeHandler.isSignalProperty()); } +void tst_qqmlproperty::signalExpressionWithoutObject() +{ + QQmlProperty invalid; + QQmlPropertyPrivate::setSignalExpression(invalid, nullptr); + QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(invalid); + QVERIFY(!expr); +} + QTEST_MAIN(tst_qqmlproperty) #include "tst_qqmlproperty.moc" -- cgit v1.2.3 From 8d4ceba0cf9c316c4d8dc4f23a65bdc2dbae61ad Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 19 Feb 2021 07:11:58 +0100 Subject: docs: Fix documentation of the Locale numberOptions property Document the property not the enum type. Fixes: QTBUG-91196 Change-Id: Id11a436caf1c683a0e70a1b8e8ce86c6118725d8 Reviewed-by: Paul Wicking (cherry picked from commit 3670395af58f21f203ce2289a04feef7c6de53f5) Reviewed-by: Qt Cherry-pick Bot --- src/qml/qml/qqmllocale.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 97a72aae53..937b4128f9 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -891,7 +891,7 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con */ /*! - \qmlproperty enumeration QtQml::Locale::NumberOption + \qmlproperty enumeration QtQml::Locale::numberOptions Holds a set of options for number-to-string and string-to-number conversions. -- cgit v1.2.3 From 1ae99163d51baf3cc070867d4a4735cb406f1cc7 Mon Sep 17 00:00:00 2001 From: Bartlomiej Moskal Date: Tue, 19 Jan 2021 11:47:54 +0100 Subject: qquicktextinput: Fix validation for IM event If validation did not pass after text pre-editing is finished, it need to be roll back to state before pre-editing started. Before this change, if validation did not pass, text was always rolled back to previous state. In pre-editing text case, it means back to the state in which part of the text was removed (and later changed to pre-edited text). It may cause a situation of removing part of the text that was already validated Fixes: QTBUG-90239 Change-Id: I3ec39e0f6b8a93d4e6fd190af30d4c80a0e495eb Reviewed-by: Rami Potinkara Reviewed-by: Assam Boudjelthia (cherry picked from commit b1ae151acc80254ab0ec2937c55b99223205875c) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktextinput.cpp | 20 +++++++++++++++++++- src/quick/items/qquicktextinput_p_p.h | 10 +++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 253ab9d3fb..c069639310 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -3468,8 +3468,12 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) } QString oldPreeditString = m_textLayout.preeditAreaText(); m_textLayout.setPreeditArea(m_cursor, event->preeditString()); - if (oldPreeditString != m_textLayout.preeditAreaText()) + if (oldPreeditString != m_textLayout.preeditAreaText()) { emit q->preeditTextChanged(); + if (!event->preeditString().isEmpty() && m_undoPreeditState == -1) + // Pre-edit text started. Remember state for undo purpose. + m_undoPreeditState = priorState; + } const int oldPreeditCursor = m_preeditCursor; m_preeditCursor = event->preeditString().length(); hasImState = !event->preeditString().isEmpty(); @@ -3511,6 +3515,11 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle | Qt::ImCurrentSelection); } + + // Empty pre-edit text handled. Clean m_undoPreeditState + if (event->preeditString().isEmpty()) + m_undoPreeditState = -1; + } #endif // im @@ -3587,6 +3596,12 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo if (m_maskData) checkIsValid(); +#if QT_CONFIG(im) + // If we were during pre-edit, validateFromState should point to the state before pre-edit + // has been started. Choose the correct oldest remembered state + if (m_undoPreeditState >= 0 && (m_undoPreeditState < validateFromState || validateFromState < 0)) + validateFromState = m_undoPreeditState; +#endif if (validateFromState >= 0 && wasValidInput && !m_validInput) { if (m_transactions.count()) return false; @@ -3659,6 +3674,9 @@ void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool e } m_history.clear(); m_undoState = 0; +#if QT_CONFIG(im) + m_undoPreeditState = -1; +#endif m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos; m_textDirty = (oldText != m_text); diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index fa1146c230..2f700e3f3e 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -110,6 +110,7 @@ public: , m_cursor(0) #if QT_CONFIG(im) , m_preeditCursor(0) + , m_undoPreeditState(-1) #endif , m_blinkEnabled(false) , m_blinkTimer(0) @@ -248,6 +249,7 @@ public: int m_cursor; #if QT_CONFIG(im) int m_preeditCursor; + int m_undoPreeditState; #endif bool m_blinkEnabled; int m_blinkTimer; @@ -335,7 +337,13 @@ public: bool isUndoAvailable() const { return !m_readOnly && m_undoState; } bool isRedoAvailable() const { return !m_readOnly && m_undoState < (int)m_history.size(); } - void clearUndo() { m_history.clear(); m_undoState = 0; } + void clearUndo() { + m_history.clear(); + m_undoState = 0; +#if QT_CONFIG(im) + m_undoPreeditState = -1; +#endif + } bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); } bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; } -- cgit v1.2.3 From 95209ad697658529fa94ff3422d3f7b57d96451a Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 17 Feb 2021 10:30:28 +0100 Subject: Fix lookup of existing inline components by name Previously this would always return the inline component with ID 1. Change-Id: I49dc6eb64fcd8428667f3b22afcb7212aa792db3 Reviewed-by: Fabian Kosmale (cherry picked from commit 7104a3a6f8fe518bd8a0d0d246c0f65df340ee38) --- src/qml/qml/qqmlimport.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 6021faf14c..cf89d49eff 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -730,7 +730,8 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt if (containingType.isValid()) { // we currently cannot reference a Singleton inside itself // in that case, containingType is still invalid - if (int icID = containingType.lookupInlineComponentIdByName(typeStr) != -1) { + int icID = containingType.lookupInlineComponentIdByName(typeStr); + if (icID != -1) { *type_return = containingType.lookupInlineComponentById(icID); } else { auto icType = createICType(); -- cgit v1.2.3 From 5e849f1ca2c5b253a190c3e46a2847ed9dbec6a3 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Sat, 20 Feb 2021 13:02:06 +0100 Subject: Fix release-only windows builds The test was linking with debug version of 'foreign' in both debug and relase. Pick-to: 6.0 Change-Id: Id567626bb5d9427b39447489d623effee731bb9b Reviewed-by: Ulf Hermann --- tests/auto/qml/qmltyperegistrar/foreign/foreign.pro | 1 - tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro b/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro index 87521eac43..006439b58a 100644 --- a/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro +++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro @@ -2,7 +2,6 @@ TEMPLATE = lib QT = core macos:CONFIG -= app_bundle -CONFIG -= debug_and_release_target SOURCES = foreign.cpp HEADERS = foreign.h diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro index fe21b122c2..3589743f0c 100644 --- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro @@ -17,4 +17,9 @@ QML_IMPORT_NAME = QmlTypeRegistrarTest QML_IMPORT_VERSION = 1.0 INCLUDEPATH += foreign -LIBS += -Lforeign -lforeign +debug_and_release { + CONFIG(release, debug|release): LIBS += -Lforeign/release -lforeign + else: LIBS += -Lforeign/debug -lforeign +} else { + LIBS += -Lforeign -lforeign +} -- cgit v1.2.3 From 1893c53117ffd3317ada82bcbbc9068410885713 Mon Sep 17 00:00:00 2001 From: Jani Heikkinen Date: Wed, 3 Mar 2021 15:05:50 +0200 Subject: Bump version Change-Id: Ibf896c4231e5c165ab22edd45cd94a43b31fa616 --- .qmake.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.qmake.conf b/.qmake.conf index 4e82106546..b09daef9c6 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -4,4 +4,4 @@ CONFIG += warning_clean DEFINES += QT_NO_LINKED_LIST DEFINES += QT_NO_JAVA_STYLE_ITERATORS -MODULE_VERSION = 5.15.3 +MODULE_VERSION = 5.15.4 -- cgit v1.2.3 From d43b92b0a91daefde95f3fa9bf8aad94412d19f3 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Wed, 3 Mar 2021 16:12:07 +0100 Subject: QQmlIncubator: handle clear inside setinitialState Fixes: QTBUG-91519 Change-Id: Idfe3116c2e94b8e96300d72e15db0bc78425f517 Reviewed-by: Ulf Hermann (cherry picked from commit 2cb306c194625626957fcde44bd56473b0436f83) Reviewed-by: Qt Cherry-pick Bot --- src/qml/qml/qqmlincubator.cpp | 2 +- tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp | 33 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index f7cdbda01c..caf419b778 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -330,7 +330,7 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) ddata->rootObjectInCreation = false; if (q) { q->setInitialState(result); - if (!creator->requiredProperties().empty()) { + if (creator && !creator->requiredProperties().empty()) { const auto& unsetRequiredProperties = creator->requiredProperties(); for (const auto& unsetRequiredProperty: unsetRequiredProperties) errors << QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty); diff --git a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp index 549aae8c2b..25adf4f31d 100644 --- a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp +++ b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp @@ -71,6 +71,7 @@ private slots: void contextDelete(); void garbageCollection(); void requiredProperties(); + void deleteInSetInitialState(); private: QQmlIncubationController controller; @@ -1213,6 +1214,38 @@ void tst_qqmlincubator::requiredProperties() } } +class DeletingIncubator : public QQmlIncubator +{ + + + // QQmlIncubator interface +protected: + void statusChanged(Status) override + { + + } + void setInitialState(QObject *obj) override + { + delete obj; + clear(); + } +}; + +void tst_qqmlincubator::deleteInSetInitialState() +{ + QQmlComponent component(&engine, testFileUrl("requiredProperty.qml")); + QVERIFY(component.isReady()); + // forceCompletion immediately after creating an asynchronous object completes it + DeletingIncubator incubator; + incubator.setInitialProperties({{"requiredProperty", 42}}); + QVERIFY(incubator.isNull()); + component.create(incubator); + QVERIFY(incubator.isLoading()); + incubator.forceCompletion(); // no crash + QVERIFY(incubator.isNull()); + QCOMPARE(incubator.object(), nullptr); // object was deleted +} + QTEST_MAIN(tst_qqmlincubator) #include "tst_qqmlincubator.moc" -- cgit v1.2.3 From 18b1e8266fd98c3099724de3baf4e9a014fb72d5 Mon Sep 17 00:00:00 2001 From: Maximilian Goldstein Date: Thu, 4 Mar 2021 14:51:30 +0100 Subject: qv4generatorobject: Fix crash when creating new properties Previously HeapObject::GeneratorObject utilized a ValueArray member to store stack information. As we rely on all HeapObject members to have a constant size in order for QV4Table::inlinePropertyOffset to remain accurate, this lead to a memory conflict when a user defined his own property on the Generator. Please do not use ValueArray for any types that are user accessible or that you intend to add properties to. Now the stack information is stored into ArrayObjects instead which circumvents the issue. Fixes: QTBUG-91491 Change-Id: Id6f638bf36a3ae3c9320ac99e67214c48dc81226 Reviewed-by: Fabian Kosmale Reviewed-by: Andrei Golubev (cherry picked from commit 7ea690c61dabd2485e80e7fae9aed392ba02c846) Reviewed-by: Qt Cherry-pick Bot --- src/qml/jsruntime/qv4generatorobject.cpp | 21 +++++++++------------ src/qml/jsruntime/qv4generatorobject_p.h | 3 ++- .../data/generatorCrashNewProperty.qml | 20 ++++++++++++++++++++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 16 ++++++++++++++++ 4 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/generatorCrashNewProperty.qml diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index 67b95b39a2..4ee43d0651 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -97,24 +97,21 @@ ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Valu Function *function = gf->function(); ExecutionEngine *engine = gf->engine(); - // We need to set up a separate stack for the generator, as it's being re-entered - uint stackSize = argc // space for the original arguments - + CppStackFrame::requiredJSStackFrameSize(function); // space for the JS stack frame - - size_t requiredMemory = sizeof(GeneratorObject::Data) - sizeof(Value) + sizeof(Value) * stackSize; - Scope scope(gf); - Scoped g(scope, scope.engine->memoryManager->allocManaged(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject])); + Scoped g(scope, engine->memoryManager->allocManaged(sizeof(GeneratorObject::Data), engine->classes[EngineBase::Class_GeneratorObject])); g->setPrototypeOf(ScopedObject(scope, gf->get(scope.engine->id_prototype()))); + // We need to set up a separate JSFrame for the generator, as it's being re-entered Heap::GeneratorObject *gp = g->d(); - gp->stack.size = stackSize; - gp->stack.alloc = stackSize; + gp->values.set(engine, engine->newArrayObject(argc)); + gp->jsFrame.set(engine, engine->newArrayObject(CppStackFrame::requiredJSStackFrameSize(function))); // copy original arguments - memcpy(gp->stack.values, argv, argc*sizeof(Value)); - gp->cppFrame.init(engine, function, gp->stack.values, argc); - gp->cppFrame.setupJSFrame(&gp->stack.values[argc], *gf, gf->scope(), + for (int i = 0; i < argc; i++) + gp->values->arrayData->setArrayData(engine, i, argv[i]); + + gp->cppFrame.init(engine, function, gp->values->arrayData->values.values, argc); + gp->cppFrame.setupJSFrame(gp->jsFrame->arrayData->values.values, *gf, gf->scope(), thisObject ? *thisObject : Value::undefinedValue(), Value::undefinedValue()); diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h index d697bd871f..8e14bcfa84 100644 --- a/src/qml/jsruntime/qv4generatorobject_p.h +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -90,7 +90,8 @@ struct GeneratorPrototype : FunctionObject { Member(class, Pointer, GeneratorFunction *, function) \ Member(class, NoMark, GeneratorState, state) \ Member(class, NoMark, CppStackFrame, cppFrame) \ - Member(class, ValueArray, ValueArray, stack) + Member(class, Pointer, ArrayObject *, values) \ + Member(class, Pointer, ArrayObject *, jsFrame) DECLARE_HEAP_OBJECT(GeneratorObject, Object) { DECLARE_MARKOBJECTS(GeneratorObject); diff --git a/tests/auto/qml/qqmlecmascript/data/generatorCrashNewProperty.qml b/tests/auto/qml/qqmlecmascript/data/generatorCrashNewProperty.qml new file mode 100644 index 0000000000..f775b4c613 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/generatorCrashNewProperty.qml @@ -0,0 +1,20 @@ +// QTBUG-91491 +import QtQml 2.15 + +QtObject { + property int a: 42 + property int b: 0 + property int c: 0 + + function f(myfunc) { + let gen = myfunc(); + gen["u"] = 0 // Adding members to the generator used to cause crashes when calling next() + c = gen.next().value + } + + function refreshA() { + f(function*() { b = 12; return a }); + } + + Component.onCompleted: refreshA(); +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 9198d3bebf..3c3a2a7a99 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -239,6 +239,7 @@ private slots: void eval(); void function(); void topLevelGeneratorFunction(); + void generatorCrashNewProperty(); void qtbug_10696(); void qtbug_11606(); void qtbug_11600(); @@ -6489,6 +6490,21 @@ void tst_qqmlecmascript::topLevelGeneratorFunction() QCOMPARE(it.property("next").callWithInstance(it).property("value").toInt(), 1); } +// QTBUG-91491 +void tst_qqmlecmascript::generatorCrashNewProperty() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("generatorCrashNewProperty.qml")); + + QScopedPointer o(component.create()); + + QVERIFY2(o != nullptr, qPrintable(component.errorString())); + + QCOMPARE(o->property("a").toInt(), 42); + QCOMPARE(o->property("b").toInt(), 12); + QCOMPARE(o->property("c").toInt(), 42); +} + // Test the "Qt.include" method void tst_qqmlecmascript::include() { -- cgit v1.2.3 From e3428a1d4e1d05922ba632f0b47cb5f0e631e8e9 Mon Sep 17 00:00:00 2001 From: Bartlomiej Moskal Date: Wed, 3 Feb 2021 11:55:16 +0100 Subject: QQuickTextControl: commit pre-edit after key press New text should not be inserted during pre-edition. Before handling new text from key press event pre-edition need to be commit. Fixes: QTBUG-90362 Change-Id: Ib6a7d67738bba18b7e904347b3e631416063029b Reviewed-by: Ulf Hermann Reviewed-by: Assam Boudjelthia (cherry picked from commit 5688486289f503dcde99578c72b7b768f19e8eb9) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktextcontrol.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index 08af7e018c..616a2f889e 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -949,6 +949,12 @@ void QQuickTextControlPrivate::keyPressEvent(QKeyEvent *e) process: { if (q->isAcceptableInput(e)) { +#if QT_CONFIG(im) + // QTBUG-90362 + // Before key press event will be handled, pre-editing part should be finished + if (isPreediting()) + commitPreedit(); +#endif if (overwriteMode // no need to call deleteChar() if we have a selection, insertText // does it already -- cgit v1.2.3 From e6fa55ca6462f07ed994b51900fe3c08e9be92f0 Mon Sep 17 00:00:00 2001 From: Andreas Hartmetz Date: Fri, 5 Mar 2021 12:41:06 +0100 Subject: QQuickWindow: don't leak old screenChanged connections Connections could accumulate. Because the newest one was invoked last due to how signal-slot invocations are ordered, rendering was correct, but the stale connections caused unnecessary updates (and wasted a small amount of memory). This comes from a misunderstanding I had at the time about how QMetaObject::Connection works. Destroying or overwriting one does not affect the actual connection. While at it, also modernize the connect(). Change-Id: Idde81bdbff8947ed517bf2740d623a395c0acb74 Reviewed-by: Fabian Kosmale Reviewed-by: Shawn Rutledge (cherry picked from commit 9f8292d48913c5bc50377749c2b3e030cf16d703) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquickwindow.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index bc9179c6c8..344a767afb 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -450,15 +450,14 @@ void QQuickWindow::physicalDpiChanged() void QQuickWindow::handleScreenChanged(QScreen *screen) { Q_D(QQuickWindow); + disconnect(d->physicalDpiChangedConnection); if (screen) { physicalDpiChanged(); // When physical DPI changes on the same screen, either the resolution or the device pixel // ratio changed. We must check what it is. Device pixel ratio does not have its own // ...Changed() signal. - d->physicalDpiChangedConnection = connect(screen, SIGNAL(physicalDotsPerInchChanged(qreal)), - this, SLOT(physicalDpiChanged())); - } else { - disconnect(d->physicalDpiChangedConnection); + d->physicalDpiChangedConnection = connect(screen, &QScreen::physicalDotsPerInchChanged, + this, &QQuickWindow::physicalDpiChanged); } d->forcePolish(); -- cgit v1.2.3 From 4a2548e3129ecfc2dbe1cabd785a389baadb047d Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Fri, 12 Feb 2021 17:11:47 +0100 Subject: MouseArea: fix containsMouse behavior during visibility changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QQuickItem returns whether it contains QGuiApplicationPrivate's lastCursorPosition. Since that position stores the coordinate last seen by Qt (the window border), it will always be within the window, and within an item that covers that part of the window's border. However, QQuickWindow stores the lastMousePosition as well, and resets that value when it receives a QEvent::Leave. We can use that to test whether the window that contains the item has seen a Leave event, in which case the item is definitely not under the mouse. Notes on the test: That we use QPointF() as the "reset" value leave the small possibility that the cursor might be at position 0,0 of the window (ie inside the window), and the QQuickItem there will not be under the mouse. We can't confirm this (through an expected failure test), as QTest::mouseMove interprets a QPoint(0, 0) as "center of the window". And since we can't simulate mouse moves outside a window's boundary using QTest::mouseMove, the test needs to explicitly synthesize a QEvent::Leave for the window. Fixes: QTBUG-87197 Change-Id: I04870d6e914092275d9d790312fc702fb99f2935 Done-with: Ivan Solovev Reviewed-by: Tor Arne Vestbø (cherry picked from commit ba1246c543118515ea244787f3d7f9c1133ccf0f) Reviewed-by: Volker Hilsheimer --- src/quick/items/qquickitem.cpp | 6 +++ .../quick/qquickmousearea/data/containsMouse.qml | 14 ++++++ .../quick/qquickmousearea/tst_qquickmousearea.cpp | 50 ++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 tests/auto/quick/qquickmousearea/data/containsMouse.qml diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index c92c8e6da2..f144f3ec5d 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -7392,6 +7392,12 @@ bool QQuickItem::isUnderMouse() const if (!d->window) return false; + // QQuickWindow handles QEvent::Leave to reset the lastMousePosition + // FIXME: Using QPointF() as the reset value means an item will not be + // under the mouse if the mouse is at 0,0 of the window. + if (QQuickWindowPrivate::get(d->window)->lastMousePosition == QPointF()) + return false; + QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition; return contains(mapFromScene(d->window->mapFromGlobal(cursorPos.toPoint()))); } diff --git a/tests/auto/quick/qquickmousearea/data/containsMouse.qml b/tests/auto/quick/qquickmousearea/data/containsMouse.qml new file mode 100644 index 0000000000..c4f1299e49 --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/containsMouse.qml @@ -0,0 +1,14 @@ +import QtQuick 2.15 + +Rectangle { + width: 200 + height: 200 + visible: true + MouseArea { + id: mouseArea + objectName: "mouseArea" + anchors.fill: parent + hoverEnabled: true + visible: false + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 91fcae40af..1c99357ab7 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -160,6 +160,7 @@ private slots: void mask(); void nestedEventDelivery(); void settingHiddenInPressUngrabs(); + void containsMouseAndVisibility(); private: int startDragDistance() const { @@ -2450,6 +2451,55 @@ void tst_QQuickMouseArea::settingHiddenInPressUngrabs() QVERIFY(!mouseArea->pressed()); } +// QTBUG-87197 +void tst_QQuickMouseArea::containsMouseAndVisibility() +{ + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl("containsMouse.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + window.requestActivate(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QQuickMouseArea *mouseArea = window.rootObject()->findChild("mouseArea"); + QVERIFY(mouseArea != nullptr); + QVERIFY(!mouseArea->isVisible()); + + QTest::mouseMove(&window, QPoint(10, 10)); + QTRY_VERIFY(!mouseArea->hovered()); + + mouseArea->setVisible(true); + QVERIFY(mouseArea->isVisible()); + QTRY_VERIFY(mouseArea->hovered()); + + /* we (ab-)use QPointF() as the 'reset' value in QQuickWindow's leave-event handling, + but can't verify that this leaves an invalid interpretation of states for position + QPoint(0, 0) as QTest::mouseMove interprets a null-position as "center of the window". + + So instead, verify the current (unexpectedly expected) behavior as far as testing is + concern. + */ + QTest::mouseMove(&window, QPoint(0, 0)); + QTRY_VERIFY(mouseArea->hovered()); + QTRY_VERIFY(mouseArea->isUnderMouse()); + + // move to the edge (can't move outside) + QTest::mouseMove(&window, QPoint(window.width() - 1, window.height() / 2)); + // then pretend we left + QEvent event(QEvent::Leave); + QGuiApplication::sendEvent(&window, &event); + QVERIFY(!mouseArea->hovered()); + + // toggle mouse area visibility - the hover state should not change + mouseArea->setVisible(false); + QVERIFY(!mouseArea->isVisible()); + QVERIFY(!mouseArea->hovered()); + + mouseArea->setVisible(true); + QVERIFY(mouseArea->isVisible()); + QVERIFY(!mouseArea->hovered()); +} + QTEST_MAIN(tst_QQuickMouseArea) #include "tst_qquickmousearea.moc" -- cgit v1.2.3 From 6971b1a252497b65c181d02ffac468e73997a9bb Mon Sep 17 00:00:00 2001 From: Maximilian Goldstein Date: Tue, 23 Feb 2021 16:10:44 +0100 Subject: qqmldelegatemodel: Fix out of bounds cache removal Task-number: QTBUG-91276 Change-Id: I1ddbb4a3326d61ff94e3881beb64a14dade11c46 Reviewed-by: Ulf Hermann (cherry picked from commit 31ad81d81e623a34cd71567b9507f16601f1c1d4) Reviewed-by: Qt Cherry-pick Bot --- src/qmlmodels/qqmldelegatemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index 5ad56a1710..6db30c5fe6 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -1609,7 +1609,7 @@ void QQmlDelegateModelPrivate::itemsRemoved( removed[i] = 0; for (const Compositor::Remove &remove : removes) { - for (; cacheIndex < remove.cacheIndex; ++cacheIndex) + for (; cacheIndex < remove.cacheIndex && cacheIndex < m_cache.size(); ++cacheIndex) incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); for (int i = 1; i < m_groupCount; ++i) { -- cgit v1.2.3 From e21819bd6374cc19a92e2b6a1ee05c0bcd5a49f6 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 17 Mar 2021 17:09:10 +0100 Subject: Use unique_ptr to clarify ownership of QQuickDefaultClipNode objects The clang static analyzer warns in 3df1fff15a10a64372ed4f92ba05271f about a potential memory leak. While that particular claim is a false positive (the loop is always entered if sortedIndex is not empty), the re-use of the currentClipNode variable makes it hard to follow the object ownership, and there might still be a potential memory leak. Use std::unique_ptr to force explicit transfer of ownership, and get implicit destruction of objects not owned at the end of the scope. Change-Id: If826e1d81b92f1da60aae2262b628dcaaa2e592a Reviewed-by: Eskil Abrahamsen Blomfeldt (cherry picked from commit 02c6e7bc3aca42a188b772aa9794b919e60017e7) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktextnodeengine.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index c92cc7850e..52f898f72c 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -256,7 +256,7 @@ void QQuickTextNodeEngine::processCurrentLine() QVarLengthArray pendingOverlines; QVarLengthArray pendingStrikeOuts; if (!sortedIndexes.isEmpty()) { - QQuickDefaultClipNode *currentClipNode = m_hasSelection ? new QQuickDefaultClipNode(QRectF()) : nullptr; + std::unique_ptr currentClipNode(m_hasSelection ? new QQuickDefaultClipNode(QRectF()) : nullptr); bool currentClipNodeUsed = false; for (int i=0; i<=sortedIndexes.size(); ++i) { BinaryTreeNode *node = nullptr; @@ -304,7 +304,7 @@ void QQuickTextNodeEngine::processCurrentLine() if (currentClipNode != nullptr) { if (!currentClipNodeUsed) { - delete currentClipNode; + currentClipNode.reset(); } else { currentClipNode->setIsRectangular(true); currentClipNode->setRect(currentRect); @@ -313,9 +313,9 @@ void QQuickTextNodeEngine::processCurrentLine() } if (node != nullptr && m_hasSelection) - currentClipNode = new QQuickDefaultClipNode(QRectF()); + currentClipNode.reset(new QQuickDefaultClipNode(QRectF())); else - currentClipNode = nullptr; + currentClipNode.reset(nullptr); currentClipNodeUsed = false; if (node != nullptr) { @@ -335,7 +335,7 @@ void QQuickTextNodeEngine::processCurrentLine() if (node != nullptr) { if (node->selectionState == Selected) { - node->clipNode = currentClipNode; + node->clipNode = currentClipNode.release(); currentClipNodeUsed = true; } -- cgit v1.2.3 From c2b72da5764adfabc446ce339cb173c03af2fc0d Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 2 Mar 2021 07:54:09 +0100 Subject: Ensure that the case of the path will match then QUrl for a UNC path When a UNC path is used to locate the qmldir then when it is checked later on the original path will be compared against the one that QUrl returns. However, QUrl will convert the case of the host name to be all lower-case whereas the original string may have been in upper-case. For example, QUrl::fromLocalFile("//QT-L-R90X9VHB/tasks").toString() will output "file://qt-l-r90x9vhb/tasks". So in this case, the absoluteFilePath is changed at this point so that it will match what QUrl has for the same path to avoid a problem with it no being found. Change-Id: I2cd5d74bfec06c01635f80574ac1a6d479792855 Reviewed-by: Ulf Hermann Reviewed-by: Fabian Kosmale (cherry picked from commit e0400d08755bb40c303bbe330bc3bd6045436c22) --- src/qml/qml/qqmlimport.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index cf89d49eff..10c1b83285 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1409,11 +1409,16 @@ QQmlImports::LocalQmldirResult QQmlImportsPrivate::locateLocalQmldir( if (!absoluteFilePath.isEmpty()) { QString url; const QStringRef absolutePath = absoluteFilePath.leftRef(absoluteFilePath.lastIndexOf(Slash) + 1); - if (absolutePath.at(0) == Colon) + if (absolutePath.at(0) == Colon) { url = QLatin1String("qrc") + absolutePath; - else + } else { url = QUrl::fromLocalFile(absolutePath.toString()).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 (absoluteFilePath.startsWith(QLatin1String("//"))) + absoluteFilePath = QUrl::fromLocalFile(absoluteFilePath).toString(QUrl::RemoveScheme); + } QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache; cache->versionMajor = vmaj; cache->versionMinor = vmin; -- cgit v1.2.3 From aa9545172214cac95a6c4b7300091e4b0eb1b60d Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Mon, 22 Mar 2021 11:39:24 +0100 Subject: Revert "Use unique_ptr to clarify ownership of QQuickDefaultClipNode objects" This reverts commit 02c6e7bc3aca42a188b772aa9794b919e60017e7. It broke text selection, the currentClipNode pointer must not be reset to nullptr even if ownership is transferred to a node. Change-Id: Ia66f7ed4be17916f3725bd3cb2cbd0e7c9d6327a Reviewed-by: Eirik Aavitsland (cherry picked from commit 68324c56c1b8f4ad5dfcdc6f77abda3eedaf2dfd) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktextnodeengine.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index 52f898f72c..c92cc7850e 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -256,7 +256,7 @@ void QQuickTextNodeEngine::processCurrentLine() QVarLengthArray pendingOverlines; QVarLengthArray pendingStrikeOuts; if (!sortedIndexes.isEmpty()) { - std::unique_ptr currentClipNode(m_hasSelection ? new QQuickDefaultClipNode(QRectF()) : nullptr); + QQuickDefaultClipNode *currentClipNode = m_hasSelection ? new QQuickDefaultClipNode(QRectF()) : nullptr; bool currentClipNodeUsed = false; for (int i=0; i<=sortedIndexes.size(); ++i) { BinaryTreeNode *node = nullptr; @@ -304,7 +304,7 @@ void QQuickTextNodeEngine::processCurrentLine() if (currentClipNode != nullptr) { if (!currentClipNodeUsed) { - currentClipNode.reset(); + delete currentClipNode; } else { currentClipNode->setIsRectangular(true); currentClipNode->setRect(currentRect); @@ -313,9 +313,9 @@ void QQuickTextNodeEngine::processCurrentLine() } if (node != nullptr && m_hasSelection) - currentClipNode.reset(new QQuickDefaultClipNode(QRectF())); + currentClipNode = new QQuickDefaultClipNode(QRectF()); else - currentClipNode.reset(nullptr); + currentClipNode = nullptr; currentClipNodeUsed = false; if (node != nullptr) { @@ -335,7 +335,7 @@ void QQuickTextNodeEngine::processCurrentLine() if (node != nullptr) { if (node->selectionState == Selected) { - node->clipNode = currentClipNode.release(); + node->clipNode = currentClipNode; currentClipNodeUsed = true; } -- cgit v1.2.3 From 34ccde5cc096bc79bc44cd4129c16c1ecd8ffc46 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 17 Mar 2021 16:52:21 +0100 Subject: QQuickItemAnimation: close potential memory leak Fix static analyzer warning bff6cb4333f531d5a72f7bf6dc1485f6. If ownership of viaData is not passed to the viaAction, then the object might be leaked. Use std::unique_ptr to make ownership transfer explicit and implicitly delete unowned objects. Change-Id: I89f2a6b630941a98a74db302bc1ab08055c71974 Reviewed-by: Ulf Hermann (cherry picked from commit 78aea267209c34abeb4895712dc76c923aa46165) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquickitemanimation.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp index 2761a3c1a5..d8649697e4 100644 --- a/src/quick/items/qquickitemanimation.cpp +++ b/src/quick/items/qquickitemanimation.cpp @@ -230,8 +230,8 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act { Q_D(QQuickParentAnimation); - QQuickParentAnimationData *data = new QQuickParentAnimationData; - QQuickParentAnimationData *viaData = new QQuickParentAnimationData; + std::unique_ptr data(new QQuickParentAnimationData); + std::unique_ptr viaData(new QQuickParentAnimationData); bool hasExplicit = false; if (d->target && d->newParent) { @@ -377,8 +377,8 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act QParallelAnimationGroupJob *ag = new QParallelAnimationGroupJob; if (d->via) - viaAction->setAnimAction(viaData); - targetAction->setAnimAction(data); + viaAction->setAnimAction(viaData.release()); + targetAction->setAnimAction(data.release()); //take care of any child animations bool valid = d->defaultProperty.isValid(); @@ -405,9 +405,6 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act topLevelGroup->appendAnimation(d->via ? viaAction : targetAction); } return initInstance(topLevelGroup); - } else { - delete data; - delete viaData; } return nullptr; } -- cgit v1.2.3 From 68d8a192e21015b0e614e6a49667aabacba05d03 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Fri, 19 Mar 2021 17:41:46 +0100 Subject: QQuickTableView: forceLayout() should work, even when no items are loaded As it stood, we would return early from forceLayout if no items were loaded. This made sense, since when no items are loaded, there would be no items to lay out. But after we changed the logic so that an application can show or hide rows and columns by returning an empty size from the size providers, we now always need to do a layout to check if some rows or columns should become visible. Fixes: QTBUG-92076 Change-Id: I2a07bf8e62cfeebcbe36c01aa92eca3ed8227cd3 Reviewed-by: Mitch Curtis Reviewed-by: Shawn Rutledge (cherry picked from commit 9ba9336ec4515d157a1207fad1dcd2de311527ac) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktableview.cpp | 12 ++++++-- .../quick/qquicktableview/tst_qquicktableview.cpp | 33 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 4cfc8a57cd..ef386c3934 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -927,6 +927,15 @@ void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable() QQuickTableViewPrivate::RebuildOptions QQuickTableViewPrivate::checkForVisibilityChanges() { + // This function will check if there are any visibility changes among + // the _already loaded_ rows and columns. Note that there can be rows + // and columns to the bottom or right that was not loaded, but should + // now become visible (in case there is free space around the table). + if (loadedItems.isEmpty()) { + // Report no changes + return RebuildOption::None; + } + // Go through all columns from first to last, find the columns that used // to be hidden and not loaded, and check if they should become visible // (and vice versa). If there is a change, we need to rebuild. @@ -971,9 +980,6 @@ QQuickTableViewPrivate::RebuildOptions QQuickTableViewPrivate::checkForVisibilit void QQuickTableViewPrivate::forceLayout() { - if (loadedItems.isEmpty()) - return; - clearEdgeSizeCache(); RebuildOptions rebuildOptions = RebuildOption::None; diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index d489a873e4..15b7a3dac3 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -121,6 +121,7 @@ private slots: void checkForceLayoutFunction(); void checkForceLayoutEndUpDoingALayout(); void checkForceLayoutDuringModelChange(); + void checkForceLayoutWhenAllItemsAreHidden(); void checkContentWidthAndHeight(); void checkPageFlicking(); void checkExplicitContentWidthAndHeight(); @@ -625,6 +626,38 @@ void tst_QQuickTableView::checkForceLayoutDuringModelChange() QCOMPARE(tableView->rows(), initialRowCount + 1); } +void tst_QQuickTableView::checkForceLayoutWhenAllItemsAreHidden() +{ + // Check that you can have a TableView where all columns are + // initially hidden, and then show some columns and call + // forceLayout(). This should make the columns become visible. + LOAD_TABLEVIEW("forcelayout.qml"); + + // Tell all columns to be hidden + const char *propertyName = "columnWidths"; + view->rootObject()->setProperty(propertyName, 0); + + const int rows = 3; + const int columns = 3; + auto model = TestModelAsVariant(rows, columns); + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + // Check that the we have no items loaded + QCOMPARE(tableViewPrivate->loadedColumns.count(), 0); + QCOMPARE(tableViewPrivate->loadedRows.count(), 0); + QCOMPARE(tableViewPrivate->loadedItems.count(), 0); + + // Tell all columns to be visible + view->rootObject()->setProperty(propertyName, 10); + tableView->forceLayout(); + + QCOMPARE(tableViewPrivate->loadedRows.count(), rows); + QCOMPARE(tableViewPrivate->loadedColumns.count(), columns); + QCOMPARE(tableViewPrivate->loadedItems.count(), rows * columns); +} + void tst_QQuickTableView::checkContentWidthAndHeight() { // Check that contentWidth/Height reports the correct size of the -- cgit v1.2.3 From dbf08f32cd06ebf5ef4eeeb53fa5bab69bab57b3 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Wed, 27 Jan 2021 08:34:44 -0600 Subject: Do less work when there are no active ImageParticle particles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't mark geometry and material as dirty if there is nothing to change. Task-number: QTBUG-41867 Change-Id: I016d2d76f4ebf731f5bfc931ba616ee5d074bc65 Reviewed-by: Tomi Korpipää Reviewed-by: Laszlo Agocs (cherry picked from commit e3cb305a6a19bf394c1068a7e483e1c95e11c22b) --- src/particles/qquickimageparticle.cpp | 32 +++++++++++++++++++++++++------- src/particles/qquickimageparticle_p.h | 3 ++- src/particles/qquickparticlesystem_p.h | 2 ++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp index 4e850bfd4f..4c95fe6920 100644 --- a/src/particles/qquickimageparticle.cpp +++ b/src/particles/qquickimageparticle.cpp @@ -1132,6 +1132,7 @@ QQuickImageParticle::QQuickImageParticle(QQuickItem* parent) , m_startedImageLoading(0) , m_rhi(nullptr) , m_apiChecked(false) + , m_previousActive(false) { setFlag(ItemHasContents); } @@ -1960,11 +1961,13 @@ QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *node, UpdatePaintNodeData } if (m_system && m_system->isRunning() && !m_system->isPaused()){ - prepareNextFrame(&node); + bool dirty = prepareNextFrame(&node); if (node) { update(); - foreach (QSGGeometryNode* n, m_nodes) - n->markDirty(QSGNode::DirtyGeometry); + if (dirty) { + foreach (QSGGeometryNode* n, m_nodes) + n->markDirty(QSGNode::DirtyGeometry); + } } else if (m_startedImageLoading < 2) { update();//To call prepareNextFrame() again from the renderThread } @@ -1978,7 +1981,7 @@ QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *node, UpdatePaintNodeData return node; } -void QQuickImageParticle::prepareNextFrame(QSGNode **node) +bool QQuickImageParticle::prepareNextFrame(QSGNode **node) { if (*node == nullptr){//TODO: Staggered loading (as emitted) buildParticleNodes(node); @@ -1994,7 +1997,7 @@ void QQuickImageParticle::prepareNextFrame(QSGNode **node) qDebug() << "Total count: " << count; } if (*node == nullptr) - return; + return false; } qint64 timeStamp = m_system->systemSync(this); @@ -2015,8 +2018,23 @@ void QQuickImageParticle::prepareNextFrame(QSGNode **node) getState(m_material)->timestamp = time; break; } - foreach (QSGGeometryNode* node, m_nodes) - node->markDirty(QSGNode::DirtyMaterial); + + bool active = false; + for (auto groupId : groupIds()) { + if (m_system->groupData[groupId]->isActive()) { + active = true; + break; + } + } + + const bool dirty = active || m_previousActive; + if (dirty) { + foreach (QSGGeometryNode* node, m_nodes) + node->markDirty(QSGNode::DirtyMaterial); + } + + m_previousActive = active; + return dirty; } void QQuickImageParticle::spritesUpdate(qreal time) diff --git a/src/particles/qquickimageparticle_p.h b/src/particles/qquickimageparticle_p.h index 6eadfe10de..b040426a61 100644 --- a/src/particles/qquickimageparticle_p.h +++ b/src/particles/qquickimageparticle_p.h @@ -363,7 +363,7 @@ protected: void commit(int gIdx, int pIdx) override; QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override; - void prepareNextFrame(QSGNode**); + bool prepareNextFrame(QSGNode**); void buildParticleNodes(QSGNode**); void sceneGraphInvalidated() override; @@ -460,6 +460,7 @@ private: int m_startedImageLoading; QRhi *m_rhi; bool m_apiChecked; + bool m_previousActive; }; QT_END_NAMESPACE diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h index bf3627edd7..1ef04d90c8 100644 --- a/src/particles/qquickparticlesystem_p.h +++ b/src/particles/qquickparticlesystem_p.h @@ -205,6 +205,8 @@ public: QString name(); + bool isActive() { return freeList.count() > 0; } + void setSize(int newSize); const ID index; -- cgit v1.2.3 From de3dfe2411180c2a4754f22a1a61b19a25099a2c Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Wed, 10 Mar 2021 17:21:07 +0100 Subject: QQuickTableView: always update content size when rebuilding small tables If you have a TableView with only a couple of rows, and you add a third one, the contentHeight doesn't update. This is fine if not all rows are loaded (some are outside the viewport), but when they are all inside, it should update to reflect the exact height. The same is also the case for the contentWidth. If you add a new row that increases the with of a column (and all columns are visible), the contentWidth should update. This patch adds an extra check when we do a rebuild (which we do when you add a new row), to see if all rows or columns are loaded. And if that is the case, we update contentHeight or contentWidth, respecitively. Fixes: QTBUG-92099 Change-Id: I806bfb7c3606fca97c5d27cbb91856cc40df9fb8 Reviewed-by: Mitch Curtis (cherry picked from commit d6a5afd120838647e0dd2a420dacf06389f0a48e) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktableview.cpp | 26 +++++++- src/quick/items/qquicktableview_p_p.h | 2 + .../qquicktableview/data/sizefromdelegate.qml | 74 ++++++++++++++++++++++ .../quick/qquicktableview/tst_qquicktableview.cpp | 25 ++++++++ 4 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 tests/auto/quick/qquicktableview/data/sizefromdelegate.qml diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index ef386c3934..652050b5ec 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -642,6 +642,28 @@ int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex) return foundIndex; } +bool QQuickTableViewPrivate::allColumnsLoaded() +{ + // Returns true if all the columns in the model (that are not + // hidden by the columnWidthProvider) are currently loaded and visible. + const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd; + if (!firstColumnLoaded) + return false; + bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) == kEdgeIndexAtEnd; + return lastColumnLoaded; +} + +bool QQuickTableViewPrivate::allRowsLoaded() +{ + // Returns true if all the rows in the model (that are not hidden + // by the columnWidthProvider) are currently loaded and visible. + const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd; + if (!firstColumnLoaded) + return false; + bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) == kEdgeIndexAtEnd; + return lastColumnLoaded; +} + void QQuickTableViewPrivate::updateContentWidth() { // Note that we actually never really know what the content size / size of the full table will @@ -1929,12 +1951,12 @@ void QQuickTableViewPrivate::layoutAfterLoadingInitialTable() relayoutTableItems(); syncLoadedTableRectFromLoadedTable(); - if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth)) { + if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded()) { updateAverageColumnWidth(); updateContentWidth(); } - if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight)) { + if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded()) { updateAverageRowHeight(); updateContentHeight(); } diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index 87f07abe8c..758c0308b8 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -368,6 +368,8 @@ public: int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex); int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge); + bool allColumnsLoaded(); + bool allRowsLoaded(); inline int edgeToArrayIndex(Qt::Edge edge); void clearEdgeSizeCache(); diff --git a/tests/auto/quick/qquicktableview/data/sizefromdelegate.qml b/tests/auto/quick/qquicktableview/data/sizefromdelegate.qml new file mode 100644 index 0000000000..b4a04c89cb --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/sizefromdelegate.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Window 2.3 + +Item { + width: 640 + height: 450 + + property alias tableView: tableView + + TableView { + id: tableView + width: 600 + height: 400 + anchors.margins: 1 + delegate: tableViewDelegate + columnSpacing: 1 + rowSpacing: 1 + } + + Component { + id: tableViewDelegate + Rectangle { + color: "lightgray" + implicitWidth: text.width + implicitHeight: text.height + + Text { + id: text + anchors.centerIn: parent + text: modelData + } + } + } + +} diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 15b7a3dac3..d14c37d8e3 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -123,6 +123,7 @@ private slots: void checkForceLayoutDuringModelChange(); void checkForceLayoutWhenAllItemsAreHidden(); void checkContentWidthAndHeight(); + void checkContentWidthAndHeightForSmallTables(); void checkPageFlicking(); void checkExplicitContentWidthAndHeight(); void checkExtents_origin(); @@ -706,6 +707,30 @@ void tst_QQuickTableView::checkContentWidthAndHeight() QCOMPARE(tableView->contentHeight(), expectedSizeInit); } +void tst_QQuickTableView::checkContentWidthAndHeightForSmallTables() +{ + // For tables where all the columns in the model are loaded, we know + // the exact table width, and can therefore update the content width + // if e.g new rows are added or removed. The same is true for rows. + // This test will check that we do so. + LOAD_TABLEVIEW("sizefromdelegate.qml"); + + TestModel model(3, 3); + tableView->setModel(QVariant::fromValue(&model)); + WAIT_UNTIL_POLISHED; + + const qreal initialContentWidth = tableView->contentWidth(); + const qreal initialContentHeight = tableView->contentHeight(); + const QString longText = QStringLiteral("Adding a row with a very long text"); + model.insertRow(0); + model.setModelData(QPoint(0, 0), QSize(1, 1), longText); + + WAIT_UNTIL_POLISHED; + + QVERIFY(tableView->contentWidth() > initialContentWidth); + QVERIFY(tableView->contentHeight() > initialContentHeight); +} + void tst_QQuickTableView::checkPageFlicking() { // Check that we rebuild the table instead of refilling edges, if the viewport moves -- cgit v1.2.3 From 36ae81c73f3592f8ebc73a301cf29bf91d076391 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 17 Mar 2021 09:35:57 +0100 Subject: Remove unnecessary forward declaration This ensures that clang-tidy does not report it as a warning when it is used on application code. Change-Id: I5b84759ff87e7a5b9d119ef27650d92c86f9f831 Reviewed-by: Ulf Hermann (cherry picked from commit c2634e14273b77f8e4cb77585ab9d7eb7880bf34) Reviewed-by: Qt Cherry-pick Bot --- src/qml/jsapi/qjsvalue.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h index b3f2698471..1a2c514f04 100644 --- a/src/qml/jsapi/qjsvalue.h +++ b/src/qml/jsapi/qjsvalue.h @@ -57,7 +57,6 @@ class QDateTime; typedef QList QJSValueList; namespace QV4 { struct ExecutionEngine; - struct Value; } class Q_QML_EXPORT QJSValue -- cgit v1.2.3 From ea1681cf2ac36450778552567201662ff1a39ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Tue, 9 Mar 2021 10:13:57 +0100 Subject: DelegateModelGroup: Fix bug where item could be removed from the model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an item was removed from the DelegateModelGroup before it was completed it caused subsequent items in the model to be missing in some cases. The reason was that while populating the ListView, it iterated with an index for each item to call createItem() on. However, createItem() might call onCompleted (which in the case of QTBUG-86708 removed the item from the DelegateModel), which caused the next index we called createItem() with to be wrong (it became one step ahead). We therefore add a helper class MutableModelIterator, which keeps track of if a index in the model got removed (and if the iterator index needs to be adjusted because of that).... Task-number: QTBUG-86708 Change-Id: I33537b43727aed4f2b9bdda794b011b6684c44b4 Reviewed-by: Richard Moe Gustavsen (cherry picked from commit 0ff9db566c48172c688bf9327fe6a781dc4a1c34) Reviewed-by: Jan Arve Sæther --- src/quick/items/qquicklistview.cpp | 102 +++++++++++++++++++-- .../qml/qqmldelegatemodel/data/removeFromGroup.qml | 45 +++++++++ .../qml/qqmldelegatemodel/qqmldelegatemodel.pro | 2 +- .../qqmldelegatemodel/tst_qqmldelegatemodel.cpp | 15 +++ 4 files changed, 154 insertions(+), 10 deletions(-) create mode 100644 tests/auto/qml/qqmldelegatemodel/data/removeFromGroup.qml diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 044f863ab1..bdb3b1227f 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -382,6 +382,83 @@ private: } }; +/*! \internal + \brief A helper class for iterating over a model that might change + + When populating the ListView from a model under normal + circumstances, we would iterate over the range of model indices + correspondning to the visual range, and basically call + createItem(index++) in order to create each item. + + This will also emit Component.onCompleted() for each item, which + might do some weird things... For instance, it might remove itself + from the model, and this might change model count and the indices + of the other subsequent entries in the model. + + This class takes such changes to the model into consideration while + iterating, and will adjust the iterator index and keep track of + whether the iterator has reached the end of the range. + + It keeps track of changes to the model by connecting to + QQmlInstanceModel::modelUpdated() from its constructor. + When destroyed, it will automatically disconnect. You can + explicitly disconnect earlier by calling \fn disconnect(). +*/ +class MutableModelIterator { +public: + MutableModelIterator(QQmlInstanceModel *model, int iBegin, int iEnd) + : removedAtIndex(false) + , backwards(iEnd < iBegin) + { + conn = QObject::connect(model, &QQmlInstanceModel::modelUpdated, + [&] (const QQmlChangeSet &changeSet, bool /*reset*/) + { + for (const QQmlChangeSet::Change &rem : changeSet.removes()) { + idxEnd -= rem.count; + if (rem.start() <= index) { + index -= rem.count; + if (index < rem.start() + rem.count) + removedAtIndex = true; // model index was removed + } + } + for (const QQmlChangeSet::Change &ins : changeSet.inserts()) { + idxEnd += ins.count; + if (ins.start() <= index) + index += ins.count; + } + } + ); + index = iBegin; + idxEnd = iEnd; + } + + bool hasNext() const { + return backwards ? index > idxEnd : index < idxEnd; + } + + void next() { index += (backwards ? -1 : +1); } + + ~MutableModelIterator() + { + disconnect(); + } + + void disconnect() + { + if (conn) { + QObject::disconnect(conn); + conn = QMetaObject::Connection(); // set to nullptr + } + } + int index = 0; + int idxEnd; + unsigned removedAtIndex : 1; + unsigned backwards : 1; +private: + QMetaObject::Connection conn; +}; + + //---------------------------------------------------------------------------- bool QQuickListViewPrivate::isContentFlowReversed() const @@ -3570,7 +3647,6 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) { // Insert items before the visible item. int insertionIdx = index; - int i = 0; qreal from = tempPos - displayMarginBeginning - buffer; if (insertionIdx < visibleIndex) { @@ -3579,15 +3655,18 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch insertResult->sizeChangesBeforeVisiblePos += count * (averageSize + spacing); } } else { - for (i = count-1; i >= 0 && pos >= from; --i) { + MutableModelIterator it(model, modelIndex + count - 1, modelIndex -1); + for (; it.hasNext() && pos >= from; it.next()) { // item is before first visible e.g. in cache buffer FxViewItem *item = nullptr; - if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) - item->index = modelIndex + i; + if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index)))) + item->index = it.index; if (!item) - item = createItem(modelIndex + i, QQmlIncubator::Synchronous); + item = createItem(it.index, QQmlIncubator::Synchronous); if (!item) return false; + if (it.removedAtIndex) + continue; visibleAffected = true; visibleItems.insert(insertionIdx, item); @@ -3620,16 +3699,20 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch } } else { - for (int i = 0; i < count && pos <= lastVisiblePos; ++i) { + MutableModelIterator it(model, modelIndex, modelIndex + count); + for (; it.hasNext() && pos <= lastVisiblePos; it.next()) { visibleAffected = true; FxViewItem *item = nullptr; - if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) - item->index = modelIndex + i; + if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index)))) + item->index = it.index; bool newItem = !item; + it.removedAtIndex = false; if (!item) - item = createItem(modelIndex + i, QQmlIncubator::Synchronous); + item = createItem(it.index, QQmlIncubator::Synchronous); if (!item) return false; + if (it.removedAtIndex) + continue; visibleItems.insert(index, item); if (index == 0) @@ -3650,6 +3733,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch pos += item->size() + spacing; ++index; } + it.disconnect(); if (0 < index && index < visibleItems.count()) { FxViewItem *prevItem = visibleItems.at(index - 1); diff --git a/tests/auto/qml/qqmldelegatemodel/data/removeFromGroup.qml b/tests/auto/qml/qqmldelegatemodel/data/removeFromGroup.qml new file mode 100644 index 0000000000..4ae1a8aacc --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/removeFromGroup.qml @@ -0,0 +1,45 @@ +import QtQuick 2.8 +import QtQml.Models 2.1 + +Item { + id: root + width: 200 + height: 200 + + DelegateModel { + id: visualModel + model: ListModel { + id: myLM + ListElement { + name: "Apple" + } + ListElement { + name: "Banana" + } + ListElement { + name: "Orange" + } + } + filterOnGroup: "selected" + groups: [ + DelegateModelGroup { + name: "selected" + includeByDefault: true + } + ] + delegate: Text { + Component.onCompleted: { + if (index === 1) { + DelegateModel.inSelected = false + } + } + text: index + ": " + model.name + } + } + + // Needs an actual ListView in order for the DelegateModel to instantiate all items + ListView { + model: visualModel + anchors.fill: parent + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro b/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro index 7fdd3ab5f1..fbd72f6a44 100644 --- a/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro +++ b/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro @@ -2,7 +2,7 @@ CONFIG += testcase TARGET = tst_qqmldelegatemodel macos:CONFIG -= app_bundle -QT += qml testlib core-private qml-private qmlmodels-private +QT += qml quick testlib core-private qml-private qmlmodels-private SOURCES += tst_qqmldelegatemodel.cpp diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp index 87f42c0c8a..71550a50f3 100644 --- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp +++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "../../shared/util.h" @@ -42,6 +44,7 @@ public: private slots: void valueWithoutCallingObjectFirst_data(); void valueWithoutCallingObjectFirst(); + void filterOnGroup_removeWhenCompleted(); }; class AbstractItemModel : public QAbstractItemModel @@ -134,6 +137,18 @@ void tst_QQmlDelegateModel::valueWithoutCallingObjectFirst() QCOMPARE(model->variantValue(index, role), expectedValue); } +void tst_QQmlDelegateModel::filterOnGroup_removeWhenCompleted() +{ + QQuickView view(testFileUrl("removeFromGroup.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QQuickItem *root = view.rootObject(); + QVERIFY(root); + QQmlDelegateModel *model = root->findChild(); + QVERIFY(model); + QTest::qWaitFor([=]{ return model->count() == 2; } ); +} + QTEST_MAIN(tst_QQmlDelegateModel) #include "tst_qqmldelegatemodel.moc" -- cgit v1.2.3 From 028a80801d52edb5dd8f47387ae675b925883f9c Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Fri, 29 Jan 2021 15:50:39 +0100 Subject: QuickTest: Do not recurse forever on inline components in enumerateTestCases In TestCaseCollector::enumerateTestCases, we visit the super compilation unit of QML tpyes to check if they might be instances of TestCase. However, in the case of inline components, the super unit is the current compilation unit, and we would recurse endlessly. This does not address the issue that an inline component might actually inherit TestCase. However, as this only affects the enumeration output and does not actually affect test execution, this is not that much of an issue. It should also be noted that the enumeration also fails in any case where TestCases are loaded dynamically (with a loader), so the method is not 100% accurate even in the absence of inline components. Fixes: QTBUG-90740 Task-number: QTBUG-90762 Change-Id: I7e133d62c4f62fc46e9bd3999ff755f7ded3c386 Reviewed-by: Ulf Hermann (cherry picked from commit edca9f3d60141a823a3b4401039f260c2c7f7888) Reviewed-by: Qt CI Bot Reviewed-by: Maximilian Goldstein --- src/qmltest/quicktest.cpp | 5 +- tests/auto/quicktest/quicktest.pro | 3 +- .../quicktest/testwithcomponents/data/Sample.qml | 8 ++++ .../testwithcomponents/data/tst_setup.qml | 53 ++++++++++++++++++++++ .../testwithcomponents/testwithcomponents.pro | 10 ++++ .../tst_quicktestwithcomponents.cpp | 32 +++++++++++++ 6 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 tests/auto/quicktest/testwithcomponents/data/Sample.qml create mode 100644 tests/auto/quicktest/testwithcomponents/data/tst_setup.qml create mode 100644 tests/auto/quicktest/testwithcomponents/testwithcomponents.pro create mode 100644 tests/auto/quicktest/testwithcomponents/tst_quicktestwithcomponents.cpp diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index a266f00e87..ccf676b36c 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -309,14 +309,17 @@ private: if (!object) // Start at root of compilation unit if not enumerating a specific child object = compilationUnit->objectAt(0); + if (object->flags & Object::IsInlineComponentRoot) + return result; if (const auto superTypeUnit = compilationUnit->resolvedTypes.value( object->inheritedTypeNameIndex)->compilationUnit()) { // We have a non-C++ super type, which could indicate we're a subtype of a TestCase if (testCaseType.isValid() && superTypeUnit->url() == testCaseType.sourceUrl()) result.isTestCase = true; - else + else if (superTypeUnit->url() != compilationUnit->url()) { // urls are the same for inline component, avoid infinite recursion result = enumerateTestCases(superTypeUnit); + } if (result.isTestCase) { // Look for override of name in this type diff --git a/tests/auto/quicktest/quicktest.pro b/tests/auto/quicktest/quicktest.pro index 6d09f76c1d..2116e4d3ac 100644 --- a/tests/auto/quicktest/quicktest.pro +++ b/tests/auto/quicktest/quicktest.pro @@ -3,4 +3,5 @@ SUBDIRS = \ polish \ signalspy \ quicktestmainwithsetup \ - testfiltering + testfiltering \ + testwithcomponents diff --git a/tests/auto/quicktest/testwithcomponents/data/Sample.qml b/tests/auto/quicktest/testwithcomponents/data/Sample.qml new file mode 100644 index 0000000000..78e3008b01 --- /dev/null +++ b/tests/auto/quicktest/testwithcomponents/data/Sample.qml @@ -0,0 +1,8 @@ +import QtQuick 2.15 + +Item { + id: root + + component InlineComponent: Rectangle {} + InlineComponent{} +} diff --git a/tests/auto/quicktest/testwithcomponents/data/tst_setup.qml b/tests/auto/quicktest/testwithcomponents/data/tst_setup.qml new file mode 100644 index 0000000000..533027147e --- /dev/null +++ b/tests/auto/quicktest/testwithcomponents/data/tst_setup.qml @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.2 + + +Item { + + Component { + id: mock + Sample { + } + } + + component Mock : Sample {} + + TestCase { + id: root + name: "ComponentTest" + + function test_create() + { + let dialog = createTemporaryObject(mock, root); + verify(dialog); + } + } +} diff --git a/tests/auto/quicktest/testwithcomponents/testwithcomponents.pro b/tests/auto/quicktest/testwithcomponents/testwithcomponents.pro new file mode 100644 index 0000000000..8f64dc2ebb --- /dev/null +++ b/tests/auto/quicktest/testwithcomponents/testwithcomponents.pro @@ -0,0 +1,10 @@ +CONFIG += qmltestcase +macos:CONFIG -= app_bundle +TARGET = tst_quicktestwithcomponents + +QT += testlib quick + +SOURCES += tst_quicktestwithcomponents.cpp + +TESTDATA += \ + $$PWD/data/*.qml diff --git a/tests/auto/quicktest/testwithcomponents/tst_quicktestwithcomponents.cpp b/tests/auto/quicktest/testwithcomponents/tst_quicktestwithcomponents.cpp new file mode 100644 index 0000000000..9692347cb8 --- /dev/null +++ b/tests/auto/quicktest/testwithcomponents/tst_quicktestwithcomponents.cpp @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +QUICK_TEST_MAIN(data) -- cgit v1.2.3 From 4a5b989755cc7e92a3d9045c37d272ee114c702a Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 8 Apr 2021 13:50:46 +0200 Subject: Do not auto-clean components with live inline components The inline components do not hold a strong reference to their outer type because that would be a reference cycle. Fixes: QTBUG-92236 Change-Id: I6d76a114352653210f0ece6c198cf761d3b4eda1 Reviewed-by: Fabian Kosmale (cherry picked from commit d0d4cc528ba9e3c39c15a2292066dac1d457abd5) --- src/qml/qml/qqmlmetatype.cpp | 12 +++++++++- tests/auto/qml/qqmllanguage/data/Tab1.qml | 9 ++++++++ tests/auto/qml/qqmllanguage/data/bareInline.qml | 9 ++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 28 ++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qml/qqmllanguage/data/Tab1.qml create mode 100644 tests/auto/qml/qqmllanguage/data/bareInline.qml diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 8a1bbd9459..e4699d0b55 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1260,6 +1260,16 @@ void QQmlMetaType::unregisterType(int typeIndex) } } +static bool hasActiveInlineComponents(const QQmlTypePrivate *d) +{ + for (const QQmlType &ic : qAsConst(d->objectIdToICType)) { + const QQmlTypePrivate *icPriv = ic.priv(); + if (icPriv && icPriv->count() > 1) + return true; + } + return false; +} + void QQmlMetaType::freeUnusedTypesAndCaches() { QQmlMetaTypeDataPtr data; @@ -1274,7 +1284,7 @@ void QQmlMetaType::freeUnusedTypesAndCaches() QList::Iterator it = data->types.begin(); while (it != data->types.end()) { const QQmlTypePrivate *d = (*it).priv(); - if (d && d->count() == 1) { + if (d && d->count() == 1 && !hasActiveInlineComponents(d)) { deletedAtLeastOneType = true; removeQQmlTypePrivate(data->idToType, d); diff --git a/tests/auto/qml/qqmllanguage/data/Tab1.qml b/tests/auto/qml/qqmllanguage/data/Tab1.qml new file mode 100644 index 0000000000..e1cd6d8c34 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/Tab1.qml @@ -0,0 +1,9 @@ +import QtQuick 2.15 + +Item { + component LeftTab: Item { + } + + component RightTab: Item { + } +} diff --git a/tests/auto/qml/qqmllanguage/data/bareInline.qml b/tests/auto/qml/qqmllanguage/data/bareInline.qml new file mode 100644 index 0000000000..cb79021250 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/bareInline.qml @@ -0,0 +1,9 @@ +import QtQuick 2.9 + +Item { + width: 800 + height: 600 + visible: true + + Tab1.RightTab {} +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index e247a139ec..de8b2ef7eb 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include "testtypes.h" #include "testhttpserver.h" @@ -331,6 +332,7 @@ private slots: void arrayToContainer(); void qualifiedScopeInCustomParser(); void accessNullPointerPropertyCache(); + void bareInlineComponent(); private: QQmlEngine engine; @@ -5813,6 +5815,32 @@ void tst_qqmllanguage::accessNullPointerPropertyCache() QVERIFY(!obj.isNull()); } +void tst_qqmllanguage::bareInlineComponent() +{ + QQmlEngine engine; + + QQmlComponent c(&engine, testFileUrl("bareInline.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer o(c.create()); + QVERIFY(!o.isNull()); + + QQmlMetaType::freeUnusedTypesAndCaches(); + + bool tab1Found = false; + const auto types = QQmlMetaType::qmlTypes(); + for (const QQmlType &type : types) { + if (type.elementName() == QStringLiteral("Tab1")) { + QVERIFY(type.module().isEmpty()); + tab1Found = true; + const auto ics = type.priv()->objectIdToICType; + QVERIFY(ics.size() > 0); + for (const QQmlType &ic : ics) + QVERIFY(ic.containingType() == type); + } + } + QVERIFY(tab1Found); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" -- cgit v1.2.3 From 57ee54fb4770b618b0bbd06b069682b5cd11fc9f Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 12 Apr 2021 11:53:25 +0200 Subject: Fix incorrect depth test state with QSGRenderNode::DepthAwareRendering Not applicable to Qt 6. But in Qt 5 GL_DEPTH_TEST must be re-enabled after the QSGRenderNode has done with its work, and the rendernode specified DepthAwareRendering. For rendernodes without this flag everything is fine, but when the flag is set, the usage of the depth buffer, and so depth testing in the alpha pass, stays enabled, which means leaving GL_DEPTH_TEST as disabled will lead to incorrect rendering with semi-transparent nodes that come after the rendernode. Fixes: QTBUG-92036 Change-Id: Ide9e862e3504ded31eba5ee2a4caf804b69812e3 Reviewed-by: Andy Nichols --- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index c34327bb41..913533b389 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -4444,6 +4444,9 @@ void Renderer::renderRenderNode(Batch *batch) // legacy (GL-only) opacity = opacity->parent(); } + // having DepthAwareRendering leaves depth test on in the alpha pass + const bool depthTestWasEnabled = m_useDepthBuffer; + glDisable(GL_STENCIL_TEST); glDisable(GL_SCISSOR_TEST); glDisable(GL_DEPTH_TEST); @@ -4478,7 +4481,9 @@ void Renderer::renderRenderNode(Batch *batch) // legacy (GL-only) m_currentClipType = ClipState::NoClip; } - if (changes & QSGRenderNode::DepthState) + if (depthTestWasEnabled) + glEnable(GL_DEPTH_TEST); + else if (changes & QSGRenderNode::DepthState) glDisable(GL_DEPTH_TEST); if (changes & QSGRenderNode::ColorState) -- cgit v1.2.3 From 8ef947deab6c9345ddac2d96dc60f2490974c1df Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 9 Apr 2021 10:15:36 +0200 Subject: Don't crash when trying to invoke non-existing string converter String converters are removed in 6.2 anyway. Fixes: QTBUG-89892 Change-Id: I504c00d99580e3d27d04f420295dd97251657ef4 Reviewed-by: Fabian Kosmale (cherry picked from commit d250e0070701e9c511ef5b1fb0d23995872ad844) --- src/qml/qml/qqmlobjectcreator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 4920f83365..c1b60d13b0 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -679,8 +679,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const // otherwise, try a custom type assignment QString stringValue = compilationUnit->bindingValueAsString(binding); QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); - Q_ASSERT(converter); - QVariant value = (*converter)(stringValue); + QVariant value = converter ? (*converter)(stringValue) : QVariant(); QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex()); if (value.isNull() || metaProperty.userType() != property->propType()) { -- cgit v1.2.3 From c7c2512a6736ff9fff7d3ed1b90fb6d57a656bcb Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 29 Mar 2021 13:36:27 +0200 Subject: Do not batch lines with > 1 width in alpha pass Cannot possibly do reasonable overlap checks when we have no idea how such lines are rasterized, meaning we do not know the real bounds of the geometry. Fixes: QTBUG-91749 Change-Id: Ia444232330da2f1d29841589f0e65bb52822c4ae Reviewed-by: Eskil Abrahamsen Blomfeldt (cherry picked from commit 74c458f9fdf0639cd68684b5184bf561166e14cb) Reviewed-by: Qt Cherry-pick Bot --- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 913533b389..024c0d68ef 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -1958,7 +1958,11 @@ void Renderer::prepareAlphaBatches() if (gni->clipList() == gnj->clipList() && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode() - && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth()) + && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines + || (gni->geometry()->lineWidth() == gnj->geometry()->lineWidth() + // Must not do overlap checks when the line width is not 1, + // we have no knowledge how such lines are rasterized. + && gni->geometry()->lineWidth() == 1.0f)) && gni->geometry()->attributes() == gnj->geometry()->attributes() && gni->inheritedOpacity() == gnj->inheritedOpacity() && gni->activeMaterial()->type() == gnj->activeMaterial()->type() -- cgit v1.2.3 From e1163220c71a6dfc326534f65ebeeade972f857c Mon Sep 17 00:00:00 2001 From: Maximilian Goldstein Date: Mon, 1 Mar 2021 15:34:58 +0100 Subject: qqmlapplicationengine: Handle errors during component creation Previously QQmlApplicationEngine did not handle any errors that occurred during object creation (i.e. failures to initialize required properties) which lead to QObject::connect errors and to the error messages not getting printed among other issues. Change-Id: I69bc566a6d349c786cae82a963a621388684c8f5 Reviewed-by: Fabian Kosmale (cherry picked from commit 890cb4cb236333fd5b112fffc0e9088ecb43f2df) --- src/qml/qml/qqmlapplicationengine.cpp | 8 ++++++++ tests/auto/qml/qqmlapplicationengine/data/Required.qml | 6 ++++++ .../qqmlapplicationengine/data/requiredViolation.qml | 3 +++ .../tst_qqmlapplicationengine.cpp | 18 ++++++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 tests/auto/qml/qqmlapplicationengine/data/Required.qml create mode 100644 tests/auto/qml/qqmlapplicationengine/data/requiredViolation.qml diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 9af3437cb0..8f7f2e48eb 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -146,6 +146,14 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) break; case QQmlComponent::Ready: { auto newObj = initialProperties.empty() ? c->create() : c->createWithInitialProperties(initialProperties); + + if (c->isError()) { + qWarning() << "QQmlApplicationEngine failed to create component"; + warning(c->errors()); + q->objectCreated(nullptr, c->url()); + break; + } + objects << newObj; QObject::connect(newObj, &QObject::destroyed, q, [&](QObject *obj) { objects.removeAll(obj); }); q->objectCreated(objects.constLast(), c->url()); diff --git a/tests/auto/qml/qqmlapplicationengine/data/Required.qml b/tests/auto/qml/qqmlapplicationengine/data/Required.qml new file mode 100644 index 0000000000..acf4a00ce6 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/Required.qml @@ -0,0 +1,6 @@ +import QtQml 2.15 + +QtObject +{ + required property int foo +} diff --git a/tests/auto/qml/qqmlapplicationengine/data/requiredViolation.qml b/tests/auto/qml/qqmlapplicationengine/data/requiredViolation.qml new file mode 100644 index 0000000000..c5de4661a9 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/requiredViolation.qml @@ -0,0 +1,3 @@ +import QtQml 2.15 + +Required {} diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index f636e527c3..4306c7b8ca 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -56,6 +56,7 @@ private slots: void translationChange(); void setInitialProperties(); void failureToLoadTriggersWarningSignal(); + void errorWhileCreating(); private: QString buildDir; @@ -333,6 +334,23 @@ void tst_qqmlapplicationengine::failureToLoadTriggersWarningSignal() QTRY_COMPARE(warningObserver.count(), 1); } +void tst_qqmlapplicationengine::errorWhileCreating() +{ + auto url = testFileUrl("requiredViolation.qml"); + QQmlApplicationEngine test; + QSignalSpy observer(&test, &QQmlApplicationEngine::objectCreated); + + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlApplicationEngine failed to create component"); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(QStringLiteral("%1:5:5: Required property foo was not initialized").arg(testFileUrl("Required.qml").toString()))); + + test.load(url); + + QTRY_COMPARE(observer.count(), 1); + QList args = observer.takeFirst(); + QVERIFY(args.at(0).isNull()); + QCOMPARE(args.at(1).toUrl(), url); +} + QTEST_MAIN(tst_qqmlapplicationengine) #include "tst_qqmlapplicationengine.moc" -- cgit v1.2.3 From a2df591e390f3730285a7d572bcebae086b96485 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Wed, 17 Mar 2021 17:36:36 +0100 Subject: Assert that pointer is non-null ...else the analyser gets confused. mapFn only is true when argc is greater than 2, and the second argument is actually a function. In that case, we know that mapArguments is not null, as we've called scope.alloc. Silences report 0b232fda14. Pick-to: 6.1 Change-Id: I8d20ad2fcb0b9fcc4a6451e85f61e9a63679ee26 Reviewed-by: Andrei Golubev Reviewed-by: Ulf Hermann (cherry picked from commit 5cd39d8418df74fb8e5b50fdeff591cfc951ece2) --- src/qml/jsruntime/qv4arrayobject.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 1b8fbda789..27326b9fd8 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -254,6 +254,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V } if (mapfn) { + Q_ASSERT(mapArguments); // if mapfn is set, we always setup mapArguments with scope.alloc mapArguments[0] = *nextValue; mapArguments[1] = Value::fromDouble(k); mappedValue = mapfn->call(thisArg, mapArguments, 2); @@ -297,6 +298,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V CHECK_EXCEPTION(); if (mapfn) { + Q_ASSERT(mapArguments); // if mapfn is set, we always setup mapArguments with scope.alloc mapArguments[0] = kValue; mapArguments[1] = Value::fromDouble(k); mappedValue = mapfn->call(thisArg, mapArguments, 2); -- cgit v1.2.3 From b7f46486acb148acb45be3d20e827dbbeadb6547 Mon Sep 17 00:00:00 2001 From: Wang Chuan Date: Mon, 5 Apr 2021 11:41:48 +0800 Subject: QQuickTextInput: update cursor rectangle after padding changed The position of cursor delegate needs to be updated when we change padding, otherwise it will be in a wrong position. Fixes: QTBUG-91867 Change-Id: I89ca84fe893ebf517ab67890196eede14a4055d7 Reviewed-by: Shawn Rutledge (cherry picked from commit d98694c4023881673259ba040c10df7e71ec3d37) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktextinput.cpp | 5 ++++ .../data/checkCursorDelegateWhenPaddingChanged.qml | 16 ++++++++++++ .../quick/qquicktextinput/tst_qquicktextinput.cpp | 30 ++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 tests/auto/quick/qquicktextinput/data/checkCursorDelegateWhenPaddingChanged.qml diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index c069639310..c6944690f9 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -2952,6 +2952,7 @@ void QQuickTextInputPrivate::setTopPadding(qreal value, bool reset) } if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { updateLayout(); + q->updateCursorRectangle(); emit q->topPaddingChanged(); } } @@ -2966,6 +2967,7 @@ void QQuickTextInputPrivate::setLeftPadding(qreal value, bool reset) } if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { updateLayout(); + q->updateCursorRectangle(); emit q->leftPaddingChanged(); } } @@ -2980,6 +2982,7 @@ void QQuickTextInputPrivate::setRightPadding(qreal value, bool reset) } if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { updateLayout(); + q->updateCursorRectangle(); emit q->rightPaddingChanged(); } } @@ -2994,6 +2997,7 @@ void QQuickTextInputPrivate::setBottomPadding(qreal value, bool reset) } if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { updateLayout(); + q->updateCursorRectangle(); emit q->bottomPaddingChanged(); } } @@ -4732,6 +4736,7 @@ void QQuickTextInput::setPadding(qreal padding) d->extra.value().padding = padding; d->updateLayout(); + updateCursorRectangle(); emit paddingChanged(); if (!d->extra.isAllocated() || !d->extra->explicitTopPadding) emit topPaddingChanged(); diff --git a/tests/auto/quick/qquicktextinput/data/checkCursorDelegateWhenPaddingChanged.qml b/tests/auto/quick/qquicktextinput/data/checkCursorDelegateWhenPaddingChanged.qml new file mode 100644 index 0000000000..e6f07b4687 --- /dev/null +++ b/tests/auto/quick/qquicktextinput/data/checkCursorDelegateWhenPaddingChanged.qml @@ -0,0 +1,16 @@ +import QtQuick 2.12 + +Rectangle { + width: 200 + height: 200 + TextInput { + objectName: "textInput" + leftPadding: 10 + focus: true + cursorDelegate: Rectangle { + objectName: "cursorDelegate" + width: 5 + color: "red" + } + } +} diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 2e64c80b85..ac502bcb28 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -236,6 +236,7 @@ private slots: void QTBUG_51115_readOnlyResetsSelection(); void QTBUG_77814_InsertRemoveNoSelection(); + void checkCursorDelegateWhenPaddingChanged(); private: void simulateKey(QWindow *, int key); @@ -7054,6 +7055,35 @@ void tst_qquicktextinput::QTBUG_77814_InsertRemoveNoSelection() QCOMPARE(textInput->selectedText(), QString()); } +void tst_qquicktextinput::checkCursorDelegateWhenPaddingChanged() +{ + QQuickView view; + view.setSource(testFileUrl("checkCursorDelegateWhenPaddingChanged.qml")); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QQuickTextInput *textInput = view.rootObject()->findChild("textInput"); + QVERIFY(textInput); + + QQuickItem *cursorDelegate = textInput->findChild("cursorDelegate"); + QVERIFY(cursorDelegate); + + QCOMPARE(cursorDelegate->x(), textInput->leftPadding()); + QCOMPARE(cursorDelegate->y(), textInput->topPadding()); + + textInput->setPadding(5); + QCOMPARE(cursorDelegate->x(), textInput->leftPadding()); + QCOMPARE(cursorDelegate->y(), textInput->topPadding()); + + textInput->setTopPadding(10); + QCOMPARE(cursorDelegate->x(), textInput->leftPadding()); + QCOMPARE(cursorDelegate->y(), textInput->topPadding()); + + textInput->setLeftPadding(10); + QCOMPARE(cursorDelegate->x(), textInput->leftPadding()); + QCOMPARE(cursorDelegate->y(), textInput->topPadding()); +} + QTEST_MAIN(tst_qquicktextinput) #include "tst_qquicktextinput.moc" -- cgit v1.2.3 From fdebe9afad9341b5804e118fb2f7f24ad78f2b3d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 8 Mar 2021 13:27:55 +0100 Subject: doc: fix up QQuickItem::contains() docs - \c true and \c false - indentation - mention containmentMask() - overwritten -> overridden - hit-testing is for all pointing devices, not just the mouse Pick-to: 6.1 Change-Id: I1debe1f0b3a4f729225c462b20dd10bc4e1cf8b0 Reviewed-by: Richard Moe Gustavsen Reviewed-by: Paul Wicking (cherry picked from commit 66a0a93c0acf53d4e050066c1c480c66cd635f15) Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index f144f3ec5d..0ec28b843a 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -7812,22 +7812,23 @@ void QQuickItem::setKeepTouchGrab(bool keep) } /*! - \qmlmethod bool QtQuick::Item::contains(point point) + \qmlmethod bool QtQuick::Item::contains(point point) - Returns true if this item contains \a point, which is in local coordinates; - returns false otherwise. - */ + Returns \c true if this item contains \a point, which is in local coordinates; + returns \c false otherwise. This is the same check that is used for + hit-testing a QEventPoint during event delivery, and is affected by + containmentMask() if it is set. +*/ /*! - Returns true if this item contains \a point, which is in local coordinates; - returns false otherwise. + Returns \c true if this item contains \a point, which is in local coordinates; + returns \c false otherwise. - This function can be overwritten in order to handle point collisions in items - with custom shapes. The default implementation checks if the point is inside - the item's bounding rect. + This function can be overridden in order to handle point collisions in items + with custom shapes. The default implementation checks whether the point is inside + containmentMask() if it is set, or inside the bounding box otherwise. - Note that this method is generally used to check whether the item is under the mouse cursor, - and for that reason, the implementation of this function should be as light-weight - as possible. + \note This method is used for hit-testing each QEventPoint during event + delivery, so the implementation should be kept as lightweight as possible. */ bool QQuickItem::contains(const QPointF &point) const { -- cgit v1.2.3 From e5d149d9839516c371aea6da36fda98663e137f0 Mon Sep 17 00:00:00 2001 From: Siyeon Seo Date: Wed, 10 Mar 2021 17:14:08 +0900 Subject: Avoid crash when accessing an empty QTextLine In any case an empty QTextLine should not be used. Q_ASSERT doesn't help in non-debug build. Pick-to: 6.1 Change-Id: I10b7895bc9b4cfd061aea086f810a9f8bf6d301a Reviewed-by: Shawn Rutledge (cherry picked from commit 447831e6e0fcc37e8617430827702b59856d5edb) --- src/quick/items/qquicktext.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 8a6b73fbd5..b6c81f266c 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -1179,8 +1179,8 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) QTextLine firstLine = visibleCount == 1 && elideLayout ? elideLayout->lineAt(0) : layout.lineAt(0); - Q_ASSERT(firstLine.isValid()); - *baseline = firstLine.y() + firstLine.ascent(); + if (firstLine.isValid()) + *baseline = firstLine.y() + firstLine.ascent(); if (!customLayout) br.setHeight(height); -- cgit v1.2.3 From 1089f8f226d546aef3b032a2f4391e08a3afa48d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 26 Jan 2021 09:20:36 +0100 Subject: Prevent infinite recursion in QQuickItemPrivate::itemToWindowTransform Not sure what the XXX TODO was for, but maybe some error checking... This infinite recursion seems to happen in some pathological case, but not normally, of course. Pick-to: 6.1 Change-Id: I6ad56e4b3002d151b2e6007669c46f0507d7f0e0 Reviewed-by: Shawn Rutledge (cherry picked from commit 463ac8b43d0df8eea8efda7fc240fbb7a313010b) --- src/quick/items/qquickitem.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 0ec28b843a..bbacf23d16 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3071,8 +3071,9 @@ Returns a transform that maps points from item space into window space. */ QTransform QQuickItemPrivate::itemToWindowTransform() const { - // XXX todo - QTransform rv = parentItem?QQuickItemPrivate::get(parentItem)->itemToWindowTransform():QTransform(); + // item's parent must not be itself, otherwise calling itemToWindowTransform() on it is infinite recursion + Q_ASSERT(!parentItem || QQuickItemPrivate::get(parentItem) != this); + QTransform rv = parentItem ? QQuickItemPrivate::get(parentItem)->itemToWindowTransform() : QTransform(); itemToParentTransform(rv); return rv; } -- cgit v1.2.3