Vertical layout direction for ListView and GridView
authorBea Lam <bea.lam@nokia.com>
Tue, 20 Mar 2012 01:37:10 +0000 (11:37 +1000)
committerQt by Nokia <qt-info@nokia.com>
Tue, 17 Apr 2012 04:09:56 +0000 (06:09 +0200)
Provide verticalLayoutDirection property with TopToBottom
and BottomToTop values.

Change-Id: If6f0da5dd4735036162868d391852a661854de5b
Reviewed-by: Andrew den Exter <andrew.den-exter@nokia.com>
26 files changed:
doc/src/images/gridview-layout-lefttoright-ltr-btt.png [new file with mode: 0644]
doc/src/images/gridview-layout-lefttoright-ltr-ttb.png [new file with mode: 0644]
doc/src/images/gridview-layout-lefttoright-rtl-btt.png [new file with mode: 0644]
doc/src/images/gridview-layout-lefttoright-rtl-ttb.png [new file with mode: 0644]
doc/src/images/gridview-layout-toptobottom-ltr-btt.png [new file with mode: 0644]
doc/src/images/gridview-layout-toptobottom-ltr-ttb.png [new file with mode: 0644]
doc/src/images/gridview-layout-toptobottom-rtl-btt.png [new file with mode: 0644]
doc/src/images/gridview-layout-toptobottom-rtl-ttb.png [new file with mode: 0644]
doc/src/images/listview-layout-bottomtotop.png [new file with mode: 0644]
doc/src/images/listview-layout-lefttoright.png [new file with mode: 0644]
doc/src/images/listview-layout-righttoleft.png [new file with mode: 0644]
doc/src/images/listview-layout-toptobottom.png [new file with mode: 0644]
src/quick/items/qquickgridview.cpp
src/quick/items/qquickgridview_p.h
src/quick/items/qquickitemview.cpp
src/quick/items/qquickitemview_p.h
src/quick/items/qquickitemview_p_p.h
src/quick/items/qquicklistview.cpp
tests/auto/quick/qquickgridview/data/gridview1.qml
tests/auto/quick/qquickgridview/data/headerfooter.qml
tests/auto/quick/qquickgridview/data/layouts.qml [new file with mode: 0644]
tests/auto/quick/qquickgridview/data/resizegrid.qml [new file with mode: 0644]
tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
tests/auto/quick/qquicklistview/data/header.qml
tests/auto/quick/qquicklistview/data/headerfooter.qml
tests/auto/quick/qquicklistview/tst_qquicklistview.cpp

diff --git a/doc/src/images/gridview-layout-lefttoright-ltr-btt.png b/doc/src/images/gridview-layout-lefttoright-ltr-btt.png
new file mode 100644 (file)
index 0000000..4439f83
Binary files /dev/null and b/doc/src/images/gridview-layout-lefttoright-ltr-btt.png differ
diff --git a/doc/src/images/gridview-layout-lefttoright-ltr-ttb.png b/doc/src/images/gridview-layout-lefttoright-ltr-ttb.png
new file mode 100644 (file)
index 0000000..af745b7
Binary files /dev/null and b/doc/src/images/gridview-layout-lefttoright-ltr-ttb.png differ
diff --git a/doc/src/images/gridview-layout-lefttoright-rtl-btt.png b/doc/src/images/gridview-layout-lefttoright-rtl-btt.png
new file mode 100644 (file)
index 0000000..bc7e568
Binary files /dev/null and b/doc/src/images/gridview-layout-lefttoright-rtl-btt.png differ
diff --git a/doc/src/images/gridview-layout-lefttoright-rtl-ttb.png b/doc/src/images/gridview-layout-lefttoright-rtl-ttb.png
new file mode 100644 (file)
index 0000000..3ebf74f
Binary files /dev/null and b/doc/src/images/gridview-layout-lefttoright-rtl-ttb.png differ
diff --git a/doc/src/images/gridview-layout-toptobottom-ltr-btt.png b/doc/src/images/gridview-layout-toptobottom-ltr-btt.png
new file mode 100644 (file)
index 0000000..b60ab4b
Binary files /dev/null and b/doc/src/images/gridview-layout-toptobottom-ltr-btt.png differ
diff --git a/doc/src/images/gridview-layout-toptobottom-ltr-ttb.png b/doc/src/images/gridview-layout-toptobottom-ltr-ttb.png
new file mode 100644 (file)
index 0000000..9078cbd
Binary files /dev/null and b/doc/src/images/gridview-layout-toptobottom-ltr-ttb.png differ
diff --git a/doc/src/images/gridview-layout-toptobottom-rtl-btt.png b/doc/src/images/gridview-layout-toptobottom-rtl-btt.png
new file mode 100644 (file)
index 0000000..0d0f095
Binary files /dev/null and b/doc/src/images/gridview-layout-toptobottom-rtl-btt.png differ
diff --git a/doc/src/images/gridview-layout-toptobottom-rtl-ttb.png b/doc/src/images/gridview-layout-toptobottom-rtl-ttb.png
new file mode 100644 (file)
index 0000000..c1c353a
Binary files /dev/null and b/doc/src/images/gridview-layout-toptobottom-rtl-ttb.png differ
diff --git a/doc/src/images/listview-layout-bottomtotop.png b/doc/src/images/listview-layout-bottomtotop.png
new file mode 100644 (file)
index 0000000..980f81d
Binary files /dev/null and b/doc/src/images/listview-layout-bottomtotop.png differ
diff --git a/doc/src/images/listview-layout-lefttoright.png b/doc/src/images/listview-layout-lefttoright.png
new file mode 100644 (file)
index 0000000..2ee0e4c
Binary files /dev/null and b/doc/src/images/listview-layout-lefttoright.png differ
diff --git a/doc/src/images/listview-layout-righttoleft.png b/doc/src/images/listview-layout-righttoleft.png
new file mode 100644 (file)
index 0000000..2c54a2a
Binary files /dev/null and b/doc/src/images/listview-layout-righttoleft.png differ
diff --git a/doc/src/images/listview-layout-toptobottom.png b/doc/src/images/listview-layout-toptobottom.png
new file mode 100644 (file)
index 0000000..2054a57
Binary files /dev/null and b/doc/src/images/listview-layout-toptobottom.png differ
index 0caf93c..0518fcc 100644 (file)
@@ -92,7 +92,7 @@ public:
     }
 
     qreal size() const {
-        return view->flow() == QQuickGridView::LeftToRight ? view->cellHeight() : view->cellWidth();
+        return view->flow() == QQuickGridView::FlowLeftToRight ? view->cellHeight() : view->cellWidth();
     }
 
     qreal sectionSize() const {
@@ -100,14 +100,14 @@ public:
     }
 
     qreal rowPos() const {
-        if (view->flow() == QQuickGridView::LeftToRight)
-            return itemY();
+        if (view->flow() == QQuickGridView::FlowLeftToRight)
+            return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -view->cellHeight()-itemY() : itemY());
         else
             return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -view->cellWidth()-itemX() : itemX());
     }
 
     qreal colPos() const {
-        if (view->flow() == QQuickGridView::LeftToRight) {
+        if (view->flow() == QQuickGridView::FlowLeftToRight) {
             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
                 qreal colSize = view->cellWidth();
                 int columns = view->width()/colSize;
@@ -116,12 +116,19 @@ public:
                 return itemX();
             }
         } else {
-            return itemY();
+            if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
+                return -view->cellHeight() - itemY();
+            } else {
+                return itemY();
+            }
         }
     }
     qreal endRowPos() const {
-        if (view->flow() == QQuickGridView::LeftToRight) {
-            return itemY() + view->cellHeight();
+        if (view->flow() == QQuickGridView::FlowLeftToRight) {
+            if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
+                return -itemY();
+            else
+                return itemY() + view->cellHeight();
         } else {
             if (view->effectiveLayoutDirection() == Qt::RightToLeft)
                 return -itemX();
@@ -141,19 +148,24 @@ public:
 
 private:
     QPointF pointForPosition(qreal col, qreal row) const {
-        if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
-            if (view->flow() == QQuickGridView::LeftToRight) {
+        qreal x;
+        qreal y;
+        if (view->flow() == QQuickGridView::FlowLeftToRight) {
+            x = col;
+            y = row;
+            if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
                 int columns = view->width()/view->cellWidth();
-                return QPointF(view->cellWidth() * (columns-1) - col, row);
-            } else {
-                return QPointF(-view->cellWidth() - row, col);
+                x = view->cellWidth() * (columns-1) - col;
             }
         } else {
-            if (view->flow() == QQuickGridView::LeftToRight)
-                return QPointF(col, row);
-            else
-                return QPointF(row, col);
+            x = row;
+            y = col;
+            if (view->effectiveLayoutDirection() == Qt::RightToLeft)
+                x = -view->cellWidth() - row;
         }
+        if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
+            y = -view->cellHeight() - y;
+        return QPointF(x, y);
     }
 };
 
@@ -166,7 +178,6 @@ class QQuickGridViewPrivate : public QQuickItemViewPrivate
 public:
     virtual Qt::Orientation layoutOrientation() const;
     virtual bool isContentFlowReversed() const;
-    bool isRightToLeftTopToBottom() const;
 
     virtual qreal positionAt(int index) const;
     virtual qreal endPositionAt(int index) const;
@@ -180,6 +191,8 @@ public:
     qreal snapPosAt(qreal pos) const;
     FxViewItem *snapItemAt(qreal pos) const;
     int snapIndex() const;
+    qreal contentXForPosition(qreal pos) const;
+    qreal contentYForPosition(qreal pos) const;
 
     void resetColumns();
 
@@ -229,7 +242,7 @@ public:
     QSmoothedAnimation *highlightYAnimator;
 
     QQuickGridViewPrivate()
-        : flow(QQuickGridView::LeftToRight)
+        : flow(QQuickGridView::FlowLeftToRight)
         , cellWidth(100), cellHeight(100), columns(1)
         , snapMode(QQuickGridView::NoSnap)
         , highlightXAnimator(0), highlightYAnimator(0)
@@ -243,18 +256,15 @@ public:
 
 Qt::Orientation QQuickGridViewPrivate::layoutOrientation() const
 {
-    return flow == QQuickGridView::LeftToRight ? Qt::Vertical : Qt::Horizontal;
+    return flow == QQuickGridView::FlowLeftToRight ? Qt::Vertical : Qt::Horizontal;
 }
 
 bool QQuickGridViewPrivate::isContentFlowReversed() const
 {
-    return isRightToLeftTopToBottom();
-}
-
-bool QQuickGridViewPrivate::isRightToLeftTopToBottom() const
-{
     Q_Q(const QQuickGridView);
-    return flow == QQuickGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft;
+
+    return (flow == QQuickGridView::FlowLeftToRight && verticalLayoutDirection == QQuickItemView::BottomToTop)
+            || (flow == QQuickGridView::FlowTopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft);
 }
 
 void QQuickGridViewPrivate::changedVisibleIndex(int newIndex)
@@ -265,16 +275,8 @@ void QQuickGridViewPrivate::changedVisibleIndex(int newIndex)
 void QQuickGridViewPrivate::setPosition(qreal pos)
 {
     Q_Q(QQuickGridView);
-    if (flow == QQuickGridView::LeftToRight) {
-        q->QQuickFlickable::setContentY(pos);
-        q->QQuickFlickable::setContentX(0);
-    } else {
-        if (q->effectiveLayoutDirection() == Qt::LeftToRight)
-            q->QQuickFlickable::setContentX(pos);
-        else
-            q->QQuickFlickable::setContentX(-pos-size());
-        q->QQuickFlickable::setContentY(0);
-    }
+    q->QQuickFlickable::setContentX(contentXForPosition(pos));
+    q->QQuickFlickable::setContentY(contentYForPosition(pos));
 }
 
 qreal QQuickGridViewPrivate::originPosition() const
@@ -306,10 +308,10 @@ qreal QQuickGridViewPrivate::endPositionAt(int index) const
 }
 
 qreal QQuickGridViewPrivate::rowSize() const {
-    return flow == QQuickGridView::LeftToRight ? cellHeight : cellWidth;
+    return flow == QQuickGridView::FlowLeftToRight ? cellHeight : cellWidth;
 }
 qreal QQuickGridViewPrivate::colSize() const {
-    return flow == QQuickGridView::LeftToRight ? cellWidth : cellHeight;
+    return flow == QQuickGridView::FlowLeftToRight ? cellWidth : cellHeight;
 }
 
 qreal QQuickGridViewPrivate::colPosAt(int modelIndex) const
@@ -375,12 +377,12 @@ qreal QQuickGridViewPrivate::snapPosAt(qreal pos) const
         snapPos -= highlightStart;
         qreal maxExtent;
         qreal minExtent;
-        if (isRightToLeftTopToBottom()) {
+        if (isContentFlowReversed()) {
             maxExtent = q->minXExtent()-size();
             minExtent = q->maxXExtent()-size();
         } else {
-            maxExtent = flow == QQuickGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent();
-            minExtent = flow == QQuickGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent();
+            maxExtent = flow == QQuickGridView::FlowLeftToRight ? -q->maxYExtent() : -q->maxXExtent();
+            minExtent = flow == QQuickGridView::FlowLeftToRight ? -q->minYExtent() : -q->minXExtent();
         }
         if (snapPos > maxExtent)
             snapPos = maxExtent;
@@ -421,10 +423,49 @@ int QQuickGridViewPrivate::snapIndex() const
     return index;
 }
 
+qreal QQuickGridViewPrivate::contentXForPosition(qreal pos) const
+{
+    Q_Q(const QQuickGridView);
+    if (flow == QQuickGridView::FlowLeftToRight) {
+        // vertical scroll
+        if (q->effectiveLayoutDirection() == Qt::LeftToRight) {
+            return 0;
+        } else {
+            qreal colSize = cellWidth;
+            int columns = q->width()/colSize;
+            return -q->width() + (cellWidth * columns);
+        }
+    } else {
+        // horizontal scroll
+        if (q->effectiveLayoutDirection() == Qt::LeftToRight)
+            return pos;
+        else
+            return -pos - q->width();
+    }
+}
+
+qreal QQuickGridViewPrivate::contentYForPosition(qreal pos) const
+{
+    Q_Q(const QQuickGridView);
+    if (flow == QQuickGridView::FlowLeftToRight) {
+        // vertical scroll
+        if (verticalLayoutDirection == QQuickItemView::TopToBottom)
+            return pos;
+        else
+            return -pos - q->height();
+    } else {
+        // horizontal scroll
+        if (verticalLayoutDirection == QQuickItemView::TopToBottom)
+            return 0;
+        else
+            return -q->height();
+    }
+}
+
 void QQuickGridViewPrivate::resetColumns()
 {
     Q_Q(QQuickGridView);
-    qreal length = flow == QQuickGridView::LeftToRight ? q->width() : q->height();
+    qreal length = flow == QQuickGridView::FlowLeftToRight ? q->width() : q->height();
     columns = (int)qMax((length + colSize()/2) / colSize(), qreal(1.));
 }
 
@@ -641,15 +682,22 @@ void QQuickGridViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
 {
     Q_Q(QQuickGridView);
     qreal pos = position();
-    if (flow == QQuickGridView::LeftToRight) {
-        if (item->y() + item->height() > pos && item->y() < pos + q->height())
-            item->setPos(QPointF(colPosAt(index), rowPosAt(index)));
+    if (flow == QQuickGridView::FlowLeftToRight) {
+        if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
+            qreal y = (verticalLayoutDirection == QQuickItemView::TopToBottom)
+                    ? rowPosAt(index)
+                    : -rowPosAt(index) - item->height();
+            item->setPos(QPointF(colPosAt(index), y));
+        }
     } else {
         if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
-            if (isRightToLeftTopToBottom())
-                item->setPos(QPointF(-rowPosAt(index)-item->width(), colPosAt(index)));
+            qreal y = (verticalLayoutDirection == QQuickItemView::TopToBottom)
+                    ? colPosAt(index)
+                    : -colPosAt(index) - item->height();
+            if (flow == QQuickGridView::FlowTopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft)
+                item->setPos(QPointF(-rowPosAt(index)-item->width(), y));
             else
-                item->setPos(QPointF(rowPosAt(index), colPosAt(index)));
+                item->setPos(QPointF(rowPosAt(index), y));
         }
     }
 }
@@ -744,14 +792,14 @@ qreal QQuickGridViewPrivate::headerSize() const
 {
     if (!header)
         return 0.0;
-    return flow == QQuickGridView::LeftToRight ? header->item->height() : header->item->width();
+    return flow == QQuickGridView::FlowLeftToRight ? header->item->height() : header->item->width();
 }
 
 qreal QQuickGridViewPrivate::footerSize() const
 {
     if (!footer)
         return 0.0;
-    return flow == QQuickGridView::LeftToRight? footer->item->height() : footer->item->width();
+    return flow == QQuickGridView::FlowLeftToRight? footer->item->height() : footer->item->width();
 }
 
 bool QQuickGridViewPrivate::showHeaderForIndex(int index) const
@@ -781,17 +829,23 @@ void QQuickGridViewPrivate::updateFooter()
     qreal colOffset = 0;
     qreal rowOffset = 0;
     if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
