Skip to content

Commit

Permalink
ListView/GridView contentHeight should include delayRemove-d items
Browse files Browse the repository at this point in the history
When one or more items are in delayRemove state, the ListView's
contentHeight property should include their height. This previously
failed if the delayRemove items were at the end of the visibleItems
list.  Also applies to GridView.
  • Loading branch information
matthewvogt committed Jun 23, 2015
1 parent 3343bb7 commit 195523f
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 8 deletions.
10 changes: 7 additions & 3 deletions src/quick/items/qquickgridview.cpp
Expand Up @@ -276,9 +276,13 @@ qreal QQuickGridViewPrivate::originPosition() const
qreal QQuickGridViewPrivate::lastPosition() const
{
qreal pos = 0;
if (model && model->count()) {
// get end position of last item
pos = (rowPosAt(model->count() - 1) + rowSize());
if (model && (model->count() || !visibleItems.isEmpty())) {
qreal lastRowPos = model->count() ? rowPosAt(model->count() - 1) : 0;
if (!visibleItems.isEmpty()) {
// If there are items in delayRemove state, they may be after any items linked to the model
lastRowPos = qMax(lastRowPos, static_cast<FxGridItemSG*>(visibleItems.last())->rowPos());
}
pos = lastRowPos + rowSize();
}
return pos;
}
Expand Down
5 changes: 3 additions & 2 deletions src/quick/items/qquickitemview.cpp
Expand Up @@ -1333,7 +1333,7 @@ void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &ol
{
Q_D(QQuickItemView);
d->markExtentsDirty();
if (isComponentComplete() && d->isValid())
if (isComponentComplete() && (d->isValid() || !d->visibleItems.isEmpty()))
d->forceLayoutPolish();
QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
}
Expand Down Expand Up @@ -1793,7 +1793,7 @@ void QQuickItemViewPrivate::updateViewport()
{
Q_Q(QQuickItemView);
qreal extra = headerSize() + footerSize();
qreal contentSize = isValid() ? (endPosition() - startPosition()) : 0.0;
qreal contentSize = isValid() || !visibleItems.isEmpty() ? (endPosition() - startPosition()) : 0.0;
if (layoutOrientation() == Qt::Vertical)
q->setContentHeight(contentSize + extra);
else
Expand All @@ -1811,6 +1811,7 @@ void QQuickItemViewPrivate::layout()
if (!isValid() && !visibleItems.count()) {
clear();
setPosition(contentStartOffset());
updateViewport();
if (transitioner)
transitioner->setPopulateTransitionEnabled(false);
inLayout = false;
Expand Down
16 changes: 13 additions & 3 deletions src/quick/items/qquicklistview.cpp
Expand Up @@ -432,14 +432,24 @@ qreal QQuickListViewPrivate::lastPosition() const
{
qreal pos = 0;
if (!visibleItems.isEmpty()) {
int invisibleCount = visibleItems.count() - visibleIndex;
int invisibleCount = INT_MIN;
int delayRemovedCount = 0;
for (int i = visibleItems.count()-1; i >= 0; --i) {
if (visibleItems.at(i)->index != -1) {
invisibleCount = model->count() - visibleItems.at(i)->index - 1;
// Find the invisible count after the last visible item with known index
invisibleCount = model->count() - (visibleItems.at(i)->index + 1 + delayRemovedCount);
break;
} else if (visibleItems.at(i)->attached->delayRemove()) {
++delayRemovedCount;
}
}
pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
if (invisibleCount == INT_MIN) {
// All visible items are in delayRemove state
invisibleCount = model->count();
}
pos = (*(--visibleItems.constEnd()))->endPosition();
if (invisibleCount > 0)
pos += invisibleCount * (averageSize + spacing);
} else if (model && model->count()) {
pos = (model->count() * averageSize + (model->count()-1) * spacing);
}
Expand Down
@@ -0,0 +1,47 @@
import QtQuick 2.1

Item {
width: 400
height: 600
function takeOne()
{
gridView.model.remove(2)
}
function takeThree()
{
gridView.model.remove(4)
gridView.model.remove(2)
gridView.model.remove(0)
}
function takeAll()
{
gridView.model.clear()
}

GridView {
id: gridView

property bool useDelayRemove

height: parent.height
width: 400
cellWidth: width/2
model: ListModel {
ListElement { name: "A" }
ListElement { name: "B" }
ListElement { name: "C" }
ListElement { name: "D" }
ListElement { name: "E" }
}
delegate: Text {
id: wrapper
height: 100
text: index + gridView.count
GridView.delayRemove: gridView.useDelayRemove
GridView.onRemove: SequentialAnimation {
PauseAnimation { duration: wrapper.GridView.delayRemove ? 100 : 0 }
PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: false }
}
}
}
}
71 changes: 71 additions & 0 deletions tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
Expand Up @@ -209,6 +209,9 @@ private slots:

void displayMargin();

void contentHeightWithDelayRemove_data();
void contentHeightWithDelayRemove();

private:
QList<int> toIntList(const QVariantList &list);
void matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes);
Expand Down Expand Up @@ -6346,6 +6349,74 @@ void tst_QQuickGridView::displayMargin()
delete window;
}

void tst_QQuickGridView::contentHeightWithDelayRemove_data()
{
QTest::addColumn<bool>("useDelayRemove");
QTest::addColumn<QByteArray>("removeFunc");
QTest::addColumn<int>("countDelta");
QTest::addColumn<qreal>("contentHeightDelta");

QTest::newRow("remove without delayRemove")
<< false
<< QByteArray("takeOne")
<< -1
<< qreal(-1 * 100.0);

QTest::newRow("remove with delayRemove")
<< true
<< QByteArray("takeOne")
<< -1
<< qreal(-1 * 100.0);

QTest::newRow("remove with multiple delayRemove")
<< true
<< QByteArray("takeThree")
<< -3
<< qreal(-2 * 100.0);

QTest::newRow("clear with delayRemove")
<< true
<< QByteArray("takeAll")
<< -5
<< qreal(-3 * 100.0);
}

void tst_QQuickGridView::contentHeightWithDelayRemove()
{
QFETCH(bool, useDelayRemove);
QFETCH(QByteArray, removeFunc);
QFETCH(int, countDelta);
QFETCH(qreal, contentHeightDelta);

QQuickView *window = createView();
window->setSource(testFileUrl("contentHeightWithDelayRemove.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));

QQuickGridView *gridview = window->rootObject()->findChild<QQuickGridView*>();
QTRY_VERIFY(gridview != 0);

const int initialCount(gridview->count());
const int eventualCount(initialCount + countDelta);

const qreal initialContentHeight(gridview->contentHeight());
const int eventualContentHeight(qRound(initialContentHeight + contentHeightDelta));

gridview->setProperty("useDelayRemove", useDelayRemove);
QMetaObject::invokeMethod(window->rootObject(), removeFunc.constData());
QTest::qWait(50);
QCOMPARE(gridview->count(), eventualCount);

if (useDelayRemove) {
QCOMPARE(qRound(gridview->contentHeight()), qRound(initialContentHeight));
QTRY_COMPARE(qRound(gridview->contentHeight()), eventualContentHeight);
} else {
QCOMPARE(qRound(gridview->contentHeight()), eventualContentHeight);
}

delete window;
}

QTEST_MAIN(tst_QQuickGridView)

#include "tst_qquickgridview.moc"
Expand Down
@@ -0,0 +1,46 @@
import QtQuick 2.1

Item {
width: 400
height: 600
function takeOne()
{
listView.model.remove(2)
}
function takeThree()
{
listView.model.remove(4)
listView.model.remove(2)
listView.model.remove(0)
}
function takeAll()
{
listView.model.clear()
}

ListView {
id: listView

property bool useDelayRemove

height: parent.height
width: 400
model: ListModel {
ListElement { name: "A" }
ListElement { name: "B" }
ListElement { name: "C" }
ListElement { name: "D" }
ListElement { name: "E" }
}
delegate: Text {
id: wrapper
height: 100
text: index + listView.count
ListView.delayRemove: listView.useDelayRemove
ListView.onRemove: SequentialAnimation {
PauseAnimation { duration: wrapper.ListView.delayRemove ? 100 : 0 }
PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: false }
}
}
}
}
71 changes: 71 additions & 0 deletions tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
Expand Up @@ -231,6 +231,9 @@ private slots:

void layoutChange();

void contentHeightWithDelayRemove();
void contentHeightWithDelayRemove_data();

private:
template <class T> void items(const QUrl &source);
template <class T> void changed(const QUrl &source);
Expand Down Expand Up @@ -7402,6 +7405,74 @@ void tst_QQuickListView::layoutChange()
}
}

