diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-02-15 21:05:45 +0100 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-02-15 21:05:45 +0100 |
commit | cf142cd637620e2af6ede46c4f2cefe0206d6159 (patch) | |
tree | 10ec6455b795a45152999e74335e5ae7f592903a /src | |
parent | 70f35501da4d1bbb36703dcc7b121c454f8d57a9 (diff) | |
parent | 238cc098d785b4fe76fbc8422b340d98ff8c1a1b (diff) |
Merge remote-tracking branch 'origin/5.11' into dev
Change-Id: I2d4c012c8ca578f90d7eb56dbc6b306ac7cbb841
Diffstat (limited to 'src')
49 files changed, 480 insertions, 206 deletions
diff --git a/src/3rdparty/masm/yarr/YarrPattern.cpp b/src/3rdparty/masm/yarr/YarrPattern.cpp index ae527f065f..c7e5b6b09b 100644 --- a/src/3rdparty/masm/yarr/YarrPattern.cpp +++ b/src/3rdparty/masm/yarr/YarrPattern.cpp @@ -233,7 +233,7 @@ private: ranges.insert(i, CharacterRange(lo, hi)); return; } - // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the begining + // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the beginning // If the new range start at or before the end of the last range, then the overlap (if it starts one after the // end of the last range they concatenate, which is just as good. if (lo <= (ranges[i].end + 1)) { diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index da5a525ff0..f0fdebe162 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -633,6 +633,7 @@ void QQuickShapePath::resetFillGradient() */ QQuickShapePrivate::QQuickShapePrivate() + : effectRefCount(0) { } @@ -910,10 +911,12 @@ void QQuickShape::updatePolish() { Q_D(QQuickShape); - if (!d->spChanged) + const int currentEffectRefCount = d->extra.isAllocated() ? d->extra->recursiveEffectRefCount : 0; + if (!d->spChanged && currentEffectRefCount <= d->effectRefCount) return; d->spChanged = false; + d->effectRefCount = currentEffectRefCount; if (!d->renderer) { d->createRenderer(); @@ -925,7 +928,7 @@ void QQuickShape::updatePolish() // endSync() is where expensive calculations may happen (or get kicked off // on worker threads), depending on the backend. Therefore do this only // when the item is visible. - if (isVisible()) + if (isVisible() || d->effectRefCount > 0) d->sync(); update(); diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h index c3d84b6353..ef2775885e 100644 --- a/src/imports/shapes/qquickshape_p_p.h +++ b/src/imports/shapes/qquickshape_p_p.h @@ -170,6 +170,7 @@ public: static void asyncShapeReady(void *data); + int effectRefCount; QVector<QQuickShapePath *> sp; QElapsedTimer syncTimer; QQuickAbstractPathRenderer *renderer = nullptr; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 0d3f78d95d..0d501f48c0 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -145,9 +145,6 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) rc->invalidate(); - if (m_windows.isEmpty()) - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - delete rc; delete engine; diff --git a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp index d31156f0eb..9493920100 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp @@ -91,7 +91,6 @@ void QSGOpenVGRenderLoop::windowDestroyed(QQuickWindow *window) rc->invalidate(); delete vg; vg = nullptr; - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); } else if (vg && window == vg->window()) { vg->doneCurrent(); } diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index c95f8ad344..6de0ed6f7d 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1587,6 +1587,9 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(data); qmlUnit->unitSize = totalSize; qmlUnit->flags |= QV4::CompiledData::Unit::IsQml; + // This unit's memory was allocated with malloc on the heap, so it's + // definitely not suitable for StaticData access. + qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; qmlUnit->offsetToImports = unitSize; qmlUnit->nImports = output.imports.count(); qmlUnit->offsetToObjects = unitSize + importSize; diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 0a525e418c..7a52f3a12c 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -553,11 +553,11 @@ public: void appendAliasPropertiesToMetaObjects(); - void appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex); + QQmlCompileError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex); private: void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex); - void propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyRawData::Flags *propertyFlags); + QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyRawData::Flags *propertyFlags); void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const; @@ -668,7 +668,7 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAl } template <typename ObjectContainer> -inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias( +inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias( const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion, QQmlPropertyData::Flags *propertyFlags) { @@ -687,12 +687,16 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias auto targetAlias = targetObject.aliasesBegin(); for (uint i = 0; i < alias.localAliasIndex; ++i) ++targetAlias; - propertyDataForAlias(component, *targetAlias, type, minorVersion, propertyFlags); - return; + return propertyDataForAlias(component, *targetAlias, type, minorVersion, propertyFlags); } else if (alias.encodedMetaPropertyIndex == -1) { Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); auto *typeRef = objectContainer->resolvedTypes.value(targetObject.inheritedTypeNameIndex); - Q_ASSERT(typeRef); + if (!typeRef) { + // Can be caused by the alias target not being a valid id or property. E.g.: + // property alias dataValue: dataVal + // invalidAliasComponent { id: dataVal } + return QQmlCompileError(targetObject.location, QQmlPropertyCacheCreatorBase::tr("Invalid alias target")); + } if (typeRef->type.isValid()) *type = typeRef->type.typeId(); @@ -737,15 +741,16 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable; propertyFlags->isResettable = resettable; + return QQmlCompileError(); } template <typename ObjectContainer> -inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache( +inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache( const CompiledObject &component, int objectIndex) { const CompiledObject &object = *objectContainer->objectAt(objectIndex); if (!object.aliasCount()) - return; + return QQmlCompileError(); QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); Q_ASSERT(propertyCache); @@ -762,7 +767,9 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPrope int type = 0; int minorVersion = 0; QQmlPropertyData::Flags propertyFlags; - propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags); + QQmlCompileError error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags); + if (error.isSet()) + return error; const QString propertyName = objectContainer->stringAt(alias->nameIndex); @@ -772,6 +779,8 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPrope propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, type, minorVersion, effectiveSignalIndex++); } + + return QQmlCompileError(); } template <typename ObjectContainer> diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index df1f00f2f7..ac373aafb4 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -1023,7 +1023,11 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) } if (result == AllAliasesResolved) { - aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex); + QQmlCompileError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex); + if (error.isSet()) { + recordError(error); + return false; + } atLeastOneAliasResolved = true; } else if (result == SomeAliasesResolved) { atLeastOneAliasResolved = true; diff --git a/src/qml/doc/snippets/code/src_script_qjsengine.cpp b/src/qml/doc/snippets/code/src_script_qjsengine.cpp index 3799189f83..6c58fd8a18 100644 --- a/src/qml/doc/snippets/code/src_script_qjsengine.cpp +++ b/src/qml/doc/snippets/code/src_script_qjsengine.cpp @@ -114,3 +114,23 @@ engine.globalObject().setProperty("myObject", myScriptQObject); qDebug() << engine.evaluate("myObject.dynamicProperty").toInt(); //! [6] + + +//! [7] +class MyObject : public QObject +{ + Q_OBJECT + +public: + Q_INVOKABLE MyObject() {} +}; +//! [7] + +//! [8] +QJSValue jsMetaObject = engine.newQMetaObject(&MyObject::staticMetaObject); +engine.globalObject().setProperty("MyObject", jsMetaObject); +//! [8] + +//! [9] +engine.evaluate("var myObject = new MyObject()"); +//! [9] diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index 8a62a18eec..f939ddbcf3 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -324,6 +324,19 @@ qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", example_qjsvalue_singletontype_provider); \endcode + Alternatively, you can use a C++11 lambda: + + \code + qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue { + Q_UNUSED(engine) + + static int seedValue = 5; + QJSValue example = scriptEngine->newObject(); + example.setProperty("someProperty", seedValue++); + return example; + }); + \endcode + In order to use the registered singleton type in QML, you must import the singleton type. \qml import QtQuick 2.0 @@ -423,6 +436,18 @@ qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", example_qobject_singletontype_provider); \endcode + Alternatively, you can use a C++11 lambda: + + \code + qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qjsvalueApi", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + + SingletonTypeExample *example = new SingletonTypeExample(); + return example; + }); + \endcode + In order to use the registered singleton type in QML, you must import the singleton type. \qml import QtQuick 2.0 diff --git a/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc b/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc index 1b645a94c0..9eb8f72cf2 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc @@ -38,6 +38,8 @@ imperative code, in the case where complex custom application behavior is needed QML source code is generally loaded by the engine through QML \e documents, which are standalone documents of QML code. These can be used to define \l {QML Object Types}{QML object types} that can then be reused throughout an application. +Note that type names must begin with an uppercase letter in order +to be declared as QML object types in a QML file. \section1 Import Statements diff --git a/src/qml/doc/src/qmllanguageref/typesystem/objecttypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/objecttypes.qdoc index b74646b7d0..5f089b5ebc 100644 --- a/src/qml/doc/src/qmllanguageref/typesystem/objecttypes.qdoc +++ b/src/qml/doc/src/qmllanguageref/typesystem/objecttypes.qdoc @@ -45,6 +45,8 @@ type, as discussed in \l {qtqml-documents-definetypes.html} {Documents as QML object type definitions}, or by defining a QML type from C++ and registering the type with the QML engine, as discussed in \l{qtqml-cppintegration-definetypes.html}{Defining QML Types from C++}. +Note that in both cases, the type name must begin with an uppercase letter in +order to be declared as a QML object type in a QML file. \section1 Defining Object Types from QML diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 048c5856ce..017eabd850 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -166,16 +166,30 @@ Q_DECLARE_METATYPE(QList<int>) properties of the proxy object. No binding code is needed because it is done dynamically using the Qt meta object system. + \snippet code/src_script_qjsengine.cpp 5 + Use newQMetaObject() to wrap a QMetaObject; this gives you a "script representation" of a QObject-based class. newQMetaObject() returns a proxy script object; enum values of the class are available as properties of the proxy object. - Constructors exposed to the meta-object system ( using Q_INVOKABLE ) can be + Constructors exposed to the meta-object system (using Q_INVOKABLE) can be called from the script to create a new QObject instance with - JavaScriptOwnership. + JavaScriptOwnership. For example, given the following class definition: - \snippet code/src_script_qjsengine.cpp 5 + \snippet code/src_script_qjsengine.cpp 7 + + The \c staticMetaObject for the class can be exposed to JavaScript like so: + + \snippet code/src_script_qjsengine.cpp 8 + + Instances of the class can then be created in JavaScript: + + \snippet code/src_script_qjsengine.cpp 9 + + \note Currently only classes using the Q_OBJECT macro are supported; it is + not possible to expose the \c staticMetaObject of a Q_GADGET class to + JavaScript. \section2 Dynamic QObject Properties @@ -541,7 +555,7 @@ QJSValue QJSEngine::newQObject(QObject *object) When called as a constructor, a new instance of the class will be created. Only constructors exposed by Q_INVOKABLE will be visible from the script engine. - \sa newQObject() + \sa newQObject(), {QObject Integration} */ QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) { @@ -558,7 +572,7 @@ QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) { Creates a JavaScript object that wraps the static QMetaObject associated with class \c{T}. - \sa newQObject() + \sa newQObject(), {QObject Integration} */ diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index f1f866f5a9..45c0b6cfbb 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -175,6 +175,9 @@ static void removeFromPropertyData(Object *object, int idx, bool accessor = fals int size = o->internalClass->size; for (int i = idx; i < size; ++i) o->setProperty(v4, i, *o->propertyData(i + (accessor ? 2 : 1))); + o->setProperty(v4, size, Primitive::undefinedValue()); + if (accessor) + o->setProperty(v4, size + 1, Primitive::undefinedValue()); } void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index) diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 0b31c971f9..973541553a 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -68,6 +68,22 @@ Page *getPage(Value *val) { return reinterpret_cast<Page *>(reinterpret_cast<quintptr>(val) & ~((quintptr)(WTF::pageSize() - 1))); } +QML_NEARLY_ALWAYS_INLINE void insertInFront(PersistentValueStorage *storage, Page *p) +{ + p->header.next = reinterpret_cast<Page *>(storage->firstPage); + p->header.prev = reinterpret_cast<Page **>(&storage->firstPage); + if (p->header.next) + p->header.next->header.prev = &p->header.next; + storage->firstPage = p; +} + +QML_NEARLY_ALWAYS_INLINE void unlink(Page *p) +{ + if (p->header.prev) + *p->header.prev = p->header.next; + if (p->header.next) + p->header.next->header.prev = p->header.prev; +} Page *allocatePage(PersistentValueStorage *storage) { @@ -78,19 +94,14 @@ Page *allocatePage(PersistentValueStorage *storage) p->header.engine = storage->engine; p->header.alloc = page; - p->header.next = reinterpret_cast<Page *>(storage->firstPage); - p->header.prev = reinterpret_cast<Page **>(&storage->firstPage); p->header.refCount = 0; p->header.freeList = 0; - if (p->header.next) - p->header.next->header.prev = &p->header.next; + insertInFront(storage, p); for (int i = 0; i < kEntriesPerPage - 1; ++i) { p->values[i].setEmpty(i + 1); } p->values[kEntriesPerPage - 1].setEmpty(-1); - storage->firstPage = p; - return p; } @@ -195,6 +206,12 @@ Value *PersistentValueStorage::allocate() Value *v = p->values + p->header.freeList; p->header.freeList = v->int_32(); + + if (p->header.freeList != -1 && p != firstPage) { + unlink(p); + insertInFront(this, p); + } + ++p->header.refCount; v->setRawValue(Encode::undefined()); @@ -237,10 +254,7 @@ ExecutionEngine *PersistentValueStorage::getEngine(Value *v) void PersistentValueStorage::freePage(void *page) { Page *p = static_cast<Page *>(page); - if (p->header.prev) - *p->header.prev = p->header.next; - if (p->header.next) - p->header.next->header.prev = p->header.prev; + unlink(p); p->header.alloc.deallocate(); } diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 21af4270fd..9c33966cf7 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -108,8 +108,8 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP return result->asReturnedValue(); } - // Its possible we could delay the calculation of the "actual" context (in the case - // of sub contexts) until it is definately needed. + // It's possible we could delay the calculation of the "actual" context (in the case + // of sub contexts) until it is definitely needed. QQmlContextData *context = resource->getContext(); QQmlContextData *expressionContext = context; @@ -248,8 +248,8 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) return Object::put(m, name, value); } - // Its possible we could delay the calculation of the "actual" context (in the case - // of sub contexts) until it is definately needed. + // It's possible we could delay the calculation of the "actual" context (in the case + // of sub contexts) until it is definitely needed. QQmlContextData *context = wrapper->getContext(); QQmlContextData *expressionContext = context; diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 843fbc1d91..b3f04d69be 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -57,6 +57,8 @@ #include "qv4global_p.h" #include <private/qv4heap_p.h> +#include <private/qnumeric_p.h> + QT_BEGIN_NAMESPACE namespace QV4 { @@ -322,6 +324,8 @@ public: return d; } QML_NEARLY_ALWAYS_INLINE void setDouble(double d) { + if (qt_is_nan(d)) + d = qt_qnan(); memcpy(&_val, &d, 8); _val ^= NaNEncodeMask; Q_ASSERT(isDouble()); diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 4a0c12b9da..9c51013317 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -342,6 +342,9 @@ bool Chunk::sweep(ExecutionEngine *engine) v->destroy(b); b->_checkIsDestroyed(); } +#ifdef V4_USE_HEAPTRACK + heaptrack_report_free(itemToFree); +#endif } Q_V4_PROFILE_DEALLOC(engine, qPopulationCount((objectBitmap[i] | extendsBitmap[i]) - (blackBitmap[i] | e)) * Chunk::SlotSize, @@ -390,6 +393,9 @@ void Chunk::freeAll(ExecutionEngine *engine) b->vtable()->destroy(b); b->_checkIsDestroyed(); } +#ifdef V4_USE_HEAPTRACK + heaptrack_report_free(itemToFree); +#endif } Q_V4_PROFILE_DEALLOC(engine, (qPopulationCount(objectBitmap[i]|extendsBitmap[i]) - qPopulationCount(e)) * Chunk::SlotSize, Profiling::SmallItem); @@ -602,6 +608,9 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { done: m->setAllocatedSlots(slotsRequired); Q_V4_PROFILE_ALLOC(engine, slotsRequired * Chunk::SlotSize, Profiling::SmallItem); +#ifdef V4_USE_HEAPTRACK + heaptrack_report_alloc(m, slotsRequired * Chunk::SlotSize); +#endif // DEBUG << " " << hex << m->chunk() << m->chunk()->objectBitmap[0] << m->chunk()->extendsBitmap[0] << (m - m->chunk()->realBase()); return m; } @@ -659,6 +668,9 @@ HeapItem *HugeItemAllocator::allocate(size_t size) { chunks.push_back(HugeChunk{c, size}); Chunk::setBit(c->objectBitmap, c->first() - c->realBase()); Q_V4_PROFILE_ALLOC(engine, size, Profiling::LargeItem); +#ifdef V4_USE_HEAPTRACK + heaptrack_report_alloc(c, size); +#endif return c->first(); } @@ -675,6 +687,9 @@ static void freeHugeChunk(ChunkAllocator *chunkAllocator, const HugeItemAllocato b->_checkIsDestroyed(); } chunkAllocator->free(c.chunk, c.size); +#ifdef V4_USE_HEAPTRACK + heaptrack_report_free(c.chunk); +#endif } void HugeItemAllocator::sweep(ClassDestroyStatsCallback classCountPtr) diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 5794e6f0c5..17d145f939 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -56,6 +56,7 @@ #include <private/qqmlpropertyindex_p.h> #include <private/qv4value_p.h> #include <private/qv4persistent_p.h> +#include <private/qqmlrefcount_p.h> #include <qjsengine.h> #include <qvector.h> @@ -116,6 +117,7 @@ class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData { public: QQmlData(); + ~QQmlData(); static inline void init() { static bool initialized = false; @@ -219,12 +221,15 @@ public: quint32 jsEngineId; // id of the engine that created the jsWrapper struct DeferredData { + DeferredData(); + ~DeferredData(); unsigned int deferredIdx; QMultiHash<int, const QV4::CompiledData::Binding *> bindings; - QV4::CompiledData::CompilationUnit *compilationUnit;//Not always the same as the other compilation unit + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;//Not always the same as the other compilation unit QQmlContextData *context;//Could be either context or outerContext + Q_DISABLE_COPY(DeferredData); }; - QV4::CompiledData::CompilationUnit *compilationUnit; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; QVector<DeferredData *> deferredData; void deferData(int objectIndex, QV4::CompiledData::CompilationUnit *, QQmlContextData *); @@ -299,6 +304,7 @@ private: const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; return bits[offset] & bitFlagForBit(bit); } + Q_DISABLE_COPY(QQmlData); }; bool QQmlData::wasDeleted(const QObject *object) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 99e55a4300..3de2f736cd 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -759,13 +759,17 @@ QQmlData::QQmlData() hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), bindingBitsArraySize(InlineBindingArraySize), notifyList(0), bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), - lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0), + lineNumber(0), columnNumber(0), jsEngineId(0), propertyCache(0), guards(0), extendedData(0) { memset(bindingBitsValue, 0, sizeof(bindingBitsValue)); init(); } +QQmlData::~QQmlData() +{ +} + void QQmlData::destroyed(QAbstractDeclarativeData *d, QObject *o) { QQmlData *ddata = static_cast<QQmlData *>(d); @@ -937,6 +941,14 @@ void QQmlData::flushPendingBindingImpl(QQmlPropertyIndex index) QQmlPropertyData::DontRemoveBinding); } +QQmlData::DeferredData::DeferredData() +{ +} + +QQmlData::DeferredData::~DeferredData() +{ +} + bool QQmlEnginePrivate::baseModulesUninitialized = true; void QQmlEnginePrivate::init() { @@ -1693,7 +1705,6 @@ void QQmlData::deferData(int objectIndex, QV4::CompiledData::CompilationUnit *co QQmlData::DeferredData *deferData = new QQmlData::DeferredData; deferData->deferredIdx = objectIndex; deferData->compilationUnit = compilationUnit; - deferData->compilationUnit->addref(); deferData->context = context; const QV4::CompiledData::Object *compiledObject = compilationUnit->objectAt(objectIndex); @@ -1715,7 +1726,6 @@ void QQmlData::releaseDeferredData() while (it != deferredData.end()) { DeferredData *deferData = *it; if (deferData->bindings.isEmpty()) { - deferData->compilationUnit->release(); delete deferData; it = deferredData.erase(it); } else { @@ -1793,12 +1803,10 @@ void QQmlData::destroyed(QObject *object) if (bindings && !bindings->ref.deref()) delete bindings; - if (compilationUnit) { - compilationUnit->release(); - compilationUnit = 0; - } + compilationUnit = nullptr; - releaseDeferredData(); + qDeleteAll(deferredData); + deferredData.clear(); QQmlBoundSignal *signalHandler = signalHandlers; while (signalHandler) { diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp index 64f008cd32..1b264b025c 100644 --- a/src/qml/qml/qqmlerror.cpp +++ b/src/qml/qml/qqmlerror.cpp @@ -268,7 +268,7 @@ QtMsgType QQmlError::messageType() const \since 5.9 Sets the \a messageType for this message. The message type determines which - QDebug handlers are responsible for recieving the message. + QDebug handlers are responsible for receiving the message. */ void QQmlError::setMessageType(QtMsgType messageType) { diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index f2757c34be..a9113603b8 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -2080,29 +2080,38 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba // Dynamic plugins are differentiated by their filepath. For static plugins we // don't have that information so we use their address as key instead. const QString uniquePluginID = QString::asprintf("%p", instance); - StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); - QMutexLocker lock(&plugins->mutex); + { + StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); + QMutexLocker lock(&plugins->mutex); - // Plugin types are global across all engines and should only be - // registered once. But each engine still needs to be initialized. - bool typesRegistered = plugins->contains(uniquePluginID); - bool engineInitialized = initializedPlugins.contains(uniquePluginID); + // Plugin types are global across all engines and should only be + // registered once. But each engine still needs to be initialized. + bool typesRegistered = plugins->contains(uniquePluginID); - if (typesRegistered) { - Q_ASSERT_X(plugins->value(uniquePluginID).uri == uri, - "QQmlImportDatabase::importStaticPlugin", - "Internal error: Static plugin imported previously with different uri"); - } else { - RegisteredPlugin plugin; - plugin.uri = uri; - plugin.loader = 0; - plugins->insert(uniquePluginID, plugin); + if (typesRegistered) { + Q_ASSERT_X(plugins->value(uniquePluginID).uri == uri, + "QQmlImportDatabase::importStaticPlugin", + "Internal error: Static plugin imported previously with different uri"); + } else { + RegisteredPlugin plugin; + plugin.uri = uri; + plugin.loader = 0; + plugins->insert(uniquePluginID, plugin); - if (!registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors)) - return false; + if (!registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors)) + return false; + } + + // Release the lock on plugins early as we're done with the global part. Releasing the lock + // also allows other QML loader threads to acquire the lock while this thread is blocking + // in the initializeEngine call to the gui thread (which in turn may be busy waiting for + // other QML loader threads and thus not process the initializeEngine call). } - if (!engineInitialized) { + // The plugin's per-engine initialization does not need lock protection, as this function is + // only called from the engine specific loader thread and importDynamicPlugin as well as + // importStaticPlugin are the only places of access. + if (!initializedPlugins.contains(uniquePluginID)) { initializedPlugins.insert(uniquePluginID); if (QQmlExtensionInterface *eiface = qobject_cast<QQmlExtensionInterface *>(instance)) { @@ -2124,68 +2133,77 @@ bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QStr QFileInfo fileInfo(filePath); const QString absoluteFilePath = fileInfo.absoluteFilePath(); + QObject *instance = nullptr; bool engineInitialized = initializedPlugins.contains(absoluteFilePath); - StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); - QMutexLocker lock(&plugins->mutex); - bool typesRegistered = plugins->contains(absoluteFilePath); - - if (typesRegistered) { - Q_ASSERT_X(plugins->value(absoluteFilePath).uri == uri, - "QQmlImportDatabase::importDynamicPlugin", - "Internal error: Plugin imported previously with different uri"); - } - - if (!engineInitialized || !typesRegistered) { - if (!QQml_isFileCaseCorrect(absoluteFilePath)) { - if (errors) { - QQmlError error; - error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath)); - errors->prepend(error); - } - return false; + { + StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); + QMutexLocker lock(&plugins->mutex); + bool typesRegistered = plugins->contains(absoluteFilePath); + + if (typesRegistered) { + Q_ASSERT_X(plugins->value(absoluteFilePath).uri == uri, + "QQmlImportDatabase::importDynamicPlugin", + "Internal error: Plugin imported previously with different uri"); } - QPluginLoader* loader = 0; - if (!typesRegistered) { - loader = new QPluginLoader(absoluteFilePath); - - if (!loader->load()) { + if (!engineInitialized || !typesRegistered) { + if (!QQml_isFileCaseCorrect(absoluteFilePath)) { if (errors) { QQmlError error; - error.setDescription(loader->errorString()); + error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath)); errors->prepend(error); } - delete loader; return false; } - } else { - loader = plugins->value(absoluteFilePath).loader; - } - QObject *instance = loader->instance(); + QPluginLoader* loader = 0; + if (!typesRegistered) { + loader = new QPluginLoader(absoluteFilePath); - if (!typesRegistered) { - RegisteredPlugin plugin; - plugin.uri = uri; - plugin.loader = loader; - plugins->insert(absoluteFilePath, plugin); + if (!loader->load()) { + if (errors) { + QQmlError error; + error.setDescription(loader->errorString()); + errors->prepend(error); + } + delete loader; + return false; + } + } else { + loader = plugins->value(absoluteFilePath).loader; + } - // Continue with shared code path for dynamic and static plugins: - if (!registerPluginTypes(instance, fileInfo.absolutePath(), uri, typeNamespace, vmaj, errors)) - return false; + instance = loader->instance(); + + if (!typesRegistered) { + RegisteredPlugin plugin; + plugin.uri = uri; + plugin.loader = loader; + plugins->insert(absoluteFilePath, plugin); + + // Continue with shared code path for dynamic and static plugins: + if (!registerPluginTypes(instance, fileInfo.absolutePath(), uri, typeNamespace, vmaj, errors)) + return false; + } } - if (!engineInitialized) { - // things on the engine (eg. adding new global objects) have to be done for every - // engine. - // XXX protect against double initialization - initializedPlugins.insert(absoluteFilePath); - - if (QQmlExtensionInterface *eiface = qobject_cast<QQmlExtensionInterface *>(instance)) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); - ep->typeLoader.initializeEngine(eiface, uri.toUtf8().constData()); - } - } + // Release the lock on plugins early as we're done with the global part. Releasing the lock + // also allows other QML loader threads to acquire the lock while this thread is blocking + // in the initializeEngine call to the gui thread (which in turn may be busy waiting for + // other QML loader threads and thus not process the initializeEngine call). + } + + + if (!engineInitialized) { + // The plugin's per-engine initialization does not need lock protection, as this function is + // only called from the engine specific loader thread and importDynamicPlugin as well as + // importStaticPlugin are the only places of access. + initializedPlugins.insert(absoluteFilePath); + + if (QQmlExtensionInterface *eiface = qobject_cast<QQmlExtensionInterface *>(instance)) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + ep->typeLoader.initializeEngine(eiface, uri.toUtf8().constData()); + } } return true; diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index b324386bf5..76d40bf442 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1566,6 +1566,12 @@ QString registrationTypeString(QQmlType::RegistrationType typeType) bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, const char *uri, const QString &typeName, int majorVersion = -1) { if (!typeName.isEmpty()) { + if (typeName.at(0).isLower()) { + QString failure(QCoreApplication::translate("qmlRegisterType", "Invalid QML %1 name \"%2\"; type names must begin with an uppercase letter")); + data->typeRegistrationFailures.append(failure.arg(registrationTypeString(typeType)).arg(typeName)); + return false; + } + int typeNameLen = typeName.length(); for (int ii = 0; ii < typeNameLen; ++ii) { if (!(typeName.at(ii).isLetterOrNumber() || typeName.at(ii) == '_')) { @@ -1803,6 +1809,9 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data) else return -1; + if (!dtype.isValid()) + return -1; + QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *typeData = metaTypeData(); typeData->undeletableTypes.insert(dtype); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index f9aba69986..caf063c79f 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -203,10 +203,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI if (instance) { QQmlData *ddata = QQmlData::get(instance); Q_ASSERT(ddata); - if (ddata->compilationUnit) - ddata->compilationUnit->release(); ddata->compilationUnit = compilationUnit; - ddata->compilationUnit->addref(); } if (topLevelCreator) diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index f4c03af5b7..bef58b8c9a 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -137,7 +137,7 @@ private: QQmlEngine *engine; QV4::ExecutionEngine *v4; - QV4::CompiledData::CompilationUnit *compilationUnit; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; const QV4::CompiledData::Unit *qmlUnit; QQmlGuardedContextData parentContext; QQmlContextData *context; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 404bc0612e..8e067932bb 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -134,11 +134,11 @@ ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, } static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, QObject *qobjectSingleton, - const QQmlType &type) + const QQmlType &type, bool *ok) { - bool ok; - int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); - if (ok) + Q_ASSERT(ok != nullptr); + int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, ok); + if (*ok) return value; // ### Optimize @@ -146,10 +146,11 @@ static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, QObject *qob const QMetaObject *metaObject = qobjectSingleton->metaObject(); for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { QMetaEnum e = metaObject->enumerator(ii); - value = e.keyToValue(enumName.constData(), &ok); - if (ok) + value = e.keyToValue(enumName.constData(), ok); + if (*ok) return value; } + *ok = false; return -1; } @@ -192,8 +193,9 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp // check for enum value const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; if (includeEnums && name->startsWithUpper()) { - const int value = enumForSingleton(v4, name, qobjectSingleton, type); - if (value != -1) + bool ok = false; + const int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok); + if (ok) return QV4::Primitive::fromInt32(value).asReturnedValue(); } @@ -205,8 +207,8 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp // Warn when attempting to access a lowercased enum value, singleton case if (!ok && includeEnums && !name->startsWithUpper()) { - const int value = enumForSingleton(v4, name, qobjectSingleton, type); - if (value != -1) + enumForSingleton(v4, name, qobjectSingleton, type, &ok); + if (ok) return throwLowercaseEnumError(v4, name, type); } diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index d37065ce43..c643beeadc 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -98,6 +98,8 @@ void Heap::QQmlValueTypeWrapper::destroy() valueType->metaType.destruct(gadgetPtr); ::operator delete(gadgetPtr); } + if (_propertyCache) + _propertyCache->release(); Object::destroy(); } diff --git a/src/qmldebug/qqmlprofilereventreceiver_p.h b/src/qmldebug/qqmlprofilereventreceiver_p.h index ee8e592858..defe64a42e 100644 --- a/src/qmldebug/qqmlprofilereventreceiver_p.h +++ b/src/qmldebug/qqmlprofilereventreceiver_p.h @@ -45,6 +45,17 @@ #include <QtCore/qobject.h> +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + QT_BEGIN_NAMESPACE class QQmlProfilerEventReceiver : public QObject diff --git a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc index 27c8e51381..2705ca5e34 100644 --- a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc +++ b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc @@ -41,6 +41,10 @@ QObjectList or a \l QAbstractItemModel. The first three are useful for exposing simpler datasets, while QAbstractItemModel provides a more flexible solution for more complex models. +For a video tutorial that takes you through the whole process of exposing a C++ +model to QML, see the +\l {https://youtu.be/9BcAYDlpuT8}{Using C++ Models in QML Tutorial}. + \section2 QStringList-based Model A model may be a simple \l QStringList, which provides the contents of the list diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 8313b415bf..a1b4f961ed 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -61,14 +61,20 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1); TapHandler is a handler for taps on a touchscreen or clicks on a mouse. - Detection of a valid tap gesture depends on \l gesturePolicy. + Detection of a valid tap gesture depends on \l gesturePolicy. The default + value is DragThreshold, which requires the press and release to be close + together in both space and time. In this case, DragHandler is able to + function using only a passive grab, and therefore does not interfere with + event delivery to any other Items or Pointer Handlers. So the default + gesturePolicy is useful when you want to modify behavior of an existing + control or Item by adding a TapHandler with bindings and/or JavaScript + callbacks. + Note that buttons (such as QPushButton) are often implemented not to care whether the press and release occur close together: if you press the button and then change your mind, you need to drag all the way off the edge of the - button in order to cancel the click. Therefore the default - \l gesturePolicy is \c TapHandler.ReleaseWithinBounds. If you want to require - that the press and release are close together in both space and time, - set it to \c TapHandler.DragThreshold. + button in order to cancel the click. For this use case, set the + \l gesturePolicy to \c TapHandler.ReleaseWithinBounds. For multi-tap gestures (double-tap, triple-tap etc.), the distance moved must not exceed QPlatformTheme::MouseDoubleClickDistance with mouse and @@ -81,7 +87,7 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1); QQuickTapHandler::QQuickTapHandler(QObject *parent) : QQuickSinglePointHandler(parent) , m_pressed(false) - , m_gesturePolicy(ReleaseWithinBounds) + , m_gesturePolicy(DragThreshold) , m_tapCount(0) , m_longPressThreshold(-1) , m_lastTapTimestamp(0.0) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index f615e85e1b..18e0d30ed5 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2809,7 +2809,8 @@ void QQuickItem::stackBefore(const QQuickItem *sibling) { Q_D(QQuickItem); if (!sibling || sibling == this || !d->parentItem || d->parentItem != QQuickItemPrivate::get(sibling)->parentItem) { - qWarning("QQuickItem::stackBefore: Cannot stack before %p, which must be a sibling", sibling); + qWarning().nospace() << "QQuickItem::stackBefore: Cannot stack " + << this << " before " << sibling << ", which must be a sibling"; return; } @@ -2853,7 +2854,8 @@ void QQuickItem::stackAfter(const QQuickItem *sibling) { Q_D(QQuickItem); if (!sibling || sibling == this || !d->parentItem || d->parentItem != QQuickItemPrivate::get(sibling)->parentItem) { - qWarning("QQuickItem::stackAfter: Cannot stack after %p, which must be a sibling", sibling); + qWarning().nospace() << "QQuickItem::stackAfter: Cannot stack " + << this << " after " << sibling << ", which must be a sibling"; return; } @@ -2943,6 +2945,7 @@ void QQuickItemPrivate::addChild(QQuickItem *child) if (childPrivate->subtreeHoverEnabled && !subtreeHoverEnabled) setHasHoverInChild(true); + childPrivate->recursiveRefFromEffectItem(extra.value().recursiveEffectRefCount); markSortedChildrenDirty(child); dirty(QQuickItemPrivate::ChildrenChanged); @@ -2971,6 +2974,7 @@ void QQuickItemPrivate::removeChild(QQuickItem *child) if (childPrivate->subtreeHoverEnabled && subtreeHoverEnabled) setHasHoverInChild(false); + childPrivate->recursiveRefFromEffectItem(-extra.value().recursiveEffectRefCount); markSortedChildrenDirty(child); dirty(QQuickItemPrivate::ChildrenChanged); @@ -6071,28 +6075,48 @@ void QQuickItemPrivate::removeFromDirtyList() void QQuickItemPrivate::refFromEffectItem(bool hide) { ++extra.value().effectRefCount; - if (1 == extra->effectRefCount) { + if (extra->effectRefCount == 1) { dirty(EffectReference); - if (parentItem) QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged); + if (parentItem) + QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged); } if (hide) { if (++extra->hideRefCount == 1) dirty(HideReference); } + recursiveRefFromEffectItem(1); +} + +void QQuickItemPrivate::recursiveRefFromEffectItem(int refs) +{ + Q_Q(QQuickItem); + if (!refs) + return; + extra.value().recursiveEffectRefCount += refs; + for (int ii = 0; ii < childItems.count(); ++ii) { + QQuickItem *child = childItems.at(ii); + QQuickItemPrivate::get(child)->recursiveRefFromEffectItem(refs); + } + // Polish may rely on the effect ref count so trigger one, if item is not visible + // (if visible, it will be triggered automatically). + if (!effectiveVisible && refs > 0 && extra.value().recursiveEffectRefCount == 1) // it wasn't referenced, now it's referenced + q->polish(); } void QQuickItemPrivate::derefFromEffectItem(bool unhide) { Q_ASSERT(extra->effectRefCount); --extra->effectRefCount; - if (0 == extra->effectRefCount) { + if (extra->effectRefCount == 0) { dirty(EffectReference); - if (parentItem) QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged); + if (parentItem) + QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged); } if (unhide) { if (--extra->hideRefCount == 0) dirty(HideReference); } + recursiveRefFromEffectItem(-1); } void QQuickItemPrivate::setCulled(bool cull) @@ -8598,6 +8622,7 @@ QQuickItemPrivate::ExtraData::ExtraData() layer(0), #endif effectRefCount(0), hideRefCount(0), + recursiveEffectRefCount(0), opacityNode(0), clipNode(0), rootNode(0), acceptedMouseButtons(0), origin(QQuickItem::Center), transparentForPositioner(false) diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 02e7424d1e..ee854bb2ac 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -360,8 +360,11 @@ public: #endif QPointF userTransformOriginPoint; + // these do not include child items int effectRefCount; int hideRefCount; + // updated recursively for child items as well + int recursiveEffectRefCount; QSGOpacityNode *opacityNode; QQuickDefaultClipNode *clipNode; @@ -614,6 +617,7 @@ public: // A reference from an effect item means that this item is used by the effect, so // it should insert a root node. void refFromEffectItem(bool hide); + void recursiveRefFromEffectItem(int refs); void derefFromEffectItem(bool unhide); void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &); diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 856070634c..17a1d124ab 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -1894,6 +1894,9 @@ void QQuickItemViewPrivate::layout() inLayout = true; + // viewBounds contains bounds before any add/remove/move operation to the view + QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height()); + if (!isValid() && !visibleItems.count()) { clear(); setPosition(contentStartOffset()); @@ -1960,7 +1963,6 @@ void QQuickItemViewPrivate::layout() prepareVisibleItemTransitions(); - QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height()); for (QList<FxViewItem*>::Iterator it = releasePendingTransition.begin(); it != releasePendingTransition.end(); ) { FxViewItem *item = *it; diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 1f2828585c..33e22c1e95 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -686,6 +686,13 @@ void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status) if (status == QQmlIncubator::Ready) { object = incubator->object(); item = qmlobject_cast<QQuickItem*>(object); + if (!item) { + QQuickWindow *window = qmlobject_cast<QQuickWindow*>(object); + if (window) { + qCDebug(lcTransient) << window << "is transient for" << q->window(); + window->setTransientParent(q->window()); + } + } emit q->itemChanged(); initResize(); incubator->clear(); @@ -830,6 +837,18 @@ void QQuickLoader::componentComplete() } } +void QQuickLoader::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) +{ + if (change == ItemSceneChange) { + QQuickWindow *loadedWindow = qmlobject_cast<QQuickWindow *>(item()); + if (loadedWindow) { + qCDebug(lcTransient) << loadedWindow << "is transient for" << value.window; + loadedWindow->setTransientParent(value.window); + } + } + QQuickItem::itemChange(change, value); +} + /*! \qmlsignal QtQuick::Loader::loaded() diff --git a/src/quick/items/qquickloader_p.h b/src/quick/items/qquickloader_p.h index 27e5d1ec8b..b5137c0783 100644 --- a/src/quick/items/qquickloader_p.h +++ b/src/quick/items/qquickloader_p.h @@ -107,6 +107,7 @@ Q_SIGNALS: protected: void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; void componentComplete() override; + void itemChange(ItemChange change, const ItemChangeData &value) override; private: void setSource(const QUrl &sourceUrl, bool needsClear); diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index a09fc35f1c..557ff393b4 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -1108,7 +1108,8 @@ void QQuickTextInputPrivate::checkIsValid() Q_Q(QQuickTextInput); ValidatorState state = hasAcceptableInput(m_text); - m_validInput = state != InvalidInput; + if (!m_maskData) + m_validInput = state != InvalidInput; if (state != AcceptableInput) { if (m_acceptableInput) { m_acceptableInput = false; @@ -3561,11 +3562,15 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo #if QT_CONFIG(validator) if (m_validator) { QString textCopy = m_text; + if (m_maskData) + textCopy = maskString(0, m_text, true); int cursorCopy = m_cursor; QValidator::State state = m_validator->validate(textCopy, cursorCopy); + if (m_maskData) + textCopy = m_text; m_validInput = state != QValidator::Invalid; m_acceptableInput = state == QValidator::Acceptable; - if (m_validInput) { + if (m_validInput && !m_maskData) { if (m_text != textCopy) { internalSetText(textCopy, cursorCopy); return true; @@ -3574,31 +3579,8 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo } } #endif - - if (m_maskData) { - m_validInput = true; - if (m_text.length() != m_maxLength) { - m_validInput = false; - m_acceptableInput = false; - } else { - for (int i = 0; i < m_maxLength; ++i) { - if (m_maskData[i].separator) { - if (m_text.at(i) != m_maskData[i].maskChar) { - m_validInput = false; - m_acceptableInput = false; - break; - } - } else { - if (!isValidInput(m_text.at(i), m_maskData[i].maskChar)) { - m_acceptableInput = false; - if (m_text.at(i) != m_blank) - m_validInput = false; - break; - } - } - } - } - } + if (m_maskData) + checkIsValid(); if (validateFromState >= 0 && wasValidInput && !m_validInput) { if (m_transactions.count()) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 2e36f07fdb..6fda6ab6c6 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -2430,8 +2430,6 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve done = true; event->localize(receiver); handler->handlePointerEvent(event); - if (event->allPointsAccepted()) - done = true; } if (done) break; @@ -2532,8 +2530,6 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo } if (handlersOnly) return; - if (pointerEvent->allPointsAccepted() && !pointerEvent->isReleaseEvent()) - return; // If all points are released and the item is not the grabber, it doesn't get the event. // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario. @@ -2545,8 +2541,6 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo auto event = pointerEvent->asPointerMouseEvent(); if (event && item->acceptedMouseButtons() & event->button()) { auto point = event->point(0); - if (point->isAccepted()) - return; // The only reason to already have a mouse grabber here is // synthetic events - flickable sends one when setPressDelay is used. auto oldMouseGrabber = q->mouseGrabberItem(); diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index f048f12bf9..ab3f49d5b6 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -123,7 +123,13 @@ void QQuickWindowQmlImpl::componentComplete() { Q_D(QQuickWindowQmlImpl); d->complete = true; - if (transientParent() && !transientParent()->isVisible()) { + QQuickItem *itemParent = qmlobject_cast<QQuickItem *>(QObject::parent()); + if (itemParent && !itemParent->window()) { + qCDebug(lcTransient) << "window" << title() << "has invisible Item parent" << itemParent << "transientParent" + << transientParent() << "declared visibility" << d->visibility << "; delaying show"; + connect(itemParent, &QQuickItem::windowChanged, this, + &QQuickWindowQmlImpl::setWindowVisibility, Qt::QueuedConnection); + } else if (transientParent() && !transientParent()->isVisible()) { connect(transientParent(), &QQuickWindow::visibleChanged, this, &QQuickWindowQmlImpl::setWindowVisibility, Qt::QueuedConnection); } else { @@ -137,9 +143,10 @@ void QQuickWindowQmlImpl::setWindowVisibility() if (transientParent() && !transientParent()->isVisible()) return; - if (sender()) { - disconnect(transientParent(), &QWindow::visibleChanged, this, - &QQuickWindowQmlImpl::setWindowVisibility); + if (QQuickItem *senderItem = qmlobject_cast<QQuickItem *>(sender())) { + disconnect(senderItem, &QQuickItem::windowChanged, this, &QQuickWindowQmlImpl::setWindowVisibility); + } else if (sender()) { + disconnect(transientParent(), &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::setWindowVisibility); } // We have deferred window creation until we have the full picture of what diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index 3f0d1383b9..423f5f7321 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -97,7 +97,6 @@ void QSGSoftwareRenderLoop::windowDestroyed(QQuickWindow *window) if (m_windows.size() == 0) { rc->invalidate(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); } } diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 87ecf3e5ad..27b374e47f 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -144,7 +144,7 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material) Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame); QSGMaterialShader *s = material->createShader(); - QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QOpenGLContext *ctx = context->openglContext(); QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile(); QOpenGLShaderProgram *p = s->program(); @@ -2035,7 +2035,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip) int vboSize = 0; bool useVBO = false; - QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QOpenGLContext *ctx = m_context->openglContext(); QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile(); if (!ctx->isOpenGLES() && profile == QSurfaceFormat::CoreProfile) { @@ -2497,18 +2497,21 @@ void Renderer::updateLineWidth(QSGGeometry *g) if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES) glLineWidth(g->lineWidth()); #if !defined(QT_OPENGL_ES_2) - else if (!QOpenGLContext::currentContext()->isOpenGLES() && g->drawingMode() == GL_POINTS) { - QOpenGLFunctions_1_0 *gl1funcs = 0; - QOpenGLFunctions_3_2_Core *gl3funcs = 0; - if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) - gl3funcs = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>(); - else - gl1funcs = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_1_0>(); - Q_ASSERT(gl1funcs || gl3funcs); - if (gl1funcs) - gl1funcs->glPointSize(g->lineWidth()); - else - gl3funcs->glPointSize(g->lineWidth()); + else { + QOpenGLContext *ctx = m_context->openglContext(); + if (!ctx->isOpenGLES() && g->drawingMode() == GL_POINTS) { + QOpenGLFunctions_1_0 *gl1funcs = 0; + QOpenGLFunctions_3_2_Core *gl3funcs = 0; + if (ctx->format().profile() == QSurfaceFormat::CoreProfile) + gl3funcs = ctx->versionFunctions<QOpenGLFunctions_3_2_Core>(); + else + gl1funcs = ctx->versionFunctions<QOpenGLFunctions_1_0>(); + Q_ASSERT(gl1funcs || gl3funcs); + if (gl1funcs) + gl1funcs->glPointSize(g->lineWidth()); + else + gl3funcs->glPointSize(g->lineWidth()); + } } #endif } @@ -2614,6 +2617,8 @@ void Renderer::deleteRemovedElements() void Renderer::render() { + Q_ASSERT(m_context->openglContext() == QOpenGLContext::currentContext()); + if (Q_UNLIKELY(debug_dump())) { qDebug("\n"); QSGNodeDumper::dump(rootNode()); @@ -3156,7 +3161,7 @@ void Renderer::visualizeOverdraw() visualizeOverdraw_helper(m_nodes.value(rootNode())); // Animate the view... - QSurface *surface = QOpenGLContext::currentContext()->surface(); + QSurface *surface = m_context->openglContext()->surface(); if (surface->surfaceClass() == QSurface::Window) if (QQuickWindow *window = qobject_cast<QQuickWindow *>(static_cast<QWindow *>(surface))) window->update(); diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp index 69a8c21ed2..52366bfc35 100644 --- a/src/quick/scenegraph/coreapi/qsggeometry.cpp +++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp @@ -768,6 +768,19 @@ void QSGGeometry::updateColoredRectGeometry(QSGGeometry *g, const QRectF &rect) v[3].y = rect.bottom(); } +/*! + \enum QSGGeometry::AttributeType + + This enum identifies several attribute types. + + \value UnknownAttribute Don't care + \value PositionAttribute Position + \value ColorAttribute Color + \value TexCoordAttribute Texture coordinate + \value TexCoord1Attribute Texture coordinate 1 + \value TexCoord2Attribute Texture coordinate 2 + + */ /*! \enum QSGGeometry::DataPattern diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp index 07dc87a643..5c28f4c82f 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -443,7 +443,12 @@ void QSGMaterialShader::compile() otherwise returns \c false. */ +/*! + \fn bool QSGMaterialShader::RenderState::isCachedMaterialDataDirty() const + Returns \c true if the dirtyStates() contains the dirty cached material state, + otherwise returns \c false. + */ /*! \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index 22d57001fc..550a2182f8 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -114,6 +114,10 @@ static void qt_print_node_count() \value DirtyOpacity The opacity of a QSGOpacityNode has changed. \value DirtySubtreeBlocked The subtree has been blocked. + \omitvalue DirtyForceUpdate + \omitvalue DirtyUsePreprocess + \omitvalue DirtyPropagationMask + \sa QSGNode::markDirty() */ @@ -135,6 +139,8 @@ static void qt_print_node_count() ownership over the opaque material and will delete it when the node is destroyed or a material is assigned. \value InternalReserved Reserved for internal use. + + \omitvalue IsVisitableNode */ /*! @@ -149,6 +155,8 @@ static void qt_print_node_count() \value OpacityNodeType The type of QSGOpacityNode \value RenderNodeType The type of QSGRenderNode + \omitvalue RootNodeType + \sa type() */ diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h index f2708b2b96..cd9d689dc1 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.h +++ b/src/quick/scenegraph/coreapi/qsgnode.h @@ -77,9 +77,7 @@ public: TransformNodeType, ClipNodeType, OpacityNodeType, -#ifndef qdoc RootNodeType, -#endif RenderNodeType }; @@ -96,9 +94,8 @@ public: OwnsOpaqueMaterial = 0x00040000, // Uppermost 8 bits are reserved for internal use. -#ifndef qdoc IsVisitableNode = 0x01000000 -#else +#ifdef Q_CLANG_QDOC InternalReserved = 0x01000000 #endif }; @@ -113,7 +110,6 @@ public: DirtyMaterial = 0x2000, DirtyOpacity = 0x4000, -#ifndef qdoc DirtyForceUpdate = 0x8000, DirtyUsePreprocess = UsePreprocess, @@ -122,7 +118,6 @@ public: | DirtyNodeAdded | DirtyOpacity | DirtyForceUpdate -#endif }; Q_DECLARE_FLAGS(DirtyState, DirtyStateBit) diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index a8954848d6..5e259f2083 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -213,6 +213,22 @@ void QSGRenderNode::releaseResources() } /*! + \enum QSGRenderNode::StateFlag + + This enum is a bit mask identifying several states. + + \value DepthState Depth + \value StencilState Stencil + \value ScissorState Scissor + \value ColorState Color + \value BlendState Blend + \value CullState Cull + \value ViewportState View poirt + \value RenderTargetState Render target + + */ + +/*! \enum QSGRenderNode::RenderingFlag Possible values for the bitmask returned from flags(). @@ -354,7 +370,7 @@ QSGRenderNode::RenderState::~RenderState() */ /*! - \fn const QRegion *QSGRenderNode::clipRegion() const + \fn const QRegion *QSGRenderNode::RenderState::clipRegion() const \return the current clip region or null for backends where clipping is implemented via stencil or scissoring. diff --git a/src/quick/scenegraph/util/qsgimagenode.cpp b/src/quick/scenegraph/util/qsgimagenode.cpp index c03c91d1cb..b154023247 100644 --- a/src/quick/scenegraph/util/qsgimagenode.cpp +++ b/src/quick/scenegraph/util/qsgimagenode.cpp @@ -168,7 +168,7 @@ QT_BEGIN_NAMESPACE */ /*! - \fn QSGImageNode::TextureCoordinatesTransformMode textureCoordinatesTransform() const + \fn QSGImageNode::TextureCoordinatesTransformMode QSGImageNode::textureCoordinatesTransform() const Returns the mode used to generate texture coordinates for this node. */ @@ -187,6 +187,15 @@ QT_BEGIN_NAMESPACE \return \c true if the node takes ownership of the texture; otherwise \c false. */ +/*! + Updates the geometry \a g with the \a texture, the coordinates + in \a rect, and the texture coordinates from \a sourceRect. + + \a g is assumed to be a triangle strip of four vertices of type + QSGGeometry::TexturedPoint2D. + + \a texCoordMode is used for normalizing the \a sourceRect. + */ void QSGImageNode::rebuildGeometry(QSGGeometry *g, QSGTexture *texture, const QRectF &rect, diff --git a/src/quick/scenegraph/util/qsgsimplematerial.h b/src/quick/scenegraph/util/qsgsimplematerial.h index b5b8815b4a..8a81917bd2 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.h +++ b/src/quick/scenegraph/util/qsgsimplematerial.h @@ -138,7 +138,7 @@ template <typename State> class QSGSimpleMaterial : public QSGMaterial { public: -#ifndef qdoc +#ifndef Q_CLANG_QDOC QSGSimpleMaterial(const State &aState, PtrShaderCreateFunc func) : m_state(aState) , m_func(func) diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp index fbc8f27a63..0f9ee9cfde 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.cpp +++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp @@ -304,7 +304,17 @@ void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture) The default vertical wrap mode is \c QSGTexture::ClampToEdge. */ +/*! + \fn void QSGOpaqueTextureMaterial::setAnisotropyLevel(QSGTexture::AnisotropyLevel level) + + Sets this material's anistropy level to \a level. +*/ + +/*! + \fn QSGTexture::AnisotropyLevel QSGOpaqueTextureMaterial::anisotropyLevel() const + Returns this material's anistropy level. +*/ /*! \internal diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp index 6ee5b95dc7..a1513336a5 100644 --- a/src/quick/util/qquickimageprovider.cpp +++ b/src/quick/util/qquickimageprovider.cpp @@ -683,13 +683,15 @@ QSize QQuickImageProviderWithOptions::loadSize(const QSize &originalSize, const return res; const bool preserveAspectCropOrFit = options.preserveAspectRatioCrop() || options.preserveAspectRatioFit(); - const bool force_scale = (format == "svg" || format == "svgz"); + + if (!preserveAspectCropOrFit && (format == "svg" || format == "svgz")) + return requestedSize; qreal ratio = 0.0; - if (requestedSize.width() && (preserveAspectCropOrFit || force_scale || requestedSize.width() < originalSize.width())) { + if (requestedSize.width() && (preserveAspectCropOrFit || requestedSize.width() < originalSize.width())) { ratio = qreal(requestedSize.width()) / originalSize.width(); } - if (requestedSize.height() && (preserveAspectCropOrFit || force_scale || requestedSize.height() < originalSize.height())) { + if (requestedSize.height() && (preserveAspectCropOrFit || requestedSize.height() < originalSize.height())) { qreal hr = qreal(requestedSize.height()) / originalSize.height(); if (ratio == 0.0) ratio = hr; |