int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns;
return static_cast<FxGridItemSG*>(visibleItems.last())->colPos() - count * colSize();
}
- } else {
- return (modelIndex % columns) * colSize();
}
- return 0;
+ return (modelIndex % columns) * colSize();
}
qreal QSGGridViewPrivate::rowPosAt(int modelIndex) const
int rows = col / (columns * colSize());
return lastItem->rowPos() + rows * rowSize();
}
- } else {
- qreal pos = (modelIndex / columns) * rowSize();
- if (header)
- pos += headerSize();
- return pos;
}
- return 0;
+ return (modelIndex / columns) * rowSize();
}
FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(footer);
qreal colOffset = 0;
- qreal rowOffset;
- if (isRightToLeftTopToBottom()) {
- rowOffset = gridItem->item->width()-cellWidth;
- } else {
- rowOffset = 0;
- if (q->effectiveLayoutDirection() == Qt::RightToLeft)
- colOffset = gridItem->item->width()-cellWidth;
+ qreal rowOffset = 0;
+ if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
+ if (flow == QSGGridView::TopToBottom)
+ rowOffset = gridItem->item->width() - cellWidth;
+ else
+ colOffset = gridItem->item->width() - cellWidth;
}
if (visibleItems.count()) {
qreal endPos = lastPosition() + 1;
gridItem->setPosition(colOffset, endPos + rowOffset);
}
} else {
- qreal endPos = 0;
- if (header) {
- endPos += headerSize();
- }
- gridItem->setPosition(colOffset, endPos);
+ gridItem->setPosition(colOffset, rowOffset);
}
}
FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(header);
qreal colOffset = 0;
- qreal rowOffset;
- if (isRightToLeftTopToBottom()) {
- rowOffset = -cellWidth;
- } else {
- rowOffset = -headerSize();
- if (q->effectiveLayoutDirection() == Qt::RightToLeft)
+ qreal rowOffset = -headerSize();
+ if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
+ if (flow == QSGGridView::TopToBottom)
+ rowOffset += gridItem->item->width()-cellWidth;
+ else
colOffset = gridItem->item->width()-cellWidth;
}
if (visibleItems.count()) {
gridItem->setPosition(colOffset, startPos + rowOffset);
}
} else {
- gridItem->setPosition(colOffset, 0);
+ if (isRightToLeftTopToBottom())
+ gridItem->setPosition(colOffset, rowOffset);
+ else
+ gridItem->setPosition(colOffset, -headerSize());
}
}
rowPos += d->rowSize();
}
}
- } else if (d->itemCount == 0 && d->header) {
- rowPos = d->headerSize();
}
// Update the indexes of the following visible items.
if (removedVisible && d->visibleItems.isEmpty()) {
d->timeline.clear();
if (d->itemCount == 0) {
- d->setPosition(0);
+ d->setPosition(d->contentStartPosition());
d->updateHeader();
d->updateFooter();
}
QSGVisualModel *oldModel = d->model;
d->clear();
- d->setPosition(0);
+ d->setPosition(d->contentStartPosition());
d->model = 0;
d->modelVariant = model;
d->updateHeader();
d->updateFooter();
d->updateViewport();
+ d->setPosition(d->contentStartPosition());
if (d->isValid()) {
d->refill();
d->moveReason = QSGItemViewPrivate::SetIndex;
return isContentFlowReversed() ? -originPosition()-1 : lastPosition();
}
+qreal QSGItemViewPrivate::contentStartPosition() const
+{
+ return -headerSize();
+}
+
int QSGItemViewPrivate::findLastVisibleIndex(int defaultValue) const
{
if (visibleItems.count()) {
updateFooter();
clear();
updateViewport();
- setPosition(0);
+ setPosition(contentStartPosition());
refill();
updateCurrent(currentIndex);
}
layoutScheduled = false;
if (!isValid() && !visibleItems.count()) {
clear();
- setPosition(0);
+ setPosition(contentStartPosition());
return;
}
qreal size() const;
qreal startPosition() const;
qreal endPosition() const;
+ qreal contentStartPosition() const;
int findLastVisibleIndex(int defaultValue = -1) const;
FxViewItem *visibleItem(int modelIndex) const;
FxViewItem *firstVisibleItem() const;
delete sectionCache[i];
sectionCache[i] = 0;
}
- visiblePos = header ? headerSize() : 0;
+ visiblePos = 0;
QSGItemViewPrivate::clear();
}
listItem->setPosition(startPos - headerSize());
}
} else {
- if (itemCount == 0)
- visiblePos = headerSize();
- listItem->setPosition(0);
+ listItem->setPosition(-headerSize());
}
}
}
if (d->visibleItems.count()) {
pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
: d->visibleItems.last()->endPosition()+d->spacing+1;
- } else if (d->itemCount == 0 && d->header) {
- pos = d->headerSize();
}
int initialPos = pos;
d->timeline.clear();
if (removedVisible && d->itemCount == 0) {
d->visibleIndex = 0;
- d->visiblePos = d->header ? d->headerSize() : 0;
- d->setPosition(0);
+ d->visiblePos = 0;
+ d->setPosition(d->contentStartPosition());
d->updateHeader();
d->updateFooter();
} else {
import QtQuick 2.0
Rectangle {
+ property bool showHeader: false
+
function changeFooter() {
grid.footer = footer2
}
color: GridView.isCurrentItem ? "lightsteelblue" : "white"
}
}
+ Component {
+ id: headerComponent
+ Text { objectName: "header"; text: "Header " + x + "," + y; width: 100; height: 30 }
+ }
+
GridView {
id: grid
objectName: "grid"
cellHeight: 60
model: testModel
delegate: myDelegate
- footer: Text { objectName: "footer"; text: "Footer"; height: 30 }
+ header: parent.showHeader ? headerComponent : null
+ footer: Text { objectName: "footer"; text: "Footer " + x + "," + y; width: 100; height: 30 }
}
Component {
id: footer2
- Text { objectName: "footer2"; text: "Footer 2"; height: 20 }
+ Text { objectName: "footer2"; text: "Footer 2" + x + "," + y; width: 50; height: 20 }
}
}
cellHeight: 60
model: testModel
delegate: myDelegate
- header: Text { objectName: "header"; text: "Header"; height: 30 }
+ header: Text { objectName: "header"; text: "Header " + x + "," + y; width: 100; height: 30 }
}
Component {
id: header2
- Text { objectName: "header2"; text: "Header 2"; height: 20 }
+ Text { objectName: "header2"; text: "Header 2 " + x + "," + y; width: 50; height: 20 }
}
}
#define SRCDIR "."
#endif
+Q_DECLARE_METATYPE(Qt::LayoutDirection)
+Q_DECLARE_METATYPE(QSGGridView::Flow)
+
class tst_QSGGridView : public QObject
{
Q_OBJECT
void QTBUG_8456();
void manualHighlight();
void footer();
+ void footer_data();
void header();
+ void header_data();
void indexAt();
void onAdd();
void onAdd_data();
gridview->setContentX(80);
canvas->rootObject()->setProperty("showFooter", true);
gridview->positionViewAtEnd();
- QTRY_COMPARE(gridview->contentX(), 460.);
+ QTRY_COMPARE(gridview->contentX(), 430.);
+
+ // set current item to outside visible view, position at beginning
+ // and ensure highlight moves to current item
+ gridview->setCurrentIndex(6);
+ gridview->positionViewAtBeginning();
+ QTRY_COMPARE(gridview->contentX(), -30.);
+ QVERIFY(gridview->highlightItem());
+ QCOMPARE(gridview->highlightItem()->x(), 80.);
delete canvas;
}
delete canvas;
}
+
void tst_QSGGridView::footer()
{
+ QFETCH(QSGGridView::Flow, flow);
+ QFETCH(Qt::LayoutDirection, layoutDirection);
+ QFETCH(QPointF, initialFooterPos);
+ QFETCH(QPointF, changedFooterPos);
+ QFETCH(QPointF, initialContentPos);
+ QFETCH(QPointF, changedContentPos);
+ QFETCH(QPointF, firstDelegatePos);
+
QSGView *canvas = createView();
canvas->show();
QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
QTRY_VERIFY(gridview != 0);
+ gridview->setFlow(flow);
+ gridview->setLayoutDirection(layoutDirection);
QSGItem *contentItem = gridview->contentItem();
QTRY_VERIFY(contentItem != 0);
QSGText *footer = findItem<QSGText>(contentItem, "footer");
QVERIFY(footer);
- QCOMPARE(footer->y(), 180.0);
- QCOMPARE(footer->height(), 30.0);
+ QCOMPARE(footer->pos(), initialFooterPos);
+ QCOMPARE(footer->width(), 100.);
+ QCOMPARE(footer->height(), 30.);
+ QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
- model.removeItem(2);
- QTRY_COMPARE(footer->y(), 120.0);
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->pos(), firstDelegatePos);
+
+ if (flow == QSGGridView::LeftToRight) {
+ // shrink by one row
+ model.removeItem(2);
+ QTRY_COMPARE(footer->y(), initialFooterPos.y() - gridview->cellHeight());
+ } else {
+ // shrink by one column
+ model.removeItem(2);
+ model.removeItem(3);
+ if (layoutDirection == Qt::LeftToRight)
+ QTRY_COMPARE(footer->x(), initialFooterPos.x() - gridview->cellWidth());
+ else
+ QTRY_COMPARE(footer->x(), initialFooterPos.x() + gridview->cellWidth());
+ }
+ // remove all items
model.clear();
- QTRY_COMPARE(footer->y(), 0.0);
+ QPointF posWhenNoItems(0, 0);
+ if (layoutDirection == Qt::RightToLeft)
+ posWhenNoItems.setX(flow == QSGGridView::LeftToRight ? gridview->width() - footer->width() : -footer->width());
+ QTRY_COMPARE(footer->pos(), posWhenNoItems);
+
+ // if header is present, it's at a negative pos, so the footer should not move
+ canvas->rootObject()->setProperty("showHeader", true);
+ QVERIFY(findItem<QSGItem>(contentItem, "header") != 0);
+ QTRY_COMPARE(footer->pos(), posWhenNoItems);
+ canvas->rootObject()->setProperty("showHeader", false);
+
+ // add 30 items
for (int i = 0; i < 30; i++)
model.addItem("Item" + QString::number(i), "");
-
QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
footer = findItem<QSGText>(contentItem, "footer");
footer = findItem<QSGText>(contentItem, "footer2");
QVERIFY(footer);
- QCOMPARE(footer->y(), 600.0);
- QCOMPARE(footer->height(), 20.0);
- QCOMPARE(gridview->contentY(), 0.0);
+ QCOMPARE(footer->pos(), changedFooterPos);
+ QCOMPARE(footer->width(), 50.);
+ QCOMPARE(footer->height(), 20.);
+ QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
+
+ item = findItem<QSGItem>(contentItem, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->pos(), firstDelegatePos);
delete canvas;
}
+void tst_QSGGridView::footer_data()
+{
+ QTest::addColumn<QSGGridView::Flow>("flow");
+ QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+ QTest::addColumn<QPointF>("initialFooterPos");
+ QTest::addColumn<QPointF>("changedFooterPos");
+ QTest::addColumn<QPointF>("initialContentPos");
+ QTest::addColumn<QPointF>("changedContentPos");
+ QTest::addColumn<QPointF>("firstDelegatePos");
+
+ // footer1 = 100 x 30
+ // footer2 = 100 x 20
+ // cells = 80 * 60
+ // view width = 240
+
+ // footer below items, bottom left
+ QTest::newRow("flow left to right") << QSGGridView::LeftToRight << Qt::LeftToRight
+ << QPointF(0, 3 * 60) // 180 = height of 3 rows (cell height is 60)
+ << QPointF(0, 10 * 60) // 30 items = 10 rows
+ << QPointF(0, 0)
+ << QPointF(0, 0)
+ << QPointF(0, 0);
+
+ // footer below items, bottom right
+ QTest::newRow("flow left to right, layout right to left") << QSGGridView::LeftToRight << Qt::RightToLeft
+ << QPointF(240 - 100, 3 * 60)
+ << QPointF((240 - 100) + 50, 10 * 60) // 50 = width diff between old and new footers
+ << QPointF(0, 0)
+ << QPointF(0, 0)
+ << QPointF(240 - 80, 0);
+
+ // footer to right of items
+ QTest::newRow("flow top to bottom, layout left to right") << QSGGridView::TopToBottom << Qt::LeftToRight
+ << QPointF(2 * 80, 0) // 2 columns, cell width 80
+ << QPointF(6 * 80, 0) // 30 items = 6 columns
+ << QPointF(0, 0)
+ << QPointF(0, 0)
+ << QPointF(0, 0);
+
+ // footer to left of items
+ QTest::newRow("flow top to bottom, layout right to left") << QSGGridView::TopToBottom << Qt::RightToLeft
+ << QPointF(-(2 * 80) - 100, 0)
+ << QPointF(-(6 * 80) - 50, 0) // 50 = new footer width
+ << QPointF(-240, 0)
+ << QPointF(-240, 0) // unchanged, footer change doesn't change content pos
+ << QPointF(-80, 0);
+}
+
void tst_QSGGridView::header()
{
+ QFETCH(QSGGridView::Flow, flow);
+ QFETCH(Qt::LayoutDirection, layoutDirection);
+ QFETCH(QPointF, initialHeaderPos);
+ QFETCH(QPointF, changedHeaderPos);
+ QFETCH(QPointF, initialContentPos);
+ QFETCH(QPointF, changedContentPos);
+ QFETCH(QPointF, firstDelegatePos);
+
QSGView *canvas = createView();
TestModel model;
QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
QTRY_VERIFY(gridview != 0);
+ gridview->setFlow(flow);
+ gridview->setLayoutDirection(layoutDirection);
QSGItem *contentItem = gridview->contentItem();
QTRY_VERIFY(contentItem != 0);
QSGText *header = findItem<QSGText>(contentItem, "header");
QVERIFY(header);
- QCOMPARE(header->y(), 0.0);
- QCOMPARE(header->height(), 30.0);
- QCOMPARE(gridview->contentY(), 0.0);
+ QCOMPARE(header->pos(), initialHeaderPos);
+ QCOMPARE(header->width(), 100.);
+ QCOMPARE(header->height(), 30.);
+ QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
QVERIFY(item);
- QCOMPARE(item->y(), 30.0);
+ QCOMPARE(item->pos(), firstDelegatePos);
model.clear();
- QTRY_COMPARE(header->y(), 0.0);
+ QCOMPARE(header->pos(), initialHeaderPos); // header should stay where it is
for (int i = 0; i < 30; i++)
model.addItem("Item" + QString::number(i), "");
header = findItem<QSGText>(contentItem, "header2");
QVERIFY(header);
- QCOMPARE(header->y(), 10.0);
- QCOMPARE(header->height(), 20.0);
- QCOMPARE(gridview->contentY(), 10.0);
+ QCOMPARE(header->pos(), changedHeaderPos);
+ QCOMPARE(header->width(), 50.);
+ QCOMPARE(header->height(), 20.);
+ QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
+
+ item = findItem<QSGItem>(contentItem, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->pos(), firstDelegatePos);
delete canvas;
}
+void tst_QSGGridView::header_data()
+{
+ QTest::addColumn<QSGGridView::Flow>("flow");
+ QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+ QTest::addColumn<QPointF>("initialHeaderPos");
+ QTest::addColumn<QPointF>("changedHeaderPos");
+ QTest::addColumn<QPointF>("initialContentPos");
+ QTest::addColumn<QPointF>("changedContentPos");
+ QTest::addColumn<QPointF>("firstDelegatePos");
+
+ // header1 = 100 x 30
+ // header2 = 100 x 20
+ // cells = 80 x 60
+ // view width = 240
+
+ // header above items, top left
+ QTest::newRow("flow left to right") << QSGGridView::LeftToRight << Qt::LeftToRight
+ << QPointF(0, -30)
+ << QPointF(0, -20)
+ << QPointF(0, -30)
+ << QPointF(0, -20)
+ << QPointF(0, 0);
+
+ // header above items, top right
+ QTest::newRow("flow left to right, layout right to left") << QSGGridView::LeftToRight << Qt::RightToLeft
+ << QPointF(240 - 100, -30)
+ << QPointF((240 - 100) + 50, -20) // 50 = width diff between old and new headers
+ << QPointF(0, -30)
+ << QPointF(0, -20)
+ << QPointF(160, 0);
+
+ // header to left of items
+ QTest::newRow("flow top to bottom, layout left to right") << QSGGridView::TopToBottom << Qt::LeftToRight
+ << QPointF(-100, 0)
+ << QPointF(-50, 0)
+ << QPointF(-100, 0)
+ << QPointF(-50, 0)
+ << QPointF(0, 0);
+
+ // header to right of items
+ QTest::newRow("flow top to bottom, layout right to left") << QSGGridView::TopToBottom << Qt::RightToLeft
+ << QPointF(0, 0)
+ << QPointF(0, 0)
+ << QPointF(-(240 - 100), 0)
+ << QPointF(-(240 - 50), 0)
+ << QPointF(-80, 0);
+}
+
void tst_QSGGridView::indexAt()
{
QSGView *canvas = createView();
import QtQuick 2.0
Rectangle {
+ property bool showHeader: false
+
function changeFooter() {
list.footer = footer2
}
id: wrapper
objectName: "wrapper"
height: 20
- width: 240
+ width: 40
Text {
- text: index
+ text: index + " " + x + "," + y
}
color: ListView.isCurrentItem ? "lightsteelblue" : "white"
}
}
+ Component {
+ id: headerComponent
+ Text { objectName: "header"; text: "Header " + x + "," + y; width: 100; height: 30 }
+ }
+
ListView {
id: list
objectName: "list"
height: 320
model: testModel
delegate: myDelegate
- footer: Text { objectName: "footer"; text: "Footer"; height: 30 }
+ header: parent.showHeader ? headerComponent : null
+ footer: Text { objectName: "footer"; text: "Footer " + x + "," + y; width: 100; height: 30 }
}
Component {
id: footer2
- Text { objectName: "footer2"; text: "Footer 2"; height: 20 }
+ Text { objectName: "footer2"; text: "Footer 2 " + x + "," + y; width: 50; height: 20 }
}
}
height: 30
width: 240
Text {
- text: index
+ text: index + " " + x + "," + y
}
color: ListView.isCurrentItem ? "lightsteelblue" : "white"
}
snapMode: ListView.SnapToItem
model: testModel
delegate: myDelegate
- header: Text { objectName: "header"; text: "Header"; height: 20 }
+ header: Text { objectName: "header"; text: "Header " + x + "," + y; width: 100; height: 30 }
}
Component {
id: header2
- Text { objectName: "header2"; text: "Header 2"; height: 10 }
+ Text { objectName: "header2"; text: "Header " + x + "," + y; width: 50; height: 20 }
}
+
}
height: horizontal ? view.height : 30
color: "blue"
}
-// model: testModel
+
delegate: Text { width: 30; height: 30; text: index + "(" + x + ")" }
layoutDirection: rtl ? Qt.RightToLeft : Qt.LeftToRight
}
#define SRCDIR "."
#endif
+Q_DECLARE_METATYPE(Qt::LayoutDirection)
+Q_DECLARE_METATYPE(QSGListView::Orientation)
+
class tst_QSGListView : public QObject
{
Q_OBJECT
void manualHighlight();
void QTBUG_11105();
void header();
+ void header_data();
+ void header_delayItemCreation();
void footer();
+ void footer_data();
void headerFooter();
void resizeView();
void sizeLessThan1();
listview->positionViewAtEnd();
QTRY_COMPARE(listview->contentY(), 510.);
+ // set current item to outside visible view, position at beginning
+ // and ensure highlight moves to current item
+ listview->setCurrentIndex(1);
+ listview->positionViewAtBeginning();
+ QTRY_COMPARE(listview->contentY(), -30.);
+ QVERIFY(listview->highlightItem());
+ QCOMPARE(listview->highlightItem()->y(), 20.);
+
delete canvas;
delete testObject;
}
void tst_QSGListView::header()
{
- {
- QSGView *canvas = createView();
+ QFETCH(QSGListView::Orientation, orientation);
+ QFETCH(Qt::LayoutDirection, layoutDirection);
+ QFETCH(QPointF, initialHeaderPos);
+ QFETCH(QPointF, firstDelegatePos);
+ QFETCH(QPointF, initialContentPos);
+ QFETCH(QPointF, changedHeaderPos);
+ QFETCH(QPointF, changedContentPos);
- TestModel model;
- for (int i = 0; i < 30; i++)
- model.addItem("Item" + QString::number(i), "");
+ QSGView *canvas = createView();
- QDeclarativeContext *ctxt = canvas->rootContext();
- ctxt->setContextProperty("testModel", &model);
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
- canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header.qml"));
- qApp->processEvents();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
- QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
- QTRY_VERIFY(listview != 0);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header.qml"));
+ qApp->processEvents();
- QSGItem *contentItem = listview->contentItem();
- QTRY_VERIFY(contentItem != 0);
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+ listview->setOrientation(orientation);
+ listview->setLayoutDirection(layoutDirection);
- QSGText *header = findItem<QSGText>(contentItem, "header");
- QVERIFY(header);
- QCOMPARE(header->y(), 0.0);
- QCOMPARE(header->height(), 20.0);
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
- QCOMPARE(listview->contentY(), 0.0);
+ QSGText *header = findItem<QSGText>(contentItem, "header");
+ QVERIFY(header);
- model.clear();
- QTRY_COMPARE(header->y(), 0.0);
+ QCOMPARE(header->width(), 100.);
+ QCOMPARE(header->height(), 30.);
+ QCOMPARE(header->pos(), initialHeaderPos);
+ QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
- for (int i = 0; i < 30; i++)
- model.addItem("Item" + QString::number(i), "");
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->pos(), firstDelegatePos);
- QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
+ model.clear();
+ QCOMPARE(header->pos(), initialHeaderPos); // header should stay where it is
- header = findItem<QSGText>(contentItem, "header");
- QVERIFY(!header);
- header = findItem<QSGText>(contentItem, "header2");
- QVERIFY(header);
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
- QCOMPARE(header->y(), 10.0);
- QCOMPARE(header->height(), 10.0);
- QCOMPARE(listview->contentY(), 10.0);
+ QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
- delete canvas;
- }
- {
- QSGView *canvas = createView();
+ header = findItem<QSGText>(contentItem, "header");
+ QVERIFY(!header);
+ header = findItem<QSGText>(contentItem, "header2");
+ QVERIFY(header);
- TestModel model;
+ QCOMPARE(header->pos(), changedHeaderPos);
+ QCOMPARE(header->width(), 50.);
+ QCOMPARE(header->height(), 20.);
+ QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos);
+ QCOMPARE(item->pos(), firstDelegatePos);
- canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header1.qml"));
- qApp->processEvents();
+ delete canvas;
+}
- QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
- QTRY_VERIFY(listview != 0);
+void tst_QSGListView::header_data()
+{
+ QTest::addColumn<QSGListView::Orientation>("orientation");
+ QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+ QTest::addColumn<QPointF>("initialHeaderPos");
+ QTest::addColumn<QPointF>("changedHeaderPos");
+ QTest::addColumn<QPointF>("initialContentPos");
+ QTest::addColumn<QPointF>("changedContentPos");
+ QTest::addColumn<QPointF>("firstDelegatePos");
+
+ // header1 = 100 x 30
+ // header2 = 50 x 20
+ // delegates = 240 x 20
+ // view width = 240
+
+ // header above items, top left
+ QTest::newRow("vertical, left to right") << QSGListView::Vertical << Qt::LeftToRight
+ << QPointF(0, -30)
+ << QPointF(0, -20)
+ << QPointF(0, -30)
+ << QPointF(0, -20)
+ << QPointF(0, 0);
+
+ // header above items, top right
+ QTest::newRow("vertical, layout right to left") << QSGListView::Vertical << Qt::RightToLeft
+ << QPointF(0, -30)
+ << QPointF(0, -20)
+ << QPointF(0, -30)
+ << QPointF(0, -20)
+ << QPointF(0, 0);
+
+ // header to left of items
+ QTest::newRow("horizontal, layout left to right") << QSGListView::Horizontal << Qt::LeftToRight
+ << QPointF(-100, 0)
+ << QPointF(-50, 0)
+ << QPointF(-100, 0)
+ << QPointF(-50, 0)
+ << QPointF(0, 0);
+
+ // header to right of items
+ QTest::newRow("horizontal, layout right to left") << QSGListView::Horizontal << Qt::RightToLeft
+ << QPointF(0, 0)
+ << QPointF(0, 0)
+ << QPointF(-240 + 100, 0)
+ << QPointF(-240 + 50, 0)
+ << QPointF(-240, 0);
+}
- QSGItem *contentItem = listview->contentItem();
- QTRY_VERIFY(contentItem != 0);
+void tst_QSGListView::header_delayItemCreation()
+{
+ QSGView *canvas = createView();
- QSGText *header = findItem<QSGText>(contentItem, "header");
- QVERIFY(header);
- QCOMPARE(header->y(), 0.0);
+ TestModel model;
- QCOMPARE(listview->contentY(), 0.0);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header1.qml"));
+ qApp->processEvents();
- model.clear();
- QTRY_COMPARE(header->y(), 0.0);
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
- delete canvas;
- }
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGText *header = findItem<QSGText>(contentItem, "header");
+ QVERIFY(header);
+ QCOMPARE(header->y(), -header->height());
+
+ QCOMPARE(listview->contentY(), -header->height());
+
+ model.clear();
+ QTRY_COMPARE(header->y(), -header->height());
+
+ delete canvas;
}
void tst_QSGListView::footer()
{
+ QFETCH(QSGListView::Orientation, orientation);
+ QFETCH(Qt::LayoutDirection, layoutDirection);
+ QFETCH(QPointF, initialFooterPos);
+ QFETCH(QPointF, firstDelegatePos);
+ QFETCH(QPointF, initialContentPos);
+ QFETCH(QPointF, changedFooterPos);
+ QFETCH(QPointF, changedContentPos);
+
QSGView *canvas = createView();
TestModel model;
QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
QTRY_VERIFY(listview != 0);
+ listview->setOrientation(orientation);
+ listview->setLayoutDirection(layoutDirection);
QSGItem *contentItem = listview->contentItem();
QTRY_VERIFY(contentItem != 0);
QSGText *footer = findItem<QSGText>(contentItem, "footer");
QVERIFY(footer);
- QCOMPARE(footer->y(), 60.0);
- QCOMPARE(footer->height(), 30.0);
+ QCOMPARE(footer->pos(), initialFooterPos);
+ QCOMPARE(footer->width(), 100.);
+ QCOMPARE(footer->height(), 30.);
+ QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
+
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->pos(), firstDelegatePos);
+
+ // remove one item
model.removeItem(1);
- QTRY_COMPARE(footer->y(), 40.0);
+ if (orientation == QSGListView::Vertical) {
+ QTRY_COMPARE(footer->y(), initialFooterPos.y() - 20); // delegate height = 20
+ } else {
+ QTRY_COMPARE(footer->x(), layoutDirection == Qt::LeftToRight ?
+ initialFooterPos.x() - 40 : initialFooterPos.x() + 40); // delegate width = 40
+ }
+
+ // remove all items
model.clear();
- QTRY_COMPARE(footer->y(), 0.0);
+ QPointF posWhenNoItems(0, 0);
+ if (orientation == QSGListView::Horizontal && layoutDirection == Qt::RightToLeft)
+ posWhenNoItems.setX(-100);
+ QTRY_COMPARE(footer->pos(), posWhenNoItems);
+
+ // if header is present, it's at a negative pos, so the footer should not move
+ canvas->rootObject()->setProperty("showHeader", true);
+ QTRY_COMPARE(footer->pos(), posWhenNoItems);
+ canvas->rootObject()->setProperty("showHeader", false);
+
+ // add 30 items
for (int i = 0; i < 30; i++)
model.addItem("Item" + QString::number(i), "");
footer = findItem<QSGText>(contentItem, "footer2");
QVERIFY(footer);
- QCOMPARE(footer->y(), 600.0);
- QCOMPARE(footer->height(), 20.0);
- QCOMPARE(listview->contentY(), 0.0);
+ QCOMPARE(footer->pos(), changedFooterPos);
+ QCOMPARE(footer->width(), 50.);
+ QCOMPARE(footer->height(), 20.);
+ QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos);
+
+ item = findItem<QSGItem>(contentItem, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->pos(), firstDelegatePos);
delete canvas;
}
+void tst_QSGListView::footer_data()
+{
+ QTest::addColumn<QSGListView::Orientation>("orientation");
+ QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+ QTest::addColumn<QPointF>("initialFooterPos");
+ QTest::addColumn<QPointF>("changedFooterPos");
+ QTest::addColumn<QPointF>("initialContentPos");
+ QTest::addColumn<QPointF>("changedContentPos");
+ QTest::addColumn<QPointF>("firstDelegatePos");
+
+ // footer1 = 100 x 30
+ // footer2 = 100 x 20
+ // delegates = 40 x 20
+ // view width = 240
+
+ // footer below items, bottom left
+ QTest::newRow("vertical, layout left to right") << QSGListView::Vertical << Qt::LeftToRight
+ << QPointF(0, 3 * 20)
+ << QPointF(0, 30 * 20) // added 30 items
+ << QPointF(0, 0)
+ << QPointF(0, 0)
+ << QPointF(0, 0);
+
+ // footer below items, bottom right
+ QTest::newRow("vertical, layout right to left") << QSGListView::Vertical << Qt::RightToLeft
+ << QPointF(0, 3 * 20)
+ << QPointF(0, 30 * 20)
+ << QPointF(0, 0)
+ << QPointF(0, 0)
+ << QPointF(0, 0);
+
+ // footer to right of items
+ QTest::newRow("horizontal, layout left to right") << QSGListView::Horizontal << Qt::LeftToRight
+ << QPointF(40 * 3, 0)
+ << QPointF(40 * 30, 0)
+ << QPointF(0, 0)
+ << QPointF(0, 0)
+ << QPointF(0, 0);
+
+ // footer to left of items
+ QTest::newRow("horizontal, layout right to left") << QSGListView::Horizontal << Qt::RightToLeft
+ << QPointF(-(40 * 3) - 100, 0)
+ << QPointF(-(40 * 30) - 50, 0) // 50 = new footer width
+ << QPointF(-240, 0)
+ << QPointF(-240, 0)
+ << QPointF(-40, 0);
+}
+
class LVAccessor : public QSGListView
{
public:
QSGItem *header = findItem<QSGItem>(contentItem, "header");
QVERIFY(header);
- QCOMPARE(header->y(), 0.0);
+ QCOMPARE(header->y(), -header->height());
QSGItem *footer = findItem<QSGItem>(contentItem, "footer");
QVERIFY(footer);
- QCOMPARE(footer->y(), 20.0);
+ QCOMPARE(footer->y(), 0.);
QVERIFY(static_cast<LVAccessor*>(listview)->minY() == 0);
QVERIFY(static_cast<LVAccessor*>(listview)->maxY() == 0);
QSGItem *header = findItem<QSGItem>(contentItem, "header");
QVERIFY(header);
- QCOMPARE(header->x(), 0.0);
+ QCOMPARE(header->x(), -header->width());
QSGItem *footer = findItem<QSGItem>(contentItem, "footer");
QVERIFY(footer);
- QCOMPARE(footer->x(), 20.0);
+ QCOMPARE(footer->x(), 0.);
QVERIFY(static_cast<LVAccessor*>(listview)->minX() == 0);
QVERIFY(static_cast<LVAccessor*>(listview)->maxX() == 0);
QSGItem *header = findItem<QSGItem>(contentItem, "header");
QVERIFY(header);
- QCOMPARE(header->x(), -20.0);
+ QCOMPARE(header->x(), 0.);
QSGItem *footer = findItem<QSGItem>(contentItem, "footer");
QVERIFY(footer);
- QCOMPARE(footer->x(), -50.0);
+ QCOMPARE(footer->x(), -footer->width());
QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), 240.);
QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), 240.);