void tst_QQuickListView::contentHeightWithDelayRemove_data()
{
QTest::addColumn<bool>("useDelayRemove");
QTest::addColumn<QByteArray>("removeFunc");
QTest::addColumn<int>("countDelta");
QTest::addColumn<qreal>("contentHeightDelta");

QTest::newRow("remove without delayRemove")
<< false
<< QByteArray("takeOne")
<< -1
<< qreal(-1 * 100.0);

QTest::newRow("remove with delayRemove")
<< true
<< QByteArray("takeOne")
<< -1
<< qreal(-1 * 100.0);

QTest::newRow("remove with multiple delayRemove")
<< true
<< QByteArray("takeThree")
<< -3
<< qreal(-3 * 100.0);

QTest::newRow("clear with delayRemove")
<< true
<< QByteArray("takeAll")
<< -5
<< qreal(-5 * 100.0);
}

void tst_QQuickListView::contentHeightWithDelayRemove()
{
QFETCH(bool, useDelayRemove);
QFETCH(QByteArray, removeFunc);
QFETCH(int, countDelta);
QFETCH(qreal, contentHeightDelta);

QQuickView *window = createView();
window->setSource(testFileUrl("contentHeightWithDelayRemove.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));

QQuickListView *listview = window->rootObject()->findChild<QQuickListView*>();
QTRY_VERIFY(listview != 0);

const int initialCount(listview->count());
const int eventualCount(initialCount + countDelta);

const qreal initialContentHeight(listview->contentHeight());
const int eventualContentHeight(qRound(initialContentHeight + contentHeightDelta));

listview->setProperty("useDelayRemove", useDelayRemove);
QMetaObject::invokeMethod(window->rootObject(), removeFunc.constData());
QTest::qWait(50);
QCOMPARE(listview->count(), eventualCount);

if (useDelayRemove) {
QCOMPARE(qRound(listview->contentHeight()), qRound(initialContentHeight));
QTRY_COMPARE(qRound(listview->contentHeight()), eventualContentHeight);
} else {
QCOMPARE(qRound(listview->contentHeight()), eventualContentHeight);
}

delete window;
}

QTEST_MAIN(tst_QQuickListView)

#include "tst_qquicklistview.moc"

0 comments on commit 195523f

Please sign in to comment.