diff options
25 files changed, 289 insertions, 100 deletions
diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp index 8528a6f750..85056dffa9 100644 --- a/src/particles/qquickcustomparticle.cpp +++ b/src/particles/qquickcustomparticle.cpp @@ -39,6 +39,7 @@ #include "qquickcustomparticle_p.h" #include <QtCore/qrandom.h> +#include <QtGui/qopenglcontext.h> #include <QtQuick/private/qquickshadereffectmesh_p.h> #include <QtQuick/private/qsgshadersourcebuilder_p.h> #include <QtQml/qqmlinfo.h> diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 9379160a65..a69e862fb7 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -756,7 +756,7 @@ QString Binding::valueAsString(const CompilationUnit *unit) const #if !QT_CONFIG(translation) case Type_TranslationById: case Type_Translation: - return unit->stringAt(unit->unitData->translations()[value.translationDataIndex].stringIndex); + return unit->stringAt(unit->unitData()->translations()[value.translationDataIndex].stringIndex); #else case Type_TranslationById: { const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex]; diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 225a4443d9..f6fb93eab3 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -1343,11 +1343,11 @@ bool QJSValue::hasOwnProperty(const QString &name) const /*! * If this QJSValue is a QObject, returns the QObject pointer - * that the QJSValue represents; otherwise, returns 0. + * that the QJSValue represents; otherwise, returns \nullptr. * * If the QObject that this QJSValue wraps has been deleted, - * this function returns 0 (i.e. it is possible for toQObject() - * to return 0 even when isQObject() returns true). + * this function returns \nullptr (i.e. it is possible for toQObject() + * to return \nullptr even when isQObject() returns true). * * \sa isQObject() */ diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index ff7bcb63fa..a482cc6a67 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1011,6 +1011,11 @@ StackTrace ExecutionEngine::stackTrace(int frameLimit) const frame.line = qAbs(f->lineNumber()); frame.column = -1; stack.append(frame); + if (f->isTailCalling) { + QV4::StackFrame frame; + frame.function = QStringLiteral("[elided tail calls]"); + stack.append(frame); + } --frameLimit; f = f->parent; } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index dfe9d35194..41a21ba379 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -518,6 +518,7 @@ ReturnedValue ArrowFunction::virtualCall(const FunctionObject *fo, const Value * do { frame.pendingTailCall = false; result = Moth::VME::exec(&frame, engine); + frame.isTailCalling = true; } while (frame.pendingTailCall); frame.pop(); diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h index a97ae0e7c9..44cfef9173 100644 --- a/src/qml/jsruntime/qv4stackframe_p.h +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -125,6 +125,7 @@ struct Q_QML_EXPORT CppStackFrame { bool yieldIsIterator; bool callerCanHandleTailCall; bool pendingTailCall; + bool isTailCalling; void init(EngineBase *engine, Function *v4Function, const Value *argv, int argc, bool callerCanHandleTailCall = false) { this->engine = engine; @@ -140,6 +141,7 @@ struct Q_QML_EXPORT CppStackFrame { yieldIsIterator = false; this->callerCanHandleTailCall = callerCanHandleTailCall; pendingTailCall = false; + isTailCalling = false; } void push() { diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 024ec29a56..a949df4968 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -517,11 +517,14 @@ QVariant QQmlBinding::evaluate() QString QQmlBinding::expressionIdentifier() const { - auto f = function(); - QString url = f->sourceFile(); - quint16 lineNumber = f->compiledFunction->location.line; - quint16 columnNumber = f->compiledFunction->location.column; - return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber)); + if (auto f = function()) { + QString url = f->sourceFile(); + quint16 lineNumber = f->compiledFunction->location.line; + quint16 columnNumber = f->compiledFunction->location.column; + return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber)); + } + + return QStringLiteral("[native code]"); } void QQmlBinding::expressionChanged() diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 5ed3cc5d6a..57ea685a5d 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -756,12 +756,12 @@ QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent) } /*! - Create an object instance from this component. Returns 0 if creation + Create an object instance from this component. Returns \nullptr if creation failed. \a context specifies the context within which to create the object instance. - If \a context is 0 (the default), it will create the instance in the - engine' s \l {QQmlEngine::rootContext()}{root context}. + If \a context is \nullptr (the default), it will create the instance in the + \l {QQmlEngine::rootContext()}{root context} of the engine. The ownership of the returned object instance is transferred to the caller. @@ -791,7 +791,7 @@ QObject *QQmlComponent::create(QQmlContext *context) In general, programmers should use QQmlComponent::create() to create object instances. - Create an object instance from this component. Returns 0 if creation + Create an object instance from this component. Returns \nullptr if creation failed. \a publicContext specifies the context within which to create the object instance. diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 6db43a50eb..5841a480fc 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1304,9 +1304,7 @@ void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBa } /*! - Returns the image provider set for \a providerId. - - Returns the provider if it was found; otherwise returns 0. + Returns the image provider set for \a providerId if found; otherwise returns \nullptr. \sa QQuickImageProvider */ diff --git a/src/qml/qml/qqmllist.cpp b/src/qml/qml/qqmllist.cpp index 656a8a470b..5425bf498c 100644 --- a/src/qml/qml/qqmllist.cpp +++ b/src/qml/qml/qqmllist.cpp @@ -184,7 +184,7 @@ bool QQmlListReference::isValid() const } /*! -Returns the list property's object. Returns 0 if the reference is invalid. +Returns the list property's object. Returns \nullptr if the reference is invalid. */ QObject *QQmlListReference::object() const { @@ -193,8 +193,8 @@ QObject *QQmlListReference::object() const } /*! -Returns the QMetaObject for the elements stored in the list property. Returns 0 if the reference -is invalid. +Returns the QMetaObject for the elements stored in the list property, +or \nullptr if the reference is invalid. The QMetaObject can be used ahead of time to determine whether a given instance can be added to a list. diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index c05ae27b93..032ee7d6fc 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -497,9 +497,9 @@ QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQm d->extraData.cd->attachedPropertiesFunc = type.attachedPropertiesFunction; d->extraData.cd->attachedPropertiesType = type.attachedPropertiesMetaObject; if (d->extraData.cd->attachedPropertiesType) { - QHash<const QMetaObject *, int>::Iterator iter = d->attachedPropertyIds.find(d->baseMetaObject); - if (iter == d->attachedPropertyIds.end()) - iter = d->attachedPropertyIds.insert(d->baseMetaObject, d->index); + auto iter = QQmlTypePrivate::attachedPropertyIds.find(d->baseMetaObject); + if (iter == QQmlTypePrivate::attachedPropertyIds.end()) + iter = QQmlTypePrivate::attachedPropertyIds.insert(d->baseMetaObject, d->index); d->extraData.cd->attachedPropertiesId = *iter; } else { d->extraData.cd->attachedPropertiesId = -1; @@ -569,8 +569,16 @@ QQmlType::QQmlType(QQmlTypePrivate *priv) QQmlType::~QQmlType() { - if (d && !d->refCount.deref()) + if (d && !d->refCount.deref()) { + // If attached properties were successfully registered, deregister them. + // (They may not have been registered if some other type used the same baseMetaObject) + if (d->regType == CppType && d->extraData.cd->attachedPropertiesType) { + auto it = QQmlTypePrivate::attachedPropertyIds.find(d->baseMetaObject); + if (it != QQmlTypePrivate::attachedPropertyIds.end() && *it == d->index) + QQmlTypePrivate::attachedPropertyIds.erase(it); + } delete d; + } } QHashedString QQmlType::module() const diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 0cd4c446d9..ec6bf5a1b8 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -4800,7 +4800,7 @@ QQuickItem *QQuickItem::nextItemInFocusChain(bool forward) Returns the first visible child item found at point (\a x, \a y) within the coordinate system of this item. - Returns 0 if there is no such item. + Returns \nullptr if there is no such item. */ QQuickItem *QQuickItem::childAt(qreal x, qreal y) const { @@ -7224,7 +7224,7 @@ bool QQuickItem::isFocusScope() const If this item is a focus scope, this returns the item in its focus chain that currently has focus. - Returns 0 if this item is not a focus scope. + Returns \nullptr if this item is not a focus scope. */ QQuickItem *QQuickItem::scopedFocusItem() const { @@ -8160,7 +8160,7 @@ bool QQuickItem::isTextureProvider() const \fn QSGTextureProvider *QQuickItem::textureProvider() const Returns the texture provider for an item. The default implementation - returns 0. + returns \nullptr. This function may only be called on the rendering thread. */ diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index c99e149aa5..252e5a9c55 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -56,7 +56,7 @@ static QElapsedTimer qsg_render_timer; QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture; -QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) +QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(const QRawFont &font) : m_pendingGlyphs(64) { Q_ASSERT(font.isValid()); @@ -71,11 +71,6 @@ QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QOpenGLContext *c, const // this allows us to call pathForGlyph once and reuse the result. m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution) * QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution)); Q_ASSERT(m_referenceFont.isValid()); -#if QT_CONFIG(opengl) - m_coreProfile = (c->format().profile() == QSurfaceFormat::CoreProfile); -#else - Q_UNUSED(c) -#endif } QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache() diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index ba5c4353b2..58ecae94e7 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -62,7 +62,6 @@ #include <QtCore/qurl.h> #include <private/qfontengine_p.h> #include <QtGui/private/qdatabuffer_p.h> -#include <private/qopenglcontext_p.h> #include <private/qdistancefield_p.h> #include <private/qintrusivelist_p.h> @@ -75,7 +74,6 @@ class QSGNode; class QImage; class TextureReference; class QSGDistanceFieldGlyphNode; -class QOpenGLContext; class QSGInternalImageNode; class QSGPainterNode; class QSGInternalRectangleNode; @@ -413,7 +411,7 @@ typedef QIntrusiveList<QSGDistanceFieldGlyphConsumer, &QSGDistanceFieldGlyphCons class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCache { public: - QSGDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font); + QSGDistanceFieldGlyphCache(const QRawFont &font); virtual ~QSGDistanceFieldGlyphCache(); struct Metrics { @@ -514,8 +512,6 @@ protected: void saveTexture(GLuint textureId, int width, int height) const; #endif - inline bool isCoreProfile() const { return m_coreProfile; } - bool m_doubleGlyphResolution; protected: @@ -523,9 +519,6 @@ protected: private: int m_glyphCount; - - bool m_coreProfile; - QList<Texture> m_textures; QHash<glyph_t, GlyphData> m_glyphsData; QDataBuffer<glyph_t> m_pendingGlyphs; diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index ccc57b0b86..8121b4559e 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -67,7 +67,7 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) - : QSGDistanceFieldGlyphCache(c, font) + : QSGDistanceFieldGlyphCache(font) , m_maxTextureSize(0) , m_maxTextureCount(3) , m_areaAllocator(nullptr) @@ -89,6 +89,8 @@ QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLCont qWarning("Buffer creation failed"); } + m_coreProfile = (c->format().profile() == QSurfaceFormat::CoreProfile); + // Load a pregenerated cache if the font contains one loadPregeneratedCache(font); } diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h index a0e4387af9..c64adddd91 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h @@ -88,6 +88,7 @@ public: private: bool loadPregeneratedCache(const QRawFont &font); + inline bool isCoreProfile() const { return m_coreProfile; } struct TextureInfo { GLuint texture; @@ -137,6 +138,7 @@ private: mutable int m_maxTextureSize; int m_maxTextureCount; + bool m_coreProfile; QList<TextureInfo> m_textures; QHash<glyph_t, TextureInfo *> m_glyphsTexture; diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 720f08f69b..042eee19f5 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -389,7 +389,7 @@ QSGTexture::~QSGTexture() it to a shader that operates on the texture coordinates 0-1 instead of the texture subrect inside the atlas. - If the texture is not part of a texture atlas, this function returns 0. + If the texture is not part of a texture atlas, this function returns \nullptr. Implementations of this function are recommended to return the same instance for multiple calls to limit memory usage. diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp index ced981de3e..ebcca77f17 100644 --- a/src/quick/util/qquickimageprovider.cpp +++ b/src/quick/util/qquickimageprovider.cpp @@ -436,7 +436,7 @@ QPixmap QQuickImageProvider::requestPixmap(const QString &id, QSize *size, const /*! Implement this method to return the texture with \a id. The default - implementation returns 0. + implementation returns \nullptr. The \a id is the requested image source, with the "image:" scheme and provider identifier removed. For example, if the image \l{Image::}{source} diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index b75f069fab..94494df4c0 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -1759,6 +1759,22 @@ void tst_QJSEngine::stacktrace() QJSValue result2 = eng.evaluate(script2, fileName); QVERIFY(!result2.isError()); QVERIFY(result2.isString()); + + { + QString script3 = QString::fromLatin1( + "'use strict'\n" + "function throwUp() { throw new Error('up') }\n" + "function indirectlyThrow() { return throwUp() }\n" + "indirectlyThrow()\n" + ); + QJSValue result3 = eng.evaluate(script3); + QVERIFY(result3.isError()); + QJSValue stack = result3.property("stack"); + QVERIFY(stack.isString()); + QString stackTrace = stack.toString(); + QVERIFY(!stackTrace.contains(QStringLiteral("indirectlyThrow"))); + QVERIFY(stackTrace.contains(QStringLiteral("elide"))); + } } void tst_QJSEngine::numberParsing_data() diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index e83dac48fb..7139a1c952 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -35,6 +35,7 @@ #include <private/qqmlmetatype_p.h> #include <private/qqmlpropertyvalueinterceptor_p.h> +#include <private/qqmlengine_p.h> #include <private/qhashedstring_p.h> #include "../../shared/util.h" @@ -64,6 +65,7 @@ private slots: void unregisterCustomSingletonType(); void normalizeUrls(); + void unregisterAttachedProperties(); }; class TestType : public QObject @@ -533,6 +535,43 @@ void tst_qqmlmetatype::normalizeUrls() QVERIFY(!QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid()); } +void tst_qqmlmetatype::unregisterAttachedProperties() +{ + qmlClearTypeRegistrations(); + + const QUrl dummy("qrc:///doesnotexist.qml"); + { + QQmlEngine e; + QQmlComponent c(&e); + c.setData("import QtQuick 2.2\n Item { }", dummy); + + const QQmlType attachedType = QQmlMetaType::qmlType("QtQuick/KeyNavigation", 2, 2); + QCOMPARE(attachedType.attachedPropertiesId(QQmlEnginePrivate::get(&e)), + attachedType.index()); + + QVERIFY(c.create()); + } + + qmlClearTypeRegistrations(); + { + QQmlEngine e; + QQmlComponent c(&e); + + // The extra import shuffles the type IDs around, so that we + // get a different ID for the attached properties. If the attached + // properties aren't properly cleared, this will crash. + c.setData("import QtQml.StateMachine 1.0 \n" + "import QtQuick 2.2 \n" + "Item { KeyNavigation.up: null }", dummy); + + const QQmlType attachedType = QQmlMetaType::qmlType("QtQuick/KeyNavigation", 2, 2); + QCOMPARE(attachedType.attachedPropertiesId(QQmlEnginePrivate::get(&e)), + attachedType.index()); + + QVERIFY(c.create()); + } +} + QTEST_MAIN(tst_qqmlmetatype) #include "tst_qqmlmetatype.moc" diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp index 62027f59f4..3526eb98be 100644 --- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp +++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp @@ -88,31 +88,37 @@ void tst_qquickapplication::active() QQuickWindow window; item->setParentItem(window.contentItem()); - // not active - QVERIFY(!item->property("active").toBool()); - QVERIFY(!item->property("active2").toBool()); - - // active - window.show(); - window.requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(&window)); - QCOMPARE(QGuiApplication::focusWindow(), &window); - QVERIFY(item->property("active").toBool()); - QVERIFY(item->property("active2").toBool()); - - QWindowSystemInterface::handleWindowActivated(nullptr); - -#ifdef Q_OS_OSX - // OS X has the concept of "reactivation" - QTRY_VERIFY(QGuiApplication::focusWindow() != &window); - QVERIFY(item->property("active").toBool()); - QVERIFY(item->property("active2").toBool()); -#else - // not active again - QTRY_VERIFY(QGuiApplication::focusWindow() != &window); - QVERIFY(!item->property("active").toBool()); - QVERIFY(!item->property("active2").toBool()); -#endif + // If the platform plugin has the ApplicationState capability, app activation originate from it + // as a result of a system event. We therefore have to simulate these events here. + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ApplicationState)) { + + // Flush pending events, in case the platform have already queued real application state events + QWindowSystemInterface::flushWindowSystemEvents(); + + QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive); + QWindowSystemInterface::flushWindowSystemEvents(); + QVERIFY(item->property("active").toBool()); + QVERIFY(item->property("active2").toBool()); + + QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive); + QWindowSystemInterface::flushWindowSystemEvents(); + QVERIFY(!item->property("active").toBool()); + QVERIFY(!item->property("active2").toBool()); + } else { + // Otherwise, app activation is triggered by window activation. + window.show(); + window.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QCOMPARE(QGuiApplication::focusWindow(), &window); + QVERIFY(item->property("active").toBool()); + QVERIFY(item->property("active2").toBool()); + + // not active again + QWindowSystemInterface::handleWindowActivated(nullptr); + QTRY_VERIFY(QGuiApplication::focusWindow() != &window); + QVERIFY(!item->property("active").toBool()); + QVERIFY(!item->property("active2").toBool()); + } } void tst_qquickapplication::state() diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index 8d3abb7ce3..bf38d2d926 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -1372,6 +1372,7 @@ void tst_QQuickPathView::package() QSKIP("QTBUG-27170 view does not reliably receive polish without a running animation"); #endif + QQuickTest::qWaitForItemPolished(pathView); QQuickItem *item = findItem<QQuickItem>(pathView, "pathItem"); QVERIFY(item); QVERIFY(item->scale() != 1.0); diff --git a/tests/manual/tableview/storagemodel/main.qml b/tests/manual/tableview/storagemodel/main.qml index 2603fba0e2..725d7da7b0 100644 --- a/tests/manual/tableview/storagemodel/main.qml +++ b/tests/manual/tableview/storagemodel/main.qml @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -38,13 +38,14 @@ ****************************************************************************/ import QtQuick 2.12 -import QtQuick.Window 2.3 +import QtQuick.Window 2.12 +import Qt.labs.qmlmodels 1.0 import StorageModel 0.1 Window { id: window - width: 640 - height: 480 + width: 480 + height: 300 visible: true color: "darkgray" title: "Storage Volumes" @@ -57,17 +58,62 @@ Window { model: StorageModel { } columnSpacing: 1 rowSpacing: 1 - delegate: Rectangle { - id: tableDelegate - implicitWidth: displayText.implicitWidth + 8 - implicitHeight: displayText.implicitHeight + 14 + delegate: DelegateChooser { + role: "type" + DelegateChoice { + roleValue: "Value" + delegate: Rectangle { + color: "tomato" + implicitWidth: Math.max(100, label.implicitWidth + 8) + implicitHeight: label.implicitHeight + 4 - Text { - id: displayText - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.leftMargin: 4 - text: display + Rectangle { + x: parent.width - width + width: value * parent.width / valueMax + height: parent.height + color: "white" + } + + Text { + id: label + anchors.baseline: parent.bottom + anchors.baselineOffset: -4 + anchors.left: parent.left + anchors.leftMargin: 4 + text: valueDisplay + " of " + valueMaxDisplay + " " + heading + } + } + } + DelegateChoice { + roleValue: "Flag" + // We could use a checkbox here but that would be another component (e.g. from Controls) + delegate: Rectangle { + implicitWidth: checkBox.implicitWidth + 8 + implicitHeight: checkBox.implicitHeight + 4 + Text { + id: checkBox + anchors.baseline: parent.bottom + anchors.baselineOffset: -4 + anchors.left: parent.left + anchors.leftMargin: 4 + text: (checkState ? "☑ " : "☐ ") + heading + } + } + } + DelegateChoice { + // roleValue: "String" // default delegate + delegate: Rectangle { + implicitWidth: stringLabel.implicitWidth + 8 + implicitHeight: stringLabel.implicitHeight + 4 + Text { + id: stringLabel + anchors.baseline: parent.bottom + anchors.baselineOffset: -4 + anchors.left: parent.left + anchors.leftMargin: 4 + text: display + } + } } } } diff --git a/tests/manual/tableview/storagemodel/storagemodel.cpp b/tests/manual/tableview/storagemodel/storagemodel.cpp index b6b861a1a8..b43454b249 100644 --- a/tests/manual/tableview/storagemodel/storagemodel.cpp +++ b/tests/manual/tableview/storagemodel/storagemodel.cpp @@ -63,6 +63,20 @@ StorageModel::StorageModel(QObject *parent) : refresh(); } +QHash<int, QByteArray> StorageModel::roleNames() const { + static auto roles = QHash<int, QByteArray> { + { int(Role::Type), "type" }, + { int(Role::Heading), "heading" }, + { int(Role::Value), "value" }, + { int(Role::ValueMax), "valueMax" }, + { int(Role::ValueDisplay), "valueDisplay" }, + { int(Role::ValueMaxDisplay), "valueMaxDisplay" }, + { Qt::CheckStateRole, "checkState" }, + }; + static auto ret = roles.unite(QAbstractTableModel::roleNames());; + return ret; +} + void StorageModel::refresh() { beginResetModel(); @@ -92,7 +106,6 @@ Qt::ItemFlags StorageModel::flags(const QModelIndex &index) const { Qt::ItemFlags result = QAbstractTableModel::flags(index); switch (Column(index.column())) { - case Column::Available: case Column::IsReady: case Column::IsReadOnly: case Column::IsValid: @@ -109,7 +122,9 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const if (!index.isValid()) return QVariant(); - if (role == Qt::DisplayRole) { + switch (role) { + case Qt::DisplayRole: + case int(Role::ValueDisplay): { const QStorageInfo &volume = m_volumes.at(index.row()); switch (Column(index.column())) { case Column::RootPath: @@ -120,12 +135,8 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const return volume.device(); case Column::FileSystemName: return volume.fileSystemType(); - case Column::Total: - return QLocale().formattedDataSize(volume.bytesTotal()); case Column::Free: return QLocale().formattedDataSize(volume.bytesFree()); - case Column::Available: - return QLocale().formattedDataSize(volume.bytesAvailable()); case Column::IsReady: return volume.isReady(); case Column::IsReadOnly: @@ -135,7 +146,8 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const default: break; } - } else if (role == Qt::CheckStateRole) { + } break; + case Qt::CheckStateRole: { const QStorageInfo &volume = m_volumes.at(index.row()); switch (Column(index.column())) { case Column::IsReady: @@ -147,17 +159,16 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const default: break; } - } else if (role == Qt::TextAlignmentRole) { + } break; + case Qt::TextAlignmentRole: switch (Column(index.column())) { - case Column::Total: case Column::Free: - case Column::Available: return Qt::AlignTrailing; default: break; } return Qt::AlignLeading; - } else if (role == Qt::ToolTipRole) { + case Qt::ToolTipRole: { QLocale locale; const QStorageInfo &volume = m_volumes.at(index.row()); return tr("Root path : %1\n" @@ -186,6 +197,51 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const arg(volume.isValid() ? tr("true") : tr("false")). arg(volume.isRoot() ? tr("true") : tr("false")); } + case int(Role::Type): + switch (Column(index.column())) { + case Column::RootPath: + case Column::Name: + case Column::Device: + case Column::FileSystemName: + return QVariant::fromValue(Type::String); + break; + case Column::Free: + return QVariant::fromValue(Type::Value); + case Column::IsReady: + case Column::IsReadOnly: + case Column::IsValid: + return QVariant::fromValue(Type::Flag); + default: + break; + } + break; + case int(Role::Heading): + return headerData(index.column()); + case int(Role::Value): + switch (Column(index.column())) { + case Column::Free: + return m_volumes.at(index.row()).bytesFree(); + default: + break; + } + break; + case int(Role::ValueMax): + switch (Column(index.column())) { + case Column::Free: + return m_volumes.at(index.row()).bytesTotal(); + default: + break; + } + break; + case int(Role::ValueMaxDisplay): + switch (Column(index.column())) { + case Column::Free: + return QLocale().formattedDataSize(m_volumes.at(index.row()).bytesTotal()); + default: + break; + } + break; + } // switch (role) return QVariant(); } @@ -206,12 +262,8 @@ QVariant StorageModel::headerData(int section, Qt::Orientation orientation, int return tr("Device"); case Column::FileSystemName: return tr("File System"); - case Column::Total: - return tr("Total"); case Column::Free: return tr("Free"); - case Column::Available: - return tr("Available"); case Column::IsReady: return tr("Ready"); case Column::IsReadOnly: diff --git a/tests/manual/tableview/storagemodel/storagemodel.h b/tests/manual/tableview/storagemodel/storagemodel.h index 1f6f6f8b1f..6cbab3d850 100644 --- a/tests/manual/tableview/storagemodel/storagemodel.h +++ b/tests/manual/tableview/storagemodel/storagemodel.h @@ -65,23 +65,42 @@ public: Name, Device, FileSystemName, - Total, Free, - Available, IsReady, IsReadOnly, IsValid, Count }; + Q_ENUM(Column) + + enum class Role : int { + Type = Qt::UserRole + 1, + Heading, + Value, + ValueMax, // If we had ValueMin, it would always be zero in this example + ValueDisplay, + ValueMaxDisplay, + Count + }; + Q_ENUM(Role) + + enum class Type : int { + String, // use Qt::DisplayRole + Value, // use Role::Value and Role::ValueMax + Flag, // use Qt::CheckStateRole + Count + }; + Q_ENUM(Type) explicit StorageModel(QObject *parent = nullptr); int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; + QHash<int, QByteArray> roleNames() const override; QVariant data(const QModelIndex &index, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation = Qt::Horizontal, int role = Qt::DisplayRole) const override; public slots: void refresh(); |