ListView/GridView contentHeight should include delayRemove-d items
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. Change-Id: Id839e850367a3503123e8ac81dac6ebdccef1a1f Reviewed-by: Martin Jones <martin.jones@qinetic.com.au>
This commit is contained in:
parent
93c1b8cc6b
commit
2869aa5bc5
|
@ -267,9 +267,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;
|
||||
}
|
||||
|
|
|
@ -1344,7 +1344,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);
|
||||
}
|
||||
|
@ -1808,7 +1808,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
|
||||
|
@ -1826,6 +1826,7 @@ void QQuickItemViewPrivate::layout()
|
|||
if (!isValid() && !visibleItems.count()) {
|
||||
clear();
|
||||
setPosition(contentStartOffset());
|
||||
updateViewport();
|
||||
if (transitioner)
|
||||
transitioner->setPopulateTransitionEnabled(false);
|
||||
inLayout = false;
|
||||
|
|
|
@ -427,14 +427,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);
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -206,6 +206,9 @@ private slots:
|
|||
|
||||
void jsArrayChange();
|
||||
|
||||
void contentHeightWithDelayRemove_data();
|
||||
void contentHeightWithDelayRemove();
|
||||
|
||||
private:
|
||||
QList<int> toIntList(const QVariantList &list);
|
||||
void matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes);
|
||||
|
@ -6471,6 +6474,74 @@ void tst_QQuickGridView::jsArrayChange()
|
|||
QCOMPARE(spy.count(), 1);
|
||||
}
|
||||
|
||||
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"
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -242,6 +242,9 @@ private slots:
|
|||
void jsArrayChange();
|
||||
void objectModel();
|
||||
|
||||
void contentHeightWithDelayRemove();
|
||||
void contentHeightWithDelayRemove_data();
|
||||
|
||||
private:
|
||||
template <class T> void items(const QUrl &source);
|
||||
template <class T> void changed(const QUrl &source);
|
||||
|
@ -8063,6 +8066,74 @@ void tst_QQuickListView::objectModel()
|
|||
delete listview;
|
||||
}
|
||||
|
||||
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"
|
||||
|
|
Loading…
Reference in New Issue