diff options
author | Liang Qi <liang.qi@theqtcompany.com> | 2016-02-19 12:49:16 +0100 |
---|---|---|
committer | Liang Qi <liang.qi@theqtcompany.com> | 2016-02-19 12:59:04 +0100 |
commit | 4e7f570f921671c627040537b4dd8cdb77bda3d1 (patch) | |
tree | 7d82e1b6536d5274ab29a3e6307fedcbefd3ce78 | |
parent | 72447b6dbf98ace65ec46559337243970a668d26 (diff) | |
parent | 39df6760e4db6af700c46a420286b51113b5dadb (diff) |
Merge remote-tracking branch 'origin/5.6' into 5.7
Conflicts:
src/quick/items/qquickitem.cpp
tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
Change-Id: I3cf47faa2fe567d62fffd985aeecbefe5811cc42
27 files changed, 500 insertions, 138 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 8aae4dff1e..f529b4bc7d 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -336,6 +336,8 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje } else if (property->isV4Function()) { Scope scope(engine); ScopedContext global(scope, engine->qmlContext()); + if (!global) + global = engine->rootContext(); return QV4::QObjectMethod::create(global, object, property->coreIndex); } else if (property->isSignalHandler()) { QmlSignalHandler::initProto(engine); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 0e99e4ca89..1348151b0d 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -606,8 +606,8 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) workerScriptEngine(0), activeObjectCreator(0), networkAccessManager(0), networkAccessManagerFactory(0), urlInterceptor(0), - scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), - incubatorCount(0), incubationController(0) + scarceResourcesRefCount(0), importDatabase(e), typeLoader(e), + uniqueId(1), incubatorCount(0), incubationController(0) { } diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index e56a3fc57f..88518ac749 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -172,9 +172,8 @@ public: void referenceScarceResources(); void dereferenceScarceResources(); - QQmlTypeLoader typeLoader; QQmlImportDatabase importDatabase; - + QQmlTypeLoader typeLoader; QString offlineStoragePath; diff --git a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc index dffcabbd5b..cb281a2d4a 100644 --- a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc +++ b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc @@ -102,7 +102,7 @@ has changed. If the QList changes, it is necessary to reset the model by calling QQmlContext::setContextProperty() again. -\section2 QAbstractItemModel +\section2 QAbstractItemModel subclass A model can be defined by subclassing QAbstractItemModel. This is the best approach if you have a more complex model that cannot be supported diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 421c9762ff..da48fd869d 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -182,6 +182,8 @@ public: bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer) Q_DECL_OVERRIDE; bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) Q_DECL_OVERRIDE; + void removeItem(FxViewItem *item); + FxViewItem *newViewItem(int index, QQuickItem *item) Q_DECL_OVERRIDE; void initializeViewItem(FxViewItem *item) Q_DECL_OVERRIDE; void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer) Q_DECL_OVERRIDE; @@ -567,6 +569,17 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal return changed; } +void QQuickGridViewPrivate::removeItem(FxViewItem *item) +{ + if (item->transitionScheduledOrRunning()) { + qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item:" << item->index << item->item->objectName(); + item->releaseAfterTransition = true; + releasePendingTransition.append(item); + } else { + releaseItem(item); + } +} + bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) { FxGridItemSG *item = 0; @@ -581,13 +594,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer if (item->index != -1) visibleIndex++; visibleItems.removeFirst(); - if (item->transitionScheduledOrRunning()) { - qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item:" << item->index << item->item->objectName(); - item->releaseAfterTransition = true; - releasePendingTransition.append(item); - } else { - releaseItem(item); - } + removeItem(item); changed = true; } while (visibleItems.count() > 1 @@ -597,13 +604,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer break; qCDebug(lcItemViewDelegateLifecycle) << "refill: remove last" << visibleIndex+visibleItems.count()-1; visibleItems.removeLast(); - if (item->transitionScheduledOrRunning()) { - qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item:" << item->index << item->item->objectName(); - item->releaseAfterTransition = true; - releasePendingTransition.append(item); - } else { - releaseItem(item); - } + removeItem(item); changed = true; } @@ -2406,11 +2407,12 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch int i = count - 1; int from = tempPos - buffer - displayMarginBeginning; - while (i >= 0) { - if (rowPos > from && insertionIdx < visibleIndex) { - // item won't be visible, just note the size for repositioning - insertResult->countChangeBeforeVisible++; - } else { + if (rowPos > from && insertionIdx < visibleIndex) { + // items won't be visible, just note the size for repositioning + insertResult->countChangeBeforeVisible += count; + insertResult->sizeChangesBeforeVisiblePos += ((count + columns - 1) / columns) * rowSize(); + } else { + while (i >= 0) { // item is before first visible e.g. in cache buffer FxViewItem *item = 0; if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) @@ -2426,19 +2428,40 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch insertResult->changedFirstItem = true; if (!change.isMove()) { addedItems->append(item); - item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true); + if (transitioner) + item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true); + else + item->moveTo(QPointF(colPos, rowPos), true); } insertResult->sizeChangesBeforeVisiblePos += rowSize(); + + if (--colNum < 0 ) { + colNum = columns - 1; + rowPos -= rowSize(); + } + colPos = colNum * colSize(); + index++; + i--; } + } - if (--colNum < 0 ) { - colNum = columns - 1; - rowPos -= rowSize(); + // There may be gaps in the index sequence of visibleItems because + // of the index shift/update done before the insertion just above. + // Find if there is any... + int firstOkIdx = -1; + for (int i = 0; i <= insertionIdx && i < visibleItems.count() - 1; i++) { + if (visibleItems.at(i)->index + 1 != visibleItems.at(i + 1)->index) { + firstOkIdx = i + 1; + break; } - colPos = colNum * colSize(); - index++; - i--; } + // ... and remove all the items before that one + for (int i = 0; i < firstOkIdx; i++) { + FxViewItem *nvItem = visibleItems.takeFirst(); + addedItems->removeOne(nvItem); + removeItem(nvItem); + } + } else { int i = 0; int to = buffer+displayMarginEnd+tempPos+size()-1; @@ -2463,7 +2486,10 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch movingIntoView->append(MovedItem(item, change.moveKey(item->index))); } else { addedItems->append(item); - item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true); + if (transitioner) + item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true); + else + item->moveTo(QPointF(colPos, rowPos), true); } insertResult->sizeChangesAfterVisiblePos += rowSize(); diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 8715fa2772..4bd95d5520 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2074,6 +2074,10 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus) \value ItemRotationHasChanged The item's rotation has changed. ItemChangeData::realValue contains the new rotation. + + \value ItemDevicePixelRatioHasChanged The device pixel ratio of the screen + the item is on has changed. ItemChangedData::realValue contains the new + device pixel ratio. */ /*! @@ -2477,6 +2481,7 @@ QQuickItem *QQuickItemPrivate::prevTabChildItem(const QQuickItem *item, int star QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward) { Q_ASSERT(item); + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: item:" << item << ", forward:" << forward; if (!item->window()) return item; @@ -2487,19 +2492,25 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo bool all = QGuiApplication::styleHints()->tabFocusBehavior() == Qt::TabFocusAllControls; QQuickItem *from = 0; + bool isTabFence = item->d_func()->isTabFence; if (forward) { - from = item->parentItem(); + if (!isTabFence) + from = item->parentItem(); } else { if (!item->childItems().isEmpty()) from = item->d_func()->childItems.constFirst(); - else + else if (!isTabFence) from = item->parentItem(); } bool skip = false; QQuickItem * startItem = item; QQuickItem * firstFromItem = from; QQuickItem *current = item; + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem; + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem; do { + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: current:" << current; + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: from:" << from; skip = false; QQuickItem *last = current; @@ -2513,7 +2524,7 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo else lastChild = prevTabChildItem(current, -1); } - bool isTabFence = current->d_func()->isTabFence; + isTabFence = current->d_func()->isTabFence; if (isTabFence && !hasChildren) return current; @@ -2568,9 +2579,14 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo return startItem; } } - if (!firstFromItem) { //start from root - startItem = current; - firstFromItem = from; + if (!firstFromItem) { + if (startItem->d_func()->isTabFence) { + if (current == startItem) + firstFromItem = from; + } else { //start from root + startItem = current; + firstFromItem = from; + } } } while (skip || !current->activeFocusOnTab() || !current->isEnabled() || !current->isVisible() || !(all || QQuickItemPrivate::canAcceptTabFocus(current))); @@ -5968,6 +5984,8 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt } break; case QQuickItem::ItemAntialiasingHasChanged: + // fall through + case QQuickItem::ItemDevicePixelRatioHasChanged: q->itemChange(change, data); break; } diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 8795c71b38..b33f3d8b6a 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -172,7 +172,8 @@ public: ItemOpacityHasChanged, // value.realValue ItemActiveFocusHasChanged, // value.boolValue ItemRotationHasChanged, // value.realValue - ItemAntialiasingHasChanged // value.boolValue + ItemAntialiasingHasChanged, // value.boolValue + ItemDevicePixelRatioHasChanged // value.realValue }; union ItemChangeData { diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 0f7f1961a1..f3254cf8d7 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -1465,7 +1465,8 @@ void QQuickText::itemChange(ItemChange change, const ItemChangeData &value) { Q_D(QQuickText); Q_UNUSED(value); - if (change == ItemAntialiasingHasChanged) { + switch (change) { + case ItemAntialiasingHasChanged: if (!antialiasing()) d->font.setStyleStrategy(QFont::NoAntialias); else @@ -1473,6 +1474,22 @@ void QQuickText::itemChange(ItemChange change, const ItemChangeData &value) d->implicitWidthValid = false; d->implicitHeightValid = false; d->updateLayout(); + break; + + case ItemDevicePixelRatioHasChanged: + if (d->renderType == NativeRendering) { + // Native rendering optimizes for a given pixel grid, so its results must not be scaled. + // Text layout code respects the current device pixel ratio automatically, we only need + // to rerun layout after the ratio changed. + // Changes of implicit size should be minimal; they are hard to avoid. + d->implicitWidthValid = false; + d->implicitHeightValid = false; + d->updateLayout(); + } + break; + + default: + break; } QQuickItem::itemChange(change, value); } diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index 34b9d6efa2..2872c3e230 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -828,14 +828,15 @@ void QQuickTextNodeEngine::addToSceneGraph(QQuickTextNode *parentNode, for (int i = 0; i < node->ranges.size(); ++i) { const QPair<int, int> &range = node->ranges.at(i); - int rangeLength = range.second - range.first; + int rangeLength = range.second - range.first + 1; if (previousNode != 0) { for (int j = 0; j < previousNode->ranges.size(); ++j) { const QPair<int, int> &otherRange = previousNode->ranges.at(j); + if (range.first < otherRange.second && range.second > otherRange.first) { int start = qMax(range.first, otherRange.first); int end = qMin(range.second, otherRange.second); - rangeLength -= end - start; + rangeLength -= end - start + 1; if (rangeLength == 0) break; } @@ -849,7 +850,7 @@ void QQuickTextNodeEngine::addToSceneGraph(QQuickTextNode *parentNode, if (range.first < otherRange.second && range.second > otherRange.first) { int start = qMax(range.first, otherRange.first); int end = qMin(range.second, otherRange.second); - rangeLength -= end - start; + rangeLength -= end - start + 1; if (rangeLength == 0) break; } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 10f4fb20fc..44ff9aef81 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -294,10 +294,43 @@ void QQuickWindow::update() QQuickRenderControlPrivate::get(d->renderControl)->update(); } +static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio) +{ + if (item->flags() & QQuickItem::ItemHasContents) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->itemChange(QQuickItem::ItemDevicePixelRatioHasChanged, pixelRatio); + } + + QList <QQuickItem *> items = item->childItems(); + for (int i = 0; i < items.size(); ++i) + updatePixelRatioHelper(items.at(i), pixelRatio); +} + +void QQuickWindow::physicalDpiChanged() +{ + Q_D(QQuickWindow); + const qreal newPixelRatio = screen()->devicePixelRatio(); + if (qFuzzyCompare(newPixelRatio, d->devicePixelRatio)) + return; + d->devicePixelRatio = newPixelRatio; + if (d->contentItem) + updatePixelRatioHelper(d->contentItem, newPixelRatio); +} + void QQuickWindow::handleScreenChanged(QScreen *screen) { Q_D(QQuickWindow); - Q_UNUSED(screen) + if (screen) { + physicalDpiChanged(); + // When physical DPI changes on the same screen, either the resolution or the device pixel + // ratio changed. We must check what it is. Device pixel ratio does not have its own + // ...Changed() signal. + d->physicalDpiChangedConnection = connect(screen, SIGNAL(physicalDotsPerInchChanged(qreal)), + this, SLOT(physicalDpiChanged())); + } else { + disconnect(d->physicalDpiChangedConnection); + } + d->forcePolish(); } @@ -416,6 +449,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , touchMouseId(-1) , touchMousePressTimestamp(0) , dirtyItemList(0) + , devicePixelRatio(0) , context(0) , renderer(0) , windowManager(0) @@ -470,6 +504,9 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) Q_ASSERT(windowManager || renderControl); + if (QScreen *screen = q->screen()) + devicePixelRatio = screen->devicePixelRatio(); + QSGContext *sg; if (renderControl) { QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl); diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index 262828ad1d..e04a4a1ce2 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -202,6 +202,7 @@ protected: private Q_SLOTS: void maybeUpdate(); void cleanupSceneGraph(); + void physicalDpiChanged(); void handleScreenChanged(QScreen *screen); void setTransientParent_helper(QQuickWindow *window); void runJobsAfterSwap(); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 652320c1c9..1064be7178 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -211,6 +211,9 @@ public: QVector<QQuickItem *> itemsToPolish; + qreal devicePixelRatio; + QMetaObject::Connection physicalDpiChangedConnection; + void updateDirtyNodes(); void cleanupNodes(); void cleanupNodesOnShutdown(); diff --git a/src/quick/quick.pro b/src/quick/quick.pro index 4ff2d76a09..c8aa47d939 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -29,7 +29,7 @@ load(qt_module) include(util/util.pri) include(scenegraph/scenegraph.pri) include(items/items.pri) -include(designer/designer.pri) +!wince:include(designer/designer.pri) contains(QT_CONFIG, accessibility) { include(accessible/accessible.pri) } diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index d669a7feda..7d9997b9c8 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -1194,8 +1194,6 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q *ok = true; return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, autoTransform, appliedTransform); } - errorString = QQuickPixmap::tr("Invalid image data: %1").arg(url.toString()); - } else { errorString = QQuickPixmap::tr("Cannot open: %1").arg(url.toString()); } diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index fe3efcd948..253b050b15 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -185,6 +185,8 @@ private slots: void argumentEvaluationOrder(); + void v4FunctionWithoutQML(); + signals: void testSignal(); }; @@ -3794,6 +3796,30 @@ void tst_QJSEngine::argumentEvaluationOrder() } +class TestObject : public QObject +{ + Q_OBJECT +public: + TestObject() : called(false) {} + + bool called; + + Q_INVOKABLE void callMe(QQmlV4Function *) { + called = true; + } +}; + +void tst_QJSEngine::v4FunctionWithoutQML() +{ + TestObject obj; + QJSEngine engine; + QJSValue wrapper = engine.newQObject(&obj); + QQmlEngine::setObjectOwnership(&obj, QQmlEngine::CppOwnership); + QVERIFY(!obj.called); + wrapper.property("callMe").call(); + QVERIFY(obj.called); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/quick/qquickgridview/data/qtbug48870.qml b/tests/auto/quick/qquickgridview/data/qtbug48870.qml new file mode 100644 index 0000000000..7c0783fbb5 --- /dev/null +++ b/tests/auto/quick/qquickgridview/data/qtbug48870.qml @@ -0,0 +1,30 @@ +import QtQuick 2.6 + +Rectangle { + width: 500 + height: 500 + color: "blue" + + GridView { + id: view + objectName: "view" + anchors.fill: parent + model: testModel + cellWidth: 150 + cellHeight: 150 + readonly property int columns: Math.floor(width / cellWidth) + + delegate: Rectangle { + width: GridView.view.cellWidth + height: GridView.view.cellHeight + color: (row & 1) != (col & 1) ? "green" : "red" + readonly property int row: index / view.columns + readonly property int col: index % view.columns + + Text { + anchors.centerIn: parent + text: "Item " + index + } + } + } +} diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index b3474db7de..07c03a57d7 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -36,6 +36,7 @@ #include <QtQml/qqmlincubator.h> #include <QtQml/qqmlcontext.h> #include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickitemview_p_p.h> #include <QtQuick/private/qquickgridview_p.h> #include <QtQuick/private/qquicktext_p.h> #include <QtQml/private/qqmllistmodel_p.h> @@ -205,6 +206,7 @@ private slots: void contentHeightWithDelayRemove(); void QTBUG_45640(); + void QTBUG_48870_fastModelUpdates(); void keyNavigationEnabled(); @@ -6628,6 +6630,42 @@ void tst_QQuickGridView::keyNavigationEnabled() QCOMPARE(gridView->currentIndex(), 1); } +void tst_QQuickGridView::QTBUG_48870_fastModelUpdates() +{ + StressTestModel model; + + QScopedPointer<QQuickView> window(createView()); + QQmlContext *ctxt = window->rootContext(); + ctxt->setContextProperty("testModel", &model); + + window->setSource(testFileUrl("qtbug48870.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickGridView *view = findItem<QQuickGridView>(window->rootObject(), "view"); + QTRY_VERIFY(view != 0); + + QQuickItemViewPrivate *priv = QQuickItemViewPrivate::get(view); + bool nonUnique; + FxViewItem *item = Q_NULLPTR; + int expectedIdx; + QVERIFY(testVisibleItems(priv, &nonUnique, &item, &expectedIdx)); + + for (int i = 0; i < 10; i++) { + QTest::qWait(100); + QVERIFY2(testVisibleItems(priv, &nonUnique, &item, &expectedIdx), + qPrintable(!item ? QString("Unexpected null item") + : nonUnique ? QString("Non-unique item at %1 and %2").arg(item->index).arg(expectedIdx) + : QString("Found index %1, expected index is %3").arg(item->index).arg(expectedIdx))); + if (i % 3 != 0) { + if (i & 1) + flick(window.data(), QPoint(100, 200), QPoint(100, 0), 100); + else + flick(window.data(), QPoint(100, 200), QPoint(100, 400), 100); + } + } +} + QTEST_MAIN(tst_QQuickGridView) #include "tst_qquickgridview.moc" diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_1.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_1.qml new file mode 100644 index 0000000000..d8b7833467 --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_1.qml @@ -0,0 +1,13 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + Item { + objectName: "item" + focus: true + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_2.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_2.qml new file mode 100644 index 0000000000..445dc6d2a1 --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_2.qml @@ -0,0 +1,12 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + Item { + objectName: "item" + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_3.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_3.qml new file mode 100644 index 0000000000..806d48ebeb --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_3.qml @@ -0,0 +1,16 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + Item { + objectName: "item2" + focus: true + } + Item { + objectName: "item2" + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_4.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_4.qml new file mode 100644 index 0000000000..6fcf513b51 --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_4.qml @@ -0,0 +1,15 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + Item { + objectName: "item2" + } + Item { + objectName: "item2" + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_5.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_5.qml new file mode 100644 index 0000000000..4f7b68a8de --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_5.qml @@ -0,0 +1,16 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + TextInput { + objectName: "item1" + activeFocusOnTab: true + } + Item { + objectName: "item2" + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_6.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_6.qml new file mode 100644 index 0000000000..223476327a --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_6.qml @@ -0,0 +1,17 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + TextInput { + objectName: "item1" + activeFocusOnTab: true + } + TextInput { + objectName: "item2" + activeFocusOnTab: true + } +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 607dbccaed..b4131a2df8 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -70,6 +70,8 @@ private slots: void tabFence(); void qtbug_50516(); + void qtbug_50516_2_data(); + void qtbug_50516_2(); void keys(); void standardKeys_data(); @@ -305,6 +307,22 @@ public: QML_DECLARE_TYPE(TabFenceItem); +class TabFenceItem2 : public QQuickItem +{ + Q_OBJECT + +public: + TabFenceItem2(QQuickItem *parent = Q_NULLPTR) + : QQuickItem(parent) + { + QQuickItemPrivate *d = QQuickItemPrivate::get(this); + d->isTabFence = true; + setFlag(ItemIsFocusScope); + } +}; + +QML_DECLARE_TYPE(TabFenceItem2); + tst_QQuickItem::tst_QQuickItem() { } @@ -315,6 +333,7 @@ void tst_QQuickItem::initTestCase() qmlRegisterType<KeyTestItem>("Test",1,0,"KeyTestItem"); qmlRegisterType<HollowTestItem>("Test", 1, 0, "HollowTestItem"); qmlRegisterType<TabFenceItem>("Test", 1, 0, "TabFence"); + qmlRegisterType<TabFenceItem2>("Test", 1, 0, "TabFence2"); } void tst_QQuickItem::cleanup() @@ -1212,6 +1231,51 @@ void tst_QQuickItem::qtbug_50516() delete window; } +void tst_QQuickItem::qtbug_50516_2_data() +{ + QTest::addColumn<QString>("filename"); + QTest::addColumn<QString>("item1"); + QTest::addColumn<QString>("item2"); + + QTest::newRow("FocusScope TabFence with one Item(focused)") + << QStringLiteral("qtbug_50516_2_1.qml") << QStringLiteral("root") << QStringLiteral("root"); + QTest::newRow("FocusScope TabFence with one Item(unfocused)") + << QStringLiteral("qtbug_50516_2_2.qml") << QStringLiteral("root") << QStringLiteral("root"); + QTest::newRow("FocusScope TabFence with two Items(focused)") + << QStringLiteral("qtbug_50516_2_3.qml") << QStringLiteral("root") << QStringLiteral("root"); + QTest::newRow("FocusScope TabFence with two Items(unfocused)") + << QStringLiteral("qtbug_50516_2_4.qml") << QStringLiteral("root") << QStringLiteral("root"); + QTest::newRow("FocusScope TabFence with one Item and one TextInput(unfocused)") + << QStringLiteral("qtbug_50516_2_5.qml") << QStringLiteral("item1") << QStringLiteral("item1"); + QTest::newRow("FocusScope TabFence with two TextInputs(unfocused)") + << QStringLiteral("qtbug_50516_2_6.qml") << QStringLiteral("item1") << QStringLiteral("item2"); +} + +void tst_QQuickItem::qtbug_50516_2() +{ + QFETCH(QString, filename); + QFETCH(QString, item1); + QFETCH(QString, item2); + + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl(filename)); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + QVERIFY(window->rootObject()->hasActiveFocus()); + + QQuickItem *contentItem = window->rootObject(); + QQuickItem *next = contentItem->nextItemInFocusChain(true); + QCOMPARE(next->objectName(), item1); + next = contentItem->nextItemInFocusChain(false); + QCOMPARE(next->objectName(), item2); + + delete window; +} + void tst_QQuickItem::keys() { QQuickView *window = new QQuickView(0); diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 405acad165..658ffa1f57 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -8264,99 +8264,9 @@ void tst_QQuickListView::keyNavigationEnabled() QCOMPARE(listView->currentIndex(), 1); } -static bool testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx) -{ - QHash<QQuickItem*, int> uniqueItems; - - int skip = 0; - for (int i = 0; i < priv->visibleItems.count(); ++i) { - FxViewItem *item = priv->visibleItems.at(i); - if (!item) { - *failItem = Q_NULLPTR; - return false; - } -#if 0 - qDebug() << "\t" << item->index - << item->item - << item->position() - << (!item->item || QQuickItemPrivate::get(item->item)->culled ? "hidden" : "visible"); -#endif - if (item->index == -1) { - ++skip; - } else if (item->index != priv->visibleIndex + i - skip) { - *nonUnique = false; - *failItem = item; - *expectedIdx = priv->visibleIndex + i - skip; - return false; - } else if (uniqueItems.contains(item->item)) { - *nonUnique = true; - *failItem = item; - *expectedIdx = uniqueItems.find(item->item).value(); - return false; - } - - uniqueItems.insert(item->item, item->index); - } - - return true; -} - -class QTBUG_48870_Model : public QAbstractListModel -{ - Q_OBJECT - -public: - - QTBUG_48870_Model() - : QAbstractListModel() - , m_rowCount(20) - { - QTimer *t = new QTimer(this); - t->setInterval(500); - t->start(); - - qsrand(qHash(QDateTime::currentDateTime())); - connect(t, &QTimer::timeout, this, &QTBUG_48870_Model::updateModel); - } - - int rowCount(const QModelIndex &) const - { - return m_rowCount; - } - - QVariant data(const QModelIndex &, int) const - { - return QVariant(); - } - -public Q_SLOTS: - void updateModel() - { - if (m_rowCount > 10) { - for (int i = 0; i < 10; ++i) { - int rnum = qrand() % m_rowCount; - beginRemoveRows(QModelIndex(), rnum, rnum); - m_rowCount--; - endRemoveRows(); - } - } - if (m_rowCount < 20) { - for (int i = 0; i < 10; ++i) { - int rnum = qrand() % m_rowCount; - beginInsertRows(QModelIndex(), rnum, rnum); - m_rowCount++; - endInsertRows(); - } - } - } - -private: - int m_rowCount; -}; - void tst_QQuickListView::QTBUG_48870_fastModelUpdates() { - QTBUG_48870_Model model; + StressTestModel model; QScopedPointer<QQuickView> window(createView()); QQmlContext *ctxt = window->rootContext(); diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp index 09c88acbe6..ab58aee648 100644 --- a/tests/auto/quick/shared/viewtestutil.cpp +++ b/tests/auto/quick/shared/viewtestutil.cpp @@ -35,6 +35,7 @@ #include <QtTest/QTest> #include <private/qquickwindow_p.h> +#include <private/qquickitemview_p_p.h> QQuickView *QQuickViewTestUtil::createView() @@ -347,6 +348,85 @@ QList<QPair<QString,QString> > QQuickViewTestUtil::ListRange::getModelDataValues return data; } +QQuickViewTestUtil::StressTestModel::StressTestModel() + : QAbstractListModel() + , m_rowCount(20) +{ + QTimer *t = new QTimer(this); + t->setInterval(500); + t->start(); + + qsrand(qHash(QDateTime::currentDateTime())); + connect(t, &QTimer::timeout, this, &StressTestModel::updateModel); +} + +int QQuickViewTestUtil::StressTestModel::rowCount(const QModelIndex &) const +{ + return m_rowCount; +} + +QVariant QQuickViewTestUtil::StressTestModel::data(const QModelIndex &, int) const +{ + return QVariant(); +} + +void QQuickViewTestUtil::StressTestModel::updateModel() +{ + if (m_rowCount > 10) { + for (int i = 0; i < 10; ++i) { + int rnum = qrand() % m_rowCount; + beginRemoveRows(QModelIndex(), rnum, rnum); + m_rowCount--; + endRemoveRows(); + } + } + if (m_rowCount < 20) { + for (int i = 0; i < 10; ++i) { + int rnum = qrand() % m_rowCount; + beginInsertRows(QModelIndex(), rnum, rnum); + m_rowCount++; + endInsertRows(); + } + } +} + +bool QQuickViewTestUtil::testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx) +{ + QHash<QQuickItem*, int> uniqueItems; + + int skip = 0; + for (int i = 0; i < priv->visibleItems.count(); ++i) { + FxViewItem *item = priv->visibleItems.at(i); + if (!item) { + *failItem = Q_NULLPTR; + return false; + } +#if 0 + qDebug() << "\t" << item->index + << item->item + << item->position() + << (!item->item || QQuickItemPrivate::get(item->item)->culled ? "hidden" : "visible"); +#endif + if (item->index == -1) { + ++skip; + } else if (item->index != priv->visibleIndex + i - skip) { + *nonUnique = false; + *failItem = item; + *expectedIdx = priv->visibleIndex + i - skip; + return false; + } else if (uniqueItems.contains(item->item)) { + *nonUnique = true; + *failItem = item; + *expectedIdx = uniqueItems.find(item->item).value(); + return false; + } + + uniqueItems.insert(item->item, item->index); + } + + return true; +} + namespace QQuickTouchUtils { /* QQuickWindow does event compression and only delivers events just diff --git a/tests/auto/quick/shared/viewtestutil.h b/tests/auto/quick/shared/viewtestutil.h index 0f50180809..b11d5e4859 100644 --- a/tests/auto/quick/shared/viewtestutil.h +++ b/tests/auto/quick/shared/viewtestutil.h @@ -34,6 +34,8 @@ #include <QtCore/QAbstractListModel> QT_FORWARD_DECLARE_CLASS(QQuickView) +QT_FORWARD_DECLARE_CLASS(QQuickItemViewPrivate) +QT_FORWARD_DECLARE_CLASS(FxViewItem) namespace QQuickViewTestUtil { @@ -154,6 +156,26 @@ namespace QQuickViewTestUtil for (; f != replaced.end(); ++f, ++t) *t = *f; } + + class StressTestModel : public QAbstractListModel + { + Q_OBJECT + + public: + + StressTestModel(); + + int rowCount(const QModelIndex &) const; + QVariant data(const QModelIndex &, int) const; + + public Q_SLOTS: + void updateModel(); + + private: + int m_rowCount; + }; + + bool testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx); } namespace QQuickTouchUtils { |