Ensure items in ListView and GridViews viewport are visible.

Unrequested items and items in the cache buffer are hidden by the views.
Make these items visible again if they enter the viewport due to a
relayout.

Change-Id: I574d1513e88c31c3c01a4a20ac77c21111279d3e
Reviewed-by: Bea Lam <bea.lam@nokia.com>
This commit is contained in:
Andrew den Exter 2011-12-09 12:55:32 +10:00 committed by Qt by Nokia
parent f3c7d8c357
commit ac50bb1b85
7 changed files with 488 additions and 8 deletions

View File

@ -542,6 +542,9 @@ void QQuickGridViewPrivate::updateViewport()
void QQuickGridViewPrivate::layoutVisibleItems()
{
if (visibleItems.count()) {
const qreal from = isContentFlowReversed() ? -position() - size() : position();
const qreal to = isContentFlowReversed() ? -position() : position() + size();
FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
qreal rowPos = firstItem->rowPos();
qreal colPos = firstItem->colPos();
@ -549,6 +552,7 @@ void QQuickGridViewPrivate::layoutVisibleItems()
if (colPos != col * colSize()) {
colPos = col * colSize();
firstItem->setPosition(colPos, rowPos);
firstItem->item->setVisible(rowPos + rowSize() >= from && rowPos <= to);
}
for (int i = 1; i < visibleItems.count(); ++i) {
FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.at(i));
@ -558,6 +562,7 @@ void QQuickGridViewPrivate::layoutVisibleItems()
}
colPos = col * colSize();
item->setPosition(colPos, rowPos);
item->item->setVisible(rowPos + rowSize() >= from && rowPos <= to);
}
}
}

View File

@ -1651,6 +1651,7 @@ void QQuickItemViewPrivate::releaseItem(FxViewItem *item)
itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
if (model->release(item->item) == 0) {
// item was not destroyed, and we no longer reference it.
item->item->setVisible(false);
unrequestedItems.insert(item->item, model->indexOf(item->item, q));
}
delete item;

View File

