diff options
author | Liang Qi <liang.qi@qt.io> | 2017-11-06 18:12:53 +0100 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2017-11-06 18:12:53 +0100 |
commit | 853b4b08d54f70d04d371d707e50760ffecf2f77 (patch) | |
tree | 2220ac8be8ae9bb5b25cf560eec3dc973c09b8e8 | |
parent | 047bdf90db4d00d0462b9faf2044070be4a0e682 (diff) | |
parent | 87999238141588987b3cef6cd68ff62a7e3e8daa (diff) |
24 files changed, 759 insertions, 86 deletions
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index 3d08c4c809..d2cf7dc57f 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -337,6 +337,9 @@ void QQmlEngineDebugServiceImpl::buildObjectList(QDataStream &message, QQmlContext *ctxt, const QList<QPointer<QObject> > &instances) { + if (!ctxt->isValid()) + return; + QQmlContextData *p = QQmlContextData::get(ctxt); QString ctxtName = ctxt->objectName(); @@ -399,11 +402,8 @@ QQmlEngineDebugServiceImpl::objectData(QObject *object) } QQmlContext *context = qmlContext(object); - if (context) { - QQmlContextData *cdata = QQmlContextData::get(context); - if (cdata) - rv.idString = cdata->findObjectId(object); - } + if (context && context->isValid()) + rv.idString = QQmlContextData::get(context)->findObjectId(object); rv.objectName = object->objectName(); rv.objectId = QQmlDebugService::idForObject(object); @@ -564,14 +564,14 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message) QObject *object = QQmlDebugService::objectForId(objectId); QQmlContext *context = qmlContext(object); - if (!context) { + if (!context || !context->isValid()) { QQmlEngine *engine = qobject_cast<QQmlEngine *>( QQmlDebugService::objectForId(engineId)); if (engine && m_engines.contains(engine)) context = engine->rootContext(); } QVariant result; - if (context) { + if (context && context->isValid()) { QQmlExpression exprObj(context, object, expr); bool undefined = false; QVariant value = exprObj.evaluate(&undefined); @@ -632,7 +632,7 @@ bool QQmlEngineDebugServiceImpl::setBinding(int objectId, QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); - if (object && context) { + if (object && context && context->isValid()) { QQmlProperty property(object, propertyName, context); if (property.isValid()) { @@ -677,7 +677,7 @@ bool QQmlEngineDebugServiceImpl::resetBinding(int objectId, const QString &prope QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); - if (object && context) { + if (object && context && context->isValid()) { QStringRef parentPropertyRef(&propertyName); const int idx = parentPropertyRef.indexOf(QLatin1Char('.')); if (idx != -1) @@ -732,11 +732,9 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth { QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); - if (!object || !context || !context->engine()) + if (!object || !context || !context->isValid()) return false; QQmlContextData *contextData = QQmlContextData::get(context); - if (!contextData) - return false; QQmlPropertyData dummy; QQmlPropertyData *prop = diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 8f3c8ea8dd..f60f148495 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -897,6 +897,8 @@ void QQmlData::setQueuedForDeletion(QObject *object) if (ddata->ownContext) { Q_ASSERT(ddata->ownContext == ddata->context); ddata->context->emitDestruction(); + if (ddata->ownContext->contextObject == object) + ddata->ownContext->contextObject = nullptr; ddata->ownContext = 0; ddata->context = 0; } diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index ee5f177855..2f639af790 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -739,7 +739,8 @@ void QQuickFlickable::setContentX(qreal pos) d->hData.explicitValue = true; d->resetTimeline(d->hData); d->hData.vTime = d->timeline.time(); - movementEnding(true, false); + if (isMoving() || isFlicking()) + movementEnding(true, false); if (-pos != d->hData.move.value()) d->hData.move.setValue(-pos); } @@ -756,7 +757,8 @@ void QQuickFlickable::setContentY(qreal pos) d->vData.explicitValue = true; d->resetTimeline(d->vData); d->vData.vTime = d->timeline.time(); - movementEnding(false, true); + if (isMoving() || isFlicking()) + movementEnding(false, true); if (-pos != d->vData.move.value()) d->vData.move.setValue(-pos); } @@ -2180,25 +2182,25 @@ qreal QQuickFlickable::originX() const void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center) { Q_D(QQuickFlickable); - if (w != d->hData.viewSize) { - qreal oldSize = d->hData.viewSize; - d->hData.viewSize = w; - d->contentItem->setWidth(w); + const qreal oldHSize = d->hData.viewSize; + const qreal oldVSize = d->vData.viewSize; + const bool needToUpdateWidth = w != oldHSize; + const bool needToUpdateHeight = h != oldVSize; + d->hData.viewSize = w; + d->vData.viewSize = h; + d->contentItem->setSize(QSizeF(w, h)); + if (needToUpdateWidth) emit contentWidthChanged(); - if (center.x() != 0) { - qreal pos = center.x() * w / oldSize; - setContentX(contentX() + pos - center.x()); - } - } - if (h != d->vData.viewSize) { - qreal oldSize = d->vData.viewSize; - d->vData.viewSize = h; - d->contentItem->setHeight(h); + if (needToUpdateHeight) emit contentHeightChanged(); - if (center.y() != 0) { - qreal pos = center.y() * h / oldSize; - setContentY(contentY() + pos - center.y()); - } + + if (center.x() != 0) { + qreal pos = center.x() * w / oldHSize; + setContentX(contentX() + pos - center.x()); + } + if (center.y() != 0) { + qreal pos = center.y() * h / oldVSize; + setContentY(contentY() + pos - center.y()); } d->updateBeginningEnd(); } diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 1d0d042839..478499e209 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -926,7 +926,6 @@ void QQuickItemView::setDisplacedTransition(QQuickTransition *transition) void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode) { - Q_Q(QQuickItemView); if (!isValid()) return; if (mode < QQuickItemView::Beginning || mode > QQuickItemView::SnapPosition) @@ -988,7 +987,6 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode) qreal minExtent = calculatedMinExtent(); pos = qMax(pos, minExtent); moveReason = QQuickItemViewPrivate::Other; - q->cancelFlick(); setPosition(pos); if (highlight) { @@ -1246,16 +1244,26 @@ void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometry void QQuickItemView::destroyRemoved() { Q_D(QQuickItemView); + + bool hasRemoveTransition = false; + bool hasRemoveTransitionAsTarget = false; + if (d->transitioner) { + hasRemoveTransition = d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false); + hasRemoveTransitionAsTarget = d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true); + } + for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin(); it != d->visibleItems.end();) { FxViewItem *item = *it; if (item->index == -1 && (!item->attached || item->attached->delayRemove() == false)) { - if (d->transitioner && d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)) { + if (hasRemoveTransitionAsTarget) { // don't remove from visibleItems until next layout() d->runDelayedRemoveTransition = true; QObject::disconnect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved())); ++it; } else { + if (hasRemoveTransition) + d->runDelayedRemoveTransition = true; d->releaseItem(item); it = d->visibleItems.erase(it); } @@ -1384,7 +1392,6 @@ void QQuickItemView::trackedPositionChanged() pos = qMax(trackedPos, toItemPos); } if (viewPos != pos) { - cancelFlick(); d->calcVelocity = true; d->setPosition(pos); d->calcVelocity = false; @@ -1934,8 +1941,9 @@ void QQuickItemViewPrivate::layout() if (transitioner) { // items added in the last refill() may need to be transitioned in - e.g. a remove // causes items to slide up into view - if (transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, false) - || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) { + if (lastIndexInView != -1 && + (transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, false) + || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false))) { translateAndTransitionItemsAfter(lastIndexInView, insertionPosChanges, removalPosChanges); } @@ -1978,7 +1986,6 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult } updateUnrequestedIndexes(); - moveReason = QQuickItemViewPrivate::Other; FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? *visibleItems.constBegin() : 0; int prevItemCount = itemCount; diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp index 02cf8209d1..30088846a6 100644 --- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp @@ -193,6 +193,12 @@ QRegion QSGAbstractSoftwareRenderer::optimizeRenderList() } } + if (m_obscuredRegion.contains(m_background->rect().toAlignedRect())) { + m_isOpaque = true; + } else { + m_isOpaque = false; + } + // Empty dirtyRegion (for second pass) m_dirtyRegion = QRegion(); m_obscuredRegion = QRegion(); diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h index 04a17ea377..c68a933384 100644 --- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h @@ -86,6 +86,8 @@ protected: void setBackgroundSize(const QSize &size); QColor backgroundColor(); QSize backgroundSize(); + // only known after calling optimizeRenderList() + bool isOpaque() const { return m_isOpaque; } private: void nodeAdded(QSGNode *node); @@ -102,6 +104,7 @@ private: QRegion m_dirtyRegion; QRegion m_obscuredRegion; + bool m_isOpaque; QSGSoftwareRenderableNodeUpdater *m_nodeUpdater; }; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h index f21667fdf7..5c95eb064a 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h @@ -124,8 +124,8 @@ public: QRectF rect() const; -private: const QPixmap &pixmap() const; +private: QRectF m_targetRect; QRectF m_innerTargetRect; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp index bd5d8f72c0..9d30c43f87 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp @@ -229,9 +229,6 @@ void QSGSoftwareLayer::grab() if (m_pixmap.size() != m_size) { m_pixmap = QPixmap(m_size); m_pixmap.setDevicePixelRatio(m_device_pixel_ratio); - // This fill here is wasteful, but necessary because it is the only way - // to force a QImage based pixmap to have an alpha channel. - m_pixmap.fill(Qt::transparent); } // Render texture. diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp index ad6cf39425..186fd92fb7 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp @@ -82,13 +82,6 @@ void QSGSoftwarePixmapRenderer::render(QPaintDevice *target) setBackgroundSize(QSize(target->width(), target->height())); setBackgroundColor(clearColor()); - QPainter painter(target); - painter.setRenderHint(QPainter::Antialiasing); - painter.setWindow(m_projectionRect); - auto rc = static_cast<QSGSoftwareRenderContext *>(context()); - QPainter *prevPainter = rc->m_activePainter; - rc->m_activePainter = &painter; - renderTimer.start(); buildRenderList(); qint64 buildRenderListTime = renderTimer.restart(); @@ -101,6 +94,19 @@ void QSGSoftwarePixmapRenderer::render(QPaintDevice *target) optimizeRenderList(); qint64 optimizeRenderListTime = renderTimer.restart(); + if (!isOpaque() && target->devType() == QInternal::Pixmap) { + // This fill here is wasteful, but necessary because it is the only way + // to force a QImage based pixmap to have an alpha channel. + static_cast<QPixmap *>(target)->fill(Qt::transparent); + } + + QPainter painter(target); + painter.setRenderHint(QPainter::Antialiasing); + painter.setWindow(m_projectionRect); + auto rc = static_cast<QSGSoftwareRenderContext *>(context()); + QPainter *prevPainter = rc->m_activePainter; + rc->m_activePainter = &painter; + QRegion paintedRegion = renderNodes(&painter); qint64 renderTime = renderTimer.elapsed(); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h index 9f1913205b..114137fb55 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h @@ -133,6 +133,8 @@ public: QRectF bounds() const; + bool isOpaque() const { return !m_pixmap.hasAlphaChannel(); } + private: QPixmap m_pixmap; QRectF m_bounds; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index cecc6c21ca..bd2946c675 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -134,73 +134,58 @@ void QSGSoftwareRenderableNode::update() { // Update the Node properties m_isDirty = true; + m_isOpaque = false; QRectF boundingRect; switch (m_nodeType) { case QSGSoftwareRenderableNode::SimpleRect: - if (m_handle.simpleRectNode->color().alpha() == 255 && !m_transform.isRotating()) + if (m_handle.simpleRectNode->color().alpha() == 255) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.simpleRectNode->rect(); break; case QSGSoftwareRenderableNode::SimpleTexture: - if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel() && !m_transform.isRotating()) + if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel()) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.simpleTextureNode->rect(); break; case QSGSoftwareRenderableNode::Image: - // There isn't a way to tell, so assume it's not - m_isOpaque = false; + m_isOpaque = !m_handle.imageNode->pixmap().hasAlphaChannel(); boundingRect = m_handle.imageNode->rect().toRect(); break; case QSGSoftwareRenderableNode::Painter: - if (m_handle.painterNode->opaquePainting() && !m_transform.isRotating()) + if (m_handle.painterNode->opaquePainting()) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = QRectF(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height()); break; case QSGSoftwareRenderableNode::Rectangle: - if (m_handle.rectangleNode->isOpaque() && !m_transform.isRotating()) + if (m_handle.rectangleNode->isOpaque()) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.rectangleNode->rect(); break; case QSGSoftwareRenderableNode::Glyph: // Always has alpha - m_isOpaque = false; - boundingRect = m_handle.glpyhNode->boundingRect(); break; case QSGSoftwareRenderableNode::NinePatch: - // Difficult to tell, assume non-opaque - m_isOpaque = false; + m_isOpaque = m_handle.ninePatchNode->isOpaque(); boundingRect = m_handle.ninePatchNode->bounds(); break; case QSGSoftwareRenderableNode::SimpleRectangle: - if (m_handle.simpleRectangleNode->color().alpha() == 255 && !m_transform.isRotating()) + if (m_handle.simpleRectangleNode->color().alpha() == 255) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.simpleRectangleNode->rect(); break; case QSGSoftwareRenderableNode::SimpleImage: - if (!m_handle.simpleImageNode->texture()->hasAlphaChannel() && !m_transform.isRotating()) + if (!m_handle.simpleImageNode->texture()->hasAlphaChannel()) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.simpleImageNode->rect(); break; @@ -211,10 +196,8 @@ void QSGSoftwareRenderableNode::update() break; #endif case QSGSoftwareRenderableNode::RenderNode: - if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering) && !m_transform.isRotating()) + if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering)) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.renderNode->rect(); break; @@ -222,6 +205,9 @@ void QSGSoftwareRenderableNode::update() break; } + if (m_transform.isRotating()) + m_isOpaque = false; + const QRectF transformedRect = m_transform.mapRect(boundingRect); m_boundingRectMin = toRectMin(transformedRect); m_boundingRectMax = toRectMax(transformedRect); diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index d9a4777115..57e95f7b89 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -99,6 +99,8 @@ private: const QmlDebugObjectReference &oref, bool recursive) const; + void getContexts(); + QQmlDebugConnection *m_conn; QQmlEngineDebugClient *m_dbg; QQmlEngine *m_engine; @@ -138,6 +140,7 @@ private slots: void regression_QTCREATORBUG_7451(); void queryObjectWithNonStreamableTypes(); void asynchronousCreate(); + void invalidContexts(); }; QmlDebugObjectReference tst_QQmlEngineDebugService::findRootObject( @@ -248,6 +251,22 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( } } +void tst_QQmlEngineDebugService::getContexts() +{ + bool success = false; + + m_dbg->queryAvailableEngines(&success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); + + QList<QmlDebugEngineReference> engines = m_dbg->engines(); + QCOMPARE(engines.count(), 1); + m_dbg->queryRootContexts(engines.first().debugId, &success); + + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); +} + void tst_QQmlEngineDebugService::initTestCase() { qmlRegisterType<NonScriptProperty>("Test", 1, 0, "NonScriptPropertyElement"); @@ -1289,6 +1308,26 @@ void tst_QQmlEngineDebugService::asynchronousCreate() { QTRY_COMPARE(m_dbg->object().idString, QLatin1String("asyncRect")); } +void tst_QQmlEngineDebugService::invalidContexts() +{ + getContexts(); + const int base = m_dbg->rootContext().contexts.count(); + QQmlContext context(m_engine); + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), base + 1); + QQmlContextData *contextData = QQmlContextData::get(&context); + contextData->invalidate(); + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), base); + QQmlContextData *rootData = QQmlContextData::get(m_engine->rootContext()); + rootData->invalidate(); + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), 0); + contextData->setParent(rootData); // makes context valid again, but not root. + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), 0); +} + int main(int argc, char *argv[]) { int _argc = argc + 1; diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 0bb913d104..3f66daf87f 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -38,6 +38,7 @@ #include <private/qqmlvaluetype_p.h> #include <math.h> #include "../../shared/util.h" +#include "../shared/geometrytestutil.h" #include "../shared/viewtestutil.h" #include "../shared/visualtestutil.h" @@ -790,7 +791,14 @@ void tst_qquickflickable::resizeContent() QCOMPARE(obj->contentWidth(), 300.); QCOMPARE(obj->contentHeight(), 300.); + QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(obj); + QSizeChangeListener sizeListener(fp->contentItem); + QMetaObject::invokeMethod(root, "resizeContent"); + for (const QSize sizeOnGeometryChanged : sizeListener) { + // Check that we have the correct size on all signals + QCOMPARE(sizeOnGeometryChanged, QSize(600, 600)); + } QCOMPARE(obj->contentX(), 100.); QCOMPARE(obj->contentY(), 100.); diff --git a/tests/auto/quick/qquickgridview/data/qtbug57225.qml b/tests/auto/quick/qquickgridview/data/qtbug57225.qml new file mode 100644 index 0000000000..3871e5d273 --- /dev/null +++ b/tests/auto/quick/qquickgridview/data/qtbug57225.qml @@ -0,0 +1,95 @@ +import QtQuick 2.0 + +Rectangle { + id: root + width: 200 + height: 200 + + property int duration: 100 + property int count: grid.count + + Component { + id: myDelegate + Rectangle { + id: wrapper + + property string nameData: name + property bool removalStarted: false + property real minX: 0 + property real minY: 0 + + onXChanged: if (removalStarted) grid.recordPosition(x, y) + onYChanged: if (removalStarted) grid.recordPosition(x, y) + + objectName: "wrapper" + width: 80 + height: 80 + border.width: 1 + Column { + Text { text: index } + Text { + text: wrapper.x + ", " + wrapper.y + } + Text { + id: textName + objectName: "textName" + text: name + } + } + color: GridView.isCurrentItem ? "lightsteelblue" : "white" + + GridView.onRemove: SequentialAnimation { + PropertyAction { target: wrapper; property: "removalStarted"; value: true } + PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: true } + NumberAnimation { target: wrapper; property: "scale"; to: 0.5; duration: root.duration; easing.type: Easing.InOutQuad } + PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: false } + PropertyAction { target: grid; property: "animationDone"; value: true } + } + + } + } + + GridView { + id: grid + + property int displaceTransitionsDone: 0 + property bool animationDone: false + property point minimumPosition: Qt.point(0, 0) + + signal delegateMoved(real x, real y) + + objectName: "grid" + focus: true + anchors.fill: parent + cacheBuffer: 0 + cellWidth: 80 + cellHeight: 80 + model: testModel + delegate: myDelegate + + displaced: Transition { + id: transition + SequentialAnimation { + NumberAnimation { + properties: "x,y" + duration: root.duration + easing.type: Easing.OutBounce + } + ScriptAction { script: grid.displaceTransitionsDone += 1 } + } + } + + function recordPosition(index, x, y) { + if (x < minimumPosition.x || y < minimumPosition.y) { + minimumPosition = Qt.point(x, y) + } + } + } + + Rectangle { + anchors.fill: grid + color: "lightsteelblue" + opacity: 0.2 + } +} + diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index 2b14842658..388ecc2ab8 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -153,6 +153,8 @@ private slots: void multipleTransitions(); void multipleTransitions_data(); void multipleDisplaced(); + void regression_QTBUG_57225(); + void regression_QTBUG_57225_data(); void inserted_leftToRight_RtL_TtB(); void inserted_leftToRight_RtL_TtB_data(); @@ -5798,6 +5800,65 @@ void tst_QQuickGridView::multipleDisplaced() delete window; } +void tst_QQuickGridView::regression_QTBUG_57225() +{ + QFETCH(int, initialCount); + QFETCH(int, removeIndex); + QFETCH(int, removeCount); + QFETCH(int, expectedDisplaceTransitions); + + // deleting all visible items should not cause a repositioning of said items. + + QaimModel model; + for (int i = 0; i < initialCount; i++) + model.addItem("Original item" + QString::number(i), ""); + + QQuickView *window = createView(); + QQmlContext *ctxt = window->rootContext(); + ctxt->setContextProperty("testModel", &model); + window->setSource(testFileUrl("qtbug57225.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickGridView *gridview = findItem<QQuickGridView>(window->rootObject(), "grid"); + QVERIFY(gridview != 0); + QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false); + + model.removeItems(removeIndex, removeCount); + QTRY_VERIFY(gridview->property("animationDone").toBool()); + + // verify that none of the removed items has moved to a negative position + QPoint minimumPosition = gridview->property("minimumPosition").toPoint(); + QVERIFY(minimumPosition.x() >= 0); + QVERIFY(minimumPosition.y() >= 0); + + // wait some more time to let the displaced transition happen + QTest::qWait(window->rootObject()->property("duration").toInt()); + QTRY_VERIFY2(gridview->property("displaceTransitionsDone").toInt() >= expectedDisplaceTransitions, + QByteArray::number(gridview->property("displaceTransitionsDone").toInt()).constData()); + + delete window; +} + +void tst_QQuickGridView::regression_QTBUG_57225_data() +{ + QTest::addColumn<int>("initialCount"); + QTest::addColumn<int>("removeIndex"); + QTest::addColumn<int>("removeCount"); + QTest::addColumn<int>("expectedDisplaceTransitions"); + + // no displace transitions should happen + QTest::newRow("remove all visible items") << + 20 << 0 << 8 << 0; + + // check that the removal animation is performed + QTest::newRow("remove items in between") << + 20 << 1 << 2 << 3; + + QTest::newRow("remove items in between - 2") << + 20 << 2 << 3 << 1; +} + void tst_QQuickGridView::cacheBuffer() { QQuickView *window = createView(); diff --git a/tests/auto/quick/qquicklistview/data/appendDuringScrollDown.qml b/tests/auto/quick/qquicklistview/data/appendDuringScrollDown.qml new file mode 100644 index 0000000000..af35c29143 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/appendDuringScrollDown.qml @@ -0,0 +1,28 @@ +import QtQuick 2.6 + +ListView { + width: 320; height: 240 + focus: true + delegate: Text { + height: 40; width: parent.width + text: model.text + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + highlight: Rectangle { color: "red" } + model: ListModel { + ListElement { text: "0" } + ListElement { text: "1" } + ListElement { text: "2" } + ListElement { text: "3" } + ListElement { text: "4" } + ListElement { text: "5" } + ListElement { text: "6" } + ListElement { text: "7" } + ListElement { text: "8" } + ListElement { text: "9" } + } + + readonly property Item topItem: itemAt(0, contentY) + onTopItemChanged: model.append({ "text": "new" }) +} diff --git a/tests/auto/quick/qquicklistview/data/qtbug34576.qml b/tests/auto/quick/qquicklistview/data/qtbug34576.qml new file mode 100644 index 0000000000..f407d8ebe3 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/qtbug34576.qml @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +import QtQuick 2.7 + +Rectangle { + id: root + width: 320 + height: 240 + color: "black" + + property int current: list.currentIndex + property int horizontalVelocityZeroCount: 0 + + ListView { + id: list + objectName: "list" + anchors.fill: parent + + focus: true + + orientation: ListView.Horizontal + + snapMode: ListView.SnapToItem + flickableDirection: Flickable.HorizontalFlick + + model: 10 + delegate: Item { + width: root.width / 3 + height: root.height + Rectangle { + anchors.centerIn: parent + width: 50 + height: 50 + color: list.currentIndex === index ? "red" : "white" + } + } + + onHorizontalVelocityChanged: { + if (list.horizontalVelocity === 0.0) + root.horizontalVelocityZeroCount++ + } + + } + + Rectangle { + color: "red" + width: 50 + height: 50 + anchors.left: parent.left + anchors.bottom: parent.bottom + + MouseArea { + anchors.fill: parent + onClicked: { + list.currentIndex--; + } + } + } + + Rectangle { + color: "red" + width: 50 + height: 50 + anchors.right: parent.right + anchors.bottom: parent.bottom + + MouseArea { + anchors.fill: parent + onClicked: { + list.currentIndex++; + } + } + } +} + diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 0d0f234d33..a31cb37c16 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -254,10 +254,13 @@ private slots: void QTBUG_50105(); void keyNavigationEnabled(); + void QTBUG_61269_appendDuringScrollDown(); void QTBUG_50097_stickyHeader_positionViewAtIndex(); void itemFiltered(); void releaseItems(); + void QTBUG_34576_velocityZero(); + private: template <class T> void items(const QUrl &source); template <class T> void changed(const QUrl &source); @@ -4681,29 +4684,24 @@ void tst_QQuickListView::indexAt_itemAt() void tst_QQuickListView::incrementalModel() { QScopedPointer<QQuickView> window(createView()); - QSKIP("QTBUG-30716"); IncrementalModel model; QQmlContext *ctxt = window->rootContext(); ctxt->setContextProperty("testModel", &model); window->setSource(testFileUrl("displaylist.qml")); - qApp->processEvents(); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); QQuickListView *listview = findItem<QQuickListView>(window->rootObject(), "list"); QTRY_VERIFY(listview != 0); - listview->forceLayout(); QQuickItem *contentItem = listview->contentItem(); QTRY_VERIFY(contentItem != 0); - - listview->forceLayout(); - QTRY_COMPARE(listview->count(), 20); + QTRY_COMPARE(listview->count(), 35); listview->positionViewAtIndex(10, QQuickListView::Beginning); - - listview->forceLayout(); - QTRY_COMPARE(listview->count(), 25); + QTRY_COMPARE(listview->count(), 45); } void tst_QQuickListView::onAdd() @@ -8468,6 +8466,37 @@ void tst_QQuickListView::keyNavigationEnabled() QCOMPARE(listView->currentIndex(), 1); } +void tst_QQuickListView::QTBUG_61269_appendDuringScrollDown() +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("appendDuringScrollDown.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + QQuickListView *listView = qobject_cast<QQuickListView *>(window->rootObject()); + QQuickItem *highlightItem = listView->highlightItem(); + QVERIFY(listView); + QCOMPARE(listView->isKeyNavigationEnabled(), true); + listView->setHighlightMoveVelocity(400); + listView->setHighlightMoveDuration(-1); // let it animate + listView->setFocus(true); + QVERIFY(listView->hasActiveFocus()); + qreal highlightYLimit = listView->height() - highlightItem->height(); // should be 200 + + for (int i = 1; i < 15; ++i) { + QTest::keyClick(window.data(), Qt::Key_Down); + + // Wait for the highlight movement animation to finish. + QTRY_COMPARE(highlightItem->y(), 40.0 * i); + + // As we scroll down, the QML will append rows to its own model. + // Make sure the highlighted row and highlight item stay within the view. + // In QTBUG-62864 and QTBUG-61269, it would go off the bottom. + QVERIFY(highlightItem->y() - listView->contentY() <= highlightYLimit); + } +} + void tst_QQuickListView::QTBUG_48870_fastModelUpdates() { StressTestModel model; @@ -8575,6 +8604,54 @@ void tst_QQuickListView::releaseItems() listview->setModel(123); } +void tst_QQuickListView::QTBUG_34576_velocityZero() +{ + QQuickView *window = new QQuickView(0); + window->setGeometry(0,0,240,320); + + QQmlContext *ctxt = window->rootContext(); + + QString filename(testFile("qtbug34576.qml")); + window->setSource(QUrl::fromLocalFile(filename)); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickListView *listview = findItem<QQuickListView>(window->rootObject(), "list"); + QTRY_VERIFY(listview != 0); + QQuickItem *contentItem = listview->contentItem(); + QTRY_VERIFY(contentItem != 0); + QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); + + QSignalSpy horizontalVelocitySpy(listview, SIGNAL(horizontalVelocityChanged())); + + // currentIndex is initialized to 0 + QCOMPARE(listview->currentIndex(), 0); + + // set currentIndex to last item currently visible item + window->rootObject()->setProperty("horizontalVelocityZeroCount", QVariant(0)); + listview->setCurrentIndex(2); + QTRY_COMPARE(window->rootObject()->property("current").toInt(), 2); + QTRY_COMPARE(horizontalVelocitySpy.count(), 0); + QTRY_COMPARE(window->rootObject()->property("horizontalVelocityZeroCount").toInt(), 0); + + // click button which increases currentIndex + QTest::mousePress(window, Qt::LeftButton, 0, QPoint(295,215)); + QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(295,215)); + + // verify that currentIndexChanged is triggered + QVERIFY(horizontalVelocitySpy.wait()); + + // set currentIndex to item out of view to cause listview scroll + QTRY_COMPARE(window->rootObject()->property("current").toInt(), 3); + QTRY_COMPARE(horizontalVelocitySpy.count() > 0, true); + QVERIFY(horizontalVelocitySpy.wait(1000)); + + // velocity should be always > 0.0 + QTRY_COMPARE(window->rootObject()->property("horizontalVelocityZeroCount").toInt(), 0); + + delete window; +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" diff --git a/tests/benchmarks/benchmarks.pro b/tests/benchmarks/benchmarks.pro index 07a6d5ecaa..6d62fa09d9 100644 --- a/tests/benchmarks/benchmarks.pro +++ b/tests/benchmarks/benchmarks.pro @@ -1,5 +1,5 @@ TEMPLATE = subdirs -SUBDIRS = qml +SUBDIRS = qml quick qtConfig(private_tests) { qtConfig(opengl(es1|es2)?):SUBDIRS += particles } diff --git a/tests/benchmarks/quick/events/data/mouseevent.qml b/tests/benchmarks/quick/events/data/mouseevent.qml new file mode 100644 index 0000000000..0959cc025e --- /dev/null +++ b/tests/benchmarks/quick/events/data/mouseevent.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +Item { + id: root + width: 400 + height: 400 + MouseArea { + anchors.fill: parent + } + + Item { + width: 400 + height: 400 + + MouseArea { + objectName: "mouseArea" + anchors.fill: parent + } + } +} diff --git a/tests/benchmarks/quick/events/data/touchevent.qml b/tests/benchmarks/quick/events/data/touchevent.qml new file mode 100644 index 0000000000..f9a4c84189 --- /dev/null +++ b/tests/benchmarks/quick/events/data/touchevent.qml @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +Item { + id: root + width: 400 + height: 400 + + MouseArea { + anchors.fill: parent + } + + Item { + width: 400 + height: 400 + + MultiPointTouchArea { + anchors.fill: parent + touchPoints: [ TouchPoint { id: point1 }] + touchPoints: [ TouchPoint { id: point2 }] + } + } +} diff --git a/tests/benchmarks/quick/events/events.pro b/tests/benchmarks/quick/events/events.pro new file mode 100644 index 0000000000..88e85d0278 --- /dev/null +++ b/tests/benchmarks/quick/events/events.pro @@ -0,0 +1,11 @@ +CONFIG += benchmark +TEMPLATE = app +TARGET = tst_qevents +QT += quick quick-private qml testlib +macos:CONFIG -= app_bundle + +SOURCES += tst_events.cpp + +include (../../../auto/shared/util.pri) +include (../../../auto/quick/shared/util.pri) + diff --git a/tests/benchmarks/quick/events/tst_events.cpp b/tests/benchmarks/quick/events/tst_events.cpp new file mode 100644 index 0000000000..344842153b --- /dev/null +++ b/tests/benchmarks/quick/events/tst_events.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QtQuick> +#include <QtQuick/private/qquickmousearea_p.h> +#include <QDebug> +#include "../../../auto/shared/util.h" +#include "../../../auto/quick/shared/viewtestutil.h" + + +class TestView : public QQuickView +{ +public: + void handleEvent(QEvent *ev) { event(ev); } +}; + + +class tst_events : public QQmlDataTest +{ + Q_OBJECT + +private slots: + void mousePressRelease(); + void mouseMove(); + void touchToMousePressRelease(); + void touchToMousePressMove(); + +public slots: + void initTestCase() { + QQmlDataTest::initTestCase(); + window.setBaseSize(QSize(400, 400)); + window.setSource(testFileUrl("mouseevent.qml")); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + } + +private: + TestView window; +}; + +void tst_events::mousePressRelease() +{ + QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea"); + QCOMPARE(mouseArea->pressed(), false); + + QBENCHMARK { + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0); + window.handleEvent(&pressEvent); + QCOMPARE(mouseArea->pressed(), true); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0); + window.handleEvent(&releaseEvent); + } + QCOMPARE(mouseArea->pressed(), false); +} + +void tst_events::mouseMove() +{ + QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea"); + QCOMPARE(mouseArea->pressed(), false); + + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0); + window.handleEvent(&pressEvent); + QCOMPARE(mouseArea->pressed(), true); + QMouseEvent moveEvent1(QEvent::MouseMove, QPoint(101, 100), Qt::LeftButton, Qt::LeftButton, 0); + QMouseEvent moveEvent2(QEvent::MouseMove, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0); + QBENCHMARK { + window.handleEvent(&moveEvent1); + window.handleEvent(&moveEvent2); + } + QCOMPARE(mouseArea->pressed(), true); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0); + window.handleEvent(&releaseEvent); + QCOMPARE(mouseArea->pressed(), false); +} + +void tst_events::touchToMousePressRelease() +{ + QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea"); + QCOMPARE(mouseArea->pressed(), false); + + auto device = QTest::createTouchDevice(); + auto p = QPoint(80, 80); + + QBENCHMARK { + QTest::touchEvent(&window, device).press(0, p, &window).commit(); + QCOMPARE(mouseArea->pressed(), true); + QTest::touchEvent(&window, device).release(0, p, &window).commit(); + } + QCOMPARE(mouseArea->pressed(), false); +} + +void tst_events::touchToMousePressMove() +{ + QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea"); + QCOMPARE(mouseArea->pressed(), false); + + auto device = QTest::createTouchDevice(); + auto p = QPoint(80, 80); + auto p2 = QPoint(81, 80); + + QTest::touchEvent(&window, device).press(0, p, &window).commit(); + QQuickTouchUtils::flush(&window); + QCOMPARE(mouseArea->pressed(), true); + + QBENCHMARK { + QTest::touchEvent(&window, device).move(0, p, &window).commit(); + QCOMPARE(mouseArea->pressed(), true); + QTest::touchEvent(&window, device).move(0, p2, &window).commit(); + } + QCOMPARE(mouseArea->pressed(), true); + QTest::touchEvent(&window, device).release(0, p, &window).commit(); + QQuickTouchUtils::flush(&window); + QCOMPARE(mouseArea->pressed(), false); +} + +QTEST_MAIN(tst_events) +#include "tst_events.moc" diff --git a/tests/benchmarks/quick/quick.pro b/tests/benchmarks/quick/quick.pro new file mode 100644 index 0000000000..87df78bd2f --- /dev/null +++ b/tests/benchmarks/quick/quick.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + events |