From: Martin Jones Date: Fri, 23 Sep 2011 06:13:51 +0000 (+1000) Subject: Support margins in Flickable. X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=14228a41471a327f6e600220a795a1c6cb23c2b2;p=konrad%2Fqtdeclarative.git Support margins in Flickable. It is sometimes desireable to leave a margin/add decoration around the content of a Flickable. This adds topMargin, leftMargin, bottomMargin rightMargin, xOrigin and yOrigin properties to Flickable. Task-number: QTBUG-21362 Change-Id: Ia24ea4c63e7a8de683b68100baac782c6f3a66bb Reviewed-on: http://codereview.qt-project.org/5445 Reviewed-by: Bea Lam --- diff --git a/doc/src/declarative/whatsnew.qdoc b/doc/src/declarative/whatsnew.qdoc index da00160..ea575a6 100644 --- a/doc/src/declarative/whatsnew.qdoc +++ b/doc/src/declarative/whatsnew.qdoc @@ -94,6 +94,7 @@ The Binding element can now be used as a value source, and will also restore any set binding when its \e when clause becomes false. Flickable: added dragging, draggingHorizontally and draggingVerically properties. +Added topMargin, bottomMargin, leftMargin, rightMargin, xOrigin, yOrigin properties. Image has two new properties: horizontalAlignment and verticalAlignment. It also has a new value for fillMode (Image.Pad) that does not transform the image. diff --git a/src/declarative/items/qsgflickable.cpp b/src/declarative/items/qsgflickable.cpp index 36d7db2..13cb966 100644 --- a/src/declarative/items/qsgflickable.cpp +++ b/src/declarative/items/qsgflickable.cpp @@ -423,6 +423,16 @@ void QSGFlickablePrivate::updateBeginningEnd() atBoundaryChange = true; } + if (vData.extentsChanged) { + vData.extentsChanged = false; + emit q->yOriginChanged(); + } + + if (hData.extentsChanged) { + hData.extentsChanged = false; + emit q->xOriginChanged(); + } + if (atBoundaryChange) emit q->isAtBoundaryChanged(); @@ -594,6 +604,7 @@ qreal QSGFlickable::contentX() const void QSGFlickable::setContentX(qreal pos) { Q_D(QSGFlickable); + d->hData.explicitValue = true; d->timeline.reset(d->hData.move); d->vTime = d->timeline.time(); movementXEnding(); @@ -612,6 +623,7 @@ qreal QSGFlickable::contentY() const void QSGFlickable::setContentY(qreal pos) { Q_D(QSGFlickable); + d->vData.explicitValue = true; d->timeline.reset(d->vData.move); d->vTime = d->timeline.time(); movementYEnding(); @@ -1137,23 +1149,37 @@ void QSGFlickable::timerEvent(QTimerEvent *event) qreal QSGFlickable::minYExtent() const { - return 0.0; + Q_D(const QSGFlickable); + return d->vData.startMargin; } qreal QSGFlickable::minXExtent() const { - return 0.0; + Q_D(const QSGFlickable); + return d->hData.startMargin; } /* returns -ve */ qreal QSGFlickable::maxXExtent() const { - return width() - vWidth(); + Q_D(const QSGFlickable); + return width() - vWidth() - d->hData.endMargin; } /* returns -ve */ qreal QSGFlickable::maxYExtent() const { - return height() - vHeight(); + Q_D(const QSGFlickable); + return height() - vHeight() - d->vData.endMargin; +} + +void QSGFlickable::componentComplete() +{ + Q_D(QSGFlickable); + QSGItem::componentComplete(); + if (!d->hData.explicitValue && d->hData.startMargin != 0.) + setContentX(-minXExtent()); + if (!d->vData.explicitValue && d->vData.startMargin != 0.) + setContentY(-minYExtent()); } void QSGFlickable::viewportMoved() @@ -1372,6 +1398,7 @@ void QSGFlickable::setContentWidth(qreal w) d->contentItem->setWidth(width()); else d->contentItem->setWidth(w); + d->hData.markExtentsDirty(); // Make sure that we're entirely in view. if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { d->fixupMode = QSGFlickablePrivate::Immediate; @@ -1400,6 +1427,7 @@ void QSGFlickable::setContentHeight(qreal h) d->contentItem->setHeight(height()); else d->contentItem->setHeight(h); + d->vData.markExtentsDirty(); // Make sure that we're entirely in view. if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { d->fixupMode = QSGFlickablePrivate::Immediate; @@ -1413,8 +1441,124 @@ void QSGFlickable::setContentHeight(qreal h) } /*! + \qmlproperty real QtQuick2::Flickable::topMargin + \qmlproperty real QtQuick2::Flickable::leftMargin + \qmlproperty real QtQuick2::Flickable::bottomMargin + \qmlproperty real QtQuick2::Flickable::rightMargin + + These properties hold the margins around the content. This space is reserved + in addition to the contentWidth and contentHeight. +*/ + + +qreal QSGFlickable::topMargin() const +{ + Q_D(const QSGFlickable); + return d->vData.startMargin; +} + +void QSGFlickable::setTopMargin(qreal m) +{ + Q_D(QSGFlickable); + if (d->vData.startMargin == m) + return; + d->vData.startMargin = m; + d->vData.markExtentsDirty(); + if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { + d->fixupMode = QSGFlickablePrivate::Immediate; + d->fixupY(); + } + emit topMarginChanged(); + d->updateBeginningEnd(); +} + +qreal QSGFlickable::bottomMargin() const +{ + Q_D(const QSGFlickable); + return d->vData.endMargin; +} + +void QSGFlickable::setBottomMargin(qreal m) +{ + Q_D(QSGFlickable); + if (d->vData.endMargin == m) + return; + d->vData.endMargin = m; + d->vData.markExtentsDirty(); + if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { + d->fixupMode = QSGFlickablePrivate::Immediate; + d->fixupY(); + } + emit bottomMarginChanged(); + d->updateBeginningEnd(); +} + +qreal QSGFlickable::leftMargin() const +{ + Q_D(const QSGFlickable); + return d->hData.startMargin; +} + +void QSGFlickable::setLeftMargin(qreal m) +{ + Q_D(QSGFlickable); + if (d->hData.startMargin == m) + return; + d->hData.startMargin = m; + d->hData.markExtentsDirty(); + if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { + d->fixupMode = QSGFlickablePrivate::Immediate; + d->fixupX(); + } + emit leftMarginChanged(); + d->updateBeginningEnd(); +} + +qreal QSGFlickable::rightMargin() const +{ + Q_D(const QSGFlickable); + return d->hData.endMargin; +} + +void QSGFlickable::setRightMargin(qreal m) +{ + Q_D(QSGFlickable); + if (d->hData.endMargin == m) + return; + d->hData.endMargin = m; + d->hData.markExtentsDirty(); + if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { + d->fixupMode = QSGFlickablePrivate::Immediate; + d->fixupX(); + } + emit rightMarginChanged(); + d->updateBeginningEnd(); +} + +/*! + \qmlproperty real QtQuick2::Flickable::xOrigin + \qmlproperty real QtQuick2::Flickable::yOrigin + + These properties hold the origin of the content. This is usually (0,0), however + ListView and GridView may have an arbitrary origin due to delegate size variation, + or item insertion/removal outside the visible region. +*/ + +qreal QSGFlickable::yOrigin() const +{ + Q_D(const QSGFlickable); + return -minYExtent() + d->vData.startMargin; +} + +qreal QSGFlickable::xOrigin() const +{ + Q_D(const QSGFlickable); + return -minXExtent() + d->hData.startMargin; +} + + +/*! \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center) - \preliminary Resizes the content to \a width x \a height about \a center. @@ -1453,7 +1597,6 @@ void QSGFlickable::resizeContent(qreal w, qreal h, QPointF center) /*! \qmlmethod QtQuick2::Flickable::returnToBounds() - \preliminary Ensures the content is within legal bounds. diff --git a/src/declarative/items/qsgflickable_p.h b/src/declarative/items/qsgflickable_p.h index d638b92..54581a5 100644 --- a/src/declarative/items/qsgflickable_p.h +++ b/src/declarative/items/qsgflickable_p.h @@ -63,6 +63,14 @@ class Q_AUTOTEST_EXPORT QSGFlickable : public QSGItem Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged) Q_PROPERTY(QSGItem *contentItem READ contentItem CONSTANT) + Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged) + Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged) + Q_PROPERTY(qreal yOrigin READ yOrigin NOTIFY yOriginChanged) + + Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) + Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) + Q_PROPERTY(qreal xOrigin READ xOrigin NOTIFY xOriginChanged) + Q_PROPERTY(qreal horizontalVelocity READ horizontalVelocity NOTIFY horizontalVelocityChanged) Q_PROPERTY(qreal verticalVelocity READ verticalVelocity NOTIFY verticalVelocityChanged) @@ -122,6 +130,21 @@ public: qreal contentY() const; virtual void setContentY(qreal pos); + qreal topMargin() const; + void setTopMargin(qreal m); + + qreal bottomMargin() const; + void setBottomMargin(qreal m); + + qreal leftMargin() const; + void setLeftMargin(qreal m); + + qreal rightMargin() const; + void setRightMargin(qreal m); + + virtual qreal yOrigin() const; + virtual qreal xOrigin() const; + bool isMoving() const; bool isMovingHorizontally() const; bool isMovingVertically() const; @@ -169,6 +192,12 @@ Q_SIGNALS: void contentHeightChanged(); void contentXChanged(); void contentYChanged(); + void topMarginChanged(); + void bottomMarginChanged(); + void leftMarginChanged(); + void rightMarginChanged(); + void yOriginChanged(); + void xOriginChanged(); void movingChanged(); void movingHorizontallyChanged(); void movingVerticallyChanged(); @@ -219,6 +248,7 @@ protected: virtual qreal maxYExtent() const; qreal vWidth() const; qreal vHeight() const; + virtual void componentComplete(); virtual void viewportMoved(); virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); diff --git a/src/declarative/items/qsgflickable_p_p.h b/src/declarative/items/qsgflickable_p_p.h index 21c3106..b3690b4 100644 --- a/src/declarative/items/qsgflickable_p_p.h +++ b/src/declarative/items/qsgflickable_p_p.h @@ -95,8 +95,10 @@ public: struct AxisData { AxisData(QSGFlickablePrivate *fp, void (QSGFlickablePrivate::*func)(qreal)) - : move(fp, func), viewSize(-1), smoothVelocity(fp), atEnd(false), atBeginning(true) - , fixingUp(false), inOvershoot(false), dragging(false) + : move(fp, func), viewSize(-1), startMargin(0), endMargin(0) + , smoothVelocity(fp), atEnd(false), atBeginning(true) + , fixingUp(false), inOvershoot(false), dragging(false), extentsChanged(false) + , explicitValue(false), minExtentDirty(true), maxExtentDirty(true) {} void reset() { @@ -106,6 +108,12 @@ public: inOvershoot = false; } + void markExtentsDirty() { + minExtentDirty = true; + maxExtentDirty = true; + extentsChanged = true; + } + void addVelocitySample(qreal v, qreal maxVelocity); void updateVelocity(); @@ -117,6 +125,8 @@ public: qreal dragMaxBound; qreal velocity; qreal flickTarget; + qreal startMargin; + qreal endMargin; QSGFlickablePrivate::Velocity smoothVelocity; QPODVector velocityBuffer; bool atEnd : 1; @@ -124,6 +134,10 @@ public: bool fixingUp : 1; bool inOvershoot : 1; bool dragging : 1; + bool extentsChanged : 1; + bool explicitValue : 1; + mutable bool minExtentDirty : 1; + mutable bool maxExtentDirty : 1; }; void flickX(qreal velocity); diff --git a/src/declarative/items/qsgitemview.cpp b/src/declarative/items/qsgitemview.cpp index 291592b..63040c1 100644 --- a/src/declarative/items/qsgitemview.cpp +++ b/src/declarative/items/qsgitemview.cpp @@ -401,8 +401,7 @@ void QSGItemView::setHeader(QDeclarativeComponent *headerComponent) d->header = 0; d->headerComponent = headerComponent; - d->minExtentDirty = true; - d->maxExtentDirty = true; + d->markExtentsDirty(); if (isComponentComplete()) { d->updateHeader(); @@ -724,14 +723,12 @@ void QSGItemViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeo if (header && header->item == item) { updateHeader(); - minExtentDirty = true; - maxExtentDirty = true; + markExtentsDirty(); if (!q->isMoving() && !q->isFlicking()) fixupPosition(); } else if (footer && footer->item == item) { updateFooter(); - minExtentDirty = true; - maxExtentDirty = true; + markExtentsDirty(); if (!q->isMoving() && !q->isFlicking()) fixupPosition(); } @@ -892,8 +889,7 @@ void QSGItemView::trackedPositionChanged() void QSGItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_D(QSGItemView); - d->maxExtentDirty = true; - d->minExtentDirty = true; + d->markExtentsDirty(); QSGFlickable::geometryChanged(newGeometry, oldGeometry); } @@ -904,8 +900,8 @@ qreal QSGItemView::minYExtent() const if (d->layoutOrientation() == Qt::Horizontal) return QSGFlickable::minYExtent(); - if (d->minExtentDirty) { - d->minExtent = -d->startPosition(); + if (d->vData.minExtentDirty) { + d->minExtent = d->vData.startMargin-d->startPosition(); if (d->header) d->minExtent += d->headerSize(); if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { @@ -914,7 +910,7 @@ qreal QSGItemView::minYExtent() const d->minExtent -= d->visibleItem(0)->sectionSize(); d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd)); } - d->minExtentDirty = false; + d->vData.minExtentDirty = false; } return d->minExtent; @@ -926,7 +922,7 @@ qreal QSGItemView::maxYExtent() const if (d->layoutOrientation() == Qt::Horizontal) return height(); - if (d->maxExtentDirty) { + if (d->vData.maxExtentDirty) { if (!d->model || !d->model->count()) { d->maxExtent = d->header ? -d->headerSize() : 0; d->maxExtent += height(); @@ -940,10 +936,11 @@ qreal QSGItemView::maxYExtent() const if (d->footer) d->maxExtent -= d->footerSize(); + d->maxExtent -= d->vData.endMargin; qreal minY = minYExtent(); if (d->maxExtent > minY) d->maxExtent = minY; - d->maxExtentDirty = false; + d->vData.maxExtentDirty = false; } return d->maxExtent; } @@ -954,12 +951,13 @@ qreal QSGItemView::minXExtent() const if (d->layoutOrientation() == Qt::Vertical) return QSGFlickable::minXExtent(); - if (d->minExtentDirty) { + if (d->hData.minExtentDirty) { d->minExtent = -d->startPosition(); qreal highlightStart; qreal highlightEnd; qreal endPositionFirstItem = 0; if (d->isContentFlowReversed()) { + d->minExtent += d->hData.endMargin; if (d->model && d->model->count()) endPositionFirstItem = d->positionAt(d->model->count()-1); else if (d->header) @@ -974,6 +972,7 @@ qreal QSGItemView::minXExtent() const if (d->minExtent < maxX) d->minExtent = maxX; } else { + d->minExtent += d->hData.startMargin; endPositionFirstItem = d->endPositionAt(0); highlightStart = d->highlightRangeStart; highlightEnd = d->highlightRangeEnd; @@ -984,7 +983,7 @@ qreal QSGItemView::minXExtent() const d->minExtent += highlightStart; d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd)); } - d->minExtentDirty = false; + d->hData.minExtentDirty = false; } return d->minExtent; @@ -996,7 +995,7 @@ qreal QSGItemView::maxXExtent() const if (d->layoutOrientation() == Qt::Vertical) return width(); - if (d->maxExtentDirty) { + if (d->hData.maxExtentDirty) { qreal highlightStart; qreal highlightEnd; qreal lastItemPosition = 0; @@ -1028,14 +1027,16 @@ qreal QSGItemView::maxXExtent() const if (d->isContentFlowReversed()) { if (d->header) d->maxExtent -= d->headerSize(); + d->maxExtent -= d->hData.startMargin; } else { if (d->footer) d->maxExtent -= d->footerSize(); + d->maxExtent -= d->hData.endMargin; qreal minX = minXExtent(); if (d->maxExtent > minX) d->maxExtent = minX; } - d->maxExtentDirty = false; + d->hData.maxExtentDirty = false; } return d->maxExtent; @@ -1057,6 +1058,14 @@ void QSGItemView::setContentY(qreal pos) QSGFlickable::setContentY(pos); } +qreal QSGItemView::xOrigin() const +{ + Q_D(const QSGItemView); + if (d->isContentFlowReversed()) + return -maxXExtent() + d->size() - d->hData.startMargin; + else + return -minXExtent() + d->hData.startMargin; +} void QSGItemView::updatePolish() { @@ -1111,7 +1120,6 @@ QSGItemViewPrivate::QSGItemViewPrivate() , ownModel(false), wrap(false), lazyRelease(false), deferredRelease(false) , inApplyModelChanges(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false) , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false) - , minExtentDirty(true), maxExtentDirty(true) { } @@ -1144,7 +1152,16 @@ qreal QSGItemViewPrivate::endPosition() const qreal QSGItemViewPrivate::contentStartPosition() const { - return -headerSize(); + Q_Q(const QSGItemView); + qreal pos = -headerSize(); + if (layoutOrientation() == Qt::Vertical) + pos -= vData.startMargin; + else if (isContentFlowReversed()) + pos -= hData.endMargin; + else + pos -= hData.startMargin; + + return pos; } int QSGItemViewPrivate::findLastVisibleIndex(int defaultValue) const @@ -1263,8 +1280,7 @@ void QSGItemViewPrivate::clear() createHighlight(); trackedItem = 0; - minExtentDirty = true; - maxExtentDirty = true; + markExtentsDirty(); itemCount = 0; } @@ -1318,8 +1334,7 @@ void QSGItemViewPrivate::refill(qreal from, qreal to, bool doBuffer) } if (changed) { - minExtentDirty = true; - maxExtentDirty = true; + markExtentsDirty(); visibleItemsChanged(); } else if (!doBuffer && buffer && bufferMode != NoBuffer) { refill(from, to, true); @@ -1376,8 +1391,7 @@ void QSGItemViewPrivate::layout() layoutVisibleItems(); refill(); - minExtentDirty = true; - maxExtentDirty = true; + markExtentsDirty(); updateHighlight(); @@ -1404,6 +1418,8 @@ bool QSGItemViewPrivate::applyModelChanges() int prevCount = itemCount; bool removedVisible = false; + bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty() + || !currentChanges.pendingChanges.inserts().isEmpty(); FxViewItem *firstVisible = firstVisibleItem(); FxViewItem *origVisibleItemsFirst = visibleItems.count() ? visibleItems.first() : 0; @@ -1516,8 +1532,12 @@ bool QSGItemViewPrivate::applyModelChanges() if (prevCount != itemCount) emit q->countChanged(); + bool visibleAffected = removedVisible || addedVisible || !currentChanges.pendingChanges.changes().isEmpty(); + if (!visibleAffected && viewportChanged) + updateViewport(); + inApplyModelChanges = false; - return removedVisible || addedVisible || !currentChanges.pendingChanges.changes().isEmpty(); + return visibleAffected; } FxViewItem *QSGItemViewPrivate::createItem(int modelIndex) diff --git a/src/declarative/items/qsgitemview_p.h b/src/declarative/items/qsgitemview_p.h index 9d25eab..7b8efbb 100644 --- a/src/declarative/items/qsgitemview_p.h +++ b/src/declarative/items/qsgitemview_p.h @@ -154,6 +154,7 @@ public: virtual void setContentX(qreal pos); virtual void setContentY(qreal pos); + virtual qreal xOrigin() const; signals: void modelChanged(); diff --git a/src/declarative/items/qsgitemview_p_p.h b/src/declarative/items/qsgitemview_p_p.h index 73cb68c..2d39dce 100644 --- a/src/declarative/items/qsgitemview_p_p.h +++ b/src/declarative/items/qsgitemview_p_p.h @@ -142,6 +142,13 @@ public: void checkVisible() const; + void markExtentsDirty() { + if (layoutOrientation() == Qt::Vertical) + vData.markExtentsDirty(); + else + hData.markExtentsDirty(); + } + QDeclarativeGuard model; QVariant modelVariant; int itemCount; @@ -188,8 +195,6 @@ public: bool autoHighlight : 1; bool highlightRangeStartValid : 1; bool highlightRangeEndValid : 1; - mutable bool minExtentDirty : 1; - mutable bool maxExtentDirty : 1; protected: virtual Qt::Orientation layoutOrientation() const = 0; diff --git a/tests/auto/declarative/qsgflickable/data/margins.qml b/tests/auto/declarative/qsgflickable/data/margins.qml new file mode 100644 index 0000000..4866bd8 --- /dev/null +++ b/tests/auto/declarative/qsgflickable/data/margins.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 + +Flickable { + width: 200; height: 200 + contentWidth: row.width; contentHeight: row.height + + topMargin: 20 + bottomMargin: 30 + leftMargin: 40 + rightMargin: 50 + + Row { + id: row + Repeater { + model: 4 + Rectangle { width: 400; height: 600; color: "blue" } + } + } +} diff --git a/tests/auto/declarative/qsgflickable/tst_qsgflickable.cpp b/tests/auto/declarative/qsgflickable/tst_qsgflickable.cpp index e75914a..554be23 100644 --- a/tests/auto/declarative/qsgflickable/tst_qsgflickable.cpp +++ b/tests/auto/declarative/qsgflickable/tst_qsgflickable.cpp @@ -80,6 +80,7 @@ private slots: void movingAndDragging(); void disabled(); void flickVelocity(); + void margins(); private: QDeclarativeEngine engine; @@ -563,6 +564,65 @@ void tst_qsgflickable::flickVelocity() delete canvas; } +void tst_qsgflickable::margins() +{ + QDeclarativeEngine engine; + QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/margins.qml")); + QSGItem *root = qobject_cast(c.create()); + QSGFlickable *obj = qobject_cast(root); + QVERIFY(obj != 0); + + // starting state + QCOMPARE(obj->contentX(), -40.); + QCOMPARE(obj->contentY(), -20.); + QCOMPARE(obj->contentWidth(), 1600.); + QCOMPARE(obj->contentHeight(), 600.); + QCOMPARE(obj->xOrigin(), 0.); + QCOMPARE(obj->yOrigin(), 0.); + + // Reduce left margin + obj->setLeftMargin(30); + QTRY_COMPARE(obj->contentX(), -30.); + + // Reduce top margin + obj->setTopMargin(20); + QTRY_COMPARE(obj->contentY(), -20.); + + // position to the far right, including margin + obj->setContentX(1600 + 50 - obj->width()); + obj->returnToBounds(); + QTest::qWait(200); + QCOMPARE(obj->contentX(), 1600. + 50. - obj->width()); + + // position beyond the far right, including margin + obj->setContentX(1600 + 50 - obj->width() + 1.); + obj->returnToBounds(); + QTRY_COMPARE(obj->contentX(), 1600. + 50. - obj->width()); + + // Reduce right margin + obj->setRightMargin(40); + QTRY_COMPARE(obj->contentX(), 1600. + 40. - obj->width()); + QCOMPARE(obj->contentWidth(), 1600.); + + // position to the far bottom, including margin + obj->setContentY(600 + 30 - obj->height()); + obj->returnToBounds(); + QTest::qWait(200); + QCOMPARE(obj->contentY(), 600. + 30. - obj->height()); + + // position beyond the far bottom, including margin + obj->setContentY(600 + 30 - obj->height() + 1.); + obj->returnToBounds(); + QTRY_COMPARE(obj->contentY(), 600. + 30. - obj->height()); + + // Reduce bottom margin + obj->setBottomMargin(20); + QTRY_COMPARE(obj->contentY(), 600. + 20. - obj->height()); + QCOMPARE(obj->contentHeight(), 600.); + + delete root; +} + void tst_qsgflickable::flick(QSGView *canvas, const QPoint &from, const QPoint &to, int duration) { const int pointCount = 5; diff --git a/tests/auto/declarative/qsggridview/data/margins.qml b/tests/auto/declarative/qsggridview/data/margins.qml new file mode 100644 index 0000000..d369658 --- /dev/null +++ b/tests/auto/declarative/qsggridview/data/margins.qml @@ -0,0 +1,55 @@ +import QtQuick 2.0 + +Rectangle { + id: root + + width: 240 + height: 320 + color: "#ffffff" + Component { + id: myDelegate + Rectangle { + id: wrapper + objectName: "wrapper" + width: 100 + height: 80 + border.color: "blue" + property string name: model.name + Text { + text: index + } + Text { + x: 40 + text: wrapper.x + ", " + wrapper.y + } + Text { + y: 20 + id: textName + objectName: "textName" + text: name + } + Text { + y: 40 + id: textNumber + objectName: "textNumber" + text: number + } + color: GridView.isCurrentItem ? "lightsteelblue" : "white" + } + } + GridView { + id: grid + objectName: "grid" + width: 240 + height: 320 + cellWidth: 100 + cellHeight: 80 + leftMargin: 30 + rightMargin: 50 + flow: GridView.TopToBottom + layoutDirection: (testRightToLeft == true) ? Qt.RightToLeft : Qt.LeftToRight + model: testModel + delegate: myDelegate + } + Text { anchors.bottom: parent.bottom; text: grid.contentX } +} diff --git a/tests/auto/declarative/qsggridview/tst_qsggridview.cpp b/tests/auto/declarative/qsggridview/tst_qsggridview.cpp index 5d35ffc..f5cda2b 100644 --- a/tests/auto/declarative/qsggridview/tst_qsggridview.cpp +++ b/tests/auto/declarative/qsggridview/tst_qsggridview.cpp @@ -110,6 +110,7 @@ private slots: void testQtQuick11Attributes(); void testQtQuick11Attributes_data(); void columnCount(); + void margins(); private: QSGView *createView(); @@ -2803,6 +2804,136 @@ void tst_QSGGridView::columnCount() QCOMPARE(items.at(9)->y(), qreal(100)); } +void tst_QSGGridView::margins() +{ + { + QSGView *canvas = createView(); + canvas->show(); + + TestModel model; + for (int i = 0; i < 40; i++) + model.addItem("Item" + QString::number(i), ""); + + QDeclarativeContext *ctxt = canvas->rootContext(); + ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); + + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/margins.qml")); + qApp->processEvents(); + + QSGGridView *gridview = findItem(canvas->rootObject(), "grid"); + QTRY_VERIFY(gridview != 0); + + QSGItem *contentItem = gridview->contentItem(); + QTRY_VERIFY(contentItem != 0); + + QCOMPARE(gridview->contentX(), -30.); + QCOMPARE(gridview->xOrigin(), 0.); + + // check end bound + gridview->positionViewAtEnd(); + qreal pos = gridview->contentX(); + gridview->setContentX(pos + 80); + gridview->returnToBounds(); + QTRY_COMPARE(gridview->contentX(), pos + 50); + + // remove item before visible and check that left margin is maintained + // and xOrigin is updated + gridview->setContentX(200); + model.removeItems(0, 4); + QTest::qWait(100); + gridview->setContentX(-50); + gridview->returnToBounds(); + QCOMPARE(gridview->xOrigin(), 100.); + QTRY_COMPARE(gridview->contentX(), 70.); + + // reduce left margin + gridview->setLeftMargin(20); + QCOMPARE(gridview->xOrigin(), 100.); + QTRY_COMPARE(gridview->contentX(), 80.); + + // check end bound + gridview->positionViewAtEnd(); + QCOMPARE(gridview->xOrigin(), 0.); // positionViewAtEnd() resets origin + pos = gridview->contentX(); + gridview->setContentX(pos + 80); + gridview->returnToBounds(); + QTRY_COMPARE(gridview->contentX(), pos + 50); + + // reduce right margin + pos = gridview->contentX(); + gridview->setRightMargin(40); + QCOMPARE(gridview->xOrigin(), 0.); + QTRY_COMPARE(gridview->contentX(), pos-10); + + delete canvas; + } + { + //RTL + QSGView *canvas = createView(); + canvas->show(); + + TestModel model; + for (int i = 0; i < 40; i++) + model.addItem("Item" + QString::number(i), ""); + + QDeclarativeContext *ctxt = canvas->rootContext(); + ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(true)); + + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/margins.qml")); + qApp->processEvents(); + + QSGGridView *gridview = findItem(canvas->rootObject(), "grid"); + QTRY_VERIFY(gridview != 0); + + QSGItem *contentItem = gridview->contentItem(); + QTRY_VERIFY(contentItem != 0); + + QCOMPARE(gridview->contentX(), -240+30.); + QCOMPARE(gridview->xOrigin(), 0.); + + // check end bound + gridview->positionViewAtEnd(); + qreal pos = gridview->contentX(); + gridview->setContentX(pos - 80); + gridview->returnToBounds(); + QTRY_COMPARE(gridview->contentX(), pos - 50); + + // remove item before visible and check that left margin is maintained + // and xOrigin is updated + gridview->setContentX(-400); + model.removeItems(0, 4); + QTest::qWait(100); + gridview->setContentX(-240+50); + gridview->returnToBounds(); + QCOMPARE(gridview->xOrigin(), -100.); + QTRY_COMPARE(gridview->contentX(), -240-70.); + + // reduce left margin (i.e. right side due to RTL) + pos = gridview->contentX(); + gridview->setLeftMargin(20); + QCOMPARE(gridview->xOrigin(), -100.); + QTRY_COMPARE(gridview->contentX(), -240-80.); + + // check end bound + gridview->positionViewAtEnd(); + QCOMPARE(gridview->xOrigin(), 0.); // positionViewAtEnd() resets origin + pos = gridview->contentX(); + gridview->setContentX(pos - 80); + gridview->returnToBounds(); + QTRY_COMPARE(gridview->contentX(), pos - 50); + + // reduce right margin (i.e. left side due to RTL) + pos = gridview->contentX(); + gridview->setRightMargin(40); + QCOMPARE(gridview->xOrigin(), 0.); + QTRY_COMPARE(gridview->contentX(), pos+10); + + delete canvas; + } +} + QSGView *tst_QSGGridView::createView() { QSGView *canvas = new QSGView(0); diff --git a/tests/auto/declarative/qsglistview/data/margins.qml b/tests/auto/declarative/qsglistview/data/margins.qml new file mode 100644 index 0000000..19bbef5 --- /dev/null +++ b/tests/auto/declarative/qsglistview/data/margins.qml @@ -0,0 +1,47 @@ +import QtQuick 2.0 + +Rectangle { + id: root + width: 240 + height: 320 + color: "#ffffff" + + Component { + id: myDelegate + Rectangle { + id: wrapper + objectName: "wrapper" + height: 20 + width: 240 + Text { + text: index + } + Text { + x: 30 + id: textName + objectName: "textName" + text: name + } + Text { + x: 120 + id: textNumber + objectName: "textNumber" + text: number + } + Text { + x: 200 + text: wrapper.y + } + color: ListView.isCurrentItem ? "lightsteelblue" : "white" + } + } + ListView { + id: list + objectName: "list" + anchors.fill: parent + topMargin: 30 + bottomMargin: 50 + model: testModel + delegate: myDelegate + } +} diff --git a/tests/auto/declarative/qsglistview/tst_qsglistview.cpp b/tests/auto/declarative/qsglistview/tst_qsglistview.cpp index ce1587d..291877c 100644 --- a/tests/auto/declarative/qsglistview/tst_qsglistview.cpp +++ b/tests/auto/declarative/qsglistview/tst_qsglistview.cpp @@ -136,6 +136,7 @@ private slots: void onRemove_data(); void rightToLeft(); void test_mirroring(); + void margins(); private: template void items(); @@ -3542,6 +3543,68 @@ void tst_QSGListView::test_mirroring() delete canvasB; } +void tst_QSGListView::margins() +{ + QSGView *canvas = createView(); + + TestModel2 model; + for (int i = 0; i < 50; i++) + model.addItem("Item" + QString::number(i), ""); + + QDeclarativeContext *ctxt = canvas->rootContext(); + ctxt->setContextProperty("testModel", &model); + + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/margins.qml")); + canvas->show(); + qApp->processEvents(); + + QSGListView *listview = findItem(canvas->rootObject(), "list"); + QTRY_VERIFY(listview != 0); + + QSGItem *contentItem = listview->contentItem(); + QTRY_VERIFY(contentItem != 0); + + QCOMPARE(listview->contentY(), -30.); + QCOMPARE(listview->yOrigin(), 0.); + + // check end bound + listview->positionViewAtEnd(); + qreal pos = listview->contentY(); + listview->setContentY(pos + 80); + listview->returnToBounds(); + QTRY_COMPARE(listview->contentY(), pos + 50); + + // remove item before visible and check that top margin is maintained + // and yOrigin is updated + listview->setContentY(100); + model.removeItem(1); + QTest::qWait(100); + listview->setContentY(-50); + listview->returnToBounds(); + QCOMPARE(listview->yOrigin(), 20.); + QTRY_COMPARE(listview->contentY(), -10.); + + // reduce top margin + listview->setTopMargin(20); + QCOMPARE(listview->yOrigin(), 20.); + QTRY_COMPARE(listview->contentY(), 0.); + + // check end bound + listview->positionViewAtEnd(); + pos = listview->contentY(); + listview->setContentY(pos + 80); + listview->returnToBounds(); + QTRY_COMPARE(listview->contentY(), pos + 50); + + // reduce bottom margin + pos = listview->contentY(); + listview->setBottomMargin(40); + QCOMPARE(listview->yOrigin(), 20.); + QTRY_COMPARE(listview->contentY(), pos-10); + + delete canvas; +} + void tst_QSGListView::qListModelInterface_items() { items();