From 9bd6361400a2a4e4045a090de73d70082cc6d1bf Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Thu, 13 Oct 2011 17:41:51 +1000 Subject: [PATCH] Cannot flick to the end of a horizontal ListView with LayoutMirroring Forward port of fix for QTBUG-21756. minXExtent calculated the offset due to highlight range incorrectly (reversed) when mirroring enabled. Also us same algorithm for fixup() in GridView and ListView uses. For QtQuick 2, this change also reverses the beginning and end highlight range, as it should be, i.e. the preferredHighlightBegin is from the right side in RightToLeft mode. Also added snapping tests. Task-number: QTBUG-21756 Change-Id: Ica0ba4ab5db0ce9c77f2da75e9f8293550bd37d1 Reviewed-by: Martin Jones --- doc/src/declarative/whatsnew.qdoc | 5 +- src/declarative/items/qsggridview.cpp | 77 +++++--------- src/declarative/items/qsgitemview.cpp | 42 ++----- src/declarative/items/qsglistview.cpp | 70 ++++-------- .../graphicsitems/qdeclarativegridview.cpp | 23 ++--- .../graphicsitems/qdeclarativelistview.cpp | 2 +- .../declarative/qsggridview/data/snapToRow.qml | 49 ++++++++ .../declarative/qsggridview/tst_qsggridview.cpp | 116 +++++++++++++++++++ .../declarative/qsglistview/data/snapToItem.qml | 49 ++++++++ .../declarative/qsglistview/tst_qsglistview.cpp | 117 ++++++++++++++++++++ 10 files changed, 406 insertions(+), 144 deletions(-) create mode 100644 tests/auto/declarative/qsggridview/data/snapToRow.qml create mode 100644 tests/auto/declarative/qsglistview/data/snapToItem.qml diff --git a/doc/src/declarative/whatsnew.qdoc b/doc/src/declarative/whatsnew.qdoc index 84245a5..4c44166 100644 --- a/doc/src/declarative/whatsnew.qdoc +++ b/doc/src/declarative/whatsnew.qdoc @@ -117,8 +117,9 @@ Text improvements: PathView now has a \c currentItem property -ListView and GridView now have headerItem and footerItem properties (the instantiated -header and footer items). +ListView and GridView: + - now have headerItem and footerItem properties (the instantiated header and footer items). + - In RightToLeft layout the preferredHighlightBegin/End are now also reversed. ListView section.labelPositioning property added to allow keeping the current section label at the start and/or next section label at the end of the view. diff --git a/src/declarative/items/qsggridview.cpp b/src/declarative/items/qsggridview.cpp index fd2742c..6b01583 100644 --- a/src/declarative/items/qsggridview.cpp +++ b/src/declarative/items/qsggridview.cpp @@ -334,7 +334,10 @@ qreal QSGGridViewPrivate::snapPosAt(qreal pos) const Q_Q(const QSGGridView); qreal snapPos = 0; if (!visibleItems.isEmpty()) { - qreal highlightStart = isRightToLeftTopToBottom() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart; + qreal highlightStart = highlightRangeStart; + if (isRightToLeftTopToBottom()) + highlightStart = highlightRangeEndValid ? -size() + highlightRangeEnd : -size(); + pos += highlightStart; pos += rowSize()/2; snapPos = static_cast(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize(); @@ -807,19 +810,7 @@ void QSGGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) fixupMode = moveReason == Mouse ? fixupMode : Immediate; - qreal highlightStart; - qreal highlightEnd; - qreal viewPos; - if (isRightToLeftTopToBottom()) { - // Handle Right-To-Left exceptions - viewPos = -position()-size(); - highlightStart = highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart; - highlightEnd = highlightRangeEndValid ? size()-highlightRangeStart : highlightRangeEnd; - } else { - viewPos = position(); - highlightStart = highlightRangeStart; - highlightEnd = highlightRangeEnd; - } + qreal viewPos = isRightToLeftTopToBottom() ? -position()-size() : position(); bool strictHighlightRange = haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange; if (snapMode != QSGGridView::NoSnap) { @@ -836,40 +827,35 @@ void QSGGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) bias = -bias; tempPosition -= bias; } - FxViewItem *topItem = snapItemAt(tempPosition+highlightStart); + FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart); if (!topItem && strictHighlightRange && currentItem) { // StrictlyEnforceRange always keeps an item in range updateHighlight(); topItem = currentItem; } - FxViewItem *bottomItem = snapItemAt(tempPosition+highlightEnd); + FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd); if (!bottomItem && strictHighlightRange && currentItem) { // StrictlyEnforceRange always keeps an item in range updateHighlight(); bottomItem = currentItem; } qreal pos; - if (topItem && bottomItem && strictHighlightRange) { - qreal topPos = qMin(topItem->position() - highlightStart, -maxExtent); - qreal bottomPos = qMax(bottomItem->position() - highlightEnd, -minExtent); - pos = qAbs(data.move + topPos) < qAbs(data.move + bottomPos) ? topPos : bottomPos; - } else if (topItem) { - qreal headerPos = 0; - if (header) - headerPos = isRightToLeftTopToBottom() ? static_cast(header)->rowPos() + cellWidth - headerSize() : static_cast(header)->rowPos(); - if (topItem->index == 0 && header && tempPosition+highlightStart < headerPos+headerSize()/2 && !strictHighlightRange) { - pos = isRightToLeftTopToBottom() ? - headerPos + highlightStart - size() : headerPos - highlightStart; + bool isInBounds = -position() > maxExtent && -position() <= minExtent; + if (topItem && (isInBounds || strictHighlightRange)) { + qreal headerPos = header ? static_cast(header)->rowPos() : 0; + if (topItem->index == 0 && header && tempPosition+highlightRangeStart < headerPos+headerSize()/2 && !strictHighlightRange) { + pos = isRightToLeftTopToBottom() ? - headerPos + highlightRangeStart - size() : headerPos - highlightRangeStart; } else { if (isRightToLeftTopToBottom()) - pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent); + pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent); else - pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent); + pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent); } - } else if (bottomItem) { + } else if (bottomItem && isInBounds) { if (isRightToLeftTopToBottom()) - pos = qMax(qMin(-bottomItem->position() + highlightEnd - size(), -maxExtent), -minExtent); + pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent); else - pos = qMax(qMin(bottomItem->position() - highlightEnd, -maxExtent), -minExtent); + pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent); } else { QSGItemViewPrivate::fixup(data, minExtent, maxExtent); return; @@ -890,10 +876,10 @@ void QSGGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) if (currentItem) { updateHighlight(); qreal pos = static_cast(currentItem)->rowPos(); - if (viewPos < pos + rowSize() - highlightEnd) - viewPos = pos + rowSize() - highlightEnd; - if (viewPos > pos - highlightStart) - viewPos = pos - highlightStart; + if (viewPos < pos + rowSize() - highlightRangeEnd) + viewPos = pos + rowSize() - highlightRangeEnd; + if (viewPos > pos - highlightRangeStart) + viewPos = pos - highlightRangeStart; if (isRightToLeftTopToBottom()) viewPos = -viewPos-size(); timeline.reset(data.move); @@ -1543,22 +1529,11 @@ void QSGGridView::viewportMoved() if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { // reposition highlight qreal pos = d->highlight->position(); - qreal viewPos; - qreal highlightStart; - qreal highlightEnd; - if (d->isRightToLeftTopToBottom()) { - viewPos = -d->position()-d->size(); - highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; - highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; - } else { - viewPos = d->position(); - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - } - if (pos > viewPos + highlightEnd - d->highlight->size()) - pos = viewPos + highlightEnd - d->highlight->size(); - if (pos < viewPos + highlightStart) - pos = viewPos + highlightStart; + qreal viewPos = d->isRightToLeftTopToBottom() ? -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) + pos = viewPos + d->highlightRangeStart; if (pos != d->highlight->position()) { d->highlightXAnimator->stop(); diff --git a/src/declarative/items/qsgitemview.cpp b/src/declarative/items/qsgitemview.cpp index 075024d..5b95097 100644 --- a/src/declarative/items/qsgitemview.cpp +++ b/src/declarative/items/qsgitemview.cpp @@ -821,30 +821,14 @@ void QSGItemView::trackedPositionChanged() if (d->trackedItem != d->currentItem) { trackedSize += d->currentItem->sectionSize(); } - qreal viewPos; - qreal highlightStart; - qreal highlightEnd; - if (d->isContentFlowReversed()) { - viewPos = -d->position()-d->size(); - highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; - highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; - } else { - viewPos = d->position(); - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - } + qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position(); qreal pos = viewPos; if (d->haveHighlightRange) { - if (d->highlightRange == StrictlyEnforceRange) { - if (trackedPos > pos + highlightEnd - d->trackedItem->size()) - pos = trackedPos - highlightEnd + d->trackedItem->size(); - if (trackedPos < pos + highlightStart) - pos = trackedPos - highlightStart; - } else { - if (trackedPos > pos + highlightEnd - trackedSize) - pos = trackedPos - highlightEnd + trackedSize; - if (trackedPos < pos + highlightStart) - pos = trackedPos - highlightStart; + if (trackedPos > pos + d->highlightRangeEnd - trackedSize) + pos = trackedPos - d->highlightRangeEnd + trackedSize; + if (trackedPos < pos + d->highlightRangeStart) + pos = trackedPos - d->highlightRangeStart; + if (d->highlightRange != StrictlyEnforceRange) { if (pos > d->endPosition() - d->size()) pos = d->endPosition() - d->size(); if (pos < d->startPosition()) @@ -967,10 +951,8 @@ qreal QSGItemView::minXExtent() const endPositionFirstItem = d->positionAt(d->model->count()-1); else if (d->header) d->minExtent += d->headerSize(); - highlightStart = d->highlightRangeStartValid - ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem) - : d->size() - (d->lastPosition()-endPositionFirstItem); - highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size(); + 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(); @@ -986,7 +968,9 @@ qreal QSGItemView::minXExtent() const } if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { d->minExtent += highlightStart; - d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd)); + d->minExtent = d->isContentFlowReversed() + ? qMin(d->minExtent, endPositionFirstItem + highlightEnd) + : qMax(d->minExtent, -(endPositionFirstItem - highlightEnd)); } d->hData.minExtentDirty = false; } @@ -1006,8 +990,8 @@ qreal QSGItemView::maxXExtent() const qreal lastItemPosition = 0; d->maxExtent = 0; if (d->isContentFlowReversed()) { - highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size(); - highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size(); + 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; diff --git a/src/declarative/items/qsglistview.cpp b/src/declarative/items/qsglistview.cpp index 75472c2..5ea9efe 100644 --- a/src/declarative/items/qsglistview.cpp +++ b/src/declarative/items/qsglistview.cpp @@ -1251,19 +1251,7 @@ void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) fixupMode = moveReason == Mouse ? fixupMode : Immediate; bool strictHighlightRange = haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange; - qreal highlightStart; - qreal highlightEnd; - qreal viewPos; - if (isRightToLeft()) { - // Handle Right-To-Left exceptions - viewPos = -position()-size(); - highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart; - highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd; - } else { - viewPos = position(); - highlightStart = highlightRangeStart; - highlightEnd = highlightRangeEnd; - } + qreal viewPos = isRightToLeft() ? -position()-size() : position(); if (snapMode != QSGListView::NoSnap && moveReason != QSGListViewPrivate::SetIndex) { qreal tempPosition = isRightToLeft() ? -position()-size() : position(); @@ -1279,13 +1267,13 @@ void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) bias = -bias; tempPosition -= bias; } - FxViewItem *topItem = snapItemAt(tempPosition+highlightStart); + FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart); if (!topItem && strictHighlightRange && currentItem) { // StrictlyEnforceRange always keeps an item in range updateHighlight(); topItem = currentItem; } - FxViewItem *bottomItem = snapItemAt(tempPosition+highlightEnd); + FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd); if (!bottomItem && strictHighlightRange && currentItem) { // StrictlyEnforceRange always keeps an item in range updateHighlight(); @@ -1294,19 +1282,19 @@ void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) qreal pos; bool isInBounds = -position() > maxExtent && -position() <= minExtent; if (topItem && (isInBounds || strictHighlightRange)) { - if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2 && !strictHighlightRange) { - pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart; + if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) { + pos = isRightToLeft() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart; } else { if (isRightToLeft()) - pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent); + pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent); else - pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent); + pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent); } } else if (bottomItem && isInBounds) { if (isRightToLeft()) - pos = qMax(qMin(-bottomItem->position() + highlightEnd - size(), -maxExtent), -minExtent); + pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent); else - pos = qMax(qMin(bottomItem->position() - highlightEnd, -maxExtent), -minExtent); + pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent); } else { QSGItemViewPrivate::fixup(data, minExtent, maxExtent); return; @@ -1326,10 +1314,10 @@ void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) } else if (currentItem && strictHighlightRange && moveReason != QSGListViewPrivate::SetIndex) { updateHighlight(); qreal pos = static_cast(currentItem)->itemPosition(); - if (viewPos < pos + static_cast(currentItem)->itemSize() - highlightEnd) - viewPos = pos + static_cast(currentItem)->itemSize() - highlightEnd; - if (viewPos > pos - highlightStart) - viewPos = pos - highlightStart; + if (viewPos < pos + static_cast(currentItem)->itemSize() - highlightRangeEnd) + viewPos = pos + static_cast(currentItem)->itemSize() - highlightRangeEnd; + if (viewPos > pos - highlightRangeStart) + viewPos = pos - highlightRangeStart; if (isRightToLeft()) viewPos = -viewPos-size(); @@ -1364,7 +1352,7 @@ void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, } qreal maxDistance = 0; qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value(); - qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart; + // -ve velocity means list is moving up/left if (velocity > 0) { if (data.move.value() < minExtent) { @@ -1374,7 +1362,7 @@ void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal bias = dist < averageSize/2 ? averageSize/2 : 0; if (isRightToLeft()) bias = -bias; - data.flickTarget = -snapPosAt(-(dataValue - highlightStart) - bias) + highlightStart; + data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart; maxDistance = qAbs(data.flickTarget - data.move.value()); velocity = maxVelocity; } else { @@ -1391,7 +1379,7 @@ void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal bias = -dist < averageSize/2 ? averageSize/2 : 0; if (isRightToLeft()) bias = -bias; - data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + bias) + highlightStart; + data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart; maxDistance = qAbs(data.flickTarget - data.move.value()); velocity = -maxVelocity; } else { @@ -1428,7 +1416,7 @@ void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGListView::SnapOneItem) { if (snapMode != QSGListView::SnapOneItem) { qreal distTemp = isRightToLeft() ? -dist : dist; - data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart; + data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart; } data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget; if (overShoot) { @@ -1484,7 +1472,7 @@ void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal newtarget = data.flickTarget; if (snapMode != QSGListView::NoSnap || highlightRange == QSGListView::StrictlyEnforceRange) { qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget; - newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart; + newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart; newtarget = isRightToLeft() ? -newtarget+size() : newtarget; } if (velocity < 0 && newtarget <= maxExtent) @@ -2167,23 +2155,11 @@ void QSGListView::viewportMoved() if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { // reposition highlight qreal pos = d->highlight->position(); - qreal viewPos; - qreal highlightStart; - qreal highlightEnd; - if (d->isRightToLeft()) { - // Handle Right-To-Left exceptions - viewPos = -d->position()-d->size(); - highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; - highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; - } else { - viewPos = d->position(); - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - } - if (pos > viewPos + highlightEnd - d->highlight->size()) - pos = viewPos + highlightEnd - d->highlight->size(); - if (pos < viewPos + highlightStart) - pos = viewPos + highlightStart; + qreal viewPos = d->isRightToLeft() ? -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) + pos = viewPos + d->highlightRangeStart; if (pos != d->highlight->position()) { d->highlightPosAnimator->stop(); static_cast(d->highlight)->setPosition(pos); diff --git a/src/qtquick1/graphicsitems/qdeclarativegridview.cpp b/src/qtquick1/graphicsitems/qdeclarativegridview.cpp index feabbf0..2ed1ca1 100644 --- a/src/qtquick1/graphicsitems/qdeclarativegridview.cpp +++ b/src/qtquick1/graphicsitems/qdeclarativegridview.cpp @@ -1095,23 +1095,17 @@ void QDeclarative1GridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal bottomItem = currentItem; } qreal pos; - if (topItem && bottomItem && strictHighlightRange) { - qreal topPos = qMin(topItem->rowPos() - highlightStart, -maxExtent); - qreal bottomPos = qMax(bottomItem->rowPos() - highlightEnd, -minExtent); - pos = qAbs(data.move + topPos) < qAbs(data.move + bottomPos) ? topPos : bottomPos; - } else if (topItem) { - qreal headerPos = 0; - if (header) - headerPos = isRightToLeftTopToBottom() ? header->rowPos() + cellWidth - headerSize() : header->rowPos(); - if (topItem->index == 0 && header && tempPosition+highlightStart < headerPos+headerSize()/2 && !strictHighlightRange) { - pos = isRightToLeftTopToBottom() ? - headerPos + highlightStart - size() : headerPos - highlightStart; + bool isInBounds = -position() > maxExtent && -position() <= minExtent; + if (topItem && (isInBounds || strictHighlightRange)) { + if (topItem->index == 0 && header && tempPosition+highlightStart < header->rowPos()+headerSize()/2 && !strictHighlightRange) { + pos = isRightToLeftTopToBottom() ? - header->rowPos() + highlightStart - size() : header->rowPos() - highlightStart; } else { if (isRightToLeftTopToBottom()) pos = qMax(qMin(-topItem->rowPos() + highlightStart - size(), -maxExtent), -minExtent); else pos = qMax(qMin(topItem->rowPos() - highlightStart, -maxExtent), -minExtent); } - } else if (bottomItem) { + } else if (bottomItem && isInBounds) { if (isRightToLeftTopToBottom()) pos = qMax(qMin(-bottomItem->rowPos() + highlightEnd - size(), -maxExtent), -minExtent); else @@ -2267,9 +2261,10 @@ qreal QDeclarative1GridView::minXExtent() const qreal extent = -d->startPosition(); qreal highlightStart; qreal highlightEnd; - qreal endPositionFirstItem; + qreal endPositionFirstItem = 0; if (d->isRightToLeftTopToBottom()) { - endPositionFirstItem = d->rowPosAt(d->model->count()-1); + if (d->model && d->model->count()) + endPositionFirstItem = d->rowPosAt(d->model->count()-1); highlightStart = d->highlightRangeStartValid ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem) : d->size() - (d->lastPosition()-endPositionFirstItem); @@ -2284,7 +2279,7 @@ qreal QDeclarative1GridView::minXExtent() const extent += d->header->item->width(); } if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - extent += highlightStart; + extent += d->isRightToLeftTopToBottom() ? -highlightStart : highlightStart; extent = qMax(extent, -(endPositionFirstItem - highlightEnd)); } return extent; diff --git a/src/qtquick1/graphicsitems/qdeclarativelistview.cpp b/src/qtquick1/graphicsitems/qdeclarativelistview.cpp index 7b9cb12..6430f6a 100644 --- a/src/qtquick1/graphicsitems/qdeclarativelistview.cpp +++ b/src/qtquick1/graphicsitems/qdeclarativelistview.cpp @@ -2773,7 +2773,7 @@ qreal QDeclarative1ListView::minXExtent() const d->minExtent += d->header->size(); } if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->minExtent += highlightStart; + d->minExtent += d->isRightToLeft() ? -highlightStart : highlightStart; d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1)); } d->minExtentDirty = false; diff --git a/tests/auto/declarative/qsggridview/data/snapToRow.qml b/tests/auto/declarative/qsggridview/data/snapToRow.qml new file mode 100644 index 0000000..f079a04 --- /dev/null +++ b/tests/auto/declarative/qsggridview/data/snapToRow.qml @@ -0,0 +1,49 @@ +import QtQuick 2.0 + +Rectangle { + id: root + width: 240 + height: 240 + color: "#ffffff" + + Component { + id: myDelegate + Rectangle { + id: wrapper + objectName: "wrapper" + height: 80 + width: 80 + Column { + Text { + text: index + } + Text { + text: wrapper.x + ", " + wrapper.y + } + } + color: GridView.isCurrentItem ? "lightsteelblue" : "transparent" + } + } + GridView { + id: grid + objectName: "grid" + anchors.fill: parent + cellWidth: 80 + cellHeight: 80 + preferredHighlightBegin: 20 + preferredHighlightEnd: 100 + snapMode: GridView.SnapToRow + layoutDirection: Qt.RightToLeft + flow: GridView.TopToBottom + highlightRangeMode: GridView.StrictlyEnforceRange + highlight: Rectangle { width: 80; height: 80; color: "yellow" } + model: 54 + delegate: myDelegate + } + + Text { + anchors.right: parent.right + anchors.bottom: parent.bottom + text: grid.contentX + ", " + grid.contentY + } +} diff --git a/tests/auto/declarative/qsggridview/tst_qsggridview.cpp b/tests/auto/declarative/qsggridview/tst_qsggridview.cpp index 03975e2..b6a601d 100644 --- a/tests/auto/declarative/qsggridview/tst_qsggridview.cpp +++ b/tests/auto/declarative/qsggridview/tst_qsggridview.cpp @@ -110,9 +110,12 @@ private slots: void columnCount(); void margins(); void creationContext(); + void snapToRow_data(); + void snapToRow(); private: QSGView *createView(); + void flick(QSGView *canvas, const QPoint &from, const QPoint &to, int duration); template T *findItem(QSGItem *parent, const QString &id, int index=-1); template @@ -3121,6 +3124,101 @@ void tst_QSGGridView::creationContext() QCOMPARE(item->property("text").toString(), QString("Hello!")); } +void tst_QSGGridView::snapToRow_data() +{ + QTest::addColumn("flow"); + QTest::addColumn("layoutDirection"); + QTest::addColumn("highlightRangeMode"); + QTest::addColumn("flickStart"); + QTest::addColumn("flickEnd"); + QTest::addColumn("snapAlignment"); + QTest::addColumn("endExtent"); + QTest::addColumn("startExtent"); + + QTest::newRow("vertical, left to right") << QSGGridView::LeftToRight << Qt::LeftToRight << int(QSGItemView::NoHighlightRange) + << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0; + + QTest::newRow("horizontal, left to right") << QSGGridView::TopToBottom << Qt::LeftToRight << int(QSGItemView::NoHighlightRange) + << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0; + + QTest::newRow("horizontal, right to left") << QSGGridView::TopToBottom << Qt::RightToLeft << int(QSGItemView::NoHighlightRange) + << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 << -240.0; + + QTest::newRow("vertical, left to right, enforce range") << QSGGridView::LeftToRight << Qt::LeftToRight << int(QSGItemView::StrictlyEnforceRange) + << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0; + + QTest::newRow("horizontal, left to right, enforce range") << QSGGridView::TopToBottom << Qt::LeftToRight << int(QSGItemView::StrictlyEnforceRange) + << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0; + + QTest::newRow("horizontal, right to left, enforce range") << QSGGridView::TopToBottom << Qt::RightToLeft << int(QSGItemView::StrictlyEnforceRange) + << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 - 140.0 << -220.0; +} + +void tst_QSGGridView::snapToRow() +{ + QFETCH(QSGGridView::Flow, flow); + QFETCH(Qt::LayoutDirection, layoutDirection); + QFETCH(int, highlightRangeMode); + QFETCH(QPoint, flickStart); + QFETCH(QPoint, flickEnd); + QFETCH(qreal, snapAlignment); + QFETCH(qreal, endExtent); + QFETCH(qreal, startExtent); + + QSGView *canvas = createView(); + + canvas->setSource(QUrl::fromLocalFile(TESTDATA("snapToRow.qml"))); + canvas->show(); + qApp->processEvents(); + + QSGGridView *gridview = findItem(canvas->rootObject(), "grid"); + QTRY_VERIFY(gridview != 0); + + gridview->setFlow(flow); + gridview->setLayoutDirection(layoutDirection); + gridview->setHighlightRangeMode(QSGItemView::HighlightRangeMode(highlightRangeMode)); + + QSGItem *contentItem = gridview->contentItem(); + QTRY_VERIFY(contentItem != 0); + + // confirm that a flick hits an item boundary + flick(canvas, flickStart, flickEnd, 180); + QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops + if (flow == QSGGridView::LeftToRight) + QCOMPARE(qreal(fmod(gridview->contentY(),80.0)), snapAlignment); + else + QCOMPARE(qreal(fmod(gridview->contentX(),80.0)), snapAlignment); + + // flick to end + do { + flick(canvas, flickStart, flickEnd, 180); + QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops + } while (flow == QSGGridView::LeftToRight + ? !gridview->isAtYEnd() + : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning()); + + if (flow == QSGGridView::LeftToRight) + QCOMPARE(gridview->contentY(), endExtent); + else + QCOMPARE(gridview->contentX(), endExtent); + + // flick to start + do { + flick(canvas, flickEnd, flickStart, 180); + QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops + } while (flow == QSGGridView::LeftToRight + ? !gridview->isAtYBeginning() + : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd()); + + if (flow == QSGGridView::LeftToRight) + QCOMPARE(gridview->contentY(), startExtent); + else + QCOMPARE(gridview->contentX(), startExtent); + + delete canvas; +} + + QSGView *tst_QSGGridView::createView() { QSGView *canvas = new QSGView(0); @@ -3129,6 +3227,24 @@ QSGView *tst_QSGGridView::createView() return canvas; } +void tst_QSGGridView::flick(QSGView *canvas, const QPoint &from, const QPoint &to, int duration) +{ + const int pointCount = 5; + QPoint diff = to - from; + + // send press, five equally spaced moves, and release. + QTest::mousePress(canvas, Qt::LeftButton, 0, from); + + for (int i = 0; i < pointCount; ++i) { + QMouseEvent mv(QEvent::MouseMove, from + (i+1)*diff/pointCount, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QApplication::sendEvent(canvas, &mv); + QTest::qWait(duration/pointCount); + QCoreApplication::processEvents(); + } + + QTest::mouseRelease(canvas, Qt::LeftButton, 0, to); +} + /* Find an item with the specified objectName. If index is supplied then the item must also evaluate the {index} expression equal to index diff --git a/tests/auto/declarative/qsglistview/data/snapToItem.qml b/tests/auto/declarative/qsglistview/data/snapToItem.qml new file mode 100644 index 0000000..6f20107 --- /dev/null +++ b/tests/auto/declarative/qsglistview/data/snapToItem.qml @@ -0,0 +1,49 @@ +import QtQuick 2.0 + +Rectangle { + id: root + width: 240 + height: 240 + color: "#ffffff" + + Component { + id: myDelegate + Rectangle { + id: wrapper + objectName: "wrapper" + height: 80 + width: 80 + Column { + Text { + text: index + } + Text { + text: wrapper.x + ", " + wrapper.y + } + } + color: ListView.isCurrentItem ? "lightsteelblue" : "transparent" + } + } + ListView { + id: list + objectName: "list" + anchors.fill: parent +// preferredHighlightBegin: 20 +// preferredHighlightEnd: 100 + preferredHighlightBegin: 20 + preferredHighlightEnd: 100 + snapMode: ListView.SnapToItem + orientation: ListView.Horizontal + layoutDirection: Qt.RightToLeft + highlightRangeMode: ListView.StrictlyEnforceRange + highlight: Rectangle { width: 80; height: 80; color: "yellow" } + model: 18 + delegate: myDelegate + } + + Text { + anchors.right: parent.right + anchors.bottom: parent.bottom + text: list.contentX + ", " + list.contentY + } +} diff --git a/tests/auto/declarative/qsglistview/tst_qsglistview.cpp b/tests/auto/declarative/qsglistview/tst_qsglistview.cpp index 386a03b..be29712 100644 --- a/tests/auto/declarative/qsglistview/tst_qsglistview.cpp +++ b/tests/auto/declarative/qsglistview/tst_qsglistview.cpp @@ -55,6 +55,7 @@ #include "../shared/util.h" #include "../../../shared/util.h" #include "incrementalmodel.h" +#include Q_DECLARE_METATYPE(Qt::LayoutDirection) Q_DECLARE_METATYPE(QSGListView::Orientation) @@ -139,6 +140,8 @@ private slots: void test_mirroring(); void margins(); void creationContext(); + void snapToItem_data(); + void snapToItem(); private: template void items(); @@ -149,6 +152,7 @@ private: template void moved(); template void clear(); QSGView *createView(); + void flick(QSGView *canvas, const QPoint &from, const QPoint &to, int duration); QSGItem *findVisibleChild(QSGItem *parent, const QString &objectName); template T *findItem(QSGItem *parent, const QString &id, int index=-1); @@ -3800,6 +3804,100 @@ void tst_QSGListView::margins() delete canvas; } +void tst_QSGListView::snapToItem_data() +{ + QTest::addColumn("orientation"); + QTest::addColumn("layoutDirection"); + QTest::addColumn("highlightRangeMode"); + QTest::addColumn("flickStart"); + QTest::addColumn("flickEnd"); + QTest::addColumn("snapAlignment"); + QTest::addColumn("endExtent"); + QTest::addColumn("startExtent"); + + QTest::newRow("vertical, left to right") << QSGListView::Vertical << Qt::LeftToRight << int(QSGItemView::NoHighlightRange) + << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0; + + QTest::newRow("horizontal, left to right") << QSGListView::Horizontal << Qt::LeftToRight << int(QSGItemView::NoHighlightRange) + << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0; + + QTest::newRow("horizontal, right to left") << QSGListView::Horizontal << Qt::RightToLeft << int(QSGItemView::NoHighlightRange) + << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 << -240.0; + + QTest::newRow("vertical, left to right, enforce range") << QSGListView::Vertical << Qt::LeftToRight << int(QSGItemView::StrictlyEnforceRange) + << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0; + + QTest::newRow("horizontal, left to right, enforce range") << QSGListView::Horizontal << Qt::LeftToRight << int(QSGItemView::StrictlyEnforceRange) + << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0; + + QTest::newRow("horizontal, right to left, enforce range") << QSGListView::Horizontal << Qt::RightToLeft << int(QSGItemView::StrictlyEnforceRange) + << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 - 140.0 << -220.0; +} + +void tst_QSGListView::snapToItem() +{ + QFETCH(QSGListView::Orientation, orientation); + QFETCH(Qt::LayoutDirection, layoutDirection); + QFETCH(int, highlightRangeMode); + QFETCH(QPoint, flickStart); + QFETCH(QPoint, flickEnd); + QFETCH(qreal, snapAlignment); + QFETCH(qreal, endExtent); + QFETCH(qreal, startExtent); + + QSGView *canvas = createView(); + + canvas->setSource(QUrl::fromLocalFile(TESTDATA("snapToItem.qml"))); + canvas->show(); + qApp->processEvents(); + + QSGListView *listview = findItem(canvas->rootObject(), "list"); + QTRY_VERIFY(listview != 0); + + listview->setOrientation(orientation); + listview->setLayoutDirection(layoutDirection); + listview->setHighlightRangeMode(QSGItemView::HighlightRangeMode(highlightRangeMode)); + + QSGItem *contentItem = listview->contentItem(); + QTRY_VERIFY(contentItem != 0); + + // confirm that a flick hits an item boundary + flick(canvas, flickStart, flickEnd, 180); + QTRY_VERIFY(listview->isMoving() == false); // wait until it stops + if (orientation == QSGListView::Vertical) + QCOMPARE(qreal(fmod(listview->contentY(),80.0)), snapAlignment); + else + QCOMPARE(qreal(fmod(listview->contentX(),80.0)), snapAlignment); + + // flick to end + do { + flick(canvas, flickStart, flickEnd, 180); + QTRY_VERIFY(listview->isMoving() == false); // wait until it stops + } while (orientation == QSGListView::Vertical + ? !listview->isAtYEnd() + : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning()); + + if (orientation == QSGListView::Vertical) + QCOMPARE(listview->contentY(), endExtent); + else + QCOMPARE(listview->contentX(), endExtent); + + // flick to start + do { + flick(canvas, flickEnd, flickStart, 180); + QTRY_VERIFY(listview->isMoving() == false); // wait until it stops + } while (orientation == QSGListView::Vertical + ? !listview->isAtYBeginning() + : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd()); + + if (orientation == QSGListView::Vertical) + QCOMPARE(listview->contentY(), startExtent); + else + QCOMPARE(listview->contentX(), startExtent); + + delete canvas; +} + void tst_QSGListView::qListModelInterface_items() { items(); @@ -3922,6 +4020,25 @@ QSGView *tst_QSGListView::createView() return canvas; } +void tst_QSGListView::flick(QSGView *canvas, const QPoint &from, const QPoint &to, int duration) +{ + const int pointCount = 5; + QPoint diff = to - from; + + // send press, five equally spaced moves, and release. + QTest::mousePress(canvas, Qt::LeftButton, 0, from); + + for (int i = 0; i < pointCount; ++i) { + QMouseEvent mv(QEvent::MouseMove, from + (i+1)*diff/pointCount, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QApplication::sendEvent(canvas, &mv); + QTest::qWait(duration/pointCount); + QCoreApplication::processEvents(); + } + + QTest::mouseRelease(canvas, Qt::LeftButton, 0, to); +} + + QSGItem *tst_QSGListView::findVisibleChild(QSGItem *parent, const QString &objectName) { QSGItem *item = 0; -- 1.7.2.5