-        if (flow == QQuickGridView::TopToBottom)
-            rowOffset = gridItem->item->width() - cellWidth;
+        if (flow == QQuickGridView::FlowTopToBottom)
+            rowOffset += gridItem->item->width() - cellWidth;
+        else
+            colOffset += gridItem->item->width() - cellWidth;
+    }
+    if (verticalLayoutDirection == QQuickItemView::BottomToTop) {
+        if (flow == QQuickGridView::FlowTopToBottom)
+            colOffset += gridItem->item->height() - cellHeight;
         else
-            colOffset = gridItem->item->width() - cellWidth;
+            rowOffset += gridItem->item->height() - cellHeight;
     }
     if (visibleItems.count()) {
         qreal endPos = lastPosition();
         if (findLastVisibleIndex() == model->count()-1) {
             gridItem->setPosition(colOffset, endPos + rowOffset);
         } else {
-            qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size();
+            qreal visiblePos = isContentFlowReversed() ? -position() : position() + size();
             if (endPos <= visiblePos || gridItem->endPosition() <= endPos + rowOffset)
                 gridItem->setPosition(colOffset, endPos + rowOffset);
         }
@@ -820,23 +874,29 @@ void QQuickGridViewPrivate::updateHeader()
     qreal colOffset = 0;
     qreal rowOffset = -headerSize();
     if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
-        if (flow == QQuickGridView::TopToBottom)
-            rowOffset += gridItem->item->width()-cellWidth;
+        if (flow == QQuickGridView::FlowTopToBottom)
+            rowOffset += gridItem->item->width() - cellWidth;
+        else
+            colOffset += gridItem->item->width() - cellWidth;
+    }
+    if (verticalLayoutDirection == QQuickItemView::BottomToTop) {
+        if (flow == QQuickGridView::FlowTopToBottom)
+            colOffset += gridItem->item->height() - cellHeight;
         else
-            colOffset = gridItem->item->width()-cellWidth;
+            rowOffset += gridItem->item->height() - cellHeight;
     }
     if (visibleItems.count()) {
         qreal startPos = originPosition();
         if (visibleIndex == 0) {
             gridItem->setPosition(colOffset, startPos + rowOffset);
         } else {
-            qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position();
-            qreal headerPos = isRightToLeftTopToBottom() ? gridItem->rowPos() + cellWidth - headerSize() : gridItem->rowPos();
+            qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
+            qreal headerPos = isContentFlowReversed() ? gridItem->rowPos() + cellWidth - headerSize() : gridItem->rowPos();
             if (tempPos <= startPos || headerPos > startPos + rowOffset)
                 gridItem->setPosition(colOffset, startPos + rowOffset);
         }
     } else {
-        if (isRightToLeftTopToBottom())
+        if (isContentFlowReversed())
             gridItem->setPosition(colOffset, rowOffset);
         else
             gridItem->setPosition(colOffset, -headerSize());
@@ -861,7 +921,7 @@ void QQuickGridViewPrivate::initializeCurrentItem()
 void QQuickGridViewPrivate::fixupPosition()
 {
     moveReason = Other;
-    if (flow == QQuickGridView::LeftToRight)
+    if (flow == QQuickGridView::FlowLeftToRight)
         fixupY();
     else
         fixupX();
@@ -869,17 +929,17 @@ void QQuickGridViewPrivate::fixupPosition()
 
 void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
 {
-    if ((flow == QQuickGridView::TopToBottom && &data == &vData)
-        || (flow == QQuickGridView::LeftToRight && &data == &hData))
+    if ((flow == QQuickGridView::FlowTopToBottom && &data == &vData)
+        || (flow == QQuickGridView::FlowLeftToRight && &data == &hData))
         return;
 
     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
 
-    qreal viewPos = isRightToLeftTopToBottom() ? -position()-size() : position();
+    qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
 
     bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange;
     if (snapMode != QQuickGridView::NoSnap) {
-        qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position();
+        qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
         if (snapMode == QQuickGridView::SnapOneRow && moveReason == Mouse) {
             // if we've been dragged < rowSize()/2 then bias towards the next row
             qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
@@ -888,7 +948,7 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
                 bias = rowSize()/2;
             else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -rowSize()/2)
                 bias = -rowSize()/2;
-            if (isRightToLeftTopToBottom())
+            if (isContentFlowReversed())
                 bias = -bias;
             tempPosition -= bias;
         }
@@ -909,15 +969,15 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
         if (topItem && (isInBounds || strictHighlightRange)) {
             qreal headerPos = header ? static_cast<FxGridItemSG*>(header)->rowPos() : 0;
             if (topItem->index == 0 && header && tempPosition+highlightRangeStart < headerPos+headerSize()/2 && !strictHighlightRange) {
-                pos = isRightToLeftTopToBottom() ? - headerPos + highlightRangeStart - size() : headerPos - highlightRangeStart;
+                pos = isContentFlowReversed() ? - headerPos + highlightRangeStart - size() : headerPos - highlightRangeStart;
             } else {
-                if (isRightToLeftTopToBottom())
+                if (isContentFlowReversed())
                     pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
                 else
                     pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
             }
         } else if (bottomItem && isInBounds) {
-            if (isRightToLeftTopToBottom())
+            if (isContentFlowReversed())
                 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
             else
                 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
@@ -945,7 +1005,7 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
                 viewPos = pos + rowSize() - highlightRangeEnd;
             if (viewPos > pos - highlightRangeStart)
                 viewPos = pos - highlightRangeStart;
-            if (isRightToLeftTopToBottom())
+            if (isContentFlowReversed())
                 viewPos = -viewPos-size();
             timeline.reset(data.move);
             if (viewPos != position()) {
@@ -977,7 +1037,7 @@ void QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
         return;
     }
     qreal maxDistance = 0;
-    qreal dataValue = isRightToLeftTopToBottom() ? -data.move.value()+size() : data.move.value();
+    qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
     // -ve velocity means list is moving up/left
     if (velocity > 0) {
         if (data.move.value() < minExtent) {
@@ -985,7 +1045,7 @@ void QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
                 // if we've been dragged < averageSize/2 then bias towards the next item
                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
                 qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0;
-                if (isRightToLeftTopToBottom())
+                if (isContentFlowReversed())
                     bias = -bias;
                 data.flickTarget = -snapPosAt(-dataValue - bias);
                 maxDistance = qAbs(data.flickTarget - data.move.value());
@@ -1002,7 +1062,7 @@ void QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
                 // if we've been dragged < averageSize/2 then bias towards the next item
                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
                 qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0;
-                if (isRightToLeftTopToBottom())
+                if (isContentFlowReversed())
                     bias = -bias;
                 data.flickTarget = -snapPosAt(-dataValue + bias);
                 maxDistance = qAbs(data.flickTarget - data.move.value());
@@ -1034,10 +1094,10 @@ void QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
             if (v > 0)
                 dist = -dist;
             if (snapMode != QQuickGridView::SnapOneRow) {
-                qreal distTemp = isRightToLeftTopToBottom() ? -dist : dist;
+                qreal distTemp = isContentFlowReversed() ? -dist : dist;
                 data.flickTarget = -snapPosAt(-dataValue + distTemp);
             }
-            data.flickTarget = isRightToLeftTopToBottom() ? -data.flickTarget+size() : data.flickTarget;
+            data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
             if (overShoot) {
                 if (data.flickTarget >= minExtent) {
                     overshootDist = overShootDistance(vSize);
@@ -1159,6 +1219,60 @@ void QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
     to set this property to true in order to clip the items that are partially or
     fully outside the view.
 
+
+    \section1 GridView layouts
+
+    The layout of the items in a GridView can be controlled by these properties:
+
+    \list
+    \li \l flow - controls whether items flow from left to right (as a series of rows)
+        or from top to bottom (as a series of columns). This value can be either
+        GridView.LeftToRight or GridView.TopToBottom.
+    \li \l layoutDirection - controls the horizontal layout direction: that is, whether items
+        are laid out from the left side of the view to the right, or vice-versa. This value can
+        be either Qt.LeftToRight or Qt.RightToLeft.
+    \li \l verticalLayoutDirection - controls the vertical layout direction: that is, whether items
+        are laid out from the top of the view down towards the bottom of the view, or vice-versa.
+        This value can be either GridView.TopToBottom or GridView.BottomToTop.
+    \endlist
+
+    By default, a GridView flows from left to right, and items are laid out from left to right
+    horizontally, and from top to bottom vertically.
+
+    These properties can be combined to produce a variety of layouts, as shown in the table below.
+    The GridViews in the first row all have a \l flow value of GridView.LeftToRight, but use
+    different combinations of horizontal and vertical layout directions (specified by \l layoutDirection
+    and \l verticalLayoutDirection respectively). Similarly, the GridViews in the second row below
+    all have a \l flow value of GridView.TopToBottom, but use different combinations of horizontal and
+    vertical layout directions to lay out their items in different ways.
+
+    \table
+    \header
+        \li {4, 1}
+            \bold GridViews with GridView.LeftToRight flow
+    \row
+        \li \bold (H) Left to right \bold (V) Top to bottom
+            \image gridview-layout-lefttoright-ltr-ttb.png
+        \li \bold (H) Right to left \bold (V) Top to bottom
+            \image gridview-layout-lefttoright-rtl-ttb.png
+        \li \bold (H) Left to right \bold (V) Bottom to top
+            \image gridview-layout-lefttoright-ltr-btt.png
+        \li \bold (H) Right to left \bold (V) Bottom to top
+            \image gridview-layout-lefttoright-rtl-btt.png
+    \header
+        \li {4, 1}
+            \bold GridViews with GridView.TopToBottom flow
+    \row
+        \li \bold (H) Left to right \bold (V) Top to bottom
+            \image gridview-layout-toptobottom-ltr-ttb.png
+        \li \bold (H) Right to left \bold (V) Top to bottom
+            \image gridview-layout-toptobottom-rtl-ttb.png
+        \li \bold (H) Left to right \bold (V) Bottom to top
+            \image gridview-layout-toptobottom-ltr-btt.png
+        \li \bold (H) Right to left \bold (V) Bottom to top
+            \image gridview-layout-toptobottom-rtl-btt.png
+    \endtable
+
     \sa {declarative/modelviews/gridview}{GridView example}
 */
 
@@ -1380,6 +1494,8 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
   \b Note: If GridView::flow is set to GridView.LeftToRight, this is not to be confused if
   GridView::layoutDirection is set to Qt.RightToLeft. The GridView.LeftToRight flow value simply
   indicates that the flow is horizontal.
+
+  \sa GridView::effectiveLayoutDirection, GridView::verticalLayoutDirection
 */
 
 
@@ -1393,6 +1509,21 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
 
     \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
 */
+
+/*!
+  \qmlproperty enumeration QtQuick2::GridView::verticalLayoutDirection
+  This property holds the vertical layout direction of the grid.
+
+  Possible values:
+
+  \list
+  \li GridView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
+  \li GridView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
+  \endlist
+
+  \sa GridView::layoutDirection
+*/
+
 /*!
   \qmlproperty bool QtQuick2::GridView::keyNavigationWraps
   This property holds whether the grid wraps key navigation
@@ -1461,7 +1592,7 @@ void QQuickGridView::setFlow(Flow flow)
     Q_D(QQuickGridView);
     if (d->flow != flow) {
         d->flow = flow;
-        if (d->flow == LeftToRight) {
+        if (d->flow == FlowLeftToRight) {
             setContentWidth(-1);
             setFlickableDirection(VerticalFlick);
         } else {
@@ -1877,12 +2008,17 @@ void QQuickGridView::viewportMoved()
         return;
     d->inViewportMoved = true;
 
-    if (yflick())
-        d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
-    else if (d->isRightToLeftTopToBottom())
-        d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
-    else
-        d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
+    if (yflick()) {
+        if (d->isContentFlowReversed())
+            d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
+        else
+            d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
+    } else {
+        if (d->isContentFlowReversed())
+            d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
+        else
+            d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
+    }
 
     d->refillOrLayout();
 
@@ -1904,7 +2040,7 @@ void QQuickGridView::viewportMoved()
         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
             // reposition highlight
             qreal pos = d->highlight->position();
-            qreal viewPos = d->isRightToLeftTopToBottom() ? -d->position()-d->size() : d->position();
+            qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
             if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
                 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
             if (pos < viewPos + d->highlightRangeStart)
@@ -1923,7 +2059,7 @@ void QQuickGridView::viewportMoved()
             if (idx >= 0 && idx != d->currentIndex) {
                 d->updateCurrent(idx);
                 if (d->currentItem && static_cast<FxGridItemSG*>(d->currentItem)->colPos() != static_cast<FxGridItemSG*>(d->highlight)->colPos() && d->autoHighlight) {
-                    if (d->flow == LeftToRight)
+                    if (d->flow == FlowLeftToRight)
                         d->highlightXAnimator->to = d->currentItem->itemX();
                     else
                         d->highlightYAnimator->to = d->currentItem->itemY();
@@ -1970,6 +2106,16 @@ void QQuickGridView::geometryChanged(const QRectF &newGeometry, const QRectF &ol
 {
     Q_D(QQuickGridView);
     d->resetColumns();
+
+    if (newGeometry.width() != oldGeometry.width()
+            && newGeometry.height() != oldGeometry.height()) {
+        d->setPosition(d->position());
+    } else if (newGeometry.width() != oldGeometry.width()) {
+        QQuickFlickable::setContentX(d->contentXForPosition(d->position()));
+    } else if (newGeometry.height() != oldGeometry.height()) {
+        QQuickFlickable::setContentY(d->contentYForPosition(d->position()));
+    }
+
     QQuickItemView::geometryChanged(newGeometry, oldGeometry);
 }
 
@@ -1990,15 +2136,29 @@ void QQuickGridView::moveCurrentIndexUp()
     const int count = d->model ? d->model->count() : 0;
     if (!count)
         return;
-    if (d->flow == QQuickGridView::LeftToRight) {
-        if (currentIndex() >= d->columns || d->wrap) {
-            int index = currentIndex() - d->columns;
-            setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+    if (d->verticalLayoutDirection == QQuickItemView::TopToBottom) {
+        if (d->flow == QQuickGridView::FlowLeftToRight) {
+            if (currentIndex() >= d->columns || d->wrap) {
+                int index = currentIndex() - d->columns;
+                setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+            }
+        } else {
+            if (currentIndex() > 0 || d->wrap) {
+                int index = currentIndex() - 1;
+                setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+            }
         }
     } else {
-        if (currentIndex() > 0 || d->wrap) {
-            int index = currentIndex() - 1;
-            setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+        if (d->flow == QQuickGridView::FlowLeftToRight) {
+            if (currentIndex() < count - d->columns || d->wrap) {
+                int index = currentIndex()+d->columns;
+                setCurrentIndex((index >= 0 && index < count) ? index : 0);
+            }
+        } else {
+            if (currentIndex() < count - 1 || d->wrap) {
+                int index = currentIndex() + 1;
+                setCurrentIndex((index >= 0 && index < count) ? index : 0);
+            }
         }
     }
 }
@@ -2018,15 +2178,30 @@ void QQuickGridView::moveCurrentIndexDown()
     const int count = d->model ? d->model->count() : 0;
     if (!count)
         return;
-    if (d->flow == QQuickGridView::LeftToRight) {
-        if (currentIndex() < count - d->columns || d->wrap) {
-            int index = currentIndex()+d->columns;
-            setCurrentIndex((index >= 0 && index < count) ? index : 0);
+
+    if (d->verticalLayoutDirection == QQuickItemView::TopToBottom) {
+        if (d->flow == QQuickGridView::FlowLeftToRight) {
+            if (currentIndex() < count - d->columns || d->wrap) {
+                int index = currentIndex()+d->columns;
+                setCurrentIndex((index >= 0 && index < count) ? index : 0);
+            }
+        } else {
+            if (currentIndex() < count - 1 || d->wrap) {
+                int index = currentIndex() + 1;
+                setCurrentIndex((index >= 0 && index < count) ? index : 0);
+            }
         }
     } else {
-        if (currentIndex() < count - 1 || d->wrap) {
-            int index = currentIndex() + 1;
-            setCurrentIndex((index >= 0 && index < count) ? index : 0);
+        if (d->flow == QQuickGridView::FlowLeftToRight) {
+            if (currentIndex() >= d->columns || d->wrap) {
+                int index = currentIndex() - d->columns;
+                setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+            }
+        } else {
+            if (currentIndex() > 0 || d->wrap) {
+                int index = currentIndex() - 1;
+                setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+            }
         }
     }
 }
@@ -2047,7 +2222,7 @@ void QQuickGridView::moveCurrentIndexLeft()
     if (!count)
         return;
     if (effectiveLayoutDirection() == Qt::LeftToRight) {
-        if (d->flow == QQuickGridView::LeftToRight) {
+        if (d->flow == QQuickGridView::FlowLeftToRight) {
             if (currentIndex() > 0 || d->wrap) {
                 int index = currentIndex() - 1;
                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
@@ -2059,7 +2234,7 @@ void QQuickGridView::moveCurrentIndexLeft()
             }
         }
     } else {
-        if (d->flow == QQuickGridView::LeftToRight) {
+        if (d->flow == QQuickGridView::FlowLeftToRight) {
             if (currentIndex() < count - 1 || d->wrap) {
                 int index = currentIndex() + 1;
                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
@@ -2090,7 +2265,7 @@ void QQuickGridView::moveCurrentIndexRight()
     if (!count)
         return;
     if (effectiveLayoutDirection() == Qt::LeftToRight) {
-        if (d->flow == QQuickGridView::LeftToRight) {
+        if (d->flow == QQuickGridView::FlowLeftToRight) {
             if (currentIndex() < count - 1 || d->wrap) {
                 int index = currentIndex() + 1;
                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
@@ -2102,7 +2277,7 @@ void QQuickGridView::moveCurrentIndexRight()
             }
         }
     } else {
-        if (d->flow == QQuickGridView::LeftToRight) {
+        if (d->flow == QQuickGridView::FlowLeftToRight) {
             if (currentIndex() > 0 || d->wrap) {
                 int index = currentIndex() - 1;
                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
@@ -2146,7 +2321,7 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &
         }
     }
 
-    qreal tempPos = isRightToLeftTopToBottom() ? -position()-size()+q->width()+1 : position();
+    qreal tempPos = isContentFlowReversed() ? -position()-size()+q->width()+1 : position();
     qreal colPos = 0;
     qreal rowPos = 0;
     int colNum = 0;
index ac3c8f0..789c641 100644 (file)
@@ -69,13 +69,17 @@ class Q_AUTOTEST_EXPORT QQuickGridView : public QQuickItemView
     Q_CLASSINFO("DefaultProperty", "data")
 
 public:
+    enum Flow {
+        FlowLeftToRight = LeftToRight,
+        FlowTopToBottom = TopToBottom
+    };
+
     QQuickGridView(QQuickItem *parent=0);
     ~QQuickGridView();
 
     virtual void setHighlightFollowsCurrentItem(bool);
     virtual void setHighlightMoveDuration(int);
 
-    enum Flow { LeftToRight, TopToBottom };
     Flow flow() const;
     void setFlow(Flow);
 
index 64c05aa..e70e923 100644 (file)
@@ -475,6 +475,21 @@ Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const
         return d->layoutDirection;
 }
 
+QQuickItemView::VerticalLayoutDirection QQuickItemView::verticalLayoutDirection() const
+{
+    Q_D(const QQuickItemView);
+    return d->verticalLayoutDirection;
+}
+
+void QQuickItemView::setVerticalLayoutDirection(VerticalLayoutDirection layoutDirection)
+{
+    Q_D(QQuickItemView);
+    if (d->verticalLayoutDirection != layoutDirection) {
+        d->verticalLayoutDirection = layoutDirection;
+        d->regenerate();
+        emit verticalLayoutDirectionChanged();
+    }
+}
 
 QQmlComponent *QQuickItemView::header() const
 {
@@ -818,7 +833,7 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
     FxViewItem *item = visibleItem(idx);
     qreal maxExtent;
     if (layoutOrientation() == Qt::Vertical)
-        maxExtent = -q->maxYExtent();
+        maxExtent = isContentFlowReversed() ? q->minYExtent()-size(): -q->maxYExtent();
     else
         maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent();
     if (!item) {
@@ -864,7 +879,7 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
         pos = qMin(pos, maxExtent);
         qreal minExtent;
         if (layoutOrientation() == Qt::Vertical)
-            minExtent = -q->minYExtent();
+            minExtent = isContentFlowReversed() ? q->maxYExtent()-size(): -q->minYExtent();
         else
             minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent();
         pos = qMax(pos, minExtent);
@@ -948,6 +963,89 @@ int QQuickItemViewPrivate::findMoveKeyIndex(QQuickChangeSet::MoveKey key, const
     return -1;
 }
 
+qreal QQuickItemViewPrivate::minExtentForAxis(const AxisData &axisData, bool forXAxis) const
+{
+    Q_Q(const QQuickItemView);
+
+    qreal highlightStart;
+    qreal highlightEnd;
+    qreal endPositionFirstItem = 0;
+    qreal extent = -startPosition() + axisData.startMargin;
+    if (isContentFlowReversed()) {
+        if (model && model->count())
+            endPositionFirstItem = positionAt(model->count()-1);
+        else
+            extent += headerSize();
+        highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
+        highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
+        extent += footerSize();
+        qreal maxExtentAlongAxis = forXAxis ? q->maxXExtent() : q->maxYExtent();
+        if (extent < maxExtentAlongAxis)
+            extent = maxExtentAlongAxis;
+    } else {
+        endPositionFirstItem = endPositionAt(0);
+        highlightStart = highlightRangeStart;
+        highlightEnd = highlightRangeEnd;
+        extent += headerSize();
+    }
+    if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
+        extent += highlightStart;
+        FxViewItem *firstItem = visibleItem(0);
+        if (firstItem)
+            extent -= firstItem->sectionSize();
+        extent = isContentFlowReversed()
+                            ? qMin(extent, endPositionFirstItem + highlightEnd)
+                            : qMax(extent, -(endPositionFirstItem - highlightEnd));
+    }
+    return extent;
+}
+
+qreal QQuickItemViewPrivate::maxExtentForAxis(const AxisData &axisData, bool forXAxis) const
+{
+    Q_Q(const QQuickItemView);
+
+    qreal highlightStart;
+    qreal highlightEnd;
+    qreal lastItemPosition = 0;
+    qreal extent = 0;
+    if (isContentFlowReversed()) {
+        highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
+        highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
+        lastItemPosition = endPosition();
+    } else {
+        highlightStart = highlightRangeStart;
+        highlightEnd = highlightRangeEnd;
+        if (model && model->count())
+            lastItemPosition = positionAt(model->count()-1);
+    }
+    if (!model || !model->count()) {
+        if (!isContentFlowReversed())
+            maxExtent = header ? -headerSize() : 0;
+        extent += forXAxis ? q->width() : q->height();
+    } else if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
+        extent = -(lastItemPosition - highlightStart);
+        if (highlightEnd != highlightStart) {
+            extent = isContentFlowReversed()
+                    ? qMax(extent, -(endPosition() - highlightEnd))
+                    : qMin(extent, -(endPosition() - highlightEnd));
+        }
+    } else {
+        extent = -(endPosition() - (forXAxis ? q->width() : q->height()));
+    }
+    if (isContentFlowReversed()) {
+        extent -= headerSize();
+        extent -= axisData.endMargin;
+    } else {
+        extent -= footerSize();
+        extent -= axisData.endMargin;
+        qreal minExtentAlongAxis = forXAxis ? q->minXExtent() : q->minYExtent();
+        if (extent > minExtentAlongAxis)
+            extent = minExtentAlongAxis;
+    }
+
+    return extent;
+}
+
 // for debugging only
 void QQuickItemViewPrivate::checkVisible() const
 {
@@ -1119,12 +1217,17 @@ void QQuickItemView::trackedPositionChanged()
                 toItemEndPos -= startOffset;
             } else if (d->showFooterForIndex(d->currentIndex)) {
                 qreal endOffset = d->footerSize();
-                if (d->layoutOrientation() == Qt::Vertical)
-                    endOffset += d->vData.endMargin;
-                else if (d->isContentFlowReversed())
-                    endOffset += d->hData.startMargin;
-                else
-                    endOffset += d->hData.endMargin;
+                if (d->layoutOrientation() == Qt::Vertical) {
+                    if (d->isContentFlowReversed())
+                        endOffset += d->vData.startMargin;
+                    else
+                        endOffset += d->vData.endMargin;
+                } else {
+                    if (d->isContentFlowReversed())
+                        endOffset += d->hData.startMargin;
+                    else
+                        endOffset += d->hData.endMargin;
+                }
                 trackedPos += endOffset;
                 trackedEndPos += endOffset;
                 toItemPos += endOffset;
@@ -1166,7 +1269,6 @@ void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &ol
     QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
 }
 
-
 qreal QQuickItemView::minYExtent() const
 {
     Q_D(const QQuickItemView);
@@ -1174,15 +1276,7 @@ qreal QQuickItemView::minYExtent() const
         return QQuickFlickable::minYExtent();
 
     if (d->vData.minExtentDirty) {
-        d->minExtent = d->vData.startMargin-d->startPosition();
-        if (d->header)
-            d->minExtent += d->headerSize();
-        if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
-            d->minExtent += d->highlightRangeStart;
-            if (d->visibleItem(0))
-                d->minExtent -= d->visibleItem(0)->sectionSize();
-            d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd));
-        }
+        d->minExtent = d->minExtentForAxis(d->vData, false);
         d->vData.minExtentDirty = false;
     }
 
@@ -1196,25 +1290,10 @@ qreal QQuickItemView::maxYExtent() const
         return height();
 
     if (d->vData.maxExtentDirty) {
-        if (!d->model || !d->model->count()) {
-            d->maxExtent = d->header ? -d->headerSize() : 0;
-            d->maxExtent += height();
-        } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
-            d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
-            if (d->highlightRangeEnd != d->highlightRangeStart)
-                d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd));
-        } else {
-            d->maxExtent = -(d->endPosition() - height());
-        }
-
-        if (d->footer)
-            d->maxExtent -= d->footerSize();
-        d->maxExtent -= d->vData.endMargin;
-        qreal minY = minYExtent();
-        if (d->maxExtent > minY)
-            d->maxExtent = minY;
+        d->maxExtent = d->maxExtentForAxis(d->vData, false);
         d->vData.maxExtentDirty = false;
     }
+
     return d->maxExtent;
 }
 
@@ -1225,35 +1304,7 @@ qreal QQuickItemView::minXExtent() const
         return QQuickFlickable::minXExtent();
 
     if (d->hData.minExtentDirty) {
-        d->minExtent = -d->startPosition() + d->hData.startMargin;
-        qreal highlightStart;
-        qreal highlightEnd;
-        qreal endPositionFirstItem = 0;
-        if (d->isContentFlowReversed()) {
-            if (d->model && d->model->count())
-                endPositionFirstItem = d->positionAt(d->model->count()-1);
-            else if (d->header)
-                d->minExtent += d->headerSize();
-            highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size();
-            highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size();
-            if (d->footer)
-                d->minExtent += d->footerSize();
-            qreal maxX = maxXExtent();
-            if (d->minExtent < maxX)
-                d->minExtent = maxX;
-        } else {
-            endPositionFirstItem = d->endPositionAt(0);
-            highlightStart = d->highlightRangeStart;
-            highlightEnd = d->highlightRangeEnd;
-            if (d->header)
-                d->minExtent += d->headerSize();
-        }
-        if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
-            d->minExtent += highlightStart;
-            d->minExtent = d->isContentFlowReversed()
-                                ? qMin(d->minExtent, endPositionFirstItem + highlightEnd)
-                                : qMax(d->minExtent, -(endPositionFirstItem - highlightEnd));
-        }
+        d->minExtent = d->minExtentForAxis(d->hData, true);
         d->hData.minExtentDirty = false;
     }
 
@@ -1267,46 +1318,7 @@ qreal QQuickItemView::maxXExtent() const
         return width();
 
     if (d->hData.maxExtentDirty) {
-        qreal highlightStart;
-        qreal highlightEnd;
-        qreal lastItemPosition = 0;
-        d->maxExtent = 0;
-        if (d->isContentFlowReversed()) {
-            highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size();
-            highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size();
-            lastItemPosition = d->endPosition();
-        } else {
-            highlightStart = d->highlightRangeStart;
-            highlightEnd = d->highlightRangeEnd;
-            if (d->model && d->model->count())
-                lastItemPosition = d->positionAt(d->model->count()-1);
-        }
-        if (!d->model || !d->model->count()) {
-            if (!d->isContentFlowReversed())
-                d->maxExtent = d->header ? -d->headerSize() : 0;
-            d->maxExtent += width();
-        } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
-            d->maxExtent = -(lastItemPosition - highlightStart);
-            if (highlightEnd != highlightStart) {
-                d->maxExtent = d->isContentFlowReversed()
-                        ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd))
-                        : qMin(d->maxExtent, -(d->endPosition() - highlightEnd));
-            }
-        } else {
-            d->maxExtent = -(d->endPosition() - width());
-        }
-        if (d->isContentFlowReversed()) {
-            if (d->header)
-                d->maxExtent -= d->headerSize();
-            d->maxExtent -= d->hData.endMargin;
-        } else {
-            if (d->footer)
-                d->maxExtent -= d->footerSize();
-            d->maxExtent -= d->hData.endMargin;
-            qreal minX = minXExtent();
-            if (d->maxExtent > minX)
-                d->maxExtent = minX;
-        }
+        d->maxExtent = d->maxExtentForAxis(d->hData, true);
         d->hData.maxExtentDirty = false;
     }
 
@@ -1338,6 +1350,15 @@ qreal QQuickItemView::xOrigin() const
         return -minXExtent() + d->hData.startMargin;
 }
 
+qreal QQuickItemView::yOrigin() const
+{
+    Q_D(const QQuickItemView);
+    if (d->isContentFlowReversed())
+        return -maxYExtent() + d->size() - d->vData.endMargin;
+    else
+        return -minYExtent() + d->vData.startMargin;
+}
+
 void QQuickItemView::updatePolish()
 {
     Q_D(QQuickItemView);
@@ -1385,7 +1406,7 @@ void QQuickItemView::componentComplete()
 QQuickItemViewPrivate::QQuickItemViewPrivate()
     : itemCount(0)
     , buffer(0), bufferMode(BufferBefore | BufferAfter)
-    , layoutDirection(Qt::LeftToRight)
+    , layoutDirection(Qt::LeftToRight), verticalLayoutDirection(QQuickItemView::TopToBottom)
     , moveReason(Other)
     , visibleIndex(0)
     , currentIndex(-1), currentItem(0)
@@ -1442,13 +1463,17 @@ qreal QQuickItemViewPrivate::endPosition() const
 qreal QQuickItemViewPrivate::contentStartOffset() const
 {
     qreal pos = -headerSize();
-    if (layoutOrientation() == Qt::Vertical)
-        pos -= vData.startMargin;
-    else if (isContentFlowReversed())
-        pos -= hData.endMargin;
-    else
-        pos -= hData.startMargin;
-
+    if (layoutOrientation() == Qt::Vertical) {
+        if (isContentFlowReversed())
+            pos -= vData.endMargin;
+        else
+            pos -= vData.startMargin;
+    } else {
+        if (isContentFlowReversed())
+            pos -= hData.endMargin;
+        else
+            pos -= hData.startMargin;
+    }
     return pos;
 }
 
index f252fb5..89e5930 100644 (file)
@@ -70,6 +70,7 @@ class Q_AUTOTEST_EXPORT QQuickItemView : public QQuickFlickable
 
     Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged)
     Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged)
+    Q_PROPERTY(VerticalLayoutDirection verticalLayoutDirection READ verticalLayoutDirection WRITE setVerticalLayoutDirection NOTIFY verticalLayoutDirectionChanged)
 
     Q_PROPERTY(QQmlComponent *header READ header WRITE setHeader NOTIFY headerChanged)
     Q_PROPERTY(QQuickItem *headerItem READ headerItem NOTIFY headerItemChanged)
@@ -95,8 +96,25 @@ class Q_AUTOTEST_EXPORT QQuickItemView : public QQuickFlickable
 
     Q_ENUMS(HighlightRangeMode)
     Q_ENUMS(PositionMode)
+    Q_ENUMS(VerticalLayoutDirection)
+    Q_ENUMS(LayoutDirection)
 
 public:
+    // this holds all layout enum values so they can be referred to by other enums
+    // to ensure consistent values - e.g. QML references to GridView.TopToBottom flow
+    // and GridView.TopToBottom vertical layout direction should have same value
+    enum LayoutDirection {
+        LeftToRight = Qt::LeftToRight,
+        RightToLeft = Qt::RightToLeft,
+        VerticalTopToBottom,
+        VerticalBottomToTop
+    };
+
+    enum VerticalLayoutDirection {
+        TopToBottom = VerticalTopToBottom,
+        BottomToTop = VerticalBottomToTop
+    };
+
     QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent = 0);
     ~QQuickItemView();
 
@@ -123,6 +141,9 @@ public:
     void setLayoutDirection(Qt::LayoutDirection);
     Qt::LayoutDirection effectiveLayoutDirection() const;
 
+    VerticalLayoutDirection verticalLayoutDirection() const;
+    void setVerticalLayoutDirection(VerticalLayoutDirection layoutDirection);
+
     QQmlComponent *footer() const;
     void setFooter(QQmlComponent *);
     QQuickItem *footerItem() const;
@@ -189,6 +210,7 @@ public:
     virtual void setContentX(qreal pos);
     virtual void setContentY(qreal pos);
     virtual qreal xOrigin() const;
+    virtual qreal yOrigin() const;
 
 signals:
     void modelChanged();
@@ -202,6 +224,7 @@ signals:
 
     void layoutDirectionChanged();
     void effectiveLayoutDirectionChanged();
+    void verticalLayoutDirectionChanged();
 
     void headerChanged();
     void footerChanged();
index 1e29faf..8fc83a9 100644 (file)
@@ -204,6 +204,10 @@ public:
     void updateUnrequestedPositions();
     void updateVisibleIndex();
     void positionViewAtIndex(int index, int mode);
+
+    qreal minExtentForAxis(const AxisData &axisData, bool forXAxis) const;
+    qreal maxExtentForAxis(const AxisData &axisData, bool forXAxis) const;
+
     void applyPendingChanges();
     bool applyModelChanges(ChangeResult *insertionResult, ChangeResult *removalResult);
     bool applyRemovalChange(const QQuickChangeSet::Remove &removal, ChangeResult *changeResult, int *removedCount);
@@ -248,6 +252,7 @@ public:
     int buffer;
     int bufferMode;
     Qt::LayoutDirection layoutDirection;
+    QQuickItemView::VerticalLayoutDirection verticalLayoutDirection;
 
     MovementReason moveReason;
 
index b65cb85..df50d3e 100644 (file)
@@ -73,6 +73,7 @@ public:
     virtual Qt::Orientation layoutOrientation() const;
     virtual bool isContentFlowReversed() const;
     bool isRightToLeft() const;
+    bool isBottomToTop() const;
 
     virtual qreal positionAt(int index) const;
     virtual qreal endPositionAt(int index) const;
@@ -265,7 +266,7 @@ public:
     qreal position() const {
         if (section()) {
             if (view->orientation() == QQuickListView::Vertical)
-                return section()->y();
+                return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -section()->height()-section()->y() : section()->y());
             else
                 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
         } else {
@@ -274,7 +275,7 @@ public:
     }
     qreal itemPosition() const {
         if (view->orientation() == QQuickListView::Vertical)
-            return itemY();
+            return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -item->height()-itemY() : itemY());
         else
             return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX());
     }
@@ -294,7 +295,9 @@ public:
     }
     qreal endPosition() const {
         if (view->orientation() == QQuickListView::Vertical) {
-            return itemY() + item->height();
+            return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop
+                    ? -itemY()
+                    : itemY() + item->height());
         } else {
             return (view->effectiveLayoutDirection() == Qt::RightToLeft
                     ? -itemX()
@@ -305,7 +308,10 @@ public:
         // position the section immediately even if there is a transition
         if (section()) {
             if (view->orientation() == QQuickListView::Vertical) {
-                section()->setY(pos);
+                if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
+                    section()->setY(-section()->height()-pos);
+                else
+                    section()->setY(pos);
             } else {
                 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
                     section()->setX(-section()->width()-pos);
@@ -331,9 +337,15 @@ public:
 private:
     QPointF pointForPosition(qreal pos) const {
         if (view->orientation() == QQuickListView::Vertical) {
-            if (section())
-                pos += section()->height();
-            return QPointF(itemX(), pos);
+            if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
+                if (section())
+                    pos += section()->height();
+                return QPointF(itemX(), -item->height() - pos);
+            } else {
+                if (section())
+                    pos += section()->height();
+                return QPointF(itemX(), pos);
+            }
         } else {
             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
                 if (section())
@@ -352,7 +364,7 @@ private:
 
 bool QQuickListViewPrivate::isContentFlowReversed() const
 {
-    return isRightToLeft();
+    return isRightToLeft() || isBottomToTop();
 }
 
 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
@@ -366,6 +378,11 @@ bool QQuickListViewPrivate::isRightToLeft() const
     return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
 }
 
+bool QQuickListViewPrivate::isBottomToTop() const
+{
+    return orient == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop;
+}
+
 // Returns the item before modelIndex, if created.
 // May return an item marked for removal.
 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
@@ -391,7 +408,10 @@ void QQuickListViewPrivate::setPosition(qreal pos)
 {
     Q_Q(QQuickListView);
     if (orient == QQuickListView::Vertical) {
-        q->QQuickFlickable::setContentY(pos);
+        if (isBottomToTop())
+            q->QQuickFlickable::setContentY(-pos-size());
+        else
+            q->QQuickFlickable::setContentY(pos);
     } else {
         if (isRightToLeft())
             q->QQuickFlickable::setContentX(-pos-size());
@@ -797,8 +817,12 @@ void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
     Q_Q(QQuickListView);
     qreal pos = position();
     if (orient == QQuickListView::Vertical) {
-        if (item->y() + item->height() > pos && item->y() < pos + q->height())
-            item->setY(positionAt(index));
+        if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
+            if (isBottomToTop())
+                item->setY(-positionAt(index)-item->height());
+            else
+                item->setY(positionAt(index));
+        }
     } else {
         if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
             if (isRightToLeft())
@@ -880,7 +904,7 @@ void QQuickListViewPrivate::updateHighlight()
     if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
         // auto-update highlight
         FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
-        highlightPosAnimator->to = isRightToLeft()
+        highlightPosAnimator->to = isContentFlowReversed()
                 ? -listItem->itemPosition()-listItem->itemSize()
                 : listItem->itemPosition();
         highlightSizeAnimator->to = listItem->itemSize();
@@ -987,8 +1011,8 @@ void QQuickListViewPrivate::updateStickySections()
             || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
         return;
 
-    bool isRtl = isRightToLeft();
-    qreal viewPos = isRightToLeft() ? -position()-size() : position();
+    bool isFlowReversed = isContentFlowReversed();
+    qreal viewPos = isFlowReversed ? -position()-size() : position();
     QQuickItem *sectionItem = 0;
     QQuickItem *lastSectionItem = 0;
     int index = 0;
@@ -1000,14 +1024,14 @@ void QQuickListViewPrivate::updateStickySections()
             qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
             bool visTop = true;
             if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
-                visTop = isRtl ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
+                visTop = isFlowReversed ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
             bool visBot = true;
             if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
-                visBot = isRtl ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
+                visBot = isFlowReversed ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
             section->setVisible(visBot && visTop);
             if (visTop && !sectionItem)
                 sectionItem = section;
-            if (isRtl) {
+            if (isFlowReversed) {
                if (-sectionPos <= viewPos + size())
                     lastSectionItem = section;
             } else {
@@ -1031,17 +1055,18 @@ void QQuickListViewPrivate::updateStickySections()
             return;
 
         qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
-        bool atBeginning = orient == QQuickListView::Vertical ? vData.atBeginning : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
+        bool atBeginning = orient == QQuickListView::Vertical ? (isBottomToTop() ? vData.atEnd : vData.atBeginning) : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
+
         currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
-        qreal pos = isRtl ? position() + size() - sectionSize : viewPos;
+        qreal pos = isFlowReversed ? position() + size() - sectionSize : viewPos;
         if (sectionItem) {
             qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
-            pos = isRtl ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
+            pos = isFlowReversed ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
         }
         if (header)
-            pos = isRtl ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
+            pos = isFlowReversed ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
         if (footer)
-            pos = isRtl ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
+            pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
         if (orient == QQuickListView::Vertical)
             currentSectionItem->setY(pos);
         else
@@ -1065,13 +1090,13 @@ void QQuickListViewPrivate::updateStickySections()
 
         qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
         nextSectionItem->setVisible(!nextSection.isEmpty());
-        qreal pos = isRtl ? position() : viewPos + size() - sectionSize;
+        qreal pos = isFlowReversed ? position() : viewPos + size() - sectionSize;
         if (lastSectionItem) {
             qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
-            pos = isRtl ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
+            pos = isFlowReversed ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
         }
         if (header)
-            pos = isRtl ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
+            pos = isFlowReversed ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
         if (orient == QQuickListView::Vertical)
             nextSectionItem->setY(pos);
         else
@@ -1162,7 +1187,7 @@ void QQuickListViewPrivate::updateCurrentSection()
         // section when that changes.  Clearing lastVisibleSection will also
         // force searching.
         QString lastSection = currentSection;
-        qreal endPos = isRightToLeft() ? -position() : position() + size();
+        qreal endPos = isContentFlowReversed() ? -position() : position() + size();
         if (nextSectionItem && !inlineSections)
             endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
         while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
@@ -1341,10 +1366,10 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
     bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
 
-    qreal viewPos = isRightToLeft() ? -position()-size() : position();
+    qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
 
     if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
-        qreal tempPosition = isRightToLeft() ? -position()-size() : position();
+        qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
         if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
             // if we've been dragged < averageSize/2 then bias towards the next item
             qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
@@ -1353,7 +1378,7 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
                 bias = averageSize/2;
             else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
                 bias = -averageSize/2;
-            if (isRightToLeft())
+            if (isContentFlowReversed())
                 bias = -bias;
             tempPosition -= bias;
         }
@@ -1373,15 +1398,15 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
         bool isInBounds = -position() > maxExtent && -position() <= minExtent;
         if (topItem && (isInBounds || strictHighlightRange)) {
             if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
-                pos = isRightToLeft() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
+                pos = isContentFlowReversed() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
             } else {
-                if (isRightToLeft())
+                if (isContentFlowReversed())
                     pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
                 else
                     pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
             }
         } else if (bottomItem && isInBounds) {
-            if (isRightToLeft())
+            if (isContentFlowReversed())
                 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
             else
                 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
@@ -1408,7 +1433,7 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
             viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
         if (viewPos > pos - highlightRangeStart)
             viewPos = pos - highlightRangeStart;
-        if (isRightToLeft())
+        if (isContentFlowReversed())
             viewPos = -viewPos-size();
 
         timeline.reset(data.move);
@@ -1441,7 +1466,7 @@ void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
         return;
     }
     qreal maxDistance = 0;
-    qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
+    qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
 
     // -ve velocity means list is moving up/left
     if (velocity > 0) {
@@ -1450,7 +1475,7 @@ void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
                 // if we've been dragged < averageSize/2 then bias towards the next item
                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
                 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
-                if (isRightToLeft())
+                if (isContentFlowReversed())
                     bias = -bias;
                 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
                 maxDistance = qAbs(data.flickTarget - data.move.value());
@@ -1467,7 +1492,7 @@ void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
                 // if we've been dragged < averageSize/2 then bias towards the next item
                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
                 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
-                if (isRightToLeft())
+                if (isContentFlowReversed())
                     bias = -bias;
                 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
                 maxDistance = qAbs(data.flickTarget - data.move.value());
@@ -1505,10 +1530,10 @@ void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
                 dist = -dist;
             if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
                 if (snapMode != QQuickListView::SnapOneItem) {
-                    qreal distTemp = isRightToLeft() ? -dist : dist;
+                    qreal distTemp = isContentFlowReversed() ? -dist : dist;
                     data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
                 }
-                data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
+                data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
                 if (overShoot) {
                     if (data.flickTarget >= minExtent) {
                         overshootDist = overShootDistance(vSize);
@@ -1561,9 +1586,9 @@ void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
             // reevaluate the target boundary.
             qreal newtarget = data.flickTarget;
             if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
-                qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
+                qreal tempFlickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
                 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
-                newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
+                newtarget = isContentFlowReversed() ? -newtarget+size() : newtarget;
             }
             if (velocity < 0 && newtarget <= maxExtent)
                 newtarget = maxExtent - overshootDist;
@@ -1655,6 +1680,45 @@ void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
     to set \e {clip: true} in order to have the out of view items clipped
     nicely.
 
+
+    \section1 ListView layouts
+
+    The layout of the items in a ListView can be controlled by these properties:
+
+    \list
+    \li \l orientation - controls whether items flow horizontally or vertically.
+        This value can be either Qt.Horizontal or Qt.Vertical.
+    \li \l layoutDirection - controls the horizontal layout direction for a
+        horizontally-oriented view: that is, whether items are laid out from the left side of
+        the view to the right, or vice-versa. This value can be either Qt.LeftToRight or Qt.RightToLeft.
+    \li \l verticalLayoutDirection - controls the vertical layout direction for a vertically-oriented
+        view: that is, whether items are laid out from the top of the view down towards the bottom of
+        the view, or vice-versa. This value can be either ListView.TopToBottom or ListView.BottomToTop.
+    \endlist
+
+    By default, a ListView has a vertical orientation, and items are laid out from top to bottom. The
+    table below shows the different layouts that a ListView can have, depending on the values of
+    the properties listed above.
+
+    \table
+    \header
+        \li {2, 1}
+            \bold ListViews with Qt.Vertical orientation
+    \row
+        \li Top to bottom
+            \image listview-layout-toptobottom.png
+        \li Bottom to top
+            \image listview-layout-bottomtotop.png
+    \header
+        \li {2, 1}
+            \bold ListViews with Qt.Horizontal orientation
+    \row
+        \li Left to right
+            \image listview-layout-lefttoright.png
+        \li Right to left
+            \image listview-layout-righttoleft.png
+    \endtable
+
     \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
 */
 QQuickListView::QQuickListView(QQuickItem *parent)
@@ -1956,7 +2020,7 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
 
 /*!
   \qmlproperty enumeration QtQuick2::ListView::layoutDirection
-  This property holds the layout direction of the horizontal list.
+  This property holds the layout direction of a horizontally-oriented list.
 
   Possible values:
 
@@ -1965,13 +2029,15 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
   \li Qt.RightToLeft - Items will be laid out from right to let.
   \endlist
 
-  \sa ListView::effectiveLayoutDirection
+  Setting this property has no effect if the \l orientation is Qt.Vertical.
+
+  \sa ListView::effectiveLayoutDirection, ListView::verticalLayoutDirection
 */
 
 
 /*!
     \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
-    This property holds the effective layout direction of the horizontal list.
+    This property holds the effective layout direction of a horizontally-oriented list.
 
     When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
     the visual layout direction of the horizontal list will be mirrored. However, the
@@ -1980,6 +2046,24 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
     \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
 */
 
+
+/*!
+  \qmlproperty enumeration QtQuick2::ListView::verticalLayoutDirection
+  This property holds the layout direction of a vertically-oriented list.
+
+  Possible values:
+
+  \list
+  \li ListView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
+  \li ListView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
+  \endlist
+
+  Setting this property has no effect if the \l orientation is Qt.Horizontal.
+
+  \sa ListView::layoutDirection
+*/
+
+
 /*!
     \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
     This property holds whether the list wraps key navigation.
@@ -2550,12 +2634,17 @@ void QQuickListView::viewportMoved()
         return;
     d->inViewportMoved = true;
 
-    if (yflick())
-        d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
-    else if (d->isRightToLeft())
-        d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
-    else
-        d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
+    if (yflick()) {
+        if (d->isBottomToTop())
+            d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
+        else
+            d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
+    } else {
+        if (d->isRightToLeft())
+            d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
+        else
+            d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
+    }
 
     d->refillOrLayout();
 
@@ -2575,7 +2664,7 @@ void QQuickListView::viewportMoved()
         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
             // reposition highlight
             qreal pos = d->highlight->position();
-            qreal viewPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
+            qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
             if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
                 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
             if (pos < viewPos + d->highlightRangeStart)
@@ -2641,7 +2730,8 @@ void QQuickListView::keyPressEvent(QKeyEvent *event)
     if (d->model && d->model->count() && d->interactive) {
         if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
                     || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
-                    || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Up)) {
+                    || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Up)
+                    || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Down)) {
             if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
                 decrementCurrentIndex();
                 event->accept();
@@ -2652,7 +2742,8 @@ void QQuickListView::keyPressEvent(QKeyEvent *event)
             }
         } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
                     || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
-                    || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Down)) {
+                   || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Down)
+                   || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Up)) {
             if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
                 incrementCurrentIndex();
                 event->accept();
@@ -2670,10 +2761,14 @@ void QQuickListView::keyPressEvent(QKeyEvent *event)
 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
 {
     Q_D(QQuickListView);
-    if (d->isRightToLeft() && d->orient == QQuickListView::Horizontal) {
+    if (d->isRightToLeft()) {
         // maintain position relative to the right edge
         int dx = newGeometry.width() - oldGeometry.width();
         setContentX(contentX() - dx);
+    } else if (d->isBottomToTop()) {
+        // maintain position relative to the bottom edge
+        int dy = newGeometry.height() - oldGeometry.height();
+        setContentY(contentY() - dy);
     }
     QQuickItemView::geometryChanged(newGeometry, oldGeometry);
 }
@@ -2740,7 +2835,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &
     int modelIndex = change.index;
     int count = change.count;
 
-    qreal tempPos = isRightToLeft() ? -position()-size() : position();
+    qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
     int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
 
     if (index < 0) {
index 4bf6f0b..1424955 100644 (file)
@@ -26,8 +26,9 @@ Rectangle {
                     text: index
                 }
                 Text {
-                    x: 40
+                    x: 30
                     text: wrapper.x + ", " + wrapper.y
+                    font.pixelSize: 12
                 }
                 Text {
                     y: 20
@@ -58,12 +59,11 @@ Rectangle {
         height: 320
         cellWidth: 80
         cellHeight: 60
-        flow: (testTopToBottom == false) ? GridView.LeftToRight : GridView.TopToBottom
-        layoutDirection: (testRightToLeft == true) ? Qt.RightToLeft : Qt.LeftToRight
         model: testModel
         delegate: myDelegate
         header: root.showHeader ? headerFooter : null
         footer: root.showFooter ? headerFooter : null
         cacheBuffer: root.cacheBuffer
+        focus: true
     }
 }
index 322cfed..f0f73a0 100644 (file)
@@ -2,24 +2,22 @@ import QtQuick 2.0
 
 GridView {
     id: view
-    property bool horizontal: false
-    property bool rtl: false
+
     width: 240
     height: 320
 
     model: testModel
-    
-    flow: horizontal ? GridView.TopToBottom : GridView.LeftToRight
+
     header: Rectangle {
         objectName: "header"
-        width: horizontal ? 20 : view.width
-        height: horizontal ? view.height : 20
+        width: flow == GridView.TopToBottom ? 20 : view.width
+        height: flow == GridView.TopToBottom ? view.height : 20
         color: "red"
     }
     footer: Rectangle {
         objectName: "footer"
-        width: horizontal ? 30 : view.width
-        height: horizontal ? view.height : 30
+        width: flow == GridView.TopToBottom ? 30 : view.width
+        height: flow == GridView.TopToBottom ? view.height : 30
         color: "blue"
     }
 
@@ -27,5 +25,4 @@ GridView {
     cellHeight: 80;
 
     delegate: Text { width: 80; height: 80; text: index + "(" + x + ")" }
-    layoutDirection: rtl ? Qt.RightToLeft : Qt.LeftToRight
 }
diff --git a/tests/auto/quick/qquickgridview/data/layouts.qml b/tests/auto/quick/qquickgridview/data/layouts.qml
new file mode 100644 (file)
index 0000000..e00351f
--- /dev/null
@@ -0,0 +1,62 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: root
+    width: 240
+    height: 320
+
+    property bool showHeader: false
+    property bool showFooter: false
+
+    Component {
+        id: myDelegate
+        Rectangle {
+            id: wrapper
+            objectName: "wrapper"
+            width: 80
+            height: 60
+            border.width: 1
+            Text { text: index }
+            Text {
+                x: 30
+                text: wrapper.x + ", " + wrapper.y
+                font.pixelSize: 12
+            }
+            Text {
+                y: 20
+                id: textName
+                objectName: "textName"
+                text: name
+            }
+            Text {
+                y: 40
+                id: textNumber
+                objectName: "textNumber"
+                text: number
+            }
+
+            property string theName: name
+            color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+        }
+    }
+
+    Component {
+        id: headerFooter
+        Rectangle { width: 30; height: 320; color: "blue" }
+    }
+
+    GridView {
+        objectName: "grid"
+        width: 240
+        height: 320
+        cellWidth: 80
+        cellHeight: 60
+        flow: (testTopToBottom == false) ? GridView.LeftToRight : GridView.TopToBottom
+        layoutDirection: (testRightToLeft == true) ? Qt.RightToLeft : Qt.LeftToRight
+        verticalLayoutDirection: (testBottomToTop == true) ? GridView.BottomToTop : GridView.TopToBottom
+        model: testModel
+        delegate: myDelegate
+        header: root.showHeader ? headerFooter : null
+        footer: root.showFooter ? headerFooter : null
+    }
+}
diff --git a/tests/auto/quick/qquickgridview/data/resizegrid.qml b/tests/auto/quick/qquickgridview/data/resizegrid.qml
new file mode 100644 (file)
index 0000000..7ea2f12
--- /dev/null
@@ -0,0 +1,51 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: root
+
+    Component {
+        id: myDelegate
+        Rectangle {
+            id: wrapper
+            objectName: "wrapper"
+            width: 80
+            height: 60
+            border.width: 1
+            Text { text: index }
+            Text {
+                x: 30
+                text: wrapper.x + ", " + wrapper.y
+                font.pixelSize: 12
+            }
+            Text {
+                y: 20
+                id: textName
+                objectName: "textName"
+                text: name
+            }
+            color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+        }
+    }
+
+    // the grid is specifically placed inside another item to test a bug where
+    // resizing from anchor changes did not update the content pos correctly
+    Item {
+        anchors.fill: parent
+
+        GridView {
+            clip: true
+            objectName: "grid"
+            anchors.fill: parent
+            cellWidth: 80
+            cellHeight: 60
+
+            flow: (testTopToBottom == false) ? GridView.LeftToRight : GridView.TopToBottom
+            layoutDirection: (testRightToLeft == true) ? Qt.RightToLeft : Qt.LeftToRight
+            verticalLayoutDirection: (testBottomToTop == true) ? GridView.BottomToTop : GridView.TopToBottom
+            model: testModel
+            delegate: myDelegate
+        }
+
+    }
+}
+
index 5399a04..bc0108c 100644 (file)
 #include "../shared/visualtestutil.h"
 #include <QtGui/qguiapplication.h>
 
-Q_DECLARE_METATYPE(Qt::LayoutDirection)
 Q_DECLARE_METATYPE(QQuickGridView::Flow)
+Q_DECLARE_METATYPE(Qt::LayoutDirection)
+Q_DECLARE_METATYPE(QQuickItemView::VerticalLayoutDirection)
+Q_DECLARE_METATYPE(Qt::Key)
 
 using namespace QQuickViewTestUtil;
 using namespace QQuickVisualTestUtil;
@@ -76,19 +78,19 @@ private slots:
     void init();
     void items();
     void changed();
-    void inserted();
-    void inserted_more();
-    void inserted_more_data();
+    void inserted_basic();
+    void inserted_defaultLayout(QQuickGridView::Flow flow = QQuickGridView::FlowLeftToRight, Qt::LayoutDirection horizLayout = Qt::LeftToRight, QQuickItemView::VerticalLayoutDirection verticalLayout = QQuickItemView::TopToBottom);
+    void inserted_defaultLayout_data();
     void insertBeforeVisible();
     void insertBeforeVisible_data();
-    void removed();
-    void removed_more();
-    void removed_more_data();
+    void removed_basic();
+    void removed_defaultLayout(QQuickGridView::Flow flow = QQuickGridView::FlowLeftToRight, Qt::LayoutDirection horizLayout = Qt::LeftToRight, QQuickItemView::VerticalLayoutDirection verticalLayout = QQuickItemView::TopToBottom);
+    void removed_defaultLayout_data();
     void addOrRemoveBeforeVisible();
     void addOrRemoveBeforeVisible_data();
     void clear();
-    void moved();
-    void moved_data();
+    void moved_defaultLayout(QQuickGridView::Flow flow = QQuickGridView::FlowLeftToRight, Qt::LayoutDirection horizLayout = Qt::LeftToRight, QQuickItemView::VerticalLayoutDirection verticalLayout = QQuickItemView::TopToBottom);
+    void moved_defaultLayout_data();
     void multipleChanges_condensed() { multipleChanges(true); }
     void multipleChanges_condensed_data() { multipleChanges_data(); }
     void multipleChanges_uncondensed() { multipleChanges(false); }
@@ -97,6 +99,8 @@ private slots:
     void changeFlow();
     void currentIndex();
     void noCurrentIndex();
+    void keyNavigation();
+    void keyNavigation_data();
     void defaultValues();
     void properties();
     void propertyChanges();
@@ -116,7 +120,11 @@ private slots:
     void header();
     void header_data();
     void headerFooter();
+    void headerFooter_data();
+    void resetModel_headerFooter();
     void resizeViewAndRepaint();
+    void resizeGrid();
+    void resizeGrid_data();
     void changeColumnCount();
     void indexAt_itemAt_data();
     void indexAt_itemAt();
@@ -150,6 +158,51 @@ private slots:
     void multipleTransitions_data();
     void multipleDisplaced();
 
+    void inserted_leftToRight_RtL_TtB();
+    void inserted_leftToRight_RtL_TtB_data();
+    void inserted_leftToRight_LtR_BtT();
+    void inserted_leftToRight_LtR_BtT_data();
+    void inserted_leftToRight_RtL_BtT();
+    void inserted_leftToRight_RtL_BtT_data();
+    void inserted_topToBottom_LtR_TtB();
+    void inserted_topToBottom_LtR_TtB_data();
+    void inserted_topToBottom_RtL_TtB();
+    void inserted_topToBottom_RtL_TtB_data();
+    void inserted_topToBottom_LtR_BtT();
+    void inserted_topToBottom_LtR_BtT_data();
+    void inserted_topToBottom_RtL_BtT();
+    void inserted_topToBottom_RtL_BtT_data();
+
+    void removed_leftToRight_RtL_TtB();
+    void removed_leftToRight_RtL_TtB_data();
+    void removed_leftToRight_LtR_BtT();
+    void removed_leftToRight_LtR_BtT_data();
+    void removed_leftToRight_RtL_BtT();
+    void removed_leftToRight_RtL_BtT_data();
+    void removed_topToBottom_LtR_TtB();
+    void removed_topToBottom_LtR_TtB_data();
+    void removed_topToBottom_RtL_TtB();
+    void removed_topToBottom_RtL_TtB_data();
+    void removed_topToBottom_LtR_BtT();
+    void removed_topToBottom_LtR_BtT_data();
+    void removed_topToBottom_RtL_BtT();
+    void removed_topToBottom_RtL_BtT_data();
+
+    void moved_leftToRight_RtL_TtB();
+    void moved_leftToRight_RtL_TtB_data();
+    void moved_leftToRight_LtR_BtT();
+    void moved_leftToRight_LtR_BtT_data();
+    void moved_leftToRight_RtL_BtT();
+    void moved_leftToRight_RtL_BtT_data();
+    void moved_topToBottom_LtR_TtB();
+    void moved_topToBottom_LtR_TtB_data();
+    void moved_topToBottom_RtL_TtB();
+    void moved_topToBottom_RtL_TtB_data();
+    void moved_topToBottom_LtR_BtT();
+    void moved_topToBottom_LtR_BtT_data();
+    void moved_topToBottom_RtL_BtT();
+    void moved_topToBottom_RtL_BtT_data();
+
 private:
     QList<int> toIntList(const QVariantList &list);
     void matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes);
@@ -159,6 +212,63 @@ private:
     void multipleChanges(bool condensed);
     void multipleChanges_data();
 
+    QPointF expectedItemPos(QQuickGridView *grid, int index, qreal rowOffset = 0) {
+        qreal x;
+        qreal y;
+        if (grid->flow() == QQuickGridView::FlowLeftToRight) {
+            int columns = grid->width() / grid->cellWidth();
+            x = (index % columns) * grid->cellWidth();
+            y = (index / columns) * grid->cellHeight();
+            if (grid->effectiveLayoutDirection() == Qt::RightToLeft) {
+                int col = (index % columns) * grid->cellWidth();
+                x = grid->cellWidth() * (columns - 1) - col;
+            }
+
+            qreal offset = grid->cellHeight() * rowOffset;
+            if (grid->verticalLayoutDirection() == QQuickItemView::TopToBottom)
+                y += offset;
+            else
+                y = -grid->cellHeight() - y - offset;
+        } else {
+            int rows = grid->height() / grid->cellHeight();
+            x = (index / rows) * grid->cellWidth();
+            y = (index % rows) * grid->cellHeight();
+            if (grid->effectiveLayoutDirection() == Qt::RightToLeft)
+                x = -x - grid->cellWidth();
+
+            qreal offset = grid->cellWidth() * rowOffset;
+            if (grid->effectiveLayoutDirection() == Qt::RightToLeft)
+                x -= offset;
+            else
+                x += offset;
+            if (grid->verticalLayoutDirection() == QQuickItemView::BottomToTop)
+                y = -grid->cellHeight() - y;
+        }
+        return QPointF(x, y);
+    }
+
+    // Sets contentY (or contentX in TopToBottom flow) according to given row offset
+    // (in LeftToRight flow) or col offset (in TopToBottom).
+    bool setContentPos(QQuickGridView *gridview, qreal rowOrColOffset) {
+        bool contentPosChanged = (rowOrColOffset != 0);
+        qreal contentOffset = gridview->flow() == QQuickGridView::FlowLeftToRight
+                ? rowOrColOffset * gridview->cellHeight()
+                : rowOrColOffset * gridview->cellWidth();
+
+        if (gridview->flow() == QQuickGridView::FlowLeftToRight) {
+            if (gridview->verticalLayoutDirection() == QQuickItemView::BottomToTop)
+                contentOffset = -gridview->height() - contentOffset;
+        } else {
+            if (gridview->effectiveLayoutDirection() == Qt::RightToLeft)
+                contentOffset = -gridview->width() - contentOffset;
+        }
+        if (gridview->flow() == QQuickGridView::FlowLeftToRight)
+            gridview->setContentY(contentOffset);
+        else
+            gridview->setContentX(contentOffset);
+        return contentPosChanged;
+    }
+
 #ifdef SHARE_VIEWS
     QQuickView *getView() {
         if (m_view) {
@@ -222,8 +332,6 @@ void tst_QQuickGridView::items()
 
     QQmlContext *ctxt = canvas->rootContext();
     ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
 
     canvas->setSource(testFileUrl("gridview1.qml"));
     qApp->processEvents();
@@ -272,8 +380,6 @@ void tst_QQuickGridView::changed()
 
     QQmlContext *ctxt = canvas->rootContext();
     ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
 
     canvas->setSource(testFileUrl("gridview1.qml"));
     qApp->processEvents();
@@ -295,7 +401,7 @@ void tst_QQuickGridView::changed()
     delete canvas;
 }
 
-void tst_QQuickGridView::inserted()
+void tst_QQuickGridView::inserted_basic()
 {
     QQuickView *canvas = createView();
     canvas->show();
@@ -305,11 +411,7 @@ void tst_QQuickGridView::inserted()
     model.addItem("John", "2345");
     model.addItem("Bob", "54321");
 
-    QQmlContext *ctxt = canvas->rootContext();
-    ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-
+    canvas->rootContext()->setContextProperty("testModel", &model);
     canvas->setSource(testFileUrl("gridview1.qml"));
     qApp->processEvents();
 
@@ -375,11 +477,15 @@ void tst_QQuickGridView::inserted()
     delete canvas;
 }
 
-void tst_QQuickGridView::inserted_more()
+void tst_QQuickGridView::inserted_defaultLayout(QQuickGridView::Flow flow,
+                                       Qt::LayoutDirection horizLayout,
+                                       QQuickItemView::VerticalLayoutDirection verticalLayout)
 {
     QFETCH(qreal, contentYRowOffset);
     QFETCH(int, insertIndex);
     QFETCH(int, insertCount);
+    QFETCH(int, insertIndex_ttb);
+    QFETCH(int, insertCount_ttb);
     QFETCH(qreal, rowOffsetAfterMove);
 
     QaimModel model;
@@ -389,10 +495,10 @@ void tst_QQuickGridView::inserted_more()
     QQuickView *canvas = getView();
     QQmlContext *ctxt = canvas->rootContext();
     ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-
-    canvas->setSource(testFileUrl("gridview1.qml"));
+    ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
+    ctxt->setContextProperty("testRightToLeft", horizLayout == Qt::RightToLeft);
+    ctxt->setContextProperty("testBottomToTop", verticalLayout == QQuickGridView::BottomToTop);
+    canvas->setSource(testFileUrl("layouts.qml"));
     canvas->show();
     qApp->processEvents();
 
@@ -401,8 +507,12 @@ void tst_QQuickGridView::inserted_more()
     QQuickItem *contentItem = gridview->contentItem();
     QTRY_VERIFY(contentItem != 0);
 
-    gridview->setContentY(contentYRowOffset * 60.0);
-    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+    if (flow == QQuickGridView::FlowTopToBottom) {
+        insertIndex = insertIndex_ttb;
+        insertCount = insertCount_ttb;
+    }
+    if (setContentPos(gridview, contentYRowOffset))
+        QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
     QList<QPair<QString, QString> > newData;
     for (int i=0; i<insertCount; i++)
@@ -413,74 +523,76 @@ void tst_QQuickGridView::inserted_more()
     // check visibleItems.first() is in correct position
     QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
     QVERIFY(item0);
-    QCOMPARE(item0->y(), 0.0);
+    QPointF firstPos(0, 0);
+    if (horizLayout == Qt::RightToLeft)
+        firstPos.rx() = flow == QQuickGridView::FlowLeftToRight ? gridview->width() - gridview->cellWidth() : -gridview->cellWidth();
+    if (verticalLayout == QQuickItemView::BottomToTop)
+        firstPos.ry() -= gridview->cellHeight();
+    QCOMPARE(item0->pos(), firstPos);
 
     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
     int firstVisibleIndex = -1;
     for (int i=0; i<items.count(); i++) {
-        if (items[i]->y() >= gridview->contentY()) {
-            QQmlExpression e(qmlContext(items[i]), items[i], "index");
-            firstVisibleIndex = e.evaluate().toInt();
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        if (item && item->isVisible()) {
+            firstVisibleIndex = i;
             break;
         }
     }
     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
 
     // Confirm items positioned correctly and indexes correct
-    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    QQuickText *name;
-    QQuickText *number;
-    qreal pixelOffset = 60 * rowOffsetAfterMove;
-    for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
+    for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
-
-        QCOMPARE(item->x(), (i%3)*80.0);
-        QCOMPARE(item->y(), (i/3)*60.0 + pixelOffset);
-
-        name = findItem<QQuickText>(contentItem, "textName", i);
+        QCOMPARE(item->pos(), expectedItemPos(gridview, i, rowOffsetAfterMove));
+        QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
         QVERIFY(name != 0);
         QCOMPARE(name->text(), model.name(i));
-        number = findItem<QQuickText>(contentItem, "textNumber", i);
-        QVERIFY(number != 0);
-        QCOMPARE(number->text(), model.number(i));
     }
 
     releaseView(canvas);
 }
 
-void tst_QQuickGridView::inserted_more_data()
+void tst_QQuickGridView::inserted_defaultLayout_data()
 {
     QTest::addColumn<qreal>("contentYRowOffset");
     QTest::addColumn<int>("insertIndex");
     QTest::addColumn<int>("insertCount");
+    QTest::addColumn<int>("insertIndex_ttb");
+    QTest::addColumn<int>("insertCount_ttb");
     QTest::addColumn<qreal>("rowOffsetAfterMove");
 
     QTest::newRow("add 1, before visible items")
             << 2.0     // show 6-23
             << 5 << 1
+               << 9 << 1
             << 0.0;   // insert 1 above first visible, grid is rearranged; first visible moves forward within its row
                       // new 1st visible item is at 0
 
     QTest::newRow("add 2, before visible items")
             << 2.0     // show 6-23
             << 5 << 2
+               << 9 << 2
             << 0.0;   // insert 2 above first visible, grid is rearranged; first visible moves forward within its row
 
     QTest::newRow("add 3, before visible items")
             << 2.0     // show 6-23
             << 5 << 3
+               << 9 << 5
             << -1.0;   // insert 3 (1 row) above first visible in negative pos, first visible does not move
 
     QTest::newRow("add 5, before visible items")
             << 2.0     // show 6-23
             << 5 << 5
+               << 9 << 7
             << -1.0;   // insert 1 row + 2 items above first visible, 1 row added at negative pos,
                         // grid is rearranged and first visible moves forward within its row
 
     QTest::newRow("add 6, before visible items")
             << 2.0     // show 6-23
             << 5 << 6
+               << 9 << 10
             << -1.0 * 2;   // insert 2 rows above first visible in negative pos, first visible does not move
 
 
@@ -488,63 +600,75 @@ void tst_QQuickGridView::inserted_more_data()
    QTest::newRow("add 1, at start of visible, content at start")
             << 0.0
             << 0 << 1
+               << 0 << 1
             << 0.0;
 
     QTest::newRow("add multiple, at start of visible, content at start")
             << 0.0
             << 0 << 3
+               << 0 << 5
             << 0.0;
 
     QTest::newRow("add 1, at start of visible, content not at start")
             << 2.0     // show 6-23
             << 6 << 1
+               << 10 << 1
             << 0.0;
 
     QTest::newRow("add multiple, at start of visible, content not at start")
             << 2.0     // show 6-23
             << 6 << 3
+               << 10 << 5
             << 0.0;
 
 
     QTest::newRow("add 1, at end of visible, content at start")
             << 0.0
             << 17 << 1
+               << 14 << 1
             << 0.0;
 
-    QTest::newRow("add 1, at end of visible, content at start")
+    QTest::newRow("add row, at end of visible, content at start")
             << 0.0
             << 17 << 3
+               << 14 << 5
             << 0.0;
 
     QTest::newRow("add 1, at end of visible, content not at start")
             << 2.0     // show 6-23
-            << 23 << 1
+            << 17+6 << 1
+               << 14+10 << 1
             << 0.0;
 
     QTest::newRow("add multiple, at end of visible, content not at start")
             << 2.0     // show 6-23
-            << 23 << 3
+            << 17+6 << 3
+               << 14+10 << 5
             << 0.0;
 
 
     QTest::newRow("add 1, after visible, content at start")
             << 0.0
             << 20 << 1
+               << 18 << 1
             << 0.0;
 
-    QTest::newRow("add 1, after visible, content at start")
+    QTest::newRow("add row, after visible, content at start")
             << 0.0
             << 20 << 3
+               << 18 << 5
             << 0.0;
 
     QTest::newRow("add 1, after visible, content not at start")
             << 2.0     // show 6-23
-            << 24 << 1
+            << 20+6 << 1
+               << 18+10 << 1
             << 0.0;
 
     QTest::newRow("add multiple, after visible, content not at start")
             << 2.0     // show 6-23
-            << 24 << 3
+            << 20+6 << 3
+               << 18+10 << 3
             << 0.0;
 }
 
@@ -563,8 +687,6 @@ void tst_QQuickGridView::insertBeforeVisible()
 
     QQmlContext *ctxt = canvas->rootContext();
     ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
     canvas->setSource(testFileUrl("gridview1.qml"));
     canvas->show();
     qApp->processEvents();
@@ -639,7 +761,7 @@ void tst_QQuickGridView::insertBeforeVisible_data()
     QTest::newRow("insert multiple at 1, 500 buffer") << 1 << 6 << 500;
 }
 
-void tst_QQuickGridView::removed()
+void tst_QQuickGridView::removed_basic()
 {
     QQuickView *canvas = createView();
     canvas->show();
@@ -648,11 +770,7 @@ void tst_QQuickGridView::removed()
     for (int i = 0; i < 40; i++)
         model.addItem("Item" + QString::number(i), "");
 
-    QQmlContext *ctxt = canvas->rootContext();
-    ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-
+    canvas->rootContext()->setContextProperty("testModel", &model);
     canvas->setSource(testFileUrl("gridview1.qml"));
     qApp->processEvents();
 
@@ -784,16 +902,19 @@ void tst_QQuickGridView::removed()
     delete canvas;
 }
 
-void tst_QQuickGridView::removed_more()
+void tst_QQuickGridView::removed_defaultLayout(QQuickGridView::Flow flow,
+                                               Qt::LayoutDirection horizLayout,
+                                               QQuickItemView::VerticalLayoutDirection verticalLayout)
 {
     QFETCH(qreal, contentYRowOffset);
     QFETCH(int, removeIndex);
     QFETCH(int, removeCount);
+    QFETCH(int, removeIndex_ttb);
+    QFETCH(int, removeCount_ttb);
     QFETCH(qreal, rowOffsetAfterMove);
     QFETCH(QString, firstVisible);
+    QFETCH(QString, firstVisible_ttb);
 
-    QQuickText *name;
-    QQuickText *number;
     QQuickView *canvas = getView();
 
     QaimModel model;
@@ -802,9 +923,10 @@ void tst_QQuickGridView::removed_more()
 
     QQmlContext *ctxt = canvas->rootContext();
     ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-    canvas->setSource(testFileUrl("gridview1.qml"));
+    ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
+    ctxt->setContextProperty("testRightToLeft", horizLayout == Qt::RightToLeft);
+    ctxt->setContextProperty("testBottomToTop", verticalLayout == QQuickGridView::BottomToTop);
+    canvas->setSource(testFileUrl("layouts.qml"));
     canvas->show();
     qApp->processEvents();
 
@@ -813,8 +935,13 @@ void tst_QQuickGridView::removed_more()
     QQuickItem *contentItem = gridview->contentItem();
     QTRY_VERIFY(contentItem != 0);
 
-    gridview->setContentY(contentYRowOffset * 60.0);
-    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+    if (flow == QQuickGridView::FlowTopToBottom) {
+        removeIndex = removeIndex_ttb;
+        removeCount = removeCount_ttb;
+        firstVisible = firstVisible_ttb;
+    }
+    if (setContentPos(gridview, contentYRowOffset))
+        QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
     model.removeItems(removeIndex, removeCount);
     QTRY_COMPARE(gridview->property("count").toInt(), model.count());
@@ -822,80 +949,87 @@ void tst_QQuickGridView::removed_more()
     QString firstName;
     int firstVisibleIndex = -1;
     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+    QRectF viewRect(gridview->contentX(), gridview->contentY(), gridview->width(), gridview->height());
     for (int i=0; i<items.count(); i++) {
-        if (items[i]->y() >= gridview->contentY()) {
-            QQmlExpression e(qmlContext(items[i]), items[i], "index");
-            firstVisibleIndex = e.evaluate().toInt();
-            QQmlExpression en(qmlContext(items[i]), items[i], "name");
-            firstName = en.evaluate().toString();
-            break;
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        if (item) {
+            QRectF itemRect(item->x(), item->y(), item->width(), item->height());
+            if (item->isVisible() && viewRect.intersects(itemRect)) {
+                firstVisibleIndex = i;
+                QQmlExpression en(qmlContext(item), item, "name");
+                firstName = en.evaluate().toString();
+                break;
+            }
         }
     }
     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
     QCOMPARE(firstName, firstVisible);
 
     // Confirm items positioned correctly and indexes correct
-    qreal pixelOffset = 60 * rowOffsetAfterMove;
     for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
-
-        QTRY_COMPARE(item->x(), (i%3)*80.0);
-        QTRY_COMPARE(item->y(), (i/3)*60.0 + pixelOffset);
-
-        name = findItem<QQuickText>(contentItem, "textName", i);
+        QCOMPARE(item->pos(), expectedItemPos(gridview, i, rowOffsetAfterMove));
+        QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
         QVERIFY(name != 0);
         QTRY_COMPARE(name->text(), model.name(i));
-        number = findItem<QQuickText>(contentItem, "textNumber", i);
-        QVERIFY(number != 0);
-        QTRY_COMPARE(number->text(), model.number(i));
     }
 
     releaseView(canvas);
 }
 
-void tst_QQuickGridView::removed_more_data()
+void tst_QQuickGridView::removed_defaultLayout_data()
 {
     QTest::addColumn<qreal>("contentYRowOffset");
     QTest::addColumn<int>("removeIndex");
     QTest::addColumn<int>("removeCount");
+    QTest::addColumn<int>("removeIndex_ttb");
+    QTest::addColumn<int>("removeCount_ttb");
     QTest::addColumn<qreal>("rowOffsetAfterMove");
     QTest::addColumn<QString>("firstVisible");
+    QTest::addColumn<QString>("firstVisible_ttb");
 
     QTest::newRow("remove 1, before visible items")
             << 2.0     // show 6-23
             << 2 << 1
-            << 0.0 << "Item7";
+               << 4 << 1
+            << 0.0 << "Item7" << "Item11";
 
     QTest::newRow("remove 1, before visible position")
             << 2.0     // show 6-23
             << 3 << 1
-            << 0.0 << "Item7";
+               << 5 << 1
+            << 0.0 << "Item7" << "Item11";
 
-    QTest::newRow("remove multiple, all before visible items")
+    QTest::newRow("remove multiple (1 row), all before visible items")
             << 2.0
             << 1 << 3
-            << 1.0 << "Item6";    // removed top row, slide down by 1 row
+               << 1 << 5
+            << 1.0 << "Item6" << "Item10";    // removed top row, slide down by 1 row
 
     QTest::newRow("remove multiple, all before visible items, remove item 0")
             << 2.0
             << 0 << 4
-            << 1.0 << "Item7";    // removed top row, slide down by 1 row
+               << 0 << 6
+            << 1.0 << "Item7" << "Item11";    // removed top row, slide down by 1 row
 
     QTest::newRow("remove multiple rows, all before visible items")
             << 4.0     // show 12-29
             << 1 << 7
-            << 2.0 << "Item13";
+               << 1 << 12
+            << 2.0 << "Item13" << "Item17";
 
     QTest::newRow("remove one row before visible, content y not on item border")
             << 1.5
             << 0 << 3
-            << 1.0 << "Item6"; // 1 row removed
+               << 0 << 5
+            << 1.0 << "Item3" << "Item5"; // 1 row removed
 
     QTest::newRow("remove mix of visible/non-visible")
             << 2.0     // show 6-23
             << 2 << 3
-            << 1.0 << "Item6"; // 1 row removed
+               << 4 << 3
+            << 1.0 << "Item6" << "Item8"; // 1 row removed
 
 
     // remove 3,4,5 before the visible pos, first row moves down to just before the visible pos,
@@ -903,80 +1037,95 @@ void tst_QQuickGridView::removed_more_data()
     QTest::newRow("remove multiple, mix of items from before and within visible items")
             << 2.0
             << 3 << 5
-            << 1.0 << "Item8";    // adjust for the 1 row removed before the visible
+                << 5 << 7
+            << 1.0 << "Item8" << "Item12";    // adjust for the 1 row removed before the visible
 
     QTest::newRow("remove multiple, mix of items from before and within visible items, remove item 0")
             << 2.0
             << 0 << 8
-            << 1.0 * 2 << "Item8";    // adjust for the 2 rows removed before the visible
+               << 0 << 12
+            << 1.0 * 2 << "Item8" << "Item12";    // adjust for the 2 rows removed before the visible
 
 
     QTest::newRow("remove 1, from start of visible, content at start")
             << 0.0
             << 0 << 1
-            << 0.0 << "Item1";
+               << 0 << 1
+            << 0.0 << "Item1" << "Item1";
 
     QTest::newRow("remove multiple, from start of visible, content at start")
             << 0.0
             << 0 << 3
-            << 0.0 << "Item3";
+               << 0 << 5
+            << 0.0 << "Item3" << "Item5";
 
     QTest::newRow("remove 1, from start of visible, content not at start")
             << 2.0     // show 6-23
             << 4 << 1
-            << 0.0 << "Item7";
+               << 7 << 1
+            << 0.0 << "Item7" << "Item11";
 
     QTest::newRow("remove multiple, from start of visible, content not at start")
             << 2.0     // show 6-23
             << 4 << 3
-            << 0.0 << "Item9";
+               << 7 << 5
+            << 0.0 << "Item9" << "Item15";
 
 
     QTest::newRow("remove 1, from middle of visible, content at start")
             << 0.0
             << 10 << 1
-            << 0.0 << "Item0";
+               << 12 << 1
+            << 0.0 << "Item0" << "Item0";
 
     QTest::newRow("remove multiple, from middle of visible, content at start")
             << 0.0
             << 10 << 5
-            << 0.0 << "Item0";
+               << 12 << 5
+            << 0.0 << "Item0" << "Item0";
 
     QTest::newRow("remove 1, from middle of visible, content not at start")
             << 2.0     // show 6-23
             << 10 << 1
-            << 0.0 << "Item6";
+               << 12 << 1
+            << 0.0 << "Item6" << "Item10";
 
     QTest::newRow("remove multiple, from middle of visible, content not at start")
             << 2.0     // show 6-23
             << 10 << 5
-            << 0.0 << "Item6";
+               << 12 << 7
+            << 0.0 << "Item6" << "Item10";
 
 
     QTest::newRow("remove 1, after visible, content at start")
             << 0.0
             << 16 << 1
-            << 0.0 << "Item0";
+               << 15 << 1
+            << 0.0 << "Item0" << "Item0";
 
     QTest::newRow("remove multiple, after visible, content at start")
             << 0.0
             << 16 << 5
-            << 0.0 << "Item0";
+               << 15 << 7
+            << 0.0 << "Item0" << "Item0";
 
     QTest::newRow("remove 1, after visible, content not at start")
             << 2.0     // show 6-23
             << 16+4 << 1
-            << 0.0 << "Item6";
+               << 15+10 << 1
+            << 0.0 << "Item6" << "Item10";
 
     QTest::newRow("remove multiple, after visible, content not at start")
             << 2.0     // show 6-23
             << 16+4 << 5
-            << 0.0 << "Item6";
+               << 15+10 << 7
+            << 0.0 << "Item6" << "Item10";
 
     QTest::newRow("remove multiple, mix of items from within and after visible items")
             << 2.0     // show 6-23
             << 20 << 5
-            << 0.0 << "Item6";
+               << 22 << 7
+            << 0.0 << "Item6" << "Item10";
 }
 
 void tst_QQuickGridView::addOrRemoveBeforeVisible()
@@ -994,10 +1143,7 @@ void tst_QQuickGridView::addOrRemoveBeforeVisible()
     for (int i = 0; i < 30; i++)
         model.addItem("Item" + QString::number(i), "");
 
-    QQmlContext *ctxt = canvas->rootContext();
-    ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
+    canvas->rootContext()->setContextProperty("testModel", &model);
     canvas->setSource(testFileUrl("gridview1.qml"));
 
     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
@@ -1073,11 +1219,7 @@ void tst_QQuickGridView::clear()
     for (int i = 0; i < 30; i++)
         model.addItem("Item" + QString::number(i), "");
 
-    QQmlContext *ctxt = canvas->rootContext();
-    ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-
+    canvas->rootContext()->setContextProperty("testModel", &model);
     canvas->setSource(testFileUrl("gridview1.qml"));
     canvas->show();
     qApp->processEvents();
@@ -1104,28 +1246,31 @@ void tst_QQuickGridView::clear()
     delete canvas;
 }
 
-void tst_QQuickGridView::moved()
+void tst_QQuickGridView::moved_defaultLayout(QQuickGridView::Flow flow,
+                                             Qt::LayoutDirection horizLayout,
+                                             QQuickItemView::VerticalLayoutDirection verticalLayout)
 {
     QFETCH(qreal, contentYRowOffset);
     QFETCH(int, from);
     QFETCH(int, to);
     QFETCH(int, count);
+    QFETCH(int, from_ttb);
+    QFETCH(int, to_ttb);
+    QFETCH(int, count_ttb);
     QFETCH(qreal, rowOffsetAfterMove);
 
-    QQuickText *name;
-    QQuickText *number;
     QQuickView *canvas = getView();
 
     QaimModel model;
     for (int i = 0; i < 30; i++)
         model.addItem("Item" + QString::number(i), "");
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QDeclarativeContext *ctxt = canvas->rootContext();
     ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-
-    canvas->setSource(testFileUrl("gridview1.qml"));
+    ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
+    ctxt->setContextProperty("testRightToLeft", horizLayout == Qt::RightToLeft);
+    ctxt->setContextProperty("testBottomToTop", verticalLayout == QQuickGridView::BottomToTop);
+    canvas->setSource(testFileUrl("layouts.qml"));
     canvas->show();
     qApp->processEvents();
 
@@ -1138,33 +1283,41 @@ void tst_QQuickGridView::moved()
     QQuickItem *currentItem = gridview->currentItem();
     QTRY_VERIFY(currentItem != 0);
 
-    if (contentYRowOffset != 0) {
-        gridview->setContentY(contentYRowOffset * 60.0);
-        QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+    if (flow == QQuickGridView::FlowTopToBottom) {
+        from = from_ttb;
+        to = to_ttb;
+        count = count_ttb;
     }
+    if (setContentPos(gridview, contentYRowOffset))
+        QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
     model.moveItems(from, to, count);
     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
     // Confirm items positioned correctly and indexes correct
-    int firstVisibleIndex = qCeil(gridview->contentY() / 60.0) * 3;
-    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    qreal pixelOffset = 60 * rowOffsetAfterMove;
-    for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
-        if (i >= firstVisibleIndex + 18)    // index has moved out of view
-            continue;
+    QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+    int firstVisibleIndex = -1;
+    for (int i=0; i<items.count(); i++) {
         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
-
-        QTRY_COMPARE(item->x(), (i%3)*80.0);
-        QTRY_COMPARE(item->y(), (i/3)*60.0 + pixelOffset);
+        if (item && item->isVisible()) {
+            firstVisibleIndex = i;
+            break;
+        }
+    }
+    QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
 
-        name = findItem<QQuickText>(contentItem, "textName", i);
+    for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        if (!item &&
+                ( (flow == QQuickGridView::FlowLeftToRight && i >= firstVisibleIndex + (3*6))
+                || flow == QQuickGridView::FlowTopToBottom && i >= firstVisibleIndex + (5*3) ) ) {
+            continue;   // index has moved out of view
+        }
+        QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+        QCOMPARE(item->pos(), expectedItemPos(gridview, i, rowOffsetAfterMove));
+        QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
         QVERIFY(name != 0);
         QTRY_COMPARE(name->text(), model.name(i));
-        number = findItem<QQuickText>(contentItem, "textNumber", i);
-        QVERIFY(number != 0);
-        QTRY_COMPARE(number->text(), model.number(i));
 
         // current index should have been updated
         if (item == currentItem)
@@ -1174,12 +1327,15 @@ void tst_QQuickGridView::moved()
     releaseView(canvas);
 }
 
-void tst_QQuickGridView::moved_data()
+void tst_QQuickGridView::moved_defaultLayout_data()
 {
     QTest::addColumn<qreal>("contentYRowOffset");
     QTest::addColumn<int>("from");
     QTest::addColumn<int>("to");
     QTest::addColumn<int>("count");
+    QTest::addColumn<int>("from_ttb");
+    QTest::addColumn<int>("to_ttb");
+    QTest::addColumn<int>("count_ttb");
     QTest::addColumn<qreal>("rowOffsetAfterMove");
 
     // model starts with 30 items, each 80x60, in area 240x320
@@ -1192,139 +1348,166 @@ void tst_QQuickGridView::moved_data()
     QTest::newRow("move 1 forwards, within visible items")
             << 0.0
             << 1 << 8 << 1
+               << 2 << 12 << 1
             << 0.0;
 
     QTest::newRow("move 1 forwards, from non-visible -> visible")
             << 2.0     // show 6-23
-            << 1 << 23 << 1
+            << 1 << 8+6 << 1
+               << 2 << 12+10 << 1
             << 0.0;
 
     QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
             << 2.0     // // show 6-23
             << 0 << 6 << 1
+               << 0 << 10 << 1
             << 0.0;
 
     QTest::newRow("move 1 forwards, from visible -> non-visible")
             << 0.0
             << 1 << 20 << 1
+               << 1 << 20 << 1
             << 0.0;
 
     QTest::newRow("move 1 forwards, from visible -> non-visible (move first item)")
             << 0.0
             << 0 << 20 << 1
+               << 0 << 20 << 1
             << 0.0;
 
 
     QTest::newRow("move 1 backwards, within visible items")
             << 0.0
             << 10 << 5 << 1
+               << 10 << 5 << 1
             << 0.0;
 
     QTest::newRow("move 1 backwards, within visible items (to first index)")
             << 0.0
             << 10 << 0 << 1
+               << 10 << 0 << 1
             << 0.0;
 
     QTest::newRow("move 1 backwards, from non-visible -> visible")
             << 0.0
             << 28 << 8 << 1
+               << 28 << 8 << 1
             << 0.0;
 
     QTest::newRow("move 1 backwards, from non-visible -> visible (move last item)")
             << 0.0
             << 29 << 14 << 1
+               << 29 << 14 << 1
             << 0.0;
 
     QTest::newRow("move 1 backwards, from visible -> non-visible")
             << 2.0     // show 6-23
             << 7 << 1 << 1
+               << 10 << 1 << 1
             << 0.0;     // only 1 item moved back, so items shift accordingly and first row doesn't move
 
     QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)")
             << 2.0     // show 6-23
             << 7 << 0 << 1
+               << 10 << 0 << 1
             << 0.0;     // only 1 item moved back, so items shift accordingly and first row doesn't move
 
 
     QTest::newRow("move multiple forwards, within visible items")
             << 0.0
             << 0 << 5 << 3
+               << 0 << 7 << 5
             << 0.0;
 
     QTest::newRow("move multiple backwards, within visible items (move first item)")
             << 0.0
             << 10 << 0 << 3
+               << 12 << 0 << 5
             << 0.0;
 
     QTest::newRow("move multiple forwards, before visible items")
             << 2.0     // show 6-23
             << 3 << 4 << 3      // 3, 4, 5 move to after 6
+               << 5 << 6 << 5
             << 1.0;      // row of 3,4,5 has moved down
 
     QTest::newRow("move multiple forwards, from non-visible -> visible")
             << 2.0     // show 6-23
             << 1 << 6 << 3
+               << 1 << 10 << 5
             << 1.0; // 1st row (it's above visible area) disappears, 0 drops down 1 row, first visible item (6) stays where it is
 
     QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)")
             << 2.0     // show 6-23
             << 0 << 6 << 3
+               << 0 << 10 << 5
             << 1.0;    // top row moved and shifted to below 3rd row, all items should shift down by 1 row
 
     QTest::newRow("move multiple forwards, mix of non-visible/visible")
             << 2.0
             << 3 << 16 << 6
+               << 5 << 18 << 10
             << 1.0;    // top two rows removed, third row is now the first visible
 
     QTest::newRow("move multiple forwards, to bottom of view")
             << 0.0
             << 5 << 13 << 5
+               << 1 << 8 << 6
             << 0.0;
 
     QTest::newRow("move multiple forwards, to bottom of view, first row -> last")
             << 0.0
             << 0 << 15 << 3
+               << 0 << 10 << 5
             << 0.0;
 
     QTest::newRow("move multiple forwards, to bottom of view, content y not 0")
             << 2.0
             << 5+4 << 13+4 << 5
+               << 11 << 19 << 6
             << 0.0;
 
     QTest::newRow("move multiple forwards, from visible -> non-visible")
             << 0.0
             << 1 << 16 << 3
+               << 1 << 18 << 5
             << 0.0;
 
     QTest::newRow("move multiple forwards, from visible -> non-visible (move first item)")
             << 0.0
             << 0 << 16 << 3
+               << 1 << 18 << 5
             << 0.0;
 
 
     QTest::newRow("move multiple backwards, within visible items")
             << 0.0
             << 4 << 1 << 3
+               << 7 << 1 << 5
             << 0.0;
 
     QTest::newRow("move multiple backwards, from non-visible -> visible")
             << 0.0
             << 20 << 4 << 3
+               << 20 << 4 << 5
             << 0.0;
 
     QTest::newRow("move multiple backwards, from non-visible -> visible (move last item)")
             << 0.0
             << 27 << 10 << 3
+               << 25 << 8 << 5
             << 0.0;
 
     QTest::newRow("move multiple backwards, from visible -> non-visible")
             << 2.0     // show 6-23
             << 16 << 1 << 3
+               << 17 << 1 << 5
             << -1.0;   // to minimize movement, items are added above visible area, all items move up by 1 row
 
     QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)")
             << 2.0     // show 6-23
             << 16 << 0 << 3
+               << 17 << 0 << 5
             << -1.0;   // 16,17,18 move to above item 0, all items move up by 1 row
 }
 
@@ -1341,11 +1524,7 @@ void tst_QQuickGridView::multipleChanges(bool condensed)
     for (int i = 0; i < startCount; i++)
         model.addItem("Item" + QString::number(i), "");
 
-    QQmlContext *ctxt = canvas->rootContext();
-    ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-
+    canvas->rootContext()->setContextProperty("testModel", &model);
     canvas->setSource(testFileUrl("gridview1.qml"));
     canvas->show();
     qApp->processEvents();
@@ -1588,11 +1767,7 @@ void tst_QQuickGridView::swapWithFirstItem()
     for (int i = 0; i < 30; i++)
         model.addItem("Item" + QString::number(i), "");
 
-    QQmlContext *ctxt = canvas->rootContext();
-    ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-
+    canvas->rootContext()->setContextProperty("testModel", &model);
     canvas->setSource(testFileUrl("gridview1.qml"));
     canvas->show();
     qApp->processEvents();
@@ -1639,57 +1814,11 @@ void tst_QQuickGridView::currentIndex()
     QCOMPARE(gridview->currentItem()->y(), gridview->highlightItem()->y());
     QCOMPARE(gridview->contentY(), 400.0);
 
-    gridview->moveCurrentIndexRight();
-    QCOMPARE(gridview->currentIndex(), 36);
-    gridview->moveCurrentIndexDown();
-    QCOMPARE(gridview->currentIndex(), 39);
-    gridview->moveCurrentIndexUp();
-    QCOMPARE(gridview->currentIndex(), 36);
-    gridview->moveCurrentIndexLeft();
-    QCOMPARE(gridview->currentIndex(), 35);
-
-    // wait until motion stops
-    QTRY_VERIFY(gridview->verticalVelocity() == 0.0);
-
-    // no wrap
     gridview->setCurrentIndex(0);
     QCOMPARE(gridview->currentIndex(), 0);
     // confirm that the velocity is updated
     QTRY_VERIFY(gridview->verticalVelocity() != 0.0);
 
-    gridview->moveCurrentIndexUp();
-    QCOMPARE(gridview->currentIndex(), 0);
-
-    gridview->moveCurrentIndexLeft();
-    QCOMPARE(gridview->currentIndex(), 0);
-
-    gridview->setCurrentIndex(model.count()-1);
-    QCOMPARE(gridview->currentIndex(), model.count()-1);
-
-    gridview->moveCurrentIndexRight();
-    QCOMPARE(gridview->currentIndex(), model.count()-1);
-
-    gridview->moveCurrentIndexDown();
-    QCOMPARE(gridview->currentIndex(), model.count()-1);
-
-    // with wrap
-    gridview->setWrapEnabled(true);
-
-    gridview->setCurrentIndex(0);
-    QCOMPARE(gridview->currentIndex(), 0);
-
-    gridview->moveCurrentIndexLeft();
-    QCOMPARE(gridview->currentIndex(), model.count()-1);
-
-    qApp->processEvents();
-    QTRY_COMPARE(gridview->contentY(), 880.0);
-
-    gridview->moveCurrentIndexRight();
-    QCOMPARE(gridview->currentIndex(), 0);
-
-    QTRY_COMPARE(gridview->contentY(), 0.0);
-
-
     // footer should become visible if it is out of view, and then current index moves to the first row
     canvas->rootObject()->setProperty("showFooter", true);
     QTRY_VERIFY(gridview->footerItem());
@@ -1708,78 +1837,6 @@ void tst_QQuickGridView::currentIndex()
     QTRY_COMPARE(gridview->contentY(), -gridview->headerItem()->height());
     canvas->rootObject()->setProperty("showHeader", false);
 
-
-    // Test keys
-    canvas->requestActivateWindow();
-    QTest::qWaitForWindowShown(canvas);
-    QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
-
-    gridview->setCurrentIndex(0);
-
-    QTest::keyClick(canvas, Qt::Key_Down);
-    QCOMPARE(gridview->currentIndex(), 3);
-
-    QTest::keyClick(canvas, Qt::Key_Up);
-    QCOMPARE(gridview->currentIndex(), 0);
-
-    // hold down Key_Down
-    for (int i=0; i<(model.count() / 3) - 1; i++) {
-        QTest::simulateEvent(canvas, true, Qt::Key_Down, Qt::NoModifier, "", true);
-        QTRY_COMPARE(gridview->currentIndex(), i*3 + 3);
-    }
-    QTest::keyRelease(canvas, Qt::Key_Down);
-    QTRY_COMPARE(gridview->currentIndex(), 57);
-    QTRY_COMPARE(gridview->contentY(), 880.0);
-
-    // hold down Key_Up
-    for (int i=(model.count() / 3) - 1; i > 0; i--) {
-        QTest::simulateEvent(canvas, true, Qt::Key_Up, Qt::NoModifier, "", true);
-        QTRY_COMPARE(gridview->currentIndex(), i*3 - 3);
-    }
-    QTest::keyRelease(canvas, Qt::Key_Up);
-    QTRY_COMPARE(gridview->currentIndex(), 0);
-    QTRY_COMPARE(gridview->contentY(), 0.0);
-
-
-    gridview->setFlow(QQuickGridView::TopToBottom);
-
-    canvas->requestActivateWindow();
-    QTest::qWaitForWindowShown(canvas);
-    QVERIFY(qGuiApp->focusWindow() == canvas);
-    qApp->processEvents();
-
-    QTest::keyClick(canvas, Qt::Key_Right);
-    QCOMPARE(gridview->currentIndex(), 5);
-
-    QTest::keyClick(canvas, Qt::Key_Left);
-    QCOMPARE(gridview->currentIndex(), 0);
-
-    QTest::keyClick(canvas, Qt::Key_Down);
-    QCOMPARE(gridview->currentIndex(), 1);
-
-    QTest::keyClick(canvas, Qt::Key_Up);
-    QCOMPARE(gridview->currentIndex(), 0);
-
-    // hold down Key_Right
-    for (int i=0; i<(model.count() / 5) - 1; i++) {
-        QTest::simulateEvent(canvas, true, Qt::Key_Right, Qt::NoModifier, "", true);
-        QTRY_COMPARE(gridview->currentIndex(), i*5 + 5);
-    }
-
-    QTest::keyRelease(canvas, Qt::Key_Right);
-    QTRY_COMPARE(gridview->currentIndex(), 55);
-    QTRY_COMPARE(gridview->contentX(), 720.0);
-
-    // hold down Key_Left
-    for (int i=(model.count() / 5) - 1; i > 0; i--) {
-        QTest::simulateEvent(canvas, true, Qt::Key_Left, Qt::NoModifier, "", true);
-        QTRY_COMPARE(gridview->currentIndex(), i*5 - 5);
-    }
-    QTest::keyRelease(canvas, Qt::Key_Left);
-    QTRY_COMPARE(gridview->currentIndex(), 0);
-    QTRY_COMPARE(gridview->contentX(), 0.0);
-
-
     // turn off auto highlight
     gridview->setHighlightFollowsCurrentItem(false);
     QVERIFY(gridview->highlightFollowsCurrentItem() == false);
@@ -1803,54 +1860,6 @@ void tst_QQuickGridView::currentIndex()
     QVERIFY(!gridview->highlightItem());
     QVERIFY(!gridview->currentItem());
 
-    gridview->setHighlightFollowsCurrentItem(true);
-
-    gridview->setFlow(QQuickGridView::LeftToRight);
-    gridview->setLayoutDirection(Qt::RightToLeft);
-
-    canvas->requestActivateWindow();
-    QTest::qWaitForWindowShown(canvas);
-    QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
-    qApp->processEvents();
-
-    gridview->setCurrentIndex(35);
-
-    QTest::keyClick(canvas, Qt::Key_Right);
-    QCOMPARE(gridview->currentIndex(), 34);
-
-    QTest::keyClick(canvas, Qt::Key_Down);
-    QCOMPARE(gridview->currentIndex(), 37);
-
-    QTest::keyClick(canvas, Qt::Key_Up);
-    QCOMPARE(gridview->currentIndex(), 34);
-
-    QTest::keyClick(canvas, Qt::Key_Left);
-    QCOMPARE(gridview->currentIndex(), 35);
-
-
-    // turn off auto highlight
-    gridview->setHighlightFollowsCurrentItem(false);
-    QVERIFY(gridview->highlightFollowsCurrentItem() == false);
-    QVERIFY(gridview->highlightItem());
-    hlPosX = gridview->highlightItem()->x();
-    hlPosY = gridview->highlightItem()->y();
-
-    gridview->setCurrentIndex(5);
-    QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
-    QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
-
-    // insert item before currentIndex
-    gridview->setCurrentIndex(28);
-    model.insertItem(0, "Foo", "1111");
-    QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
-
-    // check removing highlight by setting currentIndex to -1;
-    gridview->setCurrentIndex(-1);
-
-    QCOMPARE(gridview->currentIndex(), -1);
-    QVERIFY(!gridview->highlightItem());
-    QVERIFY(!gridview->currentItem());
-
     // moving currentItem out of view should make it invisible
     gridview->setCurrentIndex(0);
     QTRY_VERIFY(gridview->currentItem()->isVisible());
@@ -1897,6 +1906,199 @@ void tst_QQuickGridView::noCurrentIndex()
     delete canvas;
 }
 
+void tst_QQuickGridView::keyNavigation()
+{
+    QFETCH(QQuickGridView::Flow, flow);
+    QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
+    QFETCH(Qt::Key, forwardsKey);
+    QFETCH(Qt::Key, backwardsKey);
+    QFETCH(QPointF, contentPosAtFirstItem);
+    QFETCH(QPointF, contentPosAtLastItem);
+    QFETCH(int, indexRightOf7);
+    QFETCH(int, indexLeftOf7);
+    QFETCH(int, indexUpFrom7);
+    QFETCH(int, indexDownFrom7);
+
+    QmlListModel model;
+    for (int i = 0; i < 18; i++)
+        model.addItem("Item" + QString::number(i), "");
+
+    QQuickView *canvas = getView();
+    canvas->rootContext()->setContextProperty("testModel", &model);
+    canvas->setSource(testFileUrl("gridview1.qml"));
+    canvas->show();
+    qApp->processEvents();
+
+    QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
+    QTRY_VERIFY(gridview != 0);
+    gridview->setFlow(flow);
+    gridview->setLayoutDirection(layoutDirection);
+    gridview->setVerticalLayoutDirection(verticalLayoutDirection);
+    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+
+    canvas->requestActivateWindow();
+    QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
+    QCOMPARE(gridview->currentIndex(), 0);
+
+    QTest::keyClick(canvas, forwardsKey);
+    QCOMPARE(gridview->currentIndex(), 1);
+
+    QTest::keyClick(canvas, backwardsKey);
+    QCOMPARE(gridview->currentIndex(), 0);
+
+    gridview->setCurrentIndex(7);
+    gridview->moveCurrentIndexRight();
+    QCOMPARE(gridview->currentIndex(), indexRightOf7);
+    gridview->moveCurrentIndexLeft();
+    QCOMPARE(gridview->currentIndex(), 7);
+    gridview->moveCurrentIndexLeft();
+    QCOMPARE(gridview->currentIndex(), indexLeftOf7);
+    gridview->moveCurrentIndexRight();
+    QCOMPARE(gridview->currentIndex(), 7);
+    gridview->moveCurrentIndexUp();
+    QCOMPARE(gridview->currentIndex(), indexUpFrom7);
+    gridview->moveCurrentIndexDown();
+    QCOMPARE(gridview->currentIndex(), 7);
+    gridview->moveCurrentIndexDown();
+    QCOMPARE(gridview->currentIndex(), indexDownFrom7);
+
+    gridview->setCurrentIndex(7);
+    QTest::keyClick(canvas, Qt::Key_Right);
+    QCOMPARE(gridview->currentIndex(), indexRightOf7);
+    QTest::keyClick(canvas, Qt::Key_Left);
+    QCOMPARE(gridview->currentIndex(), 7);
+    QTest::keyClick(canvas, Qt::Key_Left);
+    QCOMPARE(gridview->currentIndex(), indexLeftOf7);
+    QTest::keyClick(canvas, Qt::Key_Right);
+    QCOMPARE(gridview->currentIndex(), 7);
+    QTest::keyClick(canvas, Qt::Key_Up);
+    QCOMPARE(gridview->currentIndex(), indexUpFrom7);
+    QTest::keyClick(canvas, Qt::Key_Down);
+    QCOMPARE(gridview->currentIndex(), 7);
+    QTest::keyClick(canvas, Qt::Key_Down);
+    QCOMPARE(gridview->currentIndex(), indexDownFrom7);
+
+    // hold down a key to go forwards
+    gridview->setCurrentIndex(0);
+    for (int i=0; i<model.count()-1; i++) {
+//        QTest::qWait(500);
+        QTest::simulateEvent(canvas, true, forwardsKey, Qt::NoModifier, "", true);
+        QTRY_COMPARE(gridview->currentIndex(), i+1);
+    }
+    QTest::keyRelease(canvas, forwardsKey);
+    QTRY_COMPARE(gridview->currentIndex(), model.count()-1);
+    QTRY_COMPARE(gridview->contentX(), contentPosAtLastItem.x());
+    QTRY_COMPARE(gridview->contentY(), contentPosAtLastItem.y());
+
+    // hold down a key to go backwards
+    for (int i=model.count()-1; i > 0; i--) {
+        QTest::simulateEvent(canvas, true, backwardsKey, Qt::NoModifier, "", true);
+        QTRY_COMPARE(gridview->currentIndex(), i-1);
+    }
+    QTest::keyRelease(canvas, backwardsKey);
+    QTRY_COMPARE(gridview->currentIndex(), 0);
+    QTRY_COMPARE(gridview->contentX(), contentPosAtFirstItem.x());
+    QTRY_COMPARE(gridview->contentY(), contentPosAtFirstItem.y());
+
+    // no wrap
+    QVERIFY(!gridview->isWrapEnabled());
+    QTest::keyClick(canvas, forwardsKey);
+    QCOMPARE(gridview->currentIndex(), 1);
+    QTest::keyClick(canvas, backwardsKey);
+    QCOMPARE(gridview->currentIndex(), 0);
+
+    QTest::keyClick(canvas, backwardsKey);
+    QCOMPARE(gridview->currentIndex(), 0);
+
+    // with wrap
+    gridview->setWrapEnabled(true);
+    QVERIFY(gridview->isWrapEnabled());
+
+    QTest::keyClick(canvas, backwardsKey);
+    QCOMPARE(gridview->currentIndex(), model.count()-1);
+    QTRY_COMPARE(gridview->contentX(), contentPosAtLastItem.x());
+    QTRY_COMPARE(gridview->contentY(), contentPosAtLastItem.y());
+
+    QTest::keyClick(canvas, forwardsKey);
+    QCOMPARE(gridview->currentIndex(), 0);
+    QTRY_COMPARE(gridview->contentX(), contentPosAtFirstItem.x());
+    QTRY_COMPARE(gridview->contentY(), contentPosAtFirstItem.y());
+
+    releaseView(canvas);
+}
+
+void tst_QQuickGridView::keyNavigation_data()
+{
+    QTest::addColumn<QQuickGridView::Flow>("flow");
+    QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
+    QTest::addColumn<Qt::Key>("forwardsKey");
+    QTest::addColumn<Qt::Key>("backwardsKey");
+    QTest::addColumn<QPointF>("contentPosAtFirstItem");
+    QTest::addColumn<QPointF>("contentPosAtLastItem");
+    QTest::addColumn<int>("indexRightOf7");
+    QTest::addColumn<int>("indexLeftOf7");
+    QTest::addColumn<int>("indexUpFrom7");
+    QTest::addColumn<int>("indexDownFrom7");
+
+    QTest::newRow("LeftToRight, LtR, TtB")
+            << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << Qt::Key_Right << Qt::Key_Left
+            << QPointF(0, 0)
+            << QPointF(0, 40)
+            << 8 << 6 << 4 << 10;
+
+    QTest::newRow("LeftToRight, RtL, TtB")
+            << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
+            << Qt::Key_Left << Qt::Key_Right
+            << QPointF(0, 0)
+            << QPointF(0, 40)
+            << 6 << 8 << 4 << 10;
+
+    QTest::newRow("LeftToRight, LtR, BtT")
+            << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
+            << Qt::Key_Right << Qt::Key_Left
+            << QPointF(0, -320)
+            << QPointF(0, -360)
+            << 8 << 6 << 10 << 4;
+
+    QTest::newRow("LeftToRight, RtL, BtT")
+            << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
+            << Qt::Key_Left << Qt::Key_Right
+            << QPointF(0, -320)
+            << QPointF(0, -360)
+            << 6 << 8 << 10 << 4;
+
+    QTest::newRow("TopToBottom, LtR, TtB")
+            << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << Qt::Key_Down << Qt::Key_Up
+            << QPointF(0, 0)
+            << QPointF(80, 0)
+            << 12 << 2 << 6 << 8;
+
+    QTest::newRow("TopToBottom, RtL, TtB")
+            << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
+            << Qt::Key_Down << Qt::Key_Up
+            << QPointF(-240, 0)
+            << QPointF(-320, 0)
+            << 2 << 12 << 6 << 8;
+
+    QTest::newRow("TopToBottom, LtR, BtT")
+            << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
+            << Qt::Key_Up << Qt::Key_Down
+            << QPointF(0, -320)
+            << QPointF(80, -320)
+            << 12 << 2 << 8 << 6;
+
+    QTest::newRow("TopToBottom, RtL, BtT")
+            << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
+            << Qt::Key_Up << Qt::Key_Down
+            << QPointF(-240, -320)
+            << QPointF(-320, -320)
+            << 2 << 12 << 8 << 6;
+}
+
 void tst_QQuickGridView::changeFlow()
 {
     QQuickView *canvas = createView();
@@ -1909,8 +2111,9 @@ void tst_QQuickGridView::changeFlow()
     ctxt->setContextProperty("testModel", &model);
     ctxt->setContextProperty("testRightToLeft", QVariant(false));
     ctxt->setContextProperty("testTopToBottom", QVariant(false));
+    ctxt->setContextProperty("testBottomToTop", QVariant(false));
 
-    canvas->setSource(testFileUrl("gridview1.qml"));
+    canvas->setSource(testFileUrl("layouts.qml"));
     qApp->processEvents();
 
     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
@@ -2056,15 +2259,15 @@ void tst_QQuickGridView::propertyChanges()
 
     QTRY_COMPARE(gridView->isWrapEnabled(), true);
     QTRY_COMPARE(gridView->cacheBuffer(), 10);
-    QTRY_COMPARE(gridView->flow(), QQuickGridView::LeftToRight);
+    QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowLeftToRight);
 
     gridView->setWrapEnabled(false);
     gridView->setCacheBuffer(3);
-    gridView->setFlow(QQuickGridView::TopToBottom);
+    gridView->setFlow(QQuickGridView::FlowTopToBottom);
 
     QTRY_COMPARE(gridView->isWrapEnabled(), false);
     QTRY_COMPARE(gridView->cacheBuffer(), 3);
-    QTRY_COMPARE(gridView->flow(), QQuickGridView::TopToBottom);
+    QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowTopToBottom);
 
     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
     QTRY_COMPARE(cacheBufferSpy.count(),1);
@@ -2072,14 +2275,14 @@ void tst_QQuickGridView::propertyChanges()
 
     gridView->setWrapEnabled(false);
     gridView->setCacheBuffer(3);
-    gridView->setFlow(QQuickGridView::TopToBottom);
+    gridView->setFlow(QQuickGridView::FlowTopToBottom);
 
     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
     QTRY_COMPARE(cacheBufferSpy.count(),1);
     QTRY_COMPARE(flowSpy.count(),1);
 
-    gridView->setFlow(QQuickGridView::LeftToRight);
-    QTRY_COMPARE(gridView->flow(), QQuickGridView::LeftToRight);
+    gridView->setFlow(QQuickGridView::FlowLeftToRight);
+    QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowLeftToRight);
 
     gridView->setWrapEnabled(true);
     gridView->setCacheBuffer(5);
@@ -2103,11 +2306,11 @@ void tst_QQuickGridView::propertyChanges()
     QTRY_COMPARE(layoutSpy.count(),1);
     QTRY_COMPARE(flowSpy.count(),2);
 
-    gridView->setFlow(QQuickGridView::TopToBottom);
-    QTRY_COMPARE(gridView->flow(), QQuickGridView::TopToBottom);
+    gridView->setFlow(QQuickGridView::FlowTopToBottom);
+    QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowTopToBottom);
     QTRY_COMPARE(flowSpy.count(),3);
 
-    gridView->setFlow(QQuickGridView::TopToBottom);
+    gridView->setFlow(QQuickGridView::FlowTopToBottom);
     QTRY_COMPARE(flowSpy.count(),3);
 
     delete canvas;
@@ -2208,8 +2411,9 @@ void tst_QQuickGridView::positionViewAtIndex()
     ctxt->setContextProperty("testModel", &model);
     ctxt->setContextProperty("testRightToLeft", QVariant(false));
     ctxt->setContextProperty("testTopToBottom", QVariant(false));
+    ctxt->setContextProperty("testBottomToTop", QVariant(false));
 
-    canvas->setSource(testFileUrl("gridview1.qml"));
+    canvas->setSource(testFileUrl("layouts.qml"));
     canvas->show();
     qApp->processEvents();
 
@@ -2406,11 +2610,7 @@ void tst_QQuickGridView::snapping()
     for (int i = 0; i < 40; i++)
         model.addItem("Item" + QString::number(i), "");
 
-    QQmlContext *ctxt = canvas->rootContext();
-    ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-
+    canvas->rootContext()->setContextProperty("testModel", &model);
     canvas->setSource(testFileUrl("gridview1.qml"));
     qApp->processEvents();
 
@@ -2512,8 +2712,9 @@ void tst_QQuickGridView::positionViewAtIndex_rightToLeft()
     ctxt->setContextProperty("testModel", &model);
     ctxt->setContextProperty("testTopToBottom", QVariant(true));
     ctxt->setContextProperty("testRightToLeft", QVariant(true));
+    ctxt->setContextProperty("testBottomToTop", QVariant(false));
 
-    canvas->setSource(testFileUrl("gridview1.qml"));
+    canvas->setSource(testFileUrl("layouts.qml"));
     qApp->processEvents();
 
     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
@@ -2841,8 +3042,8 @@ void tst_QQuickGridView::manualHighlight()
     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
 
-    gridview->setFlow(QQuickGridView::TopToBottom);
-    QTRY_COMPARE(gridview->flow(), QQuickGridView::TopToBottom);
+    gridview->setFlow(QQuickGridView::FlowTopToBottom);
+    QTRY_COMPARE(gridview->flow(), QQuickGridView::FlowTopToBottom);
 
     gridview->setCurrentIndex(0);
     QTRY_COMPARE(gridview->currentIndex(), 0);
@@ -2858,10 +3059,10 @@ void tst_QQuickGridView::footer()
 {
     QFETCH(QQuickGridView::Flow, flow);
     QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
     QFETCH(QPointF, initialFooterPos);
     QFETCH(QPointF, changedFooterPos);
     QFETCH(QPointF, initialContentPos);
-    QFETCH(QPointF, changedContentPos);
     QFETCH(QPointF, firstDelegatePos);
     QFETCH(QPointF, resizeContentPos);
 
@@ -2874,7 +3075,6 @@ void tst_QQuickGridView::footer()
 
     QQmlContext *ctxt = canvas->rootContext();
     ctxt->setContextProperty("testModel", &model);
-
     canvas->setSource(testFileUrl("footer.qml"));
     qApp->processEvents();
 
@@ -2882,13 +3082,14 @@ void tst_QQuickGridView::footer()
     QTRY_VERIFY(gridview != 0);
     gridview->setFlow(flow);
     gridview->setLayoutDirection(layoutDirection);
+    gridview->setVerticalLayoutDirection(verticalLayoutDirection);
+    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
     QQuickItem *contentItem = gridview->contentItem();
     QTRY_VERIFY(contentItem != 0);
 
     QQuickText *footer = findItem<QQuickText>(contentItem, "footer");
     QVERIFY(footer);
-
     QVERIFY(footer == gridview->footerItem());
 
     QCOMPARE(footer->pos(), initialFooterPos);
@@ -2896,7 +3097,7 @@ void tst_QQuickGridView::footer()
     QCOMPARE(footer->height(), 30.);
     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
 
-    if (flow == QQuickGridView::LeftToRight)
+    if (flow == QQuickGridView::FlowLeftToRight)
         QCOMPARE(gridview->contentHeight(), (model.count()+2) / 3 * 60. + footer->height());
     else
         QCOMPARE(gridview->contentWidth(), (model.count()+3) / 5 * 80. + footer->width());
@@ -2905,10 +3106,13 @@ void tst_QQuickGridView::footer()
     QVERIFY(item);
     QCOMPARE(item->pos(), firstDelegatePos);
 
-    if (flow == QQuickGridView::LeftToRight) {
+    if (flow == QQuickGridView::FlowLeftToRight) {
         // shrink by one row
         model.removeItem(2);
-        QTRY_COMPARE(footer->y(), initialFooterPos.y() - gridview->cellHeight());
+        if (verticalLayoutDirection == QQuickItemView::TopToBottom)
+            QTRY_COMPARE(footer->y(), initialFooterPos.y() - gridview->cellHeight());
+        else
+            QTRY_COMPARE(footer->y(), initialFooterPos.y() + gridview->cellHeight());
     } else {
         // shrink by one column
         model.removeItem(2);
@@ -2924,10 +3128,12 @@ void tst_QQuickGridView::footer()
 
     QPointF posWhenNoItems(0, 0);
     if (layoutDirection == Qt::RightToLeft)
-        posWhenNoItems.setX(flow == QQuickGridView::LeftToRight ? gridview->width() - footer->width() : -footer->width());
+        posWhenNoItems.setX(flow == QQuickGridView::FlowLeftToRight ? gridview->width() - footer->width() : -footer->width());
+    if (verticalLayoutDirection == QQuickItemView::BottomToTop)
+        posWhenNoItems.setY(-footer->height());
     QTRY_COMPARE(footer->pos(), posWhenNoItems);
 
-    // if header is present, it's at a negative pos, so the footer should not move
+    // if header is toggled, it shouldn't affect the footer position
     canvas->rootObject()->setProperty("showHeader", true);
     QVERIFY(findItem<QQuickItem>(contentItem, "header") != 0);
     QTRY_COMPARE(footer->pos(), posWhenNoItems);
@@ -2946,13 +3152,14 @@ void tst_QQuickGridView::footer()
     QVERIFY(!footer);
     footer = findItem<QQuickText>(contentItem, "footer2");
     QVERIFY(footer);
-
     QVERIFY(footer == gridview->footerItem());
 
     QCOMPARE(footer->pos(), changedFooterPos);
     QCOMPARE(footer->width(), 50.);
     QCOMPARE(footer->height(), 20.);
-    QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
+
+    // changing the footer shouldn't change the content pos
+    QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
 
     item = findItem<QQuickItem>(contentItem, "wrapper", 0);
     QVERIFY(item);
@@ -2970,10 +3177,10 @@ void tst_QQuickGridView::footer_data()
 {
     QTest::addColumn<QQuickGridView::Flow>("flow");
     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
     QTest::addColumn<QPointF>("initialFooterPos");
     QTest::addColumn<QPointF>("changedFooterPos");
     QTest::addColumn<QPointF>("initialContentPos");
-    QTest::addColumn<QPointF>("changedContentPos");
     QTest::addColumn<QPointF>("firstDelegatePos");
     QTest::addColumn<QPointF>("resizeContentPos");
 
@@ -2984,46 +3191,84 @@ void tst_QQuickGridView::footer_data()
     // view height = 320
 
     // footer below items, bottom left
-    QTest::newRow("flow left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight
+    QTest::newRow("LeftToRight, LtR, TtB")
+        << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
         << 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)
-        << QPointF(0, 10 * 60 - 320 + 10);
+        << QPointF(0, (10 * 60) - 320 + 10);
 
     // footer below items, bottom right
-    QTest::newRow("flow left to right, layout right to left") << QQuickGridView::LeftToRight << Qt::RightToLeft
+    QTest::newRow("LeftToRight, RtL, TtB")
+        << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
         << 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)
-        << QPointF(0, 10 * 60 - 320 + 10);
-
-    // footer to right of items
-    QTest::newRow("flow top to bottom, layout left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight
+        << QPointF(0, (10 * 60) - 320 + 10);
+
+    // footer above items, top left
+    QTest::newRow("LeftToRight, LtR, BtT")
+        << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
+        << QPointF(0, -(3 * 60) - 30)
+        << QPointF(0, -(10 * 60) - 20)
+        << QPointF(0, -320)
+        << QPointF(0, -60)
+        << QPointF(0, -(10 * 60) - 10);
+
+    // footer above items, top right
+    QTest::newRow("LeftToRight, RtL, BtT")
+        << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
+        << QPointF(240 - 100, -(3 * 60) - 30)
+        << QPointF((240 - 100) + 50, -(10 * 60) - 20)
+        << QPointF(0, -320)
+        << QPointF(240 - 80, -60)
+        << QPointF(0, -(10 * 60) - 10);
+
+
+    // footer to right of items, bottom right
+    QTest::newRow("TopToBottom, LtR, TtB")
+        << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
         << 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)
-        << QPointF(6 * 80 - 240 + 40, 0);
+        << QPointF((6 * 80) - 240 + 40, 0);
 
-    // footer to left of items
-    QTest::newRow("flow top to bottom, layout right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft
+    // footer to left of items, bottom right
+    QTest::newRow("TopToBottom, RtL, TtB")
+        << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
         << 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)
         << QPointF(-(6 * 80) - 40, 0);
+
+    // footer to right of items, top right
+    QTest::newRow("TopToBottom, LtR, BtT")
+        << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
+        << QPointF(2 * 80, -30)
+        << QPointF(6 * 80, -20)
+        << QPointF(0, -320)
+        << QPointF(0, -60)
+        << QPointF((6 * 80) - 240 + 40, -320);
+
+    // footer to left of items, top left
+    QTest::newRow("TopToBottom, RtL, BtT")
+        << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
+        << QPointF(-(2 * 80) - 100, -30)
+        << QPointF(-(6 * 80) - 50, -20)
+        << QPointF(-240, -320)
+        << QPointF(-80, -60)
+        << QPointF(-(6 * 80) - 40, -320);
 }
 
 void tst_QQuickGridView::header()
 {
     QFETCH(QQuickGridView::Flow, flow);
     QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
     QFETCH(QPointF, initialHeaderPos);
     QFETCH(QPointF, changedHeaderPos);
     QFETCH(QPointF, initialContentPos);
@@ -3047,6 +3292,7 @@ void tst_QQuickGridView::header()
     QTRY_VERIFY(gridview != 0);
     gridview->setFlow(flow);
     gridview->setLayoutDirection(layoutDirection);
+    gridview->setVerticalLayoutDirection(verticalLayoutDirection);
     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
     QQuickItem *contentItem = gridview->contentItem();
@@ -3054,7 +3300,6 @@ void tst_QQuickGridView::header()
 
     QQuickText *header = findItem<QQuickText>(contentItem, "header");
     QVERIFY(header);
-
     QVERIFY(header == gridview->headerItem());
 
     QCOMPARE(header->pos(), initialHeaderPos);
@@ -3062,7 +3307,7 @@ void tst_QQuickGridView::header()
     QCOMPARE(header->height(), 30.);
     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
 
-    if (flow == QQuickGridView::LeftToRight)
+    if (flow == QQuickGridView::FlowLeftToRight)
         QCOMPARE(gridview->contentHeight(), (model.count()+2) / 3 * 60. + header->height());
     else
         QCOMPARE(gridview->contentWidth(), (model.count()+3) / 5 * 80. + header->width());
@@ -3120,6 +3365,7 @@ void tst_QQuickGridView::header()
     QTRY_VERIFY(gridview != 0);
     gridview->setFlow(flow);
     gridview->setLayoutDirection(layoutDirection);
+    gridview->setVerticalLayoutDirection(verticalLayoutDirection);
     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
     gridview->setWidth(240);
@@ -3134,6 +3380,7 @@ void tst_QQuickGridView::header_data()
 {
     QTest::addColumn<QQuickGridView::Flow>("flow");
     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
     QTest::addColumn<QPointF>("initialHeaderPos");
     QTest::addColumn<QPointF>("changedHeaderPos");
     QTest::addColumn<QPointF>("initialContentPos");
@@ -3147,7 +3394,8 @@ void tst_QQuickGridView::header_data()
     // view width = 240
 
     // header above items, top left
-    QTest::newRow("flow left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight
+    QTest::newRow("LeftToRight, LtR, TtB")
+        << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
         << QPointF(0, -30)
         << QPointF(0, -20)
         << QPointF(0, -30)
@@ -3156,7 +3404,8 @@ void tst_QQuickGridView::header_data()
         << QPointF(0, -10);
 
     // header above items, top right
-    QTest::newRow("flow left to right, layout right to left") << QQuickGridView::LeftToRight << Qt::RightToLeft
+    QTest::newRow("LeftToRight, RtL, TtB")
+        << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
         << QPointF(240 - 100, -30)
         << QPointF((240 - 100) + 50, -20)     // 50 = width diff between old and new headers
         << QPointF(0, -30)
@@ -3164,8 +3413,30 @@ void tst_QQuickGridView::header_data()
         << QPointF(160, 0)
         << QPointF(0, -10);
 
-    // header to left of items
-    QTest::newRow("flow top to bottom, layout left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight
+    // header below items, bottom left
+    QTest::newRow("LeftToRight, LtR, BtT")
+        << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
+        << QPointF(0, 0)
+        << QPointF(0, 0)
+        << QPointF(0, -320 + 30)
+        << QPointF(0, -320 + 20)
+        << QPointF(0, -60)
+        << QPointF(0, -320 + 10);
+
+    // header above items, top right
+    QTest::newRow("LeftToRight, RtL, BtT")
+        << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
+        << QPointF(240 - 100, 0)
+        << QPointF((240 - 100) + 50, 0)
+        << QPointF(0, -320 + 30)
+        << QPointF(0, -320 + 20)
+        << QPointF(160, -60)
+        << QPointF(0, -320 + 10);
+
+
+    // header to left of items, bottom left
+    QTest::newRow("TopToBottom, LtR, TtB")
+        << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
         << QPointF(-100, 0)
         << QPointF(-50, 0)
         << QPointF(-100, 0)
@@ -3173,14 +3444,35 @@ void tst_QQuickGridView::header_data()
         << QPointF(0, 0)
         << QPointF(-40, 0);
 
-    // header to right of items
-    QTest::newRow("flow top to bottom, layout right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft
+    // header to right of items, bottom right
+    QTest::newRow("TopToBottom, RtL, TtB")
+        << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
         << QPointF(0, 0)
         << QPointF(0, 0)
         << QPointF(-(240 - 100), 0)
         << QPointF(-(240 - 50), 0)
         << QPointF(-80, 0)
         << QPointF(-(240 - 40), 0);
+
+    // header to left of items, top left
+    QTest::newRow("TopToBottom, LtR, BtT")
+        << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
+        << QPointF(-100, -30)
+        << QPointF(-50, -20)
+        << QPointF(-100, -320)
+        << QPointF(-50, -320)
+        << QPointF(0, -60)
+        << QPointF(-40, -320);
+
+    // header to right of items, top right
+    QTest::newRow("TopToBottom, RtL, BtT")
+        << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
+        << QPointF(0, -30)
+        << QPointF(0, -20)
+        << QPointF(-(240 - 100), -320)
+        << QPointF(-(240 - 50), -320)
+        << QPointF(-80, -60)
+        << QPointF(-(240 - 40), -320);
 }
 
 class GVAccessor : public QQuickGridView
@@ -3194,138 +3486,144 @@ public:
 
 void tst_QQuickGridView::headerFooter()
 {
-    {
-        // Vertical
-        QQuickView *canvas = createView();
-
-        QmlListModel model;
-        QQmlContext *ctxt = canvas->rootContext();
-        ctxt->setContextProperty("testModel", &model);
-
-        canvas->setSource(testFileUrl("headerfooter.qml"));
-        qApp->processEvents();
-
-        QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
-        QTRY_VERIFY(gridview != 0);
-
-        QQuickItem *contentItem = gridview->contentItem();
-        QTRY_VERIFY(contentItem != 0);
-
-        QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
-        QVERIFY(header);
-        QCOMPARE(header->y(), -header->height());
-
-        QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
-        QVERIFY(footer);
-        QCOMPARE(footer->y(), 0.);
-
-        QCOMPARE(static_cast<GVAccessor*>(gridview)->minY(), header->height());
-        QCOMPARE(static_cast<GVAccessor*>(gridview)->maxY(), header->height());
-
-        delete canvas;
-    }
-    {
-        // Horizontal
-        QQuickView *canvas = createView();
-
-        QmlListModel model;
-        QQmlContext *ctxt = canvas->rootContext();
-        ctxt->setContextProperty("testModel", &model);
-
-        canvas->setSource(testFileUrl("headerfooter.qml"));
-        canvas->rootObject()->setProperty("horizontal", true);
-        qApp->processEvents();
-
-        QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
-        QTRY_VERIFY(gridview != 0);
-
-        QQuickItem *contentItem = gridview->contentItem();
-        QTRY_VERIFY(contentItem != 0);
-
-        QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
-        QVERIFY(header);
-        QCOMPARE(header->x(), -header->width());
+    QFETCH(QQuickGridView::Flow, flow);
+    QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
+    QFETCH(QPointF, headerPos);
+    QFETCH(QPointF, footerPos);
+    QFETCH(QPointF, minPos);
+    QFETCH(QPointF, maxPos);
 
-        QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
-        QVERIFY(footer);
-        QCOMPARE(footer->x(), 0.);
+    QQuickView *canvas = getView();
 
-        QCOMPARE(static_cast<GVAccessor*>(gridview)->minX(), header->width());
-        QCOMPARE(static_cast<GVAccessor*>(gridview)->maxX(), header->width());
+    QmlListModel model;
+    QQmlContext *ctxt = canvas->rootContext();
+    ctxt->setContextProperty("testModel", &model);
+    canvas->setSource(testFileUrl("headerfooter.qml"));
+    canvas->show();
+    qApp->processEvents();
 
-        delete canvas;
-    }
-    {
-        // Horizontal RTL
-        QQuickView *canvas = createView();
+    QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
+    QTRY_VERIFY(gridview != 0);
+    gridview->setFlow(flow);
+    gridview->setLayoutDirection(layoutDirection);
+    gridview->setVerticalLayoutDirection(verticalLayoutDirection);
+    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
-        QmlListModel model;
-        QQmlContext *ctxt = canvas->rootContext();
-        ctxt->setContextProperty("testModel", &model);
+    QQuickItem *contentItem = gridview->contentItem();
+    QTRY_VERIFY(contentItem != 0);
 
-        canvas->setSource(testFileUrl("headerfooter.qml"));
-        canvas->rootObject()->setProperty("horizontal", true);
-        canvas->rootObject()->setProperty("rtl", true);
-        qApp->processEvents();
+    QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
+    QVERIFY(header);
+    QCOMPARE(header->pos(), headerPos);
 
-        QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
-        QTRY_VERIFY(gridview != 0);
+    QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
+    QVERIFY(footer);
+    QCOMPARE(footer->pos(), footerPos);
 
-        QQuickItem *contentItem = gridview->contentItem();
-        QTRY_VERIFY(contentItem != 0);
+    QCOMPARE(static_cast<GVAccessor*>(gridview)->minX(), minPos.x());
+    QCOMPARE(static_cast<GVAccessor*>(gridview)->minY(), minPos.y());
+    QCOMPARE(static_cast<GVAccessor*>(gridview)->maxX(), maxPos.x());
+    QCOMPARE(static_cast<GVAccessor*>(gridview)->maxY(), maxPos.y());
 
-        QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
-        QVERIFY(header);
-        QCOMPARE(header->x(), 0.);
+    releaseView(canvas);
+}
 
-        QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
-        QVERIFY(footer);
-        QCOMPARE(footer->x(), -footer->width());
+void tst_QQuickGridView::headerFooter_data()
+{
+    QTest::addColumn<QQuickGridView::Flow>("flow");
+    QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
+    QTest::addColumn<QPointF>("headerPos");
+    QTest::addColumn<QPointF>("footerPos");
+    QTest::addColumn<QPointF>("minPos");
+    QTest::addColumn<QPointF>("maxPos");
+
+    // header is 240x20 (or 20x320 in TopToBottom)
+    // footer is 240x30 (or 30x320 in TopToBottom)
+
+    QTest::newRow("LeftToRight, LtR, TtB")
+            << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << QPointF(0, -20) << QPointF(0, 0)
+            << QPointF(0, 20) << QPointF(240, 20);
+
+    QTest::newRow("LeftToRight, RtL, TtB")
+            << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
+            << QPointF(0, -20) << QPointF(0, 0)
+            << QPointF(0, 20) << QPointF(240, 20);
+
+    QTest::newRow("LeftToRight, LtR, BtT")
+            << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
+            << QPointF(0, 0) << QPointF(0, -30)
+            << QPointF(0, 320 - 20) << QPointF(240, 320 - 20);  // content flow is reversed
+
+    QTest::newRow("LeftToRight, RtL, BtT")
+            << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
+            << QPointF(0, 0) << QPointF(0, -30)
+            << QPointF(0, 320 - 20) << QPointF(240, 320 - 20);  // content flow is reversed
+
+
+    QTest::newRow("TopToBottom, LtR, TtB")
+            << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << QPointF(-20, 0) << QPointF(0, 0)
+            << QPointF(20, 0) << QPointF(20, 320);
+
+    QTest::newRow("TopToBottom, RtL, TtB")
+            << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
+            << QPointF(0, 0) << QPointF(-30, 0)
+            << QPointF(240 - 20, 0) << QPointF(240 - 20, 320);  // content flow is reversed
+
+    QTest::newRow("TopToBottom, LtR, BtT")
+            << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
+            << QPointF(-20, -320) << QPointF(0, -320)
+            << QPointF(20, 0) << QPointF(20, 320);
+
+    QTest::newRow("TopToBottom, RtL, BtT")
+            << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
+            << QPointF(0, -320) << QPointF(-30, -320)
+            << QPointF(240 - 20, 0) << QPointF(240 - 20, 320);  // content flow is reversed
+}
 
-        QCOMPARE(static_cast<GVAccessor*>(gridview)->minX(), 240. - header->width());
-        QCOMPARE(static_cast<GVAccessor*>(gridview)->maxX(), 240. - header->width());
+void tst_QQuickGridView::resetModel_headerFooter()
+{
+    // Resetting a model shouldn't crash in views with header/footer
 
-        delete canvas;
-    }
-    {
-        // Reset model
-        QQuickView *canvas = createView();
+    QQuickView *canvas = createView();
 
-        QaimModel model;
-        for (int i = 0; i < 6; i++)
-            model.addItem("Item" + QString::number(i), "");
-        QQmlContext *ctxt = canvas->rootContext();
-        ctxt->setContextProperty("testModel", &model);
+    QaimModel model;
+    for (int i = 0; i < 6; i++)
+        model.addItem("Item" + QString::number(i), "");
+    QQmlContext *ctxt = canvas->rootContext();
+    ctxt->setContextProperty("testModel", &model);
 
-        canvas->setSource(testFileUrl("headerfooter.qml"));
-        qApp->processEvents();
+    canvas->setSource(testFileUrl("headerfooter.qml"));
+    qApp->processEvents();
 
-        QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
-        QTRY_VERIFY(gridview != 0);
+    QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
+    QTRY_VERIFY(gridview != 0);
 
-        QQuickItem *contentItem = gridview->contentItem();
-        QTRY_VERIFY(contentItem != 0);
+    QQuickItem *contentItem = gridview->contentItem();
+    QTRY_VERIFY(contentItem != 0);
 
-        QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
-        QVERIFY(header);
-        QCOMPARE(header->y(), -header->height());
+    QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
+    QVERIFY(header);
+    QCOMPARE(header->y(), -header->height());
 
-        QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
-        QVERIFY(footer);
-        QCOMPARE(footer->y(), 80.*2);
+    QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
+    QVERIFY(footer);
+    QCOMPARE(footer->y(), 80.*2);
 
-        model.reset();
+    model.reset();
 
-        header = findItem<QQuickItem>(contentItem, "header");
-        QVERIFY(header);
-        QCOMPARE(header->y(), -header->height());
+    header = findItem<QQuickItem>(contentItem, "header");
+    QVERIFY(header);
+    QCOMPARE(header->y(), -header->height());
 
-        footer = findItem<QQuickItem>(contentItem, "footer");
-        QVERIFY(footer);
-        QCOMPARE(footer->y(), 80.*2);
+    footer = findItem<QQuickItem>(contentItem, "footer");
+    QVERIFY(footer);
+    QCOMPARE(footer->y(), 80.*2);
 
-        delete canvas;
-    }
+    delete canvas;
 }
 
 void tst_QQuickGridView::resizeViewAndRepaint()
@@ -3399,6 +3697,147 @@ void tst_QQuickGridView::resizeViewAndRepaint()
     delete canvas;
 }
 
+void tst_QQuickGridView::resizeGrid()
+{
+    QFETCH(QQuickGridView::Flow, flow);
+    QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
+    QFETCH(QPointF, initialContentPos);
+    QFETCH(QPointF, firstItemPos);
+
+    QaimModel model;
+    for (int i = 0; i < 30; i++)
+        model.addItem("Item" + QString::number(i), "");
+
+    QQuickView *canvas = getView();
+    QQmlContext *ctxt = canvas->rootContext();
+    ctxt->setContextProperty("testModel", &model);
+    ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
+    ctxt->setContextProperty("testRightToLeft", layoutDirection == Qt::RightToLeft);
+    ctxt->setContextProperty("testBottomToTop", verticalLayoutDirection == QQuickGridView::BottomToTop);
+    canvas->setSource(testFileUrl("resizegrid.qml"));
+    canvas->show();
+    qApp->processEvents();
+
+    QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
+    QTRY_VERIFY(gridview != 0);
+    QQuickItem *contentItem = gridview->contentItem();
+    QTRY_VERIFY(contentItem != 0);
+
+    // set the width to slightly larger than 3 items across, to test
+    // items are aligned correctly in right-to-left
+    canvas->rootObject()->setWidth(260);
+    canvas->rootObject()->setHeight(320);
+    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+
+    QCOMPARE(gridview->contentX(), initialContentPos.x());
+    QCOMPARE(gridview->contentY(), initialContentPos.y());
+
+    QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
+    QVERIFY(item0);
+    QCOMPARE(item0->pos(), firstItemPos);
+
+    // Confirm items positioned correctly and indexes correct
+    QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+    QVERIFY(items.count() >= 18 && items.count() <= 21);
+    for (int i = 0; i < model.count() && i < items.count(); ++i) {
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+        QCOMPARE(item->pos(), expectedItemPos(gridview, i, 0));
+        QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
+        QVERIFY(name != 0);
+        QCOMPARE(name->text(), model.name(i));
+    }
+
+    // change from 3x5 grid to 4x7
+    canvas->rootObject()->setWidth(canvas->rootObject()->width() + 80);
+    canvas->rootObject()->setHeight(canvas->rootObject()->height() + 60*2);
+    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+
+    // other than in LeftToRight+RightToLeft layout, the first item should not move
+    // if view is resized
+    QCOMPARE(findItem<QQuickItem>(contentItem, "wrapper", 0), item0);
+    if (flow == QQuickGridView::FlowLeftToRight && layoutDirection == Qt::RightToLeft)
+        firstItemPos.rx() += 80;
+    QCOMPARE(item0->pos(), firstItemPos);
+
+    QPointF newContentPos = initialContentPos;
+    if (flow == QQuickGridView::FlowTopToBottom && layoutDirection == Qt::RightToLeft)
+        newContentPos.rx() -= 80.0;
+    if (verticalLayoutDirection == QQuickItemView::BottomToTop)
+        newContentPos.ry() -= 60.0 * 2;
+    QCOMPARE(gridview->contentX(), newContentPos.x());
+    QCOMPARE(gridview->contentY(), newContentPos.y());
+
+    // Confirm items positioned correctly and indexes correct
+    items = findItems<QQuickItem>(contentItem, "wrapper");
+    QVERIFY(items.count() >= 28);
+    for (int i = 0; i < model.count() && i < items.count(); ++i) {
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+        QCOMPARE(item->pos(), expectedItemPos(gridview, i, 0));
+        QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
+        QVERIFY(name != 0);
+        QCOMPARE(name->text(), model.name(i));
+    }
+
+    releaseView(canvas);
+}
+
+void tst_QQuickGridView::resizeGrid_data()
+{
+    QTest::addColumn<QQuickGridView::Flow>("flow");
+    QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
+    QTest::addColumn<QPointF>("initialContentPos");
+    QTest::addColumn<QPointF>("firstItemPos");
+
+    // Initial view width is 260, so in LeftToRight + right-to-left mode the
+    // content x should be -20
+
+    QTest::newRow("LeftToRight, LtR, TtB")
+            << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << QPointF(0, 0)
+            << QPointF(0, 0);
+
+    QTest::newRow("LeftToRight, RtL, TtB")
+            << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
+            << QPointF(-20.0, 0)
+            << QPointF(80.0 * 2, 0);
+
+    QTest::newRow("LeftToRight, LtR, BtT")
+            << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
+            << QPointF(0, -320)
+            << QPointF(0, -60.0);
+
+    QTest::newRow("LeftToRight, RtL, BtT")
+            << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
+            << QPointF(-20.0, -320)
+            << QPointF(80.0 * 2, -60.0);
+
+
+    QTest::newRow("TopToBottom, LtR, TtB")
+            << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << QPointF(0, 0)
+            << QPointF(0, 0);
+
+    QTest::newRow("TopToBottom, RtL, TtB")
+            << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
+            << QPointF(-260, 0)
+            << QPointF(-80.0, 0);
+
+    QTest::newRow("TopToBottom, LtR, BtT")
+            << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
+            << QPointF(0, -320)
+            << QPointF(0, -60.0);
+
+    QTest::newRow("TopToBottom, RtL, BtT")
+            << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
+            << QPointF(-260, -320)
+            << QPointF(-80.0, -60.0);
+}
+
+
 void tst_QQuickGridView::changeColumnCount()
 {
     QmlListModel model;
@@ -3487,11 +3926,7 @@ void tst_QQuickGridView::indexAt_itemAt()
     model.addItem("Ben", "04321");
     model.addItem("Jim", "0780");
 
-    QQmlContext *ctxt = canvas->rootContext();
-    ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-
+    canvas->rootContext()->setContextProperty("testModel", &model);
     canvas->setSource(testFileUrl("gridview1.qml"));
     qApp->processEvents();
 
@@ -3805,22 +4240,22 @@ void tst_QQuickGridView::snapToRow_data()
     QTest::addColumn<qreal>("endExtent");
     QTest::addColumn<qreal>("startExtent");
 
-    QTest::newRow("vertical, left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("vertical, left to right") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 800.0 << 0.0;
 
-    QTest::newRow("horizontal, left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("horizontal, left to right") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 800.0 << 0.0;
 
-    QTest::newRow("horizontal, right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("horizontal, right to left") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -800.0 - 240.0 << -240.0;
 
-    QTest::newRow("vertical, left to right, enforce range") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("vertical, left to right, enforce range") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 940.0 << -20.0;
 
-    QTest::newRow("horizontal, left to right, enforce range") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("horizontal, left to right, enforce range") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 940.0 << -20.0;
 
-    QTest::newRow("horizontal, right to left, enforce range") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("horizontal, right to left, enforce range") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -800.0 - 240.0 - 140.0 << -220.0;
 }
 
@@ -3855,7 +4290,7 @@ void tst_QQuickGridView::snapToRow()
     // confirm that a flick hits an item boundary
     flick(canvas, flickStart, flickEnd, 180);
     QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
-    if (flow == QQuickGridView::LeftToRight)
+    if (flow == QQuickGridView::FlowLeftToRight)
         QCOMPARE(qreal(fmod(gridview->contentY(),80.0)), snapAlignment);
     else
         QCOMPARE(qreal(fmod(gridview->contentX(),80.0)), snapAlignment);
@@ -3864,11 +4299,11 @@ void tst_QQuickGridView::snapToRow()
     do {
         flick(canvas, flickStart, flickEnd, 180);
         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
-    } while (flow == QQuickGridView::LeftToRight
+    } while (flow == QQuickGridView::FlowLeftToRight
            ? !gridview->isAtYEnd()
            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning());
 
-    if (flow == QQuickGridView::LeftToRight)
+    if (flow == QQuickGridView::FlowLeftToRight)
         QCOMPARE(gridview->contentY(), endExtent);
     else
         QCOMPARE(gridview->contentX(), endExtent);
@@ -3877,11 +4312,11 @@ void tst_QQuickGridView::snapToRow()
     do {
         flick(canvas, flickEnd, flickStart, 180);
         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
-    } while (flow == QQuickGridView::LeftToRight
+    } while (flow == QQuickGridView::FlowLeftToRight
            ? !gridview->isAtYBeginning()
            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd());
 
-    if (flow == QQuickGridView::LeftToRight)
+    if (flow == QQuickGridView::FlowLeftToRight)
         QCOMPARE(gridview->contentY(), startExtent);
     else
         QCOMPARE(gridview->contentX(), startExtent);
@@ -3900,22 +4335,22 @@ void tst_QQuickGridView::snapOneRow_data()
     QTest::addColumn<qreal>("endExtent");
     QTest::addColumn<qreal>("startExtent");
 
-    QTest::newRow("vertical, left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("vertical, left to right") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
         << QPoint(20, 200) << QPoint(20, 20) << 100.0 << 240.0 << 0.0;
 
-    QTest::newRow("horizontal, left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("horizontal, left to right") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
         << QPoint(200, 20) << QPoint(20, 20) << 100.0 << 240.0 << 0.0;
 
-    QTest::newRow("horizontal, right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("horizontal, right to left") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
         << QPoint(20, 20) << QPoint(200, 20) << -340.0 << -240.0 - 240.0 << -240.0;
 
-    QTest::newRow("vertical, left to right, enforce range") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("vertical, left to right, enforce range") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(20, 200) << QPoint(20, 20) << 100.0 << 340.0 << -20.0;
 
-    QTest::newRow("horizontal, left to right, enforce range") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("horizontal, left to right, enforce range") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(200, 20) << QPoint(20, 20) << 100.0 << 340.0 << -20.0;
 
-    QTest::newRow("horizontal, right to left, enforce range") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("horizontal, right to left, enforce range") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(20, 20) << QPoint(200, 20) << -340.0 << -240.0 - 240.0 - 100.0 << -220.0;
 }
 
@@ -3952,7 +4387,7 @@ void tst_QQuickGridView::snapOneRow()
     // confirm that a flick hits next row boundary
     flick(canvas, flickStart, flickEnd, 180);
     QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
-    if (flow == QQuickGridView::LeftToRight)
+    if (flow == QQuickGridView::FlowLeftToRight)
         QCOMPARE(gridview->contentY(), snapAlignment);
     else
         QCOMPARE(gridview->contentX(), snapAlignment);
@@ -3966,7 +4401,7 @@ void tst_QQuickGridView::snapOneRow()
     do {
         flick(canvas, flickStart, flickEnd, 180);
         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
-    } while (flow == QQuickGridView::LeftToRight
+    } while (flow == QQuickGridView::FlowLeftToRight
            ? !gridview->isAtYEnd()
            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning());
 
@@ -3975,7 +4410,7 @@ void tst_QQuickGridView::snapOneRow()
         QCOMPARE(currentIndexSpy.count(), 3);
     }
 
-    if (flow == QQuickGridView::LeftToRight)
+    if (flow == QQuickGridView::FlowLeftToRight)
         QCOMPARE(gridview->contentY(), endExtent);
     else
         QCOMPARE(gridview->contentX(), endExtent);
@@ -3984,11 +4419,11 @@ void tst_QQuickGridView::snapOneRow()
     do {
         flick(canvas, flickEnd, flickStart, 180);
         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
-    } while (flow == QQuickGridView::LeftToRight
+    } while (flow == QQuickGridView::FlowLeftToRight
            ? !gridview->isAtYBeginning()
            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd());
 
-    if (flow == QQuickGridView::LeftToRight)
+    if (flow == QQuickGridView::FlowLeftToRight)
         QCOMPARE(gridview->contentY(), startExtent);
     else
         QCOMPARE(gridview->contentX(), startExtent);
@@ -5312,11 +5747,7 @@ void tst_QQuickGridView::cacheBuffer()
     for (int i = 0; i < 90; i++)
         model.addItem("Item" + QString::number(i), "");
 
-    QQmlContext *ctxt = canvas->rootContext();
-    ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testRightToLeft", QVariant(false));
-    ctxt->setContextProperty("testTopToBottom", QVariant(false));
-
+    canvas->rootContext()->setContextProperty("testModel", &model);
     canvas->setSource(testFileUrl("gridview1.qml"));
     canvas->show();
     qApp->processEvents();
@@ -5620,6 +6051,220 @@ void tst_QQuickGridView::unrequestedVisibility()
     delete canvas;
 }
 
+
+void tst_QQuickGridView::inserted_leftToRight_RtL_TtB()
+{
+    inserted_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft);
+}
+
+void tst_QQuickGridView::inserted_leftToRight_RtL_TtB_data()
+{
+    inserted_defaultLayout_data();
+}
+
+void tst_QQuickGridView::inserted_topToBottom_LtR_TtB()
+{
+    inserted_defaultLayout(QQuickGridView::FlowTopToBottom);
+}
+
+void tst_QQuickGridView::inserted_topToBottom_LtR_TtB_data()
+{
+    inserted_defaultLayout_data();
+}
+
+void tst_QQuickGridView::inserted_topToBottom_RtL_TtB()
+{
+    inserted_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft);
+}
+
+void tst_QQuickGridView::inserted_topToBottom_RtL_TtB_data()
+{
+    inserted_defaultLayout_data();
+}
+
+void tst_QQuickGridView::inserted_leftToRight_LtR_BtT()
+{
+    inserted_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::LeftToRight, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::inserted_leftToRight_LtR_BtT_data()
+{
+    inserted_defaultLayout_data();
+}
+
+void tst_QQuickGridView::inserted_leftToRight_RtL_BtT()
+{
+    inserted_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::inserted_leftToRight_RtL_BtT_data()
+{
+    inserted_defaultLayout_data();
+}
+
+void tst_QQuickGridView::inserted_topToBottom_LtR_BtT()
+{
+    inserted_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::LeftToRight, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::inserted_topToBottom_LtR_BtT_data()
+{
+    inserted_defaultLayout_data();
+}
+
+void tst_QQuickGridView::inserted_topToBottom_RtL_BtT()
+{
+    inserted_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::inserted_topToBottom_RtL_BtT_data()
+{
+    inserted_defaultLayout_data();
+}
+
+
+void tst_QQuickGridView::removed_leftToRight_RtL_TtB()
+{
+    removed_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft);
+}
+
+void tst_QQuickGridView::removed_leftToRight_RtL_TtB_data()
+{
+    removed_defaultLayout_data();
+}
+
+void tst_QQuickGridView::removed_topToBottom_LtR_TtB()
+{
+    removed_defaultLayout(QQuickGridView::FlowTopToBottom);
+}
+
+void tst_QQuickGridView::removed_topToBottom_LtR_TtB_data()
+{
+    removed_defaultLayout_data();
+}
+
+void tst_QQuickGridView::removed_topToBottom_RtL_TtB()
+{
+    removed_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft);
+}
+
+void tst_QQuickGridView::removed_topToBottom_RtL_TtB_data()
+{
+    removed_defaultLayout_data();
+}
+
+void tst_QQuickGridView::removed_leftToRight_LtR_BtT()
+{
+    removed_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::LeftToRight, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::removed_leftToRight_LtR_BtT_data()
+{
+    removed_defaultLayout_data();
+}
+
+void tst_QQuickGridView::removed_leftToRight_RtL_BtT()
+{
+    removed_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::removed_leftToRight_RtL_BtT_data()
+{
+    removed_defaultLayout_data();
+}
+
+void tst_QQuickGridView::removed_topToBottom_LtR_BtT()
+{
+    removed_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::LeftToRight, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::removed_topToBottom_LtR_BtT_data()
+{
+    removed_defaultLayout_data();
+}
+
+void tst_QQuickGridView::removed_topToBottom_RtL_BtT()
+{
+    removed_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::removed_topToBottom_RtL_BtT_data()
+{
+    removed_defaultLayout_data();
+}
+
+
+void tst_QQuickGridView::moved_leftToRight_RtL_TtB()
+{
+    moved_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft);
+}
+
+void tst_QQuickGridView::moved_leftToRight_RtL_TtB_data()
+{
+    moved_defaultLayout_data();
+}
+
+void tst_QQuickGridView::moved_topToBottom_LtR_TtB()
+{
+    moved_defaultLayout(QQuickGridView::FlowTopToBottom);
+}
+
+void tst_QQuickGridView::moved_topToBottom_LtR_TtB_data()
+{
+    moved_defaultLayout_data();
+}
+
+void tst_QQuickGridView::moved_topToBottom_RtL_TtB()
+{
+    moved_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft);
+}
+
+void tst_QQuickGridView::moved_topToBottom_RtL_TtB_data()
+{
+    moved_defaultLayout_data();
+}
+
+void tst_QQuickGridView::moved_leftToRight_LtR_BtT()
+{
+    moved_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::LeftToRight, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::moved_leftToRight_LtR_BtT_data()
+{
+    moved_defaultLayout_data();
+}
+
+void tst_QQuickGridView::moved_leftToRight_RtL_BtT()
+{
+    moved_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::moved_leftToRight_RtL_BtT_data()
+{
+    moved_defaultLayout_data();
+}
+
+void tst_QQuickGridView::moved_topToBottom_LtR_BtT()
+{
+    moved_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::LeftToRight, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::moved_topToBottom_LtR_BtT_data()
+{
+    moved_defaultLayout_data();
+}
+
+void tst_QQuickGridView::moved_topToBottom_RtL_BtT()
+{
+    moved_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft, QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickGridView::moved_topToBottom_RtL_BtT_data()
+{
+    moved_defaultLayout_data();
+}
+
+
 QList<int> tst_QQuickGridView::toIntList(const QVariantList &list)
 {
     QList<int> ret;
index bf70310..1cc4ae0 100644 (file)
@@ -15,7 +15,7 @@ Rectangle {
             height: 30
             width: 240
             Text {
-                text: index + " " + x + "," + y
+                text: index + " " + parent.x + "," + parent.y
             }
             color: ListView.isCurrentItem ? "lightsteelblue" : "white"
         }
index 4c3eeca..07a331a 100644 (file)
@@ -2,27 +2,24 @@ import QtQuick 2.0
 
 ListView {
     id: view
-    property bool horizontal: false
-    property bool rtl: false
+
     width: 240
     height: 320
 
     model: testModel
-    
-    orientation: horizontal ? ListView.Horizontal : ListView.Vertical
+
     header: Rectangle {
         objectName: "header"
-        width: horizontal ? 20 : view.width
-        height: horizontal ? view.height : 20
+        width: orientation == ListView.Horizontal ? 20 : view.width
+        height: orientation == ListView.Horizontal ? view.height : 20
         color: "red"
     }
     footer: Rectangle {
         objectName: "footer"
-        width: horizontal ? 30 : view.width
-        height: horizontal ? view.height : 30
+        width: orientation == ListView.Horizontal ? 30 : view.width
+        height: orientation == ListView.Horizontal ? view.height : 30
         color: "blue"
     }
 
     delegate: Text { width: 30; height: 30; text: index + "(" + x + ")" }
-    layoutDirection: rtl ? Qt.RightToLeft : Qt.LeftToRight
 }
index a3f9e93..3369cf7 100644 (file)
@@ -58,7 +58,9 @@
 #include <math.h>
 
 Q_DECLARE_METATYPE(Qt::LayoutDirection)
+Q_DECLARE_METATYPE(QQuickItemView::VerticalLayoutDirection)
 Q_DECLARE_METATYPE(QQuickListView::Orientation)
+Q_DECLARE_METATYPE(Qt::Key)
 
 using namespace QQuickViewTestUtil;
 using namespace QQuickVisualTestUtil;
@@ -89,6 +91,8 @@ private slots:
     void qAbstractItemModel_inserted();
     void qAbstractItemModel_inserted_more();
     void qAbstractItemModel_inserted_more_data();
+    void qAbstractItemModel_inserted_more_bottomToTop();
+    void qAbstractItemModel_inserted_more_bottomToTop_data();
 
     void qListModelInterface_removed();
     void qListModelInterface_removed_more();
@@ -97,6 +101,8 @@ private slots:
     void qAbstractItemModel_removed();
     void qAbstractItemModel_removed_more();
     void qAbstractItemModel_removed_more_data();
+    void qAbstractItemModel_removed_more_bottomToTop();
+    void qAbstractItemModel_removed_more_bottomToTop_data();
 
     void qListModelInterface_moved();
     void qListModelInterface_moved_data();
@@ -104,6 +110,8 @@ private slots:
     void qListModelInterface_package_moved_data();
     void qAbstractItemModel_moved();
     void qAbstractItemModel_moved_data();
+    void qAbstractItemModel_moved_bottomToTop();
+    void qAbstractItemModel_moved_bottomToTop_data();
 
     void multipleChanges_condensed() { multipleChanges(true); }
     void multipleChanges_condensed_data() { multipleChanges_data(); }
@@ -113,6 +121,7 @@ private slots:
     void qListModelInterface_clear();
     void qListModelInterface_package_clear();
     void qAbstractItemModel_clear();
+    void qAbstractItemModel_clear_bottomToTop();
 
     void insertBeforeVisible();
     void insertBeforeVisible_data();
@@ -122,6 +131,8 @@ private slots:
     void currentIndex_delayedItemCreation_data();
     void currentIndex();
     void noCurrentIndex();
+    void keyNavigation();
+    void keyNavigation_data();
     void enforceRange();
     void enforceRange_withoutHighlight();
     void spacing();
@@ -144,6 +155,8 @@ private slots:
     void footer();
     void footer_data();
     void headerFooter();
+    void headerFooter_data();
+    void resetModel_headerFooter();
     void resizeView();
     void resizeViewAndRepaint();
     void sizeLessThan1();
@@ -197,11 +210,11 @@ private:
     template <class T> void items(const QUrl &source, bool forceLayout);
     template <class T> void changed(const QUrl &source, bool forceLayout);
     template <class T> void inserted(const QUrl &source);
-    template <class T> void inserted_more();
+    template <class T> void inserted_more(QQuickItemView::VerticalLayoutDirection verticalLayoutDirection = QQuickItemView::TopToBottom);
     template <class T> void removed(const QUrl &source, bool animated);
-    template <class T> void removed_more(const QUrl &source);
-    template <class T> void moved(const QUrl &source);
-    template <class T> void clear(const QUrl &source);
+    template <class T> void removed_more(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection = QQuickItemView::TopToBottom);
+    template <class T> void moved(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection = QQuickItemView::TopToBottom);
+    template <class T> void clear(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection = QQuickItemView::TopToBottom);
     template <class T> void sections(const QUrl &source);
 
     void multipleChanges(bool condensed);
@@ -527,7 +540,7 @@ void tst_QQuickListView::inserted(const QUrl &source)
 }
 
 template <class T>
-void tst_QQuickListView::inserted_more()
+void tst_QQuickListView::inserted_more(QQuickItemView::VerticalLayoutDirection verticalLayoutDirection)
 {
     QFETCH(qreal, contentY);
     QFETCH(int, insertIndex);
@@ -555,8 +568,15 @@ void tst_QQuickListView::inserted_more()
     QQuickItem *contentItem = listview->contentItem();
     QTRY_VERIFY(contentItem != 0);
 
+    bool waitForPolish = (contentY != 0);
+    if (verticalLayoutDirection == QQuickItemView::BottomToTop) {
+        listview->setVerticalLayoutDirection(verticalLayoutDirection);
+        QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+        contentY = -listview->height() - contentY;
+    }
     listview->setContentY(contentY);
-    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+    if (waitForPolish)
+        QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
     QList<QPair<QString, QString> > newData;
     for (int i=0; i<insertCount; i++)
@@ -567,27 +587,32 @@ void tst_QQuickListView::inserted_more()
     // check visibleItems.first() is in correct position
     QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
     QVERIFY(item0);
-    QCOMPARE(item0->y(), itemsOffsetAfterMove);
+    if (verticalLayoutDirection == QQuickItemView::BottomToTop)
+        QCOMPARE(item0->y(), -item0->height() - itemsOffsetAfterMove);
+    else
+        QCOMPARE(item0->y(), itemsOffsetAfterMove);
 
     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
     int firstVisibleIndex = -1;
     for (int i=0; i<items.count(); i++) {
-        if (items[i]->y() >= contentY) {
-            QQmlExpression e(qmlContext(items[i]), items[i], "index");
-            firstVisibleIndex = e.evaluate().toInt();
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        if (item && item->isVisible()) {
+            firstVisibleIndex = i;
             break;
         }
     }
     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
 
     // Confirm items positioned correctly and indexes correct
-    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
     QQuickText *name;
     QQuickText *number;
-    for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
+    for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
-        QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
+        qreal pos = i*20.0 + itemsOffsetAfterMove;
+        if (verticalLayoutDirection == QQuickItemView::BottomToTop)
+            pos = -item0->height() - pos;
+        QTRY_COMPARE(item->y(), pos);
         name = findItem<QQuickText>(contentItem, "textName", i);
         QVERIFY(name != 0);
         QTRY_COMPARE(name->text(), model.name(i));
@@ -957,15 +982,13 @@ void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
 }
 
 template <class T>
-void tst_QQuickListView::removed_more(const QUrl &source)
+void tst_QQuickListView::removed_more(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection)
 {
     QFETCH(qreal, contentY);
     QFETCH(int, removeIndex);
     QFETCH(int, removeCount);
     QFETCH(qreal, itemsOffsetAfterMove);
 
-    QQuickText *name;
-    QQuickText *number;
     QQuickView *canvas = getView();
 
     T model;
@@ -988,13 +1011,15 @@ void tst_QQuickListView::removed_more(const QUrl &source)
     QQuickItem *contentItem = listview->contentItem();
     QTRY_VERIFY(contentItem != 0);
 
+    bool waitForPolish = (contentY != 0);
+    if (verticalLayoutDirection == QQuickItemView::BottomToTop) {
+        listview->setVerticalLayoutDirection(verticalLayoutDirection);
+        QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+        contentY = -listview->height() - contentY;
+    }
     listview->setContentY(contentY);
-    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
-
-    // wait for refill (after refill, items above the firstVisibleIndex-1 should not be rendered)
-    int firstVisibleIndex = contentY / 20;
-    if (firstVisibleIndex - 2 >= 0)
-        QTRY_VERIFY(!findItem<QQuickText>(contentItem, "textName", firstVisibleIndex - 2));
+    if (waitForPolish)
+        QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
     model.removeItems(removeIndex, removeCount);
     QTRY_COMPARE(listview->property("count").toInt(), model.count());
@@ -1002,24 +1027,33 @@ void tst_QQuickListView::removed_more(const QUrl &source)
     // check visibleItems.first() is in correct position
     QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
     QVERIFY(item0);
-    QCOMPARE(item0->y(), itemsOffsetAfterMove);
+    QVERIFY(item0);
+    if (verticalLayoutDirection == QQuickItemView::BottomToTop)
+        QCOMPARE(item0->y(), -item0->height() - itemsOffsetAfterMove);
+    else
+        QCOMPARE(item0->y(), itemsOffsetAfterMove);
 
     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+    int firstVisibleIndex = -1;
     for (int i=0; i<items.count(); i++) {
-        if (items[i]->y() >= contentY) {
-            QQmlExpression e(qmlContext(items[i]), items[i], "index");
-            firstVisibleIndex = e.evaluate().toInt();
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        if (item && item->isVisible()) {
+            firstVisibleIndex = i;
             break;
         }
     }
     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
 
     // Confirm items positioned correctly and indexes correct
-    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
+    QQuickText *name;
+    QQuickText *number;
+    for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
-        QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
+        qreal pos = i*20.0 + itemsOffsetAfterMove;
+        if (verticalLayoutDirection == QQuickItemView::BottomToTop)
+            pos = -item0->height() - pos;
+        QTRY_COMPARE(item->y(), pos);
         name = findItem<QQuickText>(contentItem, "textName", i);
         QVERIFY(name != 0);
         QTRY_COMPARE(name->text(), model.name(i));
@@ -1136,7 +1170,7 @@ void tst_QQuickListView::removed_more_data()
 }
 
 template <class T>
-void tst_QQuickListView::clear(const QUrl &source)
+void tst_QQuickListView::clear(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection)
 {
     QQuickView *canvas = createView();
 
@@ -1158,13 +1192,19 @@ void tst_QQuickListView::clear(const QUrl &source)
     QTRY_VERIFY(listview != 0);
     QQuickItem *contentItem = listview->contentItem();
     QTRY_VERIFY(contentItem != 0);
+
+    listview->setVerticalLayoutDirection(verticalLayoutDirection);
     QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
     model.clear();
 
+    QTRY_COMPARE(findItems<QQuickListView>(contentItem, "wrapper").count(), 0);
     QTRY_VERIFY(listview->count() == 0);
     QTRY_VERIFY(listview->currentItem() == 0);
-    QTRY_VERIFY(listview->contentY() == 0);
+    if (verticalLayoutDirection == QQuickItemView::TopToBottom)
+        QTRY_COMPARE(listview->contentY(), 0.0);
+    else
+        QTRY_COMPARE(listview->contentY(), -listview->height());
     QVERIFY(listview->currentIndex() == -1);
 
     // confirm sanity when adding an item to cleared list
@@ -1178,7 +1218,7 @@ void tst_QQuickListView::clear(const QUrl &source)
 }
 
 template <class T>
-void tst_QQuickListView::moved(const QUrl &source)
+void tst_QQuickListView::moved(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection)
 {
     QFETCH(qreal, contentY);
     QFETCH(int, from);
@@ -1209,15 +1249,19 @@ void tst_QQuickListView::moved(const QUrl &source)
     QTRY_VERIFY(listview != 0);
     QQuickItem *contentItem = listview->contentItem();
     QTRY_VERIFY(contentItem != 0);
-    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
-    QQuickItem *currentItem = listview->currentItem();
-    QTRY_VERIFY(currentItem != 0);
+    // always need to wait for view to be painted before the first move()
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
-    if (contentY != 0) {
-        listview->setContentY(contentY);
+    bool waitForPolish = (contentY != 0);
+    if (verticalLayoutDirection == QQuickItemView::BottomToTop) {
+        listview->setVerticalLayoutDirection(verticalLayoutDirection);
         QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+        contentY = -listview->height() - contentY;
     }
+    listview->setContentY(contentY);
+    if (waitForPolish)
+        QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
     model.moveItems(from, to, count);
     QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
@@ -1225,22 +1269,22 @@ void tst_QQuickListView::moved(const QUrl &source)
     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
     int firstVisibleIndex = -1;
     for (int i=0; i<items.count(); i++) {
-        if (items[i]->y() >= contentY) {
-            QQmlExpression e(qmlContext(items[i]), items[i], "index");
-            firstVisibleIndex = e.evaluate().toInt();
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        if (item && item->isVisible()) {
+            firstVisibleIndex = i;
             break;
         }
     }
     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
 
     // Confirm items positioned correctly and indexes correct
-    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
-        if (i >= firstVisibleIndex + 16)    // index has moved out of view
-            continue;
+    for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
-        QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
+        qreal pos = i*20.0 + itemsOffsetAfterMove;
+        if (verticalLayoutDirection == QQuickItemView::BottomToTop)
+            pos = -item->height() - pos;
+        QTRY_COMPARE(item->y(), pos);
         name = findItem<QQuickText>(contentItem, "textName", i);
         QVERIFY(name != 0);
         QTRY_COMPARE(name->text(), model.name(i));
@@ -1249,7 +1293,7 @@ void tst_QQuickListView::moved(const QUrl &source)
         QTRY_COMPARE(number->text(), model.number(i));
 
         // current index should have been updated
-        if (item == currentItem)
+        if (item == listview->currentItem())
             QTRY_COMPARE(listview->currentIndex(), i);
     }
 
@@ -2340,35 +2384,11 @@ void tst_QQuickListView::currentIndex()
     QCOMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 20));
     QCOMPARE(listview->highlightItem()->y(), listview->currentItem()->y());
 
-    // no wrap
     listview->setCurrentIndex(0);
     QCOMPARE(listview->currentIndex(), 0);
     // confirm that the velocity is updated
     QTRY_VERIFY(listview->verticalVelocity() != 0.0);
 
-    listview->incrementCurrentIndex();
-    QCOMPARE(listview->currentIndex(), 1);
-    listview->decrementCurrentIndex();
-    QCOMPARE(listview->currentIndex(), 0);
-
-    listview->decrementCurrentIndex();
-    QCOMPARE(listview->currentIndex(), 0);
-
-    // with wrap
-    ctxt->setContextProperty("testWrap", QVariant(true));
-    QVERIFY(listview->isWrapEnabled());
-
-    listview->decrementCurrentIndex();
-    QCOMPARE(listview->currentIndex(), model.count()-1);
-
-    QTRY_COMPARE(listview->contentY(), 280.0);
-
-    listview->incrementCurrentIndex();
-    QCOMPARE(listview->currentIndex(), 0);
-
-    QTRY_COMPARE(listview->contentY(), 0.0);
-
-
     // footer should become visible if it is out of view, and then current index is set to count-1
     canvas->rootObject()->setProperty("showFooter", true);
     QTRY_VERIFY(listview->footerItem());
@@ -2387,40 +2407,6 @@ void tst_QQuickListView::currentIndex()
     QTRY_COMPARE(listview->contentY(), -listview->headerItem()->height());
     canvas->rootObject()->setProperty("showHeader", false);
 
-
-    // Test keys
-    canvas->show();
-    canvas->requestActivateWindow();
-    QTest::qWaitForWindowShown(canvas);
-    QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
-
-    listview->setCurrentIndex(0);
-
-    QTest::keyClick(canvas, Qt::Key_Down);
-    QCOMPARE(listview->currentIndex(), 1);
-
-    QTest::keyClick(canvas, Qt::Key_Up);
-    QCOMPARE(listview->currentIndex(), 0);
-
-    // hold down Key_Down
-    for (int i=0; i<model.count()-1; i++) {
-        QTest::simulateEvent(canvas, true, Qt::Key_Down, Qt::NoModifier, "", true);
-        QTRY_COMPARE(listview->currentIndex(), i+1);
-    }
-    QTest::keyRelease(canvas, Qt::Key_Down);
-    QTRY_COMPARE(listview->currentIndex(), model.count()-1);
-    QTRY_COMPARE(listview->contentY(), 280.0);
-
-    // hold down Key_Up
-    for (int i=model.count()-1; i > 0; i--) {
-        QTest::simulateEvent(canvas, true, Qt::Key_Up, Qt::NoModifier, "", true);
-        QTRY_COMPARE(listview->currentIndex(), i-1);
-    }
-    QTest::keyRelease(canvas, Qt::Key_Up);
-    QTRY_COMPARE(listview->currentIndex(), 0);
-    QTRY_COMPARE(listview->contentY(), 0.0);
-
-
     // turn off auto highlight
     listview->setHighlightFollowsCurrentItem(false);
     QVERIFY(listview->highlightFollowsCurrentItem() == false);
@@ -2443,6 +2429,7 @@ void tst_QQuickListView::currentIndex()
     QVERIFY(!listview->highlightItem());
     QVERIFY(!listview->currentItem());
 
+    // moving currentItem out of view should make it invisible
     listview->setCurrentIndex(0);
     QTRY_VERIFY(listview->currentItem()->isVisible());
     listview->setContentY(200);
@@ -2489,6 +2476,129 @@ void tst_QQuickListView::noCurrentIndex()
     delete canvas;
 }
 
+void tst_QQuickListView::keyNavigation()
+{
+    QFETCH(QQuickListView::Orientation, orientation);
+    QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
+    QFETCH(Qt::Key, forwardsKey);
+    QFETCH(Qt::Key, backwardsKey);
+    QFETCH(QPointF, contentPosAtFirstItem);
+    QFETCH(QPointF, contentPosAtLastItem);
+
+    QmlListModel model;
+    for (int i = 0; i < 30; i++)
+        model.addItem("Item" + QString::number(i), "");
+
+    QQuickView *canvas = getView();
+    TestObject *testObject = new TestObject;
+    canvas->rootContext()->setContextProperty("testModel", &model);
+    canvas->rootContext()->setContextProperty("testObject", testObject);
+    canvas->setSource(testFileUrl("listviewtest.qml"));
+    canvas->show();
+    qApp->processEvents();
+
+    QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
+    QTRY_VERIFY(listview != 0);
+
+    listview->setOrientation(orientation);
+    listview->setLayoutDirection(layoutDirection);
+    listview->setVerticalLayoutDirection(verticalLayoutDirection);
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+
+    canvas->requestActivateWindow();
+    QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
+    QCOMPARE(listview->currentIndex(), 0);
+
+    QTest::keyClick(canvas, forwardsKey);
+    QCOMPARE(listview->currentIndex(), 1);
+
+    QTest::keyClick(canvas, backwardsKey);
+    QCOMPARE(listview->currentIndex(), 0);
+
+    // hold down a key to go forwards
+    for (int i=0; i<model.count()-1; i++) {
+        QTest::simulateEvent(canvas, true, forwardsKey, Qt::NoModifier, "", true);
+        QTRY_COMPARE(listview->currentIndex(), i+1);
+    }
+    QTest::keyRelease(canvas, forwardsKey);
+    QTRY_COMPARE(listview->currentIndex(), model.count()-1);
+    QTRY_COMPARE(listview->contentX(), contentPosAtLastItem.x());
+    QTRY_COMPARE(listview->contentY(), contentPosAtLastItem.y());
+
+    // hold down a key to go backwards
+    for (int i=model.count()-1; i > 0; i--) {
+        QTest::simulateEvent(canvas, true, backwardsKey, Qt::NoModifier, "", true);
+        QTRY_COMPARE(listview->currentIndex(), i-1);
+    }
+    QTest::keyRelease(canvas, backwardsKey);
+    QTRY_COMPARE(listview->currentIndex(), 0);
+    QTRY_COMPARE(listview->contentX(), contentPosAtFirstItem.x());
+    QTRY_COMPARE(listview->contentY(), contentPosAtFirstItem.y());
+
+    // no wrap
+    QVERIFY(!listview->isWrapEnabled());
+    listview->incrementCurrentIndex();
+    QCOMPARE(listview->currentIndex(), 1);
+    listview->decrementCurrentIndex();
+    QCOMPARE(listview->currentIndex(), 0);
+
+    listview->decrementCurrentIndex();
+    QCOMPARE(listview->currentIndex(), 0);
+
+    // with wrap
+    listview->setWrapEnabled(true);
+    QVERIFY(listview->isWrapEnabled());
+
+    listview->decrementCurrentIndex();
+    QCOMPARE(listview->currentIndex(), model.count()-1);
+    QTRY_COMPARE(listview->contentX(), contentPosAtLastItem.x());
+    QTRY_COMPARE(listview->contentY(), contentPosAtLastItem.y());
+
+    listview->incrementCurrentIndex();
+    QCOMPARE(listview->currentIndex(), 0);
+    QTRY_COMPARE(listview->contentX(), contentPosAtFirstItem.x());
+    QTRY_COMPARE(listview->contentY(), contentPosAtFirstItem.y());
+
+    releaseView(canvas);
+    delete testObject;
+}
+
+void tst_QQuickListView::keyNavigation_data()
+{
+    QTest::addColumn<QQuickListView::Orientation>("orientation");
+    QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
+    QTest::addColumn<Qt::Key>("forwardsKey");
+    QTest::addColumn<Qt::Key>("backwardsKey");
+    QTest::addColumn<QPointF>("contentPosAtFirstItem");
+    QTest::addColumn<QPointF>("contentPosAtLastItem");
+
+    QTest::newRow("Vertical, TopToBottom")
+            << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << Qt::Key_Down << Qt::Key_Up
+            << QPointF(0, 0)
+            << QPointF(0, 30*20 - 320);
+
+    QTest::newRow("Vertical, BottomToTop")
+            << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
+            << Qt::Key_Up << Qt::Key_Down
+            << QPointF(0, -320)
+            << QPointF(0, -(30 * 20));
+
+    QTest::newRow("Horizontal, LeftToRight")
+            << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << Qt::Key_Right << Qt::Key_Left
+            << QPointF(0, 0)
+            << QPointF(30*240 - 240, 0);
+
+    QTest::newRow("Horizontal, RightToLeft")
+            << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
+            << Qt::Key_Left << Qt::Key_Right
+            << QPointF(-240, 0)
+            << QPointF(-(30 * 240), 0);
+}
+
 void tst_QQuickListView::itemList()
 {
     QQuickView *canvas = createView();
@@ -3110,11 +3220,12 @@ void tst_QQuickListView::header()
 {
     QFETCH(QQuickListView::Orientation, orientation);
     QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
     QFETCH(QPointF, initialHeaderPos);
-    QFETCH(QPointF, firstDelegatePos);
-    QFETCH(QPointF, initialContentPos);
     QFETCH(QPointF, changedHeaderPos);
+    QFETCH(QPointF, initialContentPos);
     QFETCH(QPointF, changedContentPos);
+    QFETCH(QPointF, firstDelegatePos);
     QFETCH(QPointF, resizeContentPos);
 
     QmlListModel model;
@@ -3133,6 +3244,8 @@ void tst_QQuickListView::header()
     QTRY_VERIFY(listview != 0);
     listview->setOrientation(orientation);
     listview->setLayoutDirection(layoutDirection);
+    listview->setVerticalLayoutDirection(verticalLayoutDirection);
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
     QQuickItem *contentItem = listview->contentItem();
     QTRY_VERIFY(contentItem != 0);
@@ -3183,6 +3296,11 @@ void tst_QQuickListView::header()
     QVERIFY(item);
     QCOMPARE(item->pos(), firstDelegatePos);
 
+    listview->positionViewAtBeginning();
+    header->setHeight(10);
+    header->setWidth(40);
+    QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), resizeContentPos);
+
     releaseView(canvas);
 
 
@@ -3200,6 +3318,7 @@ void tst_QQuickListView::header()
     QTRY_VERIFY(listview != 0);
     listview->setOrientation(orientation);
     listview->setLayoutDirection(layoutDirection);
+    listview->setVerticalLayoutDirection(verticalLayoutDirection);
     QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
     listview->setWidth(240);
@@ -3214,6 +3333,7 @@ void tst_QQuickListView::header_data()
 {
     QTest::addColumn<QQuickListView::Orientation>("orientation");
     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
     QTest::addColumn<QPointF>("initialHeaderPos");
     QTest::addColumn<QPointF>("changedHeaderPos");
     QTest::addColumn<QPointF>("initialContentPos");
@@ -3223,11 +3343,11 @@ void tst_QQuickListView::header_data()
 
     // header1 = 100 x 30
     // header2 = 50 x 20
-    // delegates = 240 x 20
+    // delegates = 240 x 30
     // view width = 240
 
     // header above items, top left
-    QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight
+    QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
         << QPointF(0, -30)
         << QPointF(0, -20)
         << QPointF(0, -30)
@@ -3236,7 +3356,7 @@ void tst_QQuickListView::header_data()
         << QPointF(0, -10);
 
     // header above items, top right
-    QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft
+    QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft << QQuickItemView::TopToBottom
         << QPointF(0, -30)
         << QPointF(0, -20)
         << QPointF(0, -30)
@@ -3245,7 +3365,7 @@ void tst_QQuickListView::header_data()
         << QPointF(0, -10);
 
     // header to left of items
-    QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight
+    QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom
         << QPointF(-100, 0)
         << QPointF(-50, 0)
         << QPointF(-100, 0)
@@ -3254,13 +3374,22 @@ void tst_QQuickListView::header_data()
         << QPointF(-40, 0);
 
     // header to right of items
-    QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft
+    QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
         << QPointF(0, 0)
         << QPointF(0, 0)
         << QPointF(-240 + 100, 0)
         << QPointF(-240 + 50, 0)
         << QPointF(-240, 0)
         << QPointF(-240 + 40, 0);
+
+    // header below items
+    QTest::newRow("vertical, bottom to top") << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
+        << QPointF(0, 0)
+        << QPointF(0, 0)
+        << QPointF(0, -320 + 30)
+        << QPointF(0, -320 + 20)
+        << QPointF(0, -30)
+        << QPointF(0, -320 + 10);
 }
 
 void tst_QQuickListView::header_delayItemCreation()
@@ -3295,6 +3424,7 @@ void tst_QQuickListView::footer()
 {
     QFETCH(QQuickListView::Orientation, orientation);
     QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
     QFETCH(QPointF, initialFooterPos);
     QFETCH(QPointF, firstDelegatePos);
     QFETCH(QPointF, initialContentPos);
@@ -3319,6 +3449,7 @@ void tst_QQuickListView::footer()
     QTRY_VERIFY(listview != 0);
     listview->setOrientation(orientation);
     listview->setLayoutDirection(layoutDirection);
+    listview->setVerticalLayoutDirection(verticalLayoutDirection);
     QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
     QQuickItem *contentItem = listview->contentItem();
@@ -3347,7 +3478,8 @@ void tst_QQuickListView::footer()
     model.removeItem(1);
 
     if (orientation == QQuickListView::Vertical) {
-        QTRY_COMPARE(footer->y(), initialFooterPos.y() - 20);   // delegate height = 20
+        QTRY_COMPARE(footer->y(), verticalLayoutDirection == QQuickItemView::TopToBottom ?
+                initialFooterPos.y() - 20 : initialFooterPos.y() + 20);  // delegate width = 40
     } else {
         QTRY_COMPARE(footer->x(), layoutDirection == Qt::LeftToRight ?
                 initialFooterPos.x() - 40 : initialFooterPos.x() + 40);  // delegate width = 40
@@ -3359,6 +3491,8 @@ void tst_QQuickListView::footer()
     QPointF posWhenNoItems(0, 0);
     if (orientation == QQuickListView::Horizontal && layoutDirection == Qt::RightToLeft)
         posWhenNoItems.setX(-100);
+    else if (orientation == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop)
+        posWhenNoItems.setY(-30);
     QTRY_COMPARE(footer->pos(), posWhenNoItems);
 
     // if header is present, it's at a negative pos, so the footer should not move
@@ -3403,6 +3537,7 @@ void tst_QQuickListView::footer_data()
 {
     QTest::addColumn<QQuickListView::Orientation>("orientation");
     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
     QTest::addColumn<QPointF>("initialFooterPos");
     QTest::addColumn<QPointF>("changedFooterPos");
     QTest::addColumn<QPointF>("initialContentPos");
@@ -3417,7 +3552,7 @@ void tst_QQuickListView::footer_data()
     // view height = 320
 
     // footer below items, bottom left
-    QTest::newRow("vertical, layout left to right") << QQuickListView::Vertical << Qt::LeftToRight
+    QTest::newRow("vertical, layout left to right") << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
         << QPointF(0, 3 * 20)
         << QPointF(0, 30 * 20)  // added 30 items
         << QPointF(0, 0)
@@ -3426,7 +3561,7 @@ void tst_QQuickListView::footer_data()
         << QPointF(0, 30 * 20 - 320 + 10);
 
     // footer below items, bottom right
-    QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft
+    QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft << QQuickItemView::TopToBottom
         << QPointF(0, 3 * 20)
         << QPointF(0, 30 * 20)
         << QPointF(0, 0)
@@ -3435,7 +3570,7 @@ void tst_QQuickListView::footer_data()
         << QPointF(0, 30 * 20 - 320 + 10);
 
     // footer to right of items
-    QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight
+    QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom
         << QPointF(40 * 3, 0)
         << QPointF(40 * 30, 0)
         << QPointF(0, 0)
@@ -3444,13 +3579,22 @@ void tst_QQuickListView::footer_data()
         << QPointF(40 * 30 - 240 + 40, 0);
 
     // footer to left of items
-    QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft
+    QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
         << QPointF(-(40 * 3) - 100, 0)
         << QPointF(-(40 * 30) - 50, 0)     // 50 = new footer width
         << QPointF(-240, 0)
         << QPointF(-240, 0)
         << QPointF(-40, 0)
         << QPointF(-(40 * 30) - 40, 0);
+
+    // footer above items
+    QTest::newRow("vertical, layout left to right") << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
+        << QPointF(0, -(3 * 20) - 30)
+        << QPointF(0, -(30 * 20) - 20)
+        << QPointF(0, -320)
+        << QPointF(0, -320)
+        << QPointF(0, -20)
+        << QPointF(0, -(30 * 20) - 10);
 }
 
 class LVAccessor : public QQuickListView
@@ -3462,140 +3606,127 @@ public:
     qreal maxX() const { return maxXExtent(); }
 };
 
+
 void tst_QQuickListView::headerFooter()
 {
-    {
-        // Vertical
-        QQuickView *canvas = createView();
-
-        QmlListModel model;
-        QQmlContext *ctxt = canvas->rootContext();
-        ctxt->setContextProperty("testModel", &model);
-
-        canvas->setSource(testFileUrl("headerfooter.qml"));
-        qApp->processEvents();
-
-        QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
-        QTRY_VERIFY(listview != 0);
-
-        QQuickItem *contentItem = listview->contentItem();
-        QTRY_VERIFY(contentItem != 0);
-
-        QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
-        QVERIFY(header);
-        QCOMPARE(header->y(), -header->height());
-
-        QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
-        QVERIFY(footer);
-        QCOMPARE(footer->y(), 0.);
-
-        QCOMPARE(static_cast<LVAccessor*>(listview)->minY(), header->height());
-        QCOMPARE(static_cast<LVAccessor*>(listview)->maxY(), header->height());
-
-        delete canvas;
-    }
-    {
-        // Horizontal
-        QQuickView *canvas = createView();
-
-        QmlListModel model;
-        QQmlContext *ctxt = canvas->rootContext();
-        ctxt->setContextProperty("testModel", &model);
-
-        canvas->setSource(testFileUrl("headerfooter.qml"));
-        canvas->rootObject()->setProperty("horizontal", true);
-        qApp->processEvents();
-
-        QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
-        QTRY_VERIFY(listview != 0);
-
-        QQuickItem *contentItem = listview->contentItem();
-        QTRY_VERIFY(contentItem != 0);
-
-        QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
-        QVERIFY(header);
-        QCOMPARE(header->x(), -header->width());
+    QFETCH(QQuickListView::Orientation, orientation);
+    QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
+    QFETCH(QPointF, headerPos);
+    QFETCH(QPointF, footerPos);
+    QFETCH(QPointF, minPos);
+    QFETCH(QPointF, maxPos);
 
-        QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
-        QVERIFY(footer);
-        QCOMPARE(footer->x(), 0.);
+    QQuickView *canvas = getView();
 
-        QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), header->width());
-        QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), header->width());
+    QmlListModel model;
+    QQmlContext *ctxt = canvas->rootContext();
+    ctxt->setContextProperty("testModel", &model);
+    canvas->setSource(testFileUrl("headerfooter.qml"));
+    canvas->show();
+    qApp->processEvents();
 
-        delete canvas;
-    }
-    {
-        // Horizontal RTL
-        QQuickView *canvas = createView();
+    QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
+    QTRY_VERIFY(listview != 0);
+    listview->setOrientation(orientation);
+    listview->setLayoutDirection(layoutDirection);
+    listview->setVerticalLayoutDirection(verticalLayoutDirection);
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
-        QmlListModel model;
-        QQmlContext *ctxt = canvas->rootContext();
-        ctxt->setContextProperty("testModel", &model);
+    QQuickItem *contentItem = listview->contentItem();
+    QTRY_VERIFY(contentItem != 0);
 
-        canvas->setSource(testFileUrl("headerfooter.qml"));
-        canvas->rootObject()->setProperty("horizontal", true);
-        canvas->rootObject()->setProperty("rtl", true);
-        qApp->processEvents();
+    QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
+    QVERIFY(header);
+    QCOMPARE(header->pos(), headerPos);
 
-        QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
-        QTRY_VERIFY(listview != 0);
+    QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
+    QVERIFY(footer);
+    QCOMPARE(footer->pos(), footerPos);
 
-        QQuickItem *contentItem = listview->contentItem();
-        QTRY_VERIFY(contentItem != 0);
+    QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), minPos.x());
+    QCOMPARE(static_cast<LVAccessor*>(listview)->minY(), minPos.y());
+    QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), maxPos.x());
+    QCOMPARE(static_cast<LVAccessor*>(listview)->maxY(), maxPos.y());
 
-        QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
-        QVERIFY(header);
-        QCOMPARE(header->x(), 0.);
+    releaseView(canvas);
+}
 
-        QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
-        QVERIFY(footer);
-        QCOMPARE(footer->x(), -footer->width());
+void tst_QQuickListView::headerFooter_data()
+{
+    QTest::addColumn<QQuickListView::Orientation>("orientation");
+    QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
+    QTest::addColumn<QPointF>("headerPos");
+    QTest::addColumn<QPointF>("footerPos");
+    QTest::addColumn<QPointF>("minPos");
+    QTest::addColumn<QPointF>("maxPos");
+
+    // header is 240x20 (or 20x320 in Horizontal orientation)
+    // footer is 240x30 (or 30x320 in Horizontal orientation)
+
+    QTest::newRow("Vertical, TopToBottom")
+            << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << QPointF(0, -20) << QPointF(0, 0)
+            << QPointF(0, 20) << QPointF(240, 20);
+
+    QTest::newRow("Vertical, BottomToTop")
+            << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
+            << QPointF(0, 0) << QPointF(0, -30)
+            << QPointF(0, 320 - 20) << QPointF(240, 320 - 20);  // content flow is reversed
+
+
+    QTest::newRow("Horizontal, LeftToRight")
+            << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << QPointF(-20, 0) << QPointF(0, 0)
+            << QPointF(20, 0) << QPointF(20, 320);
+
+    QTest::newRow("Horizontal, RightToLeft")
+            << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
+            << QPointF(0, 0) << QPointF(-30, 0)
+            << QPointF(240 - 20, 0) << QPointF(240 - 20, 320);  // content flow is reversed
+}
 
-        QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), 240. - header->width());
-        QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), 240. - header->width());
+void tst_QQuickListView::resetModel_headerFooter()
+{
+    // Resetting a model shouldn't crash in views with header/footer
 
-        delete canvas;
-    }
-    {
-        // Reset model
-        QQuickView *canvas = createView();
+    QQuickView *canvas = createView();
 
-        QaimModel model;
-        for (int i = 0; i < 4; i++)
-            model.addItem("Item" + QString::number(i), "");
-        QQmlContext *ctxt = canvas->rootContext();
-        ctxt->setContextProperty("testModel", &model);
+    QaimModel model;
+    for (int i = 0; i < 4; i++)
+        model.addItem("Item" + QString::number(i), "");
+    QQmlContext *ctxt = canvas->rootContext();
+    ctxt->setContextProperty("testModel", &model);
 
-        canvas->setSource(testFileUrl("headerfooter.qml"));
-        qApp->processEvents();
+    canvas->setSource(testFileUrl("headerfooter.qml"));
+    qApp->processEvents();
 
-        QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
-        QTRY_VERIFY(listview != 0);
+    QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
+    QTRY_VERIFY(listview != 0);
 
-        QQuickItem *contentItem = listview->contentItem();
-        QTRY_VERIFY(contentItem != 0);
+    QQuickItem *contentItem = listview->contentItem();
+    QTRY_VERIFY(contentItem != 0);
 
-        QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
-        QVERIFY(header);
-        QCOMPARE(header->y(), -header->height());
+    QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
+    QVERIFY(header);
+    QCOMPARE(header->y(), -header->height());
 
-        QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
-        QVERIFY(footer);
-        QCOMPARE(footer->y(), 30.*4);
+    QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
+    QVERIFY(footer);
+    QCOMPARE(footer->y(), 30.*4);
 
-        model.reset();
+    model.reset();
 
-        header = findItem<QQuickItem>(contentItem, "header");
-        QVERIFY(header);
-        QCOMPARE(header->y(), -header->height());
+    header = findItem<QQuickItem>(contentItem, "header");
+    QVERIFY(header);
+    QCOMPARE(header->y(), -header->height());
 
-        footer = findItem<QQuickItem>(contentItem, "footer");
-        QVERIFY(footer);
-        QCOMPARE(footer->y(), 30.*4);
+    footer = findItem<QQuickItem>(contentItem, "footer");
+    QVERIFY(footer);
+    QCOMPARE(footer->y(), 30.*4);
 
-        delete canvas;
-    }
+    delete canvas;
 }
 
 void tst_QQuickListView::resizeView()
@@ -4315,13 +4446,14 @@ void tst_QQuickListView::marginsResize()
 {
     QFETCH(QQuickListView::Orientation, orientation);
     QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
     QFETCH(qreal, start);
     QFETCH(qreal, end);
 
     QPoint flickStart(20, 20);
     QPoint flickEnd(20, 20);
     if (orientation == QQuickListView::Vertical)
-        flickStart.ry() += 180;
+        flickStart.ry() += (verticalLayoutDirection == QQuickItemView::TopToBottom) ? 180 : -180;
     else
         flickStart.rx() += (layoutDirection == Qt::LeftToRight) ? 180 : -180;
 
@@ -4336,6 +4468,7 @@ void tst_QQuickListView::marginsResize()
 
     listview->setOrientation(orientation);
     listview->setLayoutDirection(layoutDirection);
+    listview->setVerticalLayoutDirection(verticalLayoutDirection);
     QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
     // view is resized after componentCompleted - top margin should still be visible
@@ -4381,20 +4514,34 @@ void tst_QQuickListView::marginsResize_data()
 {
     QTest::addColumn<QQuickListView::Orientation>("orientation");
     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickListView::VerticalLayoutDirection>("verticalLayoutDirection");
     QTest::addColumn<qreal>("start");
     QTest::addColumn<qreal>("end");
 
     // in Right to Left mode, leftMargin still means leftMargin - it doesn't reverse to mean rightMargin
 
-    QTest::newRow("vertical") << QQuickListView::Vertical << Qt::LeftToRight << -40.0 << 1020.0;
-    QTest::newRow("horizontal") << QQuickListView::Horizontal << Qt::LeftToRight << -40.0 << 1020.0;
-    QTest::newRow("horizontal, rtl") << QQuickListView::Horizontal << Qt::RightToLeft << -180.0 << -1240.0;
+    QTest::newRow("vertical")
+            << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << -40.0 << 1020.0;
+
+    QTest::newRow("vertical, BottomToTop")
+            << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
+            << -180.0 << -1240.0;
+
+    QTest::newRow("horizontal")
+            << QQuickListView::Horizontal<< Qt::LeftToRight << QQuickItemView::TopToBottom
+            << -40.0 << 1020.0;
+
+    QTest::newRow("horizontal, rtl")
+            << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
+            << -180.0 << -1240.0;
 }
 
 void tst_QQuickListView::snapToItem_data()
 {
     QTest::addColumn<QQuickListView::Orientation>("orientation");
     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
     QTest::addColumn<int>("highlightRangeMode");
     QTest::addColumn<QPoint>("flickStart");
     QTest::addColumn<QPoint>("flickEnd");
@@ -4402,22 +4549,36 @@ void tst_QQuickListView::snapToItem_data()
     QTest::addColumn<qreal>("endExtent");
     QTest::addColumn<qreal>("startExtent");
 
-    QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("vertical, top to bottom")
+        << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange)
         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 560.0 << 0.0;
 
-    QTest::newRow("horizontal, left to right") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("vertical, bottom to top")
+        << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop << int(QQuickItemView::NoHighlightRange)
+        << QPoint(20, 20) << QPoint(20, 200) << -60.0 << -560.0 - 240.0 << -240.0;
+
+    QTest::newRow("horizontal, left to right")
+        << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange)
         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 560.0 << 0.0;
 
-    QTest::newRow("horizontal, right to left") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("horizontal, right to left")
+        << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange)
         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -560.0 - 240.0 << -240.0;
 
-    QTest::newRow("vertical, left to right, enforce range") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("vertical, top to bottom, enforce range")
+        << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 700.0 << -20.0;
 
-    QTest::newRow("horizontal, left to right, enforce range") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("vertical, bottom to top, enforce range")
+        << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop << int(QQuickItemView::StrictlyEnforceRange)
+        << QPoint(20, 20) << QPoint(20, 200) << -60.0 << -560.0 - 240.0 - 140.0 << -220.0;
+
+    QTest::newRow("horizontal, left to right, enforce range")
+        << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 700.0 << -20.0;
 
-    QTest::newRow("horizontal, right to left, enforce range") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("horizontal, right to left, enforce range")
+        << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -560.0 - 240.0 - 140.0 << -220.0;
 }
 
@@ -4425,6 +4586,7 @@ void tst_QQuickListView::snapToItem()
 {
     QFETCH(QQuickListView::Orientation, orientation);
     QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
     QFETCH(int, highlightRangeMode);
     QFETCH(QPoint, flickStart);
     QFETCH(QPoint, flickEnd);
@@ -4443,6 +4605,7 @@ void tst_QQuickListView::snapToItem()
 
     listview->setOrientation(orientation);
     listview->setLayoutDirection(layoutDirection);
+    listview->setVerticalLayoutDirection(verticalLayoutDirection);
     listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
     QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
@@ -4462,7 +4625,7 @@ void tst_QQuickListView::snapToItem()
         flick(canvas, flickStart, flickEnd, 180);
         QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
     } while (orientation == QQuickListView::Vertical
-           ? !listview->isAtYEnd()
+           ? verticalLayoutDirection == QQuickItemView::TopToBottom ? !listview->isAtYEnd() : !listview->isAtYBeginning()
            : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning());
 
     if (orientation == QQuickListView::Vertical)
@@ -4475,7 +4638,7 @@ void tst_QQuickListView::snapToItem()
         flick(canvas, flickEnd, flickStart, 180);
         QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
     } while (orientation == QQuickListView::Vertical
-           ? !listview->isAtYBeginning()
+           ? verticalLayoutDirection == QQuickItemView::TopToBottom ? !listview->isAtYBeginning() : !listview->isAtYEnd()
            : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd());
 
     if (orientation == QQuickListView::Vertical)
@@ -4551,6 +4714,16 @@ void tst_QQuickListView::qAbstractItemModel_inserted_more_data()
     inserted_more_data();
 }
 
+void tst_QQuickListView::qAbstractItemModel_inserted_more_bottomToTop()
+{
+    inserted_more<QaimModel>(QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickListView::qAbstractItemModel_inserted_more_bottomToTop_data()
+{
+    inserted_more_data();
+}
+
 void tst_QQuickListView::qListModelInterface_removed()
 {
     removed<QmlListModel>(testFileUrl("listviewtest.qml"), false);
@@ -4589,6 +4762,16 @@ void tst_QQuickListView::qAbstractItemModel_removed_more_data()
     removed_more_data();
 }
 
+void tst_QQuickListView::qAbstractItemModel_removed_more_bottomToTop()
+{
+    removed_more<QaimModel>(testFileUrl("listviewtest.qml"), QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickListView::qAbstractItemModel_removed_more_bottomToTop_data()
+{
+    removed_more_data();
+}
+
 void tst_QQuickListView::qListModelInterface_moved()
 {
     moved<QmlListModel>(testFileUrl("listviewtest.qml"));
@@ -4619,6 +4802,16 @@ void tst_QQuickListView::qAbstractItemModel_moved_data()
     moved_data();
 }
 
+void tst_QQuickListView::qAbstractItemModel_moved_bottomToTop()
+{
+    moved<QaimModel>(testFileUrl("listviewtest-package.qml"), QQuickItemView::BottomToTop);
+}
+
+void tst_QQuickListView::qAbstractItemModel_moved_bottomToTop_data()
+{
+    moved_data();
+}
+
 void tst_QQuickListView::qListModelInterface_clear()
 {
     clear<QmlListModel>(testFileUrl("listviewtest.qml"));
@@ -4634,6 +4827,11 @@ void tst_QQuickListView::qAbstractItemModel_clear()
     clear<QaimModel>(testFileUrl("listviewtest.qml"));
 }
 
+void tst_QQuickListView::qAbstractItemModel_clear_bottomToTop()
+{
+    clear<QaimModel>(testFileUrl("listviewtest.qml"), QQuickItemView::BottomToTop);
+}
+
 void tst_QQuickListView::qListModelInterface_sections()
 {
     sections<QmlListModel>(testFileUrl("listview-sections.qml"));
@@ -4732,6 +4930,7 @@ void tst_QQuickListView::snapOneItem_data()
 {
     QTest::addColumn<QQuickListView::Orientation>("orientation");
     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
     QTest::addColumn<int>("highlightRangeMode");
     QTest::addColumn<QPoint>("flickStart");
     QTest::addColumn<QPoint>("flickEnd");
@@ -4739,22 +4938,36 @@ void tst_QQuickListView::snapOneItem_data()
     QTest::addColumn<qreal>("endExtent");
     QTest::addColumn<qreal>("startExtent");
 
-    QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("vertical, top to bottom")
+        << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange)
         << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 560.0 << 0.0;
 
-    QTest::newRow("horizontal, left to right") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("vertical, bottom to top")
+        << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop << int(QQuickItemView::NoHighlightRange)
+        << QPoint(20, 20) << QPoint(20, 200) << -420.0 << -560.0 - 240.0 << -240.0;
+
+    QTest::newRow("horizontal, left to right")
+        << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange)
         << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 560.0 << 0.0;
 
-    QTest::newRow("horizontal, right to left") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
+    QTest::newRow("horizontal, right to left")
+        << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange)
         << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -560.0 - 240.0 << -240.0;
 
-    QTest::newRow("vertical, left to right, enforce range") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("vertical, top to bottom, enforce range")
+        << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 580.0 << -20.0;
 
-    QTest::newRow("horizontal, left to right, enforce range") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("vertical, bottom to top, enforce range")
+        << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop << int(QQuickItemView::StrictlyEnforceRange)
+        << QPoint(20, 20) << QPoint(20, 200) << -420.0 << -580.0 - 240.0 << -220.0;
+
+    QTest::newRow("horizontal, left to right, enforce range")
+        << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 580.0 << -20.0;
 
-    QTest::newRow("horizontal, right to left, enforce range") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
+    QTest::newRow("horizontal, right to left, enforce range")
+        << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange)
         << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -580.0 - 240.0 << -220.0;
 }
 
@@ -4762,6 +4975,7 @@ void tst_QQuickListView::snapOneItem()
 {
     QFETCH(QQuickListView::Orientation, orientation);
     QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
     QFETCH(int, highlightRangeMode);
     QFETCH(QPoint, flickStart);
     QFETCH(QPoint, flickEnd);
@@ -4785,6 +4999,7 @@ void tst_QQuickListView::snapOneItem()
 
     listview->setOrientation(orientation);
     listview->setLayoutDirection(layoutDirection);
+    listview->setVerticalLayoutDirection(verticalLayoutDirection);
     listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
     QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
@@ -4811,7 +5026,7 @@ void tst_QQuickListView::snapOneItem()
         flick(canvas, flickStart, flickEnd, 180);
         QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
     } while (orientation == QQuickListView::Vertical
-           ? !listview->isAtYEnd()
+           ? verticalLayoutDirection == QQuickItemView::TopToBottom ? !listview->isAtYEnd() : !listview->isAtYBeginning()
            : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning());
 
     if (orientation == QQuickListView::Vertical)
@@ -4829,7 +5044,7 @@ void tst_QQuickListView::snapOneItem()
         flick(canvas, flickEnd, flickStart, 180);
         QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
     } while (orientation == QQuickListView::Vertical
-           ? !listview->isAtYBeginning()
+           ? verticalLayoutDirection == QQuickItemView::TopToBottom ? !listview->isAtYBeginning() : !listview->isAtYEnd()
            : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd());
 
     if (orientation == QQuickListView::Vertical)
@@ -6304,3 +6519,4 @@ QTEST_MAIN(tst_QQuickListView)
 
 #include "tst_qquicklistview.moc"
 
+