From 53dd7206a7a2ba4393d558bf76fa23f4a4e05183 Mon Sep 17 00:00:00 2001 From: Robert Griebl Date: Tue, 30 Nov 2010 23:00:07 +0100 Subject: QScroller merge, part 2 This merge consists of a QScroller based Flickable/Listview/Gridview replacement for QML. The complete history is in http://scm.dev.nokia.troll.no/projects/qt/repos/rgriebls-qt-flickgesture/logs/4.7-flickgesture (part 1 is the actual QScroller implementation / part 3 is QWebView support) Task-number: QTBUG-13390 Reviewed-by: Ralf Engels --- .../graphicsitems/qdeclarativeflickable.cpp | 1169 +++++----------- .../graphicsitems/qdeclarativeflickable_p.h | 22 +- .../graphicsitems/qdeclarativeflickable_p_p.h | 103 +- .../graphicsitems/qdeclarativegridview.cpp | 1202 +++++++--------- .../graphicsitems/qdeclarativegridview_p.h | 9 +- .../graphicsitems/qdeclarativelistview.cpp | 1428 +++++++++----------- .../graphicsitems/qdeclarativelistview_p.h | 9 +- .../tst_qdeclarativeflickable.cpp | 4 +- .../tst_qdeclarativegridview.cpp | 2 + .../tst_qdeclarativelistview.cpp | 8 +- 10 files changed, 1530 insertions(+), 2426 deletions(-) diff --git a/src/declarative/graphicsitems/qdeclarativeflickable.cpp b/src/declarative/graphicsitems/qdeclarativeflickable.cpp index 98c213c..96f54a8 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable.cpp +++ b/src/declarative/graphicsitems/qdeclarativeflickable.cpp @@ -44,14 +44,12 @@ #include #include #include -#include -QT_BEGIN_NAMESPACE +#include +#include -// FlickThreshold determines how far the "mouse" must have moved -// before we perform a flick. -static const int FlickThreshold = 20; +QT_BEGIN_NAMESPACE QDeclarativeFlickableVisibleArea::QDeclarativeFlickableVisibleArea(QDeclarativeFlickable *parent) : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.) @@ -79,6 +77,11 @@ qreal QDeclarativeFlickableVisibleArea::yPosition() const return m_yPosition; } +/*! \internal + Updates the page position and ratio values and sends out the appropriate position + and ratio changed signals. + Will be called by updateBeginningEnd. +*/ void QDeclarativeFlickableVisibleArea::updateVisible() { QDeclarativeFlickablePrivate *p = static_cast(QGraphicsItemPrivate::get(flickable)); @@ -90,9 +93,11 @@ void QDeclarativeFlickableVisibleArea::updateVisible() // Vertical const qreal viewheight = flickable->height(); - const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent(); - qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight); - qreal pageSize = viewheight / (maxyextent + viewheight); + const qreal maxyextent = flickable->maxYExtent(); + qreal pagePos = (p->contentItem->pos().y() + flickable->minYExtent()) / maxyextent; + qreal pageSize = viewheight / maxyextent; + + // qDebug() << "updateVisible" << viewheight << "ext:" << flickable->minYExtent() <<"-"<maxYExtent() << "pagesize:"<width(); - const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent(); - pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth); - pageSize = viewwidth / (maxxextent + viewwidth); + const qreal maxxextent = flickable->maxXExtent(); + pagePos = (p->contentItem->pos().x() + flickable->minXExtent()) / maxxextent; + pageSize = viewwidth / maxxextent; if (pageSize != m_widthRatio) { m_widthRatio = pageSize; @@ -131,15 +136,19 @@ void QDeclarativeFlickableVisibleArea::updateVisible() QDeclarativeFlickablePrivate::QDeclarativeFlickablePrivate() : contentItem(new QDeclarativeItem) - , hData(this, &QDeclarativeFlickablePrivate::setRoundedViewportX) - , vData(this, &QDeclarativeFlickablePrivate::setRoundedViewportY) - , flickingHorizontally(false), flickingVertically(false) - , hMoved(false), vMoved(false) - , movingHorizontally(false), movingVertically(false) - , stealMouse(false), pressed(false), interactive(true), calcVelocity(false) - , deceleration(500), maxVelocity(2000), reportedVelocitySmoothing(100) - , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(600) - , vTime(0), visibleArea(0) + , updateScrollerValuesRequested(false) + , duringScrollEvent(false) + , isUserGenerated(false) + , isFlicking(false) + , isMoving(false) + , movingHorizontally(false) + , movingVertically(false) + , atBeginningX(true) + , atBeginningY(true) + , atEndX(false) + , atEndY(false) + , interactive(true) + , visibleArea(0) , flickableDirection(QDeclarativeFlickable::AutoFlickDirection) , boundsBehavior(QDeclarativeFlickable::DragAndOvershootBounds) { @@ -150,45 +159,39 @@ void QDeclarativeFlickablePrivate::init() Q_Q(QDeclarativeFlickable); QDeclarative_setParent_noEvent(contentItem, q); contentItem->setParentItem(q); - static int timelineUpdatedIdx = -1; - static int timelineCompletedIdx = -1; - static int flickableTickedIdx = -1; - static int flickableMovementEndingIdx = -1; - if (timelineUpdatedIdx == -1) { - timelineUpdatedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("updated()"); - timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()"); - flickableTickedIdx = QDeclarativeFlickable::staticMetaObject.indexOfSlot("ticked()"); - flickableMovementEndingIdx = QDeclarativeFlickable::staticMetaObject.indexOfSlot("movementEnding()"); - } - QMetaObject::connect(&timeline, timelineUpdatedIdx, - q, flickableTickedIdx, Qt::DirectConnection); - QMetaObject::connect(&timeline, timelineCompletedIdx, - q, flickableMovementEndingIdx, Qt::DirectConnection); + q->setAcceptedMouseButtons(Qt::LeftButton); - q->setFiltersChildEvents(true); - QDeclarativeItemPrivate *viewportPrivate = static_cast(QGraphicsItemPrivate::get(contentItem)); - viewportPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); - lastPosTime.invalidate(); + q->setAcceptTouchEvents(true); + + QScroller::grabGesture(q, QScroller::LeftMouseButtonGesture); + QScroller *scroller = QScroller::scroller(q); + + QScrollerProperties sp = scroller->scrollerProperties(); + sp.setScrollMetric(QScrollerProperties::MousePressEventDelay, 0); + sp.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 1.0); + sp.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 1.0); + scroller->setScrollerProperties(sp); + + QObject::connect(scroller, SIGNAL(stateChanged(QScroller::State)), q, SLOT(scrollerStateChanged(QScroller::State))); } -/* - Returns the amount to overshoot by given a velocity. - Will be roughly in range 0 - size/4 +/*! /internal + Requests a prepare event to be send to the scroller informing it about changed + positions or scroll area bounds. */ -qreal QDeclarativeFlickablePrivate::overShootDistance(qreal velocity, qreal size) +void QDeclarativeFlickablePrivate::updateScrollerValues() { - if (maxVelocity <= 0) - return 0.0; - - velocity = qAbs(velocity); - if (velocity > maxVelocity) - velocity = maxVelocity; - qreal dist = size / 4 * velocity / maxVelocity; - return dist; + Q_Q(QDeclarativeFlickable); + //qDebug() << "DeclarativeFlickablePrivate::updateScrollerValues" << duringScrollEvent; + if (!duringScrollEvent) + QScroller::scroller(q)->resendPrepareEvent(); + else + updateScrollerValuesRequested = true; } void QDeclarativeFlickablePrivate::itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeom, const QRectF &oldGeom) { + // TODO Q_Q(QDeclarativeFlickable); if (item == contentItem) { if (newGeom.x() != oldGeom.x()) @@ -198,149 +201,41 @@ void QDeclarativeFlickablePrivate::itemGeometryChanged(QDeclarativeItem *item, c } } -void QDeclarativeFlickablePrivate::flickX(qreal velocity) -{ - Q_Q(QDeclarativeFlickable); - flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity); -} - -void QDeclarativeFlickablePrivate::flickY(qreal velocity) -{ - Q_Q(QDeclarativeFlickable); - flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity); -} - -void QDeclarativeFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) -{ - Q_Q(QDeclarativeFlickable); - qreal maxDistance = -1; - bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds; - // -ve velocity means list is moving up - if (velocity > 0) { - if (data.move.value() < minExtent) - maxDistance = qAbs(minExtent - data.move.value() + (overShoot?overShootDistance(velocity,vSize):0)); - data.flickTarget = minExtent; - } else { - if (data.move.value() > maxExtent) - maxDistance = qAbs(maxExtent - data.move.value()) + (overShoot?overShootDistance(velocity,vSize):0); - data.flickTarget = maxExtent; - } - if (maxDistance > 0) { - qreal v = velocity; - if (maxVelocity != -1 && maxVelocity < qAbs(v)) { - if (v < 0) - v = -maxVelocity; - else - v = maxVelocity; - } - timeline.reset(data.move); - timeline.accel(data.move, v, deceleration, maxDistance); - timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); - if (!flickingHorizontally && q->xflick()) { - flickingHorizontally = true; - emit q->flickingChanged(); - emit q->flickingHorizontallyChanged(); - if (!flickingVertically) - emit q->flickStarted(); - } - if (!flickingVertically && q->yflick()) { - flickingVertically = true; - emit q->flickingChanged(); - emit q->flickingVerticallyChanged(); - if (!flickingHorizontally) - emit q->flickStarted(); - } - } else { - timeline.reset(data.move); - fixup(data, minExtent, maxExtent); - } -} - -void QDeclarativeFlickablePrivate::fixupY_callback(void *data) -{ - ((QDeclarativeFlickablePrivate *)data)->fixupY(); -} - -void QDeclarativeFlickablePrivate::fixupX_callback(void *data) -{ - ((QDeclarativeFlickablePrivate *)data)->fixupX(); -} - -void QDeclarativeFlickablePrivate::fixupX() -{ - Q_Q(QDeclarativeFlickable); - fixup(hData, q->minXExtent(), q->maxXExtent()); -} - -void QDeclarativeFlickablePrivate::fixupY() +/*! \internal + Updates the atBeginning and atEnd flags and sends out the changed signals +*/ +void QDeclarativeFlickablePrivate::updateBeginningEnd() { Q_Q(QDeclarativeFlickable); - fixup(vData, q->minYExtent(), q->maxYExtent()); -} -void QDeclarativeFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) -{ - if (data.move.value() > minExtent || maxExtent > minExtent) { - timeline.reset(data.move); - if (data.move.value() != minExtent) { - if (fixupDuration) { - qreal dist = minExtent - data.move; - timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); - timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); - } else { - timeline.set(data.move, minExtent); - } - } - } else if (data.move.value() < maxExtent) { - timeline.reset(data.move); - if (fixupDuration) { - qreal dist = maxExtent - data.move; - timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); - timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); - } else { - timeline.set(data.move, minExtent); - } - } - vTime = timeline.time(); -} + bool boundaryChanged = false; -void QDeclarativeFlickablePrivate::updateBeginningEnd() -{ - Q_Q(QDeclarativeFlickable); - bool atBoundaryChange = false; + bool newAtBeginningX = -contentItem->pos().x() <= q->minXExtent(); + bool newAtEndX = -contentItem->pos().x() >= (q->maxXExtent() - width()); + bool newAtBeginningY = -contentItem->pos().y() <= q->minYExtent(); + bool newAtEndY = -contentItem->pos().y() >= (q->maxYExtent() - height()); - // Vertical - const int maxyextent = int(-q->maxYExtent()); - const qreal ypos = -vData.move.value(); - bool atBeginning = (ypos <= -q->minYExtent()); - bool atEnd = (maxyextent <= ypos); - - if (atBeginning != vData.atBeginning) { - vData.atBeginning = atBeginning; - atBoundaryChange = true; + // Horizontal + if (newAtBeginningX != atBeginningX) { + atBeginningX = newAtBeginningX; + boundaryChanged = true; } - if (atEnd != vData.atEnd) { - vData.atEnd = atEnd; - atBoundaryChange = true; + if (newAtEndX != atEndX) { + atEndX = newAtEndX; + boundaryChanged = true; } - // Horizontal - const int maxxextent = int(-q->maxXExtent()); - const qreal xpos = -hData.move.value(); - atBeginning = (xpos <= -q->minXExtent()); - atEnd = (maxxextent <= xpos); - - if (atBeginning != hData.atBeginning) { - hData.atBeginning = atBeginning; - atBoundaryChange = true; + // Vertical + if (newAtBeginningY != atBeginningY) { + atBeginningY = newAtBeginningY; + boundaryChanged = true; } - if (atEnd != hData.atEnd) { - hData.atEnd = atEnd; - atBoundaryChange = true; + if (newAtEndY != atEndY) { + atEndY = newAtEndY; + boundaryChanged = true; } - if (atBoundaryChange) + if (boundaryChanged) emit q->isAtBoundaryChanged(); if (visibleArea) @@ -484,13 +379,14 @@ qreal QDeclarativeFlickable::contentX() const void QDeclarativeFlickable::setContentX(qreal pos) { Q_D(QDeclarativeFlickable); - d->timeline.reset(d->hData.move); - d->vTime = d->timeline.time(); - movementXEnding(); - if (-pos != d->hData.move.value()) { - d->hData.move.setValue(-pos); - viewportMoved(); - } + QScroller::scroller(this)->stop(); + + if (pos == -d->contentItem->x()) + return; + viewportAboutToMove(QPointF(pos, contentY())); + d->contentItem->setX(-pos); + viewportMoved(); + emit contentXChanged(); } qreal QDeclarativeFlickable::contentY() const @@ -502,13 +398,14 @@ qreal QDeclarativeFlickable::contentY() const void QDeclarativeFlickable::setContentY(qreal pos) { Q_D(QDeclarativeFlickable); - d->timeline.reset(d->vData.move); - d->vTime = d->timeline.time(); - movementYEnding(); - if (-pos != d->vData.move.value()) { - d->vData.move.setValue(-pos); - viewportMoved(); - } + QScroller::scroller(this)->stop(); + + if (pos == -d->contentItem->y()) + return; + viewportAboutToMove(QPointF(contentX(), pos)); + d->contentItem->setY(-pos); + viewportMoved(); + emit contentYChanged(); } /*! @@ -535,16 +432,7 @@ void QDeclarativeFlickable::setInteractive(bool interactive) Q_D(QDeclarativeFlickable); if (interactive != d->interactive) { d->interactive = interactive; - if (!interactive && (d->flickingHorizontally || d->flickingVertically)) { - d->timeline.clear(); - d->vTime = d->timeline.time(); - d->flickingHorizontally = false; - d->flickingVertically = false; - emit flickingChanged(); - emit flickingHorizontallyChanged(); - emit flickingVerticallyChanged(); - emit flickEnded(); - } + QScroller::scroller(this)->stop(); emit interactiveChanged(); } } @@ -554,19 +442,15 @@ void QDeclarativeFlickable::setInteractive(bool interactive) \qmlproperty real Flickable::verticalVelocity The instantaneous velocity of movement along the x and y axes, in pixels/sec. - - The reported velocity is smoothed to avoid erratic output. */ qreal QDeclarativeFlickable::horizontalVelocity() const { - Q_D(const QDeclarativeFlickable); - return d->hData.smoothVelocity.value(); + return QScroller::scroller(this)->velocity().x(); } qreal QDeclarativeFlickable::verticalVelocity() const { - Q_D(const QDeclarativeFlickable); - return d->vData.smoothVelocity.value(); + return QScroller::scroller(this)->velocity().y(); } /*! @@ -581,30 +465,25 @@ qreal QDeclarativeFlickable::verticalVelocity() const bool QDeclarativeFlickable::isAtXEnd() const { Q_D(const QDeclarativeFlickable); - return d->hData.atEnd; + return d->atEndX; } bool QDeclarativeFlickable::isAtXBeginning() const { Q_D(const QDeclarativeFlickable); - return d->hData.atBeginning; + return d->atBeginningX; } bool QDeclarativeFlickable::isAtYEnd() const { Q_D(const QDeclarativeFlickable); - return d->vData.atEnd; + return d->atEndY; } bool QDeclarativeFlickable::isAtYBeginning() const { Q_D(const QDeclarativeFlickable); - return d->vData.atBeginning; -} - -void QDeclarativeFlickable::ticked() -{ - viewportMoved(); + return d->atBeginningY; } /*! @@ -639,6 +518,52 @@ QDeclarativeFlickableVisibleArea *QDeclarativeFlickable::visibleArea() return d->visibleArea; } +void QDeclarativeFlickable::scrollerStateChanged(QScroller::State state) +{ + Q_D(QDeclarativeFlickable); + + //qDebug() << "scrollerStateChanged" << int(state); + + switch (state) { + case QScroller::Inactive: + d->isUserGenerated = false; + // fall through + case QScroller::Pressed: + if (d->isMoving) { + d->isMoving = false; + emit movingChanged(); + emit movementEnded(); + } + if (d->isFlicking) { + d->isFlicking = false; + emit flickingChanged(); + emit flickEnded(); + } + break; + + case QScroller::Dragging: + d->isUserGenerated = true; + if (!d->isMoving) { + d->isMoving = true; + emit movingChanged(); + emit movementStarted(); + } + break; + + case QScroller::Scrolling: + QScroller::scroller(this)->resendPrepareEvent(); // sometimes snappoints depend on the scrolling direction. + if (d->isUserGenerated) { + if (!d->isFlicking) { + emit flickStarted(); + emit flickingChanged(); + } + d->isFlicking = true; + } + break; + } +} + + /*! \qmlproperty enumeration Flickable::flickableDirection @@ -669,409 +594,92 @@ void QDeclarativeFlickable::setFlickableDirection(FlickableDirection direction) } } -void QDeclarativeFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event) -{ - if (interactive && timeline.isActive() && (qAbs(hData.velocity) > 10 || qAbs(vData.velocity) > 10)) - stealMouse = true; // If we've been flicked then steal the click. - else - stealMouse = false; - pressed = true; - timeline.clear(); - hData.velocity = 0; - vData.velocity = 0; - hData.dragStartOffset = 0; - vData.dragStartOffset = 0; - lastPos = QPoint(); - QDeclarativeItemPrivate::start(lastPosTime); - pressPos = event->pos(); - hData.pressPos = hData.move.value(); - vData.pressPos = vData.move.value(); - flickingHorizontally = false; - flickingVertically = false; - QDeclarativeItemPrivate::start(pressTime); - QDeclarativeItemPrivate::start(velocityTime); -} - -void QDeclarativeFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event) -{ - Q_Q(QDeclarativeFlickable); - if (!interactive || !lastPosTime.isValid()) - return; - bool rejectY = false; - bool rejectX = false; - - bool stealY = false; - bool stealX = false; - - if (q->yflick()) { - int dy = int(event->pos().y() - pressPos.y()); - if (qAbs(dy) > QApplication::startDragDistance() || QDeclarativeItemPrivate::elapsed(pressTime) > 200) { - if (!vMoved) - vData.dragStartOffset = dy; - qreal newY = dy + vData.pressPos - vData.dragStartOffset; - const qreal minY = q->minYExtent(); - const qreal maxY = q->maxYExtent(); - if (newY > minY) - newY = minY + (newY - minY) / 2; - if (newY < maxY && maxY - minY <= 0) - newY = maxY + (newY - maxY) / 2; - if (boundsBehavior == QDeclarativeFlickable::StopAtBounds && (newY > minY || newY < maxY)) { - rejectY = true; - if (newY < maxY) { - newY = maxY; - rejectY = false; - } - if (newY > minY) { - newY = minY; - rejectY = false; - } - } - if (!rejectY && stealMouse) { - vData.move.setValue(qRound(newY)); - vMoved = true; - } - if (qAbs(dy) > QApplication::startDragDistance()) - stealY = true; - } - } - - if (q->xflick()) { - int dx = int(event->pos().x() - pressPos.x()); - if (qAbs(dx) > QApplication::startDragDistance() || QDeclarativeItemPrivate::elapsed(pressTime) > 200) { - if (!hMoved) - hData.dragStartOffset = dx; - qreal newX = dx + hData.pressPos - hData.dragStartOffset; - const qreal minX = q->minXExtent(); - const qreal maxX = q->maxXExtent(); - if (newX > minX) - newX = minX + (newX - minX) / 2; - if (newX < maxX && maxX - minX <= 0) - newX = maxX + (newX - maxX) / 2; - if (boundsBehavior == QDeclarativeFlickable::StopAtBounds && (newX > minX || newX < maxX)) { - rejectX = true; - if (newX < maxX) { - newX = maxX; - rejectX = false; - } - if (newX > minX) { - newX = minX; - rejectX = false; - } - } - if (!rejectX && stealMouse) { - hData.move.setValue(qRound(newX)); - hMoved = true; - } - - if (qAbs(dx) > QApplication::startDragDistance()) - stealX = true; - } - } - - stealMouse = stealX || stealY; - - if (!lastPos.isNull()) { - qreal elapsed = qreal(QDeclarativeItemPrivate::restart(lastPosTime)) / 1000.; - if (elapsed <= 0) - elapsed = 1; - if (q->yflick()) { - qreal diff = event->pos().y() - lastPos.y(); - // average to reduce the effect of spurious moves - vData.velocity += diff / elapsed; - vData.velocity /= 2; - } - - if (q->xflick()) { - qreal diff = event->pos().x() - lastPos.x(); - // average to reduce the effect of spurious moves - hData.velocity += diff / elapsed; - hData.velocity /= 2; - } - } - - if (rejectY) vData.velocity = 0; - if (rejectX) hData.velocity = 0; - - if (hMoved || vMoved) { - q->movementStarting(); - q->viewportMoved(); - } - - lastPos = event->pos(); -} - -void QDeclarativeFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event) -{ - Q_Q(QDeclarativeFlickable); - stealMouse = false; - q->setKeepMouseGrab(false); - pressed = false; - if (!lastPosTime.isValid()) - return; - - if (QDeclarativeItemPrivate::elapsed(lastPosTime) > 100) { - // if we drag then pause before release we should not cause a flick. - hData.velocity = 0.0; - vData.velocity = 0.0; - } - - vTime = timeline.time(); - if (qAbs(vData.velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold) - flickY(vData.velocity); - else - fixupY(); - - if (qAbs(hData.velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold) - flickX(hData.velocity); - else - fixupX(); - - lastPosTime.invalidate(); - - if (!timeline.isActive()) - q->movementEnding(); -} - -void QDeclarativeFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event) -{ - Q_D(QDeclarativeFlickable); - if (d->interactive) { - d->handleMousePressEvent(event); - event->accept(); - } else { - QDeclarativeItem::mousePressEvent(event); - } -} - -void QDeclarativeFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event) -{ - Q_D(QDeclarativeFlickable); - if (d->interactive) { - d->handleMouseMoveEvent(event); - if (d->stealMouse) - setKeepMouseGrab(true); - event->accept(); - } else { - QDeclarativeItem::mouseMoveEvent(event); - } -} - -void QDeclarativeFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) -{ - Q_D(QDeclarativeFlickable); - if (d->interactive) { - d->clearDelayedPress(); - d->handleMouseReleaseEvent(event); - event->accept(); - ungrabMouse(); - } else { - QDeclarativeItem::mouseReleaseEvent(event); - } -} - -void QDeclarativeFlickable::wheelEvent(QGraphicsSceneWheelEvent *event) -{ - Q_D(QDeclarativeFlickable); - if (!d->interactive) { - QDeclarativeItem::wheelEvent(event); - } else if (yflick()) { - if (event->delta() > 0) - d->vData.velocity = qMax(event->delta() - d->vData.smoothVelocity.value(), qreal(250.0)); - else - d->vData.velocity = qMin(event->delta() - d->vData.smoothVelocity.value(), qreal(-250.0)); - d->flickingVertically = false; - d->flickY(d->vData.velocity); - if (d->flickingVertically) { - d->vMoved = true; - movementStarting(); - } - event->accept(); - } else if (xflick()) { - if (event->delta() > 0) - d->hData.velocity = qMax(event->delta() - d->hData.smoothVelocity.value(), qreal(250.0)); - else - d->hData.velocity = qMin(event->delta() - d->hData.smoothVelocity.value(), qreal(-250.0)); - d->flickingHorizontally = false; - d->flickX(d->hData.velocity); - if (d->flickingHorizontally) { - d->hMoved = true; - movementStarting(); - } - event->accept(); - } else { - QDeclarativeItem::wheelEvent(event); - } -} - -void QDeclarativeFlickablePrivate::captureDelayedPress(QGraphicsSceneMouseEvent *event) -{ - Q_Q(QDeclarativeFlickable); - if (!q->scene() || pressDelay <= 0) - return; - delayedPressTarget = q->scene()->mouseGrabberItem(); - delayedPressEvent = new QGraphicsSceneMouseEvent(event->type()); - delayedPressEvent->setAccepted(false); - for (int i = 0x1; i <= 0x10; i <<= 1) { - if (event->buttons() & i) { - Qt::MouseButton button = Qt::MouseButton(i); - delayedPressEvent->setButtonDownPos(button, event->buttonDownPos(button)); - delayedPressEvent->setButtonDownScenePos(button, event->buttonDownScenePos(button)); - delayedPressEvent->setButtonDownScreenPos(button, event->buttonDownScreenPos(button)); - } - } - delayedPressEvent->setButtons(event->buttons()); - delayedPressEvent->setButton(event->button()); - delayedPressEvent->setPos(event->pos()); - delayedPressEvent->setScenePos(event->scenePos()); - delayedPressEvent->setScreenPos(event->screenPos()); - delayedPressEvent->setLastPos(event->lastPos()); - delayedPressEvent->setLastScenePos(event->lastScenePos()); - delayedPressEvent->setLastScreenPos(event->lastScreenPos()); - delayedPressEvent->setModifiers(event->modifiers()); - delayedPressTimer.start(pressDelay, q); -} - -void QDeclarativeFlickablePrivate::clearDelayedPress() -{ - if (delayedPressEvent) { - delayedPressTimer.stop(); - delete delayedPressEvent; - delayedPressEvent = 0; - } -} - -void QDeclarativeFlickablePrivate::setRoundedViewportX(qreal x) -{ - contentItem->setX(qRound(x)); -} - -void QDeclarativeFlickablePrivate::setRoundedViewportY(qreal y) -{ - contentItem->setY(qRound(y)); -} - -void QDeclarativeFlickable::timerEvent(QTimerEvent *event) +/*! \internal + Returns the minimum horizontal content position. + Note: this function will be overloaded. + */ +qreal QDeclarativeFlickable::minXExtent() const { - Q_D(QDeclarativeFlickable); - if (event->timerId() == d->delayedPressTimer.timerId()) { - d->delayedPressTimer.stop(); - if (d->delayedPressEvent) { - QDeclarativeItem *grabber = scene() ? qobject_cast(scene()->mouseGrabberItem()) : 0; - if (!grabber || grabber != this) { - // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay) - // so we reset the grabber - if (scene()->mouseGrabberItem() == d->delayedPressTarget) - d->delayedPressTarget->ungrabMouse(); - //Use the event handler that will take care of finding the proper item to propagate the event - QApplication::sendEvent(scene(), d->delayedPressEvent); - } - delete d->delayedPressEvent; - d->delayedPressEvent = 0; - } - } + return 0.0; } +/*! \internal + Returns the minimum vertical content position. + Note: this function will be overloaded. + */ qreal QDeclarativeFlickable::minYExtent() const { return 0.0; } - -qreal QDeclarativeFlickable::minXExtent() const +/*! \internal + Returns the maximum horizontal content position. + */ +qreal QDeclarativeFlickable::maxXExtent() const { - return 0.0; + Q_D(const QDeclarativeFlickable); + return qMax(0.0, d->contentItem->width()); } -/* returns -ve */ -qreal QDeclarativeFlickable::maxXExtent() const +/*! \internal + Returns the maximum vertical content position. + */ +qreal QDeclarativeFlickable::maxYExtent() const { - return width() - vWidth(); + Q_D(const QDeclarativeFlickable); + return qMax(0.0, d->contentItem->height()); } -/* returns -ve */ -qreal QDeclarativeFlickable::maxYExtent() const + +/*! \internal + This function is called before the viewport starts to move. + This is a good chance to fill up some buffers. + */ +void QDeclarativeFlickable::viewportAboutToMove(QPointF newPos) { - return height() - vHeight(); + Q_UNUSED(newPos); } +/*! \internal + This function is called after the viewport was moved (and the + move finished) + */ void QDeclarativeFlickable::viewportMoved() { Q_D(QDeclarativeFlickable); - - qreal prevX = d->lastFlickablePosition.x(); - qreal prevY = d->lastFlickablePosition.y(); - d->velocityTimeline.clear(); - if (d->pressed || d->calcVelocity) { - int elapsed = QDeclarativeItemPrivate::restart(d->velocityTime); - if (elapsed > 0) { - qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed; - qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed; - d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing); - d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing); - d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing); - d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing); - } - } else { - if (d->timeline.time() > d->vTime) { - qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime); - qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime); - d->hData.smoothVelocity.setValue(horizontalVelocity); - d->vData.smoothVelocity.setValue(verticalVelocity); - } - } - - d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value()); - - d->vTime = d->timeline.time(); d->updateBeginningEnd(); } void QDeclarativeFlickable::geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry) + const QRectF &oldGeometry) { Q_D(QDeclarativeFlickable); QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); bool changed = false; if (newGeometry.width() != oldGeometry.width()) { - if (xflick()) - changed = true; - if (d->hData.viewSize < 0) { - d->contentItem->setWidth(width()); - emit contentWidthChanged(); - } - // Make sure that we're entirely in view. - if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { - int oldDuration = d->fixupDuration; - d->fixupDuration = 0; - d->fixupX(); - d->fixupDuration = oldDuration; - } + changed = true; + // strangely this causes problems + // d->contentItem->setWidth(newGeometry.width()); + emit contentWidthChanged(); } if (newGeometry.height() != oldGeometry.height()) { - if (yflick()) - changed = true; - if (d->vData.viewSize < 0) { - d->contentItem->setHeight(height()); - emit contentHeightChanged(); - } - // Make sure that we're entirely in view. - if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { - int oldDuration = d->fixupDuration; - d->fixupDuration = 0; - d->fixupY(); - d->fixupDuration = oldDuration; - } + changed = true; + // strangely this causes problems + // d->contentItem->setHeight(newGeometry.height()); + emit contentHeightChanged(); } - if (changed) - d->updateBeginningEnd(); -} + if (changed) { + QScroller *scroller = QScroller::scroller(this); -void QDeclarativeFlickable::cancelFlick() -{ - Q_D(QDeclarativeFlickable); - d->timeline.reset(d->hData.move); - d->timeline.reset(d->vData.move); - movementEnding(); + //qDebug() << "xxx geometryChanged: "<state() == QScroller::Inactive) + scroller->ensureVisible( QRectF(0.0, 0.0, + d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); + else + d->updateScrollerValues(); + d->updateBeginningEnd(); + } } void QDeclarativeFlickablePrivate::data_append(QDeclarativeListProperty *prop, QObject *o) @@ -1157,10 +765,12 @@ QDeclarativeListProperty QDeclarativeFlickable::flickableChildr of the flickable, and flicks will not overshoot. \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary of the Flickable, but flicks will not overshoot. - \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged + \o Flickable.DragAndOvershootBounds - the contents can be dragged beyond the boundary of the Flickable, and can overshoot the boundary when flicked. \endlist + + The default is platform dependant. */ QDeclarativeFlickable::BoundsBehavior QDeclarativeFlickable::boundsBehavior() const { @@ -1174,6 +784,23 @@ void QDeclarativeFlickable::setBoundsBehavior(BoundsBehavior b) if (b == d->boundsBehavior) return; d->boundsBehavior = b; + + QScroller *scroller = QScroller::scroller(this); + QScrollerProperties props = scroller->scrollerProperties(); + if (b == StopAtBounds) { + props.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.0); + props.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.0); + + } else if (b == DragOverBounds) { + props.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 1.0); + props.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.0); + + } else { + props.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 1.0); + props.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 1.0); + } + scroller->setScrollerProperties(props); + emit boundsBehaviorChanged(); } @@ -1197,26 +824,27 @@ void QDeclarativeFlickable::setBoundsBehavior(BoundsBehavior b) qreal QDeclarativeFlickable::contentWidth() const { Q_D(const QDeclarativeFlickable); - return d->hData.viewSize; + return d->contentItem->width(); } void QDeclarativeFlickable::setContentWidth(qreal w) { Q_D(QDeclarativeFlickable); - if (d->hData.viewSize == w) + if (d->contentItem->width() == w) return; - d->hData.viewSize = w; + if (w < 0) d->contentItem->setWidth(width()); else d->contentItem->setWidth(w); + // Make sure that we're entirely in view. - if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { - int oldDuration = d->fixupDuration; - d->fixupDuration = 0; - d->fixupX(); - d->fixupDuration = oldDuration; - } + QScroller *scroller = QScroller::scroller(this); + if (scroller->state() == QScroller::Inactive) + scroller->ensureVisible( QRectF(0.0, 0.0, d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); + else + d->updateScrollerValues(); + emit contentWidthChanged(); d->updateBeginningEnd(); } @@ -1224,170 +852,142 @@ void QDeclarativeFlickable::setContentWidth(qreal w) qreal QDeclarativeFlickable::contentHeight() const { Q_D(const QDeclarativeFlickable); - return d->vData.viewSize; + return d->contentItem->height(); } void QDeclarativeFlickable::setContentHeight(qreal h) { Q_D(QDeclarativeFlickable); - if (d->vData.viewSize == h) + if (d->contentItem->height() == h) return; - d->vData.viewSize = h; + if (h < 0) d->contentItem->setHeight(height()); else d->contentItem->setHeight(h); + // Make sure that we're entirely in view. - if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { - int oldDuration = d->fixupDuration; - d->fixupDuration = 0; - d->fixupY(); - d->fixupDuration = oldDuration; - } + QScroller *scroller = QScroller::scroller(this); + if (scroller->state() == QScroller::Inactive) { + scroller->ensureVisible( QRectF(minXExtent(), minYExtent(), + d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); + } else + d->updateScrollerValues(); + emit contentHeightChanged(); d->updateBeginningEnd(); } -qreal QDeclarativeFlickable::vWidth() const +bool QDeclarativeFlickable::event(QEvent *event) { - Q_D(const QDeclarativeFlickable); - if (d->hData.viewSize < 0) - return width(); - else - return d->hData.viewSize; -} + Q_D(QDeclarativeFlickable); -qreal QDeclarativeFlickable::vHeight() const -{ - Q_D(const QDeclarativeFlickable); - if (d->vData.viewSize < 0) - return height(); - else - return d->vData.viewSize; -} + switch (event->type()) { + case QEvent::ScrollPrepare: { + if (!d->interactive) + return false; + + QScrollPrepareEvent *se = static_cast(event); + se->setViewportSize(QSizeF(d->width(), d->height())); + + QRectF contentPosRange(QPointF(minXExtent(), minYExtent()), + QPointF(maxXExtent() - d->width(), maxYExtent() - d->height())); + if (d->flickableDirection == HorizontalFlick) + contentPosRange.setHeight(0); + if (d->flickableDirection == VerticalFlick) + contentPosRange.setWidth(0); + se->setContentPosRange(contentPosRange); + se->setContentPos(-d->contentItem->pos()); + + //qDebug() << "QDeclarativeFlickable::event: ScPrEv " << this + // << " pos:" << se->contentPos() + // << " range:" << se->contentPosRange(); + + se->accept(); + d->updateScrollerValuesRequested = false; + return true; + } + case QEvent::Scroll: { + d->duringScrollEvent = true; -bool QDeclarativeFlickable::xflick() const -{ - Q_D(const QDeclarativeFlickable); - if (d->flickableDirection == QDeclarativeFlickable::AutoFlickDirection) - return vWidth() != width(); - return d->flickableDirection & QDeclarativeFlickable::HorizontalFlick; -} + QScrollEvent *se = static_cast(event); -bool QDeclarativeFlickable::yflick() const -{ - Q_D(const QDeclarativeFlickable); - if (d->flickableDirection == QDeclarativeFlickable::AutoFlickDirection) - return vHeight() != height(); - return d->flickableDirection & QDeclarativeFlickable::VerticalFlick; -} + /* + qDebug() << "QDeclarativeFlickable::event: Scroll " + << " scrollerPos:" << se->contentPos() + << " pos:" << -d->contentItem->pos() + << " overshoot:" << se->overshootDistance() + << " newPos: " << (se->contentPos() + se->overshootDistance()); + */ -bool QDeclarativeFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event) -{ - Q_D(QDeclarativeFlickable); - QGraphicsSceneMouseEvent mouseEvent(event->type()); - QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); - - QGraphicsScene *s = scene(); - QDeclarativeItem *grabber = s ? qobject_cast(s->mouseGrabberItem()) : 0; - bool stealThisEvent = d->stealMouse; - if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) { - mouseEvent.setAccepted(false); - for (int i = 0x1; i <= 0x10; i <<= 1) { - if (event->buttons() & i) { - Qt::MouseButton button = Qt::MouseButton(i); - mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button))); - } - } - mouseEvent.setScenePos(event->scenePos()); - mouseEvent.setLastScenePos(event->lastScenePos()); - mouseEvent.setPos(mapFromScene(event->scenePos())); - mouseEvent.setLastPos(mapFromScene(event->lastScenePos())); - - switch(mouseEvent.type()) { - case QEvent::GraphicsSceneMouseMove: - d->handleMouseMoveEvent(&mouseEvent); - break; - case QEvent::GraphicsSceneMousePress: - if (d->delayedPressEvent) - return false; - - d->handleMousePressEvent(&mouseEvent); - d->captureDelayedPress(event); - stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above - break; - case QEvent::GraphicsSceneMouseRelease: - if (d->delayedPressEvent) { - // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay) - // so we reset the grabber - if (s->mouseGrabberItem() == d->delayedPressTarget) - d->delayedPressTarget->ungrabMouse(); - //Use the event handler that will take care of finding the proper item to propagate the event - QApplication::sendEvent(scene(), d->delayedPressEvent); - d->clearDelayedPress(); - // We send the release - scene()->sendEvent(s->mouseGrabberItem(), event); - // And the event has been consumed - return true; - } - d->handleMouseReleaseEvent(&mouseEvent); - break; - default: - break; - } - grabber = qobject_cast(s->mouseGrabberItem()); - if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) { - d->clearDelayedPress(); - grabMouse(); - } + QPointF oldPos = -d->contentItem->pos(); + QPointF newPos = se->contentPos() + se->overshootDistance(); + QPointF delta = newPos - oldPos; - return stealThisEvent || d->delayedPressEvent; - } else if (d->lastPosTime.isValid()) { - d->lastPosTime.invalidate(); - } - if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) { - d->clearDelayedPress(); - d->stealMouse = false; - d->pressed = false; + viewportAboutToMove(newPos); + d->contentItem->setPos(-newPos); + viewportMoved(); + if (delta.x()) + emit contentXChanged(); + if (delta.y()) + emit contentYChanged(); + + d->duringScrollEvent = false; + if (d->updateScrollerValuesRequested) + QScroller::scroller(this)->resendPrepareEvent(); + + bool newMovingHorizontally = delta.x() != 0; + bool newMovingVertically = delta.y() != 0; + if (se->scrollState() == QScrollEvent::ScrollFinished) { + newMovingHorizontally = false; + newMovingVertically = false; + } + + if (newMovingHorizontally != d->movingHorizontally) { + d->movingHorizontally = newMovingHorizontally; + emit movingHorizontallyChanged(); + } + if (newMovingVertically != d->movingVertically) { + d->movingVertically = newMovingVertically; + emit movingVerticallyChanged(); + } + + if (newMovingHorizontally) + emit horizontalVelocityChanged(); + if (newMovingVertically) + emit verticalVelocityChanged(); + + se->accept(); + return true; } - return false; -} - -bool QDeclarativeFlickable::sceneEventFilter(QGraphicsItem *i, QEvent *e) -{ - Q_D(QDeclarativeFlickable); - if (!isVisible() || !d->interactive) - return QDeclarativeItem::sceneEventFilter(i, e); - switch (e->type()) { - case QEvent::GraphicsSceneMousePress: - case QEvent::GraphicsSceneMouseMove: - case QEvent::GraphicsSceneMouseRelease: - return sendMouseEvent(static_cast(e)); default: break; } - - return QDeclarativeItem::sceneEventFilter(i, e); + return QDeclarativeItem::event(event); } /*! \qmlproperty real Flickable::maximumFlickVelocity - This property holds the maximum velocity that the user can flick the view in pixels/second. + This property holds the maximum velocity that the user can flick the view in pixel per second - The default is 2000 pixels/s + The default is platform dependant. */ qreal QDeclarativeFlickable::maximumFlickVelocity() const { - Q_D(const QDeclarativeFlickable); - return d->maxVelocity; + // 4000 ~ the conversion factor from [m/s] to [pix/s] on a 100dpi screen + return 4000 * QScroller::scroller(const_cast(this))->scrollerProperties().scrollMetric(QScrollerProperties::MaximumVelocity).toDouble(); } void QDeclarativeFlickable::setMaximumFlickVelocity(qreal v) { - Q_D(QDeclarativeFlickable); - if (v == d->maxVelocity) + // 4000 ~ the conversion factor from [m/s] to [pix/s] on a 100dpi screen + v /= qreal(4000); + QScrollerProperties props = QScroller::scroller(this)->scrollerProperties(); + qreal oldV = props.scrollMetric(QScrollerProperties::MaximumVelocity).toReal(); + if (v == oldV) return; - d->maxVelocity = v; + props.setScrollMetric(QScrollerProperties::MaximumVelocity, v); + QScroller::scroller(this)->setScrollerProperties(props); emit maximumFlickVelocityChanged(); } @@ -1395,27 +995,31 @@ void QDeclarativeFlickable::setMaximumFlickVelocity(qreal v) \qmlproperty real Flickable::flickDeceleration This property holds the rate at which a flick will decelerate. - The default is 500. + The default is platform dependant. */ qreal QDeclarativeFlickable::flickDeceleration() const { - Q_D(const QDeclarativeFlickable); - return d->deceleration; + // 4000 ~ the conversion factor from [m/s] to [pix/s] on a 100dpi screen + return qreal(4000) * QScroller::scroller(const_cast(this))->scrollerProperties().scrollMetric(QScrollerProperties::DecelerationFactor).toDouble(); } void QDeclarativeFlickable::setFlickDeceleration(qreal deceleration) { - Q_D(QDeclarativeFlickable); - if (deceleration == d->deceleration) + // 4000 ~ the conversion factor from [m/s] to [pix/s] on a 100dpi screen + deceleration /= qreal(4000); + QScrollerProperties props = QScroller::scroller(this)->scrollerProperties(); + qreal oldDeceleration = props.scrollMetric(QScrollerProperties::DecelerationFactor).toReal(); + if (deceleration == oldDeceleration) return; - d->deceleration = deceleration; + props.setScrollMetric(QScrollerProperties::DecelerationFactor, deceleration); + QScroller::scroller(this)->setScrollerProperties(props); emit flickDecelerationChanged(); } bool QDeclarativeFlickable::isFlicking() const { Q_D(const QDeclarativeFlickable); - return d->flickingHorizontally || d->flickingVertically; + return d->isFlicking; } /*! @@ -1429,13 +1033,13 @@ bool QDeclarativeFlickable::isFlicking() const bool QDeclarativeFlickable::isFlickingHorizontally() const { Q_D(const QDeclarativeFlickable); - return d->flickingHorizontally; + return d->movingHorizontally && d->isFlicking; } bool QDeclarativeFlickable::isFlickingVertically() const { Q_D(const QDeclarativeFlickable); - return d->flickingVertically; + return d->movingVertically && d->isFlicking; } /*! @@ -1451,16 +1055,18 @@ bool QDeclarativeFlickable::isFlickingVertically() const */ int QDeclarativeFlickable::pressDelay() const { - Q_D(const QDeclarativeFlickable); - return d->pressDelay; + return int(qreal(1000) * QScroller::scroller(const_cast(this))->scrollerProperties().scrollMetric(QScrollerProperties::MousePressEventDelay).toReal()); } void QDeclarativeFlickable::setPressDelay(int delay) { - Q_D(QDeclarativeFlickable); - if (d->pressDelay == delay) + qreal pressDelay = qreal(delay) / qreal(1000); // [ms] -> [s] + QScrollerProperties props = QScroller::scroller(this)->scrollerProperties(); + qreal oldPressDelay = props.scrollMetric(QScrollerProperties::MousePressEventDelay).toReal(); + if (pressDelay == oldPressDelay) return; - d->pressDelay = delay; + props.setScrollMetric(QScrollerProperties::MousePressEventDelay, pressDelay); + QScroller::scroller(this)->setScrollerProperties(props); emit pressDelayChanged(); } @@ -1468,7 +1074,7 @@ void QDeclarativeFlickable::setPressDelay(int delay) bool QDeclarativeFlickable::isMoving() const { Q_D(const QDeclarativeFlickable); - return d->movingHorizontally || d->movingVertically; + return d->isMoving; } /*! @@ -1492,83 +1098,4 @@ bool QDeclarativeFlickable::isMovingVertically() const return d->movingVertically; } -void QDeclarativeFlickable::movementStarting() -{ - Q_D(QDeclarativeFlickable); - if (d->hMoved && !d->movingHorizontally) { - d->movingHorizontally = true; - emit movingChanged(); - emit movingHorizontallyChanged(); - if (!d->movingVertically) - emit movementStarted(); - } - else if (d->vMoved && !d->movingVertically) { - d->movingVertically = true; - emit movingChanged(); - emit movingVerticallyChanged(); - if (!d->movingHorizontally) - emit movementStarted(); - } -} - -void QDeclarativeFlickable::movementEnding() -{ - Q_D(QDeclarativeFlickable); - movementXEnding(); - movementYEnding(); - d->hData.smoothVelocity.setValue(0); - d->vData.smoothVelocity.setValue(0); -} - -void QDeclarativeFlickable::movementXEnding() -{ - Q_D(QDeclarativeFlickable); - if (d->flickingHorizontally) { - d->flickingHorizontally = false; - emit flickingChanged(); - emit flickingHorizontallyChanged(); - if (!d->flickingVertically) - emit flickEnded(); - } - if (!d->pressed && !d->stealMouse) { - if (d->movingHorizontally) { - d->movingHorizontally = false; - d->hMoved = false; - emit movingChanged(); - emit movingHorizontallyChanged(); - if (!d->movingVertically) - emit movementEnded(); - } - } -} - -void QDeclarativeFlickable::movementYEnding() -{ - Q_D(QDeclarativeFlickable); - if (d->flickingVertically) { - d->flickingVertically = false; - emit flickingChanged(); - emit flickingVerticallyChanged(); - if (!d->flickingHorizontally) - emit flickEnded(); - } - if (!d->pressed && !d->stealMouse) { - if (d->movingVertically) { - d->movingVertically = false; - d->vMoved = false; - emit movingChanged(); - emit movingVerticallyChanged(); - if (!d->movingHorizontally) - emit movementEnded(); - } - } -} - -void QDeclarativeFlickablePrivate::updateVelocity() -{ - Q_Q(QDeclarativeFlickable); - emit q->horizontalVelocityChanged(); - emit q->verticalVelocityChanged(); -} - QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativeflickable_p.h b/src/declarative/graphicsitems/qdeclarativeflickable_p.h index 6e4d8ed..b79b08c 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable_p.h +++ b/src/declarative/graphicsitems/qdeclarativeflickable_p.h @@ -43,6 +43,7 @@ #define QDECLARATIVEFLICKABLE_H #include "qdeclarativeitem.h" +#include QT_BEGIN_HEADER @@ -175,19 +176,12 @@ Q_SIGNALS: void flickEnded(); protected: - virtual bool sceneEventFilter(QGraphicsItem *, QEvent *); - void mousePressEvent(QGraphicsSceneMouseEvent *event); - void mouseMoveEvent(QGraphicsSceneMouseEvent *event); - void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); - void wheelEvent(QGraphicsSceneWheelEvent *event); - void timerEvent(QTimerEvent *event); + virtual bool event(QEvent *e); QDeclarativeFlickableVisibleArea *visibleArea(); protected Q_SLOTS: - virtual void ticked(); - void movementStarting(); - void movementEnding(); + virtual void scrollerStateChanged(QScroller::State); protected: void movementXEnding(); @@ -196,16 +190,10 @@ protected: virtual qreal minYExtent() const; virtual qreal maxXExtent() const; virtual qreal maxYExtent() const; - qreal vWidth() const; - qreal vHeight() const; + virtual void viewportAboutToMove(QPointF newPos); virtual void viewportMoved(); virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); - bool sendMouseEvent(QGraphicsSceneMouseEvent *event); - - bool xflick() const; - bool yflick() const; - void cancelFlick(); protected: QDeclarativeFlickable(QDeclarativeFlickablePrivate &dd, QDeclarativeItem *parent); @@ -213,6 +201,8 @@ protected: private: Q_DISABLE_COPY(QDeclarativeFlickable) Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeFlickable) + + friend class QDeclarativeFlickableVisibleArea; }; diff --git a/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h b/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h index 92cf748..1f348ab 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h @@ -78,94 +78,35 @@ public: QDeclarativeFlickablePrivate(); void init(); - struct Velocity : public QDeclarativeTimeLineValue - { - Velocity(QDeclarativeFlickablePrivate *p) - : parent(p) {} - virtual void setValue(qreal v) { - if (v != value()) { - QDeclarativeTimeLineValue::setValue(v); - parent->updateVelocity(); - } - } - QDeclarativeFlickablePrivate *parent; - }; - - struct AxisData { - AxisData(QDeclarativeFlickablePrivate *fp, void (QDeclarativeFlickablePrivate::*func)(qreal)) - : move(fp, func), viewSize(-1), smoothVelocity(fp), atEnd(false), atBeginning(true) - {} - - QDeclarativeTimeLineValueProxy move; - qreal viewSize; - qreal pressPos; - qreal dragStartOffset; - qreal velocity; - qreal flickTarget; - QDeclarativeFlickablePrivate::Velocity smoothVelocity; - bool atEnd : 1; - bool atBeginning : 1; - }; - - void flickX(qreal velocity); - void flickY(qreal velocity); - virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); - - void fixupX(); - void fixupY(); - virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); - void updateBeginningEnd(); - void captureDelayedPress(QGraphicsSceneMouseEvent *event); - void clearDelayedPress(); - - void setRoundedViewportX(qreal x); - void setRoundedViewportY(qreal y); - - qreal overShootDistance(qreal velocity, qreal size); - + void updateScrollerValues(); + void updateOvershootPolicy(); void itemGeometryChanged(QDeclarativeItem *, const QRectF &, const QRectF &); + // private slot + void _q_scrollerStateChanged(QScroller::State state); + qreal dragStartOffset; + public: QDeclarativeItem *contentItem; - AxisData hData; - AxisData vData; - - QDeclarativeTimeLine timeline; - bool flickingHorizontally : 1; - bool flickingVertically : 1; - bool hMoved : 1; - bool vMoved : 1; - bool movingHorizontally : 1; - bool movingVertically : 1; - bool stealMouse : 1; - bool pressed : 1; - bool interactive : 1; - bool calcVelocity : 1; - QElapsedTimer lastPosTime; - QPointF lastPos; - QPointF pressPos; - QElapsedTimer pressTime; - qreal deceleration; - qreal maxVelocity; - QElapsedTimer velocityTime; - QPointF lastFlickablePosition; - qreal reportedVelocitySmoothing; - QGraphicsSceneMouseEvent *delayedPressEvent; - QGraphicsItem *delayedPressTarget; - QBasicTimer delayedPressTimer; - int pressDelay; - int fixupDuration; - - static void fixupY_callback(void *); - static void fixupX_callback(void *); - - void updateVelocity(); - int vTime; - QDeclarativeTimeLine velocityTimeline; + bool updateScrollerValuesRequested; + bool duringScrollEvent; + + bool isUserGenerated; + bool isFlicking; + bool isMoving; + bool movingHorizontally; + bool movingVertically; + + bool atBeginningX; + bool atBeginningY; + bool atEndX; + bool atEndY; + + bool interactive; + QDeclarativeFlickableVisibleArea *visibleArea; QDeclarativeFlickable::FlickableDirection flickableDirection; QDeclarativeFlickable::BoundsBehavior boundsBehavior; diff --git a/src/declarative/graphicsitems/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp index 6f38f63..b6af7dc 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview.cpp +++ b/src/declarative/graphicsitems/qdeclarativegridview.cpp @@ -95,24 +95,42 @@ public: //---------------------------------------------------------------------------- +/* + Some explanations: + The grid view is not creating graphics items for every entry + in the model. + + Instead it's creating only number of items that are currently + visible in visibleItems. The first model index of those items is + visibleIndex +*/ + class QDeclarativeGridViewPrivate : public QDeclarativeFlickablePrivate { Q_DECLARE_PUBLIC(QDeclarativeGridView) public: QDeclarativeGridViewPrivate() - : currentItem(0), flow(QDeclarativeGridView::LeftToRight) - , visibleIndex(0) , currentIndex(-1) - , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), itemCount(0) - , highlightRangeStart(0), highlightRangeEnd(0), highlightRange(QDeclarativeGridView::NoHighlightRange) - , highlightComponent(0), highlight(0), trackedItem(0) - , moveReason(Other), buffer(0), highlightXAnimator(0), highlightYAnimator(0) - , highlightMoveDuration(150) - , footerComponent(0), footer(0), headerComponent(0), header(0) - , bufferMode(BufferBefore | BufferAfter), snapMode(QDeclarativeGridView::NoSnap) - , ownModel(false), wrap(false), autoHighlight(true) - , fixCurrentVisibility(false), lazyRelease(false), layoutScheduled(false) - , deferredRelease(false), haveHighlightRange(false), currentIndexCleared(false) {} + : currentItem(0), flow(QDeclarativeGridView::LeftToRight) + , visibleIndex(0) , currentIndex(-1) + , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), modelCount(0) + , highlightRangeStart(0), highlightRangeEnd(0), highlightRange(QDeclarativeGridView::NoHighlightRange) + , highlightComponent(0), highlight(0) + , moveReason(Other) + , buffer(0) + , bufferMode(BufferBefore | BufferAfter) + , highlightXAnimator(0), highlightYAnimator(0) + , highlightMoveDuration(150) + , footerComponent(0) + , footer(0) + , headerComponent(0) + , header(0) + , snapMode(QDeclarativeGridView::NoSnap) + , ownModel(false), wrap(false), autoHighlight(true) + , fixCurrentVisibility(false), lazyRelease(false), layoutScheduled(false) + , deferredRelease(false), haveHighlightRange(false) + , currentIndexCleared(false) + {} void init(); void clear(); @@ -120,18 +138,15 @@ public: void releaseItem(FxGridItem *item); void refill(qreal from, qreal to, bool doBuffer=false); - void updateGrid(); void scheduleLayout(); void layout(); void updateUnrequestedIndexes(); void updateUnrequestedPositions(); - void updateTrackedItem(); - void createHighlight(); - void updateHighlight(); - void updateCurrent(int modelIndex); + void recreateHighlight(); + void updateHighlight(bool smooth = true); + void setCurrentIndex(int modelIndex); void updateHeader(); void updateFooter(); - void fixupPosition(); FxGridItem *visibleItem(int modelIndex) const { if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { @@ -144,10 +159,28 @@ public: return 0; } + /* The following methods are using flow independent coordinates. + row and column also change the direction depending on the flow. + */ + + /*! \internal + Returns the position of the view. + Depending on the flow this can be either the x or the y content coordinate + */ qreal position() const { Q_Q(const QDeclarativeGridView); return flow == QDeclarativeGridView::LeftToRight ? q->contentY() : q->contentX(); } + + void scrollTo(qreal pos) { + Q_Q(QDeclarativeGridView); + QScroller *scroller = QScroller::scroller(q); + if (flow == QDeclarativeGridView::LeftToRight) + scroller->scrollTo(QPoint(q->contentX(), pos)); + else + scroller->scrollTo(QPoint(pos, q->contentY())); + } + void setPosition(qreal pos) { Q_Q(QDeclarativeGridView); if (flow == QDeclarativeGridView::LeftToRight) @@ -159,22 +192,39 @@ public: Q_Q(const QDeclarativeGridView); return flow == QDeclarativeGridView::LeftToRight ? q->height() : q->width(); } - qreal startPosition() const { - qreal pos = 0; - if (!visibleItems.isEmpty()) - pos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); - return pos; + + /*! \internal + Returns the row size of the header to be added to the total item size. + */ + qreal headerSize() const { + if (!header) + return 0; + if (flow == QDeclarativeGridView::LeftToRight) + return header->item->height(); + else + return header->item->width(); + } + + /*! \internal + Returns the row size of the footer to be added to the total item size. + */ + qreal footerSize() const { + if (!footer) + return 0; + if (flow == QDeclarativeGridView::LeftToRight) + return footer->item->height(); + else + return footer->item->width(); } - qreal endPosition() const { - qreal pos = 0; - if (model && model->count()) - pos = rowPosAt(model->count() - 1) + rowSize(); - return pos; + qreal contentSize() const { + if (!model || !model->isValid()) + return 0.0; + return ((modelCount + columns - 1 /*round up*/) / columns) * rowSize(); } bool isValid() const { - return model && model->count() && model->isValid(); + return model && modelCount && model->isValid(); } int rowSize() const { @@ -185,51 +235,13 @@ public: } qreal colPosAt(int modelIndex) const { - if (FxGridItem *item = visibleItem(modelIndex)) - return item->colPos(); - if (!visibleItems.isEmpty()) { - if (modelIndex < visibleIndex) { - int count = (visibleIndex - modelIndex) % columns; - int col = visibleItems.first()->colPos() / colSize(); - col = (columns - count + col) % columns; - return col * colSize(); - } else { - int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns; - return visibleItems.last()->colPos() - count * colSize(); - } - } else { - return (modelIndex % columns) * colSize(); - } - return 0; + return (modelIndex % columns) * colSize(); } qreal rowPosAt(int modelIndex) const { - if (FxGridItem *item = visibleItem(modelIndex)) - return item->rowPos(); - if (!visibleItems.isEmpty()) { - if (modelIndex < visibleIndex) { - int firstCol = visibleItems.first()->colPos() / colSize(); - int col = visibleIndex - modelIndex + (columns - firstCol - 1); - int rows = col / columns; - return visibleItems.first()->rowPos() - rows * rowSize(); - } else { - int count = modelIndex - visibleItems.last()->index; - int col = visibleItems.last()->colPos() + count * colSize(); - int rows = col / (columns * colSize()); - return visibleItems.last()->rowPos() + rows * rowSize(); - } - } else { - qreal pos = (modelIndex / columns) * rowSize(); - if (header) { - qreal headerSize = flow == QDeclarativeGridView::LeftToRight - ? header->item->height() - : header->item->width(); - pos += headerSize; - } - return pos; - } - return 0; + return (modelIndex / columns) * rowSize() + headerSize(); } + /*! Returns the first of the visibleItems that is visible */ FxGridItem *firstVisibleItem() const { const qreal pos = position(); for (int i = 0; i < visibleItems.count(); ++i) { @@ -240,16 +252,17 @@ public: return visibleItems.count() ? visibleItems.first() : 0; } + /*! \internal + Returns the last visible model index. delayRemove items are not counted (as they don't have an index) + */ int lastVisibleIndex() const { - int lastIndex = -1; - for (int i = visibleItems.count()-1; i >= 0; --i) { - FxGridItem *gridItem = visibleItems.at(i); - if (gridItem->index != -1) { - lastIndex = gridItem->index; - break; - } + // find the last valid model index + for (int i = visibleItems.count() - 1; i >= 0; --i) { + FxGridItem *item = visibleItems.at(i); + if (item->index != -1) + return item->index; } - return lastIndex; + return visibleIndex; } // Map a model index to visibleItems list index. @@ -268,51 +281,22 @@ public: return -1; // Not in visibleList } - qreal snapPosAt(qreal pos) const { - Q_Q(const QDeclarativeGridView); - qreal snapPos = 0; - if (!visibleItems.isEmpty()) { - pos += rowSize()/2; - snapPos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); - snapPos = pos - fmodf(pos - snapPos, qreal(rowSize())); - qreal maxExtent = flow == QDeclarativeGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); - qreal minExtent = flow == QDeclarativeGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); - if (snapPos > maxExtent) - snapPos = maxExtent; - if (snapPos < minExtent) - snapPos = minExtent; - } - return snapPos; - } - - FxGridItem *snapItemAt(qreal pos) { - for (int i = 0; i < visibleItems.count(); ++i) { - FxGridItem *item = visibleItems[i]; - if (item->index == -1) - continue; - qreal itemTop = item->rowPos(); - if (item->index == model->count()-1 || (itemTop+rowSize()/2 >= pos)) - return item; - } - if (visibleItems.count() && visibleItems.first()->rowPos() <= pos) - return visibleItems.first(); - return 0; - } - - int snapIndex() { - int index = currentIndex; - for (int i = 0; i < visibleItems.count(); ++i) { - FxGridItem *item = visibleItems[i]; - if (item->index == -1) - continue; - qreal itemTop = item->rowPos(); - if (itemTop >= highlight->rowPos()-rowSize()/2 && itemTop < highlight->rowPos()+rowSize()/2) { - index = item->index; - if (item->colPos() >= highlight->colPos()-colSize()/2 && item->colPos() < highlight->colPos()+colSize()/2) - return item->index; - } - } - return index; + /*! \internal + Return the model index of the visible item which is + next to the highlight. + */ + int snapIndex() + { + // which is the next row and column around the hightlight + int nextRow = qRound(highlight->rowPos() / rowSize()); + int nextCol = qRound(highlight->colPos() / colSize()); + + // calculate and verify index + int index = nextRow * columns + nextCol; + if (index < 0 || index >= modelCount) + return currentIndex; + else + return index; } virtual void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { @@ -322,7 +306,6 @@ public: if (newGeometry.height() != oldGeometry.height() || newGeometry.width() != oldGeometry.width()) { if (q->isComponentComplete()) { - updateGrid(); scheduleLayout(); } } @@ -330,12 +313,10 @@ public: updateHeader(); updateFooter(); } + if (currentItem && currentItem->item == item) + updateHighlight(); } - virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); - virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); - // for debugging only void checkVisible() const { int skip = 0; @@ -354,34 +335,71 @@ public: QDeclarativeGuard model; QVariant modelVariant; QList visibleItems; + + /* + These are items that were not requested but send by createdItem from the model. + Usually these items were created because another view showing the same model + needed them. + */ QHash unrequestedItems; + FxGridItem *currentItem; QDeclarativeGridView::Flow flow; + + /* + The visibleIndex is the modelIndex of the first item in visibleItems + visible row. + */ int visibleIndex; + + /* + The currentIndex is the model index of the currently highlighted item. + */ int currentIndex; + int cellWidth; int cellHeight; + + /* + The number of columns this layout currently has. + */ int columns; + + /* + The model index of the item that is currently created via createItem + */ int requestedIndex; - int itemCount; + + /* + This is the number of items in the model. + */ + int modelCount; + qreal highlightRangeStart; qreal highlightRangeEnd; QDeclarativeGridView::HighlightRangeMode highlightRange; QDeclarativeComponent *highlightComponent; FxGridItem *highlight; - FxGridItem *trackedItem; enum MovementReason { Other, SetIndex, Mouse }; MovementReason moveReason; + + /* + The maximum number of pixels that should be covered by buffered + delegates in front and behind the visible area. + */ int buffer; + enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; + int bufferMode; + QSmoothedAnimation *highlightXAnimator; QSmoothedAnimation *highlightYAnimator; int highlightMoveDuration; + QDeclarativeComponent *footerComponent; FxGridItem *footer; QDeclarativeComponent *headerComponent; FxGridItem *header; - enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; - int bufferMode; + QDeclarativeGridView::SnapMode snapMode; bool ownModel : 1; @@ -398,7 +416,6 @@ public: void QDeclarativeGridViewPrivate::init() { Q_Q(QDeclarativeGridView); - QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); q->setFlag(QGraphicsItem::ItemIsFocusScope); q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick); addItemChangeListener(this, Geometry); @@ -412,9 +429,8 @@ void QDeclarativeGridViewPrivate::clear() visibleIndex = 0; releaseItem(currentItem); currentItem = 0; - createHighlight(); - trackedItem = 0; - itemCount = 0; + recreateHighlight(); + modelCount = 0; } FxGridItem *QDeclarativeGridViewPrivate::createItem(int modelIndex) @@ -446,11 +462,6 @@ void QDeclarativeGridViewPrivate::releaseItem(FxGridItem *item) Q_Q(QDeclarativeGridView); if (!item || !model) return; - if (trackedItem == item) { - QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); - QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); - trackedItem = 0; - } if (model->release(item->item) == 0) { // item was not destroyed, and we no longer reference it. unrequestedItems.insert(item->item, model->indexOf(item->item, q)); @@ -461,9 +472,12 @@ void QDeclarativeGridViewPrivate::releaseItem(FxGridItem *item) void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) { Q_Q(QDeclarativeGridView); - if (!isValid() || !q->isComponentComplete()) + if (!q->isComponentComplete()) return; - itemCount = model->count(); + + if (!doBuffer && buffer && bufferMode != NoBuffer) + doBuffer = true; + qreal bufferFrom = from - buffer; qreal bufferTo = to + buffer; qreal fillFrom = from; @@ -475,29 +489,32 @@ void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) bool changed = false; - int colPos = colPosAt(visibleIndex); - int rowPos = rowPosAt(visibleIndex); - int modelIndex = visibleIndex; - if (visibleItems.count()) { - rowPos = visibleItems.last()->rowPos(); - colPos = visibleItems.last()->colPos() + colSize(); - if (colPos > colSize() * (columns-1)) { - colPos = 0; - rowPos += rowSize(); - } - int i = visibleItems.count() - 1; - while (i > 0 && visibleItems.at(i)->index == -1) - --i; - modelIndex = visibleItems.at(i)->index + 1; + updateHeader(); + + columns = (int)qMax((flow == QDeclarativeGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); + + // -- update the positions of all visible items + for (int i = 0; i < visibleItems.count(); ++i) { + qreal colPos = colPosAt(i + visibleIndex); + qreal rowPos = rowPosAt(i + visibleIndex); + FxGridItem *item = visibleItems.at(i); + item->setPosition(colPos, rowPos); } - int colNum = colPos / colSize(); + // determine the next position + int modelIndex = lastVisibleIndex(); + if (!visibleItems.isEmpty()) + modelIndex++; + qreal colPos = colPosAt(modelIndex); + qreal rowPos = rowPosAt(modelIndex); + + int colNum = colPos / colSize(); FxGridItem *item = 0; // Item creation and release is staggered in order to avoid // creating/releasing multiple items in one frame // while flicking (as much as possible). - while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) { + while (modelIndex < modelCount && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) { // qDebug() << "refill: append item" << modelIndex; if (!(item = createItem(modelIndex))) break; @@ -571,31 +588,17 @@ void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) } else { deferredRelease = true; } - if (changed) { - if (header) - updateHeader(); - if (footer) - updateFooter(); - if (flow == QDeclarativeGridView::LeftToRight) - q->setContentHeight(endPosition() - startPosition()); - else - q->setContentWidth(endPosition() - startPosition()); - } else if (!doBuffer && buffer && bufferMode != NoBuffer) { - refill(from, to, true); - } - lazyRelease = false; -} -void QDeclarativeGridViewPrivate::updateGrid() -{ - Q_Q(QDeclarativeGridView); - columns = (int)qMax((flow == QDeclarativeGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); - if (isValid()) { - if (flow == QDeclarativeGridView::LeftToRight) - q->setContentHeight(endPosition() - startPosition()); - else - q->setContentWidth(endPosition() - startPosition()); - } + if (flow == QDeclarativeGridView::LeftToRight) + q->setContentHeight(contentSize()); + else + q->setContentWidth(contentSize()); + + updateFooter(); + + updateUnrequestedPositions(); + + lazyRelease = false; } void QDeclarativeGridViewPrivate::scheduleLayout() @@ -609,53 +612,9 @@ void QDeclarativeGridViewPrivate::scheduleLayout() void QDeclarativeGridViewPrivate::layout() { - Q_Q(QDeclarativeGridView); layoutScheduled = false; - if (!isValid() && !visibleItems.count()) { - clear(); - return; - } - if (visibleItems.count()) { - qreal rowPos = visibleItems.first()->rowPos(); - qreal colPos = visibleItems.first()->colPos(); - int col = visibleIndex % columns; - if (colPos != col * colSize()) { - colPos = col * colSize(); - visibleItems.first()->setPosition(colPos, rowPos); - } - for (int i = 1; i < visibleItems.count(); ++i) { - FxGridItem *item = visibleItems.at(i); - colPos += colSize(); - if (colPos > colSize() * (columns-1)) { - colPos = 0; - rowPos += rowSize(); - } - item->setPosition(colPos, rowPos); - } - } - if (header) - updateHeader(); - if (footer) - updateFooter(); - q->refill(); - updateHighlight(); - moveReason = Other; - if (flow == QDeclarativeGridView::LeftToRight) { - q->setContentHeight(endPosition() - startPosition()); - fixupY(); - } else { - q->setContentWidth(endPosition() - startPosition()); - fixupX(); - } - updateUnrequestedPositions(); -} -void QDeclarativeGridViewPrivate::updateUnrequestedIndexes() -{ - Q_Q(QDeclarativeGridView); - QHash::iterator it; - for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) - *it = model->indexOf(it.key(), q); + refill(position(), position() + size() - 1); } void QDeclarativeGridViewPrivate::updateUnrequestedPositions() @@ -670,35 +629,11 @@ void QDeclarativeGridViewPrivate::updateUnrequestedPositions() } } -void QDeclarativeGridViewPrivate::updateTrackedItem() -{ - Q_Q(QDeclarativeGridView); - FxGridItem *item = currentItem; - if (highlight) - item = highlight; - - if (trackedItem && item != trackedItem) { - QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); - QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); - trackedItem = 0; - } - - if (!trackedItem && item) { - trackedItem = item; - QObject::connect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); - QObject::connect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); - } - if (trackedItem) - q->trackedPositionChanged(); -} - -void QDeclarativeGridViewPrivate::createHighlight() +void QDeclarativeGridViewPrivate::recreateHighlight() { Q_Q(QDeclarativeGridView); bool changed = false; if (highlight) { - if (trackedItem == highlight) - trackedItem = 0; delete highlight->item; delete highlight; highlight = 0; @@ -724,8 +659,6 @@ void QDeclarativeGridViewPrivate::createHighlight() } } else { item = new QDeclarativeItem; - QDeclarative_setParent_noEvent(item, q->contentItem()); - item->setParentItem(q->contentItem()); } if (item) { QDeclarative_setParent_noEvent(item, q->contentItem()); @@ -750,59 +683,88 @@ void QDeclarativeGridViewPrivate::createHighlight() emit q->highlightItemChanged(); } -void QDeclarativeGridViewPrivate::updateHighlight() +void QDeclarativeGridViewPrivate::updateHighlight(bool smooth) { if ((!currentItem && highlight) || (currentItem && !highlight)) - createHighlight(); - if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) { - // auto-update highlight - highlightXAnimator->to = currentItem->item->x(); - highlightYAnimator->to = currentItem->item->y(); - highlight->item->setWidth(currentItem->item->width()); - highlight->item->setHeight(currentItem->item->height()); - highlightXAnimator->restart(); - highlightYAnimator->restart(); + recreateHighlight(); + + // --- move the current item between the highlight range + if (moveReason == QDeclarativeGridViewPrivate::SetIndex) { + // ensure that the tracked item is inside the highlight range + + // reposition view + if (currentItem) { + qreal pos = currentItem->rowPos(); + qreal viewPos = position(); + + if (autoHighlight && haveHighlightRange) { + if (pos > viewPos + highlightRangeEnd - rowSize()) + viewPos = pos - highlightRangeEnd + rowSize(); + if (pos < viewPos + highlightRangeStart) + viewPos = pos - highlightRangeStart; + + } else { + if (pos > viewPos + size() - rowSize()) + viewPos = pos - size() + rowSize(); + if (pos < viewPos + 0) + viewPos = pos - 0; + } + if (smooth) + scrollTo(viewPos); + else + setPosition(viewPos); + } + + // move the highlight + if (currentItem && autoHighlight && highlight) { + highlight->item->setWidth(currentItem->item->width()); + highlight->item->setHeight(currentItem->item->height()); + highlightXAnimator->to = currentItem->item->x(); + highlightYAnimator->to = currentItem->item->y(); + if (smooth) { + highlightXAnimator->restart(); + highlightYAnimator->restart(); + } else { + highlightXAnimator->stop(); + highlightYAnimator->stop(); + highlight->item->setPos(QPointF(highlightXAnimator->to, + highlightYAnimator->to)); + } + } } - updateTrackedItem(); } -void QDeclarativeGridViewPrivate::updateCurrent(int modelIndex) +void QDeclarativeGridViewPrivate::setCurrentIndex(int newIndex) { Q_Q(QDeclarativeGridView); - if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { - if (currentItem) { + + // --- release the old item + if (currentItem && currentIndex != newIndex ) { currentItem->attached->setIsCurrentItem(false); releaseItem(currentItem); currentItem = 0; - currentIndex = modelIndex; - emit q->currentIndexChanged(); - updateHighlight(); - } else if (currentIndex != modelIndex) { - currentIndex = modelIndex; - emit q->currentIndexChanged(); - } - return; } - if (currentItem && currentIndex == modelIndex) { - updateHighlight(); - return; - } + int oldIndex = currentIndex; + currentIndex = newIndex; - FxGridItem *oldCurrentItem = currentItem; - currentIndex = modelIndex; - currentItem = createItem(modelIndex); - fixCurrentVisibility = true; - if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) - oldCurrentItem->attached->setIsCurrentItem(false); - if (currentItem) { - currentItem->setPosition(colPosAt(modelIndex), rowPosAt(modelIndex)); - currentItem->item->setFocus(true); - currentItem->attached->setIsCurrentItem(true); + bool visible = (q->isComponentComplete() && isValid() && !currentItem && + newIndex >= 0 && newIndex < modelCount); + + // --- set the new item + if (visible) { + currentItem = createItem(newIndex); + if (currentItem) { + currentItem->setPosition(colPosAt(newIndex), rowPosAt(newIndex)); + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + } } + updateHighlight(); - emit q->currentIndexChanged(); - releaseItem(oldCurrentItem); + + if (currentIndex != oldIndex) + emit q->currentIndexChanged(); } void QDeclarativeGridViewPrivate::updateFooter() @@ -830,24 +792,7 @@ void QDeclarativeGridViewPrivate::updateFooter() } } if (footer) { - if (visibleItems.count()) { - qreal endPos = endPosition(); - if (lastVisibleIndex() == model->count()-1) { - footer->setPosition(0, endPos); - } else { - qreal visiblePos = position() + q->height(); - if (endPos <= visiblePos || footer->endRowPos() < endPos) - footer->setPosition(0, endPos); - } - } else { - qreal endPos = 0; - if (header) { - endPos += flow == QDeclarativeGridView::LeftToRight - ? header->item->height() - : header->item->width(); - } - footer->setPosition(0, endPos); - } + footer->setPosition(0, contentSize() + headerSize()); } } @@ -876,194 +821,10 @@ void QDeclarativeGridViewPrivate::updateHeader() } } if (header) { - if (visibleItems.count()) { - qreal startPos = startPosition(); - qreal headerSize = flow == QDeclarativeGridView::LeftToRight - ? header->item->height() - : header->item->width(); - if (visibleIndex == 0) { - header->setPosition(0, startPos - headerSize); - } else { - if (position() <= startPos || header->rowPos() > startPos - headerSize) - header->setPosition(0, startPos - headerSize); - } - } else { - header->setPosition(0, 0); - } + header->setPosition(0, 0); } } -void QDeclarativeGridViewPrivate::fixupPosition() -{ - moveReason = Other; - if (flow == QDeclarativeGridView::LeftToRight) - fixupY(); - else - fixupX(); -} - -void QDeclarativeGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) -{ - if ((flow == QDeclarativeGridView::TopToBottom && &data == &vData) - || (flow == QDeclarativeGridView::LeftToRight && &data == &hData)) - return; - - int oldDuration = fixupDuration; - fixupDuration = moveReason == Mouse ? fixupDuration : 0; - - if (snapMode != QDeclarativeGridView::NoSnap) { - FxGridItem *topItem = snapItemAt(position()+highlightRangeStart); - FxGridItem *bottomItem = snapItemAt(position()+highlightRangeEnd); - qreal pos; - if (topItem && bottomItem && haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { - qreal topPos = qMin(topItem->rowPos() - highlightRangeStart, -maxExtent); - qreal bottomPos = qMax(bottomItem->rowPos() - highlightRangeEnd, -minExtent); - pos = qAbs(data.move + topPos) < qAbs(data.move + bottomPos) ? topPos : bottomPos; - } else if (topItem) { - pos = qMax(qMin(topItem->rowPos() - highlightRangeStart, -maxExtent), -minExtent); - } else if (bottomItem) { - pos = qMax(qMin(bottomItem->rowPos() - highlightRangeStart, -maxExtent), -minExtent); - } else { - fixupDuration = oldDuration; - return; - } - if (currentItem && haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { - updateHighlight(); - qreal currPos = currentItem->rowPos(); - if (pos < currPos + rowSize() - highlightRangeEnd) - pos = currPos + rowSize() - highlightRangeEnd; - if (pos > currPos - highlightRangeStart) - pos = currPos - highlightRangeStart; - } - - qreal dist = qAbs(data.move + pos); - if (dist > 0) { - timeline.reset(data.move); - if (fixupDuration) - timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - else - timeline.set(data.move, -pos); - vTime = timeline.time(); - } - } else if (haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { - if (currentItem) { - updateHighlight(); - qreal pos = currentItem->rowPos(); - qreal viewPos = position(); - if (viewPos < pos + rowSize() - highlightRangeEnd) - viewPos = pos + rowSize() - highlightRangeEnd; - if (viewPos > pos - highlightRangeStart) - viewPos = pos - highlightRangeStart; - - timeline.reset(data.move); - if (viewPos != position()) { - if (fixupDuration) - timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - else - timeline.set(data.move, -viewPos); - } - vTime = timeline.time(); - } - } else { - QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); - } - fixupDuration = oldDuration; -} - -void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) -{ - Q_Q(QDeclarativeGridView); - moveReason = Mouse; - if ((!haveHighlightRange || highlightRange != QDeclarativeGridView::StrictlyEnforceRange) - && snapMode == QDeclarativeGridView::NoSnap) { - QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); - return; - } - qreal maxDistance = 0; - // -ve velocity means list is moving up - if (velocity > 0) { - if (data.move.value() < minExtent) { - if (snapMode == QDeclarativeGridView::SnapOneRow) { - if (FxGridItem *item = firstVisibleItem()) - maxDistance = qAbs(item->rowPos() + data.move.value()); - } else { - maxDistance = qAbs(minExtent - data.move.value()); - } - } - if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange) - data.flickTarget = minExtent; - } else { - if (data.move.value() > maxExtent) { - if (snapMode == QDeclarativeGridView::SnapOneRow) { - qreal pos = snapPosAt(-data.move.value()) + rowSize(); - maxDistance = qAbs(pos + data.move.value()); - } else { - maxDistance = qAbs(maxExtent - data.move.value()); - } - } - if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange) - data.flickTarget = maxExtent; - } - bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds; - if (maxDistance > 0 || overShoot) { - // This mode requires the grid to stop exactly on a row boundary. - qreal v = velocity; - if (maxVelocity != -1 && maxVelocity < qAbs(v)) { - if (v < 0) - v = -maxVelocity; - else - v = maxVelocity; - } - qreal accel = deceleration; - qreal v2 = v * v; - qreal overshootDist = 0.0; - if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeGridView::SnapOneRow) { - // + rowSize()/4 to encourage moving at least one item in the flick direction - qreal dist = v2 / (accel * 2.0) + rowSize()/4; - dist = qMin(dist, maxDistance); - if (v > 0) - dist = -dist; - data.flickTarget = -snapPosAt(-(data.move.value() - highlightRangeStart) + dist) + highlightRangeStart; - qreal adjDist = -data.flickTarget + data.move.value(); - if (qAbs(adjDist) > qAbs(dist)) { - // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration - qreal adjv2 = accel * 2.0f * qAbs(adjDist); - if (adjv2 > v2) { - v2 = adjv2; - v = qSqrt(v2); - if (dist > 0) - v = -v; - } - } - dist = adjDist; - accel = v2 / (2.0f * qAbs(dist)); - } else { - data.flickTarget = velocity > 0 ? minExtent : maxExtent; - overshootDist = overShoot ? overShootDistance(v, vSize) : 0; - } - timeline.reset(data.move); - timeline.accel(data.move, v, accel, maxDistance + overshootDist); - timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); - if (!flickingHorizontally && q->xflick()) { - flickingHorizontally = true; - emit q->flickingChanged(); - emit q->flickingHorizontallyChanged(); - emit q->flickStarted(); - } - if (!flickingVertically && q->yflick()) { - flickingVertically = true; - emit q->flickingChanged(); - emit q->flickingVerticallyChanged(); - emit q->flickStarted(); - } - } else { - timeline.reset(data.move); - fixup(data, minExtent, maxExtent); - } -} - - //---------------------------------------------------------------------------- /*! @@ -1207,10 +968,10 @@ QVariant QDeclarativeGridView::model() const return d->modelVariant; } -void QDeclarativeGridView::setModel(const QVariant &model) +void QDeclarativeGridView::setModel(const QVariant &newModel) { Q_D(QDeclarativeGridView); - if (d->modelVariant == model) + if (d->modelVariant == newModel) return; if (d->model) { disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); @@ -1220,13 +981,18 @@ void QDeclarativeGridView::setModel(const QVariant &model) disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); } + d->clear(); - d->modelVariant = model; - QObject *object = qvariant_cast(model); + QDeclarativeVisualModel *oldModel = d->model; + d->model = 0; + d->modelCount = 0; + + d->modelVariant = newModel; + QObject *object = qvariant_cast(newModel); QDeclarativeVisualModel *vim = 0; if (object && (vim = qobject_cast(object))) { if (d->ownModel) { - delete d->model; + delete oldModel; d->ownModel = false; } d->model = vim; @@ -1234,24 +1000,22 @@ void QDeclarativeGridView::setModel(const QVariant &model) if (!d->ownModel) { d->model = new QDeclarativeVisualDataModel(qmlContext(this), this); d->ownModel = true; + } else { + d->model = oldModel; } if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) - dataModel->setModel(model); + dataModel->setModel(newModel); } + if (d->model) { + d->modelCount = d->model->count(); d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore | QDeclarativeGridViewPrivate::BufferAfter; if (isComponentComplete()) { refill(); - if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { + if ((d->currentIndex >= d->modelCount || d->currentIndex < 0) && !d->currentIndexCleared) { setCurrentIndex(0); } else { - d->moveReason = QDeclarativeGridViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); - d->updateTrackedItem(); - } - d->moveReason = QDeclarativeGridViewPrivate::Other; + d->setCurrentIndex(d->currentIndex); } } connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); @@ -1314,12 +1078,7 @@ void QDeclarativeGridView::setDelegate(QDeclarativeComponent *delegate) d->currentItem = 0; refill(); d->moveReason = QDeclarativeGridViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); - d->updateTrackedItem(); - } - d->moveReason = QDeclarativeGridViewPrivate::Other; + d->setCurrentIndex(d->currentIndex); } emit delegateChanged(); } @@ -1351,12 +1110,15 @@ void QDeclarativeGridView::setCurrentIndex(int index) Q_D(QDeclarativeGridView); if (d->requestedIndex >= 0) // currently creating item return; + d->currentIndexCleared = (index == -1); + if (index == d->currentIndex) return; + if (isComponentComplete() && d->isValid()) { d->moveReason = QDeclarativeGridViewPrivate::SetIndex; - d->updateCurrent(index); + d->setCurrentIndex(index); } else { d->currentIndex = index; emit currentIndexChanged(); @@ -1396,9 +1158,7 @@ QDeclarativeItem *QDeclarativeGridView::highlightItem() int QDeclarativeGridView::count() const { Q_D(const QDeclarativeGridView); - if (d->model) - return d->model->count(); - return 0; + return d->modelCount; } /*! @@ -1422,7 +1182,7 @@ void QDeclarativeGridView::setHighlight(QDeclarativeComponent *highlight) Q_D(QDeclarativeGridView); if (highlight != d->highlightComponent) { d->highlightComponent = highlight; - d->updateCurrent(d->currentIndex); + d->recreateHighlight(); emit highlightChanged(); } } @@ -1599,10 +1359,8 @@ void QDeclarativeGridView::setFlow(Flow flow) setContentHeight(-1); setFlickableDirection(QDeclarativeFlickable::HorizontalFlick); } - d->clear(); - d->updateGrid(); - refill(); - d->updateCurrent(d->currentIndex); + d->layout(); + d->setCurrentIndex(d->currentIndex); emit flowChanged(); } } @@ -1687,9 +1445,8 @@ void QDeclarativeGridView::setCellWidth(int cellWidth) Q_D(QDeclarativeGridView); if (cellWidth != d->cellWidth && cellWidth > 0) { d->cellWidth = qMax(1, cellWidth); - d->updateGrid(); - emit cellWidthChanged(); d->layout(); + emit cellWidthChanged(); } } @@ -1704,9 +1461,8 @@ void QDeclarativeGridView::setCellHeight(int cellHeight) Q_D(QDeclarativeGridView); if (cellHeight != d->cellHeight && cellHeight > 0) { d->cellHeight = qMax(1, cellHeight); - d->updateGrid(); - emit cellHeightChanged(); d->layout(); + emit cellHeightChanged(); } } /*! @@ -1764,8 +1520,7 @@ void QDeclarativeGridView::setFooter(QDeclarativeComponent *footer) d->footer = 0; } d->footerComponent = footer; - d->updateFooter(); - d->updateGrid(); + d->layout(); emit footerChanged(); } } @@ -1794,9 +1549,7 @@ void QDeclarativeGridView::setHeader(QDeclarativeComponent *header) d->header = 0; } d->headerComponent = header; - d->updateHeader(); - d->updateFooter(); - d->updateGrid(); + d->layout(); emit headerChanged(); } } @@ -1820,116 +1573,140 @@ void QDeclarativeGridView::setContentY(qreal pos) bool QDeclarativeGridView::event(QEvent *event) { Q_D(QDeclarativeGridView); - if (event->type() == QEvent::User) { + QScroller *scroller = QScroller::scroller(this); + + switch (event->type()) { + case QEvent::User: d->layout(); return true; + + case QEvent::ScrollPrepare: { + qreal snapOffset = 0; + bool forceSnapping = false; + // bool useEndPosition = false; + bool ignoreHeaders = false; + + // --- do the highlight range + if (d->haveHighlightRange) { + snapOffset = -d->highlightRangeStart; + ignoreHeaders = true; + } + + // just before scrolling set the snap points if needed + QList snapPoints; + // -- snap to every point (SnapToRow) + if (d->snapMode == QDeclarativeGridView::SnapToRow || forceSnapping) { + if (d->header && !ignoreHeaders) + snapPoints.append(0 + snapOffset); + foreach (FxGridItem *item, d->visibleItems) + snapPoints.append(item->rowPos() + snapOffset); + if (d->footer && !ignoreHeaders) + snapPoints.append(d->headerSize() + d->contentSize() + snapOffset); + + // -- snap to the next three point (SnapOneRow) + } else if (d->snapMode == QDeclarativeGridView::SnapOneRow) { + // here we just set three snap points around the current position. + + qreal rowPos = d->rowPosAt(d->currentIndex); + if (rowPos - d->rowSize() >= 0) + snapPoints.append( rowPos - d->rowSize()); + else if( d->header ) + snapPoints.append(0); // position of the header + + snapPoints.append(rowPos); + + if (rowPos + d->rowSize() < d->headerSize() + d->contentSize()) + snapPoints.append(rowPos + d->rowSize()); + else if( d->footer ) + snapPoints.append(d->headerSize() + d->contentSize() + snapOffset); // position of the footer + } + + if (d->flow == QDeclarativeGridView::LeftToRight) { + scroller->setSnapPositionsX(0.0, 0.0); + scroller->setSnapPositionsY(snapPoints); + } else { + scroller->setSnapPositionsX(snapPoints); + scroller->setSnapPositionsY(0.0, 0.0); + } + } + break; + + default: + break; } return QDeclarativeFlickable::event(event); } -void QDeclarativeGridView::viewportMoved() +void QDeclarativeGridView::scrollerStateChanged(QScroller::State state) { Q_D(QDeclarativeGridView); - QDeclarativeFlickable::viewportMoved(); - if (!d->itemCount) - return; - d->lazyRelease = true; - if (d->flickingHorizontally || d->flickingVertically) { - if (yflick()) { - if (d->vData.velocity > 0) - d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; - else if (d->vData.velocity < 0) - d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; - } + QDeclarativeFlickable::scrollerStateChanged(state); - if (xflick()) { - if (d->hData.velocity > 0) - d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; - else if (d->hData.velocity < 0) - d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; - } - } - refill(); - if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically) - d->moveReason = QDeclarativeGridViewPrivate::Mouse; - if (d->moveReason != QDeclarativeGridViewPrivate::SetIndex) { - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { - // reposition highlight - qreal pos = d->highlight->rowPos(); - qreal viewPos = d->position(); - if (pos > viewPos + d->highlightRangeEnd - d->rowSize()) - pos = viewPos + d->highlightRangeEnd - d->rowSize(); - if (pos < viewPos + d->highlightRangeStart) - pos = viewPos + d->highlightRangeStart; - d->highlight->setPosition(d->highlight->colPos(), qRound(pos)); - - // update current index - int idx = d->snapIndex(); - if (idx >= 0 && idx != d->currentIndex) { - d->updateCurrent(idx); - if (d->currentItem && d->currentItem->colPos() != d->highlight->colPos() && d->autoHighlight) { - if (d->flow == LeftToRight) - d->highlightXAnimator->to = d->currentItem->item->x(); - else - d->highlightYAnimator->to = d->currentItem->item->y(); - } - } + if (state == QScroller::Inactive) { + d->bufferMode = QDeclarativeGridViewPrivate::NoBuffer; + if (d->highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { + d->updateHighlight(); // nudge the highlight in the right position if needed. } } } -qreal QDeclarativeGridView::minYExtent() const +qreal QDeclarativeGridView::minExtent() const { Q_D(const QDeclarativeGridView); - if (d->flow == QDeclarativeGridView::TopToBottom) - return QDeclarativeFlickable::minYExtent(); - qreal extent = -d->startPosition(); - if (d->header && d->visibleItems.count()) - extent += d->header->item->height(); - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - extent += d->highlightRangeStart; - extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd)); + + qreal extent = 0.0; + if (d->modelCount && + d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + extent -= d->highlightRangeStart; + // extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd)); } return extent; } -qreal QDeclarativeGridView::maxYExtent() const +qreal QDeclarativeGridView::maxExtent() const { Q_D(const QDeclarativeGridView); - if (d->flow == QDeclarativeGridView::TopToBottom) - return QDeclarativeFlickable::maxYExtent(); - qreal extent; - if (!d->model || !d->model->count()) { - extent = 0; - } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - extent = -(d->rowPosAt(d->model->count()-1) - d->highlightRangeStart); - if (d->highlightRangeEnd != d->highlightRangeStart) - extent = qMin(extent, -(d->endPosition() - d->highlightRangeEnd + 1)); - } else { - extent = -(d->endPosition() - height()); + + qreal extent = d->contentSize(); + extent += d->headerSize(); + extent += d->footerSize(); + + if (d->visibleItems.count() && + d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + extent = qMin(extent + d->size() - d->highlightRangeEnd + 1, + // ensure that the last item fits fully behind hightlightRangeStart + d->rowPosAt(d->modelCount-1) + d->size() - d->highlightRangeStart); } - if (d->footer) - extent -= d->footer->item->height(); - const qreal minY = minYExtent(); - if (extent > minY) - extent = minY; + return extent; } +qreal QDeclarativeGridView::minYExtent() const +{ + Q_D(const QDeclarativeGridView); + if (d->flow != QDeclarativeGridView::LeftToRight) + return QDeclarativeFlickable::minXExtent(); + else + return minExtent(); +} + +qreal QDeclarativeGridView::maxYExtent() const +{ + Q_D(const QDeclarativeGridView); + if (d->flow != QDeclarativeGridView::LeftToRight) + return QDeclarativeFlickable::maxXExtent(); + else + return maxExtent(); +} + qreal QDeclarativeGridView::minXExtent() const { Q_D(const QDeclarativeGridView); if (d->flow == QDeclarativeGridView::LeftToRight) return QDeclarativeFlickable::minXExtent(); - qreal extent = -d->startPosition(); - if (d->header && d->visibleItems.count()) - extent += d->header->item->width(); - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - extent += d->highlightRangeStart; - extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd)); - } - return extent; + else + return minExtent(); } qreal QDeclarativeGridView::maxXExtent() const @@ -1937,32 +1714,70 @@ qreal QDeclarativeGridView::maxXExtent() const Q_D(const QDeclarativeGridView); if (d->flow == QDeclarativeGridView::LeftToRight) return QDeclarativeFlickable::maxXExtent(); - qreal extent; - if (!d->model || !d->model->count()) { - extent = 0; - } if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - extent = -(d->rowPosAt(d->model->count()-1) - d->highlightRangeStart); - if (d->highlightRangeEnd != d->highlightRangeStart) - extent = qMin(extent, -(d->endPosition() - d->highlightRangeEnd + 1)); - } else { - extent = -(d->endPosition() - width()); + else + return maxExtent(); +} + +void QDeclarativeGridView::viewportAboutToMove(QPointF newPos) +{ + Q_D(QDeclarativeGridView); + + // need to refill before moving + if (d->flow == LeftToRight) + d->refill(newPos.y(), newPos.y() + height() - 1); + else + d->refill(newPos.x(), newPos.x() + width() - 1); + + d->lazyRelease = true; + + if (isFlickingHorizontally()) { + if (horizontalVelocity() > 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; + else if (horizontalVelocity() < 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; + + } else if (isFlickingVertically()) { + if (verticalVelocity() > 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; + else if (verticalVelocity() < 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; + } + + if (d->isUserGenerated) + d->moveReason = QDeclarativeGridViewPrivate::Mouse; + + if (d->haveHighlightRange && d->highlight && d->highlightRange == StrictlyEnforceRange) { + + d->highlightXAnimator->stop(); + d->highlightYAnimator->stop(); + + // reposition highlight + qreal pos = d->highlight->rowPos(); + qreal viewPos = (d->flow == QDeclarativeGridView::LeftToRight) ? newPos.y() : newPos.x(); + if (pos > viewPos + d->highlightRangeEnd - d->rowSize()) + pos = viewPos + d->highlightRangeEnd - d->rowSize(); + if (pos < viewPos + d->highlightRangeStart) + pos = viewPos + d->highlightRangeStart; + d->highlight->setPosition(d->highlight->colPos(), pos); + + // update current index + if (d->moveReason != QDeclarativeGridViewPrivate::SetIndex) { + int idx = d->snapIndex(); + if (idx >= 0 && idx != d->currentIndex) { + d->setCurrentIndex(idx); + } + } } - if (d->footer) - extent -= d->footer->item->width(); - const qreal minX = minXExtent(); - if (extent > minX) - extent = minX; - return extent; } void QDeclarativeGridView::keyPressEvent(QKeyEvent *event) { Q_D(QDeclarativeGridView); keyPressPreHandler(event); + if (event->isAccepted()) return; - if (d->model && d->model->count() && d->interactive) { - d->moveReason = QDeclarativeGridViewPrivate::SetIndex; + if (d->model && d->modelCount && d->interactive) { int oldCurrent = currentIndex(); switch (event->key()) { case Qt::Key_Up: @@ -1985,7 +1800,6 @@ void QDeclarativeGridView::keyPressEvent(QKeyEvent *event) return; } } - d->moveReason = QDeclarativeGridViewPrivate::Other; event->ignore(); QDeclarativeFlickable::keyPressEvent(event); } @@ -2002,7 +1816,7 @@ void QDeclarativeGridView::keyPressEvent(QKeyEvent *event) void QDeclarativeGridView::moveCurrentIndexUp() { Q_D(QDeclarativeGridView); - const int count = d->model ? d->model->count() : 0; + const int count = d->modelCount; if (!count) return; if (d->flow == QDeclarativeGridView::LeftToRight) { @@ -2030,7 +1844,7 @@ void QDeclarativeGridView::moveCurrentIndexUp() void QDeclarativeGridView::moveCurrentIndexDown() { Q_D(QDeclarativeGridView); - const int count = d->model ? d->model->count() : 0; + const int count = d->modelCount; if (!count) return; if (d->flow == QDeclarativeGridView::LeftToRight) { @@ -2058,7 +1872,7 @@ void QDeclarativeGridView::moveCurrentIndexDown() void QDeclarativeGridView::moveCurrentIndexLeft() { Q_D(QDeclarativeGridView); - const int count = d->model ? d->model->count() : 0; + const int count = d->modelCount; if (!count) return; if (d->flow == QDeclarativeGridView::LeftToRight) { @@ -2086,7 +1900,7 @@ void QDeclarativeGridView::moveCurrentIndexLeft() void QDeclarativeGridView::moveCurrentIndexRight() { Q_D(QDeclarativeGridView); - const int count = d->model ? d->model->count() : 0; + const int count = d->modelCount; if (!count) return; if (d->flow == QDeclarativeGridView::LeftToRight) { @@ -2137,7 +1951,7 @@ void QDeclarativeGridView::moveCurrentIndexRight() void QDeclarativeGridView::positionViewAtIndex(int index, int mode) { Q_D(QDeclarativeGridView); - if (!d->isValid() || index < 0 || index >= d->model->count()) + if (!d->isValid() || index < 0 || index >= d->modelCount) return; if (mode < Beginning || mode > Contain) return; @@ -2182,15 +1996,12 @@ void QDeclarativeGridView::positionViewAtIndex(int index, int mode) if (itemPos < pos) pos = itemPos; } - qreal maxExtent = d->flow == QDeclarativeGridView::LeftToRight ? -maxYExtent() : -maxXExtent(); - pos = qMin(pos, maxExtent); - qreal minExtent = d->flow == QDeclarativeGridView::LeftToRight ? -minYExtent() : -minXExtent(); - pos = qMax(pos, minExtent); + pos += d->headerSize(); + pos = qMin(pos, maxExtent() - d->size()); + pos = qMax(pos, minExtent()); d->moveReason = QDeclarativeGridViewPrivate::Other; - cancelFlick(); d->setPosition(pos); } - d->fixupPosition(); } /*! @@ -2221,95 +2032,43 @@ void QDeclarativeGridView::componentComplete() { Q_D(QDeclarativeGridView); QDeclarativeFlickable::componentComplete(); - d->updateGrid(); + d->layout(); if (d->isValid()) { refill(); d->moveReason = QDeclarativeGridViewPrivate::SetIndex; if (d->currentIndex < 0 && !d->currentIndexCleared) - d->updateCurrent(0); + d->setCurrentIndex(0); else - d->updateCurrent(d->currentIndex); + d->setCurrentIndex(d->currentIndex); if (d->highlight && d->currentItem) { d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); - d->updateTrackedItem(); } - d->moveReason = QDeclarativeGridViewPrivate::Other; - d->fixupPosition(); + d->updateHighlight(false); } } -void QDeclarativeGridView::trackedPositionChanged() +void QDeclarativeGridView::itemsInserted(int modelIndex, int count) { Q_D(QDeclarativeGridView); - if (!d->trackedItem || !d->currentItem) + + if (!isComponentComplete()) { + d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count return; - if (d->moveReason == QDeclarativeGridViewPrivate::SetIndex) { - const qreal trackedPos = d->trackedItem->rowPos(); - const qreal viewPos = d->position(); - qreal pos = viewPos; - if (d->haveHighlightRange) { - if (d->highlightRange == StrictlyEnforceRange) { - if (trackedPos > pos + d->highlightRangeEnd - d->rowSize()) - pos = trackedPos - d->highlightRangeEnd + d->rowSize(); - if (trackedPos < pos + d->highlightRangeStart) - pos = trackedPos - d->highlightRangeStart; - } else { - if (trackedPos < d->startPosition() + d->highlightRangeStart) { - pos = d->startPosition(); - } else if (d->trackedItem->endRowPos() > d->endPosition() - d->size() + d->highlightRangeEnd) { - pos = d->endPosition() - d->size() + 1; - if (pos < d->startPosition()) - pos = d->startPosition(); - } else { - if (trackedPos < viewPos + d->highlightRangeStart) { - pos = trackedPos - d->highlightRangeStart; - } else if (trackedPos > viewPos + d->highlightRangeEnd - d->rowSize()) { - pos = trackedPos - d->highlightRangeEnd + d->rowSize(); - } - } - } - } else { - if (trackedPos < viewPos && d->currentItem->rowPos() < viewPos) { - pos = d->currentItem->rowPos() < trackedPos ? trackedPos : d->currentItem->rowPos(); - } else if (d->trackedItem->endRowPos() >= viewPos + d->size() - && d->currentItem->endRowPos() >= viewPos + d->size()) { - if (d->trackedItem->endRowPos() <= d->currentItem->endRowPos()) { - pos = d->trackedItem->endRowPos() - d->size() + 1; - if (d->rowSize() > d->size()) - pos = trackedPos; - } else { - pos = d->currentItem->endRowPos() - d->size() + 1; - if (d->rowSize() > d->size()) - pos = d->currentItem->rowPos(); - } - } - } - if (viewPos != pos) { - cancelFlick(); - d->calcVelocity = true; - d->setPosition(pos); - d->calcVelocity = false; - } } -} -void QDeclarativeGridView::itemsInserted(int modelIndex, int count) -{ - Q_D(QDeclarativeGridView); - if (!isComponentComplete()) - return; + d->moveReason = QDeclarativeGridViewPrivate::Other; if (!d->visibleItems.count() || d->model->count() <= 1) { d->scheduleLayout(); - if (d->itemCount && d->currentIndex >= modelIndex) { + if (d->modelCount && d->currentIndex >= modelIndex) { // adjust current item index d->currentIndex += count; if (d->currentItem) d->currentItem->index = d->currentIndex; emit currentIndexChanged(); } else if (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared)) { - d->updateCurrent(0); + d->setCurrentIndex(0); } - d->itemCount += count; + d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count emit countChanged(); return; } @@ -2340,7 +2099,7 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) emit currentIndexChanged(); } d->scheduleLayout(); - d->itemCount += count; + d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count emit countChanged(); return; } @@ -2414,7 +2173,7 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) } } - if (d->itemCount && d->currentIndex >= modelIndex) { + if (d->modelCount && d->currentIndex >= modelIndex) { // adjust current item index d->currentIndex += count; if (d->currentItem) { @@ -2428,17 +2187,17 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) for (int j = 0; j < added.count(); ++j) added.at(j)->attached->emitAdd(); - d->itemCount += count; + d->modelCount += count; emit countChanged(); } void QDeclarativeGridView::itemsRemoved(int modelIndex, int count) { Q_D(QDeclarativeGridView); + d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count if (!isComponentComplete()) return; - d->itemCount -= count; bool currentRemoved = d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count; bool removedVisible = false; @@ -2486,8 +2245,8 @@ void QDeclarativeGridView::itemsRemoved(int modelIndex, int count) d->releaseItem(d->currentItem); d->currentItem = 0; d->currentIndex = -1; - if (d->itemCount) - d->updateCurrent(qMin(modelIndex, d->itemCount-1)); + if (d->modelCount) + d->setCurrentIndex(qMin(modelIndex, d->modelCount-1)); } // update visibleIndex @@ -2500,13 +2259,7 @@ void QDeclarativeGridView::itemsRemoved(int modelIndex, int count) } if (removedVisible && d->visibleItems.isEmpty()) { - d->timeline.clear(); - if (d->itemCount == 0) { d->setPosition(0); - d->updateHeader(); - d->updateFooter(); - update(); - } } emit countChanged(); @@ -2634,14 +2387,13 @@ void QDeclarativeGridView::modelReset() { Q_D(QDeclarativeGridView); d->clear(); + d->modelCount = d->model->count(); refill(); d->moveReason = QDeclarativeGridViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); + d->setCurrentIndex(d->currentIndex); if (d->highlight && d->currentItem) { d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); - d->updateTrackedItem(); } - d->moveReason = QDeclarativeGridViewPrivate::Other; emit countChanged(); } @@ -2666,14 +2418,6 @@ void QDeclarativeGridView::destroyingItem(QDeclarativeItem *item) d->unrequestedItems.remove(item); } -void QDeclarativeGridView::animStopped() -{ - Q_D(QDeclarativeGridView); - d->bufferMode = QDeclarativeGridViewPrivate::NoBuffer; - if (d->haveHighlightRange && d->highlightRange == QDeclarativeGridView::StrictlyEnforceRange) - d->updateHighlight(); -} - void QDeclarativeGridView::refill() { Q_D(QDeclarativeGridView); diff --git a/src/declarative/graphicsitems/qdeclarativegridview_p.h b/src/declarative/graphicsitems/qdeclarativegridview_p.h index ee632b1..22407f4 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview_p.h +++ b/src/declarative/graphicsitems/qdeclarativegridview_p.h @@ -188,18 +188,22 @@ Q_SIGNALS: void headerChanged(); void footerChanged(); +protected Q_SLOTS: + void scrollerStateChanged(QScroller::State state); + protected: virtual bool event(QEvent *event); - virtual void viewportMoved(); + virtual qreal minExtent() const; + virtual qreal maxExtent() const; virtual qreal minYExtent() const; virtual qreal maxYExtent() const; virtual qreal minXExtent() const; virtual qreal maxXExtent() const; + virtual void viewportAboutToMove(QPointF newPos); virtual void keyPressEvent(QKeyEvent *); virtual void componentComplete(); private Q_SLOTS: - void trackedPositionChanged(); void itemsInserted(int index, int count); void itemsRemoved(int index, int count); void itemsMoved(int from, int to, int count); @@ -207,7 +211,6 @@ private Q_SLOTS: void destroyRemoved(); void createdItem(int index, QDeclarativeItem *item); void destroyingItem(QDeclarativeItem *item); - void animStopped(); private: void refill(); diff --git a/src/declarative/graphicsitems/qdeclarativelistview.cpp b/src/declarative/graphicsitems/qdeclarativelistview.cpp index 450b6af..27f7b52 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview.cpp +++ b/src/declarative/graphicsitems/qdeclarativelistview.cpp @@ -56,6 +56,11 @@ QT_BEGIN_NAMESPACE +template static const T &forceConst(const T &t) +{ + return t; +} + void QDeclarativeViewSection::setProperty(const QString &property) { if (property != m_property) { @@ -93,40 +98,62 @@ QString QDeclarativeViewSection::sectionString(const QString &value) class FxListItem { public: - FxListItem(QDeclarativeItem *i, QDeclarativeListView *v) : item(i), section(0), view(v) { + FxListItem(QDeclarativeItem *i, QDeclarativeListView *v) + : item(i), section(0), view(v) + { attached = static_cast(qmlAttachedPropertiesObject(item)); if (attached) attached->setView(view); } + ~FxListItem() {} + + /*! /internal + The position of the item plus section header. + */ qreal position() const { if (section) return (view->orientation() == QDeclarativeListView::Vertical ? section->y() : section->x()); else return (view->orientation() == QDeclarativeListView::Vertical ? item->y() : item->x()); } + + /*! /internal + The position of the item itself + */ qreal itemPosition() const { return (view->orientation() == QDeclarativeListView::Vertical ? item->y() : item->x()); } + + /*! /internal + The size of the item plus section header. + */ qreal size() const { if (section) return (view->orientation() == QDeclarativeListView::Vertical ? item->height()+section->height() : item->width()+section->width()); else return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width()); } + + /*! /internal + The size of the item itself + */ qreal itemSize() const { return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width()); } + qreal sectionSize() const { if (section) return (view->orientation() == QDeclarativeListView::Vertical ? section->height() : section->width()); return 0.0; } + qreal endPosition() const { return (view->orientation() == QDeclarativeListView::Vertical ? item->y() + (item->height() >= 1.0 ? item->height() : 1) : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1; } + void setPosition(qreal pos) { if (view->orientation() == QDeclarativeListView::Vertical) { if (section) { @@ -142,12 +169,14 @@ public: item->setX(pos); } } + void setSize(qreal size) { if (view->orientation() == QDeclarativeListView::Vertical) item->setHeight(size); else item->setWidth(size); } + bool contains(int x, int y) const { return (x >= item->x() && x < item->x() + item->width() && y >= item->y() && y < item->y() + item->height()); @@ -171,19 +200,22 @@ public: : currentItem(0), orient(QDeclarativeListView::Vertical) , visiblePos(0), visibleIndex(0) , averageSize(100.0), currentIndex(-1), requestedIndex(-1) - , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0) - , highlightComponent(0), highlight(0), trackedItem(0) + , modelCount(0) + , highlightRangeStart(0), highlightRangeEnd(0) + , highlightComponent(0), highlight(0) , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0) , sectionCriteria(0), spacing(0.0) , highlightMoveSpeed(400), highlightMoveDuration(-1) , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QDeclarativeListView::NoHighlightRange) - , snapMode(QDeclarativeListView::NoSnap), overshootDist(0.0) - , footerComponent(0), footer(0), headerComponent(0), header(0) + , snapMode(QDeclarativeListView::NoSnap) + , footerComponent(0), footer(0) + , headerComponent(0), header(0) , bufferMode(BufferBefore | BufferAfter) , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false) - , correctFlick(false), inFlickCorrection(false), lazyRelease(false) - , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false) - , inViewportMoved(false) + , lazyRelease(false) + , deferredRelease(false) + , layoutScheduled(false) + , currentIndexCleared(false) , minExtentDirty(true), maxExtentDirty(true) {} @@ -228,10 +260,25 @@ public: return 0; } + /*! \internal + Returns the last visible model index. delayRemove items are not counted (as they don't have an index) + */ + int lastVisibleIndex() const + { + // find the last valid model index + for (int i = visibleItems.count() - 1; i >= 0; --i) { + FxListItem *item = visibleItems.at(i); + if (item->index != -1) + return item->index; + } + return visibleIndex; + } + qreal position() const { Q_Q(const QDeclarativeListView); return orient == QDeclarativeListView::Vertical ? q->contentY() : q->contentX(); } + void setPosition(qreal pos) { Q_Q(QDeclarativeListView); if (orient == QDeclarativeListView::Vertical) @@ -239,33 +286,53 @@ public: else q->QDeclarativeFlickable::setContentX(pos); } + + void scrollToPosition(qreal pos) { + Q_Q(QDeclarativeListView); + if (orient == QDeclarativeListView::Vertical) + QScroller::scroller(q)->scrollTo(QPointF(q->contentX(), pos)); + else + QScroller::scroller(q)->scrollTo(QPointF(pos, q->contentY())); + } + qreal size() const { Q_Q(const QDeclarativeListView); return orient == QDeclarativeListView::Vertical ? q->height() : q->width(); } + /*! /internal + Estimates the current position of the first visible item counting the header (the first item starts at position header->size()). + */ qreal startPosition() const { qreal pos = 0; if (!visibleItems.isEmpty()) { - pos = (*visibleItems.constBegin())->position(); - if (visibleIndex > 0) - pos -= visibleIndex * (averageSize + spacing); + pos = visibleItems.first()->position(); + pos -= visibleIndex * (averageSize + spacing); } + if( header ) + pos -= header->size(); return pos; } + /*! /internal + Estimates the full length of the list including header and footer. + */ qreal endPosition() const { - qreal pos = 0; + qreal pos = -1; if (!visibleItems.isEmpty()) { int invisibleCount = visibleItems.count() - visibleIndex; + for (int i = visibleItems.count()-1; i >= 0; --i) { if (visibleItems.at(i)->index != -1) { - invisibleCount = model->count() - visibleItems.at(i)->index - 1; + invisibleCount = modelCount - visibleItems.at(i)->index - 1; + pos = visibleItems.at(i)->endPosition(); break; } } - pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing); + pos = visibleItems.last()->endPosition() + invisibleCount * (averageSize + spacing); } + if (footer) + pos += footer->size(); return pos; } @@ -280,17 +347,10 @@ public: cs = currentItem->size() + spacing; --count; } - return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs; + return forceConst(visibleItems).first()->position() - count * (averageSize + spacing) - cs; } else { - int idx = visibleItems.count() - 1; - while (idx >= 0 && visibleItems.at(idx)->index == -1) - --idx; - if (idx < 0) - idx = visibleIndex; - else - idx = visibleItems.at(idx)->index; - int count = modelIndex - idx - 1; - return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1; + int count = modelIndex - lastVisibleIndex() - 1; + return forceConst(visibleItems).last()->endPosition() + spacing + count * (averageSize + spacing) + 1; } } return 0; @@ -302,17 +362,10 @@ public: if (!visibleItems.isEmpty()) { if (modelIndex < visibleIndex) { int count = visibleIndex - modelIndex; - return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1; + return forceConst(visibleItems).first()->position() - (count - 1) * (averageSize + spacing) - spacing - 1; } else { - int idx = visibleItems.count() - 1; - while (idx >= 0 && visibleItems.at(idx)->index == -1) - --idx; - if (idx < 0) - idx = visibleIndex; - else - idx = visibleItems.at(idx)->index; - int count = modelIndex - idx - 1; - return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing); + int count = modelIndex - lastVisibleIndex() - 1; + return forceConst(visibleItems).last()->endPosition() + count * (averageSize + spacing); } } return 0; @@ -332,52 +385,32 @@ public: } bool isValid() const { - return model && model->count() && model->isValid(); + return model && modelCount && model->isValid(); } - qreal snapPosAt(qreal pos) { - if (FxListItem *snapItem = snapItemAt(pos)) - return snapItem->position(); - if (visibleItems.count()) { - qreal firstPos = visibleItems.first()->position(); - qreal endPos = visibleItems.last()->position(); - if (pos < firstPos) { - return firstPos - qRound((firstPos - pos) / averageSize) * averageSize; - } else if (pos > endPos) - return endPos + qRound((pos - endPos) / averageSize) * averageSize; - } - return qRound((pos - startPosition()) / averageSize) * averageSize + startPosition(); - } + /** \internal + Returns the index of the item which is at or around the highlight. + */ + int snapIndex() { + int index = currentIndex; - FxListItem *snapItemAt(qreal pos) { - FxListItem *snapItem = 0; + // qDebug() << "snapIndex" << index << "hp:" << highlight->position() << highlight->size() << "items:" << visibleItems.count(); for (int i = 0; i < visibleItems.count(); ++i) { FxListItem *item = visibleItems[i]; if (item->index == -1) continue; + index = item->index; qreal itemTop = item->position(); - if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1) - return item; - if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos) - snapItem = item; - } - return snapItem; - } - - int lastVisibleIndex() const { - int lastIndex = -1; - for (int i = visibleItems.count()-1; i >= 0; --i) { - FxListItem *listItem = visibleItems.at(i); - if (listItem->index != -1) { - lastIndex = listItem->index; - break; - } + // qDebug() << " "<size(); + if (itemTop + item->size() / 2 > highlight->position()) + return item->index; } - return lastIndex; + return index; } // map a model index to visibleItems index. - int mapFromModel(int modelIndex) const { + int mapFromModel(int modelIndex) const + { if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) return -1; for (int i = 0; i < visibleItems.count(); ++i) { @@ -390,22 +423,28 @@ public: return -1; // Not in visibleList } - void updateViewport() { + void updateViewport() + { Q_Q(QDeclarativeListView); - if (orient == QDeclarativeListView::Vertical) { - q->setContentHeight(endPosition() - startPosition() + 1); - } else { - q->setContentWidth(endPosition() - startPosition() + 1); - } + if (orient == QDeclarativeListView::Vertical) + q->setContentHeight(q->maxExtent() - q->minExtent() + 1); + else + q->setContentWidth(q->maxExtent() - q->minExtent() + 1); } - void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { - Q_Q(QDeclarativeListView); + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) + { + + // qDebug() << "itemGeometryChanged" << newGeometry; + + // Q_Q(QDeclarativeListView); QDeclarativeFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); if (item != contentItem && (!highlight || item != highlight->item)) { if ((orient == QDeclarativeListView::Vertical && newGeometry.height() != oldGeometry.height()) || (orient == QDeclarativeListView::Horizontal && newGeometry.width() != oldGeometry.width())) { scheduleLayout(); + minExtentDirty = true; + maxExtentDirty = true; } } if ((header && header->item == item) || (footer && footer->item == item)) { @@ -414,8 +453,6 @@ public: } if (currentItem && currentItem->item == item) updateHighlight(); - if (trackedItem && trackedItem->item == item) - q->trackedPositionChanged(); } // for debugging only @@ -436,40 +473,60 @@ public: void layout(); void updateUnrequestedIndexes(); void updateUnrequestedPositions(); - void updateTrackedItem(); - void createHighlight(); - void updateHighlight(); + void recreateHighlight(); + void updateHighlight(bool smooth = true); void createSection(FxListItem *); void updateSections(); void updateCurrentSection(); - void updateCurrent(int); + void setCurrentIndex(int); void updateAverage(); void updateHeader(); void updateFooter(); - void fixupPosition(); - virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); - virtual void flick(QDeclarativeFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); QDeclarativeGuard model; QVariant modelVariant; QList visibleItems; + + /* + These are items that were not requested but send by createdItem from the model. + Usually these items were created because another view showing the same model + needed them. + */ QHash unrequestedItems; + FxListItem *currentItem; QDeclarativeListView::Orientation orient; + + /* + This is the position of the first visible item. + It is stored in case when all items are deleted and we need to create new + ones. + */ qreal visiblePos; + + /* + The visibleIndex is the modelIndex of the first item in visibleItems with an index != -1 + */ int visibleIndex; qreal averageSize; - int currentIndex; + int currentIndex; // the model index of the currentItem + + /* + The model index of the item that is currently created via createItem + */ int requestedIndex; - int itemCount; + + /* + This is the number of items in the model. + */ + int modelCount; + qreal highlightRangeStart; qreal highlightRangeEnd; QDeclarativeComponent *highlightComponent; FxListItem *highlight; - FxListItem *trackedItem; enum MovementReason { Other, SetIndex, Mouse }; - MovementReason moveReason; + MovementReason moveReason; // the moveReason determines if the highlight needs to be centered or if the currentItem needs to be changed. int buffer; QSmoothedAnimation *highlightPosAnimator; QSmoothedAnimation *highlightSizeAnimator; @@ -484,7 +541,6 @@ public: int highlightResizeDuration; QDeclarativeListView::HighlightRangeMode highlightRange; QDeclarativeListView::SnapMode snapMode; - qreal overshootDist; QDeclarativeComponent *footerComponent; FxListItem *footer; QDeclarativeComponent *headerComponent; @@ -498,13 +554,10 @@ public: bool wrap : 1; bool autoHighlight : 1; bool haveHighlightRange : 1; - bool correctFlick : 1; - bool inFlickCorrection : 1; bool lazyRelease : 1; bool deferredRelease : 1; bool layoutScheduled : 1; bool currentIndexCleared : 1; - bool inViewportMoved : 1; mutable bool minExtentDirty : 1; mutable bool maxExtentDirty : 1; }; @@ -514,14 +567,12 @@ void QDeclarativeListViewPrivate::init() Q_Q(QDeclarativeListView); q->setFlag(QGraphicsItem::ItemIsFocusScope); addItemChangeListener(this, Geometry); - QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick); ::memset(sectionCache, 0, sizeof(QDeclarativeItem*) * sectionCacheSize); } void QDeclarativeListViewPrivate::clear() { - timeline.clear(); for (int i = 0; i < visibleItems.count(); ++i) releaseItem(visibleItems.at(i)); visibleItems.clear(); @@ -533,13 +584,20 @@ void QDeclarativeListViewPrivate::clear() visibleIndex = 0; releaseItem(currentItem); currentItem = 0; - createHighlight(); - trackedItem = 0; + recreateHighlight(); minExtentDirty = true; maxExtentDirty = true; - itemCount = 0; + modelCount = 0; + + setPosition(0); + updateHeader(); + updateFooter(); } +/** \internal + Tries to create the FxListItem for the given model index. + \returns the item or 0 if the item could not be created. +*/ FxListItem *QDeclarativeListViewPrivate::createItem(int modelIndex) { Q_Q(QDeclarativeListView); @@ -559,13 +617,16 @@ FxListItem *QDeclarativeListViewPrivate::createItem(int modelIndex) else listItem->attached->m_prevSection = sectionAt(modelIndex-1); } - if (modelIndex < model->count()-1) { + if (modelIndex < modelCount - 1) { if (FxListItem *item = visibleItem(modelIndex+1)) listItem->attached->m_nextSection = item->attached->section(); else listItem->attached->m_nextSection = sectionAt(modelIndex+1); } } + + // qDebug() << "createItem"<completePending()) { // complete listItem->item->setZValue(1); @@ -592,9 +653,10 @@ void QDeclarativeListViewPrivate::releaseItem(FxListItem *item) Q_Q(QDeclarativeListView); if (!item || !model) return; - if (trackedItem == item) - trackedItem = 0; QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item->item)); + + // qDebug() << "releaseItem"<<(currentItem==item?"current":(highlight==item?"highlight":"normal"))<index<<"at"<< item->position(); + itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry); if (model->release(item->item) == 0) { // item was not destroyed, and we no longer reference it. @@ -616,12 +678,20 @@ void QDeclarativeListViewPrivate::releaseItem(FxListItem *item) delete item; } +/** \internal + Updates the visible items so that they cover the from and to range. + updateViewport should be called afterwards to handle cases where + the viewport size is changed by different sized items. +*/ void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer) { Q_Q(QDeclarativeListView); if (!isValid() || !q->isComponentComplete()) return; - itemCount = model->count(); + + if (!doBuffer && buffer && bufferMode != NoBuffer) + doBuffer = true; + qreal bufferFrom = from - buffer; qreal bufferTo = to + buffer; qreal fillFrom = from; @@ -631,34 +701,55 @@ void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer) if (doBuffer && (bufferMode & BufferBefore)) fillFrom = bufferFrom; - int modelIndex = visibleIndex; - qreal itemEnd = visiblePos-1; + bool changed = false; + + // -- layout the items if (!visibleItems.isEmpty()) { - visiblePos = (*visibleItems.constBegin())->position(); - itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing; - int i = visibleItems.count() - 1; - while (i > 0 && visibleItems.at(i)->index == -1) - --i; - modelIndex = visibleItems.at(i)->index + 1; + qreal oldEnd = forceConst(visibleItems).last()->endPosition(); + qreal pos = forceConst(visibleItems).first()->position() + forceConst(visibleItems).first()->size() + spacing; + for (int i=1; i < visibleItems.count(); ++i) { + FxListItem *item = visibleItems.at(i); + if (item->position() != pos) { + // qDebug() << "Moved item"<position()<<"to"<setPosition(pos); + // changed = true; + } + pos += item->size() + spacing; + } + // move current item if it is after the visible items. + if (currentItem && currentIndex > lastVisibleIndex()) + currentItem->setPosition(currentItem->position() + (forceConst(visibleItems).last()->endPosition() - oldEnd)); } - bool changed = false; + qreal endPos = visiblePos; + int modelIndex = lastVisibleIndex(); + if (!visibleItems.isEmpty()) { + endPos = forceConst(visibleItems).last()->endPosition() + spacing + 1; + modelIndex++; + } + + // qDebug() << "REFILL: from" << from << "to" << to << "doBuffer" << doBuffer << "visiblePos" << visiblePos << "endPos" << endPos << "lvindex" << lastVisibleIndex() << "mindex" << modelIndex; + + FxListItem *item = 0; - qreal pos = itemEnd + 1; - while (modelIndex < model->count() && pos <= fillTo) { -// qDebug() << "refill: append item" << modelIndex << "pos" << pos; + + // -- add items to the back + while (modelIndex < modelCount && endPos <= fillTo) { + // qDebug() << "refill: append item" << modelIndex << "endPos" << endPos; if (!(item = createItem(modelIndex))) break; - item->setPosition(pos); - pos += item->size() + spacing; + item->setPosition(endPos); + endPos += item->size() + spacing; visibleItems.append(item); ++modelIndex; changed = true; if (doBuffer) // never buffer more than one item per frame break; } - while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) { -// qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos; + + // -- add items to the front + while (visibleIndex > 0 && visibleIndex <= modelCount && visiblePos-1 >= fillFrom) { + // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos; if (!(item = createItem(visibleIndex-1))) break; --visibleIndex; @@ -670,35 +761,56 @@ void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer) break; } + // TODO: why do we need deferredRelease? Only release every second frame? if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create - while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) { - if (item->attached->delayRemove()) - break; -// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition(); - if (item->index != -1) - visibleIndex++; - visibleItems.removeFirst(); - releaseItem(item); - changed = true; - } - while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) { - if (item->attached->delayRemove()) - break; -// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position(); - visibleItems.removeLast(); - releaseItem(item); - changed = true; + if (visibleItems.count() > 1) { + // only delete items if we have more than could be visible + if (forceConst(visibleItems).last()->endPosition() - forceConst(visibleItems).first()->position() > size() ) { + while (!visibleItems.isEmpty() && + (item = forceConst(visibleItems).first()) && item->endPosition() < bufferFrom) { + if (item->attached->delayRemove()) + break; + // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition(); + if (item->index != -1) + { + visibleIndex++; + // visiblePos += item->size() + spacing; + } + visibleItems.removeFirst(); + releaseItem(item); + changed = true; + } + while (!visibleItems.isEmpty() && + (item = forceConst(visibleItems).last()) && item->position() > bufferTo) { + if (item->attached->delayRemove()) + break; + // qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position(); + if (item->index != -1) + { + // endPos -= item->size() + spacing; + } + visibleItems.removeLast(); + releaseItem(item); + changed = true; + } + } } deferredRelease = false; } else { deferredRelease = true; } + + + if (changed) { + // qDebug()<<"REFILLED: from" << from << "to" << to << "fillFrom" << fillFrom << "fillTo" << fillTo << "endPos:" << endPos << "buffer" << buffer; minExtentDirty = true; maxExtentDirty = true; if (visibleItems.count()) - visiblePos = (*visibleItems.constBegin())->position(); + visiblePos = forceConst(visibleItems).first()->position(); updateAverage(); + + // update the highlight position in cases where we are estimating it if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) { currentItem->setPosition(positionAt(currentIndex)); updateHighlight(); @@ -706,16 +818,15 @@ void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer) if (sectionCriteria) updateCurrentSection(); - if (header) - updateHeader(); - if (footer) - updateFooter(); - updateViewport(); updateUnrequestedPositions(); - } else if (!doBuffer && buffer && bufferMode != NoBuffer) { - refill(from, to, true); + updateScrollerValues(); + updateViewport(); } lazyRelease = false; + if (header) + updateHeader(); + if (footer) + updateFooter(); } void QDeclarativeListViewPrivate::scheduleLayout() @@ -736,31 +847,11 @@ void QDeclarativeListViewPrivate::layout() setPosition(0); return; } - if (!visibleItems.isEmpty()) { - qreal oldEnd = visibleItems.last()->endPosition(); - qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing; - for (int i=1; i < visibleItems.count(); ++i) { - FxListItem *item = visibleItems.at(i); - item->setPosition(pos); - pos += item->size() + spacing; - } - // move current item if it is after the visible items. - if (currentItem && currentIndex > lastVisibleIndex()) - currentItem->setPosition(currentItem->position() + (visibleItems.last()->endPosition() - oldEnd)); - } q->refill(); - minExtentDirty = true; - maxExtentDirty = true; + // qDebug() << "highlight1" << currentItem << highlight; updateHighlight(); - if (!q->isMoving() && !q->isFlicking()) { - fixupPosition(); - q->refill(); - } - if (header) - updateHeader(); - if (footer) - updateFooter(); - updateViewport(); + // qDebug() << "highlight2" << currentItem << highlight; + q->refill(); } void QDeclarativeListViewPrivate::updateUnrequestedIndexes() @@ -790,24 +881,13 @@ void QDeclarativeListViewPrivate::updateUnrequestedPositions() } } -void QDeclarativeListViewPrivate::updateTrackedItem() +void QDeclarativeListViewPrivate::recreateHighlight() { Q_Q(QDeclarativeListView); - FxListItem *item = currentItem; - if (highlight) - item = highlight; - trackedItem = item; - if (trackedItem) - q->trackedPositionChanged(); -} -void QDeclarativeListViewPrivate::createHighlight() -{ - Q_Q(QDeclarativeListView); + // qDebug() << "recreate Highlight"; bool changed = false; if (highlight) { - if (trackedItem == highlight) - trackedItem = 0; delete highlight->item; delete highlight; highlight = 0; @@ -819,6 +899,7 @@ void QDeclarativeListViewPrivate::createHighlight() } if (currentItem) { + // qDebug()<<"new highlight"; QDeclarativeItem *item = 0; if (highlightComponent) { QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q)); @@ -865,29 +946,76 @@ void QDeclarativeListViewPrivate::createHighlight() changed = true; } } - if (changed) + if (changed) { + updateHighlight(); emit q->highlightItemChanged(); + } } -void QDeclarativeListViewPrivate::updateHighlight() +/* + This function will recreate or delete the highlight (depening on the state) and + then try to move it to a correct position. + It will not update the current item (that is done in viewportMoved() + If \c smooth is set to true then all updates will be using an animation. +*/ +void QDeclarativeListViewPrivate::updateHighlight(bool smooth) { + if ((!currentItem && highlight) || (currentItem && !highlight)) - createHighlight(); - if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) { - // auto-update highlight - highlightPosAnimator->to = currentItem->itemPosition(); - highlightSizeAnimator->to = currentItem->itemSize(); - if (orient == QDeclarativeListView::Vertical) { - if (highlight->item->width() == 0) - highlight->item->setWidth(currentItem->item->width()); - } else { - if (highlight->item->height() == 0) - highlight->item->setHeight(currentItem->item->height()); + recreateHighlight(); + + // --- move the current item between the highlight range + if (moveReason == QDeclarativeListViewPrivate::SetIndex) { + // ensure that the tracked item is inside the highlight range + + // reposition view + if (currentItem) { + qreal pos = currentItem->position(); + qreal viewPos = position(); + + if (autoHighlight && haveHighlightRange) { + if (pos > viewPos + highlightRangeEnd - currentItem->size()) + viewPos = pos - highlightRangeEnd + currentItem->size(); + if (pos < viewPos + highlightRangeStart) + viewPos = pos - highlightRangeStart; + + } else { + if (pos > viewPos + size() - currentItem->size()) + viewPos = pos - size() + currentItem->size(); + if (pos < viewPos + 0) + viewPos = pos - 0; + } + if (smooth) + scrollToPosition(viewPos); + else + { + setPosition(viewPos); + // qDebug() << "setPos to "<to = currentItem->itemPosition(); + highlightSizeAnimator->to = currentItem->itemSize(); + if (orient == QDeclarativeListView::Vertical) { + if (highlight->item->width() == 0) + highlight->item->setWidth(currentItem->item->width()); + } else { + if (highlight->item->height() == 0) + highlight->item->setHeight(currentItem->item->height()); + } + if (smooth) { + highlightPosAnimator->restart(); + highlightSizeAnimator->restart(); + } else { + highlightPosAnimator->stop(); + highlightSizeAnimator->stop(); + highlight->setPosition(highlightPosAnimator->to); + highlight->setSize(highlightSizeAnimator->to); + } } - highlightPosAnimator->restart(); - highlightSizeAnimator->restart(); } - updateTrackedItem(); } void QDeclarativeListViewPrivate::createSection(FxListItem *listItem) @@ -968,14 +1096,17 @@ void QDeclarativeListViewPrivate::updateSections() } } if (prevAtt) { - if (idx > 0 && idx < model->count()-1) - prevAtt->setNextSection(sectionAt(idx+1)); + if (idx > 0 && idx < modelCount - 1) + prevAtt->setNextSection(sectionAt(idx + 1)); else prevAtt->setNextSection(QString()); } } } +/** \internal + Updates the currentSection variable and emits the changed signals. +*/ void QDeclarativeListViewPrivate::updateCurrentSection() { Q_Q(QDeclarativeListView); @@ -986,6 +1117,7 @@ void QDeclarativeListViewPrivate::updateCurrentSection() } return; } + int index = 0; while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position()) ++index; @@ -994,61 +1126,56 @@ void QDeclarativeListViewPrivate::updateCurrentSection() if (index < visibleItems.count()) newSection = visibleItems.at(index)->attached->section(); else - newSection = visibleItems.first()->attached->section(); + newSection = forceConst(visibleItems).first()->attached->section(); if (newSection != currentSection) { currentSection = newSection; emit q->currentSectionChanged(); } } -void QDeclarativeListViewPrivate::updateCurrent(int modelIndex) +void QDeclarativeListViewPrivate::setCurrentIndex(int newIndex) { Q_Q(QDeclarativeListView); - if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { + + // --- release the old item + if (currentItem && currentIndex != newIndex) { + currentItem->attached->setIsCurrentItem(false); + releaseItem(currentItem); + currentItem = 0; + } + + int oldIndex = currentIndex; + currentIndex = newIndex; + + bool visible = (q->isComponentComplete() && isValid() && !currentItem && + newIndex >= 0 && newIndex < modelCount); + + // --- set the new item + if (visible) { + currentItem = createItem(currentIndex); if (currentItem) { - currentItem->attached->setIsCurrentItem(false); - releaseItem(currentItem); - currentItem = 0; - currentIndex = modelIndex; - emit q->currentIndexChanged(); - updateHighlight(); - } else if (currentIndex != modelIndex) { - currentIndex = modelIndex; - emit q->currentIndexChanged(); + if (currentIndex == visibleIndex - 1 && visibleItems.count()) { + // We can calculate exact postion in this case + currentItem->setPosition(forceConst(visibleItems).first()->position() - currentItem->size() - spacing); + } else { + // Create current item now and position as best we can. + // Its position will be corrected when it becomes visible. + currentItem->setPosition(positionAt(currentIndex)); + } + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + // Avoid showing section delegate twice. We still need the section heading so that + // currentItem positioning works correctly. + // This is slightly sub-optimal, but section heading caching minimizes the impact. + if (currentItem->section) + currentItem->section->setVisible(false); } - return; } - if (currentItem && currentIndex == modelIndex) { - updateHighlight(); - return; - } - FxListItem *oldCurrentItem = currentItem; - currentIndex = modelIndex; - currentItem = createItem(modelIndex); - if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) - oldCurrentItem->attached->setIsCurrentItem(false); - if (currentItem) { - if (modelIndex == visibleIndex - 1 && visibleItems.count()) { - // We can calculate exact postion in this case - currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing); - } else { - // Create current item now and position as best we can. - // Its position will be corrected when it becomes visible. - currentItem->setPosition(positionAt(modelIndex)); - } - currentItem->item->setFocus(true); - currentItem->attached->setIsCurrentItem(true); - // Avoid showing section delegate twice. We still need the section heading so that - // currentItem positioning works correctly. - // This is slightly sub-optimal, but section heading caching minimizes the impact. - if (currentItem->section) - currentItem->section->setVisible(false); - } - updateHighlight(); - emit q->currentIndexChanged(); - // Release the old current item - releaseItem(oldCurrentItem); + updateHighlight(visible); + + if (currentIndex != oldIndex) + emit q->currentIndexChanged(); } void QDeclarativeListViewPrivate::updateAverage() @@ -1061,13 +1188,13 @@ void QDeclarativeListViewPrivate::updateAverage() averageSize = qRound(sum / visibleItems.count()); } -void QDeclarativeListViewPrivate::updateFooter() +void QDeclarativeListViewPrivate::updateHeader() { Q_Q(QDeclarativeListView); - if (!footer && footerComponent) { + if (!header && headerComponent) { QDeclarativeItem *item = 0; QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); - QObject *nobj = footerComponent->create(context); + QObject *nobj = headerComponent->create(context); if (nobj) { QDeclarative_setParent_noEvent(context, nobj); item = qobject_cast(nobj); @@ -1079,35 +1206,26 @@ void QDeclarativeListViewPrivate::updateFooter() if (item) { QDeclarative_setParent_noEvent(item, q->contentItem()); item->setParentItem(q->contentItem()); - item->setZValue(1); + item->setZValue(100); QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); - footer = new FxListItem(item, q); + header = new FxListItem(item, q); + if (!visibleItems.isEmpty()) + visiblePos = header->size(); } } - if (footer) { - if (visibleItems.count()) { - qreal endPos = endPosition() + 1; - if (lastVisibleIndex() == model->count()-1) { - footer->setPosition(endPos); - } else { - qreal visiblePos = position() + q->height(); - if (endPos <= visiblePos || footer->position() < endPos) - footer->setPosition(endPos); - } - } else { - footer->setPosition(visiblePos); - } + if (header) { + header->setPosition(startPosition()); } } -void QDeclarativeListViewPrivate::updateHeader() +void QDeclarativeListViewPrivate::updateFooter() { Q_Q(QDeclarativeListView); - if (!header && headerComponent) { + if (!footer && footerComponent) { QDeclarativeItem *item = 0; QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); - QObject *nobj = headerComponent->create(context); + QObject *nobj = footerComponent->create(context); if (nobj) { QDeclarative_setParent_noEvent(context, nobj); item = qobject_cast(nobj); @@ -1122,233 +1240,11 @@ void QDeclarativeListViewPrivate::updateHeader() item->setZValue(1); QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); - header = new FxListItem(item, q); - if (visibleItems.isEmpty()) - visiblePos = header->size(); - } - } - if (header) { - if (visibleItems.count()) { - qreal startPos = startPosition(); - if (visibleIndex == 0) { - header->setPosition(startPos - header->size()); - } else { - if (position() <= startPos || header->position() > startPos - header->size()) - header->setPosition(startPos - header->size()); - } - } else { - header->setPosition(0); - } - } -} - -void QDeclarativeListViewPrivate::fixupPosition() -{ - if ((haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange) - || snapMode != QDeclarativeListView::NoSnap) - moveReason = Other; - if (orient == QDeclarativeListView::Vertical) - fixupY(); - else - fixupX(); -} - -void QDeclarativeListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) -{ - if ((orient == QDeclarativeListView::Horizontal && &data == &vData) - || (orient == QDeclarativeListView::Vertical && &data == &hData)) - return; - - correctFlick = false; - int oldDuration = fixupDuration; - fixupDuration = moveReason == Mouse ? fixupDuration : 0; - - if (currentItem && haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange) { - updateHighlight(); - qreal pos = currentItem->itemPosition(); - qreal viewPos = position(); - if (viewPos < pos + currentItem->itemSize() - highlightRangeEnd) - viewPos = pos + currentItem->itemSize() - highlightRangeEnd; - if (viewPos > pos - highlightRangeStart) - viewPos = pos - highlightRangeStart; - - timeline.reset(data.move); - if (viewPos != position()) { - if (fixupDuration) - timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - else - timeline.set(data.move, -viewPos); - } - vTime = timeline.time(); - } else if (snapMode != QDeclarativeListView::NoSnap) { - FxListItem *topItem = snapItemAt(position()+highlightRangeStart); - FxListItem *bottomItem = snapItemAt(position()+highlightRangeEnd); - qreal pos; - if (topItem) { - pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent); - } else if (bottomItem) { - pos = qMax(qMin(bottomItem->position() - highlightRangeStart, -maxExtent), -minExtent); - } else { - fixupDuration = oldDuration; - return; - } - - qreal dist = qAbs(data.move + pos); - if (dist > 0) { - timeline.reset(data.move); - if (fixupDuration) - timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - else - timeline.set(data.move, -pos); - vTime = timeline.time(); - } - } else { - QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); - } - fixupDuration = oldDuration; -} - -void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) -{ - Q_Q(QDeclarativeListView); - - moveReason = Mouse; - if ((!haveHighlightRange || highlightRange != QDeclarativeListView::StrictlyEnforceRange) && snapMode == QDeclarativeListView::NoSnap) { - correctFlick = true; - QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); - return; - } - qreal maxDistance = 0; - // -ve velocity means list is moving up/left - if (velocity > 0) { - if (data.move.value() < minExtent) { - if (snapMode == QDeclarativeListView::SnapOneItem) { - if (FxListItem *item = firstVisibleItem()) - maxDistance = qAbs(item->position() + data.move.value()); - } else { - maxDistance = qAbs(minExtent - data.move.value()); - } - } - if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange) - data.flickTarget = minExtent; - } else { - if (data.move.value() > maxExtent) { - if (snapMode == QDeclarativeListView::SnapOneItem) { - if (FxListItem *item = nextVisibleItem()) - maxDistance = qAbs(item->position() + data.move.value()); - } else { - maxDistance = qAbs(maxExtent - data.move.value()); - } - } - if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange) - data.flickTarget = maxExtent; - } - bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds; - if (maxDistance > 0 || overShoot) { - // These modes require the list to stop exactly on an item boundary. - // The initial flick will estimate the boundary to stop on. - // Since list items can have variable sizes, the boundary will be - // reevaluated and adjusted as we approach the boundary. - qreal v = velocity; - if (maxVelocity != -1 && maxVelocity < qAbs(v)) { - if (v < 0) - v = -maxVelocity; - else - v = maxVelocity; - } - if (!flickingHorizontally && !flickingVertically) { - // the initial flick - estimate boundary - qreal accel = deceleration; - qreal v2 = v * v; - overshootDist = 0.0; - // + averageSize/4 to encourage moving at least one item in the flick direction - qreal dist = v2 / (accel * 2.0) + averageSize/4; - if (maxDistance > 0) - dist = qMin(dist, maxDistance); - if (v > 0) - dist = -dist; - if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeListView::SnapOneItem) { - data.flickTarget = -snapPosAt(-(data.move.value() - highlightRangeStart) + dist) + highlightRangeStart; - if (overShoot) { - if (data.flickTarget >= minExtent) { - overshootDist = overShootDistance(v, vSize); - data.flickTarget += overshootDist; - } else if (data.flickTarget <= maxExtent) { - overshootDist = overShootDistance(v, vSize); - data.flickTarget -= overshootDist; - } - } - qreal adjDist = -data.flickTarget + data.move.value(); - if (qAbs(adjDist) > qAbs(dist)) { - // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration - qreal adjv2 = accel * 2.0f * qAbs(adjDist); - if (adjv2 > v2) { - v2 = adjv2; - v = qSqrt(v2); - if (dist > 0) - v = -v; - } - } - dist = adjDist; - accel = v2 / (2.0f * qAbs(dist)); - } else if (overShoot) { - data.flickTarget = data.move.value() - dist; - if (data.flickTarget >= minExtent) { - overshootDist = overShootDistance(v, vSize); - data.flickTarget += overshootDist; - } else if (data.flickTarget <= maxExtent) { - overshootDist = overShootDistance(v, vSize); - data.flickTarget -= overshootDist; - } - } - timeline.reset(data.move); - timeline.accel(data.move, v, accel, maxDistance + overshootDist); - timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); - if (!flickingHorizontally && q->xflick()) { - flickingHorizontally = true; - emit q->flickingChanged(); - emit q->flickingHorizontallyChanged(); - emit q->flickStarted(); - } - if (!flickingVertically && q->yflick()) { - flickingVertically = true; - emit q->flickingChanged(); - emit q->flickingVerticallyChanged(); - emit q->flickStarted(); - } - correctFlick = true; - } else { - // reevaluate the target boundary. - qreal newtarget = data.flickTarget; - if (snapMode != QDeclarativeListView::NoSnap || highlightRange == QDeclarativeListView::StrictlyEnforceRange) - newtarget = -snapPosAt(-(data.flickTarget - highlightRangeStart)) + highlightRangeStart; - if (velocity < 0 && newtarget <= maxExtent) - newtarget = maxExtent - overshootDist; - else if (velocity > 0 && newtarget >= minExtent) - newtarget = minExtent + overshootDist; - if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do - if (qAbs(velocity) < MinimumFlickVelocity) - correctFlick = false; - return; - } - data.flickTarget = newtarget; - qreal dist = -newtarget + data.move.value(); - if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) { - correctFlick = false; - timeline.reset(data.move); - fixup(data, minExtent, maxExtent); - return; - } - timeline.reset(data.move); - timeline.accelDistance(data.move, v, -dist); - timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + footer = new FxListItem(item, q); } - } else { - correctFlick = false; - timeline.reset(data.move); - fixup(data, minExtent, maxExtent); } + if (footer) + footer->setPosition(endPosition() + 1 - footer->size()); } //---------------------------------------------------------------------------- @@ -1513,10 +1409,10 @@ QVariant QDeclarativeListView::model() const return d->modelVariant; } -void QDeclarativeListView::setModel(const QVariant &model) +void QDeclarativeListView::setModel(const QVariant &newModel) { Q_D(QDeclarativeListView); - if (d->modelVariant == model) + if (d->modelVariant == newModel) return; if (d->model) { disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); @@ -1527,12 +1423,15 @@ void QDeclarativeListView::setModel(const QVariant &model) disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); } + d->clear(); QDeclarativeVisualModel *oldModel = d->model; d->model = 0; - d->setPosition(0); - d->modelVariant = model; - QObject *object = qvariant_cast(model); + d->modelCount = 0; + // d->setPosition(0); // we should do a re-layout and that should do a setViewportHeight and that should check the current position. + + d->modelVariant = newModel; + QObject *object = qvariant_cast(newModel); QDeclarativeVisualModel *vim = 0; if (object && (vim = qobject_cast(object))) { if (d->ownModel) { @@ -1548,24 +1447,14 @@ void QDeclarativeListView::setModel(const QVariant &model) d->model = oldModel; } if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) - dataModel->setModel(model); + dataModel->setModel(newModel); } + if (d->model) { + d->modelCount = d->model->count(); d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter; - if (isComponentComplete()) { - updateSections(); - refill(); - if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { - setCurrentIndex(0); - } else { - d->moveReason = QDeclarativeListViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - d->highlight->setPosition(d->currentItem->position()); - d->updateTrackedItem(); - } - } - } + + //qDebug() << "setModel: " << d->model<<"model:" << isComponentComplete(); connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); @@ -1575,6 +1464,17 @@ void QDeclarativeListView::setModel(const QVariant &model) connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); emit countChanged(); } + + if (isComponentComplete()) { + updateSections(); + refill(); + if ((d->currentIndex < 0 || d->currentIndex >= d->modelCount) && !d->currentIndexCleared) { + setCurrentIndex(0); + } else { + d->setCurrentIndex(d->currentIndex); + } + d->updateViewport(); + } emit modelChanged(); } @@ -1619,9 +1519,11 @@ void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate) d->model = new QDeclarativeVisualDataModel(qmlContext(this)); d->ownModel = true; } + //qDebug() << "setDelegate" << delegate; if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) { dataModel->setDelegate(delegate); if (isComponentComplete()) { + // TODO: why not call clear() ? for (int i = 0; i < d->visibleItems.count(); ++i) d->releaseItem(d->visibleItems.at(i)); d->visibleItems.clear(); @@ -1630,13 +1532,15 @@ void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate) updateSections(); refill(); d->moveReason = QDeclarativeListViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); + d->setCurrentIndex(d->currentIndex); if (d->highlight && d->currentItem) { d->highlight->setPosition(d->currentItem->position()); - d->updateTrackedItem(); } + d->updateHighlight(); + d->updateViewport(); } } + //qDebug() << "setDelegate current" << d->currentIndex << d->currentItem; emit delegateChanged(); } @@ -1648,10 +1552,10 @@ void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate) \c currentItem holds the current item. Setting the currentIndex to -1 will clear the highlight and set currentItem to null. - If highlightFollowsCurrentItem is \c true, setting either of these - properties will smoothly scroll the ListView so that the current + If highlightFollowsCurrentItem is \c true, setting either of these + properties will smoothly scroll the ListView so that the current item becomes visible. - + Note that the position of the current item may only be approximate until it becomes visible in the view. */ @@ -1664,14 +1568,17 @@ int QDeclarativeListView::currentIndex() const void QDeclarativeListView::setCurrentIndex(int index) { Q_D(QDeclarativeListView); - if (d->requestedIndex >= 0) // currently creating item + if (d->requestedIndex >= 0) // currently creating an item return; + d->currentIndexCleared = (index == -1); + if (index == d->currentIndex) return; + if (isComponentComplete() && d->isValid()) { d->moveReason = QDeclarativeListViewPrivate::SetIndex; - d->updateCurrent(index); + d->setCurrentIndex(index); } else if (d->currentIndex != index) { d->currentIndex = index; emit currentIndexChanged(); @@ -1711,9 +1618,8 @@ QDeclarativeItem *QDeclarativeListView::highlightItem() int QDeclarativeListView::count() const { Q_D(const QDeclarativeListView); - if (d->model) - return d->model->count(); - return 0; + Q_ASSERT( d->model || d->modelCount == 0 ); + return d->modelCount; } /*! @@ -1738,9 +1644,7 @@ void QDeclarativeListView::setHighlight(QDeclarativeComponent *highlight) Q_D(QDeclarativeListView); if (highlight != d->highlightComponent) { d->highlightComponent = highlight; - d->createHighlight(); - if (d->currentItem) - d->updateHighlight(); + d->recreateHighlight(); emit highlightChanged(); } } @@ -1928,17 +1832,22 @@ void QDeclarativeListView::setOrientation(QDeclarativeListView::Orientation orie setContentHeight(-1); setFlickableDirection(HorizontalFlick); } - d->clear(); - d->setPosition(0); - refill(); + + // -- swap the coordinates and then let layout do the final positioning + for (int i = 0; i < d->visibleItems.count(); ++i) + d->visibleItems.at(i)->item->setPos(d->visibleItems.at(i)->item->pos().y(), + d->visibleItems.at(i)->item->pos().x()); + + d->layout(); + d->updateViewport(); emit orientationChanged(); - d->updateCurrent(d->currentIndex); + d->setCurrentIndex(d->currentIndex); } } /*! \qmlproperty bool ListView::keyNavigationWraps - This property holds whether the list wraps key navigation. + This property holds whether the list wraps key navigation. If this is true, key navigation that would move the current item selection past the end of the list instead wraps around and moves the selection to @@ -1994,6 +1903,7 @@ void QDeclarativeListView::setCacheBuffer(int b) if (isComponentComplete()) { d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter; refill(); + d->updateViewport(); } emit cacheBufferChanged(); } @@ -2182,6 +2092,7 @@ void QDeclarativeListView::setSnapMode(SnapMode mode) if (d->snapMode != mode) { d->snapMode = mode; emit snapModeChanged(); + QScroller::scroller(this)->resendPrepareEvent(); } } @@ -2269,153 +2180,165 @@ void QDeclarativeListView::setContentY(qreal pos) bool QDeclarativeListView::event(QEvent *event) { Q_D(QDeclarativeListView); - if (event->type() == QEvent::User) { + + switch (event->type()) { + case QEvent::User: d->layout(); return true; + + case QEvent::ScrollPrepare: { + QScroller *scroller = QScroller::scroller(this); + qreal snapOffset = 0; + bool forceSnapping = false; + bool useEndPosition = false; + bool ignoreHeaders = false; + + // --- do the highlight range + if (d->haveHighlightRange) { + snapOffset = -d->highlightRangeStart; + ignoreHeaders = true; + } + + // --- now finally do the snap points + QList snapPoints; + // -- snap to every point (SnapToItem) + if (d->snapMode == QDeclarativeListView::SnapToItem || forceSnapping) { + // - snap to begin of item + if (!useEndPosition) { + if (d->header && !ignoreHeaders) + snapPoints.append(d->header->itemPosition() + snapOffset); + foreach (FxListItem *item, d->visibleItems) + snapPoints.append(item->itemPosition() + snapOffset); + if (d->footer && !ignoreHeaders) + snapPoints.append(d->footer->itemPosition() + snapOffset); + + // - snap to end of item + } else { + if (d->header && !ignoreHeaders) + snapPoints.append(d->header->endPosition() + snapOffset); + foreach (FxListItem *item, d->visibleItems) + snapPoints.append(item->endPosition() + snapOffset); + if (d->footer && !ignoreHeaders) + snapPoints.append(d->footer->endPosition() + snapOffset); + } + + // -- snap to the next three point (SnapOneItem) + } else if (d->snapMode == QDeclarativeListView::SnapOneItem) { + // here we just set three snap points around the current position. + // TODO + + // - snap to begin of item + if (!useEndPosition) { + qreal currentSnapPos = (d->positionAt(d->currentIndex) + snapOffset); + if (d->currentIndex > 0) + snapPoints.append(d->positionAt(d->currentIndex - 1) + snapOffset); + snapPoints.append(d->positionAt(d->currentIndex) + snapOffset); + // TODO: don't use last point if we are between two points already + if (d->currentIndex < d->modelCount - 1 && currentSnapPos <= d->position()) + snapPoints.append(d->positionAt(d->currentIndex + 1) + snapOffset); + + // - snap to end of item + } else { + snapPoints.append(d->endPositionAt(d->currentIndex - 1) + 1 + snapOffset); + snapPoints.append(d->endPositionAt(d->currentIndex) + 1 + snapOffset); + // TODO: don't use last point if we are between two points already + snapPoints.append(d->endPositionAt(d->currentIndex + 1) + 1 + snapOffset); + } + } + + if (d->orient == QDeclarativeListView::Vertical) { + scroller->setSnapPositionsX(0.0, 0.0); + scroller->setSnapPositionsY(snapPoints); + } else { + scroller->setSnapPositionsX(snapPoints); + scroller->setSnapPositionsY(0.0, 0.0); + } + } + break; + + default: + break; } return QDeclarativeFlickable::event(event); } -void QDeclarativeListView::viewportMoved() +void QDeclarativeListView::scrollerStateChanged(QScroller::State state) { Q_D(QDeclarativeListView); - QDeclarativeFlickable::viewportMoved(); - if (!d->itemCount) - return; - // Recursion can occur due to refill changing the content size. - if (d->inViewportMoved) - return; - d->inViewportMoved = true; - d->lazyRelease = true; - refill(); - if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically) - d->moveReason = QDeclarativeListViewPrivate::Mouse; - if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) { - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { - // reposition highlight - qreal pos = d->highlight->position(); - qreal viewPos = 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; - d->highlightPosAnimator->stop(); - d->highlight->setPosition(qRound(pos)); - - // update current index - if (FxListItem *snapItem = d->snapItemAt(d->highlight->position())) { - if (snapItem->index >= 0 && snapItem->index != d->currentIndex) - d->updateCurrent(snapItem->index); - } - } - } + QDeclarativeFlickable::scrollerStateChanged(state); - if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) { - d->inFlickCorrection = true; - // Near an end and it seems that the extent has changed? - // Recalculate the flick so that we don't end up in an odd position. - if (yflick()) { - if (d->vData.velocity > 0) { - const qreal minY = minYExtent(); - if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2) - && minY != d->vData.flickTarget) - d->flickY(-d->vData.smoothVelocity.value()); - d->bufferMode = QDeclarativeListViewPrivate::BufferBefore; - } else if (d->vData.velocity < 0) { - const qreal maxY = maxYExtent(); - if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2) - && maxY != d->vData.flickTarget) - d->flickY(-d->vData.smoothVelocity.value()); - d->bufferMode = QDeclarativeListViewPrivate::BufferAfter; - } + if (state == QScroller::Inactive) { + d->bufferMode = QDeclarativeListViewPrivate::NoBuffer; + if (d->highlightRange == QDeclarativeListView::StrictlyEnforceRange) { + d->updateHighlight(); // nudge the highlight in the right position if needed. } - - if (xflick()) { - if (d->hData.velocity > 0) { - const qreal minX = minXExtent(); - if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2) - && minX != d->hData.flickTarget) - d->flickX(-d->hData.smoothVelocity.value()); - d->bufferMode = QDeclarativeListViewPrivate::BufferBefore; - } else if (d->hData.velocity < 0) { - const qreal maxX = maxXExtent(); - if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2) - && maxX != d->hData.flickTarget) - d->flickX(-d->hData.smoothVelocity.value()); - d->bufferMode = QDeclarativeListViewPrivate::BufferAfter; - } - } - d->inFlickCorrection = false; } - d->inViewportMoved = false; } -qreal QDeclarativeListView::minYExtent() const +qreal QDeclarativeListView::minExtent() const { Q_D(const QDeclarativeListView); - if (d->orient == QDeclarativeListView::Horizontal) - return QDeclarativeFlickable::minYExtent(); + if (d->minExtentDirty) { - d->minExtent = -d->startPosition(); - if (d->header && d->visibleItems.count()) - d->minExtent += d->header->size(); - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->minExtent += d->highlightRangeStart; + d->minExtent = d->startPosition(); + if (d->modelCount && + d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent -= d->highlightRangeStart; if (d->sectionCriteria) { if (d->visibleItem(0)) d->minExtent -= d->visibleItem(0)->sectionSize(); } - d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1)); } d->minExtentDirty = false; } - return d->minExtent; } -qreal QDeclarativeListView::maxYExtent() const +qreal QDeclarativeListView::maxExtent() const { Q_D(const QDeclarativeListView); - if (d->orient == QDeclarativeListView::Horizontal) - return height(); + if (d->maxExtentDirty) { - if (!d->model || !d->model->count()) { - d->maxExtent = 0; - } 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 + 1)); - } else { - d->maxExtent = -(d->endPosition() - height() + 1); + d->maxExtent = d->endPosition() + 1; + + if (d->modelCount && + d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->maxExtent = qMin(d->maxExtent + d->size() - d->highlightRangeEnd + 1, + // ensure that the last item fits fully behind hightlightRangeStart + d->positionAt(d->modelCount-1) + d->size() - d->highlightRangeStart); } - if (d->footer) - d->maxExtent -= d->footer->size(); - qreal minY = minYExtent(); - if (d->maxExtent > minY) - d->maxExtent = minY; + d->maxExtent = qMax(d->maxExtent, minExtent()); d->maxExtentDirty = false; } return d->maxExtent; } +qreal QDeclarativeListView::minYExtent() const +{ + Q_D(const QDeclarativeListView); + if (d->orient == QDeclarativeListView::Horizontal) + return QDeclarativeFlickable::minYExtent(); + else + return minExtent(); +} + +qreal QDeclarativeListView::maxYExtent() const +{ + Q_D(const QDeclarativeListView); + if (d->orient == QDeclarativeListView::Horizontal) + return height(); + else + return maxExtent(); +} + qreal QDeclarativeListView::minXExtent() const { Q_D(const QDeclarativeListView); if (d->orient == QDeclarativeListView::Vertical) return QDeclarativeFlickable::minXExtent(); - if (d->minExtentDirty) { - d->minExtent = -d->startPosition(); - if (d->header) - d->minExtent += d->header->size(); - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->minExtent += d->highlightRangeStart; - d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1)); - } - d->minExtentDirty = false; - } - - return d->minExtent; + else + return minExtent(); } qreal QDeclarativeListView::maxXExtent() const @@ -2423,25 +2346,50 @@ qreal QDeclarativeListView::maxXExtent() const Q_D(const QDeclarativeListView); if (d->orient == QDeclarativeListView::Vertical) return width(); - if (d->maxExtentDirty) { - if (!d->model || !d->model->count()) { - d->maxExtent = 0; - } 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 + 1)); - } else { - d->maxExtent = -(d->endPosition() - width() + 1); + else + return maxExtent(); +} + +void QDeclarativeListView::viewportAboutToMove(QPointF newPos) +{ + Q_D(QDeclarativeListView); + + // qDebug() << "viewport about to move"; + // need to refill before moving + if( d->orient == Horizontal ) + d->refill(newPos.x(), newPos.x() + width() - 1); + else + d->refill(newPos.y(), newPos.y() + height() - 1); + + d->lazyRelease = true; + + if (d->isUserGenerated) + d->moveReason = QDeclarativeListViewPrivate::Mouse; + + if (d->haveHighlightRange && d->highlight && d->highlightRange == StrictlyEnforceRange) { + + d->highlightPosAnimator->stop(); + + // reposition highlight + qreal pos = d->highlight->position(); + qreal viewPos = d->orient == Horizontal ? newPos.x() : newPos.y(); + if (pos > viewPos + d->highlightRangeEnd - d->highlight->size()) + pos = viewPos + d->highlightRangeEnd - d->highlight->size(); + if (pos < viewPos + d->highlightRangeStart) + pos = viewPos + d->highlightRangeStart; + d->highlight->setPosition(pos); + + // update current index + if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) { + int idx = d->snapIndex(); + if (idx >= 0 && idx != d->currentIndex) + d->setCurrentIndex(idx); } - if (d->footer) - d->maxExtent -= d->footer->size(); - qreal minX = minXExtent(); - if (d->maxExtent > minX) - d->maxExtent = minX; - d->maxExtentDirty = false; } - return d->maxExtent; + if (d->sectionCriteria) + d->updateCurrentSection(); + d->updateViewport(); } void QDeclarativeListView::keyPressEvent(QKeyEvent *event) @@ -2451,7 +2399,7 @@ void QDeclarativeListView::keyPressEvent(QKeyEvent *event) if (event->isAccepted()) return; - if (d->model && d->model->count() && d->interactive) { + if (d->model && d->modelCount && d->interactive) { if ((d->orient == QDeclarativeListView::Horizontal && event->key() == Qt::Key_Left) || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Up)) { if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) { @@ -2464,7 +2412,7 @@ void QDeclarativeListView::keyPressEvent(QKeyEvent *event) } } else if ((d->orient == QDeclarativeListView::Horizontal && event->key() == Qt::Key_Right) || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Down)) { - if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) { + if (currentIndex() < d->modelCount - 1 || (d->wrap && !event->isAutoRepeat())) { incrementCurrentIndex(); event->accept(); return; @@ -2478,8 +2426,7 @@ void QDeclarativeListView::keyPressEvent(QKeyEvent *event) QDeclarativeFlickable::keyPressEvent(event); } -void QDeclarativeListView::geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry) +void QDeclarativeListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_D(QDeclarativeListView); d->maxExtentDirty = true; @@ -2500,11 +2447,11 @@ void QDeclarativeListView::geometryChanged(const QRectF &newGeometry, void QDeclarativeListView::incrementCurrentIndex() { Q_D(QDeclarativeListView); - int count = d->model ? d->model->count() : 0; - if (count && (currentIndex() < count - 1 || d->wrap)) { + //qDebug() << "incrementCurrentIndex" << currentIndex() << "c:" << count; + if (d->modelCount && (currentIndex() < d->modelCount - 1 || d->wrap)) { d->moveReason = QDeclarativeListViewPrivate::SetIndex; int index = currentIndex()+1; - setCurrentIndex((index >= 0 && index < count) ? index : 0); + setCurrentIndex((index >= 0 && index < d->modelCount) ? index : 0); } } @@ -2520,11 +2467,11 @@ void QDeclarativeListView::incrementCurrentIndex() void QDeclarativeListView::decrementCurrentIndex() { Q_D(QDeclarativeListView); - int count = d->model ? d->model->count() : 0; - if (count && (currentIndex() > 0 || d->wrap)) { + //qDebug() << "decrementCurrentIndex" << currentIndex() << "c:" << count; + if (d->modelCount && (currentIndex() > 0 || d->wrap)) { d->moveReason = QDeclarativeListViewPrivate::SetIndex; int index = currentIndex()-1; - setCurrentIndex((index >= 0 && index < count) ? index : count-1); + setCurrentIndex((index >= 0 && index < d->modelCount) ? index : d->modelCount - 1); } } @@ -2563,8 +2510,9 @@ void QDeclarativeListView::decrementCurrentIndex() */ void QDeclarativeListView::positionViewAtIndex(int index, int mode) { + //qDebug() << "positionViewAtIndex index:" << index << "mode:" << mode; Q_D(QDeclarativeListView); - if (!d->isValid() || index < 0 || index >= d->model->count()) + if (!d->isValid() || index < 0 || index >= d->modelCount) return; if (mode < Beginning || mode > Contain) return; @@ -2610,12 +2558,9 @@ void QDeclarativeListView::positionViewAtIndex(int index, int mode) if (itemPos < pos) pos = itemPos; } - qreal maxExtent = d->orient == QDeclarativeListView::Vertical ? -maxYExtent() : -maxXExtent(); - pos = qMin(pos, maxExtent); - qreal minExtent = d->orient == QDeclarativeListView::Vertical ? -minYExtent() : -minXExtent(); - pos = qMax(pos, minExtent); + pos = qMin(pos, maxExtent() - d->size()); // shouldn't that be + 1 ? + pos = qMax(pos, minExtent()); d->moveReason = QDeclarativeListViewPrivate::Other; - cancelFlick(); d->setPosition(pos); if (d->highlight) { d->highlight->setPosition(d->currentItem->itemPosition()); @@ -2623,7 +2568,6 @@ void QDeclarativeListView::positionViewAtIndex(int index, int mode) d->updateHighlight(); } } - d->fixupPosition(); } /*! @@ -2659,15 +2603,13 @@ void QDeclarativeListView::componentComplete() refill(); d->moveReason = QDeclarativeListViewPrivate::SetIndex; if (d->currentIndex < 0 && !d->currentIndexCleared) - d->updateCurrent(0); + d->setCurrentIndex(0); else - d->updateCurrent(d->currentIndex); + d->setCurrentIndex(d->currentIndex); if (d->highlight && d->currentItem) { d->highlight->setPosition(d->currentItem->position()); - d->updateTrackedItem(); } - d->moveReason = QDeclarativeListViewPrivate::Other; - d->fixupPosition(); + d->updateHighlight(false); } } @@ -2686,88 +2628,35 @@ void QDeclarativeListView::updateSections() void QDeclarativeListView::refill() { Q_D(QDeclarativeListView); - d->refill(d->position(), d->position()+d->size()-1); + d->refill(d->position(), d->position() + d->size() - 1); } -void QDeclarativeListView::trackedPositionChanged() +void QDeclarativeListView::itemsInserted(int modelIndex, int count) { Q_D(QDeclarativeListView); - if (!d->trackedItem || !d->currentItem) + d->minExtentDirty = true; + d->maxExtentDirty = true; + + if (!isComponentComplete()) { + d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count return; - if (d->moveReason == QDeclarativeListViewPrivate::SetIndex) { - qreal trackedPos = qCeil(d->trackedItem->position()); - qreal trackedSize = d->trackedItem->size(); - if (d->trackedItem != d->currentItem) { - trackedPos -= d->currentItem->sectionSize(); - trackedSize += d->currentItem->sectionSize(); - } - const qreal viewPos = d->position(); - qreal pos = viewPos; - if (d->haveHighlightRange) { - if (d->highlightRange == StrictlyEnforceRange) { - if (trackedPos > pos + d->highlightRangeEnd - d->trackedItem->size()) - pos = trackedPos - d->highlightRangeEnd + d->trackedItem->size(); - if (trackedPos < pos + d->highlightRangeStart) - pos = trackedPos - d->highlightRangeStart; - } else { - if (trackedPos < d->startPosition() + d->highlightRangeStart) { - pos = d->startPosition(); - } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + d->highlightRangeEnd) { - pos = d->endPosition() - d->size() + 1; - if (pos < d->startPosition()) - pos = d->startPosition(); - } else { - if (trackedPos < viewPos + d->highlightRangeStart) { - pos = trackedPos - d->highlightRangeStart; - } else if (trackedPos > viewPos + d->highlightRangeEnd - trackedSize) { - pos = trackedPos - d->highlightRangeEnd + trackedSize; - } - } - } - } else { - if (trackedPos < viewPos && d->currentItem->position() < viewPos) { - pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position(); - } else if (d->trackedItem->endPosition() >= viewPos + d->size() - && d->currentItem->endPosition() >= viewPos + d->size()) { - if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) { - pos = d->trackedItem->endPosition() - d->size() + 1; - if (trackedSize > d->size()) - pos = trackedPos; - } else { - pos = d->currentItem->endPosition() - d->size() + 1; - if (d->currentItem->size() > d->size()) - pos = d->currentItem->position(); - } - } - } - if (viewPos != pos) { - cancelFlick(); - d->calcVelocity = true; - d->setPosition(pos); - d->calcVelocity = false; - } } -} -void QDeclarativeListView::itemsInserted(int modelIndex, int count) -{ - Q_D(QDeclarativeListView); - if (!isComponentComplete()) - return; d->updateUnrequestedIndexes(); d->moveReason = QDeclarativeListViewPrivate::Other; if (!d->visibleItems.count() || d->model->count() <= 1) { d->scheduleLayout(); - if (d->itemCount && d->currentIndex >= modelIndex) { + if (d->modelCount && d->currentIndex >= modelIndex) { // adjust current item index d->currentIndex += count; if (d->currentItem) d->currentItem->index = d->currentIndex; emit currentIndexChanged(); } else if (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared)) { - d->updateCurrent(0); + d->setCurrentIndex(0); } - d->itemCount += count; + d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count + d->updateScrollerValues(); emit countChanged(); return; } @@ -2799,13 +2688,16 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) emit currentIndexChanged(); } d->scheduleLayout(); - d->itemCount += count; + d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count + d->updateScrollerValues(); emit countChanged(); return; } } // At least some of the added items will be visible + if (d->layoutScheduled) + d->layout(); // index can be the next item past the end of the visible items list (i.e. appended) int pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position() @@ -2870,14 +2762,9 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) } diff = pos - initialPos; } - if (d->itemCount && d->currentIndex >= modelIndex) { + if (d->modelCount && d->currentIndex >= modelIndex) { // adjust current item index - d->currentIndex += count; - if (d->currentItem) { - d->currentItem->index = d->currentIndex; - d->currentItem->setPosition(d->currentItem->position() + diff); - } - emit currentIndexChanged(); + d->setCurrentIndex(d->currentIndex + count); } // Update the indexes of the following visible items. for (; index < d->visibleItems.count(); ++index) { @@ -2892,35 +2779,42 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) added.at(j)->attached->emitAdd(); d->updateSections(); - d->itemCount += count; + d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count + d->updateScrollerValues(); emit countChanged(); } void QDeclarativeListView::itemsRemoved(int modelIndex, int count) { Q_D(QDeclarativeListView); + d->minExtentDirty = true; + d->maxExtentDirty = true; + d->modelCount = d->model->count(); // don't rely on newCount = oldCount - count if (!isComponentComplete()) return; d->moveReason = QDeclarativeListViewPrivate::Other; d->updateUnrequestedIndexes(); - d->itemCount -= count; FxListItem *firstVisible = d->firstVisibleItem(); int preRemovedSize = 0; - bool removedVisible = false; + bool removedVisible = false; // true if we removed an visible item. // Remove the items from the visible list, skipping anything already marked for removal QList::Iterator it = d->visibleItems.begin(); while (it != d->visibleItems.end()) { FxListItem *item = *it; + + // qDebug() << "Remove visible?: "<index<<"at"<position()<<"mi"<index == -1 || item->index < modelIndex) { // already removed, or before removed items ++it; } else if (item->index >= modelIndex + count) { // after removed items + // qDebug() << "Remove item not" << item->index <<"at" << item->position() << "newINdex:"<<(item->index-count); item->index -= count; ++it; } else { // removed item + // qDebug() << "Remove item really" << item->index <<"at" << item->position(); if (!removedVisible) { d->scheduleLayout(); removedVisible = true; @@ -2931,18 +2825,22 @@ void QDeclarativeListView::itemsRemoved(int modelIndex, int count) connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection); ++it; } else { + // move the list down if we remove an item between the start of the list and the first visible item. if (item == firstVisible) - firstVisible = 0; + firstVisible = 0; if (firstVisible && item->position() < firstVisible->position()) preRemovedSize += item->size(); + it = d->visibleItems.erase(it); d->releaseItem(item); } } } - if (firstVisible && d->visibleItems.first() != firstVisible) - d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize); + + // restore the start position of the visible items + if (firstVisible && forceConst(d->visibleItems).first() != firstVisible) + forceConst(d->visibleItems).first()->setPosition(forceConst(d->visibleItems).first()->position() + preRemovedSize); // fix current if (d->currentIndex >= modelIndex + count) { @@ -2956,8 +2854,8 @@ void QDeclarativeListView::itemsRemoved(int modelIndex, int count) d->releaseItem(d->currentItem); d->currentItem = 0; d->currentIndex = -1; - if (d->itemCount) - d->updateCurrent(qMin(modelIndex, d->itemCount-1)); + if (d->modelCount) + d->setCurrentIndex(qMin(modelIndex, d->modelCount-1)); } // update visibleIndex @@ -2967,24 +2865,21 @@ void QDeclarativeListView::itemsRemoved(int modelIndex, int count) break; } } - if (removedVisible && d->visibleItems.isEmpty()) { - d->timeline.clear(); - if (d->itemCount == 0) { - d->visibleIndex = 0; - d->visiblePos = d->header ? d->header->size() : 0; - d->setPosition(0); - d->updateHeader(); - d->updateFooter(); - update(); + QScroller::scroller(this)->stop(); + // the complete visible area was removed + if (!d->modelCount) { + d->clear(); } else { if (modelIndex < d->visibleIndex) d->visibleIndex = modelIndex+1; - d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0); + d->visibleIndex = qBound(0, d->visibleIndex, d->modelCount - 1); } } d->updateSections(); + d->updateScrollerValues(); + emit countChanged(); } @@ -3104,7 +2999,7 @@ void QDeclarativeListView::itemsMoved(int from, int to, int count) } // Ensure we don't cause an ugly list scroll. - d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy); + forceConst(d->visibleItems).first()->setPosition(forceConst(d->visibleItems).first()->position() + moveBy); d->updateSections(); d->layout(); @@ -3121,14 +3016,15 @@ void QDeclarativeListView::modelReset() { Q_D(QDeclarativeListView); d->clear(); + d->modelCount = d->model->count(); d->setPosition(0); refill(); d->moveReason = QDeclarativeListViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); + d->setCurrentIndex(d->currentIndex); if (d->highlight && d->currentItem) { d->highlight->setPosition(d->currentItem->position()); - d->updateTrackedItem(); } + d->updateHighlight(); d->moveReason = QDeclarativeListViewPrivate::Other; emit countChanged(); } @@ -3152,14 +3048,6 @@ void QDeclarativeListView::destroyingItem(QDeclarativeItem *item) d->unrequestedItems.remove(item); } -void QDeclarativeListView::animStopped() -{ - Q_D(QDeclarativeListView); - d->bufferMode = QDeclarativeListViewPrivate::NoBuffer; - if (d->haveHighlightRange && d->highlightRange == QDeclarativeListView::StrictlyEnforceRange) - d->updateHighlight(); -} - QDeclarativeListViewAttached *QDeclarativeListView::qmlAttachedProperties(QObject *obj) { return new QDeclarativeListViewAttached(obj); diff --git a/src/declarative/graphicsitems/qdeclarativelistview_p.h b/src/declarative/graphicsitems/qdeclarativelistview_p.h index 2678b90..5593574 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview_p.h +++ b/src/declarative/graphicsitems/qdeclarativelistview_p.h @@ -238,13 +238,18 @@ Q_SIGNALS: void headerChanged(); void footerChanged(); +protected Q_SLOTS: + virtual void scrollerStateChanged(QScroller::State); + protected: virtual bool event(QEvent *event); - virtual void viewportMoved(); + virtual qreal minExtent() const; + virtual qreal maxExtent() const; virtual qreal minYExtent() const; virtual qreal maxYExtent() const; virtual qreal minXExtent() const; virtual qreal maxXExtent() const; + virtual void viewportAboutToMove(QPointF newPos); virtual void keyPressEvent(QKeyEvent *); virtual void geometryChanged(const QRectF &newGeometry,const QRectF &oldGeometry); virtual void componentComplete(); @@ -252,7 +257,6 @@ protected: private Q_SLOTS: void updateSections(); void refill(); - void trackedPositionChanged(); void itemsInserted(int index, int count); void itemsRemoved(int index, int count); void itemsMoved(int from, int to, int count); @@ -261,7 +265,6 @@ private Q_SLOTS: void destroyRemoved(); void createdItem(int index, QDeclarativeItem *item); void destroyingItem(QDeclarativeItem *item); - void animStopped(); }; class QDeclarativeListViewAttached : public QObject diff --git a/tests/auto/declarative/qdeclarativeflickable/tst_qdeclarativeflickable.cpp b/tests/auto/declarative/qdeclarativeflickable/tst_qdeclarativeflickable.cpp index 25edb36..3442596 100644 --- a/tests/auto/declarative/qdeclarativeflickable/tst_qdeclarativeflickable.cpp +++ b/tests/auto/declarative/qdeclarativeflickable/tst_qdeclarativeflickable.cpp @@ -99,9 +99,11 @@ void tst_qdeclarativeflickable::create() QCOMPARE(obj->verticalVelocity(), 0.); QCOMPARE(obj->isInteractive(), true); - QCOMPARE(obj->boundsBehavior(), QDeclarativeFlickable::DragAndOvershootBounds); QCOMPARE(obj->pressDelay(), 0); + QCOMPARE(obj->boundsBehavior(), QDeclarativeFlickable::DragAndOvershootBounds); + /* Those values are platform dependant QCOMPARE(obj->maximumFlickVelocity(), 2000.); + */ delete obj; } diff --git a/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp b/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp index 327bba2..ef5c976 100644 --- a/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp +++ b/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp @@ -649,6 +649,7 @@ void tst_QDeclarativeGridView::currentIndex() #endif QTRY_VERIFY(canvas->hasFocus()); QTRY_VERIFY(canvas->scene()->hasFocus()); + gridview->forceActiveFocus(); qApp->processEvents(); QTest::keyClick(canvas, Qt::Key_Down); @@ -666,6 +667,7 @@ void tst_QDeclarativeGridView::currentIndex() #endif QTRY_VERIFY(canvas->hasFocus()); QTRY_VERIFY(canvas->scene()->hasFocus()); + gridview->forceActiveFocus(); qApp->processEvents(); QTest::keyClick(canvas, Qt::Key_Right); diff --git a/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp b/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp index 37d836d..70f417f 100644 --- a/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp +++ b/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp @@ -613,10 +613,11 @@ void tst_QDeclarativeListView::removed(bool animated) listview->setCurrentIndex(10); model.removeItem(1); // post: top item will be at 40 + // let transitions settle. qApp->processEvents(); - // Confirm items positioned correctly - for (int i = 2; i < 18; ++i) { + // Confirm items positioned correctly (we were at the fourth item and deleted one) + for (int i = 2; i < 2 + 16; ++i) { QDeclarativeItem *item = findItem(contentItem, "wrapper", i); if (!item) qWarning() << "Item" << i << "not found"; QTRY_VERIFY(item); @@ -663,10 +664,12 @@ void tst_QDeclarativeListView::removed(bool animated) listview->setContentY(80); QTest::qWait(300); + // remove all visible items model.removeItems(1, 17); QTest::qWait(300); // Confirm items positioned correctly + // we were at the fourth item, but deleted 1 through 17. So we should be at 1 now. itemCount = findItems(contentItem, "wrapper").count(); for (int i = 0; i < model.count() && i < itemCount-1; ++i) { QDeclarativeItem *item = findItem(contentItem, "wrapper", i+2); @@ -1076,6 +1079,7 @@ void tst_QDeclarativeListView::currentIndex() #endif QTRY_VERIFY(canvas->hasFocus()); QTRY_VERIFY(canvas->scene()->hasFocus()); + listview->forceActiveFocus(); qApp->processEvents(); QTest::keyClick(canvas, Qt::Key_Down); -- cgit v0.12 From 01276467bc7cf9b7cf8f83dc924a47b90e5bf053 Mon Sep 17 00:00:00 2001 From: Robert Griebl Date: Sun, 5 Dec 2010 19:53:12 +0100 Subject: Fix code style issues in QScroller based Flickable replacement. --- src/declarative/graphicsitems/qdeclarativegridview.cpp | 4 ++-- src/declarative/graphicsitems/qdeclarativelistview.cpp | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/declarative/graphicsitems/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp index b6af7dc..8f6c34f 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview.cpp +++ b/src/declarative/graphicsitems/qdeclarativegridview.cpp @@ -1610,14 +1610,14 @@ bool QDeclarativeGridView::event(QEvent *event) qreal rowPos = d->rowPosAt(d->currentIndex); if (rowPos - d->rowSize() >= 0) snapPoints.append( rowPos - d->rowSize()); - else if( d->header ) + else if (d->header) snapPoints.append(0); // position of the header snapPoints.append(rowPos); if (rowPos + d->rowSize() < d->headerSize() + d->contentSize()) snapPoints.append(rowPos + d->rowSize()); - else if( d->footer ) + else if (d->footer) snapPoints.append(d->headerSize() + d->contentSize() + snapOffset); // position of the footer } diff --git a/src/declarative/graphicsitems/qdeclarativelistview.cpp b/src/declarative/graphicsitems/qdeclarativelistview.cpp index 27f7b52..d6a75ba 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview.cpp +++ b/src/declarative/graphicsitems/qdeclarativelistview.cpp @@ -309,7 +309,7 @@ public: pos = visibleItems.first()->position(); pos -= visibleIndex * (averageSize + spacing); } - if( header ) + if (header) pos -= header->size(); return pos; } @@ -960,7 +960,6 @@ void QDeclarativeListViewPrivate::recreateHighlight() */ void QDeclarativeListViewPrivate::updateHighlight(bool smooth) { - if ((!currentItem && highlight) || (currentItem && !highlight)) recreateHighlight(); @@ -2356,7 +2355,7 @@ void QDeclarativeListView::viewportAboutToMove(QPointF newPos) // qDebug() << "viewport about to move"; // need to refill before moving - if( d->orient == Horizontal ) + if (d->orient == Horizontal) d->refill(newPos.x(), newPos.x() + width() - 1); else d->refill(newPos.y(), newPos.y() + height() - 1); @@ -2837,7 +2836,6 @@ void QDeclarativeListView::itemsRemoved(int modelIndex, int count) } } - // restore the start position of the visible items if (firstVisible && forceConst(d->visibleItems).first() != firstVisible) forceConst(d->visibleItems).first()->setPosition(forceConst(d->visibleItems).first()->position() + preRemovedSize); -- cgit v0.12 From 236d4e3c5d58e69a85a362c702d830227309404d Mon Sep 17 00:00:00 2001 From: Robert Griebl Date: Mon, 6 Dec 2010 13:34:13 +0100 Subject: Fix qreal vs. double issue and a copy/paste error. Reviewed-by: Ralf Engels --- .../graphicsitems/qdeclarativeflickable.cpp | 27 +++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/declarative/graphicsitems/qdeclarativeflickable.cpp b/src/declarative/graphicsitems/qdeclarativeflickable.cpp index 96f54a8..3d47a8d 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable.cpp +++ b/src/declarative/graphicsitems/qdeclarativeflickable.cpp @@ -600,7 +600,7 @@ void QDeclarativeFlickable::setFlickableDirection(FlickableDirection direction) */ qreal QDeclarativeFlickable::minXExtent() const { - return 0.0; + return 0; } /*! \internal @@ -609,7 +609,7 @@ qreal QDeclarativeFlickable::minXExtent() const */ qreal QDeclarativeFlickable::minYExtent() const { - return 0.0; + return 0; } /*! \internal Returns the maximum horizontal content position. @@ -617,7 +617,7 @@ qreal QDeclarativeFlickable::minYExtent() const qreal QDeclarativeFlickable::maxXExtent() const { Q_D(const QDeclarativeFlickable); - return qMax(0.0, d->contentItem->width()); + return qMax(qreal(0), d->contentItem->width()); } /*! \internal @@ -626,7 +626,7 @@ qreal QDeclarativeFlickable::maxXExtent() const qreal QDeclarativeFlickable::maxYExtent() const { Q_D(const QDeclarativeFlickable); - return qMax(0.0, d->contentItem->height()); + return qMax(qreal(0), d->contentItem->height()); } /*! \internal @@ -673,10 +673,10 @@ void QDeclarativeFlickable::geometryChanged(const QRectF &newGeometry, //qDebug() << "xxx geometryChanged: "<state() == QScroller::Inactive) - scroller->ensureVisible( QRectF(0.0, 0.0, - d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); - else + if (scroller->state() == QScroller::Inactive) { + scroller->ensureVisible(QRectF(minXExtent(), minYExtent(), + d->contentItem->width(), d->contentItem->height()), 0, 0, 0); + } else d->updateScrollerValues(); d->updateBeginningEnd(); } @@ -840,9 +840,10 @@ void QDeclarativeFlickable::setContentWidth(qreal w) // Make sure that we're entirely in view. QScroller *scroller = QScroller::scroller(this); - if (scroller->state() == QScroller::Inactive) - scroller->ensureVisible( QRectF(0.0, 0.0, d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); - else + if (scroller->state() == QScroller::Inactive) { + scroller->ensureVisible(QRectF(minXExtent(), minYExtent(), + d->contentItem->width(), d->contentItem->height()), 0, 0, 0); + } else d->updateScrollerValues(); emit contentWidthChanged(); @@ -869,8 +870,8 @@ void QDeclarativeFlickable::setContentHeight(qreal h) // Make sure that we're entirely in view. QScroller *scroller = QScroller::scroller(this); if (scroller->state() == QScroller::Inactive) { - scroller->ensureVisible( QRectF(minXExtent(), minYExtent(), - d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); + scroller->ensureVisible(QRectF(minXExtent(), minYExtent(), + d->contentItem->width(), d->contentItem->height()), 0, 0, 0); } else d->updateScrollerValues(); -- cgit v0.12 From 4103f03579515c1d1993afd6b619315754be330b Mon Sep 17 00:00:00 2001 From: Robert Griebl Date: Tue, 7 Dec 2010 12:11:13 +0100 Subject: Revert "Fix qreal vs. double issue and a copy/paste error." This reverts commit 236d4e3c5d58e69a85a362c702d830227309404d. (Some weird autotest regressions have shown up - need to fix those first) --- .../graphicsitems/qdeclarativeflickable.cpp | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/declarative/graphicsitems/qdeclarativeflickable.cpp b/src/declarative/graphicsitems/qdeclarativeflickable.cpp index 3d47a8d..96f54a8 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable.cpp +++ b/src/declarative/graphicsitems/qdeclarativeflickable.cpp @@ -600,7 +600,7 @@ void QDeclarativeFlickable::setFlickableDirection(FlickableDirection direction) */ qreal QDeclarativeFlickable::minXExtent() const { - return 0; + return 0.0; } /*! \internal @@ -609,7 +609,7 @@ qreal QDeclarativeFlickable::minXExtent() const */ qreal QDeclarativeFlickable::minYExtent() const { - return 0; + return 0.0; } /*! \internal Returns the maximum horizontal content position. @@ -617,7 +617,7 @@ qreal QDeclarativeFlickable::minYExtent() const qreal QDeclarativeFlickable::maxXExtent() const { Q_D(const QDeclarativeFlickable); - return qMax(qreal(0), d->contentItem->width()); + return qMax(0.0, d->contentItem->width()); } /*! \internal @@ -626,7 +626,7 @@ qreal QDeclarativeFlickable::maxXExtent() const qreal QDeclarativeFlickable::maxYExtent() const { Q_D(const QDeclarativeFlickable); - return qMax(qreal(0), d->contentItem->height()); + return qMax(0.0, d->contentItem->height()); } /*! \internal @@ -673,10 +673,10 @@ void QDeclarativeFlickable::geometryChanged(const QRectF &newGeometry, //qDebug() << "xxx geometryChanged: "<state() == QScroller::Inactive) { - scroller->ensureVisible(QRectF(minXExtent(), minYExtent(), - d->contentItem->width(), d->contentItem->height()), 0, 0, 0); - } else + if (scroller->state() == QScroller::Inactive) + scroller->ensureVisible( QRectF(0.0, 0.0, + d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); + else d->updateScrollerValues(); d->updateBeginningEnd(); } @@ -840,10 +840,9 @@ void QDeclarativeFlickable::setContentWidth(qreal w) // Make sure that we're entirely in view. QScroller *scroller = QScroller::scroller(this); - if (scroller->state() == QScroller::Inactive) { - scroller->ensureVisible(QRectF(minXExtent(), minYExtent(), - d->contentItem->width(), d->contentItem->height()), 0, 0, 0); - } else + if (scroller->state() == QScroller::Inactive) + scroller->ensureVisible( QRectF(0.0, 0.0, d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); + else d->updateScrollerValues(); emit contentWidthChanged(); @@ -870,8 +869,8 @@ void QDeclarativeFlickable::setContentHeight(qreal h) // Make sure that we're entirely in view. QScroller *scroller = QScroller::scroller(this); if (scroller->state() == QScroller::Inactive) { - scroller->ensureVisible(QRectF(minXExtent(), minYExtent(), - d->contentItem->width(), d->contentItem->height()), 0, 0, 0); + scroller->ensureVisible( QRectF(minXExtent(), minYExtent(), + d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); } else d->updateScrollerValues(); -- cgit v0.12 From 3dfab643ccbf9fea78b1889418ccb89b25ba986b Mon Sep 17 00:00:00 2001 From: Robert Griebl Date: Tue, 7 Dec 2010 12:11:47 +0100 Subject: Revert "Fix code style issues in QScroller based Flickable replacement." This reverts commit 01276467bc7cf9b7cf8f83dc924a47b90e5bf053. (Some weird autotest regressions have shown up - need to fix those first) --- src/declarative/graphicsitems/qdeclarativegridview.cpp | 4 ++-- src/declarative/graphicsitems/qdeclarativelistview.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/declarative/graphicsitems/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp index 8f6c34f..b6af7dc 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview.cpp +++ b/src/declarative/graphicsitems/qdeclarativegridview.cpp @@ -1610,14 +1610,14 @@ bool QDeclarativeGridView::event(QEvent *event) qreal rowPos = d->rowPosAt(d->currentIndex); if (rowPos - d->rowSize() >= 0) snapPoints.append( rowPos - d->rowSize()); - else if (d->header) + else if( d->header ) snapPoints.append(0); // position of the header snapPoints.append(rowPos); if (rowPos + d->rowSize() < d->headerSize() + d->contentSize()) snapPoints.append(rowPos + d->rowSize()); - else if (d->footer) + else if( d->footer ) snapPoints.append(d->headerSize() + d->contentSize() + snapOffset); // position of the footer } diff --git a/src/declarative/graphicsitems/qdeclarativelistview.cpp b/src/declarative/graphicsitems/qdeclarativelistview.cpp index d6a75ba..27f7b52 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview.cpp +++ b/src/declarative/graphicsitems/qdeclarativelistview.cpp @@ -309,7 +309,7 @@ public: pos = visibleItems.first()->position(); pos -= visibleIndex * (averageSize + spacing); } - if (header) + if( header ) pos -= header->size(); return pos; } @@ -960,6 +960,7 @@ void QDeclarativeListViewPrivate::recreateHighlight() */ void QDeclarativeListViewPrivate::updateHighlight(bool smooth) { + if ((!currentItem && highlight) || (currentItem && !highlight)) recreateHighlight(); @@ -2355,7 +2356,7 @@ void QDeclarativeListView::viewportAboutToMove(QPointF newPos) // qDebug() << "viewport about to move"; // need to refill before moving - if (d->orient == Horizontal) + if( d->orient == Horizontal ) d->refill(newPos.x(), newPos.x() + width() - 1); else d->refill(newPos.y(), newPos.y() + height() - 1); @@ -2836,6 +2837,7 @@ void QDeclarativeListView::itemsRemoved(int modelIndex, int count) } } + // restore the start position of the visible items if (firstVisible && forceConst(d->visibleItems).first() != firstVisible) forceConst(d->visibleItems).first()->setPosition(forceConst(d->visibleItems).first()->position() + preRemovedSize); -- cgit v0.12 From 38389903bedbb45b640231c8a53e824ff3f99253 Mon Sep 17 00:00:00 2001 From: Robert Griebl Date: Tue, 7 Dec 2010 12:11:53 +0100 Subject: Revert "QScroller merge, part 2" This reverts commit 53dd7206a7a2ba4393d558bf76fa23f4a4e05183. (Some weird autotest regressions have shown up - need to fix those first) --- .../graphicsitems/qdeclarativeflickable.cpp | 1169 +++++++++++----- .../graphicsitems/qdeclarativeflickable_p.h | 22 +- .../graphicsitems/qdeclarativeflickable_p_p.h | 103 +- .../graphicsitems/qdeclarativegridview.cpp | 1202 +++++++++------- .../graphicsitems/qdeclarativegridview_p.h | 9 +- .../graphicsitems/qdeclarativelistview.cpp | 1428 +++++++++++--------- .../graphicsitems/qdeclarativelistview_p.h | 9 +- .../tst_qdeclarativeflickable.cpp | 4 +- .../tst_qdeclarativegridview.cpp | 2 - .../tst_qdeclarativelistview.cpp | 8 +- 10 files changed, 2426 insertions(+), 1530 deletions(-) diff --git a/src/declarative/graphicsitems/qdeclarativeflickable.cpp b/src/declarative/graphicsitems/qdeclarativeflickable.cpp index 96f54a8..98c213c 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable.cpp +++ b/src/declarative/graphicsitems/qdeclarativeflickable.cpp @@ -44,12 +44,14 @@ #include #include #include +#include -#include -#include +QT_BEGIN_NAMESPACE -QT_BEGIN_NAMESPACE +// FlickThreshold determines how far the "mouse" must have moved +// before we perform a flick. +static const int FlickThreshold = 20; QDeclarativeFlickableVisibleArea::QDeclarativeFlickableVisibleArea(QDeclarativeFlickable *parent) : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.) @@ -77,11 +79,6 @@ qreal QDeclarativeFlickableVisibleArea::yPosition() const return m_yPosition; } -/*! \internal - Updates the page position and ratio values and sends out the appropriate position - and ratio changed signals. - Will be called by updateBeginningEnd. -*/ void QDeclarativeFlickableVisibleArea::updateVisible() { QDeclarativeFlickablePrivate *p = static_cast(QGraphicsItemPrivate::get(flickable)); @@ -93,11 +90,9 @@ void QDeclarativeFlickableVisibleArea::updateVisible() // Vertical const qreal viewheight = flickable->height(); - const qreal maxyextent = flickable->maxYExtent(); - qreal pagePos = (p->contentItem->pos().y() + flickable->minYExtent()) / maxyextent; - qreal pageSize = viewheight / maxyextent; - - // qDebug() << "updateVisible" << viewheight << "ext:" << flickable->minYExtent() <<"-"<maxYExtent() << "pagesize:"<maxYExtent() + flickable->minYExtent(); + qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight); + qreal pageSize = viewheight / (maxyextent + viewheight); if (pageSize != m_heightRatio) { m_heightRatio = pageSize; @@ -110,9 +105,9 @@ void QDeclarativeFlickableVisibleArea::updateVisible() // Horizontal const qreal viewwidth = flickable->width(); - const qreal maxxextent = flickable->maxXExtent(); - pagePos = (p->contentItem->pos().x() + flickable->minXExtent()) / maxxextent; - pageSize = viewwidth / maxxextent; + const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent(); + pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth); + pageSize = viewwidth / (maxxextent + viewwidth); if (pageSize != m_widthRatio) { m_widthRatio = pageSize; @@ -136,19 +131,15 @@ void QDeclarativeFlickableVisibleArea::updateVisible() QDeclarativeFlickablePrivate::QDeclarativeFlickablePrivate() : contentItem(new QDeclarativeItem) - , updateScrollerValuesRequested(false) - , duringScrollEvent(false) - , isUserGenerated(false) - , isFlicking(false) - , isMoving(false) - , movingHorizontally(false) - , movingVertically(false) - , atBeginningX(true) - , atBeginningY(true) - , atEndX(false) - , atEndY(false) - , interactive(true) - , visibleArea(0) + , hData(this, &QDeclarativeFlickablePrivate::setRoundedViewportX) + , vData(this, &QDeclarativeFlickablePrivate::setRoundedViewportY) + , flickingHorizontally(false), flickingVertically(false) + , hMoved(false), vMoved(false) + , movingHorizontally(false), movingVertically(false) + , stealMouse(false), pressed(false), interactive(true), calcVelocity(false) + , deceleration(500), maxVelocity(2000), reportedVelocitySmoothing(100) + , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(600) + , vTime(0), visibleArea(0) , flickableDirection(QDeclarativeFlickable::AutoFlickDirection) , boundsBehavior(QDeclarativeFlickable::DragAndOvershootBounds) { @@ -159,39 +150,45 @@ void QDeclarativeFlickablePrivate::init() Q_Q(QDeclarativeFlickable); QDeclarative_setParent_noEvent(contentItem, q); contentItem->setParentItem(q); - + static int timelineUpdatedIdx = -1; + static int timelineCompletedIdx = -1; + static int flickableTickedIdx = -1; + static int flickableMovementEndingIdx = -1; + if (timelineUpdatedIdx == -1) { + timelineUpdatedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("updated()"); + timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()"); + flickableTickedIdx = QDeclarativeFlickable::staticMetaObject.indexOfSlot("ticked()"); + flickableMovementEndingIdx = QDeclarativeFlickable::staticMetaObject.indexOfSlot("movementEnding()"); + } + QMetaObject::connect(&timeline, timelineUpdatedIdx, + q, flickableTickedIdx, Qt::DirectConnection); + QMetaObject::connect(&timeline, timelineCompletedIdx, + q, flickableMovementEndingIdx, Qt::DirectConnection); q->setAcceptedMouseButtons(Qt::LeftButton); - q->setAcceptTouchEvents(true); - - QScroller::grabGesture(q, QScroller::LeftMouseButtonGesture); - QScroller *scroller = QScroller::scroller(q); - - QScrollerProperties sp = scroller->scrollerProperties(); - sp.setScrollMetric(QScrollerProperties::MousePressEventDelay, 0); - sp.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 1.0); - sp.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 1.0); - scroller->setScrollerProperties(sp); - - QObject::connect(scroller, SIGNAL(stateChanged(QScroller::State)), q, SLOT(scrollerStateChanged(QScroller::State))); + q->setFiltersChildEvents(true); + QDeclarativeItemPrivate *viewportPrivate = static_cast(QGraphicsItemPrivate::get(contentItem)); + viewportPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + lastPosTime.invalidate(); } -/*! /internal - Requests a prepare event to be send to the scroller informing it about changed - positions or scroll area bounds. +/* + Returns the amount to overshoot by given a velocity. + Will be roughly in range 0 - size/4 */ -void QDeclarativeFlickablePrivate::updateScrollerValues() +qreal QDeclarativeFlickablePrivate::overShootDistance(qreal velocity, qreal size) { - Q_Q(QDeclarativeFlickable); - //qDebug() << "DeclarativeFlickablePrivate::updateScrollerValues" << duringScrollEvent; - if (!duringScrollEvent) - QScroller::scroller(q)->resendPrepareEvent(); - else - updateScrollerValuesRequested = true; + if (maxVelocity <= 0) + return 0.0; + + velocity = qAbs(velocity); + if (velocity > maxVelocity) + velocity = maxVelocity; + qreal dist = size / 4 * velocity / maxVelocity; + return dist; } void QDeclarativeFlickablePrivate::itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeom, const QRectF &oldGeom) { - // TODO Q_Q(QDeclarativeFlickable); if (item == contentItem) { if (newGeom.x() != oldGeom.x()) @@ -201,41 +198,149 @@ void QDeclarativeFlickablePrivate::itemGeometryChanged(QDeclarativeItem *item, c } } -/*! \internal - Updates the atBeginning and atEnd flags and sends out the changed signals -*/ -void QDeclarativeFlickablePrivate::updateBeginningEnd() +void QDeclarativeFlickablePrivate::flickX(qreal velocity) { Q_Q(QDeclarativeFlickable); + flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity); +} - bool boundaryChanged = false; - - bool newAtBeginningX = -contentItem->pos().x() <= q->minXExtent(); - bool newAtEndX = -contentItem->pos().x() >= (q->maxXExtent() - width()); - bool newAtBeginningY = -contentItem->pos().y() <= q->minYExtent(); - bool newAtEndY = -contentItem->pos().y() >= (q->maxYExtent() - height()); +void QDeclarativeFlickablePrivate::flickY(qreal velocity) +{ + Q_Q(QDeclarativeFlickable); + flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity); +} - // Horizontal - if (newAtBeginningX != atBeginningX) { - atBeginningX = newAtBeginningX; - boundaryChanged = true; +void QDeclarativeFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QDeclarativeFlickable); + qreal maxDistance = -1; + bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds; + // -ve velocity means list is moving up + if (velocity > 0) { + if (data.move.value() < minExtent) + maxDistance = qAbs(minExtent - data.move.value() + (overShoot?overShootDistance(velocity,vSize):0)); + data.flickTarget = minExtent; + } else { + if (data.move.value() > maxExtent) + maxDistance = qAbs(maxExtent - data.move.value()) + (overShoot?overShootDistance(velocity,vSize):0); + data.flickTarget = maxExtent; + } + if (maxDistance > 0) { + qreal v = velocity; + if (maxVelocity != -1 && maxVelocity < qAbs(v)) { + if (v < 0) + v = -maxVelocity; + else + v = maxVelocity; + } + timeline.reset(data.move); + timeline.accel(data.move, v, deceleration, maxDistance); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + if (!flickingHorizontally && q->xflick()) { + flickingHorizontally = true; + emit q->flickingChanged(); + emit q->flickingHorizontallyChanged(); + if (!flickingVertically) + emit q->flickStarted(); + } + if (!flickingVertically && q->yflick()) { + flickingVertically = true; + emit q->flickingChanged(); + emit q->flickingVerticallyChanged(); + if (!flickingHorizontally) + emit q->flickStarted(); + } + } else { + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); } - if (newAtEndX != atEndX) { - atEndX = newAtEndX; - boundaryChanged = true; +} + +void QDeclarativeFlickablePrivate::fixupY_callback(void *data) +{ + ((QDeclarativeFlickablePrivate *)data)->fixupY(); +} + +void QDeclarativeFlickablePrivate::fixupX_callback(void *data) +{ + ((QDeclarativeFlickablePrivate *)data)->fixupX(); +} + +void QDeclarativeFlickablePrivate::fixupX() +{ + Q_Q(QDeclarativeFlickable); + fixup(hData, q->minXExtent(), q->maxXExtent()); +} + +void QDeclarativeFlickablePrivate::fixupY() +{ + Q_Q(QDeclarativeFlickable); + fixup(vData, q->minYExtent(), q->maxYExtent()); +} + +void QDeclarativeFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if (data.move.value() > minExtent || maxExtent > minExtent) { + timeline.reset(data.move); + if (data.move.value() != minExtent) { + if (fixupDuration) { + qreal dist = minExtent - data.move; + timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); + timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); + } else { + timeline.set(data.move, minExtent); + } + } + } else if (data.move.value() < maxExtent) { + timeline.reset(data.move); + if (fixupDuration) { + qreal dist = maxExtent - data.move; + timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); + timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); + } else { + timeline.set(data.move, minExtent); + } } + vTime = timeline.time(); +} + +void QDeclarativeFlickablePrivate::updateBeginningEnd() +{ + Q_Q(QDeclarativeFlickable); + bool atBoundaryChange = false; // Vertical - if (newAtBeginningY != atBeginningY) { - atBeginningY = newAtBeginningY; - boundaryChanged = true; + const int maxyextent = int(-q->maxYExtent()); + const qreal ypos = -vData.move.value(); + bool atBeginning = (ypos <= -q->minYExtent()); + bool atEnd = (maxyextent <= ypos); + + if (atBeginning != vData.atBeginning) { + vData.atBeginning = atBeginning; + atBoundaryChange = true; + } + if (atEnd != vData.atEnd) { + vData.atEnd = atEnd; + atBoundaryChange = true; + } + + // Horizontal + const int maxxextent = int(-q->maxXExtent()); + const qreal xpos = -hData.move.value(); + atBeginning = (xpos <= -q->minXExtent()); + atEnd = (maxxextent <= xpos); + + if (atBeginning != hData.atBeginning) { + hData.atBeginning = atBeginning; + atBoundaryChange = true; } - if (newAtEndY != atEndY) { - atEndY = newAtEndY; - boundaryChanged = true; + if (atEnd != hData.atEnd) { + hData.atEnd = atEnd; + atBoundaryChange = true; } - if (boundaryChanged) + if (atBoundaryChange) emit q->isAtBoundaryChanged(); if (visibleArea) @@ -379,14 +484,13 @@ qreal QDeclarativeFlickable::contentX() const void QDeclarativeFlickable::setContentX(qreal pos) { Q_D(QDeclarativeFlickable); - QScroller::scroller(this)->stop(); - - if (pos == -d->contentItem->x()) - return; - viewportAboutToMove(QPointF(pos, contentY())); - d->contentItem->setX(-pos); - viewportMoved(); - emit contentXChanged(); + d->timeline.reset(d->hData.move); + d->vTime = d->timeline.time(); + movementXEnding(); + if (-pos != d->hData.move.value()) { + d->hData.move.setValue(-pos); + viewportMoved(); + } } qreal QDeclarativeFlickable::contentY() const @@ -398,14 +502,13 @@ qreal QDeclarativeFlickable::contentY() const void QDeclarativeFlickable::setContentY(qreal pos) { Q_D(QDeclarativeFlickable); - QScroller::scroller(this)->stop(); - - if (pos == -d->contentItem->y()) - return; - viewportAboutToMove(QPointF(contentX(), pos)); - d->contentItem->setY(-pos); - viewportMoved(); - emit contentYChanged(); + d->timeline.reset(d->vData.move); + d->vTime = d->timeline.time(); + movementYEnding(); + if (-pos != d->vData.move.value()) { + d->vData.move.setValue(-pos); + viewportMoved(); + } } /*! @@ -432,7 +535,16 @@ void QDeclarativeFlickable::setInteractive(bool interactive) Q_D(QDeclarativeFlickable); if (interactive != d->interactive) { d->interactive = interactive; - QScroller::scroller(this)->stop(); + if (!interactive && (d->flickingHorizontally || d->flickingVertically)) { + d->timeline.clear(); + d->vTime = d->timeline.time(); + d->flickingHorizontally = false; + d->flickingVertically = false; + emit flickingChanged(); + emit flickingHorizontallyChanged(); + emit flickingVerticallyChanged(); + emit flickEnded(); + } emit interactiveChanged(); } } @@ -442,15 +554,19 @@ void QDeclarativeFlickable::setInteractive(bool interactive) \qmlproperty real Flickable::verticalVelocity The instantaneous velocity of movement along the x and y axes, in pixels/sec. + + The reported velocity is smoothed to avoid erratic output. */ qreal QDeclarativeFlickable::horizontalVelocity() const { - return QScroller::scroller(this)->velocity().x(); + Q_D(const QDeclarativeFlickable); + return d->hData.smoothVelocity.value(); } qreal QDeclarativeFlickable::verticalVelocity() const { - return QScroller::scroller(this)->velocity().y(); + Q_D(const QDeclarativeFlickable); + return d->vData.smoothVelocity.value(); } /*! @@ -465,25 +581,30 @@ qreal QDeclarativeFlickable::verticalVelocity() const bool QDeclarativeFlickable::isAtXEnd() const { Q_D(const QDeclarativeFlickable); - return d->atEndX; + return d->hData.atEnd; } bool QDeclarativeFlickable::isAtXBeginning() const { Q_D(const QDeclarativeFlickable); - return d->atBeginningX; + return d->hData.atBeginning; } bool QDeclarativeFlickable::isAtYEnd() const { Q_D(const QDeclarativeFlickable); - return d->atEndY; + return d->vData.atEnd; } bool QDeclarativeFlickable::isAtYBeginning() const { Q_D(const QDeclarativeFlickable); - return d->atBeginningY; + return d->vData.atBeginning; +} + +void QDeclarativeFlickable::ticked() +{ + viewportMoved(); } /*! @@ -518,52 +639,6 @@ QDeclarativeFlickableVisibleArea *QDeclarativeFlickable::visibleArea() return d->visibleArea; } -void QDeclarativeFlickable::scrollerStateChanged(QScroller::State state) -{ - Q_D(QDeclarativeFlickable); - - //qDebug() << "scrollerStateChanged" << int(state); - - switch (state) { - case QScroller::Inactive: - d->isUserGenerated = false; - // fall through - case QScroller::Pressed: - if (d->isMoving) { - d->isMoving = false; - emit movingChanged(); - emit movementEnded(); - } - if (d->isFlicking) { - d->isFlicking = false; - emit flickingChanged(); - emit flickEnded(); - } - break; - - case QScroller::Dragging: - d->isUserGenerated = true; - if (!d->isMoving) { - d->isMoving = true; - emit movingChanged(); - emit movementStarted(); - } - break; - - case QScroller::Scrolling: - QScroller::scroller(this)->resendPrepareEvent(); // sometimes snappoints depend on the scrolling direction. - if (d->isUserGenerated) { - if (!d->isFlicking) { - emit flickStarted(); - emit flickingChanged(); - } - d->isFlicking = true; - } - break; - } -} - - /*! \qmlproperty enumeration Flickable::flickableDirection @@ -594,92 +669,409 @@ void QDeclarativeFlickable::setFlickableDirection(FlickableDirection direction) } } -/*! \internal - Returns the minimum horizontal content position. - Note: this function will be overloaded. - */ -qreal QDeclarativeFlickable::minXExtent() const +void QDeclarativeFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event) { - return 0.0; + if (interactive && timeline.isActive() && (qAbs(hData.velocity) > 10 || qAbs(vData.velocity) > 10)) + stealMouse = true; // If we've been flicked then steal the click. + else + stealMouse = false; + pressed = true; + timeline.clear(); + hData.velocity = 0; + vData.velocity = 0; + hData.dragStartOffset = 0; + vData.dragStartOffset = 0; + lastPos = QPoint(); + QDeclarativeItemPrivate::start(lastPosTime); + pressPos = event->pos(); + hData.pressPos = hData.move.value(); + vData.pressPos = vData.move.value(); + flickingHorizontally = false; + flickingVertically = false; + QDeclarativeItemPrivate::start(pressTime); + QDeclarativeItemPrivate::start(velocityTime); +} + +void QDeclarativeFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarativeFlickable); + if (!interactive || !lastPosTime.isValid()) + return; + bool rejectY = false; + bool rejectX = false; + + bool stealY = false; + bool stealX = false; + + if (q->yflick()) { + int dy = int(event->pos().y() - pressPos.y()); + if (qAbs(dy) > QApplication::startDragDistance() || QDeclarativeItemPrivate::elapsed(pressTime) > 200) { + if (!vMoved) + vData.dragStartOffset = dy; + qreal newY = dy + vData.pressPos - vData.dragStartOffset; + const qreal minY = q->minYExtent(); + const qreal maxY = q->maxYExtent(); + if (newY > minY) + newY = minY + (newY - minY) / 2; + if (newY < maxY && maxY - minY <= 0) + newY = maxY + (newY - maxY) / 2; + if (boundsBehavior == QDeclarativeFlickable::StopAtBounds && (newY > minY || newY < maxY)) { + rejectY = true; + if (newY < maxY) { + newY = maxY; + rejectY = false; + } + if (newY > minY) { + newY = minY; + rejectY = false; + } + } + if (!rejectY && stealMouse) { + vData.move.setValue(qRound(newY)); + vMoved = true; + } + if (qAbs(dy) > QApplication::startDragDistance()) + stealY = true; + } + } + + if (q->xflick()) { + int dx = int(event->pos().x() - pressPos.x()); + if (qAbs(dx) > QApplication::startDragDistance() || QDeclarativeItemPrivate::elapsed(pressTime) > 200) { + if (!hMoved) + hData.dragStartOffset = dx; + qreal newX = dx + hData.pressPos - hData.dragStartOffset; + const qreal minX = q->minXExtent(); + const qreal maxX = q->maxXExtent(); + if (newX > minX) + newX = minX + (newX - minX) / 2; + if (newX < maxX && maxX - minX <= 0) + newX = maxX + (newX - maxX) / 2; + if (boundsBehavior == QDeclarativeFlickable::StopAtBounds && (newX > minX || newX < maxX)) { + rejectX = true; + if (newX < maxX) { + newX = maxX; + rejectX = false; + } + if (newX > minX) { + newX = minX; + rejectX = false; + } + } + if (!rejectX && stealMouse) { + hData.move.setValue(qRound(newX)); + hMoved = true; + } + + if (qAbs(dx) > QApplication::startDragDistance()) + stealX = true; + } + } + + stealMouse = stealX || stealY; + + if (!lastPos.isNull()) { + qreal elapsed = qreal(QDeclarativeItemPrivate::restart(lastPosTime)) / 1000.; + if (elapsed <= 0) + elapsed = 1; + if (q->yflick()) { + qreal diff = event->pos().y() - lastPos.y(); + // average to reduce the effect of spurious moves + vData.velocity += diff / elapsed; + vData.velocity /= 2; + } + + if (q->xflick()) { + qreal diff = event->pos().x() - lastPos.x(); + // average to reduce the effect of spurious moves + hData.velocity += diff / elapsed; + hData.velocity /= 2; + } + } + + if (rejectY) vData.velocity = 0; + if (rejectX) hData.velocity = 0; + + if (hMoved || vMoved) { + q->movementStarting(); + q->viewportMoved(); + } + + lastPos = event->pos(); +} + +void QDeclarativeFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarativeFlickable); + stealMouse = false; + q->setKeepMouseGrab(false); + pressed = false; + if (!lastPosTime.isValid()) + return; + + if (QDeclarativeItemPrivate::elapsed(lastPosTime) > 100) { + // if we drag then pause before release we should not cause a flick. + hData.velocity = 0.0; + vData.velocity = 0.0; + } + + vTime = timeline.time(); + if (qAbs(vData.velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold) + flickY(vData.velocity); + else + fixupY(); + + if (qAbs(hData.velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold) + flickX(hData.velocity); + else + fixupX(); + + lastPosTime.invalidate(); + + if (!timeline.isActive()) + q->movementEnding(); +} + +void QDeclarativeFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeFlickable); + if (d->interactive) { + d->handleMousePressEvent(event); + event->accept(); + } else { + QDeclarativeItem::mousePressEvent(event); + } +} + +void QDeclarativeFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeFlickable); + if (d->interactive) { + d->handleMouseMoveEvent(event); + if (d->stealMouse) + setKeepMouseGrab(true); + event->accept(); + } else { + QDeclarativeItem::mouseMoveEvent(event); + } +} + +void QDeclarativeFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeFlickable); + if (d->interactive) { + d->clearDelayedPress(); + d->handleMouseReleaseEvent(event); + event->accept(); + ungrabMouse(); + } else { + QDeclarativeItem::mouseReleaseEvent(event); + } +} + +void QDeclarativeFlickable::wheelEvent(QGraphicsSceneWheelEvent *event) +{ + Q_D(QDeclarativeFlickable); + if (!d->interactive) { + QDeclarativeItem::wheelEvent(event); + } else if (yflick()) { + if (event->delta() > 0) + d->vData.velocity = qMax(event->delta() - d->vData.smoothVelocity.value(), qreal(250.0)); + else + d->vData.velocity = qMin(event->delta() - d->vData.smoothVelocity.value(), qreal(-250.0)); + d->flickingVertically = false; + d->flickY(d->vData.velocity); + if (d->flickingVertically) { + d->vMoved = true; + movementStarting(); + } + event->accept(); + } else if (xflick()) { + if (event->delta() > 0) + d->hData.velocity = qMax(event->delta() - d->hData.smoothVelocity.value(), qreal(250.0)); + else + d->hData.velocity = qMin(event->delta() - d->hData.smoothVelocity.value(), qreal(-250.0)); + d->flickingHorizontally = false; + d->flickX(d->hData.velocity); + if (d->flickingHorizontally) { + d->hMoved = true; + movementStarting(); + } + event->accept(); + } else { + QDeclarativeItem::wheelEvent(event); + } +} + +void QDeclarativeFlickablePrivate::captureDelayedPress(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarativeFlickable); + if (!q->scene() || pressDelay <= 0) + return; + delayedPressTarget = q->scene()->mouseGrabberItem(); + delayedPressEvent = new QGraphicsSceneMouseEvent(event->type()); + delayedPressEvent->setAccepted(false); + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (event->buttons() & i) { + Qt::MouseButton button = Qt::MouseButton(i); + delayedPressEvent->setButtonDownPos(button, event->buttonDownPos(button)); + delayedPressEvent->setButtonDownScenePos(button, event->buttonDownScenePos(button)); + delayedPressEvent->setButtonDownScreenPos(button, event->buttonDownScreenPos(button)); + } + } + delayedPressEvent->setButtons(event->buttons()); + delayedPressEvent->setButton(event->button()); + delayedPressEvent->setPos(event->pos()); + delayedPressEvent->setScenePos(event->scenePos()); + delayedPressEvent->setScreenPos(event->screenPos()); + delayedPressEvent->setLastPos(event->lastPos()); + delayedPressEvent->setLastScenePos(event->lastScenePos()); + delayedPressEvent->setLastScreenPos(event->lastScreenPos()); + delayedPressEvent->setModifiers(event->modifiers()); + delayedPressTimer.start(pressDelay, q); +} + +void QDeclarativeFlickablePrivate::clearDelayedPress() +{ + if (delayedPressEvent) { + delayedPressTimer.stop(); + delete delayedPressEvent; + delayedPressEvent = 0; + } +} + +void QDeclarativeFlickablePrivate::setRoundedViewportX(qreal x) +{ + contentItem->setX(qRound(x)); +} + +void QDeclarativeFlickablePrivate::setRoundedViewportY(qreal y) +{ + contentItem->setY(qRound(y)); +} + +void QDeclarativeFlickable::timerEvent(QTimerEvent *event) +{ + Q_D(QDeclarativeFlickable); + if (event->timerId() == d->delayedPressTimer.timerId()) { + d->delayedPressTimer.stop(); + if (d->delayedPressEvent) { + QDeclarativeItem *grabber = scene() ? qobject_cast(scene()->mouseGrabberItem()) : 0; + if (!grabber || grabber != this) { + // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay) + // so we reset the grabber + if (scene()->mouseGrabberItem() == d->delayedPressTarget) + d->delayedPressTarget->ungrabMouse(); + //Use the event handler that will take care of finding the proper item to propagate the event + QApplication::sendEvent(scene(), d->delayedPressEvent); + } + delete d->delayedPressEvent; + d->delayedPressEvent = 0; + } + } } -/*! \internal - Returns the minimum vertical content position. - Note: this function will be overloaded. - */ qreal QDeclarativeFlickable::minYExtent() const { return 0.0; } -/*! \internal - Returns the maximum horizontal content position. - */ -qreal QDeclarativeFlickable::maxXExtent() const + +qreal QDeclarativeFlickable::minXExtent() const { - Q_D(const QDeclarativeFlickable); - return qMax(0.0, d->contentItem->width()); + return 0.0; } -/*! \internal - Returns the maximum vertical content position. - */ -qreal QDeclarativeFlickable::maxYExtent() const +/* returns -ve */ +qreal QDeclarativeFlickable::maxXExtent() const { - Q_D(const QDeclarativeFlickable); - return qMax(0.0, d->contentItem->height()); + return width() - vWidth(); } - -/*! \internal - This function is called before the viewport starts to move. - This is a good chance to fill up some buffers. - */ -void QDeclarativeFlickable::viewportAboutToMove(QPointF newPos) +/* returns -ve */ +qreal QDeclarativeFlickable::maxYExtent() const { - Q_UNUSED(newPos); + return height() - vHeight(); } -/*! \internal - This function is called after the viewport was moved (and the - move finished) - */ void QDeclarativeFlickable::viewportMoved() { Q_D(QDeclarativeFlickable); + + qreal prevX = d->lastFlickablePosition.x(); + qreal prevY = d->lastFlickablePosition.y(); + d->velocityTimeline.clear(); + if (d->pressed || d->calcVelocity) { + int elapsed = QDeclarativeItemPrivate::restart(d->velocityTime); + if (elapsed > 0) { + qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed; + qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed; + d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing); + d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing); + d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing); + d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing); + } + } else { + if (d->timeline.time() > d->vTime) { + qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime); + qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime); + d->hData.smoothVelocity.setValue(horizontalVelocity); + d->vData.smoothVelocity.setValue(verticalVelocity); + } + } + + d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value()); + + d->vTime = d->timeline.time(); d->updateBeginningEnd(); } void QDeclarativeFlickable::geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry) + const QRectF &oldGeometry) { Q_D(QDeclarativeFlickable); QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); bool changed = false; if (newGeometry.width() != oldGeometry.width()) { - changed = true; - // strangely this causes problems - // d->contentItem->setWidth(newGeometry.width()); - emit contentWidthChanged(); + if (xflick()) + changed = true; + if (d->hData.viewSize < 0) { + d->contentItem->setWidth(width()); + emit contentWidthChanged(); + } + // Make sure that we're entirely in view. + if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { + int oldDuration = d->fixupDuration; + d->fixupDuration = 0; + d->fixupX(); + d->fixupDuration = oldDuration; + } } if (newGeometry.height() != oldGeometry.height()) { - changed = true; - // strangely this causes problems - // d->contentItem->setHeight(newGeometry.height()); - emit contentHeightChanged(); + if (yflick()) + changed = true; + if (d->vData.viewSize < 0) { + d->contentItem->setHeight(height()); + emit contentHeightChanged(); + } + // Make sure that we're entirely in view. + if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { + int oldDuration = d->fixupDuration; + d->fixupDuration = 0; + d->fixupY(); + d->fixupDuration = oldDuration; + } } - if (changed) { - QScroller *scroller = QScroller::scroller(this); - - //qDebug() << "xxx geometryChanged: "<state() == QScroller::Inactive) - scroller->ensureVisible( QRectF(0.0, 0.0, - d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); - else - d->updateScrollerValues(); + if (changed) d->updateBeginningEnd(); - } +} + +void QDeclarativeFlickable::cancelFlick() +{ + Q_D(QDeclarativeFlickable); + d->timeline.reset(d->hData.move); + d->timeline.reset(d->vData.move); + movementEnding(); } void QDeclarativeFlickablePrivate::data_append(QDeclarativeListProperty *prop, QObject *o) @@ -765,12 +1157,10 @@ QDeclarativeListProperty QDeclarativeFlickable::flickableChildr of the flickable, and flicks will not overshoot. \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary of the Flickable, but flicks will not overshoot. - \o Flickable.DragAndOvershootBounds - the contents can be dragged + \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged beyond the boundary of the Flickable, and can overshoot the boundary when flicked. \endlist - - The default is platform dependant. */ QDeclarativeFlickable::BoundsBehavior QDeclarativeFlickable::boundsBehavior() const { @@ -784,23 +1174,6 @@ void QDeclarativeFlickable::setBoundsBehavior(BoundsBehavior b) if (b == d->boundsBehavior) return; d->boundsBehavior = b; - - QScroller *scroller = QScroller::scroller(this); - QScrollerProperties props = scroller->scrollerProperties(); - if (b == StopAtBounds) { - props.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.0); - props.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.0); - - } else if (b == DragOverBounds) { - props.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 1.0); - props.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.0); - - } else { - props.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 1.0); - props.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 1.0); - } - scroller->setScrollerProperties(props); - emit boundsBehaviorChanged(); } @@ -824,27 +1197,26 @@ void QDeclarativeFlickable::setBoundsBehavior(BoundsBehavior b) qreal QDeclarativeFlickable::contentWidth() const { Q_D(const QDeclarativeFlickable); - return d->contentItem->width(); + return d->hData.viewSize; } void QDeclarativeFlickable::setContentWidth(qreal w) { Q_D(QDeclarativeFlickable); - if (d->contentItem->width() == w) + if (d->hData.viewSize == w) return; - + d->hData.viewSize = w; if (w < 0) d->contentItem->setWidth(width()); else d->contentItem->setWidth(w); - // Make sure that we're entirely in view. - QScroller *scroller = QScroller::scroller(this); - if (scroller->state() == QScroller::Inactive) - scroller->ensureVisible( QRectF(0.0, 0.0, d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); - else - d->updateScrollerValues(); - + if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { + int oldDuration = d->fixupDuration; + d->fixupDuration = 0; + d->fixupX(); + d->fixupDuration = oldDuration; + } emit contentWidthChanged(); d->updateBeginningEnd(); } @@ -852,142 +1224,170 @@ void QDeclarativeFlickable::setContentWidth(qreal w) qreal QDeclarativeFlickable::contentHeight() const { Q_D(const QDeclarativeFlickable); - return d->contentItem->height(); + return d->vData.viewSize; } void QDeclarativeFlickable::setContentHeight(qreal h) { Q_D(QDeclarativeFlickable); - if (d->contentItem->height() == h) + if (d->vData.viewSize == h) return; - + d->vData.viewSize = h; if (h < 0) d->contentItem->setHeight(height()); else d->contentItem->setHeight(h); - // Make sure that we're entirely in view. - QScroller *scroller = QScroller::scroller(this); - if (scroller->state() == QScroller::Inactive) { - scroller->ensureVisible( QRectF(minXExtent(), minYExtent(), - d->contentItem->width(), d->contentItem->height() ), 0, 0, 0 ); - } else - d->updateScrollerValues(); - + if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { + int oldDuration = d->fixupDuration; + d->fixupDuration = 0; + d->fixupY(); + d->fixupDuration = oldDuration; + } emit contentHeightChanged(); d->updateBeginningEnd(); } -bool QDeclarativeFlickable::event(QEvent *event) +qreal QDeclarativeFlickable::vWidth() const { - Q_D(QDeclarativeFlickable); + Q_D(const QDeclarativeFlickable); + if (d->hData.viewSize < 0) + return width(); + else + return d->hData.viewSize; +} - switch (event->type()) { - case QEvent::ScrollPrepare: { - if (!d->interactive) - return false; - - QScrollPrepareEvent *se = static_cast(event); - se->setViewportSize(QSizeF(d->width(), d->height())); - - QRectF contentPosRange(QPointF(minXExtent(), minYExtent()), - QPointF(maxXExtent() - d->width(), maxYExtent() - d->height())); - if (d->flickableDirection == HorizontalFlick) - contentPosRange.setHeight(0); - if (d->flickableDirection == VerticalFlick) - contentPosRange.setWidth(0); - se->setContentPosRange(contentPosRange); - se->setContentPos(-d->contentItem->pos()); - - //qDebug() << "QDeclarativeFlickable::event: ScPrEv " << this - // << " pos:" << se->contentPos() - // << " range:" << se->contentPosRange(); - - se->accept(); - d->updateScrollerValuesRequested = false; - return true; - } - case QEvent::Scroll: { - d->duringScrollEvent = true; +qreal QDeclarativeFlickable::vHeight() const +{ + Q_D(const QDeclarativeFlickable); + if (d->vData.viewSize < 0) + return height(); + else + return d->vData.viewSize; +} - QScrollEvent *se = static_cast(event); +bool QDeclarativeFlickable::xflick() const +{ + Q_D(const QDeclarativeFlickable); + if (d->flickableDirection == QDeclarativeFlickable::AutoFlickDirection) + return vWidth() != width(); + return d->flickableDirection & QDeclarativeFlickable::HorizontalFlick; +} - /* - qDebug() << "QDeclarativeFlickable::event: Scroll " - << " scrollerPos:" << se->contentPos() - << " pos:" << -d->contentItem->pos() - << " overshoot:" << se->overshootDistance() - << " newPos: " << (se->contentPos() + se->overshootDistance()); - */ +bool QDeclarativeFlickable::yflick() const +{ + Q_D(const QDeclarativeFlickable); + if (d->flickableDirection == QDeclarativeFlickable::AutoFlickDirection) + return vHeight() != height(); + return d->flickableDirection & QDeclarativeFlickable::VerticalFlick; +} - QPointF oldPos = -d->contentItem->pos(); - QPointF newPos = se->contentPos() + se->overshootDistance(); - QPointF delta = newPos - oldPos; +bool QDeclarativeFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeFlickable); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); + + QGraphicsScene *s = scene(); + QDeclarativeItem *grabber = s ? qobject_cast(s->mouseGrabberItem()) : 0; + bool stealThisEvent = d->stealMouse; + if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) { + mouseEvent.setAccepted(false); + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (event->buttons() & i) { + Qt::MouseButton button = Qt::MouseButton(i); + mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button))); + } + } + mouseEvent.setScenePos(event->scenePos()); + mouseEvent.setLastScenePos(event->lastScenePos()); + mouseEvent.setPos(mapFromScene(event->scenePos())); + mouseEvent.setLastPos(mapFromScene(event->lastScenePos())); + + switch(mouseEvent.type()) { + case QEvent::GraphicsSceneMouseMove: + d->handleMouseMoveEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMousePress: + if (d->delayedPressEvent) + return false; + + d->handleMousePressEvent(&mouseEvent); + d->captureDelayedPress(event); + stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above + break; + case QEvent::GraphicsSceneMouseRelease: + if (d->delayedPressEvent) { + // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay) + // so we reset the grabber + if (s->mouseGrabberItem() == d->delayedPressTarget) + d->delayedPressTarget->ungrabMouse(); + //Use the event handler that will take care of finding the proper item to propagate the event + QApplication::sendEvent(scene(), d->delayedPressEvent); + d->clearDelayedPress(); + // We send the release + scene()->sendEvent(s->mouseGrabberItem(), event); + // And the event has been consumed + return true; + } + d->handleMouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = qobject_cast(s->mouseGrabberItem()); + if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) { + d->clearDelayedPress(); + grabMouse(); + } - viewportAboutToMove(newPos); - d->contentItem->setPos(-newPos); - viewportMoved(); - if (delta.x()) - emit contentXChanged(); - if (delta.y()) - emit contentYChanged(); - - d->duringScrollEvent = false; - if (d->updateScrollerValuesRequested) - QScroller::scroller(this)->resendPrepareEvent(); - - bool newMovingHorizontally = delta.x() != 0; - bool newMovingVertically = delta.y() != 0; - if (se->scrollState() == QScrollEvent::ScrollFinished) { - newMovingHorizontally = false; - newMovingVertically = false; - } - - if (newMovingHorizontally != d->movingHorizontally) { - d->movingHorizontally = newMovingHorizontally; - emit movingHorizontallyChanged(); - } - if (newMovingVertically != d->movingVertically) { - d->movingVertically = newMovingVertically; - emit movingVerticallyChanged(); - } - - if (newMovingHorizontally) - emit horizontalVelocityChanged(); - if (newMovingVertically) - emit verticalVelocityChanged(); - - se->accept(); - return true; + return stealThisEvent || d->delayedPressEvent; + } else if (d->lastPosTime.isValid()) { + d->lastPosTime.invalidate(); } + if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) { + d->clearDelayedPress(); + d->stealMouse = false; + d->pressed = false; + } + return false; +} + +bool QDeclarativeFlickable::sceneEventFilter(QGraphicsItem *i, QEvent *e) +{ + Q_D(QDeclarativeFlickable); + if (!isVisible() || !d->interactive) + return QDeclarativeItem::sceneEventFilter(i, e); + switch (e->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + return sendMouseEvent(static_cast(e)); default: break; } - return QDeclarativeItem::event(event); + + return QDeclarativeItem::sceneEventFilter(i, e); } /*! \qmlproperty real Flickable::maximumFlickVelocity - This property holds the maximum velocity that the user can flick the view in pixel per second + This property holds the maximum velocity that the user can flick the view in pixels/second. - The default is platform dependant. + The default is 2000 pixels/s */ qreal QDeclarativeFlickable::maximumFlickVelocity() const { - // 4000 ~ the conversion factor from [m/s] to [pix/s] on a 100dpi screen - return 4000 * QScroller::scroller(const_cast(this))->scrollerProperties().scrollMetric(QScrollerProperties::MaximumVelocity).toDouble(); + Q_D(const QDeclarativeFlickable); + return d->maxVelocity; } void QDeclarativeFlickable::setMaximumFlickVelocity(qreal v) { - // 4000 ~ the conversion factor from [m/s] to [pix/s] on a 100dpi screen - v /= qreal(4000); - QScrollerProperties props = QScroller::scroller(this)->scrollerProperties(); - qreal oldV = props.scrollMetric(QScrollerProperties::MaximumVelocity).toReal(); - if (v == oldV) + Q_D(QDeclarativeFlickable); + if (v == d->maxVelocity) return; - props.setScrollMetric(QScrollerProperties::MaximumVelocity, v); - QScroller::scroller(this)->setScrollerProperties(props); + d->maxVelocity = v; emit maximumFlickVelocityChanged(); } @@ -995,31 +1395,27 @@ void QDeclarativeFlickable::setMaximumFlickVelocity(qreal v) \qmlproperty real Flickable::flickDeceleration This property holds the rate at which a flick will decelerate. - The default is platform dependant. + The default is 500. */ qreal QDeclarativeFlickable::flickDeceleration() const { - // 4000 ~ the conversion factor from [m/s] to [pix/s] on a 100dpi screen - return qreal(4000) * QScroller::scroller(const_cast(this))->scrollerProperties().scrollMetric(QScrollerProperties::DecelerationFactor).toDouble(); + Q_D(const QDeclarativeFlickable); + return d->deceleration; } void QDeclarativeFlickable::setFlickDeceleration(qreal deceleration) { - // 4000 ~ the conversion factor from [m/s] to [pix/s] on a 100dpi screen - deceleration /= qreal(4000); - QScrollerProperties props = QScroller::scroller(this)->scrollerProperties(); - qreal oldDeceleration = props.scrollMetric(QScrollerProperties::DecelerationFactor).toReal(); - if (deceleration == oldDeceleration) + Q_D(QDeclarativeFlickable); + if (deceleration == d->deceleration) return; - props.setScrollMetric(QScrollerProperties::DecelerationFactor, deceleration); - QScroller::scroller(this)->setScrollerProperties(props); + d->deceleration = deceleration; emit flickDecelerationChanged(); } bool QDeclarativeFlickable::isFlicking() const { Q_D(const QDeclarativeFlickable); - return d->isFlicking; + return d->flickingHorizontally || d->flickingVertically; } /*! @@ -1033,13 +1429,13 @@ bool QDeclarativeFlickable::isFlicking() const bool QDeclarativeFlickable::isFlickingHorizontally() const { Q_D(const QDeclarativeFlickable); - return d->movingHorizontally && d->isFlicking; + return d->flickingHorizontally; } bool QDeclarativeFlickable::isFlickingVertically() const { Q_D(const QDeclarativeFlickable); - return d->movingVertically && d->isFlicking; + return d->flickingVertically; } /*! @@ -1055,18 +1451,16 @@ bool QDeclarativeFlickable::isFlickingVertically() const */ int QDeclarativeFlickable::pressDelay() const { - return int(qreal(1000) * QScroller::scroller(const_cast(this))->scrollerProperties().scrollMetric(QScrollerProperties::MousePressEventDelay).toReal()); + Q_D(const QDeclarativeFlickable); + return d->pressDelay; } void QDeclarativeFlickable::setPressDelay(int delay) { - qreal pressDelay = qreal(delay) / qreal(1000); // [ms] -> [s] - QScrollerProperties props = QScroller::scroller(this)->scrollerProperties(); - qreal oldPressDelay = props.scrollMetric(QScrollerProperties::MousePressEventDelay).toReal(); - if (pressDelay == oldPressDelay) + Q_D(QDeclarativeFlickable); + if (d->pressDelay == delay) return; - props.setScrollMetric(QScrollerProperties::MousePressEventDelay, pressDelay); - QScroller::scroller(this)->setScrollerProperties(props); + d->pressDelay = delay; emit pressDelayChanged(); } @@ -1074,7 +1468,7 @@ void QDeclarativeFlickable::setPressDelay(int delay) bool QDeclarativeFlickable::isMoving() const { Q_D(const QDeclarativeFlickable); - return d->isMoving; + return d->movingHorizontally || d->movingVertically; } /*! @@ -1098,4 +1492,83 @@ bool QDeclarativeFlickable::isMovingVertically() const return d->movingVertically; } +void QDeclarativeFlickable::movementStarting() +{ + Q_D(QDeclarativeFlickable); + if (d->hMoved && !d->movingHorizontally) { + d->movingHorizontally = true; + emit movingChanged(); + emit movingHorizontallyChanged(); + if (!d->movingVertically) + emit movementStarted(); + } + else if (d->vMoved && !d->movingVertically) { + d->movingVertically = true; + emit movingChanged(); + emit movingVerticallyChanged(); + if (!d->movingHorizontally) + emit movementStarted(); + } +} + +void QDeclarativeFlickable::movementEnding() +{ + Q_D(QDeclarativeFlickable); + movementXEnding(); + movementYEnding(); + d->hData.smoothVelocity.setValue(0); + d->vData.smoothVelocity.setValue(0); +} + +void QDeclarativeFlickable::movementXEnding() +{ + Q_D(QDeclarativeFlickable); + if (d->flickingHorizontally) { + d->flickingHorizontally = false; + emit flickingChanged(); + emit flickingHorizontallyChanged(); + if (!d->flickingVertically) + emit flickEnded(); + } + if (!d->pressed && !d->stealMouse) { + if (d->movingHorizontally) { + d->movingHorizontally = false; + d->hMoved = false; + emit movingChanged(); + emit movingHorizontallyChanged(); + if (!d->movingVertically) + emit movementEnded(); + } + } +} + +void QDeclarativeFlickable::movementYEnding() +{ + Q_D(QDeclarativeFlickable); + if (d->flickingVertically) { + d->flickingVertically = false; + emit flickingChanged(); + emit flickingVerticallyChanged(); + if (!d->flickingHorizontally) + emit flickEnded(); + } + if (!d->pressed && !d->stealMouse) { + if (d->movingVertically) { + d->movingVertically = false; + d->vMoved = false; + emit movingChanged(); + emit movingVerticallyChanged(); + if (!d->movingHorizontally) + emit movementEnded(); + } + } +} + +void QDeclarativeFlickablePrivate::updateVelocity() +{ + Q_Q(QDeclarativeFlickable); + emit q->horizontalVelocityChanged(); + emit q->verticalVelocityChanged(); +} + QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativeflickable_p.h b/src/declarative/graphicsitems/qdeclarativeflickable_p.h index b79b08c..6e4d8ed 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable_p.h +++ b/src/declarative/graphicsitems/qdeclarativeflickable_p.h @@ -43,7 +43,6 @@ #define QDECLARATIVEFLICKABLE_H #include "qdeclarativeitem.h" -#include QT_BEGIN_HEADER @@ -176,12 +175,19 @@ Q_SIGNALS: void flickEnded(); protected: - virtual bool event(QEvent *e); + virtual bool sceneEventFilter(QGraphicsItem *, QEvent *); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void wheelEvent(QGraphicsSceneWheelEvent *event); + void timerEvent(QTimerEvent *event); QDeclarativeFlickableVisibleArea *visibleArea(); protected Q_SLOTS: - virtual void scrollerStateChanged(QScroller::State); + virtual void ticked(); + void movementStarting(); + void movementEnding(); protected: void movementXEnding(); @@ -190,10 +196,16 @@ protected: virtual qreal minYExtent() const; virtual qreal maxXExtent() const; virtual qreal maxYExtent() const; - virtual void viewportAboutToMove(QPointF newPos); + qreal vWidth() const; + qreal vHeight() const; virtual void viewportMoved(); virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + + bool xflick() const; + bool yflick() const; + void cancelFlick(); protected: QDeclarativeFlickable(QDeclarativeFlickablePrivate &dd, QDeclarativeItem *parent); @@ -201,8 +213,6 @@ protected: private: Q_DISABLE_COPY(QDeclarativeFlickable) Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeFlickable) - - friend class QDeclarativeFlickableVisibleArea; }; diff --git a/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h b/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h index 1f348ab..92cf748 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h @@ -78,35 +78,94 @@ public: QDeclarativeFlickablePrivate(); void init(); - void updateBeginningEnd(); - - void updateScrollerValues(); - void updateOvershootPolicy(); - void itemGeometryChanged(QDeclarativeItem *, const QRectF &, const QRectF &); - - // private slot - void _q_scrollerStateChanged(QScroller::State state); + struct Velocity : public QDeclarativeTimeLineValue + { + Velocity(QDeclarativeFlickablePrivate *p) + : parent(p) {} + virtual void setValue(qreal v) { + if (v != value()) { + QDeclarativeTimeLineValue::setValue(v); + parent->updateVelocity(); + } + } + QDeclarativeFlickablePrivate *parent; + }; + + struct AxisData { + AxisData(QDeclarativeFlickablePrivate *fp, void (QDeclarativeFlickablePrivate::*func)(qreal)) + : move(fp, func), viewSize(-1), smoothVelocity(fp), atEnd(false), atBeginning(true) + {} + + QDeclarativeTimeLineValueProxy move; + qreal viewSize; + qreal pressPos; qreal dragStartOffset; + qreal velocity; + qreal flickTarget; + QDeclarativeFlickablePrivate::Velocity smoothVelocity; + bool atEnd : 1; + bool atBeginning : 1; + }; + + void flickX(qreal velocity); + void flickY(qreal velocity); + virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); + + void fixupX(); + void fixupY(); + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); -public: - QDeclarativeItem *contentItem; + void updateBeginningEnd(); + + void captureDelayedPress(QGraphicsSceneMouseEvent *event); + void clearDelayedPress(); - bool updateScrollerValuesRequested; - bool duringScrollEvent; + void setRoundedViewportX(qreal x); + void setRoundedViewportY(qreal y); - bool isUserGenerated; - bool isFlicking; - bool isMoving; - bool movingHorizontally; - bool movingVertically; + qreal overShootDistance(qreal velocity, qreal size); - bool atBeginningX; - bool atBeginningY; - bool atEndX; - bool atEndY; + void itemGeometryChanged(QDeclarativeItem *, const QRectF &, const QRectF &); - bool interactive; +public: + QDeclarativeItem *contentItem; + AxisData hData; + AxisData vData; + + QDeclarativeTimeLine timeline; + bool flickingHorizontally : 1; + bool flickingVertically : 1; + bool hMoved : 1; + bool vMoved : 1; + bool movingHorizontally : 1; + bool movingVertically : 1; + bool stealMouse : 1; + bool pressed : 1; + bool interactive : 1; + bool calcVelocity : 1; + QElapsedTimer lastPosTime; + QPointF lastPos; + QPointF pressPos; + QElapsedTimer pressTime; + qreal deceleration; + qreal maxVelocity; + QElapsedTimer velocityTime; + QPointF lastFlickablePosition; + qreal reportedVelocitySmoothing; + QGraphicsSceneMouseEvent *delayedPressEvent; + QGraphicsItem *delayedPressTarget; + QBasicTimer delayedPressTimer; + int pressDelay; + int fixupDuration; + + static void fixupY_callback(void *); + static void fixupX_callback(void *); + + void updateVelocity(); + int vTime; + QDeclarativeTimeLine velocityTimeline; QDeclarativeFlickableVisibleArea *visibleArea; QDeclarativeFlickable::FlickableDirection flickableDirection; QDeclarativeFlickable::BoundsBehavior boundsBehavior; diff --git a/src/declarative/graphicsitems/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp index b6af7dc..6f38f63 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview.cpp +++ b/src/declarative/graphicsitems/qdeclarativegridview.cpp @@ -95,42 +95,24 @@ public: //---------------------------------------------------------------------------- -/* - Some explanations: - The grid view is not creating graphics items for every entry - in the model. - - Instead it's creating only number of items that are currently - visible in visibleItems. The first model index of those items is - visibleIndex -*/ - class QDeclarativeGridViewPrivate : public QDeclarativeFlickablePrivate { Q_DECLARE_PUBLIC(QDeclarativeGridView) public: QDeclarativeGridViewPrivate() - : currentItem(0), flow(QDeclarativeGridView::LeftToRight) - , visibleIndex(0) , currentIndex(-1) - , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), modelCount(0) - , highlightRangeStart(0), highlightRangeEnd(0), highlightRange(QDeclarativeGridView::NoHighlightRange) - , highlightComponent(0), highlight(0) - , moveReason(Other) - , buffer(0) - , bufferMode(BufferBefore | BufferAfter) - , highlightXAnimator(0), highlightYAnimator(0) - , highlightMoveDuration(150) - , footerComponent(0) - , footer(0) - , headerComponent(0) - , header(0) - , snapMode(QDeclarativeGridView::NoSnap) - , ownModel(false), wrap(false), autoHighlight(true) - , fixCurrentVisibility(false), lazyRelease(false), layoutScheduled(false) - , deferredRelease(false), haveHighlightRange(false) - , currentIndexCleared(false) - {} + : currentItem(0), flow(QDeclarativeGridView::LeftToRight) + , visibleIndex(0) , currentIndex(-1) + , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), itemCount(0) + , highlightRangeStart(0), highlightRangeEnd(0), highlightRange(QDeclarativeGridView::NoHighlightRange) + , highlightComponent(0), highlight(0), trackedItem(0) + , moveReason(Other), buffer(0), highlightXAnimator(0), highlightYAnimator(0) + , highlightMoveDuration(150) + , footerComponent(0), footer(0), headerComponent(0), header(0) + , bufferMode(BufferBefore | BufferAfter), snapMode(QDeclarativeGridView::NoSnap) + , ownModel(false), wrap(false), autoHighlight(true) + , fixCurrentVisibility(false), lazyRelease(false), layoutScheduled(false) + , deferredRelease(false), haveHighlightRange(false), currentIndexCleared(false) {} void init(); void clear(); @@ -138,15 +120,18 @@ public: void releaseItem(FxGridItem *item); void refill(qreal from, qreal to, bool doBuffer=false); + void updateGrid(); void scheduleLayout(); void layout(); void updateUnrequestedIndexes(); void updateUnrequestedPositions(); - void recreateHighlight(); - void updateHighlight(bool smooth = true); - void setCurrentIndex(int modelIndex); + void updateTrackedItem(); + void createHighlight(); + void updateHighlight(); + void updateCurrent(int modelIndex); void updateHeader(); void updateFooter(); + void fixupPosition(); FxGridItem *visibleItem(int modelIndex) const { if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { @@ -159,28 +144,10 @@ public: return 0; } - /* The following methods are using flow independent coordinates. - row and column also change the direction depending on the flow. - */ - - /*! \internal - Returns the position of the view. - Depending on the flow this can be either the x or the y content coordinate - */ qreal position() const { Q_Q(const QDeclarativeGridView); return flow == QDeclarativeGridView::LeftToRight ? q->contentY() : q->contentX(); } - - void scrollTo(qreal pos) { - Q_Q(QDeclarativeGridView); - QScroller *scroller = QScroller::scroller(q); - if (flow == QDeclarativeGridView::LeftToRight) - scroller->scrollTo(QPoint(q->contentX(), pos)); - else - scroller->scrollTo(QPoint(pos, q->contentY())); - } - void setPosition(qreal pos) { Q_Q(QDeclarativeGridView); if (flow == QDeclarativeGridView::LeftToRight) @@ -192,39 +159,22 @@ public: Q_Q(const QDeclarativeGridView); return flow == QDeclarativeGridView::LeftToRight ? q->height() : q->width(); } - - /*! \internal - Returns the row size of the header to be added to the total item size. - */ - qreal headerSize() const { - if (!header) - return 0; - if (flow == QDeclarativeGridView::LeftToRight) - return header->item->height(); - else - return header->item->width(); - } - - /*! \internal - Returns the row size of the footer to be added to the total item size. - */ - qreal footerSize() const { - if (!footer) - return 0; - if (flow == QDeclarativeGridView::LeftToRight) - return footer->item->height(); - else - return footer->item->width(); + qreal startPosition() const { + qreal pos = 0; + if (!visibleItems.isEmpty()) + pos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); + return pos; } - qreal contentSize() const { - if (!model || !model->isValid()) - return 0.0; - return ((modelCount + columns - 1 /*round up*/) / columns) * rowSize(); + qreal endPosition() const { + qreal pos = 0; + if (model && model->count()) + pos = rowPosAt(model->count() - 1) + rowSize(); + return pos; } bool isValid() const { - return model && modelCount && model->isValid(); + return model && model->count() && model->isValid(); } int rowSize() const { @@ -235,13 +185,51 @@ public: } qreal colPosAt(int modelIndex) const { - return (modelIndex % columns) * colSize(); + if (FxGridItem *item = visibleItem(modelIndex)) + return item->colPos(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = (visibleIndex - modelIndex) % columns; + int col = visibleItems.first()->colPos() / colSize(); + col = (columns - count + col) % columns; + return col * colSize(); + } else { + int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns; + return visibleItems.last()->colPos() - count * colSize(); + } + } else { + return (modelIndex % columns) * colSize(); + } + return 0; } qreal rowPosAt(int modelIndex) const { - return (modelIndex / columns) * rowSize() + headerSize(); + if (FxGridItem *item = visibleItem(modelIndex)) + return item->rowPos(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int firstCol = visibleItems.first()->colPos() / colSize(); + int col = visibleIndex - modelIndex + (columns - firstCol - 1); + int rows = col / columns; + return visibleItems.first()->rowPos() - rows * rowSize(); + } else { + int count = modelIndex - visibleItems.last()->index; + int col = visibleItems.last()->colPos() + count * colSize(); + int rows = col / (columns * colSize()); + return visibleItems.last()->rowPos() + rows * rowSize(); + } + } else { + qreal pos = (modelIndex / columns) * rowSize(); + if (header) { + qreal headerSize = flow == QDeclarativeGridView::LeftToRight + ? header->item->height() + : header->item->width(); + pos += headerSize; + } + return pos; + } + return 0; } - /*! Returns the first of the visibleItems that is visible */ FxGridItem *firstVisibleItem() const { const qreal pos = position(); for (int i = 0; i < visibleItems.count(); ++i) { @@ -252,17 +240,16 @@ public: return visibleItems.count() ? visibleItems.first() : 0; } - /*! \internal - Returns the last visible model index. delayRemove items are not counted (as they don't have an index) - */ int lastVisibleIndex() const { - // find the last valid model index - for (int i = visibleItems.count() - 1; i >= 0; --i) { - FxGridItem *item = visibleItems.at(i); - if (item->index != -1) - return item->index; + int lastIndex = -1; + for (int i = visibleItems.count()-1; i >= 0; --i) { + FxGridItem *gridItem = visibleItems.at(i); + if (gridItem->index != -1) { + lastIndex = gridItem->index; + break; + } } - return visibleIndex; + return lastIndex; } // Map a model index to visibleItems list index. @@ -281,22 +268,51 @@ public: return -1; // Not in visibleList } - /*! \internal - Return the model index of the visible item which is - next to the highlight. - */ - int snapIndex() - { - // which is the next row and column around the hightlight - int nextRow = qRound(highlight->rowPos() / rowSize()); - int nextCol = qRound(highlight->colPos() / colSize()); - - // calculate and verify index - int index = nextRow * columns + nextCol; - if (index < 0 || index >= modelCount) - return currentIndex; - else - return index; + qreal snapPosAt(qreal pos) const { + Q_Q(const QDeclarativeGridView); + qreal snapPos = 0; + if (!visibleItems.isEmpty()) { + pos += rowSize()/2; + snapPos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); + snapPos = pos - fmodf(pos - snapPos, qreal(rowSize())); + qreal maxExtent = flow == QDeclarativeGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); + qreal minExtent = flow == QDeclarativeGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); + if (snapPos > maxExtent) + snapPos = maxExtent; + if (snapPos < minExtent) + snapPos = minExtent; + } + return snapPos; + } + + FxGridItem *snapItemAt(qreal pos) { + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems[i]; + if (item->index == -1) + continue; + qreal itemTop = item->rowPos(); + if (item->index == model->count()-1 || (itemTop+rowSize()/2 >= pos)) + return item; + } + if (visibleItems.count() && visibleItems.first()->rowPos() <= pos) + return visibleItems.first(); + return 0; + } + + int snapIndex() { + int index = currentIndex; + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems[i]; + if (item->index == -1) + continue; + qreal itemTop = item->rowPos(); + if (itemTop >= highlight->rowPos()-rowSize()/2 && itemTop < highlight->rowPos()+rowSize()/2) { + index = item->index; + if (item->colPos() >= highlight->colPos()-colSize()/2 && item->colPos() < highlight->colPos()+colSize()/2) + return item->index; + } + } + return index; } virtual void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { @@ -306,6 +322,7 @@ public: if (newGeometry.height() != oldGeometry.height() || newGeometry.width() != oldGeometry.width()) { if (q->isComponentComplete()) { + updateGrid(); scheduleLayout(); } } @@ -313,10 +330,12 @@ public: updateHeader(); updateFooter(); } - if (currentItem && currentItem->item == item) - updateHighlight(); } + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); + virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); + // for debugging only void checkVisible() const { int skip = 0; @@ -335,71 +354,34 @@ public: QDeclarativeGuard model; QVariant modelVariant; QList visibleItems; - - /* - These are items that were not requested but send by createdItem from the model. - Usually these items were created because another view showing the same model - needed them. - */ QHash unrequestedItems; - FxGridItem *currentItem; QDeclarativeGridView::Flow flow; - - /* - The visibleIndex is the modelIndex of the first item in visibleItems - visible row. - */ int visibleIndex; - - /* - The currentIndex is the model index of the currently highlighted item. - */ int currentIndex; - int cellWidth; int cellHeight; - - /* - The number of columns this layout currently has. - */ int columns; - - /* - The model index of the item that is currently created via createItem - */ int requestedIndex; - - /* - This is the number of items in the model. - */ - int modelCount; - + int itemCount; qreal highlightRangeStart; qreal highlightRangeEnd; QDeclarativeGridView::HighlightRangeMode highlightRange; QDeclarativeComponent *highlightComponent; FxGridItem *highlight; + FxGridItem *trackedItem; enum MovementReason { Other, SetIndex, Mouse }; MovementReason moveReason; - - /* - The maximum number of pixels that should be covered by buffered - delegates in front and behind the visible area. - */ int buffer; - enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; - int bufferMode; - QSmoothedAnimation *highlightXAnimator; QSmoothedAnimation *highlightYAnimator; int highlightMoveDuration; - QDeclarativeComponent *footerComponent; FxGridItem *footer; QDeclarativeComponent *headerComponent; FxGridItem *header; - + enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; + int bufferMode; QDeclarativeGridView::SnapMode snapMode; bool ownModel : 1; @@ -416,6 +398,7 @@ public: void QDeclarativeGridViewPrivate::init() { Q_Q(QDeclarativeGridView); + QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); q->setFlag(QGraphicsItem::ItemIsFocusScope); q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick); addItemChangeListener(this, Geometry); @@ -429,8 +412,9 @@ void QDeclarativeGridViewPrivate::clear() visibleIndex = 0; releaseItem(currentItem); currentItem = 0; - recreateHighlight(); - modelCount = 0; + createHighlight(); + trackedItem = 0; + itemCount = 0; } FxGridItem *QDeclarativeGridViewPrivate::createItem(int modelIndex) @@ -462,6 +446,11 @@ void QDeclarativeGridViewPrivate::releaseItem(FxGridItem *item) Q_Q(QDeclarativeGridView); if (!item || !model) return; + if (trackedItem == item) { + QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); + QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); + trackedItem = 0; + } if (model->release(item->item) == 0) { // item was not destroyed, and we no longer reference it. unrequestedItems.insert(item->item, model->indexOf(item->item, q)); @@ -472,12 +461,9 @@ void QDeclarativeGridViewPrivate::releaseItem(FxGridItem *item) void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) { Q_Q(QDeclarativeGridView); - if (!q->isComponentComplete()) + if (!isValid() || !q->isComponentComplete()) return; - - if (!doBuffer && buffer && bufferMode != NoBuffer) - doBuffer = true; - + itemCount = model->count(); qreal bufferFrom = from - buffer; qreal bufferTo = to + buffer; qreal fillFrom = from; @@ -489,32 +475,29 @@ void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) bool changed = false; - updateHeader(); - - columns = (int)qMax((flow == QDeclarativeGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); - - // -- update the positions of all visible items - for (int i = 0; i < visibleItems.count(); ++i) { - qreal colPos = colPosAt(i + visibleIndex); - qreal rowPos = rowPosAt(i + visibleIndex); - FxGridItem *item = visibleItems.at(i); - item->setPosition(colPos, rowPos); + int colPos = colPosAt(visibleIndex); + int rowPos = rowPosAt(visibleIndex); + int modelIndex = visibleIndex; + if (visibleItems.count()) { + rowPos = visibleItems.last()->rowPos(); + colPos = visibleItems.last()->colPos() + colSize(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + modelIndex = visibleItems.at(i)->index + 1; } - - // determine the next position - int modelIndex = lastVisibleIndex(); - if (!visibleItems.isEmpty()) - modelIndex++; - qreal colPos = colPosAt(modelIndex); - qreal rowPos = rowPosAt(modelIndex); - int colNum = colPos / colSize(); + FxGridItem *item = 0; // Item creation and release is staggered in order to avoid // creating/releasing multiple items in one frame // while flicking (as much as possible). - while (modelIndex < modelCount && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) { + while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) { // qDebug() << "refill: append item" << modelIndex; if (!(item = createItem(modelIndex))) break; @@ -588,19 +571,33 @@ void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) } else { deferredRelease = true; } - - if (flow == QDeclarativeGridView::LeftToRight) - q->setContentHeight(contentSize()); - else - q->setContentWidth(contentSize()); - - updateFooter(); - - updateUnrequestedPositions(); - + if (changed) { + if (header) + updateHeader(); + if (footer) + updateFooter(); + if (flow == QDeclarativeGridView::LeftToRight) + q->setContentHeight(endPosition() - startPosition()); + else + q->setContentWidth(endPosition() - startPosition()); + } else if (!doBuffer && buffer && bufferMode != NoBuffer) { + refill(from, to, true); + } lazyRelease = false; } +void QDeclarativeGridViewPrivate::updateGrid() +{ + Q_Q(QDeclarativeGridView); + columns = (int)qMax((flow == QDeclarativeGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); + if (isValid()) { + if (flow == QDeclarativeGridView::LeftToRight) + q->setContentHeight(endPosition() - startPosition()); + else + q->setContentWidth(endPosition() - startPosition()); + } +} + void QDeclarativeGridViewPrivate::scheduleLayout() { Q_Q(QDeclarativeGridView); @@ -612,9 +609,53 @@ void QDeclarativeGridViewPrivate::scheduleLayout() void QDeclarativeGridViewPrivate::layout() { + Q_Q(QDeclarativeGridView); layoutScheduled = false; + if (!isValid() && !visibleItems.count()) { + clear(); + return; + } + if (visibleItems.count()) { + qreal rowPos = visibleItems.first()->rowPos(); + qreal colPos = visibleItems.first()->colPos(); + int col = visibleIndex % columns; + if (colPos != col * colSize()) { + colPos = col * colSize(); + visibleItems.first()->setPosition(colPos, rowPos); + } + for (int i = 1; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems.at(i); + colPos += colSize(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + item->setPosition(colPos, rowPos); + } + } + if (header) + updateHeader(); + if (footer) + updateFooter(); + q->refill(); + updateHighlight(); + moveReason = Other; + if (flow == QDeclarativeGridView::LeftToRight) { + q->setContentHeight(endPosition() - startPosition()); + fixupY(); + } else { + q->setContentWidth(endPosition() - startPosition()); + fixupX(); + } + updateUnrequestedPositions(); +} - refill(position(), position() + size() - 1); +void QDeclarativeGridViewPrivate::updateUnrequestedIndexes() +{ + Q_Q(QDeclarativeGridView); + QHash::iterator it; + for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + *it = model->indexOf(it.key(), q); } void QDeclarativeGridViewPrivate::updateUnrequestedPositions() @@ -629,11 +670,35 @@ void QDeclarativeGridViewPrivate::updateUnrequestedPositions() } } -void QDeclarativeGridViewPrivate::recreateHighlight() +void QDeclarativeGridViewPrivate::updateTrackedItem() +{ + Q_Q(QDeclarativeGridView); + FxGridItem *item = currentItem; + if (highlight) + item = highlight; + + if (trackedItem && item != trackedItem) { + QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); + QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); + trackedItem = 0; + } + + if (!trackedItem && item) { + trackedItem = item; + QObject::connect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); + QObject::connect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); + } + if (trackedItem) + q->trackedPositionChanged(); +} + +void QDeclarativeGridViewPrivate::createHighlight() { Q_Q(QDeclarativeGridView); bool changed = false; if (highlight) { + if (trackedItem == highlight) + trackedItem = 0; delete highlight->item; delete highlight; highlight = 0; @@ -659,6 +724,8 @@ void QDeclarativeGridViewPrivate::recreateHighlight() } } else { item = new QDeclarativeItem; + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); } if (item) { QDeclarative_setParent_noEvent(item, q->contentItem()); @@ -683,88 +750,59 @@ void QDeclarativeGridViewPrivate::recreateHighlight() emit q->highlightItemChanged(); } -void QDeclarativeGridViewPrivate::updateHighlight(bool smooth) +void QDeclarativeGridViewPrivate::updateHighlight() { if ((!currentItem && highlight) || (currentItem && !highlight)) - recreateHighlight(); - - // --- move the current item between the highlight range - if (moveReason == QDeclarativeGridViewPrivate::SetIndex) { - // ensure that the tracked item is inside the highlight range - - // reposition view - if (currentItem) { - qreal pos = currentItem->rowPos(); - qreal viewPos = position(); - - if (autoHighlight && haveHighlightRange) { - if (pos > viewPos + highlightRangeEnd - rowSize()) - viewPos = pos - highlightRangeEnd + rowSize(); - if (pos < viewPos + highlightRangeStart) - viewPos = pos - highlightRangeStart; - - } else { - if (pos > viewPos + size() - rowSize()) - viewPos = pos - size() + rowSize(); - if (pos < viewPos + 0) - viewPos = pos - 0; - } - if (smooth) - scrollTo(viewPos); - else - setPosition(viewPos); - } - - // move the highlight - if (currentItem && autoHighlight && highlight) { - highlight->item->setWidth(currentItem->item->width()); - highlight->item->setHeight(currentItem->item->height()); - highlightXAnimator->to = currentItem->item->x(); - highlightYAnimator->to = currentItem->item->y(); - if (smooth) { - highlightXAnimator->restart(); - highlightYAnimator->restart(); - } else { - highlightXAnimator->stop(); - highlightYAnimator->stop(); - highlight->item->setPos(QPointF(highlightXAnimator->to, - highlightYAnimator->to)); - } - } + createHighlight(); + if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) { + // auto-update highlight + highlightXAnimator->to = currentItem->item->x(); + highlightYAnimator->to = currentItem->item->y(); + highlight->item->setWidth(currentItem->item->width()); + highlight->item->setHeight(currentItem->item->height()); + highlightXAnimator->restart(); + highlightYAnimator->restart(); } + updateTrackedItem(); } -void QDeclarativeGridViewPrivate::setCurrentIndex(int newIndex) +void QDeclarativeGridViewPrivate::updateCurrent(int modelIndex) { Q_Q(QDeclarativeGridView); - - // --- release the old item - if (currentItem && currentIndex != newIndex ) { + if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { + if (currentItem) { currentItem->attached->setIsCurrentItem(false); releaseItem(currentItem); currentItem = 0; + currentIndex = modelIndex; + emit q->currentIndexChanged(); + updateHighlight(); + } else if (currentIndex != modelIndex) { + currentIndex = modelIndex; + emit q->currentIndexChanged(); + } + return; } - int oldIndex = currentIndex; - currentIndex = newIndex; - - bool visible = (q->isComponentComplete() && isValid() && !currentItem && - newIndex >= 0 && newIndex < modelCount); - - // --- set the new item - if (visible) { - currentItem = createItem(newIndex); - if (currentItem) { - currentItem->setPosition(colPosAt(newIndex), rowPosAt(newIndex)); - currentItem->item->setFocus(true); - currentItem->attached->setIsCurrentItem(true); - } + if (currentItem && currentIndex == modelIndex) { + updateHighlight(); + return; } + FxGridItem *oldCurrentItem = currentItem; + currentIndex = modelIndex; + currentItem = createItem(modelIndex); + fixCurrentVisibility = true; + if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) + oldCurrentItem->attached->setIsCurrentItem(false); + if (currentItem) { + currentItem->setPosition(colPosAt(modelIndex), rowPosAt(modelIndex)); + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + } updateHighlight(); - - if (currentIndex != oldIndex) - emit q->currentIndexChanged(); + emit q->currentIndexChanged(); + releaseItem(oldCurrentItem); } void QDeclarativeGridViewPrivate::updateFooter() @@ -792,7 +830,24 @@ void QDeclarativeGridViewPrivate::updateFooter() } } if (footer) { - footer->setPosition(0, contentSize() + headerSize()); + if (visibleItems.count()) { + qreal endPos = endPosition(); + if (lastVisibleIndex() == model->count()-1) { + footer->setPosition(0, endPos); + } else { + qreal visiblePos = position() + q->height(); + if (endPos <= visiblePos || footer->endRowPos() < endPos) + footer->setPosition(0, endPos); + } + } else { + qreal endPos = 0; + if (header) { + endPos += flow == QDeclarativeGridView::LeftToRight + ? header->item->height() + : header->item->width(); + } + footer->setPosition(0, endPos); + } } } @@ -821,10 +876,194 @@ void QDeclarativeGridViewPrivate::updateHeader() } } if (header) { - header->setPosition(0, 0); + if (visibleItems.count()) { + qreal startPos = startPosition(); + qreal headerSize = flow == QDeclarativeGridView::LeftToRight + ? header->item->height() + : header->item->width(); + if (visibleIndex == 0) { + header->setPosition(0, startPos - headerSize); + } else { + if (position() <= startPos || header->rowPos() > startPos - headerSize) + header->setPosition(0, startPos - headerSize); + } + } else { + header->setPosition(0, 0); + } } } +void QDeclarativeGridViewPrivate::fixupPosition() +{ + moveReason = Other; + if (flow == QDeclarativeGridView::LeftToRight) + fixupY(); + else + fixupX(); +} + +void QDeclarativeGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if ((flow == QDeclarativeGridView::TopToBottom && &data == &vData) + || (flow == QDeclarativeGridView::LeftToRight && &data == &hData)) + return; + + int oldDuration = fixupDuration; + fixupDuration = moveReason == Mouse ? fixupDuration : 0; + + if (snapMode != QDeclarativeGridView::NoSnap) { + FxGridItem *topItem = snapItemAt(position()+highlightRangeStart); + FxGridItem *bottomItem = snapItemAt(position()+highlightRangeEnd); + qreal pos; + if (topItem && bottomItem && haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { + qreal topPos = qMin(topItem->rowPos() - highlightRangeStart, -maxExtent); + qreal bottomPos = qMax(bottomItem->rowPos() - highlightRangeEnd, -minExtent); + pos = qAbs(data.move + topPos) < qAbs(data.move + bottomPos) ? topPos : bottomPos; + } else if (topItem) { + pos = qMax(qMin(topItem->rowPos() - highlightRangeStart, -maxExtent), -minExtent); + } else if (bottomItem) { + pos = qMax(qMin(bottomItem->rowPos() - highlightRangeStart, -maxExtent), -minExtent); + } else { + fixupDuration = oldDuration; + return; + } + if (currentItem && haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { + updateHighlight(); + qreal currPos = currentItem->rowPos(); + if (pos < currPos + rowSize() - highlightRangeEnd) + pos = currPos + rowSize() - highlightRangeEnd; + if (pos > currPos - highlightRangeStart) + pos = currPos - highlightRangeStart; + } + + qreal dist = qAbs(data.move + pos); + if (dist > 0) { + timeline.reset(data.move); + if (fixupDuration) + timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + else + timeline.set(data.move, -pos); + vTime = timeline.time(); + } + } else if (haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { + if (currentItem) { + updateHighlight(); + qreal pos = currentItem->rowPos(); + qreal viewPos = position(); + if (viewPos < pos + rowSize() - highlightRangeEnd) + viewPos = pos + rowSize() - highlightRangeEnd; + if (viewPos > pos - highlightRangeStart) + viewPos = pos - highlightRangeStart; + + timeline.reset(data.move); + if (viewPos != position()) { + if (fixupDuration) + timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + else + timeline.set(data.move, -viewPos); + } + vTime = timeline.time(); + } + } else { + QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); + } + fixupDuration = oldDuration; +} + +void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QDeclarativeGridView); + moveReason = Mouse; + if ((!haveHighlightRange || highlightRange != QDeclarativeGridView::StrictlyEnforceRange) + && snapMode == QDeclarativeGridView::NoSnap) { + QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); + return; + } + qreal maxDistance = 0; + // -ve velocity means list is moving up + if (velocity > 0) { + if (data.move.value() < minExtent) { + if (snapMode == QDeclarativeGridView::SnapOneRow) { + if (FxGridItem *item = firstVisibleItem()) + maxDistance = qAbs(item->rowPos() + data.move.value()); + } else { + maxDistance = qAbs(minExtent - data.move.value()); + } + } + if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange) + data.flickTarget = minExtent; + } else { + if (data.move.value() > maxExtent) { + if (snapMode == QDeclarativeGridView::SnapOneRow) { + qreal pos = snapPosAt(-data.move.value()) + rowSize(); + maxDistance = qAbs(pos + data.move.value()); + } else { + maxDistance = qAbs(maxExtent - data.move.value()); + } + } + if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange) + data.flickTarget = maxExtent; + } + bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds; + if (maxDistance > 0 || overShoot) { + // This mode requires the grid to stop exactly on a row boundary. + qreal v = velocity; + if (maxVelocity != -1 && maxVelocity < qAbs(v)) { + if (v < 0) + v = -maxVelocity; + else + v = maxVelocity; + } + qreal accel = deceleration; + qreal v2 = v * v; + qreal overshootDist = 0.0; + if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeGridView::SnapOneRow) { + // + rowSize()/4 to encourage moving at least one item in the flick direction + qreal dist = v2 / (accel * 2.0) + rowSize()/4; + dist = qMin(dist, maxDistance); + if (v > 0) + dist = -dist; + data.flickTarget = -snapPosAt(-(data.move.value() - highlightRangeStart) + dist) + highlightRangeStart; + qreal adjDist = -data.flickTarget + data.move.value(); + if (qAbs(adjDist) > qAbs(dist)) { + // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration + qreal adjv2 = accel * 2.0f * qAbs(adjDist); + if (adjv2 > v2) { + v2 = adjv2; + v = qSqrt(v2); + if (dist > 0) + v = -v; + } + } + dist = adjDist; + accel = v2 / (2.0f * qAbs(dist)); + } else { + data.flickTarget = velocity > 0 ? minExtent : maxExtent; + overshootDist = overShoot ? overShootDistance(v, vSize) : 0; + } + timeline.reset(data.move); + timeline.accel(data.move, v, accel, maxDistance + overshootDist); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + if (!flickingHorizontally && q->xflick()) { + flickingHorizontally = true; + emit q->flickingChanged(); + emit q->flickingHorizontallyChanged(); + emit q->flickStarted(); + } + if (!flickingVertically && q->yflick()) { + flickingVertically = true; + emit q->flickingChanged(); + emit q->flickingVerticallyChanged(); + emit q->flickStarted(); + } + } else { + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + } +} + + //---------------------------------------------------------------------------- /*! @@ -968,10 +1207,10 @@ QVariant QDeclarativeGridView::model() const return d->modelVariant; } -void QDeclarativeGridView::setModel(const QVariant &newModel) +void QDeclarativeGridView::setModel(const QVariant &model) { Q_D(QDeclarativeGridView); - if (d->modelVariant == newModel) + if (d->modelVariant == model) return; if (d->model) { disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); @@ -981,18 +1220,13 @@ void QDeclarativeGridView::setModel(const QVariant &newModel) disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); } - d->clear(); - QDeclarativeVisualModel *oldModel = d->model; - d->model = 0; - d->modelCount = 0; - - d->modelVariant = newModel; - QObject *object = qvariant_cast(newModel); + d->modelVariant = model; + QObject *object = qvariant_cast(model); QDeclarativeVisualModel *vim = 0; if (object && (vim = qobject_cast(object))) { if (d->ownModel) { - delete oldModel; + delete d->model; d->ownModel = false; } d->model = vim; @@ -1000,22 +1234,24 @@ void QDeclarativeGridView::setModel(const QVariant &newModel) if (!d->ownModel) { d->model = new QDeclarativeVisualDataModel(qmlContext(this), this); d->ownModel = true; - } else { - d->model = oldModel; } if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) - dataModel->setModel(newModel); + dataModel->setModel(model); } - if (d->model) { - d->modelCount = d->model->count(); d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore | QDeclarativeGridViewPrivate::BufferAfter; if (isComponentComplete()) { refill(); - if ((d->currentIndex >= d->modelCount || d->currentIndex < 0) && !d->currentIndexCleared) { + if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { setCurrentIndex(0); } else { - d->setCurrentIndex(d->currentIndex); + d->moveReason = QDeclarativeGridViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); + d->updateTrackedItem(); + } + d->moveReason = QDeclarativeGridViewPrivate::Other; } } connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); @@ -1078,7 +1314,12 @@ void QDeclarativeGridView::setDelegate(QDeclarativeComponent *delegate) d->currentItem = 0; refill(); d->moveReason = QDeclarativeGridViewPrivate::SetIndex; - d->setCurrentIndex(d->currentIndex); + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); + d->updateTrackedItem(); + } + d->moveReason = QDeclarativeGridViewPrivate::Other; } emit delegateChanged(); } @@ -1110,15 +1351,12 @@ void QDeclarativeGridView::setCurrentIndex(int index) Q_D(QDeclarativeGridView); if (d->requestedIndex >= 0) // currently creating item return; - d->currentIndexCleared = (index == -1); - if (index == d->currentIndex) return; - if (isComponentComplete() && d->isValid()) { d->moveReason = QDeclarativeGridViewPrivate::SetIndex; - d->setCurrentIndex(index); + d->updateCurrent(index); } else { d->currentIndex = index; emit currentIndexChanged(); @@ -1158,7 +1396,9 @@ QDeclarativeItem *QDeclarativeGridView::highlightItem() int QDeclarativeGridView::count() const { Q_D(const QDeclarativeGridView); - return d->modelCount; + if (d->model) + return d->model->count(); + return 0; } /*! @@ -1182,7 +1422,7 @@ void QDeclarativeGridView::setHighlight(QDeclarativeComponent *highlight) Q_D(QDeclarativeGridView); if (highlight != d->highlightComponent) { d->highlightComponent = highlight; - d->recreateHighlight(); + d->updateCurrent(d->currentIndex); emit highlightChanged(); } } @@ -1359,8 +1599,10 @@ void QDeclarativeGridView::setFlow(Flow flow) setContentHeight(-1); setFlickableDirection(QDeclarativeFlickable::HorizontalFlick); } - d->layout(); - d->setCurrentIndex(d->currentIndex); + d->clear(); + d->updateGrid(); + refill(); + d->updateCurrent(d->currentIndex); emit flowChanged(); } } @@ -1445,8 +1687,9 @@ void QDeclarativeGridView::setCellWidth(int cellWidth) Q_D(QDeclarativeGridView); if (cellWidth != d->cellWidth && cellWidth > 0) { d->cellWidth = qMax(1, cellWidth); - d->layout(); + d->updateGrid(); emit cellWidthChanged(); + d->layout(); } } @@ -1461,8 +1704,9 @@ void QDeclarativeGridView::setCellHeight(int cellHeight) Q_D(QDeclarativeGridView); if (cellHeight != d->cellHeight && cellHeight > 0) { d->cellHeight = qMax(1, cellHeight); - d->layout(); + d->updateGrid(); emit cellHeightChanged(); + d->layout(); } } /*! @@ -1520,7 +1764,8 @@ void QDeclarativeGridView::setFooter(QDeclarativeComponent *footer) d->footer = 0; } d->footerComponent = footer; - d->layout(); + d->updateFooter(); + d->updateGrid(); emit footerChanged(); } } @@ -1549,7 +1794,9 @@ void QDeclarativeGridView::setHeader(QDeclarativeComponent *header) d->header = 0; } d->headerComponent = header; - d->layout(); + d->updateHeader(); + d->updateFooter(); + d->updateGrid(); emit headerChanged(); } } @@ -1573,131 +1820,101 @@ void QDeclarativeGridView::setContentY(qreal pos) bool QDeclarativeGridView::event(QEvent *event) { Q_D(QDeclarativeGridView); - QScroller *scroller = QScroller::scroller(this); - - switch (event->type()) { - case QEvent::User: + if (event->type() == QEvent::User) { d->layout(); return true; - - case QEvent::ScrollPrepare: { - qreal snapOffset = 0; - bool forceSnapping = false; - // bool useEndPosition = false; - bool ignoreHeaders = false; - - // --- do the highlight range - if (d->haveHighlightRange) { - snapOffset = -d->highlightRangeStart; - ignoreHeaders = true; - } - - // just before scrolling set the snap points if needed - QList snapPoints; - // -- snap to every point (SnapToRow) - if (d->snapMode == QDeclarativeGridView::SnapToRow || forceSnapping) { - if (d->header && !ignoreHeaders) - snapPoints.append(0 + snapOffset); - foreach (FxGridItem *item, d->visibleItems) - snapPoints.append(item->rowPos() + snapOffset); - if (d->footer && !ignoreHeaders) - snapPoints.append(d->headerSize() + d->contentSize() + snapOffset); - - // -- snap to the next three point (SnapOneRow) - } else if (d->snapMode == QDeclarativeGridView::SnapOneRow) { - // here we just set three snap points around the current position. - - qreal rowPos = d->rowPosAt(d->currentIndex); - if (rowPos - d->rowSize() >= 0) - snapPoints.append( rowPos - d->rowSize()); - else if( d->header ) - snapPoints.append(0); // position of the header - - snapPoints.append(rowPos); - - if (rowPos + d->rowSize() < d->headerSize() + d->contentSize()) - snapPoints.append(rowPos + d->rowSize()); - else if( d->footer ) - snapPoints.append(d->headerSize() + d->contentSize() + snapOffset); // position of the footer - } - - if (d->flow == QDeclarativeGridView::LeftToRight) { - scroller->setSnapPositionsX(0.0, 0.0); - scroller->setSnapPositionsY(snapPoints); - } else { - scroller->setSnapPositionsX(snapPoints); - scroller->setSnapPositionsY(0.0, 0.0); - } - } - break; - - default: - break; } return QDeclarativeFlickable::event(event); } -void QDeclarativeGridView::scrollerStateChanged(QScroller::State state) +void QDeclarativeGridView::viewportMoved() { Q_D(QDeclarativeGridView); - QDeclarativeFlickable::scrollerStateChanged(state); + QDeclarativeFlickable::viewportMoved(); + if (!d->itemCount) + return; + d->lazyRelease = true; + if (d->flickingHorizontally || d->flickingVertically) { + if (yflick()) { + if (d->vData.velocity > 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; + else if (d->vData.velocity < 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; + } - if (state == QScroller::Inactive) { - d->bufferMode = QDeclarativeGridViewPrivate::NoBuffer; - if (d->highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { - d->updateHighlight(); // nudge the highlight in the right position if needed. + if (xflick()) { + if (d->hData.velocity > 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; + else if (d->hData.velocity < 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; } } -} - -qreal QDeclarativeGridView::minExtent() const -{ - Q_D(const QDeclarativeGridView); - - qreal extent = 0.0; - if (d->modelCount && - d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - extent -= d->highlightRangeStart; - // extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd)); + refill(); + if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically) + d->moveReason = QDeclarativeGridViewPrivate::Mouse; + if (d->moveReason != QDeclarativeGridViewPrivate::SetIndex) { + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { + // reposition highlight + qreal pos = d->highlight->rowPos(); + qreal viewPos = d->position(); + if (pos > viewPos + d->highlightRangeEnd - d->rowSize()) + pos = viewPos + d->highlightRangeEnd - d->rowSize(); + if (pos < viewPos + d->highlightRangeStart) + pos = viewPos + d->highlightRangeStart; + d->highlight->setPosition(d->highlight->colPos(), qRound(pos)); + + // update current index + int idx = d->snapIndex(); + if (idx >= 0 && idx != d->currentIndex) { + d->updateCurrent(idx); + if (d->currentItem && d->currentItem->colPos() != d->highlight->colPos() && d->autoHighlight) { + if (d->flow == LeftToRight) + d->highlightXAnimator->to = d->currentItem->item->x(); + else + d->highlightYAnimator->to = d->currentItem->item->y(); + } + } + } } - return extent; } -qreal QDeclarativeGridView::maxExtent() const +qreal QDeclarativeGridView::minYExtent() const { Q_D(const QDeclarativeGridView); - - qreal extent = d->contentSize(); - extent += d->headerSize(); - extent += d->footerSize(); - - if (d->visibleItems.count() && - d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - extent = qMin(extent + d->size() - d->highlightRangeEnd + 1, - // ensure that the last item fits fully behind hightlightRangeStart - d->rowPosAt(d->modelCount-1) + d->size() - d->highlightRangeStart); + if (d->flow == QDeclarativeGridView::TopToBottom) + return QDeclarativeFlickable::minYExtent(); + qreal extent = -d->startPosition(); + if (d->header && d->visibleItems.count()) + extent += d->header->item->height(); + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + extent += d->highlightRangeStart; + extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd)); } - return extent; } -qreal QDeclarativeGridView::minYExtent() const -{ - Q_D(const QDeclarativeGridView); - if (d->flow != QDeclarativeGridView::LeftToRight) - return QDeclarativeFlickable::minXExtent(); - else - return minExtent(); -} - qreal QDeclarativeGridView::maxYExtent() const { Q_D(const QDeclarativeGridView); - if (d->flow != QDeclarativeGridView::LeftToRight) - return QDeclarativeFlickable::maxXExtent(); - else - return maxExtent(); + if (d->flow == QDeclarativeGridView::TopToBottom) + return QDeclarativeFlickable::maxYExtent(); + qreal extent; + if (!d->model || !d->model->count()) { + extent = 0; + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + extent = -(d->rowPosAt(d->model->count()-1) - d->highlightRangeStart); + if (d->highlightRangeEnd != d->highlightRangeStart) + extent = qMin(extent, -(d->endPosition() - d->highlightRangeEnd + 1)); + } else { + extent = -(d->endPosition() - height()); + } + if (d->footer) + extent -= d->footer->item->height(); + const qreal minY = minYExtent(); + if (extent > minY) + extent = minY; + return extent; } qreal QDeclarativeGridView::minXExtent() const @@ -1705,8 +1922,14 @@ qreal QDeclarativeGridView::minXExtent() const Q_D(const QDeclarativeGridView); if (d->flow == QDeclarativeGridView::LeftToRight) return QDeclarativeFlickable::minXExtent(); - else - return minExtent(); + qreal extent = -d->startPosition(); + if (d->header && d->visibleItems.count()) + extent += d->header->item->width(); + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + extent += d->highlightRangeStart; + extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd)); + } + return extent; } qreal QDeclarativeGridView::maxXExtent() const @@ -1714,70 +1937,32 @@ qreal QDeclarativeGridView::maxXExtent() const Q_D(const QDeclarativeGridView); if (d->flow == QDeclarativeGridView::LeftToRight) return QDeclarativeFlickable::maxXExtent(); - else - return maxExtent(); -} - -void QDeclarativeGridView::viewportAboutToMove(QPointF newPos) -{ - Q_D(QDeclarativeGridView); - - // need to refill before moving - if (d->flow == LeftToRight) - d->refill(newPos.y(), newPos.y() + height() - 1); - else - d->refill(newPos.x(), newPos.x() + width() - 1); - - d->lazyRelease = true; - - if (isFlickingHorizontally()) { - if (horizontalVelocity() > 0) - d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; - else if (horizontalVelocity() < 0) - d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; - - } else if (isFlickingVertically()) { - if (verticalVelocity() > 0) - d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; - else if (verticalVelocity() < 0) - d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; - } - - if (d->isUserGenerated) - d->moveReason = QDeclarativeGridViewPrivate::Mouse; - - if (d->haveHighlightRange && d->highlight && d->highlightRange == StrictlyEnforceRange) { - - d->highlightXAnimator->stop(); - d->highlightYAnimator->stop(); - - // reposition highlight - qreal pos = d->highlight->rowPos(); - qreal viewPos = (d->flow == QDeclarativeGridView::LeftToRight) ? newPos.y() : newPos.x(); - if (pos > viewPos + d->highlightRangeEnd - d->rowSize()) - pos = viewPos + d->highlightRangeEnd - d->rowSize(); - if (pos < viewPos + d->highlightRangeStart) - pos = viewPos + d->highlightRangeStart; - d->highlight->setPosition(d->highlight->colPos(), pos); - - // update current index - if (d->moveReason != QDeclarativeGridViewPrivate::SetIndex) { - int idx = d->snapIndex(); - if (idx >= 0 && idx != d->currentIndex) { - d->setCurrentIndex(idx); - } - } + qreal extent; + if (!d->model || !d->model->count()) { + extent = 0; + } if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + extent = -(d->rowPosAt(d->model->count()-1) - d->highlightRangeStart); + if (d->highlightRangeEnd != d->highlightRangeStart) + extent = qMin(extent, -(d->endPosition() - d->highlightRangeEnd + 1)); + } else { + extent = -(d->endPosition() - width()); } + if (d->footer) + extent -= d->footer->item->width(); + const qreal minX = minXExtent(); + if (extent > minX) + extent = minX; + return extent; } void QDeclarativeGridView::keyPressEvent(QKeyEvent *event) { Q_D(QDeclarativeGridView); keyPressPreHandler(event); - if (event->isAccepted()) return; - if (d->model && d->modelCount && d->interactive) { + if (d->model && d->model->count() && d->interactive) { + d->moveReason = QDeclarativeGridViewPrivate::SetIndex; int oldCurrent = currentIndex(); switch (event->key()) { case Qt::Key_Up: @@ -1800,6 +1985,7 @@ void QDeclarativeGridView::keyPressEvent(QKeyEvent *event) return; } } + d->moveReason = QDeclarativeGridViewPrivate::Other; event->ignore(); QDeclarativeFlickable::keyPressEvent(event); } @@ -1816,7 +2002,7 @@ void QDeclarativeGridView::keyPressEvent(QKeyEvent *event) void QDeclarativeGridView::moveCurrentIndexUp() { Q_D(QDeclarativeGridView); - const int count = d->modelCount; + const int count = d->model ? d->model->count() : 0; if (!count) return; if (d->flow == QDeclarativeGridView::LeftToRight) { @@ -1844,7 +2030,7 @@ void QDeclarativeGridView::moveCurrentIndexUp() void QDeclarativeGridView::moveCurrentIndexDown() { Q_D(QDeclarativeGridView); - const int count = d->modelCount; + const int count = d->model ? d->model->count() : 0; if (!count) return; if (d->flow == QDeclarativeGridView::LeftToRight) { @@ -1872,7 +2058,7 @@ void QDeclarativeGridView::moveCurrentIndexDown() void QDeclarativeGridView::moveCurrentIndexLeft() { Q_D(QDeclarativeGridView); - const int count = d->modelCount; + const int count = d->model ? d->model->count() : 0; if (!count) return; if (d->flow == QDeclarativeGridView::LeftToRight) { @@ -1900,7 +2086,7 @@ void QDeclarativeGridView::moveCurrentIndexLeft() void QDeclarativeGridView::moveCurrentIndexRight() { Q_D(QDeclarativeGridView); - const int count = d->modelCount; + const int count = d->model ? d->model->count() : 0; if (!count) return; if (d->flow == QDeclarativeGridView::LeftToRight) { @@ -1951,7 +2137,7 @@ void QDeclarativeGridView::moveCurrentIndexRight() void QDeclarativeGridView::positionViewAtIndex(int index, int mode) { Q_D(QDeclarativeGridView); - if (!d->isValid() || index < 0 || index >= d->modelCount) + if (!d->isValid() || index < 0 || index >= d->model->count()) return; if (mode < Beginning || mode > Contain) return; @@ -1996,12 +2182,15 @@ void QDeclarativeGridView::positionViewAtIndex(int index, int mode) if (itemPos < pos) pos = itemPos; } - pos += d->headerSize(); - pos = qMin(pos, maxExtent() - d->size()); - pos = qMax(pos, minExtent()); + qreal maxExtent = d->flow == QDeclarativeGridView::LeftToRight ? -maxYExtent() : -maxXExtent(); + pos = qMin(pos, maxExtent); + qreal minExtent = d->flow == QDeclarativeGridView::LeftToRight ? -minYExtent() : -minXExtent(); + pos = qMax(pos, minExtent); d->moveReason = QDeclarativeGridViewPrivate::Other; + cancelFlick(); d->setPosition(pos); } + d->fixupPosition(); } /*! @@ -2032,43 +2221,95 @@ void QDeclarativeGridView::componentComplete() { Q_D(QDeclarativeGridView); QDeclarativeFlickable::componentComplete(); - d->layout(); + d->updateGrid(); if (d->isValid()) { refill(); d->moveReason = QDeclarativeGridViewPrivate::SetIndex; if (d->currentIndex < 0 && !d->currentIndexCleared) - d->setCurrentIndex(0); + d->updateCurrent(0); else - d->setCurrentIndex(d->currentIndex); + d->updateCurrent(d->currentIndex); if (d->highlight && d->currentItem) { d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); + d->updateTrackedItem(); } - d->updateHighlight(false); + d->moveReason = QDeclarativeGridViewPrivate::Other; + d->fixupPosition(); } } -void QDeclarativeGridView::itemsInserted(int modelIndex, int count) +void QDeclarativeGridView::trackedPositionChanged() { Q_D(QDeclarativeGridView); - - if (!isComponentComplete()) { - d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count + if (!d->trackedItem || !d->currentItem) return; + if (d->moveReason == QDeclarativeGridViewPrivate::SetIndex) { + const qreal trackedPos = d->trackedItem->rowPos(); + const qreal viewPos = d->position(); + qreal pos = viewPos; + if (d->haveHighlightRange) { + if (d->highlightRange == StrictlyEnforceRange) { + if (trackedPos > pos + d->highlightRangeEnd - d->rowSize()) + pos = trackedPos - d->highlightRangeEnd + d->rowSize(); + if (trackedPos < pos + d->highlightRangeStart) + pos = trackedPos - d->highlightRangeStart; + } else { + if (trackedPos < d->startPosition() + d->highlightRangeStart) { + pos = d->startPosition(); + } else if (d->trackedItem->endRowPos() > d->endPosition() - d->size() + d->highlightRangeEnd) { + pos = d->endPosition() - d->size() + 1; + if (pos < d->startPosition()) + pos = d->startPosition(); + } else { + if (trackedPos < viewPos + d->highlightRangeStart) { + pos = trackedPos - d->highlightRangeStart; + } else if (trackedPos > viewPos + d->highlightRangeEnd - d->rowSize()) { + pos = trackedPos - d->highlightRangeEnd + d->rowSize(); + } + } + } + } else { + if (trackedPos < viewPos && d->currentItem->rowPos() < viewPos) { + pos = d->currentItem->rowPos() < trackedPos ? trackedPos : d->currentItem->rowPos(); + } else if (d->trackedItem->endRowPos() >= viewPos + d->size() + && d->currentItem->endRowPos() >= viewPos + d->size()) { + if (d->trackedItem->endRowPos() <= d->currentItem->endRowPos()) { + pos = d->trackedItem->endRowPos() - d->size() + 1; + if (d->rowSize() > d->size()) + pos = trackedPos; + } else { + pos = d->currentItem->endRowPos() - d->size() + 1; + if (d->rowSize() > d->size()) + pos = d->currentItem->rowPos(); + } + } + } + if (viewPos != pos) { + cancelFlick(); + d->calcVelocity = true; + d->setPosition(pos); + d->calcVelocity = false; + } } +} - d->moveReason = QDeclarativeGridViewPrivate::Other; +void QDeclarativeGridView::itemsInserted(int modelIndex, int count) +{ + Q_D(QDeclarativeGridView); + if (!isComponentComplete()) + return; if (!d->visibleItems.count() || d->model->count() <= 1) { d->scheduleLayout(); - if (d->modelCount && d->currentIndex >= modelIndex) { + if (d->itemCount && d->currentIndex >= modelIndex) { // adjust current item index d->currentIndex += count; if (d->currentItem) d->currentItem->index = d->currentIndex; emit currentIndexChanged(); } else if (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared)) { - d->setCurrentIndex(0); + d->updateCurrent(0); } - d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count + d->itemCount += count; emit countChanged(); return; } @@ -2099,7 +2340,7 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) emit currentIndexChanged(); } d->scheduleLayout(); - d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count + d->itemCount += count; emit countChanged(); return; } @@ -2173,7 +2414,7 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) } } - if (d->modelCount && d->currentIndex >= modelIndex) { + if (d->itemCount && d->currentIndex >= modelIndex) { // adjust current item index d->currentIndex += count; if (d->currentItem) { @@ -2187,17 +2428,17 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) for (int j = 0; j < added.count(); ++j) added.at(j)->attached->emitAdd(); - d->modelCount += count; + d->itemCount += count; emit countChanged(); } void QDeclarativeGridView::itemsRemoved(int modelIndex, int count) { Q_D(QDeclarativeGridView); - d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count if (!isComponentComplete()) return; + d->itemCount -= count; bool currentRemoved = d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count; bool removedVisible = false; @@ -2245,8 +2486,8 @@ void QDeclarativeGridView::itemsRemoved(int modelIndex, int count) d->releaseItem(d->currentItem); d->currentItem = 0; d->currentIndex = -1; - if (d->modelCount) - d->setCurrentIndex(qMin(modelIndex, d->modelCount-1)); + if (d->itemCount) + d->updateCurrent(qMin(modelIndex, d->itemCount-1)); } // update visibleIndex @@ -2259,7 +2500,13 @@ void QDeclarativeGridView::itemsRemoved(int modelIndex, int count) } if (removedVisible && d->visibleItems.isEmpty()) { + d->timeline.clear(); + if (d->itemCount == 0) { d->setPosition(0); + d->updateHeader(); + d->updateFooter(); + update(); + } } emit countChanged(); @@ -2387,13 +2634,14 @@ void QDeclarativeGridView::modelReset() { Q_D(QDeclarativeGridView); d->clear(); - d->modelCount = d->model->count(); refill(); d->moveReason = QDeclarativeGridViewPrivate::SetIndex; - d->setCurrentIndex(d->currentIndex); + d->updateCurrent(d->currentIndex); if (d->highlight && d->currentItem) { d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); + d->updateTrackedItem(); } + d->moveReason = QDeclarativeGridViewPrivate::Other; emit countChanged(); } @@ -2418,6 +2666,14 @@ void QDeclarativeGridView::destroyingItem(QDeclarativeItem *item) d->unrequestedItems.remove(item); } +void QDeclarativeGridView::animStopped() +{ + Q_D(QDeclarativeGridView); + d->bufferMode = QDeclarativeGridViewPrivate::NoBuffer; + if (d->haveHighlightRange && d->highlightRange == QDeclarativeGridView::StrictlyEnforceRange) + d->updateHighlight(); +} + void QDeclarativeGridView::refill() { Q_D(QDeclarativeGridView); diff --git a/src/declarative/graphicsitems/qdeclarativegridview_p.h b/src/declarative/graphicsitems/qdeclarativegridview_p.h index 22407f4..ee632b1 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview_p.h +++ b/src/declarative/graphicsitems/qdeclarativegridview_p.h @@ -188,22 +188,18 @@ Q_SIGNALS: void headerChanged(); void footerChanged(); -protected Q_SLOTS: - void scrollerStateChanged(QScroller::State state); - protected: virtual bool event(QEvent *event); - virtual qreal minExtent() const; - virtual qreal maxExtent() const; + virtual void viewportMoved(); virtual qreal minYExtent() const; virtual qreal maxYExtent() const; virtual qreal minXExtent() const; virtual qreal maxXExtent() const; - virtual void viewportAboutToMove(QPointF newPos); virtual void keyPressEvent(QKeyEvent *); virtual void componentComplete(); private Q_SLOTS: + void trackedPositionChanged(); void itemsInserted(int index, int count); void itemsRemoved(int index, int count); void itemsMoved(int from, int to, int count); @@ -211,6 +207,7 @@ private Q_SLOTS: void destroyRemoved(); void createdItem(int index, QDeclarativeItem *item); void destroyingItem(QDeclarativeItem *item); + void animStopped(); private: void refill(); diff --git a/src/declarative/graphicsitems/qdeclarativelistview.cpp b/src/declarative/graphicsitems/qdeclarativelistview.cpp index 27f7b52..450b6af 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview.cpp +++ b/src/declarative/graphicsitems/qdeclarativelistview.cpp @@ -56,11 +56,6 @@ QT_BEGIN_NAMESPACE -template static const T &forceConst(const T &t) -{ - return t; -} - void QDeclarativeViewSection::setProperty(const QString &property) { if (property != m_property) { @@ -98,62 +93,40 @@ QString QDeclarativeViewSection::sectionString(const QString &value) class FxListItem { public: - FxListItem(QDeclarativeItem *i, QDeclarativeListView *v) - : item(i), section(0), view(v) - { + FxListItem(QDeclarativeItem *i, QDeclarativeListView *v) : item(i), section(0), view(v) { attached = static_cast(qmlAttachedPropertiesObject(item)); if (attached) attached->setView(view); } - ~FxListItem() {} - - /*! /internal - The position of the item plus section header. - */ qreal position() const { if (section) return (view->orientation() == QDeclarativeListView::Vertical ? section->y() : section->x()); else return (view->orientation() == QDeclarativeListView::Vertical ? item->y() : item->x()); } - - /*! /internal - The position of the item itself - */ qreal itemPosition() const { return (view->orientation() == QDeclarativeListView::Vertical ? item->y() : item->x()); } - - /*! /internal - The size of the item plus section header. - */ qreal size() const { if (section) return (view->orientation() == QDeclarativeListView::Vertical ? item->height()+section->height() : item->width()+section->width()); else return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width()); } - - /*! /internal - The size of the item itself - */ qreal itemSize() const { return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width()); } - qreal sectionSize() const { if (section) return (view->orientation() == QDeclarativeListView::Vertical ? section->height() : section->width()); return 0.0; } - qreal endPosition() const { return (view->orientation() == QDeclarativeListView::Vertical ? item->y() + (item->height() >= 1.0 ? item->height() : 1) : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1; } - void setPosition(qreal pos) { if (view->orientation() == QDeclarativeListView::Vertical) { if (section) { @@ -169,14 +142,12 @@ public: item->setX(pos); } } - void setSize(qreal size) { if (view->orientation() == QDeclarativeListView::Vertical) item->setHeight(size); else item->setWidth(size); } - bool contains(int x, int y) const { return (x >= item->x() && x < item->x() + item->width() && y >= item->y() && y < item->y() + item->height()); @@ -200,22 +171,19 @@ public: : currentItem(0), orient(QDeclarativeListView::Vertical) , visiblePos(0), visibleIndex(0) , averageSize(100.0), currentIndex(-1), requestedIndex(-1) - , modelCount(0) - , highlightRangeStart(0), highlightRangeEnd(0) - , highlightComponent(0), highlight(0) + , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0) + , highlightComponent(0), highlight(0), trackedItem(0) , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0) , sectionCriteria(0), spacing(0.0) , highlightMoveSpeed(400), highlightMoveDuration(-1) , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QDeclarativeListView::NoHighlightRange) - , snapMode(QDeclarativeListView::NoSnap) - , footerComponent(0), footer(0) - , headerComponent(0), header(0) + , snapMode(QDeclarativeListView::NoSnap), overshootDist(0.0) + , footerComponent(0), footer(0), headerComponent(0), header(0) , bufferMode(BufferBefore | BufferAfter) , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false) - , lazyRelease(false) - , deferredRelease(false) - , layoutScheduled(false) - , currentIndexCleared(false) + , correctFlick(false), inFlickCorrection(false), lazyRelease(false) + , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false) + , inViewportMoved(false) , minExtentDirty(true), maxExtentDirty(true) {} @@ -260,25 +228,10 @@ public: return 0; } - /*! \internal - Returns the last visible model index. delayRemove items are not counted (as they don't have an index) - */ - int lastVisibleIndex() const - { - // find the last valid model index - for (int i = visibleItems.count() - 1; i >= 0; --i) { - FxListItem *item = visibleItems.at(i); - if (item->index != -1) - return item->index; - } - return visibleIndex; - } - qreal position() const { Q_Q(const QDeclarativeListView); return orient == QDeclarativeListView::Vertical ? q->contentY() : q->contentX(); } - void setPosition(qreal pos) { Q_Q(QDeclarativeListView); if (orient == QDeclarativeListView::Vertical) @@ -286,53 +239,33 @@ public: else q->QDeclarativeFlickable::setContentX(pos); } - - void scrollToPosition(qreal pos) { - Q_Q(QDeclarativeListView); - if (orient == QDeclarativeListView::Vertical) - QScroller::scroller(q)->scrollTo(QPointF(q->contentX(), pos)); - else - QScroller::scroller(q)->scrollTo(QPointF(pos, q->contentY())); - } - qreal size() const { Q_Q(const QDeclarativeListView); return orient == QDeclarativeListView::Vertical ? q->height() : q->width(); } - /*! /internal - Estimates the current position of the first visible item counting the header (the first item starts at position header->size()). - */ qreal startPosition() const { qreal pos = 0; if (!visibleItems.isEmpty()) { - pos = visibleItems.first()->position(); - pos -= visibleIndex * (averageSize + spacing); + pos = (*visibleItems.constBegin())->position(); + if (visibleIndex > 0) + pos -= visibleIndex * (averageSize + spacing); } - if( header ) - pos -= header->size(); return pos; } - /*! /internal - Estimates the full length of the list including header and footer. - */ qreal endPosition() const { - qreal pos = -1; + qreal pos = 0; if (!visibleItems.isEmpty()) { int invisibleCount = visibleItems.count() - visibleIndex; - for (int i = visibleItems.count()-1; i >= 0; --i) { if (visibleItems.at(i)->index != -1) { - invisibleCount = modelCount - visibleItems.at(i)->index - 1; - pos = visibleItems.at(i)->endPosition(); + invisibleCount = model->count() - visibleItems.at(i)->index - 1; break; } } - pos = visibleItems.last()->endPosition() + invisibleCount * (averageSize + spacing); + pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing); } - if (footer) - pos += footer->size(); return pos; } @@ -347,10 +280,17 @@ public: cs = currentItem->size() + spacing; --count; } - return forceConst(visibleItems).first()->position() - count * (averageSize + spacing) - cs; + return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs; } else { - int count = modelIndex - lastVisibleIndex() - 1; - return forceConst(visibleItems).last()->endPosition() + spacing + count * (averageSize + spacing) + 1; + int idx = visibleItems.count() - 1; + while (idx >= 0 && visibleItems.at(idx)->index == -1) + --idx; + if (idx < 0) + idx = visibleIndex; + else + idx = visibleItems.at(idx)->index; + int count = modelIndex - idx - 1; + return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1; } } return 0; @@ -362,10 +302,17 @@ public: if (!visibleItems.isEmpty()) { if (modelIndex < visibleIndex) { int count = visibleIndex - modelIndex; - return forceConst(visibleItems).first()->position() - (count - 1) * (averageSize + spacing) - spacing - 1; + return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1; } else { - int count = modelIndex - lastVisibleIndex() - 1; - return forceConst(visibleItems).last()->endPosition() + count * (averageSize + spacing); + int idx = visibleItems.count() - 1; + while (idx >= 0 && visibleItems.at(idx)->index == -1) + --idx; + if (idx < 0) + idx = visibleIndex; + else + idx = visibleItems.at(idx)->index; + int count = modelIndex - idx - 1; + return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing); } } return 0; @@ -385,32 +332,52 @@ public: } bool isValid() const { - return model && modelCount && model->isValid(); + return model && model->count() && model->isValid(); } - /** \internal - Returns the index of the item which is at or around the highlight. - */ - int snapIndex() { - int index = currentIndex; + qreal snapPosAt(qreal pos) { + if (FxListItem *snapItem = snapItemAt(pos)) + return snapItem->position(); + if (visibleItems.count()) { + qreal firstPos = visibleItems.first()->position(); + qreal endPos = visibleItems.last()->position(); + if (pos < firstPos) { + return firstPos - qRound((firstPos - pos) / averageSize) * averageSize; + } else if (pos > endPos) + return endPos + qRound((pos - endPos) / averageSize) * averageSize; + } + return qRound((pos - startPosition()) / averageSize) * averageSize + startPosition(); + } - // qDebug() << "snapIndex" << index << "hp:" << highlight->position() << highlight->size() << "items:" << visibleItems.count(); + FxListItem *snapItemAt(qreal pos) { + FxListItem *snapItem = 0; for (int i = 0; i < visibleItems.count(); ++i) { FxListItem *item = visibleItems[i]; if (item->index == -1) continue; - index = item->index; qreal itemTop = item->position(); - // qDebug() << " "<size(); - if (itemTop + item->size() / 2 > highlight->position()) - return item->index; + if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1) + return item; + if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos) + snapItem = item; + } + return snapItem; + } + + int lastVisibleIndex() const { + int lastIndex = -1; + for (int i = visibleItems.count()-1; i >= 0; --i) { + FxListItem *listItem = visibleItems.at(i); + if (listItem->index != -1) { + lastIndex = listItem->index; + break; + } } - return index; + return lastIndex; } // map a model index to visibleItems index. - int mapFromModel(int modelIndex) const - { + int mapFromModel(int modelIndex) const { if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) return -1; for (int i = 0; i < visibleItems.count(); ++i) { @@ -423,28 +390,22 @@ public: return -1; // Not in visibleList } - void updateViewport() - { + void updateViewport() { Q_Q(QDeclarativeListView); - if (orient == QDeclarativeListView::Vertical) - q->setContentHeight(q->maxExtent() - q->minExtent() + 1); - else - q->setContentWidth(q->maxExtent() - q->minExtent() + 1); + if (orient == QDeclarativeListView::Vertical) { + q->setContentHeight(endPosition() - startPosition() + 1); + } else { + q->setContentWidth(endPosition() - startPosition() + 1); + } } - void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) - { - - // qDebug() << "itemGeometryChanged" << newGeometry; - - // Q_Q(QDeclarativeListView); + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { + Q_Q(QDeclarativeListView); QDeclarativeFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); if (item != contentItem && (!highlight || item != highlight->item)) { if ((orient == QDeclarativeListView::Vertical && newGeometry.height() != oldGeometry.height()) || (orient == QDeclarativeListView::Horizontal && newGeometry.width() != oldGeometry.width())) { scheduleLayout(); - minExtentDirty = true; - maxExtentDirty = true; } } if ((header && header->item == item) || (footer && footer->item == item)) { @@ -453,6 +414,8 @@ public: } if (currentItem && currentItem->item == item) updateHighlight(); + if (trackedItem && trackedItem->item == item) + q->trackedPositionChanged(); } // for debugging only @@ -473,60 +436,40 @@ public: void layout(); void updateUnrequestedIndexes(); void updateUnrequestedPositions(); - void recreateHighlight(); - void updateHighlight(bool smooth = true); + void updateTrackedItem(); + void createHighlight(); + void updateHighlight(); void createSection(FxListItem *); void updateSections(); void updateCurrentSection(); - void setCurrentIndex(int); + void updateCurrent(int); void updateAverage(); void updateHeader(); void updateFooter(); + void fixupPosition(); + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); + virtual void flick(QDeclarativeFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); QDeclarativeGuard model; QVariant modelVariant; QList visibleItems; - - /* - These are items that were not requested but send by createdItem from the model. - Usually these items were created because another view showing the same model - needed them. - */ QHash unrequestedItems; - FxListItem *currentItem; QDeclarativeListView::Orientation orient; - - /* - This is the position of the first visible item. - It is stored in case when all items are deleted and we need to create new - ones. - */ qreal visiblePos; - - /* - The visibleIndex is the modelIndex of the first item in visibleItems with an index != -1 - */ int visibleIndex; qreal averageSize; - int currentIndex; // the model index of the currentItem - - /* - The model index of the item that is currently created via createItem - */ + int currentIndex; int requestedIndex; - - /* - This is the number of items in the model. - */ - int modelCount; - + int itemCount; qreal highlightRangeStart; qreal highlightRangeEnd; QDeclarativeComponent *highlightComponent; FxListItem *highlight; + FxListItem *trackedItem; enum MovementReason { Other, SetIndex, Mouse }; - MovementReason moveReason; // the moveReason determines if the highlight needs to be centered or if the currentItem needs to be changed. + MovementReason moveReason; int buffer; QSmoothedAnimation *highlightPosAnimator; QSmoothedAnimation *highlightSizeAnimator; @@ -541,6 +484,7 @@ public: int highlightResizeDuration; QDeclarativeListView::HighlightRangeMode highlightRange; QDeclarativeListView::SnapMode snapMode; + qreal overshootDist; QDeclarativeComponent *footerComponent; FxListItem *footer; QDeclarativeComponent *headerComponent; @@ -554,10 +498,13 @@ public: bool wrap : 1; bool autoHighlight : 1; bool haveHighlightRange : 1; + bool correctFlick : 1; + bool inFlickCorrection : 1; bool lazyRelease : 1; bool deferredRelease : 1; bool layoutScheduled : 1; bool currentIndexCleared : 1; + bool inViewportMoved : 1; mutable bool minExtentDirty : 1; mutable bool maxExtentDirty : 1; }; @@ -567,12 +514,14 @@ void QDeclarativeListViewPrivate::init() Q_Q(QDeclarativeListView); q->setFlag(QGraphicsItem::ItemIsFocusScope); addItemChangeListener(this, Geometry); + QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick); ::memset(sectionCache, 0, sizeof(QDeclarativeItem*) * sectionCacheSize); } void QDeclarativeListViewPrivate::clear() { + timeline.clear(); for (int i = 0; i < visibleItems.count(); ++i) releaseItem(visibleItems.at(i)); visibleItems.clear(); @@ -584,20 +533,13 @@ void QDeclarativeListViewPrivate::clear() visibleIndex = 0; releaseItem(currentItem); currentItem = 0; - recreateHighlight(); + createHighlight(); + trackedItem = 0; minExtentDirty = true; maxExtentDirty = true; - modelCount = 0; - - setPosition(0); - updateHeader(); - updateFooter(); + itemCount = 0; } -/** \internal - Tries to create the FxListItem for the given model index. - \returns the item or 0 if the item could not be created. -*/ FxListItem *QDeclarativeListViewPrivate::createItem(int modelIndex) { Q_Q(QDeclarativeListView); @@ -617,16 +559,13 @@ FxListItem *QDeclarativeListViewPrivate::createItem(int modelIndex) else listItem->attached->m_prevSection = sectionAt(modelIndex-1); } - if (modelIndex < modelCount - 1) { + if (modelIndex < model->count()-1) { if (FxListItem *item = visibleItem(modelIndex+1)) listItem->attached->m_nextSection = item->attached->section(); else listItem->attached->m_nextSection = sectionAt(modelIndex+1); } } - - // qDebug() << "createItem"<completePending()) { // complete listItem->item->setZValue(1); @@ -653,10 +592,9 @@ void QDeclarativeListViewPrivate::releaseItem(FxListItem *item) Q_Q(QDeclarativeListView); if (!item || !model) return; + if (trackedItem == item) + trackedItem = 0; QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item->item)); - - // qDebug() << "releaseItem"<<(currentItem==item?"current":(highlight==item?"highlight":"normal"))<index<<"at"<< item->position(); - itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry); if (model->release(item->item) == 0) { // item was not destroyed, and we no longer reference it. @@ -678,20 +616,12 @@ void QDeclarativeListViewPrivate::releaseItem(FxListItem *item) delete item; } -/** \internal - Updates the visible items so that they cover the from and to range. - updateViewport should be called afterwards to handle cases where - the viewport size is changed by different sized items. -*/ void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer) { Q_Q(QDeclarativeListView); if (!isValid() || !q->isComponentComplete()) return; - - if (!doBuffer && buffer && bufferMode != NoBuffer) - doBuffer = true; - + itemCount = model->count(); qreal bufferFrom = from - buffer; qreal bufferTo = to + buffer; qreal fillFrom = from; @@ -701,55 +631,34 @@ void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer) if (doBuffer && (bufferMode & BufferBefore)) fillFrom = bufferFrom; - bool changed = false; - - // -- layout the items + int modelIndex = visibleIndex; + qreal itemEnd = visiblePos-1; if (!visibleItems.isEmpty()) { - qreal oldEnd = forceConst(visibleItems).last()->endPosition(); - qreal pos = forceConst(visibleItems).first()->position() + forceConst(visibleItems).first()->size() + spacing; - for (int i=1; i < visibleItems.count(); ++i) { - FxListItem *item = visibleItems.at(i); - if (item->position() != pos) { - // qDebug() << "Moved item"<position()<<"to"<setPosition(pos); - // changed = true; - } - pos += item->size() + spacing; - } - // move current item if it is after the visible items. - if (currentItem && currentIndex > lastVisibleIndex()) - currentItem->setPosition(currentItem->position() + (forceConst(visibleItems).last()->endPosition() - oldEnd)); - } - - qreal endPos = visiblePos; - int modelIndex = lastVisibleIndex(); - if (!visibleItems.isEmpty()) { - endPos = forceConst(visibleItems).last()->endPosition() + spacing + 1; - modelIndex++; + visiblePos = (*visibleItems.constBegin())->position(); + itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing; + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + modelIndex = visibleItems.at(i)->index + 1; } - // qDebug() << "REFILL: from" << from << "to" << to << "doBuffer" << doBuffer << "visiblePos" << visiblePos << "endPos" << endPos << "lvindex" << lastVisibleIndex() << "mindex" << modelIndex; - - + bool changed = false; FxListItem *item = 0; - - // -- add items to the back - while (modelIndex < modelCount && endPos <= fillTo) { - // qDebug() << "refill: append item" << modelIndex << "endPos" << endPos; + qreal pos = itemEnd + 1; + while (modelIndex < model->count() && pos <= fillTo) { +// qDebug() << "refill: append item" << modelIndex << "pos" << pos; if (!(item = createItem(modelIndex))) break; - item->setPosition(endPos); - endPos += item->size() + spacing; + item->setPosition(pos); + pos += item->size() + spacing; visibleItems.append(item); ++modelIndex; changed = true; if (doBuffer) // never buffer more than one item per frame break; } - - // -- add items to the front - while (visibleIndex > 0 && visibleIndex <= modelCount && visiblePos-1 >= fillFrom) { - // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos; + while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) { +// qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos; if (!(item = createItem(visibleIndex-1))) break; --visibleIndex; @@ -761,56 +670,35 @@ void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer) break; } - // TODO: why do we need deferredRelease? Only release every second frame? if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create - if (visibleItems.count() > 1) { - // only delete items if we have more than could be visible - if (forceConst(visibleItems).last()->endPosition() - forceConst(visibleItems).first()->position() > size() ) { - while (!visibleItems.isEmpty() && - (item = forceConst(visibleItems).first()) && item->endPosition() < bufferFrom) { - if (item->attached->delayRemove()) - break; - // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition(); - if (item->index != -1) - { - visibleIndex++; - // visiblePos += item->size() + spacing; - } - visibleItems.removeFirst(); - releaseItem(item); - changed = true; - } - while (!visibleItems.isEmpty() && - (item = forceConst(visibleItems).last()) && item->position() > bufferTo) { - if (item->attached->delayRemove()) - break; - // qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position(); - if (item->index != -1) - { - // endPos -= item->size() + spacing; - } - visibleItems.removeLast(); - releaseItem(item); - changed = true; - } - } + while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) { + if (item->attached->delayRemove()) + break; +// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition(); + if (item->index != -1) + visibleIndex++; + visibleItems.removeFirst(); + releaseItem(item); + changed = true; + } + while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) { + if (item->attached->delayRemove()) + break; +// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position(); + visibleItems.removeLast(); + releaseItem(item); + changed = true; } deferredRelease = false; } else { deferredRelease = true; } - - - if (changed) { - // qDebug()<<"REFILLED: from" << from << "to" << to << "fillFrom" << fillFrom << "fillTo" << fillTo << "endPos:" << endPos << "buffer" << buffer; minExtentDirty = true; maxExtentDirty = true; if (visibleItems.count()) - visiblePos = forceConst(visibleItems).first()->position(); + visiblePos = (*visibleItems.constBegin())->position(); updateAverage(); - - // update the highlight position in cases where we are estimating it if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) { currentItem->setPosition(positionAt(currentIndex)); updateHighlight(); @@ -818,15 +706,16 @@ void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer) if (sectionCriteria) updateCurrentSection(); - updateUnrequestedPositions(); - updateScrollerValues(); + if (header) + updateHeader(); + if (footer) + updateFooter(); updateViewport(); + updateUnrequestedPositions(); + } else if (!doBuffer && buffer && bufferMode != NoBuffer) { + refill(from, to, true); } lazyRelease = false; - if (header) - updateHeader(); - if (footer) - updateFooter(); } void QDeclarativeListViewPrivate::scheduleLayout() @@ -847,11 +736,31 @@ void QDeclarativeListViewPrivate::layout() setPosition(0); return; } + if (!visibleItems.isEmpty()) { + qreal oldEnd = visibleItems.last()->endPosition(); + qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing; + for (int i=1; i < visibleItems.count(); ++i) { + FxListItem *item = visibleItems.at(i); + item->setPosition(pos); + pos += item->size() + spacing; + } + // move current item if it is after the visible items. + if (currentItem && currentIndex > lastVisibleIndex()) + currentItem->setPosition(currentItem->position() + (visibleItems.last()->endPosition() - oldEnd)); + } q->refill(); - // qDebug() << "highlight1" << currentItem << highlight; + minExtentDirty = true; + maxExtentDirty = true; updateHighlight(); - // qDebug() << "highlight2" << currentItem << highlight; - q->refill(); + if (!q->isMoving() && !q->isFlicking()) { + fixupPosition(); + q->refill(); + } + if (header) + updateHeader(); + if (footer) + updateFooter(); + updateViewport(); } void QDeclarativeListViewPrivate::updateUnrequestedIndexes() @@ -881,13 +790,24 @@ void QDeclarativeListViewPrivate::updateUnrequestedPositions() } } -void QDeclarativeListViewPrivate::recreateHighlight() +void QDeclarativeListViewPrivate::updateTrackedItem() { Q_Q(QDeclarativeListView); + FxListItem *item = currentItem; + if (highlight) + item = highlight; + trackedItem = item; + if (trackedItem) + q->trackedPositionChanged(); +} - // qDebug() << "recreate Highlight"; +void QDeclarativeListViewPrivate::createHighlight() +{ + Q_Q(QDeclarativeListView); bool changed = false; if (highlight) { + if (trackedItem == highlight) + trackedItem = 0; delete highlight->item; delete highlight; highlight = 0; @@ -899,7 +819,6 @@ void QDeclarativeListViewPrivate::recreateHighlight() } if (currentItem) { - // qDebug()<<"new highlight"; QDeclarativeItem *item = 0; if (highlightComponent) { QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q)); @@ -946,76 +865,29 @@ void QDeclarativeListViewPrivate::recreateHighlight() changed = true; } } - if (changed) { - updateHighlight(); + if (changed) emit q->highlightItemChanged(); - } } -/* - This function will recreate or delete the highlight (depening on the state) and - then try to move it to a correct position. - It will not update the current item (that is done in viewportMoved() - If \c smooth is set to true then all updates will be using an animation. -*/ -void QDeclarativeListViewPrivate::updateHighlight(bool smooth) +void QDeclarativeListViewPrivate::updateHighlight() { - if ((!currentItem && highlight) || (currentItem && !highlight)) - recreateHighlight(); - - // --- move the current item between the highlight range - if (moveReason == QDeclarativeListViewPrivate::SetIndex) { - // ensure that the tracked item is inside the highlight range - - // reposition view - if (currentItem) { - qreal pos = currentItem->position(); - qreal viewPos = position(); - - if (autoHighlight && haveHighlightRange) { - if (pos > viewPos + highlightRangeEnd - currentItem->size()) - viewPos = pos - highlightRangeEnd + currentItem->size(); - if (pos < viewPos + highlightRangeStart) - viewPos = pos - highlightRangeStart; - - } else { - if (pos > viewPos + size() - currentItem->size()) - viewPos = pos - size() + currentItem->size(); - if (pos < viewPos + 0) - viewPos = pos - 0; - } - if (smooth) - scrollToPosition(viewPos); - else - { - setPosition(viewPos); - // qDebug() << "setPos to "<to = currentItem->itemPosition(); - highlightSizeAnimator->to = currentItem->itemSize(); - if (orient == QDeclarativeListView::Vertical) { - if (highlight->item->width() == 0) - highlight->item->setWidth(currentItem->item->width()); - } else { - if (highlight->item->height() == 0) - highlight->item->setHeight(currentItem->item->height()); - } - if (smooth) { - highlightPosAnimator->restart(); - highlightSizeAnimator->restart(); - } else { - highlightPosAnimator->stop(); - highlightSizeAnimator->stop(); - highlight->setPosition(highlightPosAnimator->to); - highlight->setSize(highlightSizeAnimator->to); - } + createHighlight(); + if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) { + // auto-update highlight + highlightPosAnimator->to = currentItem->itemPosition(); + highlightSizeAnimator->to = currentItem->itemSize(); + if (orient == QDeclarativeListView::Vertical) { + if (highlight->item->width() == 0) + highlight->item->setWidth(currentItem->item->width()); + } else { + if (highlight->item->height() == 0) + highlight->item->setHeight(currentItem->item->height()); } + highlightPosAnimator->restart(); + highlightSizeAnimator->restart(); } + updateTrackedItem(); } void QDeclarativeListViewPrivate::createSection(FxListItem *listItem) @@ -1096,17 +968,14 @@ void QDeclarativeListViewPrivate::updateSections() } } if (prevAtt) { - if (idx > 0 && idx < modelCount - 1) - prevAtt->setNextSection(sectionAt(idx + 1)); + if (idx > 0 && idx < model->count()-1) + prevAtt->setNextSection(sectionAt(idx+1)); else prevAtt->setNextSection(QString()); } } } -/** \internal - Updates the currentSection variable and emits the changed signals. -*/ void QDeclarativeListViewPrivate::updateCurrentSection() { Q_Q(QDeclarativeListView); @@ -1117,7 +986,6 @@ void QDeclarativeListViewPrivate::updateCurrentSection() } return; } - int index = 0; while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position()) ++index; @@ -1126,56 +994,61 @@ void QDeclarativeListViewPrivate::updateCurrentSection() if (index < visibleItems.count()) newSection = visibleItems.at(index)->attached->section(); else - newSection = forceConst(visibleItems).first()->attached->section(); + newSection = visibleItems.first()->attached->section(); if (newSection != currentSection) { currentSection = newSection; emit q->currentSectionChanged(); } } -void QDeclarativeListViewPrivate::setCurrentIndex(int newIndex) +void QDeclarativeListViewPrivate::updateCurrent(int modelIndex) { Q_Q(QDeclarativeListView); - - // --- release the old item - if (currentItem && currentIndex != newIndex) { - currentItem->attached->setIsCurrentItem(false); - releaseItem(currentItem); - currentItem = 0; - } - - int oldIndex = currentIndex; - currentIndex = newIndex; - - bool visible = (q->isComponentComplete() && isValid() && !currentItem && - newIndex >= 0 && newIndex < modelCount); - - // --- set the new item - if (visible) { - currentItem = createItem(currentIndex); + if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { if (currentItem) { - if (currentIndex == visibleIndex - 1 && visibleItems.count()) { - // We can calculate exact postion in this case - currentItem->setPosition(forceConst(visibleItems).first()->position() - currentItem->size() - spacing); - } else { - // Create current item now and position as best we can. - // Its position will be corrected when it becomes visible. - currentItem->setPosition(positionAt(currentIndex)); - } - currentItem->item->setFocus(true); - currentItem->attached->setIsCurrentItem(true); - // Avoid showing section delegate twice. We still need the section heading so that - // currentItem positioning works correctly. - // This is slightly sub-optimal, but section heading caching minimizes the impact. - if (currentItem->section) - currentItem->section->setVisible(false); + currentItem->attached->setIsCurrentItem(false); + releaseItem(currentItem); + currentItem = 0; + currentIndex = modelIndex; + emit q->currentIndexChanged(); + updateHighlight(); + } else if (currentIndex != modelIndex) { + currentIndex = modelIndex; + emit q->currentIndexChanged(); } + return; } - updateHighlight(visible); - - if (currentIndex != oldIndex) - emit q->currentIndexChanged(); + if (currentItem && currentIndex == modelIndex) { + updateHighlight(); + return; + } + FxListItem *oldCurrentItem = currentItem; + currentIndex = modelIndex; + currentItem = createItem(modelIndex); + if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) + oldCurrentItem->attached->setIsCurrentItem(false); + if (currentItem) { + if (modelIndex == visibleIndex - 1 && visibleItems.count()) { + // We can calculate exact postion in this case + currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing); + } else { + // Create current item now and position as best we can. + // Its position will be corrected when it becomes visible. + currentItem->setPosition(positionAt(modelIndex)); + } + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + // Avoid showing section delegate twice. We still need the section heading so that + // currentItem positioning works correctly. + // This is slightly sub-optimal, but section heading caching minimizes the impact. + if (currentItem->section) + currentItem->section->setVisible(false); + } + updateHighlight(); + emit q->currentIndexChanged(); + // Release the old current item + releaseItem(oldCurrentItem); } void QDeclarativeListViewPrivate::updateAverage() @@ -1188,13 +1061,13 @@ void QDeclarativeListViewPrivate::updateAverage() averageSize = qRound(sum / visibleItems.count()); } -void QDeclarativeListViewPrivate::updateHeader() +void QDeclarativeListViewPrivate::updateFooter() { Q_Q(QDeclarativeListView); - if (!header && headerComponent) { + if (!footer && footerComponent) { QDeclarativeItem *item = 0; QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); - QObject *nobj = headerComponent->create(context); + QObject *nobj = footerComponent->create(context); if (nobj) { QDeclarative_setParent_noEvent(context, nobj); item = qobject_cast(nobj); @@ -1206,26 +1079,35 @@ void QDeclarativeListViewPrivate::updateHeader() if (item) { QDeclarative_setParent_noEvent(item, q->contentItem()); item->setParentItem(q->contentItem()); - item->setZValue(100); + item->setZValue(1); QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); - header = new FxListItem(item, q); - if (!visibleItems.isEmpty()) - visiblePos = header->size(); + footer = new FxListItem(item, q); } } - if (header) { - header->setPosition(startPosition()); + if (footer) { + if (visibleItems.count()) { + qreal endPos = endPosition() + 1; + if (lastVisibleIndex() == model->count()-1) { + footer->setPosition(endPos); + } else { + qreal visiblePos = position() + q->height(); + if (endPos <= visiblePos || footer->position() < endPos) + footer->setPosition(endPos); + } + } else { + footer->setPosition(visiblePos); + } } } -void QDeclarativeListViewPrivate::updateFooter() +void QDeclarativeListViewPrivate::updateHeader() { Q_Q(QDeclarativeListView); - if (!footer && footerComponent) { + if (!header && headerComponent) { QDeclarativeItem *item = 0; QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); - QObject *nobj = footerComponent->create(context); + QObject *nobj = headerComponent->create(context); if (nobj) { QDeclarative_setParent_noEvent(context, nobj); item = qobject_cast(nobj); @@ -1240,11 +1122,233 @@ void QDeclarativeListViewPrivate::updateFooter() item->setZValue(1); QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); - footer = new FxListItem(item, q); + header = new FxListItem(item, q); + if (visibleItems.isEmpty()) + visiblePos = header->size(); + } + } + if (header) { + if (visibleItems.count()) { + qreal startPos = startPosition(); + if (visibleIndex == 0) { + header->setPosition(startPos - header->size()); + } else { + if (position() <= startPos || header->position() > startPos - header->size()) + header->setPosition(startPos - header->size()); + } + } else { + header->setPosition(0); } } - if (footer) - footer->setPosition(endPosition() + 1 - footer->size()); +} + +void QDeclarativeListViewPrivate::fixupPosition() +{ + if ((haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange) + || snapMode != QDeclarativeListView::NoSnap) + moveReason = Other; + if (orient == QDeclarativeListView::Vertical) + fixupY(); + else + fixupX(); +} + +void QDeclarativeListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if ((orient == QDeclarativeListView::Horizontal && &data == &vData) + || (orient == QDeclarativeListView::Vertical && &data == &hData)) + return; + + correctFlick = false; + int oldDuration = fixupDuration; + fixupDuration = moveReason == Mouse ? fixupDuration : 0; + + if (currentItem && haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange) { + updateHighlight(); + qreal pos = currentItem->itemPosition(); + qreal viewPos = position(); + if (viewPos < pos + currentItem->itemSize() - highlightRangeEnd) + viewPos = pos + currentItem->itemSize() - highlightRangeEnd; + if (viewPos > pos - highlightRangeStart) + viewPos = pos - highlightRangeStart; + + timeline.reset(data.move); + if (viewPos != position()) { + if (fixupDuration) + timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + else + timeline.set(data.move, -viewPos); + } + vTime = timeline.time(); + } else if (snapMode != QDeclarativeListView::NoSnap) { + FxListItem *topItem = snapItemAt(position()+highlightRangeStart); + FxListItem *bottomItem = snapItemAt(position()+highlightRangeEnd); + qreal pos; + if (topItem) { + pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent); + } else if (bottomItem) { + pos = qMax(qMin(bottomItem->position() - highlightRangeStart, -maxExtent), -minExtent); + } else { + fixupDuration = oldDuration; + return; + } + + qreal dist = qAbs(data.move + pos); + if (dist > 0) { + timeline.reset(data.move); + if (fixupDuration) + timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + else + timeline.set(data.move, -pos); + vTime = timeline.time(); + } + } else { + QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); + } + fixupDuration = oldDuration; +} + +void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QDeclarativeListView); + + moveReason = Mouse; + if ((!haveHighlightRange || highlightRange != QDeclarativeListView::StrictlyEnforceRange) && snapMode == QDeclarativeListView::NoSnap) { + correctFlick = true; + QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); + return; + } + qreal maxDistance = 0; + // -ve velocity means list is moving up/left + if (velocity > 0) { + if (data.move.value() < minExtent) { + if (snapMode == QDeclarativeListView::SnapOneItem) { + if (FxListItem *item = firstVisibleItem()) + maxDistance = qAbs(item->position() + data.move.value()); + } else { + maxDistance = qAbs(minExtent - data.move.value()); + } + } + if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange) + data.flickTarget = minExtent; + } else { + if (data.move.value() > maxExtent) { + if (snapMode == QDeclarativeListView::SnapOneItem) { + if (FxListItem *item = nextVisibleItem()) + maxDistance = qAbs(item->position() + data.move.value()); + } else { + maxDistance = qAbs(maxExtent - data.move.value()); + } + } + if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange) + data.flickTarget = maxExtent; + } + bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds; + if (maxDistance > 0 || overShoot) { + // These modes require the list to stop exactly on an item boundary. + // The initial flick will estimate the boundary to stop on. + // Since list items can have variable sizes, the boundary will be + // reevaluated and adjusted as we approach the boundary. + qreal v = velocity; + if (maxVelocity != -1 && maxVelocity < qAbs(v)) { + if (v < 0) + v = -maxVelocity; + else + v = maxVelocity; + } + if (!flickingHorizontally && !flickingVertically) { + // the initial flick - estimate boundary + qreal accel = deceleration; + qreal v2 = v * v; + overshootDist = 0.0; + // + averageSize/4 to encourage moving at least one item in the flick direction + qreal dist = v2 / (accel * 2.0) + averageSize/4; + if (maxDistance > 0) + dist = qMin(dist, maxDistance); + if (v > 0) + dist = -dist; + if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeListView::SnapOneItem) { + data.flickTarget = -snapPosAt(-(data.move.value() - highlightRangeStart) + dist) + highlightRangeStart; + if (overShoot) { + if (data.flickTarget >= minExtent) { + overshootDist = overShootDistance(v, vSize); + data.flickTarget += overshootDist; + } else if (data.flickTarget <= maxExtent) { + overshootDist = overShootDistance(v, vSize); + data.flickTarget -= overshootDist; + } + } + qreal adjDist = -data.flickTarget + data.move.value(); + if (qAbs(adjDist) > qAbs(dist)) { + // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration + qreal adjv2 = accel * 2.0f * qAbs(adjDist); + if (adjv2 > v2) { + v2 = adjv2; + v = qSqrt(v2); + if (dist > 0) + v = -v; + } + } + dist = adjDist; + accel = v2 / (2.0f * qAbs(dist)); + } else if (overShoot) { + data.flickTarget = data.move.value() - dist; + if (data.flickTarget >= minExtent) { + overshootDist = overShootDistance(v, vSize); + data.flickTarget += overshootDist; + } else if (data.flickTarget <= maxExtent) { + overshootDist = overShootDistance(v, vSize); + data.flickTarget -= overshootDist; + } + } + timeline.reset(data.move); + timeline.accel(data.move, v, accel, maxDistance + overshootDist); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + if (!flickingHorizontally && q->xflick()) { + flickingHorizontally = true; + emit q->flickingChanged(); + emit q->flickingHorizontallyChanged(); + emit q->flickStarted(); + } + if (!flickingVertically && q->yflick()) { + flickingVertically = true; + emit q->flickingChanged(); + emit q->flickingVerticallyChanged(); + emit q->flickStarted(); + } + correctFlick = true; + } else { + // reevaluate the target boundary. + qreal newtarget = data.flickTarget; + if (snapMode != QDeclarativeListView::NoSnap || highlightRange == QDeclarativeListView::StrictlyEnforceRange) + newtarget = -snapPosAt(-(data.flickTarget - highlightRangeStart)) + highlightRangeStart; + if (velocity < 0 && newtarget <= maxExtent) + newtarget = maxExtent - overshootDist; + else if (velocity > 0 && newtarget >= minExtent) + newtarget = minExtent + overshootDist; + if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do + if (qAbs(velocity) < MinimumFlickVelocity) + correctFlick = false; + return; + } + data.flickTarget = newtarget; + qreal dist = -newtarget + data.move.value(); + if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) { + correctFlick = false; + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + return; + } + timeline.reset(data.move); + timeline.accelDistance(data.move, v, -dist); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + } + } else { + correctFlick = false; + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + } } //---------------------------------------------------------------------------- @@ -1409,10 +1513,10 @@ QVariant QDeclarativeListView::model() const return d->modelVariant; } -void QDeclarativeListView::setModel(const QVariant &newModel) +void QDeclarativeListView::setModel(const QVariant &model) { Q_D(QDeclarativeListView); - if (d->modelVariant == newModel) + if (d->modelVariant == model) return; if (d->model) { disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); @@ -1423,15 +1527,12 @@ void QDeclarativeListView::setModel(const QVariant &newModel) disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); } - d->clear(); QDeclarativeVisualModel *oldModel = d->model; d->model = 0; - d->modelCount = 0; - // d->setPosition(0); // we should do a re-layout and that should do a setViewportHeight and that should check the current position. - - d->modelVariant = newModel; - QObject *object = qvariant_cast(newModel); + d->setPosition(0); + d->modelVariant = model; + QObject *object = qvariant_cast(model); QDeclarativeVisualModel *vim = 0; if (object && (vim = qobject_cast(object))) { if (d->ownModel) { @@ -1447,14 +1548,24 @@ void QDeclarativeListView::setModel(const QVariant &newModel) d->model = oldModel; } if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) - dataModel->setModel(newModel); + dataModel->setModel(model); } - if (d->model) { - d->modelCount = d->model->count(); d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter; - - //qDebug() << "setModel: " << d->model<<"model:" << isComponentComplete(); + if (isComponentComplete()) { + updateSections(); + refill(); + if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { + setCurrentIndex(0); + } else { + d->moveReason = QDeclarativeListViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + d->highlight->setPosition(d->currentItem->position()); + d->updateTrackedItem(); + } + } + } connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); @@ -1464,17 +1575,6 @@ void QDeclarativeListView::setModel(const QVariant &newModel) connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); emit countChanged(); } - - if (isComponentComplete()) { - updateSections(); - refill(); - if ((d->currentIndex < 0 || d->currentIndex >= d->modelCount) && !d->currentIndexCleared) { - setCurrentIndex(0); - } else { - d->setCurrentIndex(d->currentIndex); - } - d->updateViewport(); - } emit modelChanged(); } @@ -1519,11 +1619,9 @@ void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate) d->model = new QDeclarativeVisualDataModel(qmlContext(this)); d->ownModel = true; } - //qDebug() << "setDelegate" << delegate; if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) { dataModel->setDelegate(delegate); if (isComponentComplete()) { - // TODO: why not call clear() ? for (int i = 0; i < d->visibleItems.count(); ++i) d->releaseItem(d->visibleItems.at(i)); d->visibleItems.clear(); @@ -1532,15 +1630,13 @@ void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate) updateSections(); refill(); d->moveReason = QDeclarativeListViewPrivate::SetIndex; - d->setCurrentIndex(d->currentIndex); + d->updateCurrent(d->currentIndex); if (d->highlight && d->currentItem) { d->highlight->setPosition(d->currentItem->position()); + d->updateTrackedItem(); } - d->updateHighlight(); - d->updateViewport(); } } - //qDebug() << "setDelegate current" << d->currentIndex << d->currentItem; emit delegateChanged(); } @@ -1552,10 +1648,10 @@ void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate) \c currentItem holds the current item. Setting the currentIndex to -1 will clear the highlight and set currentItem to null. - If highlightFollowsCurrentItem is \c true, setting either of these - properties will smoothly scroll the ListView so that the current + If highlightFollowsCurrentItem is \c true, setting either of these + properties will smoothly scroll the ListView so that the current item becomes visible. - + Note that the position of the current item may only be approximate until it becomes visible in the view. */ @@ -1568,17 +1664,14 @@ int QDeclarativeListView::currentIndex() const void QDeclarativeListView::setCurrentIndex(int index) { Q_D(QDeclarativeListView); - if (d->requestedIndex >= 0) // currently creating an item + if (d->requestedIndex >= 0) // currently creating item return; - d->currentIndexCleared = (index == -1); - if (index == d->currentIndex) return; - if (isComponentComplete() && d->isValid()) { d->moveReason = QDeclarativeListViewPrivate::SetIndex; - d->setCurrentIndex(index); + d->updateCurrent(index); } else if (d->currentIndex != index) { d->currentIndex = index; emit currentIndexChanged(); @@ -1618,8 +1711,9 @@ QDeclarativeItem *QDeclarativeListView::highlightItem() int QDeclarativeListView::count() const { Q_D(const QDeclarativeListView); - Q_ASSERT( d->model || d->modelCount == 0 ); - return d->modelCount; + if (d->model) + return d->model->count(); + return 0; } /*! @@ -1644,7 +1738,9 @@ void QDeclarativeListView::setHighlight(QDeclarativeComponent *highlight) Q_D(QDeclarativeListView); if (highlight != d->highlightComponent) { d->highlightComponent = highlight; - d->recreateHighlight(); + d->createHighlight(); + if (d->currentItem) + d->updateHighlight(); emit highlightChanged(); } } @@ -1832,22 +1928,17 @@ void QDeclarativeListView::setOrientation(QDeclarativeListView::Orientation orie setContentHeight(-1); setFlickableDirection(HorizontalFlick); } - - // -- swap the coordinates and then let layout do the final positioning - for (int i = 0; i < d->visibleItems.count(); ++i) - d->visibleItems.at(i)->item->setPos(d->visibleItems.at(i)->item->pos().y(), - d->visibleItems.at(i)->item->pos().x()); - - d->layout(); - d->updateViewport(); + d->clear(); + d->setPosition(0); + refill(); emit orientationChanged(); - d->setCurrentIndex(d->currentIndex); + d->updateCurrent(d->currentIndex); } } /*! \qmlproperty bool ListView::keyNavigationWraps - This property holds whether the list wraps key navigation. + This property holds whether the list wraps key navigation. If this is true, key navigation that would move the current item selection past the end of the list instead wraps around and moves the selection to @@ -1903,7 +1994,6 @@ void QDeclarativeListView::setCacheBuffer(int b) if (isComponentComplete()) { d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter; refill(); - d->updateViewport(); } emit cacheBufferChanged(); } @@ -2092,7 +2182,6 @@ void QDeclarativeListView::setSnapMode(SnapMode mode) if (d->snapMode != mode) { d->snapMode = mode; emit snapModeChanged(); - QScroller::scroller(this)->resendPrepareEvent(); } } @@ -2180,165 +2269,153 @@ void QDeclarativeListView::setContentY(qreal pos) bool QDeclarativeListView::event(QEvent *event) { Q_D(QDeclarativeListView); - - switch (event->type()) { - case QEvent::User: + if (event->type() == QEvent::User) { d->layout(); return true; - - case QEvent::ScrollPrepare: { - QScroller *scroller = QScroller::scroller(this); - qreal snapOffset = 0; - bool forceSnapping = false; - bool useEndPosition = false; - bool ignoreHeaders = false; - - // --- do the highlight range - if (d->haveHighlightRange) { - snapOffset = -d->highlightRangeStart; - ignoreHeaders = true; - } - - // --- now finally do the snap points - QList snapPoints; - // -- snap to every point (SnapToItem) - if (d->snapMode == QDeclarativeListView::SnapToItem || forceSnapping) { - // - snap to begin of item - if (!useEndPosition) { - if (d->header && !ignoreHeaders) - snapPoints.append(d->header->itemPosition() + snapOffset); - foreach (FxListItem *item, d->visibleItems) - snapPoints.append(item->itemPosition() + snapOffset); - if (d->footer && !ignoreHeaders) - snapPoints.append(d->footer->itemPosition() + snapOffset); - - // - snap to end of item - } else { - if (d->header && !ignoreHeaders) - snapPoints.append(d->header->endPosition() + snapOffset); - foreach (FxListItem *item, d->visibleItems) - snapPoints.append(item->endPosition() + snapOffset); - if (d->footer && !ignoreHeaders) - snapPoints.append(d->footer->endPosition() + snapOffset); - } - - // -- snap to the next three point (SnapOneItem) - } else if (d->snapMode == QDeclarativeListView::SnapOneItem) { - // here we just set three snap points around the current position. - // TODO - - // - snap to begin of item - if (!useEndPosition) { - qreal currentSnapPos = (d->positionAt(d->currentIndex) + snapOffset); - if (d->currentIndex > 0) - snapPoints.append(d->positionAt(d->currentIndex - 1) + snapOffset); - snapPoints.append(d->positionAt(d->currentIndex) + snapOffset); - // TODO: don't use last point if we are between two points already - if (d->currentIndex < d->modelCount - 1 && currentSnapPos <= d->position()) - snapPoints.append(d->positionAt(d->currentIndex + 1) + snapOffset); - - // - snap to end of item - } else { - snapPoints.append(d->endPositionAt(d->currentIndex - 1) + 1 + snapOffset); - snapPoints.append(d->endPositionAt(d->currentIndex) + 1 + snapOffset); - // TODO: don't use last point if we are between two points already - snapPoints.append(d->endPositionAt(d->currentIndex + 1) + 1 + snapOffset); - } - } - - if (d->orient == QDeclarativeListView::Vertical) { - scroller->setSnapPositionsX(0.0, 0.0); - scroller->setSnapPositionsY(snapPoints); - } else { - scroller->setSnapPositionsX(snapPoints); - scroller->setSnapPositionsY(0.0, 0.0); - } - } - break; - - default: - break; } return QDeclarativeFlickable::event(event); } -void QDeclarativeListView::scrollerStateChanged(QScroller::State state) +void QDeclarativeListView::viewportMoved() { Q_D(QDeclarativeListView); - QDeclarativeFlickable::scrollerStateChanged(state); + QDeclarativeFlickable::viewportMoved(); + if (!d->itemCount) + return; + // Recursion can occur due to refill changing the content size. + if (d->inViewportMoved) + return; + d->inViewportMoved = true; + d->lazyRelease = true; + refill(); + if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically) + d->moveReason = QDeclarativeListViewPrivate::Mouse; + if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) { + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { + // reposition highlight + qreal pos = d->highlight->position(); + qreal viewPos = 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; + d->highlightPosAnimator->stop(); + d->highlight->setPosition(qRound(pos)); + + // update current index + if (FxListItem *snapItem = d->snapItemAt(d->highlight->position())) { + if (snapItem->index >= 0 && snapItem->index != d->currentIndex) + d->updateCurrent(snapItem->index); + } + } + } - if (state == QScroller::Inactive) { - d->bufferMode = QDeclarativeListViewPrivate::NoBuffer; - if (d->highlightRange == QDeclarativeListView::StrictlyEnforceRange) { - d->updateHighlight(); // nudge the highlight in the right position if needed. + if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) { + d->inFlickCorrection = true; + // Near an end and it seems that the extent has changed? + // Recalculate the flick so that we don't end up in an odd position. + if (yflick()) { + if (d->vData.velocity > 0) { + const qreal minY = minYExtent(); + if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2) + && minY != d->vData.flickTarget) + d->flickY(-d->vData.smoothVelocity.value()); + d->bufferMode = QDeclarativeListViewPrivate::BufferBefore; + } else if (d->vData.velocity < 0) { + const qreal maxY = maxYExtent(); + if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2) + && maxY != d->vData.flickTarget) + d->flickY(-d->vData.smoothVelocity.value()); + d->bufferMode = QDeclarativeListViewPrivate::BufferAfter; + } } + + if (xflick()) { + if (d->hData.velocity > 0) { + const qreal minX = minXExtent(); + if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2) + && minX != d->hData.flickTarget) + d->flickX(-d->hData.smoothVelocity.value()); + d->bufferMode = QDeclarativeListViewPrivate::BufferBefore; + } else if (d->hData.velocity < 0) { + const qreal maxX = maxXExtent(); + if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2) + && maxX != d->hData.flickTarget) + d->flickX(-d->hData.smoothVelocity.value()); + d->bufferMode = QDeclarativeListViewPrivate::BufferAfter; + } + } + d->inFlickCorrection = false; } + d->inViewportMoved = false; } -qreal QDeclarativeListView::minExtent() const +qreal QDeclarativeListView::minYExtent() const { Q_D(const QDeclarativeListView); - + if (d->orient == QDeclarativeListView::Horizontal) + return QDeclarativeFlickable::minYExtent(); if (d->minExtentDirty) { - d->minExtent = d->startPosition(); - if (d->modelCount && - d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->minExtent -= d->highlightRangeStart; + d->minExtent = -d->startPosition(); + if (d->header && d->visibleItems.count()) + d->minExtent += d->header->size(); + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent += d->highlightRangeStart; if (d->sectionCriteria) { if (d->visibleItem(0)) d->minExtent -= d->visibleItem(0)->sectionSize(); } + d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1)); } d->minExtentDirty = false; } + return d->minExtent; } -qreal QDeclarativeListView::maxExtent() const +qreal QDeclarativeListView::maxYExtent() const { Q_D(const QDeclarativeListView); - + if (d->orient == QDeclarativeListView::Horizontal) + return height(); if (d->maxExtentDirty) { - d->maxExtent = d->endPosition() + 1; - - if (d->modelCount && - d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->maxExtent = qMin(d->maxExtent + d->size() - d->highlightRangeEnd + 1, - // ensure that the last item fits fully behind hightlightRangeStart - d->positionAt(d->modelCount-1) + d->size() - d->highlightRangeStart); + if (!d->model || !d->model->count()) { + d->maxExtent = 0; + } 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 + 1)); + } else { + d->maxExtent = -(d->endPosition() - height() + 1); } - d->maxExtent = qMax(d->maxExtent, minExtent()); + if (d->footer) + d->maxExtent -= d->footer->size(); + qreal minY = minYExtent(); + if (d->maxExtent > minY) + d->maxExtent = minY; d->maxExtentDirty = false; } return d->maxExtent; } -qreal QDeclarativeListView::minYExtent() const -{ - Q_D(const QDeclarativeListView); - if (d->orient == QDeclarativeListView::Horizontal) - return QDeclarativeFlickable::minYExtent(); - else - return minExtent(); -} - -qreal QDeclarativeListView::maxYExtent() const -{ - Q_D(const QDeclarativeListView); - if (d->orient == QDeclarativeListView::Horizontal) - return height(); - else - return maxExtent(); -} - qreal QDeclarativeListView::minXExtent() const { Q_D(const QDeclarativeListView); if (d->orient == QDeclarativeListView::Vertical) return QDeclarativeFlickable::minXExtent(); - else - return minExtent(); + if (d->minExtentDirty) { + d->minExtent = -d->startPosition(); + if (d->header) + d->minExtent += d->header->size(); + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent += d->highlightRangeStart; + d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1)); + } + d->minExtentDirty = false; + } + + return d->minExtent; } qreal QDeclarativeListView::maxXExtent() const @@ -2346,50 +2423,25 @@ qreal QDeclarativeListView::maxXExtent() const Q_D(const QDeclarativeListView); if (d->orient == QDeclarativeListView::Vertical) return width(); - else - return maxExtent(); -} - -void QDeclarativeListView::viewportAboutToMove(QPointF newPos) -{ - Q_D(QDeclarativeListView); - - // qDebug() << "viewport about to move"; - // need to refill before moving - if( d->orient == Horizontal ) - d->refill(newPos.x(), newPos.x() + width() - 1); - else - d->refill(newPos.y(), newPos.y() + height() - 1); - - d->lazyRelease = true; - - if (d->isUserGenerated) - d->moveReason = QDeclarativeListViewPrivate::Mouse; - - if (d->haveHighlightRange && d->highlight && d->highlightRange == StrictlyEnforceRange) { - - d->highlightPosAnimator->stop(); - - // reposition highlight - qreal pos = d->highlight->position(); - qreal viewPos = d->orient == Horizontal ? newPos.x() : newPos.y(); - if (pos > viewPos + d->highlightRangeEnd - d->highlight->size()) - pos = viewPos + d->highlightRangeEnd - d->highlight->size(); - if (pos < viewPos + d->highlightRangeStart) - pos = viewPos + d->highlightRangeStart; - d->highlight->setPosition(pos); - - // update current index - if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) { - int idx = d->snapIndex(); - if (idx >= 0 && idx != d->currentIndex) - d->setCurrentIndex(idx); + if (d->maxExtentDirty) { + if (!d->model || !d->model->count()) { + d->maxExtent = 0; + } 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 + 1)); + } else { + d->maxExtent = -(d->endPosition() - width() + 1); } + if (d->footer) + d->maxExtent -= d->footer->size(); + qreal minX = minXExtent(); + if (d->maxExtent > minX) + d->maxExtent = minX; + d->maxExtentDirty = false; } - if (d->sectionCriteria) - d->updateCurrentSection(); - d->updateViewport(); + return d->maxExtent; } void QDeclarativeListView::keyPressEvent(QKeyEvent *event) @@ -2399,7 +2451,7 @@ void QDeclarativeListView::keyPressEvent(QKeyEvent *event) if (event->isAccepted()) return; - if (d->model && d->modelCount && d->interactive) { + if (d->model && d->model->count() && d->interactive) { if ((d->orient == QDeclarativeListView::Horizontal && event->key() == Qt::Key_Left) || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Up)) { if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) { @@ -2412,7 +2464,7 @@ void QDeclarativeListView::keyPressEvent(QKeyEvent *event) } } else if ((d->orient == QDeclarativeListView::Horizontal && event->key() == Qt::Key_Right) || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Down)) { - if (currentIndex() < d->modelCount - 1 || (d->wrap && !event->isAutoRepeat())) { + if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) { incrementCurrentIndex(); event->accept(); return; @@ -2426,7 +2478,8 @@ void QDeclarativeListView::keyPressEvent(QKeyEvent *event) QDeclarativeFlickable::keyPressEvent(event); } -void QDeclarativeListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +void QDeclarativeListView::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) { Q_D(QDeclarativeListView); d->maxExtentDirty = true; @@ -2447,11 +2500,11 @@ void QDeclarativeListView::geometryChanged(const QRectF &newGeometry, const QRec void QDeclarativeListView::incrementCurrentIndex() { Q_D(QDeclarativeListView); - //qDebug() << "incrementCurrentIndex" << currentIndex() << "c:" << count; - if (d->modelCount && (currentIndex() < d->modelCount - 1 || d->wrap)) { + int count = d->model ? d->model->count() : 0; + if (count && (currentIndex() < count - 1 || d->wrap)) { d->moveReason = QDeclarativeListViewPrivate::SetIndex; int index = currentIndex()+1; - setCurrentIndex((index >= 0 && index < d->modelCount) ? index : 0); + setCurrentIndex((index >= 0 && index < count) ? index : 0); } } @@ -2467,11 +2520,11 @@ void QDeclarativeListView::incrementCurrentIndex() void QDeclarativeListView::decrementCurrentIndex() { Q_D(QDeclarativeListView); - //qDebug() << "decrementCurrentIndex" << currentIndex() << "c:" << count; - if (d->modelCount && (currentIndex() > 0 || d->wrap)) { + int count = d->model ? d->model->count() : 0; + if (count && (currentIndex() > 0 || d->wrap)) { d->moveReason = QDeclarativeListViewPrivate::SetIndex; int index = currentIndex()-1; - setCurrentIndex((index >= 0 && index < d->modelCount) ? index : d->modelCount - 1); + setCurrentIndex((index >= 0 && index < count) ? index : count-1); } } @@ -2510,9 +2563,8 @@ void QDeclarativeListView::decrementCurrentIndex() */ void QDeclarativeListView::positionViewAtIndex(int index, int mode) { - //qDebug() << "positionViewAtIndex index:" << index << "mode:" << mode; Q_D(QDeclarativeListView); - if (!d->isValid() || index < 0 || index >= d->modelCount) + if (!d->isValid() || index < 0 || index >= d->model->count()) return; if (mode < Beginning || mode > Contain) return; @@ -2558,9 +2610,12 @@ void QDeclarativeListView::positionViewAtIndex(int index, int mode) if (itemPos < pos) pos = itemPos; } - pos = qMin(pos, maxExtent() - d->size()); // shouldn't that be + 1 ? - pos = qMax(pos, minExtent()); + qreal maxExtent = d->orient == QDeclarativeListView::Vertical ? -maxYExtent() : -maxXExtent(); + pos = qMin(pos, maxExtent); + qreal minExtent = d->orient == QDeclarativeListView::Vertical ? -minYExtent() : -minXExtent(); + pos = qMax(pos, minExtent); d->moveReason = QDeclarativeListViewPrivate::Other; + cancelFlick(); d->setPosition(pos); if (d->highlight) { d->highlight->setPosition(d->currentItem->itemPosition()); @@ -2568,6 +2623,7 @@ void QDeclarativeListView::positionViewAtIndex(int index, int mode) d->updateHighlight(); } } + d->fixupPosition(); } /*! @@ -2603,13 +2659,15 @@ void QDeclarativeListView::componentComplete() refill(); d->moveReason = QDeclarativeListViewPrivate::SetIndex; if (d->currentIndex < 0 && !d->currentIndexCleared) - d->setCurrentIndex(0); + d->updateCurrent(0); else - d->setCurrentIndex(d->currentIndex); + d->updateCurrent(d->currentIndex); if (d->highlight && d->currentItem) { d->highlight->setPosition(d->currentItem->position()); + d->updateTrackedItem(); } - d->updateHighlight(false); + d->moveReason = QDeclarativeListViewPrivate::Other; + d->fixupPosition(); } } @@ -2628,35 +2686,88 @@ void QDeclarativeListView::updateSections() void QDeclarativeListView::refill() { Q_D(QDeclarativeListView); - d->refill(d->position(), d->position() + d->size() - 1); + d->refill(d->position(), d->position()+d->size()-1); } -void QDeclarativeListView::itemsInserted(int modelIndex, int count) +void QDeclarativeListView::trackedPositionChanged() { Q_D(QDeclarativeListView); - d->minExtentDirty = true; - d->maxExtentDirty = true; - - if (!isComponentComplete()) { - d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count + if (!d->trackedItem || !d->currentItem) return; + if (d->moveReason == QDeclarativeListViewPrivate::SetIndex) { + qreal trackedPos = qCeil(d->trackedItem->position()); + qreal trackedSize = d->trackedItem->size(); + if (d->trackedItem != d->currentItem) { + trackedPos -= d->currentItem->sectionSize(); + trackedSize += d->currentItem->sectionSize(); + } + const qreal viewPos = d->position(); + qreal pos = viewPos; + if (d->haveHighlightRange) { + if (d->highlightRange == StrictlyEnforceRange) { + if (trackedPos > pos + d->highlightRangeEnd - d->trackedItem->size()) + pos = trackedPos - d->highlightRangeEnd + d->trackedItem->size(); + if (trackedPos < pos + d->highlightRangeStart) + pos = trackedPos - d->highlightRangeStart; + } else { + if (trackedPos < d->startPosition() + d->highlightRangeStart) { + pos = d->startPosition(); + } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + d->highlightRangeEnd) { + pos = d->endPosition() - d->size() + 1; + if (pos < d->startPosition()) + pos = d->startPosition(); + } else { + if (trackedPos < viewPos + d->highlightRangeStart) { + pos = trackedPos - d->highlightRangeStart; + } else if (trackedPos > viewPos + d->highlightRangeEnd - trackedSize) { + pos = trackedPos - d->highlightRangeEnd + trackedSize; + } + } + } + } else { + if (trackedPos < viewPos && d->currentItem->position() < viewPos) { + pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position(); + } else if (d->trackedItem->endPosition() >= viewPos + d->size() + && d->currentItem->endPosition() >= viewPos + d->size()) { + if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) { + pos = d->trackedItem->endPosition() - d->size() + 1; + if (trackedSize > d->size()) + pos = trackedPos; + } else { + pos = d->currentItem->endPosition() - d->size() + 1; + if (d->currentItem->size() > d->size()) + pos = d->currentItem->position(); + } + } + } + if (viewPos != pos) { + cancelFlick(); + d->calcVelocity = true; + d->setPosition(pos); + d->calcVelocity = false; + } } +} +void QDeclarativeListView::itemsInserted(int modelIndex, int count) +{ + Q_D(QDeclarativeListView); + if (!isComponentComplete()) + return; d->updateUnrequestedIndexes(); d->moveReason = QDeclarativeListViewPrivate::Other; if (!d->visibleItems.count() || d->model->count() <= 1) { d->scheduleLayout(); - if (d->modelCount && d->currentIndex >= modelIndex) { + if (d->itemCount && d->currentIndex >= modelIndex) { // adjust current item index d->currentIndex += count; if (d->currentItem) d->currentItem->index = d->currentIndex; emit currentIndexChanged(); } else if (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared)) { - d->setCurrentIndex(0); + d->updateCurrent(0); } - d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count - d->updateScrollerValues(); + d->itemCount += count; emit countChanged(); return; } @@ -2688,16 +2799,13 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) emit currentIndexChanged(); } d->scheduleLayout(); - d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count - d->updateScrollerValues(); + d->itemCount += count; emit countChanged(); return; } } // At least some of the added items will be visible - if (d->layoutScheduled) - d->layout(); // index can be the next item past the end of the visible items list (i.e. appended) int pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position() @@ -2762,9 +2870,14 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) } diff = pos - initialPos; } - if (d->modelCount && d->currentIndex >= modelIndex) { + if (d->itemCount && d->currentIndex >= modelIndex) { // adjust current item index - d->setCurrentIndex(d->currentIndex + count); + d->currentIndex += count; + if (d->currentItem) { + d->currentItem->index = d->currentIndex; + d->currentItem->setPosition(d->currentItem->position() + diff); + } + emit currentIndexChanged(); } // Update the indexes of the following visible items. for (; index < d->visibleItems.count(); ++index) { @@ -2779,42 +2892,35 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) added.at(j)->attached->emitAdd(); d->updateSections(); - d->modelCount = d->model->count(); // don't rely on newCount = oldCount + count - d->updateScrollerValues(); + d->itemCount += count; emit countChanged(); } void QDeclarativeListView::itemsRemoved(int modelIndex, int count) { Q_D(QDeclarativeListView); - d->minExtentDirty = true; - d->maxExtentDirty = true; - d->modelCount = d->model->count(); // don't rely on newCount = oldCount - count if (!isComponentComplete()) return; d->moveReason = QDeclarativeListViewPrivate::Other; d->updateUnrequestedIndexes(); + d->itemCount -= count; FxListItem *firstVisible = d->firstVisibleItem(); int preRemovedSize = 0; - bool removedVisible = false; // true if we removed an visible item. + bool removedVisible = false; // Remove the items from the visible list, skipping anything already marked for removal QList::Iterator it = d->visibleItems.begin(); while (it != d->visibleItems.end()) { FxListItem *item = *it; - - // qDebug() << "Remove visible?: "<index<<"at"<position()<<"mi"<index == -1 || item->index < modelIndex) { // already removed, or before removed items ++it; } else if (item->index >= modelIndex + count) { // after removed items - // qDebug() << "Remove item not" << item->index <<"at" << item->position() << "newINdex:"<<(item->index-count); item->index -= count; ++it; } else { // removed item - // qDebug() << "Remove item really" << item->index <<"at" << item->position(); if (!removedVisible) { d->scheduleLayout(); removedVisible = true; @@ -2825,22 +2931,18 @@ void QDeclarativeListView::itemsRemoved(int modelIndex, int count) connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection); ++it; } else { - // move the list down if we remove an item between the start of the list and the first visible item. if (item == firstVisible) - firstVisible = 0; + firstVisible = 0; if (firstVisible && item->position() < firstVisible->position()) preRemovedSize += item->size(); - it = d->visibleItems.erase(it); d->releaseItem(item); } } } - - // restore the start position of the visible items - if (firstVisible && forceConst(d->visibleItems).first() != firstVisible) - forceConst(d->visibleItems).first()->setPosition(forceConst(d->visibleItems).first()->position() + preRemovedSize); + if (firstVisible && d->visibleItems.first() != firstVisible) + d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize); // fix current if (d->currentIndex >= modelIndex + count) { @@ -2854,8 +2956,8 @@ void QDeclarativeListView::itemsRemoved(int modelIndex, int count) d->releaseItem(d->currentItem); d->currentItem = 0; d->currentIndex = -1; - if (d->modelCount) - d->setCurrentIndex(qMin(modelIndex, d->modelCount-1)); + if (d->itemCount) + d->updateCurrent(qMin(modelIndex, d->itemCount-1)); } // update visibleIndex @@ -2865,21 +2967,24 @@ void QDeclarativeListView::itemsRemoved(int modelIndex, int count) break; } } + if (removedVisible && d->visibleItems.isEmpty()) { - QScroller::scroller(this)->stop(); - // the complete visible area was removed - if (!d->modelCount) { - d->clear(); + d->timeline.clear(); + if (d->itemCount == 0) { + d->visibleIndex = 0; + d->visiblePos = d->header ? d->header->size() : 0; + d->setPosition(0); + d->updateHeader(); + d->updateFooter(); + update(); } else { if (modelIndex < d->visibleIndex) d->visibleIndex = modelIndex+1; - d->visibleIndex = qBound(0, d->visibleIndex, d->modelCount - 1); + d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0); } } d->updateSections(); - d->updateScrollerValues(); - emit countChanged(); } @@ -2999,7 +3104,7 @@ void QDeclarativeListView::itemsMoved(int from, int to, int count) } // Ensure we don't cause an ugly list scroll. - forceConst(d->visibleItems).first()->setPosition(forceConst(d->visibleItems).first()->position() + moveBy); + d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy); d->updateSections(); d->layout(); @@ -3016,15 +3121,14 @@ void QDeclarativeListView::modelReset() { Q_D(QDeclarativeListView); d->clear(); - d->modelCount = d->model->count(); d->setPosition(0); refill(); d->moveReason = QDeclarativeListViewPrivate::SetIndex; - d->setCurrentIndex(d->currentIndex); + d->updateCurrent(d->currentIndex); if (d->highlight && d->currentItem) { d->highlight->setPosition(d->currentItem->position()); + d->updateTrackedItem(); } - d->updateHighlight(); d->moveReason = QDeclarativeListViewPrivate::Other; emit countChanged(); } @@ -3048,6 +3152,14 @@ void QDeclarativeListView::destroyingItem(QDeclarativeItem *item) d->unrequestedItems.remove(item); } +void QDeclarativeListView::animStopped() +{ + Q_D(QDeclarativeListView); + d->bufferMode = QDeclarativeListViewPrivate::NoBuffer; + if (d->haveHighlightRange && d->highlightRange == QDeclarativeListView::StrictlyEnforceRange) + d->updateHighlight(); +} + QDeclarativeListViewAttached *QDeclarativeListView::qmlAttachedProperties(QObject *obj) { return new QDeclarativeListViewAttached(obj); diff --git a/src/declarative/graphicsitems/qdeclarativelistview_p.h b/src/declarative/graphicsitems/qdeclarativelistview_p.h index 5593574..2678b90 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview_p.h +++ b/src/declarative/graphicsitems/qdeclarativelistview_p.h @@ -238,18 +238,13 @@ Q_SIGNALS: void headerChanged(); void footerChanged(); -protected Q_SLOTS: - virtual void scrollerStateChanged(QScroller::State); - protected: virtual bool event(QEvent *event); - virtual qreal minExtent() const; - virtual qreal maxExtent() const; + virtual void viewportMoved(); virtual qreal minYExtent() const; virtual qreal maxYExtent() const; virtual qreal minXExtent() const; virtual qreal maxXExtent() const; - virtual void viewportAboutToMove(QPointF newPos); virtual void keyPressEvent(QKeyEvent *); virtual void geometryChanged(const QRectF &newGeometry,const QRectF &oldGeometry); virtual void componentComplete(); @@ -257,6 +252,7 @@ protected: private Q_SLOTS: void updateSections(); void refill(); + void trackedPositionChanged(); void itemsInserted(int index, int count); void itemsRemoved(int index, int count); void itemsMoved(int from, int to, int count); @@ -265,6 +261,7 @@ private Q_SLOTS: void destroyRemoved(); void createdItem(int index, QDeclarativeItem *item); void destroyingItem(QDeclarativeItem *item); + void animStopped(); }; class QDeclarativeListViewAttached : public QObject diff --git a/tests/auto/declarative/qdeclarativeflickable/tst_qdeclarativeflickable.cpp b/tests/auto/declarative/qdeclarativeflickable/tst_qdeclarativeflickable.cpp index 3442596..25edb36 100644 --- a/tests/auto/declarative/qdeclarativeflickable/tst_qdeclarativeflickable.cpp +++ b/tests/auto/declarative/qdeclarativeflickable/tst_qdeclarativeflickable.cpp @@ -99,11 +99,9 @@ void tst_qdeclarativeflickable::create() QCOMPARE(obj->verticalVelocity(), 0.); QCOMPARE(obj->isInteractive(), true); - QCOMPARE(obj->pressDelay(), 0); QCOMPARE(obj->boundsBehavior(), QDeclarativeFlickable::DragAndOvershootBounds); - /* Those values are platform dependant + QCOMPARE(obj->pressDelay(), 0); QCOMPARE(obj->maximumFlickVelocity(), 2000.); - */ delete obj; } diff --git a/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp b/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp index ef5c976..327bba2 100644 --- a/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp +++ b/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp @@ -649,7 +649,6 @@ void tst_QDeclarativeGridView::currentIndex() #endif QTRY_VERIFY(canvas->hasFocus()); QTRY_VERIFY(canvas->scene()->hasFocus()); - gridview->forceActiveFocus(); qApp->processEvents(); QTest::keyClick(canvas, Qt::Key_Down); @@ -667,7 +666,6 @@ void tst_QDeclarativeGridView::currentIndex() #endif QTRY_VERIFY(canvas->hasFocus()); QTRY_VERIFY(canvas->scene()->hasFocus()); - gridview->forceActiveFocus(); qApp->processEvents(); QTest::keyClick(canvas, Qt::Key_Right); diff --git a/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp b/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp index 70f417f..37d836d 100644 --- a/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp +++ b/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp @@ -613,11 +613,10 @@ void tst_QDeclarativeListView::removed(bool animated) listview->setCurrentIndex(10); model.removeItem(1); // post: top item will be at 40 - // let transitions settle. qApp->processEvents(); - // Confirm items positioned correctly (we were at the fourth item and deleted one) - for (int i = 2; i < 2 + 16; ++i) { + // Confirm items positioned correctly + for (int i = 2; i < 18; ++i) { QDeclarativeItem *item = findItem(contentItem, "wrapper", i); if (!item) qWarning() << "Item" << i << "not found"; QTRY_VERIFY(item); @@ -664,12 +663,10 @@ void tst_QDeclarativeListView::removed(bool animated) listview->setContentY(80); QTest::qWait(300); - // remove all visible items model.removeItems(1, 17); QTest::qWait(300); // Confirm items positioned correctly - // we were at the fourth item, but deleted 1 through 17. So we should be at 1 now. itemCount = findItems(contentItem, "wrapper").count(); for (int i = 0; i < model.count() && i < itemCount-1; ++i) { QDeclarativeItem *item = findItem(contentItem, "wrapper", i+2); @@ -1079,7 +1076,6 @@ void tst_QDeclarativeListView::currentIndex() #endif QTRY_VERIFY(canvas->hasFocus()); QTRY_VERIFY(canvas->scene()->hasFocus()); - listview->forceActiveFocus(); qApp->processEvents(); QTest::keyClick(canvas, Qt::Key_Down); -- cgit v0.12