@ -692,12 +692,18 @@ void QQuickListViewPrivate::visibleItemsChanged()
void QQuickListViewPrivate::layoutVisibleItems()
{
if (!visibleItems.isEmpty()) {
bool fixedCurrent = currentItem && (*visibleItems.constBegin())->item == currentItem->item;
qreal sum = (*visibleItems.constBegin())->size();
qreal pos = (*visibleItems.constBegin())->position() + (*visibleItems.constBegin())->size() + spacing;
const qreal from = isContentFlowReversed() ? -position() - size() : position();
const qreal to = isContentFlowReversed() ? -position() : position() + size();
FxViewItem *firstItem = *visibleItems.constBegin();
bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
qreal sum = firstItem->size();
qreal pos = firstItem->position() + firstItem->size() + spacing;
firstItem->item->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
for (int i=1; i < visibleItems.count(); ++i) {
FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
item->setPosition(pos);
item->item->setVisible(item->endPosition() >= from && item->position() <= to);
pos += item->size() + spacing;
sum += item->size();
fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
@ -2373,7 +2379,6 @@ bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
int modelIndex = change.index;
int count = change.count;
qreal tempPos = isRightToLeft() ? -position()-size() : position();
int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;

View File

@ -0,0 +1,67 @@
import QtQuick 2.0
Item {
width: 240
height: 320
Component {
id: myDelegate
Package {
Rectangle {
id: leftWrapper
objectName: "wrapper"
Package.name: "left"
height: 80
width: 60
Text {
text: index
}
color: ListView.isCurrentItem ? "lightsteelblue" : "white"
}
Rectangle {
id: rightWrapper
objectName: "wrapper"
Package.name: "right"
height: 80
width: 60
Text {
text: index
}
color: ListView.isCurrentItem ? "lightsteelblue" : "white"
}
}
}
VisualDataModel {
id: visualModel
delegate: myDelegate
model: testModel
}
GridView {
id: leftList
objectName: "leftGrid"
anchors {
left: parent.left; top: parent.top;
right: parent.horizontalCenter; bottom: parent.bottom
}
model: visualModel.parts.left
cellWidth: 60
cellHeight: 80
}
GridView {
id: rightList
objectName: "rightGrid"
anchors {
left: parent.horizontalCenter; top: parent.top;
right: parent.right; bottom: parent.bottom
}
model: visualModel.parts.right
cellWidth: 60
cellHeight: 80
}
}

View File

@ -120,6 +120,7 @@ private slots:
void unaligned();
void cacheBuffer();
void asynchronous();
void unrequestedVisibility();
private:
QQuickView *createView();
@ -3747,6 +3748,174 @@ void tst_QQuickGridView::asynchronous()
delete canvas;
}
void tst_QQuickGridView::unrequestedVisibility()
{
TestModel model;
for (int i = 0; i < 30; i++)
model.addItem("Item" + QString::number(i), QString::number(i));
QQuickView *canvas = new QQuickView(0);
canvas->setGeometry(0,0,240,320);
QDeclarativeContext *ctxt = canvas->rootContext();
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("testWrap", QVariant(false));
QString filename(TESTDATA("unrequestedItems.qml"));
canvas->setSource(QUrl::fromLocalFile(filename));
canvas->show();
qApp->processEvents();
QQuickGridView *leftview = findItem<QQuickGridView>(canvas->rootObject(), "leftGrid");
QTRY_VERIFY(leftview != 0);
QQuickGridView *rightview = findItem<QQuickGridView>(canvas->rootObject(), "rightGrid");
QTRY_VERIFY(rightview != 0);
QQuickItem *leftContent = leftview->contentItem();
QTRY_VERIFY(leftContent != 0);
QQuickItem *rightContent = rightview->contentItem();
QTRY_VERIFY(rightContent != 0);
rightview->setCurrentIndex(12);
QTRY_COMPARE(leftview->contentY(), 0.0);
QTRY_COMPARE(rightview->contentY(), 240.0);
QQuickItem *item;
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 9));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 10));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
QCOMPARE(item->isVisible(), true);
rightview->setCurrentIndex(0);
QTRY_COMPARE(leftview->contentY(), 0.0);
QTRY_COMPARE(rightview->contentY(), 0.0);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
QTRY_COMPARE(item->isVisible(), true);
QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 11));
QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 11));
leftview->setCurrentIndex(12);
QTRY_COMPARE(leftview->contentY(), 240.0);
QTRY_COMPARE(rightview->contentY(), 0.0);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QTRY_COMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
QCOMPARE(item->isVisible(), false);
model.moveItems(19, 1, 1);
QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
QCOMPARE(item->isVisible(), false);
model.moveItems(3, 4, 1);
QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 2));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
QCOMPARE(item->isVisible(), false);
model.moveItems(4, 5, 1);
QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 2));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
QCOMPARE(item->isVisible(), false);
model.moveItems(9, 10, 1);
QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 2));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
QCOMPARE(item->isVisible(), false);
model.moveItems(10, 9, 1);
QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 2));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
QCOMPARE(item->isVisible(), false);
delete canvas;
}
/*
Find an item with the specified objectName. If index is supplied then the
item must also evaluate the {index} expression equal to index

View File

@ -0,0 +1,63 @@
import QtQuick 2.0
Item {
width: 240
height: 320
Component {
id: myDelegate
Package {
Rectangle {
id: leftWrapper
objectName: "wrapper"
Package.name: "left"
height: 20
width: 120
Text {
text: index
}
color: ListView.isCurrentItem ? "lightsteelblue" : "white"
}
Rectangle {
id: rightWrapper
objectName: "wrapper"
Package.name: "right"
height: 20
width: 120
Text {
text: index
}
color: ListView.isCurrentItem ? "lightsteelblue" : "white"
}
}
}
VisualDataModel {
id: visualModel
delegate: myDelegate
model: testModel
}
ListView {
id: leftList
objectName: "leftList"
anchors {
left: parent.left; top: parent.top;
right: parent.horizontalCenter; bottom: parent.bottom
}
model: visualModel.parts.left
}
ListView {
id: rightList
objectName: "rightList"
anchors {
left: parent.horizontalCenter; top: parent.top;
right: parent.right; bottom: parent.bottom
}
model: visualModel.parts.right
}
}

View File

@ -160,6 +160,7 @@ private slots:
void QTBUG_21742();
void asynchronous();
void unrequestedVisibility();
private:
template <class T> void items(const QUrl &source, bool forceLayout);
@ -176,7 +177,7 @@ private:
template<typename T>
T *findItem(QQuickItem *parent, const QString &id, int index=-1);
template<typename T>
QList<T*> findItems(QQuickItem *parent, const QString &objectName);
QList<T*> findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly = true);
void dumpTree(QQuickItem *parent, int depth = 0);
void inserted_more_data();
@ -2849,7 +2850,7 @@ void tst_QQuickListView::QTBUG_9791()
qApp->processEvents();
// Confirm items positioned correctly
int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
int itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
QCOMPARE(itemCount, 3);
for (int i = 0; i < itemCount; ++i) {
@ -4483,6 +4484,175 @@ void tst_QQuickListView::snapOneItem()
delete canvas;
}
void tst_QQuickListView::unrequestedVisibility()
{
TestModel model;
for (int i = 0; i < 30; i++)
model.addItem("Item" + QString::number(i), QString::number(i));
QQuickView *canvas = new QQuickView(0);
canvas->setGeometry(0,0,240,320);
QDeclarativeContext *ctxt = canvas->rootContext();
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("testWrap", QVariant(false));
QString filename(TESTDATA("unrequestedItems.qml"));
canvas->setSource(QUrl::fromLocalFile(filename));
canvas->show();
qApp->processEvents();
QQuickListView *leftview = findItem<QQuickListView>(canvas->rootObject(), "leftList");
QTRY_VERIFY(leftview != 0);
QQuickListView *rightview = findItem<QQuickListView>(canvas->rootObject(), "rightList");
QTRY_VERIFY(rightview != 0);
QQuickItem *leftContent = leftview->contentItem();
QTRY_VERIFY(leftContent != 0);
QQuickItem *rightContent = rightview->contentItem();
QTRY_VERIFY(rightContent != 0);
rightview->setCurrentIndex(20);
QTRY_COMPARE(leftview->contentY(), 0.0);
QTRY_COMPARE(rightview->contentY(), 100.0);
QQuickItem *item;
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 16));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 17));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
QCOMPARE(item->isVisible(), true);
rightview->setCurrentIndex(0);
QTRY_COMPARE(leftview->contentY(), 0.0);
QTRY_COMPARE(rightview->contentY(), 0.0);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
QTRY_COMPARE(item->isVisible(), true);
QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 19));
QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 19));
leftview->setCurrentIndex(20);
QTRY_COMPARE(leftview->contentY(), 100.0);
QTRY_COMPARE(rightview->contentY(), 0.0);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QTRY_COMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
QCOMPARE(item->isVisible(), false);
model.moveItems(19, 1, 1);
QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
QCOMPARE(item->isVisible(), false);
model.moveItems(3, 4, 1);
QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
QCOMPARE(item->isVisible(), false);
model.moveItems(4, 3, 1);
QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
QCOMPARE(item->isVisible(), false);
model.moveItems(16, 17, 1);
QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
QCOMPARE(item->isVisible(), false);
model.moveItems(17, 16, 1);
QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
QCOMPARE(item->isVisible(), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 6));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
QCOMPARE(item->isVisible(), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
QCOMPARE(item->isVisible(), false);
delete canvas;
}
QQuickItem *tst_QQuickListView::findVisibleChild(QQuickItem *parent, const QString &objectName)
{
QQuickItem *item = 0;
@ -4527,14 +4697,14 @@ T *tst_QQuickListView::findItem(QQuickItem *parent, const QString &objectName, i
}
template<typename T>
QList<T*> tst_QQuickListView::findItems(QQuickItem *parent, const QString &objectName)
QList<T*> tst_QQuickListView::findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly)
{
QList<T*> items;
const QMetaObject &mo = T::staticMetaObject;
//qDebug() << parent->childItems().count() << "children";
for (int i = 0; i < parent->childItems().count(); ++i) {
QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
if (!item || !item->isVisible())
if (!item || (visibleOnly && !item->isVisible()))
continue;
//qDebug() << "try" << item;
if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))