From 9b5f9c8056186c9cf3c40dbac66e5f90369339dc Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Tue, 5 Jul 2011 15:07:05 +1000 Subject: [PATCH] Refactor ListView and GridView implementations Places common code into QSGItemView. Change-Id: Ic310dbe7e16774163ba393860da64a0da7d4ea0a Reviewed-on: http://codereview.qt.nokia.com/1200 Reviewed-by: Qt Sanity Bot Reviewed-by: Bea Lam --- src/declarative/items/items.pri | 3 + src/declarative/items/qsggridview.cpp | 2034 +++++------------- src/declarative/items/qsggridview_p.h | 151 +- src/declarative/items/qsgitemview.cpp | 1363 ++++++++++++ src/declarative/items/qsgitemview_p.h | 285 +++ src/declarative/items/qsgitemview_p_p.h | 208 ++ src/declarative/items/qsglistview.cpp | 2205 +++++--------------- src/declarative/items/qsglistview_p.h | 194 +- .../declarative/qsggridview/tst_qsggridview.cpp | 4 +- 9 files changed, 2901 insertions(+), 3546 deletions(-) create mode 100644 src/declarative/items/qsgitemview.cpp create mode 100644 src/declarative/items/qsgitemview_p.h create mode 100644 src/declarative/items/qsgitemview_p_p.h diff --git a/src/declarative/items/items.pri b/src/declarative/items/items.pri index bf92025..7819980 100644 --- a/src/declarative/items/items.pri +++ b/src/declarative/items/items.pri @@ -66,6 +66,8 @@ HEADERS += \ $$PWD/qsgspriteimage_p.h \ $$PWD/qsgevent.h \ $$PWD/qsgdragtarget_p.h \ + $$PWD/qsgitemview_p.h \ + $$PWD/qsgitemview_p_p.h SOURCES += \ $$PWD/qsgevents.cpp \ @@ -109,6 +111,7 @@ SOURCES += \ $$PWD/qsgsprite.cpp \ $$PWD/qsgspriteimage.cpp \ $$PWD/qsgdragtarget.cpp \ + $$PWD/qsgitemview.cpp SOURCES += \ $$PWD/qsgshadereffectitem.cpp \ diff --git a/src/declarative/items/qsggridview.cpp b/src/declarative/items/qsggridview.cpp index 9c67246..ab02dea 100644 --- a/src/declarative/items/qsggridview.cpp +++ b/src/declarative/items/qsggridview.cpp @@ -42,6 +42,7 @@ #include "qsggridview_p.h" #include "qsgvisualitemmodel_p.h" #include "qsgflickable_p_p.h" +#include "qsgitemview_p_p.h" #include #include @@ -55,43 +56,52 @@ QT_BEGIN_NAMESPACE //---------------------------------------------------------------------------- -class FxGridItemSG +class FxGridItemSG : public FxViewItem { public: - FxGridItemSG(QSGItem *i, QSGGridView *v) : item(i), view(v) { + FxGridItemSG(QSGItem *i, QSGGridView *v, bool own) : FxViewItem(i, own), view(v) { attached = static_cast(qmlAttachedPropertiesObject(item)); if (attached) - attached->setView(view); + static_cast(attached)->setView(view); } + ~FxGridItemSG() {} + qreal position() const { + return rowPos(); + } + + qreal endPosition() const { + return endRowPos(); + } + + qreal size() const { + return view->flow() == QSGGridView::LeftToRight ? view->cellHeight() : view->cellWidth(); + } + + qreal sectionSize() const { + return 0.0; + } + qreal rowPos() const { - qreal rowPos = 0; - if (view->flow() == QSGGridView::LeftToRight) { - rowPos = item->y(); - } else { - if (view->effectiveLayoutDirection() == Qt::RightToLeft) - rowPos = -view->cellWidth()-item->x(); - else - rowPos = item->x(); - } - return rowPos; + if (view->flow() == QSGGridView::LeftToRight) + return item->y(); + else + return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -view->cellWidth()-item->x() : item->x()); } + qreal colPos() const { - qreal colPos = 0; if (view->flow() == QSGGridView::LeftToRight) { if (view->effectiveLayoutDirection() == Qt::RightToLeft) { int colSize = view->cellWidth(); int columns = view->width()/colSize; - colPos = colSize * (columns-1) - item->x(); + return colSize * (columns-1) - item->x(); } else { - colPos = item->x(); + return item->x(); } } else { - colPos = item->y(); + return item->y(); } - - return colPos; } qreal endRowPos() const { if (view->flow() == QSGGridView::LeftToRight) { @@ -123,464 +133,289 @@ public: y >= item->y() && y < item->y() + view->cellHeight()); } - QSGItem *item; QSGGridView *view; - QSGGridViewAttached *attached; - int index; }; //---------------------------------------------------------------------------- -class QSGGridViewPrivate : public QSGFlickablePrivate +class QSGGridViewPrivate : public QSGItemViewPrivate { Q_DECLARE_PUBLIC(QSGGridView) public: - QSGGridViewPrivate() - : currentItem(0), layoutDirection(Qt::LeftToRight), flow(QSGGridView::LeftToRight) - , visibleIndex(0) , currentIndex(-1) - , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), itemCount(0) - , highlightRangeStart(0), highlightRangeEnd(0) - , highlightRange(QSGGridView::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(QSGGridView::NoSnap) - , ownModel(false), wrap(false), autoHighlight(true) - , fixCurrentVisibility(false), lazyRelease(false), layoutScheduled(false) - , deferredRelease(false), haveHighlightRange(false), currentIndexCleared(false) - , highlightRangeStartValid(false), highlightRangeEndValid(false) {} - - void init(); - void clear(); - FxGridItemSG *createItem(int modelIndex); - void releaseItem(FxGridItemSG *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 updateHeader(); - void updateFooter(); - void fixupPosition(); - - FxGridItemSG *visibleItem(int modelIndex) const { - if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { - for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { - FxGridItemSG *item = visibleItems.at(i); - if (item->index == modelIndex) - return item; - } - } - return 0; - } - - bool isRightToLeftTopToBottom() const { - Q_Q(const QSGGridView); - return flow == QSGGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft; - } + virtual Qt::Orientation layoutOrientation() const; + virtual bool isContentFlowReversed() const; + bool isRightToLeftTopToBottom() const; + + virtual qreal startPosition() const; + virtual qreal positionAt(int index) const; + virtual qreal endPosition() const; + virtual qreal endPositionAt(int index) const; + virtual qreal lastPosition() const; + + qreal originPosition() const; + int rowSize() const; + int colSize() const; + qreal colPosAt(int modelIndex) const; + qreal rowPosAt(int modelIndex) const; + qreal snapPosAt(qreal pos) const; + FxViewItem *snapItemAt(qreal pos) const; + int snapIndex() const; + + virtual bool addVisibleItems(int fillFrom, int fillTo, bool doBuffer); + virtual bool removeNonVisibleItems(int bufferFrom, int bufferTo); + virtual void visibleItemsChanged(); + + virtual FxViewItem *newViewItem(int index, QSGItem *item); + virtual void repositionPackageItemAt(QSGItem *item, int index); + + virtual void createHighlight(); + virtual void updateHighlight(); + virtual void resetHighlightPosition(); + + virtual void setPosition(qreal pos); + virtual void layoutVisibleItems(); + + virtual qreal headerSize() const; + virtual qreal footerSize() const; + virtual void updateHeader(); + virtual void updateFooter(); + + virtual void changedVisibleIndex(int newIndex); + virtual void initializeCurrentItem(); + + virtual void updateViewport(); + virtual void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + virtual void fixupPosition(); + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); + virtual void flick(QSGItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); - void regenerate() { - Q_Q(QSGGridView); - if (q->isComponentComplete()) { - clear(); - updateGrid(); - setPosition(0); - q->refill(); - updateCurrent(currentIndex); - } - } + QSGGridView::Flow flow; + int cellWidth; + int cellHeight; + int columns; + QSGGridView::SnapMode snapMode; - void mirrorChange() { - Q_Q(QSGGridView); - regenerate(); - emit q->effectiveLayoutDirectionChanged(); - } + QSmoothedAnimation *highlightXAnimator; + QSmoothedAnimation *highlightYAnimator; - qreal position() const { - Q_Q(const QSGGridView); - return flow == QSGGridView::LeftToRight ? q->contentY() : q->contentX(); - } - void setPosition(qreal pos) { - Q_Q(QSGGridView); - if (flow == QSGGridView::LeftToRight) { - q->QSGFlickable::setContentY(pos); - q->QSGFlickable::setContentX(0); - } else { - if (q->effectiveLayoutDirection() == Qt::LeftToRight) - q->QSGFlickable::setContentX(pos); - else - q->QSGFlickable::setContentX(-pos-size()); - q->QSGFlickable::setContentY(0); - } - } - int size() const { - Q_Q(const QSGGridView); - return flow == QSGGridView::LeftToRight ? q->height() : q->width(); - } - qreal originPosition() const { - qreal pos = 0; - if (!visibleItems.isEmpty()) - pos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); - return pos; - } + QSGGridViewPrivate() + : flow(QSGGridView::LeftToRight) + , cellWidth(100), cellHeight(100), columns(1) + , snapMode(QSGGridView::NoSnap) + , highlightXAnimator(0), highlightYAnimator(0) + {} +}; - qreal lastPosition() const { - qreal pos = 0; - if (model && model->count()) - pos = rowPosAt(model->count() - 1) + rowSize(); - return pos; - } +Qt::Orientation QSGGridViewPrivate::layoutOrientation() const +{ + return flow == QSGGridView::LeftToRight ? Qt::Vertical : Qt::Horizontal; +} - qreal startPosition() const { - return isRightToLeftTopToBottom() ? -lastPosition()+1 : originPosition(); - } +bool QSGGridViewPrivate::isContentFlowReversed() const +{ + return isRightToLeftTopToBottom(); +} - qreal endPosition() const { - return isRightToLeftTopToBottom() ? -originPosition()+1 : lastPosition(); +bool QSGGridViewPrivate::isRightToLeftTopToBottom() const +{ + Q_Q(const QSGGridView); + return flow == QSGGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft; +} - } +void QSGGridViewPrivate::changedVisibleIndex(int newIndex) +{ + visibleIndex = newIndex / columns * columns; +} - bool isValid() const { - return model && model->count() && model->isValid(); +void QSGGridViewPrivate::setPosition(qreal pos) +{ + Q_Q(QSGGridView); + if (flow == QSGGridView::LeftToRight) { + q->QSGFlickable::setContentY(pos); + q->QSGFlickable::setContentX(0); + } else { + if (q->effectiveLayoutDirection() == Qt::LeftToRight) + q->QSGFlickable::setContentX(pos); + else + q->QSGFlickable::setContentX(-pos-size()); + q->QSGFlickable::setContentY(0); } +} - int rowSize() const { - return flow == QSGGridView::LeftToRight ? cellHeight : cellWidth; - } - int colSize() const { - return flow == QSGGridView::LeftToRight ? cellWidth : cellHeight; - } +qreal QSGGridViewPrivate::originPosition() const +{ + qreal pos = 0; + if (!visibleItems.isEmpty()) + pos = static_cast(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize(); + return pos; +} - qreal colPosAt(int modelIndex) const { - if (FxGridItemSG *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 { - if (FxGridItemSG *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) - pos += headerSize(); - return pos; - } - return 0; - } +qreal QSGGridViewPrivate::lastPosition() const +{ + qreal pos = 0; + if (model && model->count()) + pos = rowPosAt(model->count() - 1) + rowSize(); + return pos; +} - FxGridItemSG *firstVisibleItem() const { - const qreal pos = isRightToLeftTopToBottom() ? -position()-size() : position(); - for (int i = 0; i < visibleItems.count(); ++i) { - FxGridItemSG *item = visibleItems.at(i); - if (item->index != -1 && item->endRowPos() > pos) - return item; - } - return visibleItems.count() ? visibleItems.first() : 0; - } +qreal QSGGridViewPrivate::startPosition() const +{ + return isRightToLeftTopToBottom() ? -lastPosition()+1 : originPosition(); +} - int lastVisibleIndex() const { - for (int i = 0; i < visibleItems.count(); ++i) { - FxGridItemSG *item = visibleItems.at(i); - if (item->index != -1) - return item->index; - } - return -1; - } - - // Map a model index to visibleItems list index. - // These may differ if removed items are still present in the visible list, - // e.g. doing a removal animation - int mapFromModel(int modelIndex) const { - if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) - return -1; - for (int i = 0; i < visibleItems.count(); ++i) { - FxGridItemSG *listItem = visibleItems.at(i); - if (listItem->index == modelIndex) - return i + visibleIndex; - if (listItem->index > modelIndex) - return -1; - } - return -1; // Not in visibleList - } - - qreal snapPosAt(qreal pos) const { - Q_Q(const QSGGridView); - 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; - qreal minExtent; - if (isRightToLeftTopToBottom()) { - maxExtent = q->minXExtent(); - minExtent = q->maxXExtent(); - } else { - maxExtent = flow == QSGGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); - minExtent = flow == QSGGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); - } - if (snapPos > maxExtent) - snapPos = maxExtent; - if (snapPos < minExtent) - snapPos = minExtent; - } - return snapPos; - } +qreal QSGGridViewPrivate::positionAt(int index) const +{ + return rowPosAt(index); +} - FxGridItemSG *snapItemAt(qreal pos) { - for (int i = 0; i < visibleItems.count(); ++i) { - FxGridItemSG *item = visibleItems[i]; - if (item->index == -1) - continue; - qreal itemTop = item->rowPos(); - if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos) - return item; - } - return 0; - } - - int snapIndex() { - int index = currentIndex; - for (int i = 0; i < visibleItems.count(); ++i) { - FxGridItemSG *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; - } +qreal QSGGridViewPrivate::endPosition() const +{ + return isRightToLeftTopToBottom() ? -originPosition()+1 : lastPosition(); - qreal headerSize() const { - if (!header) - return 0.0; +} - return flow == QSGGridView::LeftToRight - ? header->item->height() - : header->item->width(); - } +qreal QSGGridViewPrivate::endPositionAt(int index) const +{ + return rowPosAt(index) + rowSize(); +} +int QSGGridViewPrivate::rowSize() const { + return flow == QSGGridView::LeftToRight ? cellHeight : cellWidth; +} +int QSGGridViewPrivate::colSize() const { + return flow == QSGGridView::LeftToRight ? cellWidth : cellHeight; +} - virtual void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { - Q_Q(const QSGGridView); - QSGFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); - if (item == q) { - if (newGeometry.height() != oldGeometry.height() - || newGeometry.width() != oldGeometry.width()) { - if (q->isComponentComplete()) { - updateGrid(); - scheduleLayout(); - } - } - } else if ((header && header->item == item) || (footer && footer->item == item)) { - if (header) - updateHeader(); - if (footer) - updateFooter(); +qreal QSGGridViewPrivate::colPosAt(int modelIndex) const +{ + if (FxViewItem *item = visibleItem(modelIndex)) + return static_cast(item)->colPos(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = (visibleIndex - modelIndex) % columns; + int col = static_cast(visibleItems.first())->colPos() / colSize(); + col = (columns - count + col) % columns; + return col * colSize(); + } else { + int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns; + return static_cast(visibleItems.last())->colPos() - count * colSize(); } + } else { + return (modelIndex % columns) * colSize(); } + return 0; +} - void positionViewAtIndex(int index, int mode); - 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; - for (int i = 0; i < visibleItems.count(); ++i) { - FxGridItemSG *listItem = visibleItems.at(i); - if (listItem->index == -1) { - ++skip; - } else if (listItem->index != visibleIndex + i - skip) { - for (int j = 0; j < visibleItems.count(); j++) - qDebug() << " index" << j << "item index" << visibleItems.at(j)->index; - qFatal("index %d %d %d", visibleIndex, i, listItem->index); - } +qreal QSGGridViewPrivate::rowPosAt(int modelIndex) const +{ + if (FxViewItem *item = visibleItem(modelIndex)) + return static_cast(item)->rowPos(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + FxGridItemSG *firstItem = static_cast(visibleItems.first()); + int firstCol = firstItem->colPos() / colSize(); + int col = visibleIndex - modelIndex + (columns - firstCol - 1); + int rows = col / columns; + return firstItem->rowPos() - rows * rowSize(); + } else { + FxGridItemSG *lastItem = static_cast(visibleItems.last()); + int count = modelIndex - lastItem->index; + int col = lastItem->colPos() + count * colSize(); + int rows = col / (columns * colSize()); + return lastItem->rowPos() + rows * rowSize(); } + } else { + qreal pos = (modelIndex / columns) * rowSize(); + if (header) + pos += headerSize(); + return pos; } + return 0; +} - QDeclarativeGuard model; - QVariant modelVariant; - QList visibleItems; - QHash unrequestedItems; - FxGridItemSG *currentItem; - Qt::LayoutDirection layoutDirection; - QSGGridView::Flow flow; - int visibleIndex; - int currentIndex; - int cellWidth; - int cellHeight; - int columns; - int requestedIndex; - int itemCount; - qreal highlightRangeStart; - qreal highlightRangeEnd; - QSGGridView::HighlightRangeMode highlightRange; - QDeclarativeComponent *highlightComponent; - FxGridItemSG *highlight; - FxGridItemSG *trackedItem; - enum MovementReason { Other, SetIndex, Mouse }; - MovementReason moveReason; - int buffer; - QSmoothedAnimation *highlightXAnimator; - QSmoothedAnimation *highlightYAnimator; - int highlightMoveDuration; - QDeclarativeComponent *footerComponent; - FxGridItemSG *footer; - QDeclarativeComponent *headerComponent; - FxGridItemSG *header; - enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; - int bufferMode; - QSGGridView::SnapMode snapMode; - - bool ownModel : 1; - bool wrap : 1; - bool autoHighlight : 1; - bool fixCurrentVisibility : 1; - bool lazyRelease : 1; - bool layoutScheduled : 1; - bool deferredRelease : 1; - bool haveHighlightRange : 1; - bool currentIndexCleared : 1; - bool highlightRangeStartValid : 1; - bool highlightRangeEndValid : 1; -}; -void QSGGridViewPrivate::init() +qreal QSGGridViewPrivate::snapPosAt(qreal pos) const { - Q_Q(QSGGridView); - QSGItemPrivate::get(contentItem)->childrenDoNotOverlap = true; - QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); - q->setFlag(QSGItem::ItemIsFocusScope); - q->setFlickableDirection(QSGFlickable::VerticalFlick); - addItemChangeListener(this, Geometry); + Q_Q(const QSGGridView); + qreal snapPos = 0; + if (!visibleItems.isEmpty()) { + pos += rowSize()/2; + snapPos = static_cast(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize(); + snapPos = pos - fmodf(pos - snapPos, qreal(rowSize())); + qreal maxExtent; + qreal minExtent; + if (isRightToLeftTopToBottom()) { + maxExtent = q->minXExtent(); + minExtent = q->maxXExtent(); + } else { + maxExtent = flow == QSGGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); + minExtent = flow == QSGGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); + } + if (snapPos > maxExtent) + snapPos = maxExtent; + if (snapPos < minExtent) + snapPos = minExtent; + } + return snapPos; } -void QSGGridViewPrivate::clear() +FxViewItem *QSGGridViewPrivate::snapItemAt(qreal pos) const { - for (int i = 0; i < visibleItems.count(); ++i) - releaseItem(visibleItems.at(i)); - visibleItems.clear(); - visibleIndex = 0; - releaseItem(currentItem); - currentItem = 0; - createHighlight(); - trackedItem = 0; - itemCount = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == -1) + continue; + qreal itemTop = item->position(); + if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos) + return item; + } + return 0; } -FxGridItemSG *QSGGridViewPrivate::createItem(int modelIndex) +int QSGGridViewPrivate::snapIndex() const { - Q_Q(QSGGridView); - // create object - requestedIndex = modelIndex; - FxGridItemSG *listItem = 0; - if (QSGItem *item = model->item(modelIndex, false)) { - listItem = new FxGridItemSG(item, q); - listItem->index = modelIndex; - if (model->completePending()) { - // complete - listItem->item->setZ(1); - listItem->item->setParentItem(q->contentItem()); - model->completeItem(); - } else { - listItem->item->setParentItem(q->contentItem()); + int index = currentIndex; + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItemSG *item = static_cast(visibleItems.at(i)); + if (item->index == -1) + continue; + qreal itemTop = item->position(); + FxGridItemSG *hItem = static_cast(highlight); + if (itemTop >= hItem->rowPos()-rowSize()/2 && itemTop < hItem->rowPos()+rowSize()/2) { + index = item->index; + if (item->colPos() >= hItem->colPos()-colSize()/2 && item->colPos() < hItem->colPos()+colSize()/2) + return item->index; } - unrequestedItems.remove(listItem->item); } - requestedIndex = -1; - return listItem; + return index; } - -void QSGGridViewPrivate::releaseItem(FxGridItemSG *item) +FxViewItem *QSGGridViewPrivate::newViewItem(int modelIndex, QSGItem *item) { Q_Q(QSGGridView); - 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)); - } - delete item; + Q_UNUSED(modelIndex); + return new FxGridItemSG(item, q, false); } -void QSGGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) +bool QSGGridViewPrivate::addVisibleItems(int fillFrom, int fillTo, bool doBuffer) { - Q_Q(QSGGridView); - if (!isValid() || !q->isComponentComplete()) - return; - itemCount = model->count(); - qreal bufferFrom = from - buffer; - qreal bufferTo = to + buffer; - qreal fillFrom = from; - qreal fillTo = to; - if (doBuffer && (bufferMode & BufferAfter)) - fillTo = bufferTo; - if (doBuffer && (bufferMode & BufferBefore)) - fillFrom = bufferFrom; - - 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(); + FxGridItemSG *lastItem = static_cast(visibleItems.last()); + rowPos = lastItem->rowPos(); + colPos = lastItem->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; } + int modelIndex = findLastVisibleIndex(); + modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1; + if (visibleItems.count() && (fillFrom > rowPos + rowSize()*2 || fillTo < rowPosAt(visibleIndex) - rowSize())) { // We've jumped more than a page. Estimate which items are now @@ -601,15 +436,12 @@ void QSGGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) } int colNum = colPos / colSize(); - FxGridItemSG *item = 0; + bool changed = false; - // 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)) { // qDebug() << "refill: append item" << modelIndex; - if (!(item = createItem(modelIndex))) + if (!(item = static_cast(createItem(modelIndex)))) break; item->setPosition(colPos, rowPos); visibleItems.append(item); @@ -627,17 +459,19 @@ void QSGGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) } if (visibleItems.count()) { - rowPos = visibleItems.first()->rowPos(); - colPos = visibleItems.first()->colPos() - colSize(); + FxGridItemSG *firstItem = static_cast(visibleItems.first()); + rowPos = firstItem->rowPos(); + colPos = firstItem->colPos() - colSize(); if (colPos < 0) { colPos = colSize() * (columns - 1); rowPos -= rowSize(); } } + colNum = colPos / colSize(); while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){ // qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos; - if (!(item = createItem(visibleIndex-1))) + if (!(item = static_cast(createItem(visibleIndex-1)))) break; --visibleIndex; item->setPosition(colPos, rowPos); @@ -654,87 +488,69 @@ void QSGGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) break; } - if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create - while (visibleItems.count() > 1 - && (item = visibleItems.first()) - && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) { - if (item->attached->delayRemove()) - break; + return changed; +} + +bool QSGGridViewPrivate::removeNonVisibleItems(int bufferFrom, int bufferTo) +{ + FxGridItemSG *item = 0; + bool changed = false; + + while (visibleItems.count() > 1 + && (item = static_cast(visibleItems.first())) + && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) { + if (item->attached->delayRemove()) + break; // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos(); - if (item->index != -1) - visibleIndex++; - visibleItems.removeFirst(); - releaseItem(item); - changed = true; - } - while (visibleItems.count() > 1 - && (item = visibleItems.last()) - && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) { - if (item->attached->delayRemove()) - break; -// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1; - visibleItems.removeLast(); - releaseItem(item); - changed = true; - } - deferredRelease = false; - } else { - deferredRelease = true; + if (item->index != -1) + visibleIndex++; + visibleItems.removeFirst(); + releaseItem(item); + changed = true; } - if (changed) { - if (header) - updateHeader(); - if (footer) - updateFooter(); - if (flow == QSGGridView::LeftToRight) - q->setContentHeight(endPosition() - startPosition()); - else - q->setContentWidth(endPosition() - startPosition()); - } else if (!doBuffer && buffer && bufferMode != NoBuffer) { - refill(from, to, true); + while (visibleItems.count() > 1 + && (item = static_cast(visibleItems.last())) + && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) { + if (item->attached->delayRemove()) + break; +// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1; + visibleItems.removeLast(); + releaseItem(item); + changed = true; } - lazyRelease = false; + + return changed; } -void QSGGridViewPrivate::updateGrid() +void QSGGridViewPrivate::visibleItemsChanged() { Q_Q(QSGGridView); - columns = (int)qMax((flow == QSGGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); - if (isValid()) { - if (flow == QSGGridView::LeftToRight) - q->setContentHeight(endPosition() - startPosition()); - else - q->setContentWidth(lastPosition() - originPosition()); - } + + updateHeader(); + updateFooter(); + updateViewport(); } -void QSGGridViewPrivate::scheduleLayout() +void QSGGridViewPrivate::updateViewport() { Q_Q(QSGGridView); - if (!layoutScheduled) { - layoutScheduled = true; - q->polish(); - } + columns = (int)qMax((flow == QSGGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); + QSGItemViewPrivate::updateViewport(); } -void QSGGridViewPrivate::layout() +void QSGGridViewPrivate::layoutVisibleItems() { - Q_Q(QSGGridView); - layoutScheduled = false; - if (!isValid() && !visibleItems.count()) { - clear(); - return; - } if (visibleItems.count()) { - qreal rowPos = visibleItems.first()->rowPos(); - qreal colPos = visibleItems.first()->colPos(); + FxGridItemSG *firstItem = static_cast(visibleItems.first()); + qreal rowPos = firstItem->rowPos(); + qreal colPos = firstItem->colPos(); int col = visibleIndex % columns; if (colPos != col * colSize()) { colPos = col * colSize(); - visibleItems.first()->setPosition(colPos, rowPos); + firstItem->setPosition(colPos, rowPos); } for (int i = 1; i < visibleItems.count(); ++i) { - FxGridItemSG *item = visibleItems.at(i); + FxGridItemSG *item = static_cast(visibleItems.at(i)); colPos += colSize(); if (colPos > colSize() * (columns-1)) { colPos = 0; @@ -743,68 +559,25 @@ void QSGGridViewPrivate::layout() item->setPosition(colPos, rowPos); } } - if (header) - updateHeader(); - if (footer) - updateFooter(); - q->refill(); - updateHighlight(); - moveReason = Other; - if (flow == QSGGridView::LeftToRight) { - q->setContentHeight(endPosition() - startPosition()); - fixupY(); - } else { - q->setContentWidth(endPosition() - startPosition()); - fixupX(); - } - updateUnrequestedPositions(); } -void QSGGridViewPrivate::updateUnrequestedIndexes() +void QSGGridViewPrivate::repositionPackageItemAt(QSGItem *item, int index) { Q_Q(QSGGridView); - QHash::iterator it; - for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) - *it = model->indexOf(it.key(), q); -} - -void QSGGridViewPrivate::updateUnrequestedPositions() -{ - QHash::const_iterator it; - for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) { - QSGItem *item = it.key(); - if (flow == QSGGridView::LeftToRight) { - item->setPos(QPointF(colPosAt(*it), rowPosAt(*it))); - } else { + qreal pos = position(); + if (flow == QSGGridView::LeftToRight) { + if (item->y() + item->height() > pos && item->y() < pos + q->height()) + item->setPos(QPointF(colPosAt(index), rowPosAt(index))); + } else { + if (item->x() + item->width() > pos && item->x() < pos + q->width()) { if (isRightToLeftTopToBottom()) - item->setPos(QPointF(-rowPosAt(*it)-item->width(), colPosAt(*it))); + item->setPos(QPointF(-rowPosAt(index)-item->width(), colPosAt(index))); else - item->setPos(QPointF(rowPosAt(*it), colPosAt(*it))); + item->setPos(QPointF(rowPosAt(index), colPosAt(index))); } } } -void QSGGridViewPrivate::updateTrackedItem() -{ - Q_Q(QSGGridView); - FxGridItemSG *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 QSGGridViewPrivate::createHighlight() { @@ -813,51 +586,31 @@ void QSGGridViewPrivate::createHighlight() if (highlight) { if (trackedItem == highlight) trackedItem = 0; - highlight->item->setParentItem(0); - highlight->item->deleteLater(); delete highlight; highlight = 0; + delete highlightXAnimator; delete highlightYAnimator; highlightXAnimator = 0; highlightYAnimator = 0; + changed = true; } if (currentItem) { - QSGItem *item = 0; - if (highlightComponent) { - QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q)); - QObject *nobj = highlightComponent->create(highlightContext); - if (nobj) { - QDeclarative_setParent_noEvent(highlightContext, nobj); - item = qobject_cast(nobj); - if (!item) - delete nobj; - } else { - delete highlightContext; - } - } else { - item = new QSGItem; - QDeclarative_setParent_noEvent(item, q->contentItem()); - item->setParentItem(q->contentItem()); - } + QSGItem *item = createHighlightItem(); if (item) { - QDeclarative_setParent_noEvent(item, q->contentItem()); - item->setParentItem(q->contentItem()); - highlight = new FxGridItemSG(item, q); - if (currentItem && autoHighlight) - highlight->setPosition(currentItem->colPos(), currentItem->rowPos()); + FxGridItemSG *newHighlight = new FxGridItemSG(item, q, true); + if (autoHighlight) + resetHighlightPosition(); highlightXAnimator = new QSmoothedAnimation(q); - highlightXAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("x")); + highlightXAnimator->target = QDeclarativeProperty(item, QLatin1String("x")); highlightXAnimator->userDuration = highlightMoveDuration; highlightYAnimator = new QSmoothedAnimation(q); - highlightYAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("y")); + highlightYAnimator->target = QDeclarativeProperty(item, QLatin1String("y")); highlightYAnimator->userDuration = highlightMoveDuration; - if (autoHighlight) { - highlightXAnimator->restart(); - highlightYAnimator->restart(); - } + + highlight = newHighlight; changed = true; } } @@ -875,150 +628,131 @@ void QSGGridViewPrivate::updateHighlight() highlightYAnimator->to = currentItem->item->y(); highlight->item->setWidth(currentItem->item->width()); highlight->item->setHeight(currentItem->item->height()); + highlightXAnimator->restart(); highlightYAnimator->restart(); } updateTrackedItem(); } -void QSGGridViewPrivate::updateCurrent(int modelIndex) +void QSGGridViewPrivate::resetHighlightPosition() { - Q_Q(QSGGridView); - 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; + if (highlight && currentItem) { + FxGridItemSG *cItem = static_cast(currentItem); + static_cast(highlight)->setPosition(cItem->colPos(), cItem->rowPos()); } +} - if (currentItem && currentIndex == modelIndex) { - updateHighlight(); - return; - } +qreal QSGGridViewPrivate::headerSize() const +{ + if (!header) + return 0.0; + return flow == QSGGridView::LeftToRight ? header->item->height() : header->item->width(); +} - FxGridItemSG *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(); - emit q->currentIndexChanged(); - releaseItem(oldCurrentItem); +qreal QSGGridViewPrivate::footerSize() const +{ + if (!footer) + return 0.0; + return flow == QSGGridView::LeftToRight? footer->item->height() : footer->item->width(); } void QSGGridViewPrivate::updateFooter() { Q_Q(QSGGridView); - if (!footer && footerComponent) { - QSGItem *item = 0; - QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); - QObject *nobj = footerComponent->create(context); - if (nobj) { - QDeclarative_setParent_noEvent(context, nobj); - item = qobject_cast(nobj); - if (!item) - delete nobj; - } else { - delete context; - } - if (item) { - QDeclarative_setParent_noEvent(item, q->contentItem()); - item->setParentItem(q->contentItem()); - item->setZ(1); - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); - footer = new FxGridItemSG(item, q); - } + + if (!footer) { + QSGItem *item = createComponentItem(footerComponent, true); + if (!item) + return; + item->setZ(1); + footer = new FxGridItemSG(item, q, true); } - if (footer) { - qreal colOffset = 0; - qreal rowOffset; - if (isRightToLeftTopToBottom()) { - rowOffset = footer->item->width()-cellWidth; + + FxGridItemSG *gridItem = static_cast(footer); + qreal colOffset = 0; + qreal rowOffset; + if (isRightToLeftTopToBottom()) { + rowOffset = gridItem->item->width()-cellWidth; + } else { + rowOffset = 0; + if (q->effectiveLayoutDirection() == Qt::RightToLeft) + colOffset = gridItem->item->width()-cellWidth; + } + if (visibleItems.count()) { + qreal endPos = lastPosition(); + if (findLastVisibleIndex() == model->count()-1) { + gridItem->setPosition(colOffset, endPos + rowOffset); } else { - rowOffset = 0; - if (q->effectiveLayoutDirection() == Qt::RightToLeft) - colOffset = footer->item->width()-cellWidth; + qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size(); + if (endPos <= visiblePos || gridItem->endPosition() < endPos + rowOffset) + gridItem->setPosition(colOffset, endPos + rowOffset); } - if (visibleItems.count()) { - qreal endPos = lastPosition(); - if (lastVisibleIndex() == model->count()-1) { - footer->setPosition(colOffset, endPos + rowOffset); - } else { - qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size(); - if (endPos <= visiblePos || footer->endRowPos() < endPos + rowOffset) - footer->setPosition(colOffset, endPos + rowOffset); - } - } else { - qreal endPos = 0; - if (header) { - endPos += (flow == QSGGridView::LeftToRight) ? header->item->height() : header->item->width(); - } - footer->setPosition(colOffset, endPos); + } else { + qreal endPos = 0; + if (header) { + endPos += headerSize(); } + gridItem->setPosition(colOffset, endPos); } } void QSGGridViewPrivate::updateHeader() { Q_Q(QSGGridView); - if (!header && headerComponent) { - QSGItem *item = 0; - QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); - QObject *nobj = headerComponent->create(context); - if (nobj) { - QDeclarative_setParent_noEvent(context, nobj); - item = qobject_cast(nobj); - if (!item) - delete nobj; - } else { - delete context; - } - if (item) { - QDeclarative_setParent_noEvent(item, q->contentItem()); - item->setParentItem(q->contentItem()); - item->setZ(1); - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); - header = new FxGridItemSG(item, q); - } + + if (!header) { + QSGItem *item = createComponentItem(headerComponent, true); + if (!item) + return; + item->setZ(1); + header = new FxGridItemSG(item, q, true); } - if (header) { - qreal colOffset = 0; - qreal rowOffset; - if (isRightToLeftTopToBottom()) { - rowOffset = -cellWidth; + + FxGridItemSG *gridItem = static_cast(header); + qreal colOffset = 0; + qreal rowOffset; + if (isRightToLeftTopToBottom()) { + rowOffset = -cellWidth; + } else { + rowOffset = -headerSize(); + if (q->effectiveLayoutDirection() == Qt::RightToLeft) + colOffset = gridItem->item->width()-cellWidth; + } + if (visibleItems.count()) { + qreal startPos = originPosition(); + if (visibleIndex == 0) { + gridItem->setPosition(colOffset, startPos + rowOffset); } else { - rowOffset = -headerSize(); - if (q->effectiveLayoutDirection() == Qt::RightToLeft) - colOffset = header->item->width()-cellWidth; + qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position(); + qreal headerPos = isRightToLeftTopToBottom() ? gridItem->rowPos() + cellWidth - headerSize() : gridItem->rowPos(); + if (tempPos <= startPos || headerPos > startPos + rowOffset) + gridItem->setPosition(colOffset, startPos + rowOffset); } - if (visibleItems.count()) { - qreal startPos = originPosition(); - if (visibleIndex == 0) { - header->setPosition(colOffset, startPos + rowOffset); - } else { - qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position(); - qreal headerPos = isRightToLeftTopToBottom() ? header->rowPos() + cellWidth - headerSize() : header->rowPos(); - if (tempPos <= startPos || headerPos > startPos + rowOffset) - header->setPosition(colOffset, startPos + rowOffset); - } - } else { - header->setPosition(colOffset, 0); + } else { + gridItem->setPosition(colOffset, 0); + } +} + +void QSGGridViewPrivate::initializeCurrentItem() +{ + if (currentItem && currentIndex >= 0) { + FxGridItemSG *gridItem = static_cast(currentItem); + if (gridItem) + gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex)); + } +} + +void QSGGridViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QSGGridView); + QSGItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + if (!q->isComponentComplete()) + return; + if (item == q) { + if (newGeometry.height() != oldGeometry.height() || newGeometry.width() != oldGeometry.width()) { + updateViewport(); + scheduleLayout(); } } } @@ -1056,37 +790,37 @@ void QSGGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) if (snapMode != QSGGridView::NoSnap) { qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position(); - FxGridItemSG *topItem = snapItemAt(tempPosition+highlightStart); - FxGridItemSG *bottomItem = snapItemAt(tempPosition+highlightEnd); + FxViewItem *topItem = snapItemAt(tempPosition+highlightStart); + FxViewItem *bottomItem = snapItemAt(tempPosition+highlightEnd); qreal pos; if (topItem && bottomItem && haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange) { - qreal topPos = qMin(topItem->rowPos() - highlightStart, -maxExtent); - qreal bottomPos = qMax(bottomItem->rowPos() - highlightEnd, -minExtent); + qreal topPos = qMin(topItem->position() - highlightStart, -maxExtent); + qreal bottomPos = qMax(bottomItem->position() - highlightEnd, -minExtent); pos = qAbs(data.move + topPos) < qAbs(data.move + bottomPos) ? topPos : bottomPos; } else if (topItem) { qreal headerPos = 0; if (header) - headerPos = isRightToLeftTopToBottom() ? header->rowPos() + cellWidth - headerSize() : header->rowPos(); + headerPos = isRightToLeftTopToBottom() ? static_cast(header)->rowPos() + cellWidth - headerSize() : static_cast(header)->rowPos(); if (topItem->index == 0 && header && tempPosition+highlightStart < headerPos+headerSize()/2) { pos = isRightToLeftTopToBottom() ? - headerPos + highlightStart - size() : headerPos - highlightStart; } else { if (isRightToLeftTopToBottom()) - pos = qMax(qMin(-topItem->rowPos() + highlightStart - size(), -maxExtent), -minExtent); + pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent); else - pos = qMax(qMin(topItem->rowPos() - highlightStart, -maxExtent), -minExtent); + pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent); } } else if (bottomItem) { if (isRightToLeftTopToBottom()) - pos = qMax(qMin(-bottomItem->rowPos() + highlightStart - size(), -maxExtent), -minExtent); + pos = qMax(qMin(-bottomItem->position() + highlightStart - size(), -maxExtent), -minExtent); else - pos = qMax(qMin(bottomItem->rowPos() - highlightStart, -maxExtent), -minExtent); + pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent); } else { - QSGFlickablePrivate::fixup(data, minExtent, maxExtent); + QSGItemViewPrivate::fixup(data, minExtent, maxExtent); return; } if (currentItem && haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange) { updateHighlight(); - qreal currPos = currentItem->rowPos(); + qreal currPos = static_cast(currentItem)->rowPos(); if (isRightToLeftTopToBottom()) pos = -pos-size(); // Transform Pos if required if (pos < currPos + rowSize() - highlightEnd) @@ -1111,7 +845,7 @@ void QSGGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) } else if (haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange) { if (currentItem) { updateHighlight(); - qreal pos = currentItem->rowPos(); + qreal pos = static_cast(currentItem)->rowPos(); if (viewPos < pos + rowSize() - highlightEnd) viewPos = pos + rowSize() - highlightEnd; if (viewPos > pos - highlightStart) @@ -1130,7 +864,7 @@ void QSGGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) vTime = timeline.time(); } } else { - QSGFlickablePrivate::fixup(data, minExtent, maxExtent); + QSGItemViewPrivate::fixup(data, minExtent, maxExtent); } data.inOvershoot = false; fixupMode = Normal; @@ -1144,7 +878,7 @@ void QSGGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, moveReason = Mouse; if ((!haveHighlightRange || highlightRange != QSGGridView::StrictlyEnforceRange) && snapMode == QSGGridView::NoSnap) { - QSGFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); + QSGItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); return; } qreal maxDistance = 0; @@ -1153,8 +887,8 @@ void QSGGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, if (velocity > 0) { if (data.move.value() < minExtent) { if (snapMode == QSGGridView::SnapOneRow) { - if (FxGridItemSG *item = firstVisibleItem()) - maxDistance = qAbs(item->rowPos() + dataValue); + if (FxViewItem *item = firstVisibleItem()) + maxDistance = qAbs(item->position() + dataValue); } else { maxDistance = qAbs(minExtent - data.move.value()); } @@ -1238,338 +972,38 @@ void QSGGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, //---------------------------------------------------------------------------- QSGGridView::QSGGridView(QSGItem *parent) - : QSGFlickable(*(new QSGGridViewPrivate), parent) -{ - Q_D(QSGGridView); - d->init(); -} - -QSGGridView::~QSGGridView() -{ - Q_D(QSGGridView); - d->clear(); - if (d->ownModel) - delete d->model; - delete d->header; - delete d->footer; -} - -// For internal use -int QSGGridView::modelCount() const -{ - Q_D(const QSGGridView); - return d->model->count(); -} - -QVariant QSGGridView::model() const -{ - Q_D(const QSGGridView); - return d->modelVariant; -} - -void QSGGridView::setModel(const QVariant &model) -{ - Q_D(QSGGridView); - if (d->modelVariant == model) - return; - if (d->model) { - disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); - disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); - disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); - disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); - disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); - disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*))); - } - d->clear(); - d->modelVariant = model; - QObject *object = qvariant_cast(model); - QSGVisualModel *vim = 0; - if (object && (vim = qobject_cast(object))) { - if (d->ownModel) { - delete d->model; - d->ownModel = false; - } - d->model = vim; - } else { - if (!d->ownModel) { - d->model = new QSGVisualDataModel(qmlContext(this), this); - d->ownModel = true; - } - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - dataModel->setModel(model); - } - if (d->model) { - d->bufferMode = QSGGridViewPrivate::BufferBefore | QSGGridViewPrivate::BufferAfter; - if (isComponentComplete()) { - refill(); - if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { - setCurrentIndex(0); - } else { - d->moveReason = QSGGridViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); - d->updateTrackedItem(); - } - d->moveReason = QSGGridViewPrivate::Other; - } - } - 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))); - connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); - connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); - connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*))); - emit countChanged(); - } - emit modelChanged(); -} - -QDeclarativeComponent *QSGGridView::delegate() const -{ - Q_D(const QSGGridView); - if (d->model) { - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - return dataModel->delegate(); - } - - return 0; -} - -void QSGGridView::setDelegate(QDeclarativeComponent *delegate) + : QSGItemView(*(new QSGGridViewPrivate), parent) { - Q_D(QSGGridView); - if (delegate == this->delegate()) - return; - - if (!d->ownModel) { - d->model = new QSGVisualDataModel(qmlContext(this)); - d->ownModel = true; - } - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) { - int oldCount = dataModel->count(); - dataModel->setDelegate(delegate); - if (isComponentComplete()) { - for (int i = 0; i < d->visibleItems.count(); ++i) - d->releaseItem(d->visibleItems.at(i)); - d->visibleItems.clear(); - d->releaseItem(d->currentItem); - d->currentItem = 0; - refill(); - d->moveReason = QSGGridViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); - d->updateTrackedItem(); - } - d->moveReason = QSGGridViewPrivate::Other; - } - if (oldCount != dataModel->count()) - emit countChanged(); - emit delegateChanged(); - } -} - -int QSGGridView::currentIndex() const -{ - Q_D(const QSGGridView); - return d->currentIndex; -} - -void QSGGridView::setCurrentIndex(int index) -{ - Q_D(QSGGridView); - if (d->requestedIndex >= 0) // currently creating item - return; - d->currentIndexCleared = (index == -1); - if (index == d->currentIndex) - return; - if (isComponentComplete() && d->isValid()) { - d->moveReason = QSGGridViewPrivate::SetIndex; - d->updateCurrent(index); - } else { - d->currentIndex = index; - emit currentIndexChanged(); - } -} - -QSGItem *QSGGridView::currentItem() -{ - Q_D(QSGGridView); - if (!d->currentItem) - return 0; - return d->currentItem->item; -} - -QSGItem *QSGGridView::highlightItem() -{ - Q_D(QSGGridView); - if (!d->highlight) - return 0; - return d->highlight->item; -} - -int QSGGridView::count() const -{ - Q_D(const QSGGridView); - if (d->model) - return d->model->count(); - return 0; -} - -QDeclarativeComponent *QSGGridView::highlight() const -{ - Q_D(const QSGGridView); - return d->highlightComponent; -} - -void QSGGridView::setHighlight(QDeclarativeComponent *highlight) -{ - Q_D(QSGGridView); - if (highlight != d->highlightComponent) { - d->highlightComponent = highlight; - d->updateCurrent(d->currentIndex); - emit highlightChanged(); - } } -bool QSGGridView::highlightFollowsCurrentItem() const +QSGGridView::~QSGGridView() { - Q_D(const QSGGridView); - return d->autoHighlight; } void QSGGridView::setHighlightFollowsCurrentItem(bool autoHighlight) { Q_D(QSGGridView); if (d->autoHighlight != autoHighlight) { - d->autoHighlight = autoHighlight; - if (autoHighlight) { - d->updateHighlight(); - } else if (d->highlightXAnimator) { + if (!autoHighlight && d->highlightXAnimator) { d->highlightXAnimator->stop(); d->highlightYAnimator->stop(); } + QSGItemView::setHighlightFollowsCurrentItem(autoHighlight); } } -int QSGGridView::highlightMoveDuration() const -{ - Q_D(const QSGGridView); - return d->highlightMoveDuration; -} - void QSGGridView::setHighlightMoveDuration(int duration) { Q_D(QSGGridView); if (d->highlightMoveDuration != duration) { - d->highlightMoveDuration = duration; if (d->highlightYAnimator) { - d->highlightXAnimator->userDuration = d->highlightMoveDuration; - d->highlightYAnimator->userDuration = d->highlightMoveDuration; + d->highlightXAnimator->userDuration = duration; + d->highlightYAnimator->userDuration = duration; } - emit highlightMoveDurationChanged(); - } -} - -qreal QSGGridView::preferredHighlightBegin() const -{ - Q_D(const QSGGridView); - return d->highlightRangeStart; -} - -void QSGGridView::setPreferredHighlightBegin(qreal start) -{ - Q_D(QSGGridView); - d->highlightRangeStartValid = true; - if (d->highlightRangeStart == start) - return; - d->highlightRangeStart = start; - d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - emit preferredHighlightBeginChanged(); -} - -void QSGGridView::resetPreferredHighlightBegin() -{ - Q_D(QSGGridView); - d->highlightRangeStartValid = false; - if (d->highlightRangeStart == 0) - return; - d->highlightRangeStart = 0; - emit preferredHighlightBeginChanged(); -} - -qreal QSGGridView::preferredHighlightEnd() const -{ - Q_D(const QSGGridView); - return d->highlightRangeEnd; -} - -void QSGGridView::setPreferredHighlightEnd(qreal end) -{ - Q_D(QSGGridView); - d->highlightRangeEndValid = true; - if (d->highlightRangeEnd == end) - return; - d->highlightRangeEnd = end; - d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - emit preferredHighlightEndChanged(); -} - -void QSGGridView::resetPreferredHighlightEnd() -{ - Q_D(QSGGridView); - d->highlightRangeEndValid = false; - if (d->highlightRangeEnd == 0) - return; - d->highlightRangeEnd = 0; - emit preferredHighlightEndChanged(); -} - -QSGGridView::HighlightRangeMode QSGGridView::highlightRangeMode() const -{ - Q_D(const QSGGridView); - return d->highlightRange; -} - -void QSGGridView::setHighlightRangeMode(HighlightRangeMode mode) -{ - Q_D(QSGGridView); - if (d->highlightRange == mode) - return; - d->highlightRange = mode; - d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - emit highlightRangeModeChanged(); -} - -Qt::LayoutDirection QSGGridView::layoutDirection() const -{ - Q_D(const QSGGridView); - return d->layoutDirection; -} - -void QSGGridView::setLayoutDirection(Qt::LayoutDirection layoutDirection) -{ - Q_D(QSGGridView); - if (d->layoutDirection != layoutDirection) { - d->layoutDirection = layoutDirection; - d->regenerate(); - emit layoutDirectionChanged(); - emit effectiveLayoutDirectionChanged(); + QSGItemView::setHighlightMoveDuration(duration); } } -Qt::LayoutDirection QSGGridView::effectiveLayoutDirection() const -{ - Q_D(const QSGGridView); - if (d->effectiveLayoutMirror) - return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; - else - return d->layoutDirection; -} - QSGGridView::Flow QSGGridView::flow() const { Q_D(const QSGGridView); @@ -1583,10 +1017,10 @@ void QSGGridView::setFlow(Flow flow) d->flow = flow; if (d->flow == LeftToRight) { setContentWidth(-1); - setFlickableDirection(QSGFlickable::VerticalFlick); + setFlickableDirection(VerticalFlick); } else { setContentHeight(-1); - setFlickableDirection(QSGFlickable::HorizontalFlick); + setFlickableDirection(HorizontalFlick); } setContentX(0); setContentY(0); @@ -1595,37 +1029,6 @@ void QSGGridView::setFlow(Flow flow) } } -bool QSGGridView::isWrapEnabled() const -{ - Q_D(const QSGGridView); - return d->wrap; -} - -void QSGGridView::setWrapEnabled(bool wrap) -{ - Q_D(QSGGridView); - if (d->wrap == wrap) - return; - d->wrap = wrap; - emit keyNavigationWrapsChanged(); -} - -int QSGGridView::cacheBuffer() const -{ - Q_D(const QSGGridView); - return d->buffer; -} - -void QSGGridView::setCacheBuffer(int buffer) -{ - Q_D(QSGGridView); - if (d->buffer != buffer) { - d->buffer = buffer; - if (isComponentComplete()) - refill(); - emit cacheBufferChanged(); - } -} int QSGGridView::cellWidth() const { @@ -1638,9 +1041,9 @@ void QSGGridView::setCellWidth(int cellWidth) Q_D(QSGGridView); if (cellWidth != d->cellWidth && cellWidth > 0) { d->cellWidth = qMax(1, cellWidth); - d->updateGrid(); + d->updateViewport(); emit cellWidthChanged(); - d->layout(); + d->layoutVisibleItems(); } } @@ -1655,9 +1058,9 @@ void QSGGridView::setCellHeight(int cellHeight) Q_D(QSGGridView); if (cellHeight != d->cellHeight && cellHeight > 0) { d->cellHeight = qMax(1, cellHeight); - d->updateGrid(); + d->updateViewport(); emit cellHeightChanged(); - d->layout(); + d->layoutVisibleItems(); } } @@ -1676,90 +1079,17 @@ void QSGGridView::setSnapMode(SnapMode mode) } } -QDeclarativeComponent *QSGGridView::footer() const -{ - Q_D(const QSGGridView); - return d->footerComponent; -} - -void QSGGridView::setFooter(QDeclarativeComponent *footer) -{ - Q_D(QSGGridView); - if (d->footerComponent != footer) { - if (d->footer) { - // XXX todo - the original did scene()->removeItem(). Why? - d->footer->item->setParentItem(0); - d->footer->item->deleteLater(); - delete d->footer; - d->footer = 0; - } - d->footerComponent = footer; - if (isComponentComplete()) { - d->updateFooter(); - d->updateGrid(); - d->fixupPosition(); - } - emit footerChanged(); - } -} - -QDeclarativeComponent *QSGGridView::header() const -{ - Q_D(const QSGGridView); - return d->headerComponent; -} - -void QSGGridView::setHeader(QDeclarativeComponent *header) -{ - Q_D(QSGGridView); - if (d->headerComponent != header) { - if (d->header) { - // XXX todo - the original did scene()->removeItem(). Why? - d->header->item->setParentItem(0); - d->header->item->deleteLater(); - delete d->header; - d->header = 0; - } - d->headerComponent = header; - if (isComponentComplete()) { - d->updateHeader(); - d->updateFooter(); - d->updateGrid(); - d->fixupPosition(); - } - emit headerChanged(); - } -} - -void QSGGridView::setContentX(qreal pos) -{ - Q_D(QSGGridView); - // Positioning the view manually should override any current movement state - d->moveReason = QSGGridViewPrivate::Other; - QSGFlickable::setContentX(pos); -} - -void QSGGridView::setContentY(qreal pos) -{ - Q_D(QSGGridView); - // Positioning the view manually should override any current movement state - d->moveReason = QSGGridViewPrivate::Other; - QSGFlickable::setContentY(pos); -} - -void QSGGridView::updatePolish() -{ - Q_D(QSGGridView); - QSGFlickable::updatePolish(); - d->layout(); -} void QSGGridView::viewportMoved() { Q_D(QSGGridView); - QSGFlickable::viewportMoved(); + QSGItemView::viewportMoved(); if (!d->itemCount) return; + if (d->inViewportMoved) + return; + d->inViewportMoved = true; + d->lazyRelease = true; if (d->flickingHorizontally || d->flickingVertically) { if (yflick()) { @@ -1776,36 +1106,37 @@ void QSGGridView::viewportMoved() d->bufferMode = QSGGridViewPrivate::BufferAfter; } } - refill(); + d->refill(); if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically) d->moveReason = QSGGridViewPrivate::Mouse; if (d->moveReason != QSGGridViewPrivate::SetIndex) { if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { // reposition highlight - qreal pos = d->highlight->rowPos(); + qreal pos = d->highlight->position(); qreal viewPos; qreal highlightStart; qreal highlightEnd; if (d->isRightToLeftTopToBottom()) { + viewPos = -d->position()-d->size(); highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; - viewPos = -d->position()-d->size(); } else { + viewPos = d->position(); highlightStart = d->highlightRangeStart; highlightEnd = d->highlightRangeEnd; - viewPos = d->position(); } - if (pos > viewPos + highlightEnd - d->rowSize()) - pos = viewPos + highlightEnd - d->rowSize(); + if (pos > viewPos + highlightEnd - d->highlight->size()) + pos = viewPos + highlightEnd - d->highlight->size(); if (pos < viewPos + highlightStart) pos = viewPos + highlightStart; - d->highlight->setPosition(d->highlight->colPos(), qRound(pos)); + + static_cast(d->highlight)->setPosition(static_cast(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->currentItem && static_cast(d->currentItem)->colPos() != static_cast(d->highlight)->colPos() && d->autoHighlight) { if (d->flow == LeftToRight) d->highlightXAnimator->to = d->currentItem->item->x(); else @@ -1814,120 +1145,8 @@ void QSGGridView::viewportMoved() } } } -} - -qreal QSGGridView::minYExtent() const -{ - Q_D(const QSGGridView); - if (d->flow == QSGGridView::TopToBottom) - return QSGFlickable::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 QSGGridView::maxYExtent() const -{ - Q_D(const QSGGridView); - if (d->flow == QSGGridView::TopToBottom) - return QSGFlickable::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 QSGGridView::minXExtent() const -{ - Q_D(const QSGGridView); - if (d->flow == QSGGridView::LeftToRight) - return QSGFlickable::minXExtent(); - qreal extent = -d->startPosition(); - qreal highlightStart; - qreal highlightEnd; - qreal endPositionFirstItem; - if (d->isRightToLeftTopToBottom()) { - endPositionFirstItem = d->rowPosAt(d->model->count()-1); - highlightStart = d->highlightRangeStartValid - ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem) - : d->size() - (d->lastPosition()-endPositionFirstItem); - highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size(); - if (d->footer && d->visibleItems.count()) - extent += d->footer->item->width(); - } else { - endPositionFirstItem = d->rowPosAt(0)+d->rowSize(); - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - if (d->header && d->visibleItems.count()) - extent += d->header->item->width(); - } - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - extent += highlightStart; - extent = qMax(extent, -(endPositionFirstItem - highlightEnd)); - } - return extent; -} - -qreal QSGGridView::maxXExtent() const -{ - Q_D(const QSGGridView); - if (d->flow == QSGGridView::LeftToRight) - return QSGFlickable::maxXExtent(); - qreal extent; - qreal highlightStart; - qreal highlightEnd; - qreal lastItemPosition = 0; - if (d->isRightToLeftTopToBottom()){ - highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size(); - highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size(); - lastItemPosition = d->endPosition(); - } else { - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - lastItemPosition = 0; - if (d->model && d->model->count()) - lastItemPosition = d->rowPosAt(d->model->count()-1); - } - if (!d->model || !d->model->count()) { - extent = 0; - } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - extent = -(lastItemPosition - highlightStart); - if (highlightEnd != highlightStart) - extent = d->isRightToLeftTopToBottom() - ? qMax(extent, -(d->endPosition() - highlightEnd + 1)) - : qMin(extent, -(d->endPosition() - highlightEnd + 1)); - } else { - extent = -(d->endPosition() - width()); - } - if (d->isRightToLeftTopToBottom()) { - if (d->header) - extent -= d->header->item->width(); - } else { - if (d->footer) - extent -= d->footer->item->width(); - } - const qreal minX = minXExtent(); - if (extent > minX) - extent = minX; - return extent; + d->inViewportMoved = false; } void QSGGridView::keyPressEvent(QKeyEvent *event) @@ -1959,7 +1178,7 @@ void QSGGridView::keyPressEvent(QKeyEvent *event) } d->moveReason = QSGGridViewPrivate::Other; event->ignore(); - QSGFlickable::keyPressEvent(event); + QSGItemView::keyPressEvent(event); } void QSGGridView::moveCurrentIndexUp() @@ -2066,214 +1285,6 @@ void QSGGridView::moveCurrentIndexRight() } } -void QSGGridViewPrivate::positionViewAtIndex(int index, int mode) -{ - Q_Q(QSGGridView); - if (!isValid()) - return; - if (mode < QSGGridView::Beginning || mode > QSGGridView::Contain) - return; - - int idx = qMax(qMin(index, model->count()-1), 0); - - if (layoutScheduled) - layout(); - qreal pos = isRightToLeftTopToBottom() ? -position() - size() : position(); - FxGridItemSG *item = visibleItem(idx); - qreal maxExtent; - if (flow == QSGGridView::LeftToRight) - maxExtent = -q->maxYExtent(); - else - maxExtent = isRightToLeftTopToBottom() ? q->minXExtent()-size() : -q->maxXExtent(); - if (!item) { - int itemPos = rowPosAt(idx); - // save the currently visible items in case any of them end up visible again - QList oldVisible = visibleItems; - visibleItems.clear(); - visibleIndex = idx - idx % columns; - if (flow == QSGGridView::LeftToRight) - maxExtent = -q->maxYExtent(); - else - maxExtent = isRightToLeftTopToBottom() ? q->minXExtent()-size() : -q->maxXExtent(); - setPosition(qMin(qreal(itemPos), maxExtent)); - // now release the reference to all the old visible items. - for (int i = 0; i < oldVisible.count(); ++i) - releaseItem(oldVisible.at(i)); - item = visibleItem(idx); - } - if (item) { - qreal itemPos = item->rowPos(); - switch (mode) { - case QSGGridView::Beginning: - pos = itemPos; - if (index < 0 && header) { - pos -= flow == QSGGridView::LeftToRight - ? header->item->height() - : header->item->width(); - } - break; - case QSGGridView::Center: - pos = itemPos - (size() - rowSize())/2; - break; - case QSGGridView::End: - pos = itemPos - size() + rowSize(); - if (index >= model->count() && footer) { - pos += flow == QSGGridView::LeftToRight - ? footer->item->height() - : footer->item->width(); - } - break; - case QSGGridView::Visible: - if (itemPos > pos + size()) - pos = itemPos - size() + rowSize(); - else if (item->endRowPos() < pos) - pos = itemPos; - break; - case QSGGridView::Contain: - if (item->endRowPos() > pos + size()) - pos = itemPos - size() + rowSize(); - if (itemPos < pos) - pos = itemPos; - } - pos = qMin(pos, maxExtent); - qreal minExtent; - if (flow == QSGGridView::LeftToRight) - minExtent = -q->minYExtent(); - else - minExtent = isRightToLeftTopToBottom() ? q->maxXExtent()-size() : -q->minXExtent(); - pos = qMax(pos, minExtent); - moveReason = QSGGridViewPrivate::Other; - q->cancelFlick(); - setPosition(pos); - } - fixupPosition(); -} - -void QSGGridView::positionViewAtIndex(int index, int mode) -{ - Q_D(QSGGridView); - if (!d->isValid() || index < 0 || index >= d->model->count()) - return; - d->positionViewAtIndex(index, mode); -} - -void QSGGridView::positionViewAtBeginning() -{ - Q_D(QSGGridView); - if (!d->isValid()) - return; - d->positionViewAtIndex(-1, Beginning); -} - -void QSGGridView::positionViewAtEnd() -{ - Q_D(QSGGridView); - if (!d->isValid()) - return; - d->positionViewAtIndex(d->model->count(), End); -} - -int QSGGridView::indexAt(qreal x, qreal y) const -{ - Q_D(const QSGGridView); - for (int i = 0; i < d->visibleItems.count(); ++i) { - const FxGridItemSG *listItem = d->visibleItems.at(i); - if(listItem->contains(x, y)) - return listItem->index; - } - - return -1; -} - -void QSGGridView::componentComplete() -{ - Q_D(QSGGridView); - QSGFlickable::componentComplete(); - d->updateHeader(); - d->updateFooter(); - d->updateGrid(); - if (d->isValid()) { - refill(); - d->moveReason = QSGGridViewPrivate::SetIndex; - if (d->currentIndex < 0 && !d->currentIndexCleared) - d->updateCurrent(0); - else - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); - d->updateTrackedItem(); - } - d->moveReason = QSGGridViewPrivate::Other; - d->fixupPosition(); - } -} - -void QSGGridView::trackedPositionChanged() -{ - Q_D(QSGGridView); - if (!d->trackedItem || !d->currentItem) - return; - if (d->moveReason == QSGGridViewPrivate::SetIndex) { - const qreal trackedPos = d->trackedItem->rowPos(); - qreal viewPos; - qreal highlightStart; - qreal highlightEnd; - if (d->isRightToLeftTopToBottom()) { - viewPos = -d->position()-d->size(); - highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; - highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; - } else { - viewPos = d->position(); - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - } - qreal pos = viewPos; - if (d->haveHighlightRange) { - if (d->highlightRange == StrictlyEnforceRange) { - if (trackedPos > pos + highlightEnd - d->rowSize()) - pos = trackedPos - highlightEnd + d->rowSize(); - if (trackedPos < pos + highlightStart) - pos = trackedPos - highlightStart; - } else { - if (trackedPos < d->startPosition() + highlightStart) { - pos = d->startPosition(); - } else if (d->trackedItem->endRowPos() > d->endPosition() - d->size() + highlightEnd) { - pos = d->endPosition() - d->size() + 1; - if (pos < d->startPosition()) - pos = d->startPosition(); - } else { - if (trackedPos < viewPos + highlightStart) { - pos = trackedPos - highlightStart; - } else if (trackedPos > viewPos + highlightEnd - d->rowSize()) { - pos = trackedPos - highlightEnd + d->rowSize(); - } - } - } - } else { - if (trackedPos < viewPos && d->currentItem->rowPos() < viewPos) { - pos = qMax(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 QSGGridView::itemsInserted(int modelIndex, int count) { @@ -2294,9 +1305,9 @@ void QSGGridView::itemsInserted(int modelIndex, int count) // Insert before visible items d->visibleIndex += count; for (int i = 0; i < d->visibleItems.count(); ++i) { - FxGridItemSG *listItem = d->visibleItems.at(i); - if (listItem->index != -1 && listItem->index >= modelIndex) - listItem->index += count; + FxViewItem *item = d->visibleItems.at(i); + if (item->index != -1 && item->index >= modelIndex) + item->index += count; } } if (d->currentIndex >= modelIndex) { @@ -2327,12 +1338,14 @@ void QSGGridView::itemsInserted(int modelIndex, int count) if (d->visibleItems.count()) { index -= d->visibleIndex; if (index < d->visibleItems.count()) { - colPos = d->visibleItems.at(index)->colPos(); - rowPos = d->visibleItems.at(index)->rowPos(); + FxGridItemSG *gridItem = static_cast(d->visibleItems.at(index)); + colPos = gridItem->colPos(); + rowPos = gridItem->rowPos(); } else { // appending items to visible list - colPos = d->visibleItems.at(index-1)->colPos() + d->colSize(); - rowPos = d->visibleItems.at(index-1)->rowPos(); + FxGridItemSG *gridItem = static_cast(d->visibleItems.at(index-1)); + colPos = gridItem->colPos() + d->colSize(); + rowPos = gridItem->rowPos(); if (colPos > d->colSize() * (d->columns-1)) { colPos = 0; rowPos += d->rowSize(); @@ -2344,9 +1357,9 @@ void QSGGridView::itemsInserted(int modelIndex, int count) // Update the indexes of the following visible items. for (int i = 0; i < d->visibleItems.count(); ++i) { - FxGridItemSG *listItem = d->visibleItems.at(i); - if (listItem->index != -1 && listItem->index >= modelIndex) - listItem->index += count; + FxViewItem *item = d->visibleItems.at(i); + if (item->index != -1 && item->index >= modelIndex) + item->index += count; } bool addedVisible = false; @@ -2357,7 +1370,7 @@ void QSGGridView::itemsInserted(int modelIndex, int count) d->scheduleLayout(); addedVisible = true; } - FxGridItemSG *item = d->createItem(modelIndex + i); + FxGridItemSG *item = static_cast(d->createItem(modelIndex + i)); d->visibleItems.insert(index, item); item->setPosition(colPos, rowPos); added.append(item); @@ -2379,7 +1392,7 @@ void QSGGridView::itemsInserted(int modelIndex, int count) // update visibleIndex d->visibleIndex = 0; - for (QList::Iterator it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { + for (QList::Iterator it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { if ((*it)->index != -1) { d->visibleIndex = (*it)->index; break; @@ -2391,7 +1404,7 @@ void QSGGridView::itemsInserted(int modelIndex, int count) d->currentIndex += count; if (d->currentItem) { d->currentItem->index = d->currentIndex; - d->currentItem->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex)); + static_cast(d->currentItem)->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex)); } emit currentIndexChanged(); } else if (d->itemCount == 0 && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) { @@ -2417,9 +1430,9 @@ void QSGGridView::itemsRemoved(int modelIndex, int count) bool removedVisible = false; // Remove the items from the visible list, skipping anything already marked for removal - QList::Iterator it = d->visibleItems.begin(); + QList::Iterator it = d->visibleItems.begin(); while (it != d->visibleItems.end()) { - FxGridItemSG *item = *it; + FxViewItem *item = *it; if (item->index == -1 || item->index < modelIndex) { // already removed, or before removed items if (item->index < modelIndex && !removedVisible) { @@ -2487,23 +1500,6 @@ void QSGGridView::itemsRemoved(int modelIndex, int count) emit countChanged(); } -void QSGGridView::destroyRemoved() -{ - Q_D(QSGGridView); - for (QList::Iterator it = d->visibleItems.begin(); - it != d->visibleItems.end();) { - FxGridItemSG *listItem = *it; - if (listItem->index == -1 && listItem->attached->delayRemove() == false) { - d->releaseItem(listItem); - it = d->visibleItems.erase(it); - } else { - ++it; - } - } - - // Correct the positioning of the items - d->layout(); -} void QSGGridView::itemsMoved(int from, int to, int count) { @@ -2512,15 +1508,15 @@ void QSGGridView::itemsMoved(int from, int to, int count) return; QHash moved; - FxGridItemSG *firstItem = d->firstVisibleItem(); + FxGridItemSG *firstItem = static_cast(d->firstVisibleItem()); - QList::Iterator it = d->visibleItems.begin(); + QList::Iterator it = d->visibleItems.begin(); while (it != d->visibleItems.end()) { - FxGridItemSG *item = *it; + FxViewItem *item = *it; if (item->index >= from && item->index < from + count) { // take the items that are moving item->index += (to-from); - moved.insert(item->index, item); + moved.insert(item->index, static_cast(item)); it = d->visibleItems.erase(it); } else { if (item->index > from && item->index != -1) { @@ -2537,12 +1533,12 @@ void QSGGridView::itemsMoved(int from, int to, int count) int endIndex = d->visibleIndex; it = d->visibleItems.begin(); while (it != d->visibleItems.end()) { - FxGridItemSG *item = *it; + FxViewItem *item = *it; if (remaining && item->index >= to && item->index < to + count) { // place items in the target position, reusing any existing items FxGridItemSG *movedItem = moved.take(item->index); if (!movedItem) - movedItem = d->createItem(item->index); + movedItem = static_cast(d->createItem(item->index)); it = d->visibleItems.insert(it, movedItem); if (it == d->visibleItems.begin() && firstItem) movedItem->setPosition(firstItem->colPos(), firstItem->rowPos()); @@ -2594,61 +1590,7 @@ void QSGGridView::itemsMoved(int from, int to, int count) d->releaseItem(item); } - d->layout(); -} - -void QSGGridView::modelReset() -{ - Q_D(QSGGridView); - d->clear(); - refill(); - d->moveReason = QSGGridViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); - d->updateTrackedItem(); - } - d->moveReason = QSGGridViewPrivate::Other; - - emit countChanged(); -} - -void QSGGridView::createdItem(int index, QSGItem *item) -{ - Q_D(QSGGridView); - if (d->requestedIndex != index) { - item->setParentItem(this); - d->unrequestedItems.insert(item, index); - if (d->flow == QSGGridView::LeftToRight) { - item->setPos(QPointF(d->colPosAt(index), d->rowPosAt(index))); - } else { - item->setPos(QPointF(d->rowPosAt(index), d->colPosAt(index))); - } - } -} - -void QSGGridView::destroyingItem(QSGItem *item) -{ - Q_D(QSGGridView); - d->unrequestedItems.remove(item); -} - -void QSGGridView::animStopped() -{ - Q_D(QSGGridView); - d->bufferMode = QSGGridViewPrivate::NoBuffer; - if (d->haveHighlightRange && d->highlightRange == QSGGridView::StrictlyEnforceRange) - d->updateHighlight(); -} - -void QSGGridView::refill() -{ - Q_D(QSGGridView); - if (d->isRightToLeftTopToBottom()) - d->refill(-d->position()-d->size()+1, -d->position()); - else - d->refill(d->position(), d->position()+d->size()-1); + d->layoutVisibleItems(); } diff --git a/src/declarative/items/qsggridview_p.h b/src/declarative/items/qsggridview_p.h index 8c167c8..ae4ce34 100644 --- a/src/declarative/items/qsggridview_p.h +++ b/src/declarative/items/qsggridview_p.h @@ -43,7 +43,7 @@ #ifndef QSGGRIDVIEW_P_H #define QSGGRIDVIEW_P_H -#include "qsgflickable_p.h" +#include "qsgitemview_p.h" #include @@ -55,98 +55,32 @@ QT_MODULE(Declarative) class QSGVisualModel; class QSGGridViewAttached; class QSGGridViewPrivate; -class Q_AUTOTEST_EXPORT QSGGridView : public QSGFlickable +class Q_AUTOTEST_EXPORT QSGGridView : public QSGItemView { Q_OBJECT Q_DECLARE_PRIVATE(QSGGridView) - Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) - Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) - Q_PROPERTY(QSGItem *currentItem READ currentItem NOTIFY currentIndexChanged) - Q_PROPERTY(int count READ count NOTIFY countChanged) - - Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) - Q_PROPERTY(QSGItem *highlightItem READ highlightItem NOTIFY highlightItemChanged) - Q_PROPERTY(bool highlightFollowsCurrentItem READ highlightFollowsCurrentItem WRITE setHighlightFollowsCurrentItem) - Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) - - Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged RESET resetPreferredHighlightBegin) - Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd) - Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged) - Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) - Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) - Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) - Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged) - Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged) Q_PROPERTY(int cellWidth READ cellWidth WRITE setCellWidth NOTIFY cellWidthChanged) Q_PROPERTY(int cellHeight READ cellHeight WRITE setCellHeight NOTIFY cellHeightChanged) Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged) - Q_PROPERTY(QDeclarativeComponent *header READ header WRITE setHeader NOTIFY headerChanged) - Q_PROPERTY(QDeclarativeComponent *footer READ footer WRITE setFooter NOTIFY footerChanged) - - Q_ENUMS(HighlightRangeMode) Q_ENUMS(SnapMode) Q_ENUMS(Flow) - Q_ENUMS(PositionMode) Q_CLASSINFO("DefaultProperty", "data") public: QSGGridView(QSGItem *parent=0); ~QSGGridView(); - QVariant model() const; - int modelCount() const; - void setModel(const QVariant &); - - QDeclarativeComponent *delegate() const; - void setDelegate(QDeclarativeComponent *); - - int currentIndex() const; - void setCurrentIndex(int idx); - - QSGItem *currentItem(); - QSGItem *highlightItem(); - int count() const; - - QDeclarativeComponent *highlight() const; - void setHighlight(QDeclarativeComponent *highlight); - - bool highlightFollowsCurrentItem() const; - void setHighlightFollowsCurrentItem(bool); - - int highlightMoveDuration() const; - void setHighlightMoveDuration(int); - - enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; - HighlightRangeMode highlightRangeMode() const; - void setHighlightRangeMode(HighlightRangeMode mode); - - qreal preferredHighlightBegin() const; - void setPreferredHighlightBegin(qreal); - void resetPreferredHighlightBegin(); - - qreal preferredHighlightEnd() const; - void setPreferredHighlightEnd(qreal); - void resetPreferredHighlightEnd(); - - Qt::LayoutDirection layoutDirection() const; - void setLayoutDirection(Qt::LayoutDirection); - Qt::LayoutDirection effectiveLayoutDirection() const; + virtual void setHighlightFollowsCurrentItem(bool); + virtual void setHighlightMoveDuration(int); enum Flow { LeftToRight, TopToBottom }; Flow flow() const; void setFlow(Flow); - bool isWrapEnabled() const; - void setWrapEnabled(bool); - - int cacheBuffer() const; - void setCacheBuffer(int); - int cellWidth() const; void setCellWidth(int); @@ -157,22 +91,6 @@ public: SnapMode snapMode() const; void setSnapMode(SnapMode mode); - QDeclarativeComponent *footer() const; - void setFooter(QDeclarativeComponent *); - - QDeclarativeComponent *header() const; - void setHeader(QDeclarativeComponent *); - - virtual void setContentX(qreal pos); - virtual void setContentY(qreal pos); - - enum PositionMode { Beginning, Center, End, Visible, Contain }; - - Q_INVOKABLE void positionViewAtIndex(int index, int mode); - Q_INVOKABLE int indexAt(qreal x, qreal y) const; - Q_INVOKABLE void positionViewAtBeginning(); - Q_INVOKABLE void positionViewAtEnd(); - static QSGGridViewAttached *qmlAttachedProperties(QObject *); public Q_SLOTS: @@ -182,58 +100,28 @@ public Q_SLOTS: void moveCurrentIndexRight(); Q_SIGNALS: - void countChanged(); - void currentIndexChanged(); void cellWidthChanged(); void cellHeightChanged(); - void highlightChanged(); - void highlightItemChanged(); - void preferredHighlightBeginChanged(); - void preferredHighlightEndChanged(); - void highlightRangeModeChanged(); void highlightMoveDurationChanged(); - void modelChanged(); - void delegateChanged(); void flowChanged(); - void layoutDirectionChanged(); - void effectiveLayoutDirectionChanged(); - void keyNavigationWrapsChanged(); - void cacheBufferChanged(); void snapModeChanged(); - void headerChanged(); - void footerChanged(); protected: - virtual void updatePolish(); virtual void viewportMoved(); - virtual qreal minYExtent() const; - virtual qreal maxYExtent() const; - virtual qreal minXExtent() const; - virtual qreal maxXExtent() const; 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); - void modelReset(); - void destroyRemoved(); - void createdItem(int index, QSGItem *item); - void destroyingItem(QSGItem *item); - void animStopped(); - -private: - void refill(); }; -class QSGGridViewAttached : public QObject +class QSGGridViewAttached : public QSGItemViewAttached { Q_OBJECT public: QSGGridViewAttached(QObject *parent) - : QObject(parent), m_view(0), m_isCurrent(false), m_delayRemove(false) {} + : QSGItemViewAttached(parent), m_view(0) {} ~QSGGridViewAttached() {} Q_PROPERTY(QSGGridView *view READ view NOTIFY viewChanged) @@ -245,38 +133,11 @@ public: } } - Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) - bool isCurrentItem() const { return m_isCurrent; } - void setIsCurrentItem(bool c) { - if (m_isCurrent != c) { - m_isCurrent = c; - emit currentItemChanged(); - } - } - - Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged) - bool delayRemove() const { return m_delayRemove; } - void setDelayRemove(bool delay) { - if (m_delayRemove != delay) { - m_delayRemove = delay; - emit delayRemoveChanged(); - } - } - - void emitAdd() { emit add(); } - void emitRemove() { emit remove(); } - Q_SIGNALS: - void currentItemChanged(); - void delayRemoveChanged(); - void add(); - void remove(); void viewChanged(); public: QDeclarativeGuard m_view; - bool m_isCurrent : 1; - bool m_delayRemove : 1; }; diff --git a/src/declarative/items/qsgitemview.cpp b/src/declarative/items/qsgitemview.cpp new file mode 100644 index 0000000..df242ac --- /dev/null +++ b/src/declarative/items/qsgitemview.cpp @@ -0,0 +1,1363 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying ** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgitemview_p_p.h" + +QT_BEGIN_NAMESPACE + + +FxViewItem::FxViewItem(QSGItem *i, bool own) + : item(i), ownItem(own), index(-1) +{ +} + +FxViewItem::~FxViewItem() +{ + if (ownItem && item) { + item->setParentItem(0); + item->deleteLater(); + item = 0; + } +} + +QSGItemView::QSGItemView(QSGFlickablePrivate &dd, QSGItem *parent) + : QSGFlickable(dd, parent) +{ + Q_D(QSGItemView); + d->init(); +} + +QSGItemView::~QSGItemView() +{ + Q_D(QSGItemView); + d->clear(); + if (d->ownModel) + delete d->model; + delete d->header; + delete d->footer; +} + + +QSGItem *QSGItemView::currentItem() const +{ + Q_D(const QSGItemView); + if (!d->currentItem) + return 0; + return d->currentItem->item; +} + +QVariant QSGItemView::model() const +{ + Q_D(const QSGItemView); + return d->modelVariant; +} + +void QSGItemView::setModel(const QVariant &model) +{ + Q_D(QSGItemView); + if (d->modelVariant == model) + return; + if (d->model) { + disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); + disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int))); + disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); + disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); + disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*))); + } + + QSGVisualModel *oldModel = d->model; + + d->clear(); + d->setPosition(0); + d->model = 0; + d->modelVariant = model; + + QObject *object = qvariant_cast(model); + QSGVisualModel *vim = 0; + if (object && (vim = qobject_cast(object))) { + if (d->ownModel) { + delete oldModel; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QSGVisualDataModel(qmlContext(this), this); + d->ownModel = true; + } else { + d->model = oldModel; + } + if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) + dataModel->setModel(model); + } + + if (d->model) { + d->bufferMode = QSGItemViewPrivate::BufferBefore | QSGItemViewPrivate::BufferAfter; + if (isComponentComplete()) { + updateSections(); + d->refill(); + if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { + setCurrentIndex(0); + } else { + d->moveReason = QSGItemViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QSGItemViewPrivate::Other; + } + d->updateViewport(); + } + 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))); + connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int))); + connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); + connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); + connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*))); + emit countChanged(); + } + emit modelChanged(); +} + +QDeclarativeComponent *QSGItemView::delegate() const +{ + Q_D(const QSGItemView); + if (d->model) { + if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QSGItemView::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QSGItemView); + if (delegate == this->delegate()) + return; + if (!d->ownModel) { + d->model = new QSGVisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) { + int oldCount = dataModel->count(); + dataModel->setDelegate(delegate); + if (isComponentComplete()) { + for (int i = 0; i < d->visibleItems.count(); ++i) + d->releaseItem(d->visibleItems.at(i)); + d->visibleItems.clear(); + d->releaseItem(d->currentItem); + d->currentItem = 0; + updateSections(); + d->refill(); + d->moveReason = QSGItemViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QSGItemViewPrivate::Other; + d->updateViewport(); + } + if (oldCount != dataModel->count()) + emit countChanged(); + } + emit delegateChanged(); +} + + +int QSGItemView::count() const +{ + Q_D(const QSGItemView); + if (d->model) + return d->model->count(); + return 0; +} + +int QSGItemView::currentIndex() const +{ + Q_D(const QSGItemView); + return d->currentIndex; +} + +void QSGItemView::setCurrentIndex(int index) +{ + Q_D(QSGItemView); + if (d->requestedIndex >= 0) // currently creating item + return; + d->currentIndexCleared = (index == -1); + if (index == d->currentIndex) + return; + if (isComponentComplete() && d->isValid()) { + d->moveReason = QSGItemViewPrivate::SetIndex; + d->updateCurrent(index); + } else if (d->currentIndex != index) { + d->currentIndex = index; + emit currentIndexChanged(); + } +} + + +bool QSGItemView::isWrapEnabled() const +{ + Q_D(const QSGItemView); + return d->wrap; +} + +void QSGItemView::setWrapEnabled(bool wrap) +{ + Q_D(QSGItemView); + if (d->wrap == wrap) + return; + d->wrap = wrap; + emit keyNavigationWrapsChanged(); +} + +int QSGItemView::cacheBuffer() const +{ + Q_D(const QSGItemView); + return d->buffer; +} + +void QSGItemView::setCacheBuffer(int b) +{ + Q_D(QSGItemView); + if (d->buffer != b) { + d->buffer = b; + if (isComponentComplete()) { + d->bufferMode = QSGItemViewPrivate::BufferBefore | QSGItemViewPrivate::BufferAfter; + d->refill(); + } + emit cacheBufferChanged(); + } +} + + +Qt::LayoutDirection QSGItemView::layoutDirection() const +{ + Q_D(const QSGItemView); + return d->layoutDirection; +} + +void QSGItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QSGItemView); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + d->regenerate(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} + +Qt::LayoutDirection QSGItemView::effectiveLayoutDirection() const +{ + Q_D(const QSGItemView); + if (d->effectiveLayoutMirror) + return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; + else + return d->layoutDirection; +} + + +QDeclarativeComponent *QSGItemView::header() const +{ + Q_D(const QSGItemView); + return d->headerComponent; +} + +void QSGItemView::setHeader(QDeclarativeComponent *headerComponent) +{ + Q_D(QSGItemView); + if (d->headerComponent != headerComponent) { + delete d->header; + d->header = 0; + d->headerComponent = headerComponent; + + d->minExtentDirty = true; + d->maxExtentDirty = true; + + if (isComponentComplete()) { + d->updateHeader(); + d->updateFooter(); + d->updateViewport(); + d->fixupPosition(); + } + emit headerChanged(); + } +} + +QDeclarativeComponent *QSGItemView::footer() const +{ + Q_D(const QSGItemView); + return d->footerComponent; +} + +void QSGItemView::setFooter(QDeclarativeComponent *footerComponent) +{ + Q_D(QSGItemView); + if (d->footerComponent != footerComponent) { + delete d->footer; + d->footer = 0; + d->footerComponent = footerComponent; + + if (isComponentComplete()) { + d->updateFooter(); + d->updateViewport(); + d->fixupPosition(); + } + emit footerChanged(); + } +} + +QDeclarativeComponent *QSGItemView::highlight() const +{ + Q_D(const QSGItemView); + return d->highlightComponent; +} + +void QSGItemView::setHighlight(QDeclarativeComponent *highlightComponent) +{ + Q_D(QSGItemView); + if (highlightComponent != d->highlightComponent) { + d->highlightComponent = highlightComponent; + d->createHighlight(); + if (d->currentItem) + d->updateHighlight(); + emit highlightChanged(); + } +} + +QSGItem *QSGItemView::highlightItem() const +{ + Q_D(const QSGItemView); + if (!d->highlight) + return 0; + return d->highlight->item; +} + +bool QSGItemView::highlightFollowsCurrentItem() const +{ + Q_D(const QSGItemView); + return d->autoHighlight; +} + +void QSGItemView::setHighlightFollowsCurrentItem(bool autoHighlight) +{ + Q_D(QSGItemView); + if (d->autoHighlight != autoHighlight) { + d->autoHighlight = autoHighlight; + if (autoHighlight) + d->updateHighlight(); + emit highlightFollowsCurrentItemChanged(); + } +} + +QSGItemView::HighlightRangeMode QSGItemView::highlightRangeMode() const +{ + Q_D(const QSGItemView); + return static_cast(d->highlightRange); +} + +void QSGItemView::setHighlightRangeMode(HighlightRangeMode mode) +{ + Q_D(QSGItemView); + if (d->highlightRange == mode) + return; + d->highlightRange = mode; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit highlightRangeModeChanged(); +} + +//###Possibly rename these properties, since they are very useful even without a highlight? +qreal QSGItemView::preferredHighlightBegin() const +{ + Q_D(const QSGItemView); + return d->highlightRangeStart; +} + +void QSGItemView::setPreferredHighlightBegin(qreal start) +{ + Q_D(QSGItemView); + d->highlightRangeStartValid = true; + if (d->highlightRangeStart == start) + return; + d->highlightRangeStart = start; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightBeginChanged(); +} + +void QSGItemView::resetPreferredHighlightBegin() +{ + Q_D(QSGItemView); + d->highlightRangeStartValid = false; + if (d->highlightRangeStart == 0) + return; + d->highlightRangeStart = 0; + emit preferredHighlightBeginChanged(); +} + +qreal QSGItemView::preferredHighlightEnd() const +{ + Q_D(const QSGItemView); + return d->highlightRangeEnd; +} + +void QSGItemView::setPreferredHighlightEnd(qreal end) +{ + Q_D(QSGItemView); + d->highlightRangeEndValid = true; + if (d->highlightRangeEnd == end) + return; + d->highlightRangeEnd = end; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightEndChanged(); +} + +void QSGItemView::resetPreferredHighlightEnd() +{ + Q_D(QSGItemView); + d->highlightRangeEndValid = false; + if (d->highlightRangeEnd == 0) + return; + d->highlightRangeEnd = 0; + emit preferredHighlightEndChanged(); +} + +int QSGItemView::highlightMoveDuration() const +{ + Q_D(const QSGItemView); + return d->highlightMoveDuration; +} + +void QSGItemView::setHighlightMoveDuration(int duration) +{ + Q_D(QSGItemView); + if (d->highlightMoveDuration != duration) { + d->highlightMoveDuration = duration; + emit highlightMoveDurationChanged(); + } +} + +void QSGItemViewPrivate::positionViewAtIndex(int index, int mode) +{ + Q_Q(QSGItemView); + if (!isValid()) + return; + if (mode < QSGItemView::Beginning || mode > QSGItemView::Contain) + return; + int idx = qMax(qMin(index, model->count()-1), 0); + + if (layoutScheduled) + layout(); + qreal pos = isContentFlowReversed() ? -position() - size() : position(); + FxViewItem *item = visibleItem(idx); + qreal maxExtent; + if (layoutOrientation() == Qt::Vertical) + maxExtent = -q->maxYExtent(); + else + maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent(); + if (!item) { + int itemPos = positionAt(idx); + changedVisibleIndex(idx); + // save the currently visible items in case any of them end up visible again + QList oldVisible = visibleItems; + visibleItems.clear(); + setPosition(qMin(qreal(itemPos), maxExtent)); + // now release the reference to all the old visible items. + for (int i = 0; i < oldVisible.count(); ++i) + releaseItem(oldVisible.at(i)); + item = visibleItem(idx); + } + if (item) { + const qreal itemPos = item->position(); + switch (mode) { + case QSGItemView::Beginning: + pos = itemPos; + if (index < 0 && header) + pos -= headerSize(); + break; + case QSGItemView::Center: + pos = itemPos - (size() - item->size())/2; + break; + case QSGItemView::End: + pos = itemPos - size() + item->size(); + if (index >= model->count() && footer) + pos += footerSize(); + break; + case QSGItemView::Visible: + if (itemPos > pos + size()) + pos = itemPos - size() + item->size(); + else if (item->endPosition() < pos) + pos = itemPos; + break; + case QSGItemView::Contain: + if (item->endPosition() > pos + size()) + pos = itemPos - size() + item->size(); + if (itemPos < pos) + pos = itemPos; + } + pos = qMin(pos, maxExtent); + qreal minExtent; + if (layoutOrientation() == Qt::Vertical) + minExtent = -q->minYExtent(); + else + minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent(); + pos = qMax(pos, minExtent); + moveReason = QSGItemViewPrivate::Other; + q->cancelFlick(); + setPosition(pos); + + if (highlight) { + if (autoHighlight) + resetHighlightPosition(); + updateHighlight(); + } + } + fixupPosition(); +} + +void QSGItemView::positionViewAtIndex(int index, int mode) +{ + Q_D(QSGItemView); + if (!d->isValid() || index < 0 || index >= d->model->count()) + return; + d->positionViewAtIndex(index, mode); +} + + +void QSGItemView::positionViewAtBeginning() +{ + Q_D(QSGItemView); + if (!d->isValid()) + return; + d->positionViewAtIndex(-1, Beginning); +} + +void QSGItemView::positionViewAtEnd() +{ + Q_D(QSGItemView); + if (!d->isValid()) + return; + d->positionViewAtIndex(d->model->count(), End); +} + +int QSGItemView::indexAt(qreal x, qreal y) const +{ + Q_D(const QSGItemView); + for (int i = 0; i < d->visibleItems.count(); ++i) { + const FxViewItem *item = d->visibleItems.at(i); + if (item->contains(x, y)) + return item->index; + } + + return -1; +} + + +// for debugging only +void QSGItemViewPrivate::checkVisible() const +{ + int skip = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == -1) { + ++skip; + } else if (item->index != visibleIndex + i - skip) { + qFatal("index %d %d %d", visibleIndex, i, item->index); + } + } +} + + + +void QSGItemViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QSGItemView); + QSGFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + if (!q->isComponentComplete()) + return; + + if (header && header->item == item) + updateHeader(); + else if (footer && footer->item == item) + updateFooter(); + + if (currentItem && currentItem->item == item) + updateHighlight(); + if (trackedItem && trackedItem->item == item) + q->trackedPositionChanged(); +} + +void QSGItemView::destroyRemoved() +{ + Q_D(QSGItemView); + for (QList::Iterator it = d->visibleItems.begin(); + it != d->visibleItems.end();) { + FxViewItem *item = *it; + if (item->index == -1 && item->attached->delayRemove() == false) { + d->releaseItem(item); + it = d->visibleItems.erase(it); + } else { + ++it; + } + } + + // Correct the positioning of the items + d->updateSections(); + d->layout(); +} + +void QSGItemView::itemsChanged(int, int) +{ + Q_D(QSGItemView); + d->updateSections(); + d->layout(); +} + +void QSGItemView::modelReset() +{ + Q_D(QSGItemView); + d->moveReason = QSGItemViewPrivate::SetIndex; + d->regenerate(); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QSGItemViewPrivate::Other; + + emit countChanged(); +} + +void QSGItemView::createdItem(int index, QSGItem *item) +{ + Q_D(QSGItemView); + if (d->requestedIndex != index) { + item->setParentItem(contentItem()); + d->unrequestedItems.insert(item, index); + d->repositionPackageItemAt(item, index); + } +} + +void QSGItemView::destroyingItem(QSGItem *item) +{ + Q_D(QSGItemView); + d->unrequestedItems.remove(item); +} + +void QSGItemView::animStopped() +{ + Q_D(QSGItemView); + d->bufferMode = QSGItemViewPrivate::NoBuffer; + if (d->haveHighlightRange && d->highlightRange == QSGItemView::StrictlyEnforceRange) + d->updateHighlight(); +} + + +void QSGItemView::trackedPositionChanged() +{ + Q_D(QSGItemView); + if (!d->trackedItem || !d->currentItem) + return; + if (d->moveReason == QSGItemViewPrivate::SetIndex) { + qreal trackedPos = d->trackedItem->position(); + qreal trackedSize = d->trackedItem->size(); + if (d->trackedItem != d->currentItem) { + trackedPos -= d->currentItem->sectionSize(); + trackedSize += d->currentItem->sectionSize(); + } + qreal viewPos; + qreal highlightStart; + qreal highlightEnd; + if (d->isContentFlowReversed()) { + viewPos = -d->position()-d->size(); + highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; + highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; + } else { + viewPos = d->position(); + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + } + qreal pos = viewPos; + if (d->haveHighlightRange) { + if (d->highlightRange == StrictlyEnforceRange) { + if (trackedPos > pos + highlightEnd - d->trackedItem->size()) + pos = trackedPos - highlightEnd + d->trackedItem->size(); + if (trackedPos < pos + highlightStart) + pos = trackedPos - highlightStart; + } else { + if (trackedPos < d->startPosition() + highlightStart) { + pos = d->startPosition(); + } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) { + pos = d->endPosition() - d->size() + 1; + if (pos < d->startPosition()) + pos = d->startPosition(); + } else { + if (trackedPos < viewPos + highlightStart) { + pos = trackedPos - highlightStart; + } else if (trackedPos > viewPos + highlightEnd - trackedSize) { + pos = trackedPos - highlightEnd + trackedSize; + } + } + } + } else { + if (trackedPos < viewPos && d->currentItem->position() < viewPos) { + pos = qMax(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 QSGItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QSGItemView); + d->maxExtentDirty = true; + d->minExtentDirty = true; + QSGFlickable::geometryChanged(newGeometry, oldGeometry); +} + + +qreal QSGItemView::minYExtent() const +{ + Q_D(const QSGItemView); + if (d->layoutOrientation() == Qt::Horizontal) + return QSGFlickable::minYExtent(); + + if (d->minExtentDirty) { + d->minExtent = -d->startPosition(); + if (d->header && d->visibleItems.count()) + d->minExtent += d->headerSize(); + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent += d->highlightRangeStart; + if (d->visibleItem(0)) + d->minExtent -= d->visibleItem(0)->sectionSize(); + d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1)); + } + d->minExtentDirty = false; + } + + return d->minExtent; +} + +qreal QSGItemView::maxYExtent() const +{ + Q_D(const QSGItemView); + if (d->layoutOrientation() == Qt::Horizontal) + return height(); + + if (d->maxExtentDirty) { + if (!d->model || !d->model->count()) { + d->maxExtent = d->header ? -d->headerSize() : 0; + d->maxExtent += height(); + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart); + if (d->highlightRangeEnd != d->highlightRangeStart) + d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1)); + } else { + d->maxExtent = -(d->endPosition() - height() + 1); + } + + if (d->footer) + d->maxExtent -= d->footerSize(); + qreal minY = minYExtent(); + if (d->maxExtent > minY) + d->maxExtent = minY; + d->maxExtentDirty = false; + } + return d->maxExtent; +} + +qreal QSGItemView::minXExtent() const +{ + Q_D(const QSGItemView); + if (d->layoutOrientation() == Qt::Vertical) + return QSGFlickable::minXExtent(); + + if (d->minExtentDirty) { + d->minExtent = -d->startPosition(); + qreal highlightStart; + qreal highlightEnd; + qreal endPositionFirstItem = 0; + if (d->isContentFlowReversed()) { + if (d->model && d->model->count()) + endPositionFirstItem = d->positionAt(d->model->count()-1); + else if (d->header) + d->minExtent += d->headerSize(); + highlightStart = d->highlightRangeStartValid + ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem) + : d->size() - (d->lastPosition()-endPositionFirstItem); + highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size(); + if (d->footer) + d->minExtent += d->footerSize(); + qreal maxX = maxXExtent(); + if (d->minExtent < maxX) + d->minExtent = maxX; + } else { + endPositionFirstItem = d->endPositionAt(0); + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + if (d->header && d->visibleItems.count()) + d->minExtent += d->headerSize(); + } + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent += highlightStart; + d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1)); + } + d->minExtentDirty = false; + } + + return d->minExtent; +} + +qreal QSGItemView::maxXExtent() const +{ + Q_D(const QSGItemView); + if (d->layoutOrientation() == Qt::Vertical) + return width(); + + if (d->maxExtentDirty) { + qreal highlightStart; + qreal highlightEnd; + qreal lastItemPosition = 0; + d->maxExtent = 0; + if (d->isContentFlowReversed()) { + highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size(); + highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size(); + lastItemPosition = d->endPosition(); + } else { + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + if (d->model && d->model->count()) + lastItemPosition = d->positionAt(d->model->count()-1); + } + if (!d->model || !d->model->count()) { + if (!d->isContentFlowReversed()) + d->maxExtent = d->header ? -d->headerSize() : 0; + d->maxExtent += width(); + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->maxExtent = -(lastItemPosition - highlightStart); + if (highlightEnd != highlightStart) { + d->maxExtent = d->isContentFlowReversed() + ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1)) + : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1)); + } + } else { + d->maxExtent = -(d->endPosition() - width() + 1); + } + if (d->isContentFlowReversed()) { + if (d->header && d->visibleItems.count()) + d->maxExtent -= d->headerSize(); + } else { + if (d->footer) + d->maxExtent -= d->footerSize(); + qreal minX = minXExtent(); + if (d->maxExtent > minX) + d->maxExtent = minX; + } + d->maxExtentDirty = false; + } + + return d->maxExtent; +} + +void QSGItemView::setContentX(qreal pos) +{ + Q_D(QSGItemView); + // Positioning the view manually should override any current movement state + d->moveReason = QSGItemViewPrivate::Other; + QSGFlickable::setContentX(pos); +} + +void QSGItemView::setContentY(qreal pos) +{ + Q_D(QSGItemView); + // Positioning the view manually should override any current movement state + d->moveReason = QSGItemViewPrivate::Other; + QSGFlickable::setContentY(pos); +} + + +void QSGItemView::updatePolish() +{ + Q_D(QSGItemView); + QSGFlickable::updatePolish(); + d->layout(); +} + +void QSGItemView::componentComplete() +{ + Q_D(QSGItemView); + QSGFlickable::componentComplete(); + + updateSections(); + d->updateHeader(); + d->updateFooter(); + d->updateViewport(); + if (d->isValid()) { + d->refill(); + d->moveReason = QSGItemViewPrivate::SetIndex; + if (d->currentIndex < 0 && !d->currentIndexCleared) + d->updateCurrent(0); + else + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QSGItemViewPrivate::Other; + d->fixupPosition(); + } +} + + + +QSGItemViewPrivate::QSGItemViewPrivate() + : itemCount(0) + , buffer(0), bufferMode(BufferBefore | BufferAfter) + , layoutDirection(Qt::LeftToRight) + , moveReason(Other) + , visibleIndex(0) + , currentIndex(-1), currentItem(0) + , trackedItem(0), requestedIndex(-1) + , highlightComponent(0), highlight(0) + , highlightRange(QSGItemView::NoHighlightRange) + , highlightRangeStart(0), highlightRangeEnd(0) + , highlightMoveDuration(150) + , headerComponent(0), header(0), footerComponent(0), footer(0) + , minExtent(0), maxExtent(0) + , ownModel(false), wrap(false), lazyRelease(false), deferredRelease(false) + , layoutScheduled(false), inViewportMoved(false), currentIndexCleared(false) + , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false) + , minExtentDirty(true), maxExtentDirty(true) +{ +} + +bool QSGItemViewPrivate::isValid() const +{ + return model && model->count() && model->isValid(); +} + +qreal QSGItemViewPrivate::position() const +{ + Q_Q(const QSGItemView); + return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX(); +} + +qreal QSGItemViewPrivate::size() const +{ + Q_Q(const QSGItemView); + return layoutOrientation() == Qt::Vertical ? q->height() : q->width(); +} + +int QSGItemViewPrivate::findLastVisibleIndex(int defaultValue) const +{ + if (visibleItems.count()) { + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + if (visibleItems.at(i)->index != -1) + return visibleItems.at(i)->index; + } + return defaultValue; +} + +FxViewItem *QSGItemViewPrivate::visibleItem(int modelIndex) const { + if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { + for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == modelIndex) + return item; + } + } + return 0; +} + +FxViewItem *QSGItemViewPrivate::firstVisibleItem() const { + const qreal pos = isContentFlowReversed() ? -position()-size() : position(); + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index != -1 && item->endPosition() > pos) + return item; + } + return visibleItems.count() ? visibleItems.first() : 0; +} + +// Map a model index to visibleItems list index. +// These may differ if removed items are still present in the visible list, +// e.g. doing a removal animation +int QSGItemViewPrivate::mapFromModel(int modelIndex) const +{ + if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) + return -1; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == modelIndex) + return i + visibleIndex; + if (item->index > modelIndex) + return -1; + } + return -1; // Not in visibleList +} + +void QSGItemViewPrivate::init() +{ + Q_Q(QSGItemView); + QSGItemPrivate::get(contentItem)->childrenDoNotOverlap = true; + q->setFlag(QSGItem::ItemIsFocusScope); + addItemChangeListener(this, Geometry); + QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); + q->setFlickableDirection(QSGFlickable::VerticalFlick); +} + +void QSGItemViewPrivate::updateCurrent(int modelIndex) +{ + Q_Q(QSGItemView); + 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; + } + + if (currentItem && currentIndex == modelIndex) { + updateHighlight(); + return; + } + + FxViewItem *oldCurrentItem = currentItem; + currentIndex = modelIndex; + currentItem = createItem(modelIndex); + if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) + oldCurrentItem->attached->setIsCurrentItem(false); + if (currentItem) { + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + initializeCurrentItem(); + } + + updateHighlight(); + emit q->currentIndexChanged(); + releaseItem(oldCurrentItem); +} + +void QSGItemViewPrivate::clear() +{ + timeline.clear(); + + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + visibleIndex = 0; + + releaseItem(currentItem); + currentItem = 0; + createHighlight(); + trackedItem = 0; + + minExtentDirty = true; + maxExtentDirty = true; + itemCount = 0; +} + + +void QSGItemViewPrivate::mirrorChange() +{ + Q_Q(QSGItemView); + regenerate(); + emit q->effectiveLayoutDirectionChanged(); +} + +void QSGItemViewPrivate::refill() +{ + if (isContentFlowReversed()) + refill(-position()-size()+1, -position()); + else + refill(position(), position()+size()-1); +} + +void QSGItemViewPrivate::refill(qreal from, qreal to, bool doBuffer) +{ + Q_Q(QSGItemView); + if (!isValid() || !q->isComponentComplete()) + return; + + itemCount = model->count(); + qreal bufferFrom = from - buffer; + qreal bufferTo = to + buffer; + qreal fillFrom = from; + qreal fillTo = to; + if (doBuffer && (bufferMode & BufferAfter)) + fillTo = bufferTo; + if (doBuffer && (bufferMode & BufferBefore)) + fillFrom = bufferFrom; + + // Item creation and release is staggered in order to avoid + // creating/releasing multiple items in one frame + // while flicking (as much as possible). + + bool changed = addVisibleItems(fillFrom, fillTo, doBuffer); + + if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create + if (removeNonVisibleItems(bufferFrom, bufferTo)) + changed = true; + deferredRelease = false; + } else { + deferredRelease = true; + } + + if (changed) { + minExtentDirty = true; + maxExtentDirty = true; + visibleItemsChanged(); + } else if (!doBuffer && buffer && bufferMode != NoBuffer) { + refill(from, to, true); + } + + lazyRelease = false; +} + +void QSGItemViewPrivate::regenerate() +{ + Q_Q(QSGItemView); + if (q->isComponentComplete()) { + delete header; + header = 0; + delete footer; + footer = 0; + updateHeader(); + updateFooter(); + clear(); + updateViewport(); + setPosition(0); + refill(); + updateCurrent(currentIndex); + } +} + +void QSGItemViewPrivate::scheduleLayout() +{ + Q_Q(QSGItemView); + if (!layoutScheduled) { + layoutScheduled = true; + q->polish(); + } +} + +void QSGItemViewPrivate::updateViewport() +{ + Q_Q(QSGItemView); + if (isValid()) { + if (layoutOrientation() == Qt::Vertical) + q->setContentHeight(endPosition() - startPosition() + 1); + else + q->setContentWidth(endPosition() - startPosition() + 1); + } +} + +void QSGItemViewPrivate::layout() +{ + Q_Q(QSGItemView); + layoutScheduled = false; + if (!isValid() && !visibleItems.count()) { + clear(); + setPosition(0); + return; + } + + layoutVisibleItems(); + refill(); + + minExtentDirty = true; + maxExtentDirty = true; + + updateHighlight(); + if (!q->isMoving() && !q->isFlicking()) { + fixupPosition(); + refill(); + } + + updateHeader(); + updateFooter(); + updateViewport(); + updateUnrequestedPositions(); +} + +FxViewItem *QSGItemViewPrivate::createItem(int modelIndex) +{ + Q_Q(QSGItemView); + + requestedIndex = modelIndex; + FxViewItem *viewItem = 0; + + if (QSGItem *item = model->item(modelIndex, false)) { + viewItem = newViewItem(modelIndex, item); + if (viewItem) { + viewItem->index = modelIndex; + if (model->completePending()) { + // complete + viewItem->item->setZ(1); + viewItem->item->setParentItem(q->contentItem()); + model->completeItem(); + } else { + viewItem->item->setParentItem(q->contentItem()); + } + // do other set up for the new item that should not happen + // until after bindings are evaluated + initializeViewItem(viewItem); + + unrequestedItems.remove(viewItem->item); + } + } + requestedIndex = -1; + return viewItem; +} + + +void QSGItemViewPrivate::releaseItem(FxViewItem *item) +{ + Q_Q(QSGItemView); + if (!item || !model) + return; + if (trackedItem == item) + trackedItem = 0; + QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item); + itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry); + 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)); + } + delete item; +} + +QSGItem *QSGItemViewPrivate::createHighlightItem() +{ + return createComponentItem(highlightComponent, true, true); +} + +QSGItem *QSGItemViewPrivate::createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault) +{ + Q_Q(QSGItemView); + + QSGItem *item = 0; + if (component) { + QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = component->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + } else if (createDefault) { + item = new QSGItem; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + if (receiveItemGeometryChanges) { + QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); + itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); + } + } + return item; +} + +void QSGItemViewPrivate::updateTrackedItem() +{ + Q_Q(QSGItemView); + FxViewItem *item = currentItem; + if (highlight) + item = highlight; + trackedItem = item; + + if (trackedItem) + q->trackedPositionChanged(); +} + +void QSGItemViewPrivate::updateUnrequestedIndexes() +{ + Q_Q(QSGItemView); + for (QHash::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + *it = model->indexOf(it.key(), q); +} + +void QSGItemViewPrivate::updateUnrequestedPositions() +{ + for (QHash::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + repositionPackageItemAt(it.key(), it.value()); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qsgitemview_p.h b/src/declarative/items/qsgitemview_p.h new file mode 100644 index 0000000..fb95e4f --- /dev/null +++ b/src/declarative/items/qsgitemview_p.h @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGITEMVIEW_P_H +#define QSGITEMVIEW_P_H + +#include "qsgflickable_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGItemViewPrivate; + +class Q_AUTOTEST_EXPORT QSGItemView : public QSGFlickable +{ + Q_OBJECT + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(QSGItem *currentItem READ currentItem NOTIFY currentIndexChanged) + + Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged) + Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged) + + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) + Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) + + Q_PROPERTY(QDeclarativeComponent *header READ header WRITE setHeader NOTIFY headerChanged) + Q_PROPERTY(QDeclarativeComponent *footer READ footer WRITE setFooter NOTIFY footerChanged) + + Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) + Q_PROPERTY(QSGItem *highlightItem READ highlightItem NOTIFY highlightItemChanged) + Q_PROPERTY(bool highlightFollowsCurrentItem READ highlightFollowsCurrentItem WRITE setHighlightFollowsCurrentItem NOTIFY highlightFollowsCurrentItemChanged) + Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged) + Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged RESET resetPreferredHighlightBegin) + Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd) + Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) + + Q_ENUMS(HighlightRangeMode) + Q_ENUMS(PositionMode) + +public: + QSGItemView(QSGFlickablePrivate &dd, QSGItem *parent = 0); + ~QSGItemView(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int count() const; + + int currentIndex() const; + void setCurrentIndex(int idx); + + QSGItem *currentItem() const; + + bool isWrapEnabled() const; + void setWrapEnabled(bool); + + int cacheBuffer() const; + void setCacheBuffer(int); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection(Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + + QDeclarativeComponent *footer() const; + void setFooter(QDeclarativeComponent *); + + QDeclarativeComponent *header() const; + void setHeader(QDeclarativeComponent *); + + QDeclarativeComponent *highlight() const; + void setHighlight(QDeclarativeComponent *); + + QSGItem *highlightItem() const; + + bool highlightFollowsCurrentItem() const; + virtual void setHighlightFollowsCurrentItem(bool); + + enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; + HighlightRangeMode highlightRangeMode() const; + void setHighlightRangeMode(HighlightRangeMode mode); + + qreal preferredHighlightBegin() const; + void setPreferredHighlightBegin(qreal); + void resetPreferredHighlightBegin(); + + qreal preferredHighlightEnd() const; + void setPreferredHighlightEnd(qreal); + void resetPreferredHighlightEnd(); + + int highlightMoveDuration() const; + virtual void setHighlightMoveDuration(int); + + enum PositionMode { Beginning, Center, End, Visible, Contain }; + + Q_INVOKABLE void positionViewAtIndex(int index, int mode); + Q_INVOKABLE int indexAt(qreal x, qreal y) const; + Q_INVOKABLE void positionViewAtBeginning(); + Q_INVOKABLE void positionViewAtEnd(); + + virtual void setContentX(qreal pos); + virtual void setContentY(qreal pos); + +signals: + void modelChanged(); + void delegateChanged(); + void countChanged(); + void currentIndexChanged(); + + void keyNavigationWrapsChanged(); + void cacheBufferChanged(); + + void layoutDirectionChanged(); + void effectiveLayoutDirectionChanged(); + + void headerChanged(); + void footerChanged(); + + void highlightChanged(); + void highlightItemChanged(); + void highlightFollowsCurrentItemChanged(); + void highlightRangeModeChanged(); + void preferredHighlightBeginChanged(); + void preferredHighlightEndChanged(); + void highlightMoveDurationChanged(); + +protected: + virtual void updatePolish(); + virtual void componentComplete(); + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + virtual qreal minYExtent() const; + virtual qreal maxYExtent() const; + virtual qreal minXExtent() const; + virtual qreal maxXExtent() const; + +protected slots: + virtual void updateSections() {} + void destroyRemoved(); + void itemsChanged(int index, int count); + void createdItem(int index, QSGItem *item); + void modelReset(); + void destroyingItem(QSGItem *item); + void animStopped(); + void trackedPositionChanged(); + +private: + Q_DECLARE_PRIVATE(QSGItemView) +}; + + +class Q_AUTOTEST_EXPORT QSGItemViewAttached : public QObject +{ + Q_OBJECT + + Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) + Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged) + + Q_PROPERTY(QString section READ section NOTIFY sectionChanged) + Q_PROPERTY(QString previousSection READ prevSection NOTIFY prevSectionChanged) + Q_PROPERTY(QString nextSection READ nextSection NOTIFY nextSectionChanged) + +public: + QSGItemViewAttached(QObject *parent) + : QObject(parent), m_isCurrent(false), m_delayRemove(false) {} + ~QSGItemViewAttached() {} + + bool isCurrentItem() const { return m_isCurrent; } + void setIsCurrentItem(bool c) { + if (m_isCurrent != c) { + m_isCurrent = c; + emit currentItemChanged(); + } + } + + bool delayRemove() const { return m_delayRemove; } + void setDelayRemove(bool delay) { + if (m_delayRemove != delay) { + m_delayRemove = delay; + emit delayRemoveChanged(); + } + } + + QString section() const { return m_section; } + void setSection(const QString §) { + if (m_section != sect) { + m_section = sect; + emit sectionChanged(); + } + } + + QString prevSection() const { return m_prevSection; } + void setPrevSection(const QString §) { + if (m_prevSection != sect) { + m_prevSection = sect; + emit prevSectionChanged(); + } + } + + QString nextSection() const { return m_nextSection; } + void setNextSection(const QString §) { + if (m_nextSection != sect) { + m_nextSection = sect; + emit nextSectionChanged(); + } + } + + void emitAdd() { emit add(); } + void emitRemove() { emit remove(); } + +signals: + void currentItemChanged(); + void delayRemoveChanged(); + + void add(); + void remove(); + + void sectionChanged(); + void prevSectionChanged(); + void nextSectionChanged(); + +public: + bool m_isCurrent : 1; + bool m_delayRemove : 1; + + // current only used by list view + mutable QString m_section; + QString m_prevSection; + QString m_nextSection; +}; + + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QSGITEMVIEW_P_H + diff --git a/src/declarative/items/qsgitemview_p_p.h b/src/declarative/items/qsgitemview_p_p.h new file mode 100644 index 0000000..aa1d14d --- /dev/null +++ b/src/declarative/items/qsgitemview_p_p.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGITEMVIEW_P_P_H +#define QSGITEMVIEW_P_P_H + +#include "qsgitemview_p.h" +#include "qsgflickable_p_p.h" +#include "qsgvisualitemmodel_p.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class FxViewItem +{ +public: + FxViewItem(QSGItem *, bool own); + ~FxViewItem(); + + // these are positions and sizes along the current direction of scrolling/flicking + virtual qreal position() const = 0; + virtual qreal endPosition() const = 0; + virtual qreal size() const = 0; + virtual qreal sectionSize() const = 0; + + virtual bool contains(qreal x, qreal y) const = 0; + + QSGItem *item; + bool ownItem; + int index; + QSGItemViewAttached *attached; +}; + +class QSGItemViewPrivate : public QSGFlickablePrivate +{ + Q_DECLARE_PUBLIC(QSGItemView) +public: + QSGItemViewPrivate(); + + enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; + enum MovementReason { Other, SetIndex, Mouse }; + + bool isValid() const; + qreal position() const; + qreal size() const; + int findLastVisibleIndex(int defaultValue = -1) const; + FxViewItem *visibleItem(int modelIndex) const; + FxViewItem *firstVisibleItem() const; + int mapFromModel(int modelIndex) const; + + virtual void init(); + virtual void updateCurrent(int modelIndex); + virtual void clear(); + virtual void regenerate(); + virtual void updateViewport(); + void layout(); + void refill(); + void refill(qreal from, qreal to, bool doBuffer = false); + void scheduleLayout(); + void mirrorChange(); + + FxViewItem *createItem(int modelIndex); + virtual void releaseItem(FxViewItem *item); + + QSGItem *createHighlightItem(); + QSGItem *createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault = false); + + void updateTrackedItem(); + void updateUnrequestedIndexes(); + void updateUnrequestedPositions(); + void positionViewAtIndex(int index, int mode); + + void checkVisible() const; + + QDeclarativeGuard model; + QVariant modelVariant; + int itemCount; + int buffer; + int bufferMode; + Qt::LayoutDirection layoutDirection; + + MovementReason moveReason; + + QList visibleItems; + int visibleIndex; + int currentIndex; + FxViewItem *currentItem; + FxViewItem *trackedItem; + QHash unrequestedItems; + int requestedIndex; + + // XXX split into struct + QDeclarativeComponent *highlightComponent; + FxViewItem *highlight; + int highlightRange; // enum value + qreal highlightRangeStart; + qreal highlightRangeEnd; + int highlightMoveDuration; + + QDeclarativeComponent *headerComponent; + FxViewItem *header; + QDeclarativeComponent *footerComponent; + FxViewItem *footer; + + mutable qreal minExtent; + mutable qreal maxExtent; + + bool ownModel : 1; + bool wrap : 1; + bool lazyRelease : 1; + bool deferredRelease : 1; + bool layoutScheduled : 1; + bool inViewportMoved : 1; + bool currentIndexCleared : 1; + bool haveHighlightRange : 1; + bool autoHighlight : 1; + bool highlightRangeStartValid : 1; + bool highlightRangeEndValid : 1; + mutable bool minExtentDirty : 1; + mutable bool maxExtentDirty : 1; + +protected: + virtual Qt::Orientation layoutOrientation() const = 0; + virtual bool isContentFlowReversed() const = 0; + + virtual qreal startPosition() const = 0; + virtual qreal positionAt(int index) const = 0; + virtual qreal endPosition() const = 0; + virtual qreal endPositionAt(int index) const = 0; + virtual qreal lastPosition() const = 0; + + virtual qreal headerSize() const = 0; + virtual qreal footerSize() const = 0; + virtual void updateHeader() = 0; + virtual void updateFooter() = 0; + + virtual void createHighlight() = 0; + virtual void updateHighlight() = 0; + virtual void resetHighlightPosition() = 0; + + virtual void setPosition(qreal pos) = 0; + virtual void fixupPosition() = 0; + + virtual bool addVisibleItems(int fillFrom, int fillTo, bool doBuffer) = 0; + virtual bool removeNonVisibleItems(int bufferFrom, int bufferTo) = 0; + virtual void visibleItemsChanged() = 0; + + virtual FxViewItem *newViewItem(int index, QSGItem *item) = 0; + virtual void initializeViewItem(FxViewItem *) {} + virtual void repositionPackageItemAt(QSGItem *item, int index) = 0; + + virtual void layoutVisibleItems() = 0; + + virtual void updateSections() {} + virtual void changedVisibleIndex(int newIndex) = 0; + virtual void initializeCurrentItem() {} + + virtual void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGITEMVIEW_P_P_H diff --git a/src/declarative/items/qsglistview.cpp b/src/declarative/items/qsglistview.cpp index 3e8f02c..f190e17 100644 --- a/src/declarative/items/qsglistview.cpp +++ b/src/declarative/items/qsglistview.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include "qsglistview_p.h" -#include "qsgflickable_p_p.h" +#include "qsgitemview_p_p.h" #include "qsgvisualitemmodel_p.h" #include @@ -89,15 +89,17 @@ QString QSGViewSection::sectionString(const QString &value) //---------------------------------------------------------------------------- -class FxListItemSG +class FxListItemSG : public FxViewItem { public: - FxListItemSG(QSGItem *i, QSGListView *v) : item(i), section(0), view(v) { + FxListItemSG(QSGItem *i, QSGListView *v, bool own) : FxViewItem(i, own), section(0), view(v) { attached = static_cast(qmlAttachedPropertiesObject(item)); if (attached) - attached->setView(view); + static_cast(attached)->setView(view); } + ~FxListItemSG() {} + qreal position() const { if (section) { if (view->orientation() == QSGListView::Vertical) @@ -171,580 +173,385 @@ public: y >= item->y() && y < item->y() + item->height()); } - QSGItem *item; QSGItem *section; QSGListView *view; - QSGListViewAttached *attached; - int index; }; //---------------------------------------------------------------------------- -class QSGListViewPrivate : public QSGFlickablePrivate +class QSGListViewPrivate : public QSGItemViewPrivate { Q_DECLARE_PUBLIC(QSGListView) - public: - QSGListViewPrivate() - : currentItem(0), orient(QSGListView::Vertical), layoutDirection(Qt::LeftToRight) - , visiblePos(0), visibleIndex(0) - , averageSize(100.0), currentIndex(-1), requestedIndex(-1) - , 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(QSGListView::NoHighlightRange) - , snapMode(QSGListView::NoSnap), overshootDist(0.0) - , 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) - , highlightRangeStartValid(false), highlightRangeEndValid(false) - , minExtentDirty(true), maxExtentDirty(true) - {} - - void init(); - void clear(); - FxListItemSG *createItem(int modelIndex); - void releaseItem(FxListItemSG *item); - - FxListItemSG *visibleItem(int modelIndex) const { - if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { - for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { - FxListItemSG *item = visibleItems.at(i); - if (item->index == modelIndex) - return item; - } - } - return 0; - } - - FxListItemSG *firstVisibleItem() const { - const qreal pos = isRightToLeft() ? -position()-size() : position(); - for (int i = 0; i < visibleItems.count(); ++i) { - FxListItemSG *item = visibleItems.at(i); - if (item->index != -1 && item->endPosition() > pos) - return item; - } - return visibleItems.count() ? visibleItems.first() : 0; - } + virtual Qt::Orientation layoutOrientation() const; + virtual bool isContentFlowReversed() const; + bool isRightToLeft() const; + + virtual qreal startPosition() const; + virtual qreal positionAt(int index) const; + virtual qreal endPosition() const; + virtual qreal endPositionAt(int index) const; + virtual qreal lastPosition() const; + + qreal originPosition() const; + FxViewItem *nextVisibleItem() const; + FxViewItem *itemBefore(int modelIndex) const; + QString sectionAt(int modelIndex); + qreal snapPosAt(qreal pos); + FxViewItem *snapItemAt(qreal pos); + + virtual void init(); + virtual void clear(); + + virtual bool addVisibleItems(int fillFrom, int fillTo, bool doBuffer); + virtual bool removeNonVisibleItems(int bufferFrom, int bufferTo); + virtual void visibleItemsChanged(); + + virtual FxViewItem *newViewItem(int index, QSGItem *item); + virtual void initializeViewItem(FxViewItem *item); + virtual void releaseItem(FxViewItem *item); + virtual void repositionPackageItemAt(QSGItem *item, int index); + + virtual void createHighlight(); + virtual void updateHighlight(); + virtual void resetHighlightPosition(); + + virtual void setPosition(qreal pos); + virtual void layoutVisibleItems(); + + virtual void updateSections(); + void createSection(FxListItemSG *); + void updateCurrentSection(); - FxListItemSG *nextVisibleItem() const { - const qreal pos = isRightToLeft() ? -position()-size() : position(); - bool foundFirst = false; - for (int i = 0; i < visibleItems.count(); ++i) { - FxListItemSG *item = visibleItems.at(i); - if (item->index != -1) { - if (foundFirst) - return item; - else if (item->position() < pos && item->endPosition() > pos) - foundFirst = true; - } - } - return 0; - } + virtual qreal headerSize() const; + virtual qreal footerSize() const; + virtual void updateHeader(); + virtual void updateFooter(); - // Returns the item before modelIndex, if created. - // May return an item marked for removal. - FxListItemSG *itemBefore(int modelIndex) const { - if (modelIndex < visibleIndex) - return 0; - int idx = 1; - int lastIndex = -1; - while (idx < visibleItems.count()) { - FxListItemSG *item = visibleItems.at(idx); - if (item->index != -1) - lastIndex = item->index; - if (item->index == modelIndex) - return visibleItems.at(idx-1); - ++idx; - } - if (lastIndex == modelIndex-1) - return visibleItems.last(); - return 0; - } + virtual void changedVisibleIndex(int newIndex); + virtual void initializeCurrentItem(); - void regenerate() { - Q_Q(QSGListView); - if (q->isComponentComplete()) { - if (header) { - // XXX todo - the original did scene()->removeItem(). Why? - header->item->setParentItem(0); - header->item->deleteLater(); - delete header; - header = 0; - } - if (footer) { - // XXX todo - the original did scene()->removeItem(). Why? - footer->item->setParentItem(0); - footer->item->deleteLater(); - delete footer; - footer = 0; - } - updateHeader(); - updateFooter(); - clear(); - setPosition(0); - q->refill(); - updateCurrent(currentIndex); - } - } + void updateAverage(); - void mirrorChange() { - Q_Q(QSGListView); - regenerate(); - emit q->effectiveLayoutDirectionChanged(); - } + void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + virtual void fixupPosition(); + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); + virtual void flick(QSGItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); - bool isRightToLeft() const { - Q_Q(const QSGListView); - return orient == QSGListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft; - } + QSGListView::Orientation orient; + qreal visiblePos; + qreal averageSize; + qreal spacing; + QSGListView::SnapMode snapMode; - qreal position() const { - Q_Q(const QSGListView); - return orient == QSGListView::Vertical ? q->contentY() : q->contentX(); - } - void setPosition(qreal pos) { - Q_Q(QSGListView); - if (orient == QSGListView::Vertical) { - q->QSGFlickable::setContentY(pos); - } else { - if (isRightToLeft()) - q->QSGFlickable::setContentX(-pos-size()); - else - q->QSGFlickable::setContentX(pos); - } - } - qreal size() const { - Q_Q(const QSGListView); - return orient == QSGListView::Vertical ? q->height() : q->width(); - } + QSmoothedAnimation *highlightPosAnimator; + QSmoothedAnimation *highlightSizeAnimator; + qreal highlightMoveSpeed; + qreal highlightResizeSpeed; + int highlightResizeDuration; - qreal originPosition() const { - qreal pos = 0; - if (!visibleItems.isEmpty()) { - pos = (*visibleItems.constBegin())->position(); - if (visibleIndex > 0) - pos -= visibleIndex * (averageSize + spacing); - } - return pos; - } + QSGViewSection *sectionCriteria; + QString currentSection; + static const int sectionCacheSize = 4; + QSGItem *sectionCache[sectionCacheSize]; - qreal lastPosition() const { - 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 = model->count() - visibleItems.at(i)->index - 1; - break; - } - } - pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing); - } else if (model && model->count()) { - pos = model->count() * averageSize + (model->count()-1) * spacing; - } - return pos; - } + qreal overshootDist; + bool correctFlick : 1; + bool inFlickCorrection : 1; - qreal startPosition() const { - return isRightToLeft() ? -lastPosition()-1 : originPosition(); - } + QSGListViewPrivate() + : orient(QSGListView::Vertical) + , visiblePos(0) + , averageSize(100.0), spacing(0.0) + , snapMode(QSGListView::NoSnap) + , highlightPosAnimator(0), highlightSizeAnimator(0) + , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1) + , sectionCriteria(0) + , overshootDist(0.0), correctFlick(false), inFlickCorrection(false) + {} +}; - qreal endPosition() const { - return isRightToLeft() ? -originPosition()-1 : lastPosition(); - } - - qreal positionAt(int modelIndex) const { - if (FxListItemSG *item = visibleItem(modelIndex)) - return item->position(); - if (!visibleItems.isEmpty()) { - if (modelIndex < visibleIndex) { - int count = visibleIndex - modelIndex; - qreal cs = 0; - if (modelIndex == currentIndex && currentItem) { - cs = currentItem->size() + spacing; - --count; - } - return (*visibleItems.constBegin())->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; - } - } - return 0; - } +bool QSGListViewPrivate::isContentFlowReversed() const +{ + return isRightToLeft(); +} - qreal endPositionAt(int modelIndex) const { - if (FxListItemSG *item = visibleItem(modelIndex)) - return item->endPosition(); - if (!visibleItems.isEmpty()) { - if (modelIndex < visibleIndex) { - int count = visibleIndex - modelIndex; - return (*visibleItems.constBegin())->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); - } - } - return 0; - } +Qt::Orientation QSGListViewPrivate::layoutOrientation() const +{ + return static_cast(orient); +} - QString sectionAt(int modelIndex) { - if (FxListItemSG *item = visibleItem(modelIndex)) - return item->attached->section(); +bool QSGListViewPrivate::isRightToLeft() const +{ + Q_Q(const QSGListView); + return orient == QSGListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft; +} - QString section; - if (sectionCriteria) { - QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); - section = sectionCriteria->sectionString(propValue); +FxViewItem *QSGListViewPrivate::nextVisibleItem() const +{ + const qreal pos = isRightToLeft() ? -position()-size() : position(); + bool foundFirst = false; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index != -1) { + if (foundFirst) + return item; + else if (item->position() < pos && item->endPosition() > pos) + foundFirst = true; } - - return section; } + return 0; +} - bool isValid() const { - return model && model->count() && model->isValid(); - } +// Returns the item before modelIndex, if created. +// May return an item marked for removal. +FxViewItem *QSGListViewPrivate::itemBefore(int modelIndex) const +{ + if (modelIndex < visibleIndex) + return 0; + int idx = 1; + int lastIndex = -1; + while (idx < visibleItems.count()) { + FxViewItem *item = visibleItems.at(idx); + if (item->index != -1) + lastIndex = item->index; + if (item->index == modelIndex) + return visibleItems.at(idx-1); + ++idx; + } + if (lastIndex == modelIndex-1) + return visibleItems.last(); + return 0; +} - qreal snapPosAt(qreal pos) { - if (FxListItemSG *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 - originPosition()) / averageSize) * averageSize + originPosition(); +void QSGListViewPrivate::setPosition(qreal pos) +{ + Q_Q(QSGListView); + if (orient == QSGListView::Vertical) { + q->QSGFlickable::setContentY(pos); + } else { + if (isRightToLeft()) + q->QSGFlickable::setContentX(-pos-size()); + else + q->QSGFlickable::setContentX(pos); } +} - FxListItemSG *snapItemAt(qreal pos) { - FxListItemSG *snapItem = 0; - for (int i = 0; i < visibleItems.count(); ++i) { - FxListItemSG *item = visibleItems[i]; - if (item->index == -1) - continue; - 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; +qreal QSGListViewPrivate::originPosition() const +{ + qreal pos = 0; + if (!visibleItems.isEmpty()) { + pos = (*visibleItems.constBegin())->position(); + if (visibleIndex > 0) + pos -= visibleIndex * (averageSize + spacing); } + return pos; +} - int lastVisibleIndex() const { - int lastIndex = -1; +qreal QSGListViewPrivate::lastPosition() const +{ + qreal pos = 0; + if (!visibleItems.isEmpty()) { + int invisibleCount = visibleItems.count() - visibleIndex; for (int i = visibleItems.count()-1; i >= 0; --i) { - FxListItemSG *listItem = visibleItems.at(i); - if (listItem->index != -1) { - lastIndex = listItem->index; + if (visibleItems.at(i)->index != -1) { + invisibleCount = model->count() - visibleItems.at(i)->index - 1; break; } } - return lastIndex; + pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing); + } else if (model && model->count()) { + pos = model->count() * averageSize + (model->count()-1) * spacing; } + return pos; +} - // map a model index to visibleItems index. - int mapFromModel(int modelIndex) const { - if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) - return -1; - for (int i = 0; i < visibleItems.count(); ++i) { - FxListItemSG *listItem = visibleItems.at(i); - if (listItem->index == modelIndex) - return i; - if (listItem->index > modelIndex) - return -1; +qreal QSGListViewPrivate::startPosition() const +{ + return isRightToLeft() ? -lastPosition()-1 : originPosition(); +} + +qreal QSGListViewPrivate::endPosition() const +{ + return isRightToLeft() ? -originPosition()-1 : lastPosition(); +} + +qreal QSGListViewPrivate::positionAt(int modelIndex) const +{ + if (FxViewItem *item = visibleItem(modelIndex)) + return item->position(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = visibleIndex - modelIndex; + qreal cs = 0; + if (modelIndex == currentIndex && currentItem) { + cs = currentItem->size() + spacing; + --count; + } + return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs; + } else { + int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1; + return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1; } - return -1; // Not in visibleList } + return 0; +} - void updateViewport() { - Q_Q(QSGListView); - if (orient == QSGListView::Vertical) { - q->setContentHeight(endPosition() - startPosition() + 1); +qreal QSGListViewPrivate::endPositionAt(int modelIndex) const +{ + if (FxViewItem *item = visibleItem(modelIndex)) + return item->endPosition(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = visibleIndex - modelIndex; + return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1; } else { - q->setContentWidth(endPosition() - startPosition() + 1); + int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1; + return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing); } } + return 0; +} - void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { - Q_Q(QSGListView); - QSGFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); - if (!q->isComponentComplete()) - return; - if (item != contentItem && (!highlight || item != highlight->item)) { - if ((orient == QSGListView::Vertical && newGeometry.height() != oldGeometry.height()) - || (orient == QSGListView::Horizontal && newGeometry.width() != oldGeometry.width())) { - scheduleLayout(); - } - } - if ((header && header->item == item) || (footer && footer->item == item)) { - if (header) - updateHeader(); - if (footer) - updateFooter(); - } - if (currentItem && currentItem->item == item) - updateHighlight(); - if (trackedItem && trackedItem->item == item) - q->trackedPositionChanged(); +QString QSGListViewPrivate::sectionAt(int modelIndex) +{ + if (FxViewItem *item = visibleItem(modelIndex)) + return item->attached->section(); + + QString section; + if (sectionCriteria) { + QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); + section = sectionCriteria->sectionString(propValue); } - // for debugging only - void checkVisible() const { - int skip = 0; - for (int i = 0; i < visibleItems.count(); ++i) { - FxListItemSG *listItem = visibleItems.at(i); - if (listItem->index == -1) { - ++skip; - } else if (listItem->index != visibleIndex + i - skip) { - qFatal("index %d %d %d", visibleIndex, i, listItem->index); - } - } + return section; +} + +qreal QSGListViewPrivate::snapPosAt(qreal pos) +{ + if (FxViewItem *snapItem = snapItemAt(pos)) + return snapItem->position(); + if (visibleItems.count()) { + qreal firstPos = (*visibleItems.constBegin())->position(); + qreal endPos = (*(--visibleItems.constEnd()))->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 - originPosition()) / averageSize) * averageSize + originPosition(); +} - void refill(qreal from, qreal to, bool doBuffer = false); - void scheduleLayout(); - void layout(); - void updateUnrequestedIndexes(); - void updateUnrequestedPositions(); - void updateTrackedItem(); - void createHighlight(); - void updateHighlight(); - void createSection(FxListItemSG *); - void updateSections(); - void updateCurrentSection(); - void updateCurrent(int); - void updateAverage(); - void updateHeader(); - void updateFooter(); - void fixupPosition(); - void positionViewAtIndex(int index, int mode); - virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); - virtual void flick(QSGFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); +FxViewItem *QSGListViewPrivate::snapItemAt(qreal pos) +{ + FxViewItem *snapItem = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == -1) + continue; + 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; +} - QDeclarativeGuard model; - QVariant modelVariant; - QList visibleItems; - QHash unrequestedItems; - FxListItemSG *currentItem; - QSGListView::Orientation orient; - Qt::LayoutDirection layoutDirection; - qreal visiblePos; - int visibleIndex; - qreal averageSize; - int currentIndex; - int requestedIndex; - int itemCount; - qreal highlightRangeStart; - qreal highlightRangeEnd; - QDeclarativeComponent *highlightComponent; - FxListItemSG *highlight; - FxListItemSG *trackedItem; - enum MovementReason { Other, SetIndex, Mouse }; - MovementReason moveReason; - int buffer; - QSmoothedAnimation *highlightPosAnimator; - QSmoothedAnimation *highlightSizeAnimator; - QSGViewSection *sectionCriteria; - QString currentSection; - static const int sectionCacheSize = 4; - QSGItem *sectionCache[sectionCacheSize]; - qreal spacing; - qreal highlightMoveSpeed; - int highlightMoveDuration; - qreal highlightResizeSpeed; - int highlightResizeDuration; - QSGListView::HighlightRangeMode highlightRange; - QSGListView::SnapMode snapMode; - qreal overshootDist; - QDeclarativeComponent *footerComponent; - FxListItemSG *footer; - QDeclarativeComponent *headerComponent; - FxListItemSG *header; - enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; - int bufferMode; - mutable qreal minExtent; - mutable qreal maxExtent; - - bool ownModel : 1; - 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; - bool highlightRangeStartValid : 1; - bool highlightRangeEndValid : 1; - mutable bool minExtentDirty : 1; - mutable bool maxExtentDirty : 1; -}; +void QSGListViewPrivate::changedVisibleIndex(int newIndex) +{ + visiblePos = positionAt(newIndex); + visibleIndex = newIndex; +} void QSGListViewPrivate::init() { - Q_Q(QSGListView); - QSGItemPrivate::get(contentItem)->childrenDoNotOverlap = true; - q->setFlag(QSGItem::ItemIsFocusScope); - addItemChangeListener(this, Geometry); - QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); - q->setFlickableDirection(QSGFlickable::VerticalFlick); ::memset(sectionCache, 0, sizeof(QSGItem*) * sectionCacheSize); } void QSGListViewPrivate::clear() { - timeline.clear(); - for (int i = 0; i < visibleItems.count(); ++i) - releaseItem(visibleItems.at(i)); - visibleItems.clear(); for (int i = 0; i < sectionCacheSize; ++i) { delete sectionCache[i]; sectionCache[i] = 0; } - visiblePos = header ? header->size() : 0; - visibleIndex = 0; - releaseItem(currentItem); - currentItem = 0; - createHighlight(); - trackedItem = 0; - minExtentDirty = true; - maxExtentDirty = true; - itemCount = 0; + visiblePos = header ? headerSize() : 0; + QSGItemViewPrivate::clear(); } -FxListItemSG *QSGListViewPrivate::createItem(int modelIndex) +FxViewItem *QSGListViewPrivate::newViewItem(int modelIndex, QSGItem *item) { Q_Q(QSGListView); - // create object - requestedIndex = modelIndex; - FxListItemSG *listItem = 0; - if (QSGItem *item = model->item(modelIndex, false)) { - listItem = new FxListItemSG(item, q); - listItem->index = modelIndex; - // initialise attached properties - if (sectionCriteria) { - QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); - listItem->attached->m_section = sectionCriteria->sectionString(propValue); - if (modelIndex > 0) { - if (FxListItemSG *item = itemBefore(modelIndex)) - listItem->attached->m_prevSection = item->attached->section(); - else - listItem->attached->m_prevSection = sectionAt(modelIndex-1); - } - if (modelIndex < model->count()-1) { - if (FxListItemSG *item = visibleItem(modelIndex+1)) - listItem->attached->m_nextSection = item->attached->section(); - else - listItem->attached->m_nextSection = sectionAt(modelIndex+1); - } - } - if (model->completePending()) { - // complete - listItem->item->setZ(1); - listItem->item->setParentItem(q->contentItem()); - model->completeItem(); - } else { - listItem->item->setParentItem(q->contentItem()); + + FxListItemSG *listItem = new FxListItemSG(item, q, false); + listItem->index = modelIndex; + + // initialise attached properties + if (sectionCriteria) { + QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); + listItem->attached->m_section = sectionCriteria->sectionString(propValue); + if (modelIndex > 0) { + if (FxViewItem *item = itemBefore(modelIndex)) + listItem->attached->m_prevSection = item->attached->section(); + else + listItem->attached->m_prevSection = sectionAt(modelIndex-1); } - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); - if (sectionCriteria && sectionCriteria->delegate()) { - if (listItem->attached->m_prevSection != listItem->attached->m_section) - createSection(listItem); + if (modelIndex < model->count()-1) { + if (FxViewItem *item = visibleItem(modelIndex+1)) + listItem->attached->m_nextSection = static_cast(item->attached)->section(); + else + listItem->attached->m_nextSection = sectionAt(modelIndex+1); } - unrequestedItems.remove(listItem->item); } - requestedIndex = -1; return listItem; } -void QSGListViewPrivate::releaseItem(FxListItemSG *item) +void QSGListViewPrivate::initializeViewItem(FxViewItem *item) { - Q_Q(QSGListView); - if (!item || !model) - return; - if (trackedItem == item) - trackedItem = 0; QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item); - itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry); - 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)); + itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); + + if (sectionCriteria && sectionCriteria->delegate()) { + if (item->attached->m_prevSection != item->attached->m_section) + createSection(static_cast(item)); } - if (item->section) { - int i = 0; - do { - if (!sectionCache[i]) { - sectionCache[i] = item->section; - sectionCache[i]->setVisible(false); - item->section = 0; - break; - } - ++i; - } while (i < sectionCacheSize); - delete item->section; +} + +void QSGListViewPrivate::releaseItem(FxViewItem *item) +{ + if (item) { + FxListItemSG* listItem = static_cast(item); + if (listItem->section) { + int i = 0; + do { + if (!sectionCache[i]) { + sectionCache[i] = listItem->section; + sectionCache[i]->setVisible(false); + listItem->section = 0; + break; + } + ++i; + } while (i < sectionCacheSize); + delete listItem->section; + } } - delete item; + QSGItemViewPrivate::releaseItem(item); } -void QSGListViewPrivate::refill(qreal from, qreal to, bool doBuffer) +bool QSGListViewPrivate::addVisibleItems(int fillFrom, int fillTo, bool doBuffer) { - Q_Q(QSGListView); - if (!isValid() || !q->isComponentComplete()) - return; - itemCount = model->count(); - qreal bufferFrom = from - buffer; - qreal bufferTo = to + buffer; - qreal fillFrom = from; - qreal fillTo = to; - if (doBuffer && (bufferMode & BufferAfter)) - fillTo = bufferTo; - if (doBuffer && (bufferMode & BufferBefore)) - fillFrom = bufferFrom; - - bool haveValidItems = false; - int modelIndex = visibleIndex; qreal itemEnd = visiblePos-1; - if (!visibleItems.isEmpty()) { + if (visibleItems.count()) { visiblePos = (*visibleItems.constBegin())->position(); itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing; - int i = visibleItems.count() - 1; - while (i > 0 && visibleItems.at(i)->index == -1) - --i; - if (visibleItems.at(i)->index != -1) { - haveValidItems = true; - modelIndex = visibleItems.at(i)->index + 1; - } } + int modelIndex = findLastVisibleIndex(); + bool haveValidItems = modelIndex >= 0; + modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1; + if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing || fillTo < visiblePos - averageSize - spacing)) { // We've jumped more than a page. Estimate which items are now @@ -771,7 +578,7 @@ void QSGListViewPrivate::refill(qreal from, qreal to, bool doBuffer) qreal pos = itemEnd + 1; while (modelIndex < model->count() && pos <= fillTo) { // qDebug() << "refill: append item" << modelIndex << "pos" << pos; - if (!(item = createItem(modelIndex))) + if (!(item = static_cast(createItem(modelIndex)))) break; item->setPosition(pos); pos += item->size() + spacing; @@ -783,7 +590,7 @@ void QSGListViewPrivate::refill(qreal from, qreal to, bool doBuffer) } while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) { // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos; - if (!(item = createItem(visibleIndex-1))) + if (!(item = static_cast(createItem(visibleIndex-1)))) break; --visibleIndex; visiblePos -= item->size() + spacing; @@ -794,143 +601,90 @@ void QSGListViewPrivate::refill(qreal from, qreal to, bool doBuffer) break; } - 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; + return changed; +} + +bool QSGListViewPrivate::removeNonVisibleItems(int bufferFrom, int bufferTo) +{ + FxViewItem *item = 0; + bool changed = false; + + 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; + 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) { - minExtentDirty = true; - maxExtentDirty = true; - if (visibleItems.count()) - visiblePos = (*visibleItems.constBegin())->position(); - updateAverage(); - if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) { - currentItem->setPosition(positionAt(currentIndex)); - updateHighlight(); - } - - if (sectionCriteria) - updateCurrentSection(); - if (header) - updateHeader(); - if (footer) - updateFooter(); - updateViewport(); - updateUnrequestedPositions(); - } else if (!doBuffer && buffer && bufferMode != NoBuffer) { - refill(from, to, true); + visibleItems.removeLast(); + releaseItem(item); + changed = true; } - lazyRelease = false; + + return changed; } -void QSGListViewPrivate::scheduleLayout() +void QSGListViewPrivate::visibleItemsChanged() { - Q_Q(QSGListView); - if (!layoutScheduled) { - layoutScheduled = true; - q->polish(); + if (visibleItems.count()) + visiblePos = (*visibleItems.constBegin())->position(); + updateAverage(); + if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) { + static_cast(currentItem)->setPosition(positionAt(currentIndex)); + updateHighlight(); } + if (sectionCriteria) + updateCurrentSection(); + updateHeader(); + updateFooter(); + updateViewport(); + updateUnrequestedPositions(); } -void QSGListViewPrivate::layout() +void QSGListViewPrivate::layoutVisibleItems() { - Q_Q(QSGListView); - layoutScheduled = false; - if (!isValid() && !visibleItems.count()) { - clear(); - setPosition(0); - return; - } if (!visibleItems.isEmpty()) { - bool fixedCurrent = currentItem && visibleItems.first()->item == currentItem->item; - qreal sum = visibleItems.first()->size(); - qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing; + bool fixedCurrent = currentItem && (*visibleItems.constBegin())->item == currentItem->item; + qreal sum = (*visibleItems.constBegin())->size(); + qreal pos = (*visibleItems.constBegin())->position() + (*visibleItems.constBegin())->size() + spacing; for (int i=1; i < visibleItems.count(); ++i) { - FxListItemSG *item = visibleItems.at(i); + FxListItemSG *item = static_cast(visibleItems.at(i)); item->setPosition(pos); pos += item->size() + spacing; sum += item->size(); fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item); } averageSize = qRound(sum / visibleItems.count()); - // move current item if it is not a visible item. - if (currentIndex >= 0 && currentItem && !fixedCurrent) - currentItem->setPosition(positionAt(currentIndex)); - } - q->refill(); - minExtentDirty = true; - maxExtentDirty = true; - updateHighlight(); - if (!q->isMoving() && !q->isFlicking()) { - fixupPosition(); - q->refill(); - } - if (header) - updateHeader(); - if (footer) - updateFooter(); - updateViewport(); -} -void QSGListViewPrivate::updateUnrequestedIndexes() -{ - Q_Q(QSGListView); - QHash::iterator it; - for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) - *it = model->indexOf(it.key(), q); -} - -void QSGListViewPrivate::updateUnrequestedPositions() -{ - Q_Q(QSGListView); - if (unrequestedItems.count()) { - qreal pos = position(); - QHash::const_iterator it; - for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) { - QSGItem *item = it.key(); - if (orient == QSGListView::Vertical) { - if (item->y() + item->height() > pos && item->y() < pos + q->height()) - item->setY(positionAt(*it)); - } else { - if (item->x() + item->width() > pos && item->x() < pos + q->width()) { - if (isRightToLeft()) - item->setX(-positionAt(*it)-item->width()); - else - item->setX(positionAt(*it)); - } - } + // move current item if it is not a visible item. + if (currentIndex >= 0 && currentItem && !fixedCurrent) { + static_cast(currentItem)->setPosition(positionAt(currentIndex)); } } } -void QSGListViewPrivate::updateTrackedItem() +void QSGListViewPrivate::repositionPackageItemAt(QSGItem *item, int index) { Q_Q(QSGListView); - FxListItemSG *item = currentItem; - if (highlight) - item = highlight; - trackedItem = item; - if (trackedItem) - q->trackedPositionChanged(); + qreal pos = position(); + if (orient == QSGListView::Vertical) { + if (item->y() + item->height() > pos && item->y() < pos + q->height()) + item->setY(positionAt(index)); + } else { + if (item->x() + item->width() > pos && item->x() < pos + q->width()) { + if (isRightToLeft()) + item->setX(-positionAt(index)-item->width()); + else + item->setX(positionAt(index)); + } + } } void QSGListViewPrivate::createHighlight() @@ -940,61 +694,39 @@ void QSGListViewPrivate::createHighlight() if (highlight) { if (trackedItem == highlight) trackedItem = 0; - highlight->item->setParentItem(0); - highlight->item->deleteLater(); delete highlight; highlight = 0; + delete highlightPosAnimator; delete highlightSizeAnimator; highlightPosAnimator = 0; highlightSizeAnimator = 0; + changed = true; } if (currentItem) { - QSGItem *item = 0; - if (highlightComponent) { - QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q)); - QObject *nobj = highlightComponent->create(highlightContext); - if (nobj) { - QDeclarative_setParent_noEvent(highlightContext, nobj); - item = qobject_cast(nobj); - if (!item) - delete nobj; - } else { - delete highlightContext; - } - } else { - item = new QSGItem; - } + QSGItem *item = createHighlightItem(); if (item) { - QDeclarative_setParent_noEvent(item, q->contentItem()); - item->setParentItem(q->contentItem()); - highlight = new FxListItemSG(item, q); - if (currentItem && autoHighlight) { - if (orient == QSGListView::Vertical) { - highlight->item->setHeight(currentItem->item->height()); - } else { - highlight->item->setWidth(currentItem->item->width()); - } - highlight->setPosition(currentItem->itemPosition()); + FxListItemSG *newHighlight = new FxListItemSG(item, q, true); + + if (autoHighlight) { + newHighlight->setSize(static_cast(currentItem)->itemSize()); + newHighlight->setPosition(static_cast(currentItem)->itemPosition()); } - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); const QLatin1String posProp(orient == QSGListView::Vertical ? "y" : "x"); highlightPosAnimator = new QSmoothedAnimation(q); - highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp); + highlightPosAnimator->target = QDeclarativeProperty(item, posProp); highlightPosAnimator->velocity = highlightMoveSpeed; highlightPosAnimator->userDuration = highlightMoveDuration; + const QLatin1String sizeProp(orient == QSGListView::Vertical ? "height" : "width"); highlightSizeAnimator = new QSmoothedAnimation(q); highlightSizeAnimator->velocity = highlightResizeSpeed; highlightSizeAnimator->userDuration = highlightResizeDuration; - highlightSizeAnimator->target = QDeclarativeProperty(highlight->item, sizeProp); - if (autoHighlight) { - highlightPosAnimator->restart(); - highlightSizeAnimator->restart(); - } + highlightSizeAnimator->target = QDeclarativeProperty(item, sizeProp); + + highlight = newHighlight; changed = true; } } @@ -1008,10 +740,11 @@ void QSGListViewPrivate::updateHighlight() createHighlight(); if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) { // auto-update highlight + FxListItemSG *listItem = static_cast(currentItem); highlightPosAnimator->to = isRightToLeft() - ? -currentItem->itemPosition()-currentItem->itemSize() - : currentItem->itemPosition(); - highlightSizeAnimator->to = currentItem->itemSize(); + ? -listItem->itemPosition()-listItem->itemSize() + : listItem->itemPosition(); + highlightSizeAnimator->to = listItem->itemSize(); if (orient == QSGListView::Vertical) { if (highlight->item->width() == 0) highlight->item->setWidth(currentItem->item->width()); @@ -1019,12 +752,19 @@ void QSGListViewPrivate::updateHighlight() if (highlight->item->height() == 0) highlight->item->setHeight(currentItem->item->height()); } + highlightPosAnimator->restart(); highlightSizeAnimator->restart(); } updateTrackedItem(); } +void QSGListViewPrivate::resetHighlightPosition() +{ + if (highlight && currentItem) + static_cast(highlight)->setPosition(static_cast(currentItem)->itemPosition()); +} + void QSGListViewPrivate::createSection(FxListItemSG *listItem) { Q_Q(QSGListView); @@ -1093,14 +833,14 @@ void QSGListViewPrivate::updateSections() QSGListViewAttached *prevAtt = 0; int idx = -1; for (int i = 0; i < visibleItems.count(); ++i) { - QSGListViewAttached *attached = visibleItems.at(i)->attached; + QSGListViewAttached *attached = static_cast(visibleItems.at(i)->attached); attached->setPrevSection(prevSection); if (visibleItems.at(i)->index != -1) { QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property()); attached->setSection(sectionCriteria->sectionString(propValue)); idx = visibleItems.at(i)->index; } - createSection(visibleItems.at(i)); + createSection(static_cast(visibleItems.at(i))); if (prevAtt) prevAtt->setNextSection(attached->section()); prevSection = attached->section(); @@ -1133,63 +873,36 @@ void QSGListViewPrivate::updateCurrentSection() if (index < visibleItems.count()) newSection = visibleItems.at(index)->attached->section(); else - newSection = visibleItems.first()->attached->section(); + newSection = (*visibleItems.constBegin())->attached->section(); if (newSection != currentSection) { currentSection = newSection; emit q->currentSectionChanged(); } } -void QSGListViewPrivate::updateCurrent(int modelIndex) +void QSGListViewPrivate::initializeCurrentItem() { - Q_Q(QSGListView); - 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; - } - - if (currentItem && currentIndex == modelIndex) { - updateHighlight(); - return; - } - FxListItemSG *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()) { + FxListItemSG *listItem = static_cast(currentItem); + + if (currentIndex == visibleIndex - 1 && visibleItems.count()) { // We can calculate exact postion in this case - currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing); + listItem->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)); + listItem->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); + if (listItem->section) + listItem->section->setVisible(false); + if (visibleItems.isEmpty()) - averageSize = currentItem->size(); + averageSize = listItem->size(); } - updateHighlight(); - emit q->currentIndexChanged(); - // Release the old current item - releaseItem(oldCurrentItem); } void QSGListViewPrivate::updateAverage() @@ -1202,83 +915,82 @@ void QSGListViewPrivate::updateAverage() averageSize = qRound(sum / visibleItems.count()); } +qreal QSGListViewPrivate::headerSize() const +{ + return header ? header->size() : 0.0; +} + +qreal QSGListViewPrivate::footerSize() const +{ + return footer ? footer->size() : 0.0; +} + void QSGListViewPrivate::updateFooter() { Q_Q(QSGListView); - if (!footer && footerComponent) { - QSGItem *item = 0; - QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); - QObject *nobj = footerComponent->create(context); - if (nobj) { - QDeclarative_setParent_noEvent(context, nobj); - item = qobject_cast(nobj); - if (!item) - delete nobj; - } else { - delete context; - } - if (item) { - QDeclarative_setParent_noEvent(item, q->contentItem()); - item->setParentItem(q->contentItem()); - item->setZ(1); - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); - footer = new FxListItemSG(item, q); - } + + if (!footer) { + QSGItem *item = createComponentItem(footerComponent, true); + if (!item) + return; + item->setZ(1); + footer = new FxListItemSG(item, q, true); } - if (footer) { - if (visibleItems.count()) { - qreal endPos = lastPosition() + 1; - if (lastVisibleIndex() == model->count()-1) { - footer->setPosition(endPos); - } else { - qreal visiblePos = position() + q->height(); - if (endPos <= visiblePos || footer->position() < endPos) - footer->setPosition(endPos); - } + + FxListItemSG *listItem = static_cast(footer); + if (visibleItems.count()) { + qreal endPos = lastPosition() + 1; + if (findLastVisibleIndex() == model->count()-1) { + listItem->setPosition(endPos); } else { - footer->setPosition(visiblePos); + qreal visiblePos = position() + q->height(); + if (endPos <= visiblePos || listItem->position() < endPos) + listItem->setPosition(endPos); } + } else { + listItem->setPosition(visiblePos); } } void QSGListViewPrivate::updateHeader() { Q_Q(QSGListView); - if (!header && headerComponent) { - QSGItem *item = 0; - QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); - QObject *nobj = headerComponent->create(context); - if (nobj) { - QDeclarative_setParent_noEvent(context, nobj); - item = qobject_cast(nobj); - if (!item) - delete nobj; - } else { - delete context; - } - if (item) { - QDeclarative_setParent_noEvent(item, q->contentItem()); - item->setParentItem(q->contentItem()); - item->setZ(1); - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); - header = new FxListItemSG(item, q); - } + if (!header) { + QSGItem *item = createComponentItem(headerComponent, true); + if (!item) + return; + item->setZ(1); + header = new FxListItemSG(item, q, true); } - if (header) { + + FxListItemSG *listItem = static_cast(header); + if (listItem) { if (visibleItems.count()) { qreal startPos = originPosition(); if (visibleIndex == 0) { - header->setPosition(startPos - header->size()); + listItem->setPosition(startPos - headerSize()); } else { - if (position() <= startPos || header->position() > startPos - header->size()) - header->setPosition(startPos - header->size()); + if (position() <= startPos || listItem->position() > startPos - headerSize()) + listItem->setPosition(startPos - headerSize()); } } else { if (itemCount == 0) - visiblePos = header->size(); - header->setPosition(0); + visiblePos = headerSize(); + listItem->setPosition(0); + } + } +} + +void QSGListViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QSGListView); + QSGItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + if (!q->isComponentComplete()) + return; + if (item != contentItem && (!highlight || item != highlight->item)) { + if ((orient == QSGListView::Vertical && newGeometry.height() != oldGeometry.height()) + || (orient == QSGListView::Horizontal && newGeometry.width() != oldGeometry.width())) { + scheduleLayout(); } } } @@ -1320,9 +1032,9 @@ void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) if (currentItem && haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange && moveReason != QSGListViewPrivate::SetIndex) { updateHighlight(); - qreal pos = currentItem->itemPosition(); - if (viewPos < pos + currentItem->itemSize() - highlightEnd) - viewPos = pos + currentItem->itemSize() - highlightEnd; + qreal pos = static_cast(currentItem)->itemPosition(); + if (viewPos < pos + static_cast(currentItem)->itemSize() - highlightEnd) + viewPos = pos + static_cast(currentItem)->itemSize() - highlightEnd; if (viewPos > pos - highlightStart) viewPos = pos - highlightStart; if (isRightToLeft()) @@ -1340,12 +1052,12 @@ void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) vTime = timeline.time(); } else if (snapMode != QSGListView::NoSnap && moveReason != QSGListViewPrivate::SetIndex) { qreal tempPosition = isRightToLeft() ? -position()-size() : position(); - FxListItemSG *topItem = snapItemAt(tempPosition+highlightStart); - FxListItemSG *bottomItem = snapItemAt(tempPosition+highlightEnd); + FxViewItem *topItem = snapItemAt(tempPosition+highlightStart); + FxViewItem *bottomItem = snapItemAt(tempPosition+highlightEnd); qreal pos; bool isInBounds = -position() > maxExtent && -position() < minExtent; if (topItem && isInBounds) { - if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2) { + if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+headerSize()/2) { pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart; } else { if (isRightToLeft()) @@ -1359,7 +1071,7 @@ void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) else pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent); } else { - QSGFlickablePrivate::fixup(data, minExtent, maxExtent); + QSGItemViewPrivate::fixup(data, minExtent, maxExtent); return; } @@ -1375,7 +1087,7 @@ void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) vTime = timeline.time(); } } else { - QSGFlickablePrivate::fixup(data, minExtent, maxExtent); + QSGItemViewPrivate::fixup(data, minExtent, maxExtent); } data.inOvershoot = false; fixupMode = Normal; @@ -1390,7 +1102,7 @@ void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, moveReason = Mouse; if ((!haveHighlightRange || highlightRange != QSGListView::StrictlyEnforceRange) && snapMode == QSGListView::NoSnap) { correctFlick = true; - QSGFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); + QSGItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); return; } qreal maxDistance = 0; @@ -1399,7 +1111,7 @@ void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, if (velocity > 0) { if (data.move.value() < minExtent) { if (snapMode == QSGListView::SnapOneItem) { - if (FxListItemSG *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem()) + if (FxViewItem *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem()) maxDistance = qAbs(item->position() + dataValue); } else { maxDistance = qAbs(minExtent - data.move.value()); @@ -1410,7 +1122,7 @@ void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, } else { if (data.move.value() > maxExtent) { if (snapMode == QSGListView::SnapOneItem) { - if (FxListItemSG *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem()) + if (FxViewItem *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem()) maxDistance = qAbs(item->position() + dataValue); } else { maxDistance = qAbs(maxExtent - data.move.value()); @@ -1529,304 +1241,32 @@ void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, correctFlick = false; timeline.reset(data.move); fixup(data, minExtent, maxExtent); - } -} - -//---------------------------------------------------------------------------- - -QSGListView::QSGListView(QSGItem *parent) - : QSGFlickable(*(new QSGListViewPrivate), parent) -{ - Q_D(QSGListView); - d->init(); -} - -QSGListView::~QSGListView() -{ - Q_D(QSGListView); - d->clear(); - if (d->ownModel) - delete d->model; - delete d->header; - delete d->footer; -} - -QVariant QSGListView::model() const -{ - Q_D(const QSGListView); - return d->modelVariant; -} - -void QSGListView::setModel(const QVariant &model) -{ - Q_D(QSGListView); - if (d->modelVariant == model) - return; - if (d->model) { - disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); - disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); - disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); - disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int))); - disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); - disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); - disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*))); - } - d->clear(); - QSGVisualModel *oldModel = d->model; - d->model = 0; - d->setPosition(0); - d->modelVariant = model; - QObject *object = qvariant_cast(model); - QSGVisualModel *vim = 0; - if (object && (vim = qobject_cast(object))) { - if (d->ownModel) { - delete oldModel; - d->ownModel = false; - } - d->model = vim; - } else { - if (!d->ownModel) { - d->model = new QSGVisualDataModel(qmlContext(this), this); - d->ownModel = true; - } else { - d->model = oldModel; - } - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - dataModel->setModel(model); - } - if (d->model) { - d->bufferMode = QSGListViewPrivate::BufferBefore | QSGListViewPrivate::BufferAfter; - if (isComponentComplete()) { - updateSections(); - refill(); - if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { - setCurrentIndex(0); - } else { - d->moveReason = QSGListViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->highlight->setPosition(d->currentItem->position()); - d->updateTrackedItem(); - } - } - d->updateViewport(); - } - 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))); - connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int))); - connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); - connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); - connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*))); - emit countChanged(); - } - emit modelChanged(); -} - -QDeclarativeComponent *QSGListView::delegate() const -{ - Q_D(const QSGListView); - if (d->model) { - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - return dataModel->delegate(); - } - - return 0; -} - -void QSGListView::setDelegate(QDeclarativeComponent *delegate) -{ - Q_D(QSGListView); - if (delegate == this->delegate()) - return; - if (!d->ownModel) { - d->model = new QSGVisualDataModel(qmlContext(this)); - d->ownModel = true; - } - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) { - int oldCount = dataModel->count(); - dataModel->setDelegate(delegate); - if (isComponentComplete()) { - for (int i = 0; i < d->visibleItems.count(); ++i) - d->releaseItem(d->visibleItems.at(i)); - d->visibleItems.clear(); - d->releaseItem(d->currentItem); - d->currentItem = 0; - updateSections(); - refill(); - d->moveReason = QSGListViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->highlight->setPosition(d->currentItem->position()); - d->updateTrackedItem(); - } - d->updateViewport(); - } - if (oldCount != dataModel->count()) - emit countChanged(); - } - emit delegateChanged(); -} - -int QSGListView::currentIndex() const -{ - Q_D(const QSGListView); - return d->currentIndex; -} - -void QSGListView::setCurrentIndex(int index) -{ - Q_D(QSGListView); - if (d->requestedIndex >= 0) // currently creating item - return; - d->currentIndexCleared = (index == -1); - if (index == d->currentIndex) - return; - if (isComponentComplete() && d->isValid()) { - d->moveReason = QSGListViewPrivate::SetIndex; - d->updateCurrent(index); - } else if (d->currentIndex != index) { - d->currentIndex = index; - emit currentIndexChanged(); - } -} - -QSGItem *QSGListView::currentItem() -{ - Q_D(QSGListView); - if (!d->currentItem) - return 0; - return d->currentItem->item; -} - -QSGItem *QSGListView::highlightItem() -{ - Q_D(QSGListView); - if (!d->highlight) - return 0; - return d->highlight->item; -} - -int QSGListView::count() const -{ - Q_D(const QSGListView); - if (d->model) - return d->model->count(); - return 0; -} - -QDeclarativeComponent *QSGListView::highlight() const -{ - Q_D(const QSGListView); - return d->highlightComponent; -} - -void QSGListView::setHighlight(QDeclarativeComponent *highlight) -{ - Q_D(QSGListView); - if (highlight != d->highlightComponent) { - d->highlightComponent = highlight; - d->createHighlight(); - if (d->currentItem) - d->updateHighlight(); - emit highlightChanged(); - } -} - -bool QSGListView::highlightFollowsCurrentItem() const -{ - Q_D(const QSGListView); - return d->autoHighlight; -} - -void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight) -{ - Q_D(QSGListView); - if (d->autoHighlight != autoHighlight) { - d->autoHighlight = autoHighlight; - if (autoHighlight) { - d->updateHighlight(); - } else { - if (d->highlightPosAnimator) - d->highlightPosAnimator->stop(); - if (d->highlightSizeAnimator) - d->highlightSizeAnimator->stop(); - } - emit highlightFollowsCurrentItemChanged(); - } -} - -//###Possibly rename these properties, since they are very useful even without a highlight? -qreal QSGListView::preferredHighlightBegin() const -{ - Q_D(const QSGListView); - return d->highlightRangeStart; -} - -void QSGListView::setPreferredHighlightBegin(qreal start) -{ - Q_D(QSGListView); - d->highlightRangeStartValid = true; - if (d->highlightRangeStart == start) - return; - d->highlightRangeStart = start; - d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - emit preferredHighlightBeginChanged(); -} - -void QSGListView::resetPreferredHighlightBegin() -{ - Q_D(QSGListView); - d->highlightRangeStartValid = false; - if (d->highlightRangeStart == 0) - return; - d->highlightRangeStart = 0; - emit preferredHighlightBeginChanged(); -} - -qreal QSGListView::preferredHighlightEnd() const -{ - Q_D(const QSGListView); - return d->highlightRangeEnd; -} - -void QSGListView::setPreferredHighlightEnd(qreal end) -{ - Q_D(QSGListView); - d->highlightRangeEndValid = true; - if (d->highlightRangeEnd == end) - return; - d->highlightRangeEnd = end; - d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - emit preferredHighlightEndChanged(); + } } -void QSGListView::resetPreferredHighlightEnd() +//---------------------------------------------------------------------------- + +QSGListView::QSGListView(QSGItem *parent) + : QSGItemView(*(new QSGListViewPrivate), parent) { - Q_D(QSGListView); - d->highlightRangeEndValid = false; - if (d->highlightRangeEnd == 0) - return; - d->highlightRangeEnd = 0; - emit preferredHighlightEndChanged(); } -QSGListView::HighlightRangeMode QSGListView::highlightRangeMode() const +QSGListView::~QSGListView() { - Q_D(const QSGListView); - return d->highlightRange; } -void QSGListView::setHighlightRangeMode(HighlightRangeMode mode) +void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight) { Q_D(QSGListView); - if (d->highlightRange == mode) - return; - d->highlightRange = mode; - d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - emit highlightRangeModeChanged(); + if (d->autoHighlight != autoHighlight) { + if (!autoHighlight) { + if (d->highlightPosAnimator) + d->highlightPosAnimator->stop(); + if (d->highlightSizeAnimator) + d->highlightSizeAnimator->stop(); + } + QSGItemView::setHighlightFollowsCurrentItem(autoHighlight); + } } qreal QSGListView::spacing() const @@ -1856,7 +1296,7 @@ void QSGListView::setOrientation(QSGListView::Orientation orientation) Q_D(QSGListView); if (d->orient != orientation) { d->orient = orientation; - if (d->orient == QSGListView::Vertical) { + if (d->orient == Vertical) { setContentWidth(-1); setFlickableDirection(VerticalFlick); setContentX(0); @@ -1870,66 +1310,6 @@ void QSGListView::setOrientation(QSGListView::Orientation orientation) } } -Qt::LayoutDirection QSGListView::layoutDirection() const -{ - Q_D(const QSGListView); - return d->layoutDirection; -} - -void QSGListView::setLayoutDirection(Qt::LayoutDirection layoutDirection) -{ - Q_D(QSGListView); - if (d->layoutDirection != layoutDirection) { - d->layoutDirection = layoutDirection; - d->regenerate(); - emit layoutDirectionChanged(); - emit effectiveLayoutDirectionChanged(); - } -} - -Qt::LayoutDirection QSGListView::effectiveLayoutDirection() const -{ - Q_D(const QSGListView); - if (d->effectiveLayoutMirror) - return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; - else - return d->layoutDirection; -} - -bool QSGListView::isWrapEnabled() const -{ - Q_D(const QSGListView); - return d->wrap; -} - -void QSGListView::setWrapEnabled(bool wrap) -{ - Q_D(QSGListView); - if (d->wrap == wrap) - return; - d->wrap = wrap; - emit keyNavigationWrapsChanged(); -} - -int QSGListView::cacheBuffer() const -{ - Q_D(const QSGListView); - return d->buffer; -} - -void QSGListView::setCacheBuffer(int b) -{ - Q_D(QSGListView); - if (d->buffer != b) { - d->buffer = b; - if (isComponentComplete()) { - d->bufferMode = QSGListViewPrivate::BufferBefore | QSGListViewPrivate::BufferAfter; - refill(); - } - emit cacheBufferChanged(); - } -} - QSGViewSection *QSGListView::sectionCriteria() { Q_D(QSGListView); @@ -1948,13 +1328,13 @@ QString QSGListView::currentSection() const qreal QSGListView::highlightMoveSpeed() const { - Q_D(const QSGListView);\ + Q_D(const QSGListView); return d->highlightMoveSpeed; } void QSGListView::setHighlightMoveSpeed(qreal speed) { - Q_D(QSGListView);\ + Q_D(QSGListView); if (d->highlightMoveSpeed != speed) { d->highlightMoveSpeed = speed; if (d->highlightPosAnimator) @@ -1963,32 +1343,25 @@ void QSGListView::setHighlightMoveSpeed(qreal speed) } } -int QSGListView::highlightMoveDuration() const -{ - Q_D(const QSGListView); - return d->highlightMoveDuration; -} - void QSGListView::setHighlightMoveDuration(int duration) { - Q_D(QSGListView);\ + Q_D(QSGListView); if (d->highlightMoveDuration != duration) { - d->highlightMoveDuration = duration; if (d->highlightPosAnimator) - d->highlightPosAnimator->userDuration = d->highlightMoveDuration; - emit highlightMoveDurationChanged(); + d->highlightPosAnimator->userDuration = duration; + QSGItemView::setHighlightMoveDuration(duration); } } qreal QSGListView::highlightResizeSpeed() const { - Q_D(const QSGListView);\ + Q_D(const QSGListView); return d->highlightResizeSpeed; } void QSGListView::setHighlightResizeSpeed(qreal speed) { - Q_D(QSGListView);\ + Q_D(QSGListView); if (d->highlightResizeSpeed != speed) { d->highlightResizeSpeed = speed; if (d->highlightSizeAnimator) @@ -2005,7 +1378,7 @@ int QSGListView::highlightResizeDuration() const void QSGListView::setHighlightResizeDuration(int duration) { - Q_D(QSGListView);\ + Q_D(QSGListView); if (d->highlightResizeDuration != duration) { d->highlightResizeDuration = duration; if (d->highlightSizeAnimator) @@ -2029,92 +1402,10 @@ void QSGListView::setSnapMode(SnapMode mode) } } -QDeclarativeComponent *QSGListView::footer() const -{ - Q_D(const QSGListView); - return d->footerComponent; -} - -void QSGListView::setFooter(QDeclarativeComponent *footer) -{ - Q_D(QSGListView); - if (d->footerComponent != footer) { - if (d->footer) { - // XXX todo - the original did scene()->removeItem(). Why? - d->footer->item->setParentItem(0); - d->footer->item->deleteLater(); - delete d->footer; - d->footer = 0; - } - d->footerComponent = footer; - d->minExtentDirty = true; - d->maxExtentDirty = true; - if (isComponentComplete()) { - d->updateFooter(); - d->updateViewport(); - d->fixupPosition(); - } - emit footerChanged(); - } -} - -QDeclarativeComponent *QSGListView::header() const -{ - Q_D(const QSGListView); - return d->headerComponent; -} - -void QSGListView::setHeader(QDeclarativeComponent *header) -{ - Q_D(QSGListView); - if (d->headerComponent != header) { - if (d->header) { - // XXX todo - the original did scene()->removeItem(). Why? - d->header->item->setParentItem(0); - d->header->item->deleteLater(); - delete d->header; - d->header = 0; - } - d->headerComponent = header; - d->minExtentDirty = true; - d->maxExtentDirty = true; - if (isComponentComplete()) { - d->updateHeader(); - d->updateFooter(); - d->updateViewport(); - d->fixupPosition(); - } - emit headerChanged(); - } -} - -void QSGListView::setContentX(qreal pos) -{ - Q_D(QSGListView); - // Positioning the view manually should override any current movement state - d->moveReason = QSGListViewPrivate::Other; - QSGFlickable::setContentX(pos); -} - -void QSGListView::setContentY(qreal pos) -{ - Q_D(QSGListView); - // Positioning the view manually should override any current movement state - d->moveReason = QSGListViewPrivate::Other; - QSGFlickable::setContentY(pos); -} - -void QSGListView::updatePolish() -{ - Q_D(QSGListView); - QSGFlickable::updatePolish(); - d->layout(); -} - void QSGListView::viewportMoved() { Q_D(QSGListView); - QSGFlickable::viewportMoved(); + QSGItemView::viewportMoved(); if (!d->itemCount) return; // Recursion can occur due to refill changing the content size. @@ -2122,7 +1413,7 @@ void QSGListView::viewportMoved() return; d->inViewportMoved = true; d->lazyRelease = true; - refill(); + d->refill(); if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically) d->moveReason = QSGListViewPrivate::Mouse; if (d->moveReason != QSGListViewPrivate::SetIndex) { @@ -2147,10 +1438,10 @@ void QSGListView::viewportMoved() if (pos < viewPos + highlightStart) pos = viewPos + highlightStart; d->highlightPosAnimator->stop(); - d->highlight->setPosition(qRound(pos)); + static_cast(d->highlight)->setPosition(qRound(pos)); // update current index - if (FxListItemSG *snapItem = d->snapItemAt(d->highlight->position())) { + if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) { if (snapItem->index >= 0 && snapItem->index != d->currentIndex) d->updateCurrent(snapItem->index); } @@ -2197,146 +1488,6 @@ void QSGListView::viewportMoved() d->inViewportMoved = false; } -qreal QSGListView::minYExtent() const -{ - Q_D(const QSGListView); - if (d->orient == QSGListView::Horizontal) - return QSGFlickable::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; - 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 QSGListView::maxYExtent() const -{ - Q_D(const QSGListView); - if (d->orient == QSGListView::Horizontal) - return height(); - if (d->maxExtentDirty) { - if (!d->model || !d->model->count()) { - d->maxExtent = d->header ? -d->header->size() : 0; - d->maxExtent += height(); - } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart); - if (d->highlightRangeEnd != d->highlightRangeStart) - d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1)); - } else { - d->maxExtent = -(d->endPosition() - height() + 1); - } - 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 QSGListView::minXExtent() const -{ - Q_D(const QSGListView); - if (d->orient == QSGListView::Vertical) - return QSGFlickable::minXExtent(); - if (d->minExtentDirty) { - d->minExtent = -d->startPosition(); - qreal highlightStart; - qreal highlightEnd; - qreal endPositionFirstItem = 0; - if (d->isRightToLeft()) { - if (d->model && d->model->count()) - endPositionFirstItem = d->positionAt(d->model->count()-1); - else if (d->header) - d->minExtent += d->header->size(); - highlightStart = d->highlightRangeStartValid - ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem) - : d->size() - (d->lastPosition()-endPositionFirstItem); - highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size(); - if (d->footer) - d->minExtent += d->footer->size(); - qreal maxX = maxXExtent(); - if (d->minExtent < maxX) - d->minExtent = maxX; - } else { - endPositionFirstItem = d->endPositionAt(0); - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - if (d->header && d->visibleItems.count()) - d->minExtent += d->header->size(); - } - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->minExtent += highlightStart; - d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1)); - } - d->minExtentDirty = false; - } - - return d->minExtent; -} - -qreal QSGListView::maxXExtent() const -{ - Q_D(const QSGListView); - if (d->orient == QSGListView::Vertical) - return width(); - if (d->maxExtentDirty) { - qreal highlightStart; - qreal highlightEnd; - qreal lastItemPosition = 0; - d->maxExtent = 0; - if (d->isRightToLeft()) { - highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size(); - highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size(); - lastItemPosition = d->endPosition(); - } else { - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - if (d->model && d->model->count()) - lastItemPosition = d->positionAt(d->model->count()-1); - } - if (!d->model || !d->model->count()) { - if (!d->isRightToLeft()) - d->maxExtent = d->header ? -d->header->size() : 0; - d->maxExtent += width(); - } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->maxExtent = -(lastItemPosition - highlightStart); - if (highlightEnd != highlightStart) { - d->maxExtent = d->isRightToLeft() - ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1)) - : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1)); - } - } else { - d->maxExtent = -(d->endPosition() - width() + 1); - } - if (d->isRightToLeft()) { - if (d->header && d->visibleItems.count()) - d->maxExtent -= d->header->size(); - } else { - if (d->footer) - d->maxExtent -= d->footer->size(); - qreal minX = minXExtent(); - if (d->maxExtent > minX) - d->maxExtent = minX; - } - d->maxExtentDirty = false; - } - - return d->maxExtent; -} - void QSGListView::keyPressEvent(QKeyEvent *event) { Q_D(QSGListView); @@ -2366,21 +1517,18 @@ void QSGListView::keyPressEvent(QKeyEvent *event) } } event->ignore(); - QSGFlickable::keyPressEvent(event); + QSGItemView::keyPressEvent(event); } -void QSGListView::geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry) +void QSGListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_D(QSGListView); - d->maxExtentDirty = true; - d->minExtentDirty = true; if (d->isRightToLeft() && d->orient == QSGListView::Horizontal) { // maintain position relative to the right edge int dx = newGeometry.width() - oldGeometry.width(); setContentX(contentX() - dx); } - QSGFlickable::geometryChanged(newGeometry, oldGeometry); + QSGItemView::geometryChanged(newGeometry, oldGeometry); } @@ -2406,147 +1554,6 @@ void QSGListView::decrementCurrentIndex() } } -void QSGListViewPrivate::positionViewAtIndex(int index, int mode) -{ - Q_Q(QSGListView); - if (!isValid()) - return; - if (mode < QSGListView::Beginning || mode > QSGListView::Contain) - return; - int idx = qMax(qMin(index, model->count()-1), 0); - - if (layoutScheduled) - layout(); - qreal pos = isRightToLeft() ? -position() - size() : position(); - FxListItemSG *item = visibleItem(idx); - qreal maxExtent; - if (orient == QSGListView::Vertical) - maxExtent = -q->maxYExtent(); - else - maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent(); - if (!item) { - int itemPos = positionAt(idx); - // save the currently visible items in case any of them end up visible again - QList oldVisible = visibleItems; - visibleItems.clear(); - visiblePos = itemPos; - visibleIndex = idx; - setPosition(qMin(qreal(itemPos), maxExtent)); - // now release the reference to all the old visible items. - for (int i = 0; i < oldVisible.count(); ++i) - releaseItem(oldVisible.at(i)); - item = visibleItem(idx); - } - if (item) { - const qreal itemPos = item->position(); - switch (mode) { - case QSGListView::Beginning: - pos = itemPos; - if (index < 0 && header) - pos -= header->size(); - break; - case QSGListView::Center: - pos = itemPos - (size() - item->size())/2; - break; - case QSGListView::End: - pos = itemPos - size() + item->size(); - if (index >= model->count() && footer) - pos += footer->size(); - break; - case QSGListView::Visible: - if (itemPos > pos + size()) - pos = itemPos - size() + item->size(); - else if (item->endPosition() < pos) - pos = itemPos; - break; - case QSGListView::Contain: - if (item->endPosition() > pos + size()) - pos = itemPos - size() + item->size(); - if (itemPos < pos) - pos = itemPos; - } - pos = qMin(pos, maxExtent); - qreal minExtent; - if (orient == QSGListView::Vertical) { - minExtent = -q->minYExtent(); - } else { - minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent(); - } - pos = qMax(pos, minExtent); - moveReason = QSGListViewPrivate::Other; - q->cancelFlick(); - setPosition(pos); - if (highlight) { - if (autoHighlight) { - highlight->setPosition(currentItem->itemPosition()); - highlight->setSize(currentItem->itemSize()); - } - updateHighlight(); - } - } - fixupPosition(); -} - -void QSGListView::positionViewAtIndex(int index, int mode) -{ - Q_D(QSGListView); - if (!d->isValid() || index < 0 || index >= d->model->count()) - return; - d->positionViewAtIndex(index, mode); -} - -void QSGListView::positionViewAtBeginning() -{ - Q_D(QSGListView); - if (!d->isValid()) - return; - d->positionViewAtIndex(-1, Beginning); -} - -void QSGListView::positionViewAtEnd() -{ - Q_D(QSGListView); - if (!d->isValid()) - return; - d->positionViewAtIndex(d->model->count(), End); -} - -int QSGListView::indexAt(qreal x, qreal y) const -{ - Q_D(const QSGListView); - for (int i = 0; i < d->visibleItems.count(); ++i) { - const FxListItemSG *listItem = d->visibleItems.at(i); - if(listItem->contains(x, y)) - return listItem->index; - } - - return -1; -} - -void QSGListView::componentComplete() -{ - Q_D(QSGListView); - QSGFlickable::componentComplete(); - updateSections(); - d->updateHeader(); - d->updateFooter(); - if (d->isValid()) { - refill(); - d->moveReason = QSGListViewPrivate::SetIndex; - if (d->currentIndex < 0 && !d->currentIndexCleared) - d->updateCurrent(0); - else - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->highlight->setPosition(d->currentItem->position()); - d->updateTrackedItem(); - } - d->moveReason = QSGListViewPrivate::Other; - d->fixupPosition(); - } -} - void QSGListView::updateSections() { Q_D(QSGListView); @@ -2561,86 +1568,6 @@ void QSGListView::updateSections() } } -void QSGListView::refill() -{ - Q_D(QSGListView); - if (d->isRightToLeft()) - d->refill(-d->position()-d->size()+1, -d->position()); - else - d->refill(d->position(), d->position()+d->size()-1); -} - -void QSGListView::trackedPositionChanged() -{ - Q_D(QSGListView); - if (!d->trackedItem || !d->currentItem) - return; - if (d->moveReason == QSGListViewPrivate::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(); - } - qreal viewPos; - qreal highlightStart; - qreal highlightEnd; - if (d->isRightToLeft()) { - viewPos = -d->position()-d->size(); - highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; - highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; - } else { - viewPos = d->position(); - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - } - qreal pos = viewPos; - if (d->haveHighlightRange) { - if (d->highlightRange == StrictlyEnforceRange) { - if (trackedPos > pos + highlightEnd - d->trackedItem->size()) - pos = trackedPos - highlightEnd + d->trackedItem->size(); - if (trackedPos < pos + highlightStart) - pos = trackedPos - highlightStart; - } else { - if (trackedPos < d->startPosition() + highlightStart) { - pos = d->startPosition(); - } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) { - pos = d->endPosition() - d->size() + 1; - if (pos < d->startPosition()) - pos = d->startPosition(); - } else { - if (trackedPos < viewPos + highlightStart) { - pos = trackedPos - highlightStart; - } else if (trackedPos > viewPos + highlightEnd - trackedSize) { - pos = trackedPos - highlightEnd + 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 QSGListView::itemsInserted(int modelIndex, int count) { Q_D(QSGListView); @@ -2667,9 +1594,9 @@ void QSGListView::itemsInserted(int modelIndex, int count) // Insert before visible items d->visibleIndex += count; for (int i = 0; i < d->visibleItems.count(); ++i) { - FxListItemSG *listItem = d->visibleItems.at(i); - if (listItem->index != -1 && listItem->index >= modelIndex) - listItem->index += count; + FxViewItem *item = d->visibleItems.at(i); + if (item->index != -1 && item->index >= modelIndex) + item->index += count; } } if (d->currentIndex >= modelIndex) { @@ -2692,14 +1619,14 @@ void QSGListView::itemsInserted(int modelIndex, int count) pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position() : d->visibleItems.last()->endPosition()+d->spacing+1; } else if (d->itemCount == 0 && d->header) { - pos = d->header->size(); + pos = d->headerSize(); } int initialPos = pos; int diff = 0; QList added; bool addedVisible = false; - FxListItemSG *firstVisible = d->firstVisibleItem(); + FxViewItem *firstVisible = d->firstVisibleItem(); if (firstVisible && pos < firstVisible->position()) { // Insert items before the visible item. int insertionIdx = index; @@ -2710,7 +1637,7 @@ void QSGListView::itemsInserted(int modelIndex, int count) d->scheduleLayout(); addedVisible = true; } - FxListItemSG *item = d->createItem(modelIndex + i); + FxListItemSG *item = static_cast(d->createItem(modelIndex + i)); d->visibleItems.insert(insertionIdx, item); pos -= item->size() + d->spacing; item->setPosition(pos); @@ -2720,7 +1647,7 @@ void QSGListView::itemsInserted(int modelIndex, int count) // If we didn't insert all our new items - anything // before the current index is not visible - remove it. while (insertionIdx--) { - FxListItemSG *item = d->visibleItems.takeFirst(); + FxListItemSG *item = static_cast(d->visibleItems.takeFirst()); if (item->index != -1) d->visibleIndex++; d->releaseItem(item); @@ -2728,7 +1655,7 @@ void QSGListView::itemsInserted(int modelIndex, int count) } else { // adjust pos of items before inserted items. for (int i = insertionIdx-1; i >= 0; i--) { - FxListItemSG *listItem = d->visibleItems.at(i); + FxListItemSG *listItem = static_cast(d->visibleItems.at(i)); listItem->setPosition(listItem->position() - (initialPos - pos)); } } @@ -2740,7 +1667,7 @@ void QSGListView::itemsInserted(int modelIndex, int count) d->scheduleLayout(); addedVisible = true; } - FxListItemSG *item = d->createItem(modelIndex + i); + FxListItemSG *item = static_cast(d->createItem(modelIndex + i)); d->visibleItems.insert(index, item); item->setPosition(pos); added.append(item); @@ -2760,7 +1687,7 @@ void QSGListView::itemsInserted(int modelIndex, int count) d->currentIndex += count; if (d->currentItem) { d->currentItem->index = d->currentIndex; - d->currentItem->setPosition(d->currentItem->position() + diff); + static_cast(d->currentItem)->setPosition(static_cast(d->currentItem)->position() + diff); } emit currentIndexChanged(); } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) { @@ -2768,11 +1695,11 @@ void QSGListView::itemsInserted(int modelIndex, int count) } // Update the indexes of the following visible items. for (; index < d->visibleItems.count(); ++index) { - FxListItemSG *listItem = d->visibleItems.at(index); - if (d->currentItem && listItem->item != d->currentItem->item) - listItem->setPosition(listItem->position() + diff); - if (listItem->index != -1) - listItem->index += count; + FxViewItem *item = d->visibleItems.at(index); + if (d->currentItem && item->item != d->currentItem->item) + static_cast(item)->setPosition(item->position() + diff); + if (item->index != -1) + item->index += count; } // everything is in order now - emit add() signal for (int j = 0; j < added.count(); ++j) @@ -2792,13 +1719,13 @@ void QSGListView::itemsRemoved(int modelIndex, int count) d->updateUnrequestedIndexes(); d->itemCount -= count; - FxListItemSG *firstVisible = d->firstVisibleItem(); + FxViewItem *firstVisible = d->firstVisibleItem(); int preRemovedSize = 0; bool removedVisible = false; // Remove the items from the visible list, skipping anything already marked for removal - QList::Iterator it = d->visibleItems.begin(); + QList::Iterator it = d->visibleItems.begin(); while (it != d->visibleItems.end()) { - FxListItemSG *item = *it; + FxViewItem *item = *it; if (item->index == -1 || item->index < modelIndex) { // already removed, or before removed items ++it; @@ -2829,7 +1756,7 @@ void QSGListView::itemsRemoved(int modelIndex, int count) } if (firstVisible && d->visibleItems.first() != firstVisible) - d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize); + static_cast(d->visibleItems.first())->setPosition(d->visibleItems.first()->position() + preRemovedSize); // fix current if (d->currentIndex >= modelIndex + count) { @@ -2865,7 +1792,7 @@ void QSGListView::itemsRemoved(int modelIndex, int count) d->timeline.clear(); if (removedVisible && d->itemCount == 0) { d->visibleIndex = 0; - d->visiblePos = d->header ? d->header->size() : 0; + d->visiblePos = d->header ? d->headerSize() : 0; d->setPosition(0); d->updateHeader(); d->updateFooter(); @@ -2880,25 +1807,6 @@ void QSGListView::itemsRemoved(int modelIndex, int count) emit countChanged(); } -void QSGListView::destroyRemoved() -{ - Q_D(QSGListView); - for (QList::Iterator it = d->visibleItems.begin(); - it != d->visibleItems.end();) { - FxListItemSG *listItem = *it; - if (listItem->index == -1 && listItem->attached->delayRemove() == false) { - d->releaseItem(listItem); - it = d->visibleItems.erase(it); - } else { - ++it; - } - } - - // Correct the positioning of the items - d->updateSections(); - d->layout(); -} - void QSGListView::itemsMoved(int from, int to, int count) { Q_D(QSGListView); @@ -2907,19 +1815,19 @@ void QSGListView::itemsMoved(int from, int to, int count) d->updateUnrequestedIndexes(); if (d->visibleItems.isEmpty()) { - refill(); + d->refill(); return; } d->moveReason = QSGListViewPrivate::Other; - FxListItemSG *firstVisible = d->firstVisibleItem(); + FxViewItem *firstVisible = d->firstVisibleItem(); qreal firstItemPos = firstVisible->position(); - QHash moved; + QHash moved; int moveBy = 0; - QList::Iterator it = d->visibleItems.begin(); + QList::Iterator it = d->visibleItems.begin(); while (it != d->visibleItems.end()) { - FxListItemSG *item = *it; + FxViewItem *item = *it; if (item->index >= from && item->index < from + count) { // take the items that are moving item->index += (to-from); @@ -2939,10 +1847,10 @@ void QSGListView::itemsMoved(int from, int to, int count) int endIndex = d->visibleIndex; it = d->visibleItems.begin(); while (it != d->visibleItems.end()) { - FxListItemSG *item = *it; + FxViewItem *item = *it; if (remaining && item->index >= to && item->index < to + count) { // place items in the target position, reusing any existing items - FxListItemSG *movedItem = moved.take(item->index); + FxViewItem *movedItem = moved.take(item->index); if (!movedItem) movedItem = d->createItem(item->index); if (item->index <= firstVisible->index) @@ -2964,7 +1872,7 @@ void QSGListView::itemsMoved(int from, int to, int count) // If we have moved items to the end of the visible items // then add any existing moved items that we have - while (FxListItemSG *item = moved.take(endIndex+1)) { + while (FxViewItem *item = moved.take(endIndex+1)) { d->visibleItems.append(item); ++endIndex; } @@ -2990,70 +1898,19 @@ void QSGListView::itemsMoved(int from, int to, int count) // Whatever moved items remain are no longer visible items. while (moved.count()) { int idx = moved.begin().key(); - FxListItemSG *item = moved.take(idx); + FxViewItem *item = moved.take(idx); if (d->currentItem && item->item == d->currentItem->item) - item->setPosition(d->positionAt(idx)); + static_cast(item)->setPosition(d->positionAt(idx)); d->releaseItem(item); } // Ensure we don't cause an ugly list scroll. - d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy); - - d->updateSections(); - d->layout(); -} + static_cast(d->visibleItems.first())->setPosition(d->visibleItems.first()->position() + moveBy); -void QSGListView::itemsChanged(int, int) -{ - Q_D(QSGListView); d->updateSections(); d->layout(); } -void QSGListView::modelReset() -{ - Q_D(QSGListView); - d->moveReason = QSGListViewPrivate::SetIndex; - d->regenerate(); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->highlight->setPosition(d->currentItem->position()); - d->updateTrackedItem(); - } - d->moveReason = QSGListViewPrivate::Other; - emit countChanged(); -} - -void QSGListView::createdItem(int index, QSGItem *item) -{ - Q_D(QSGListView); - if (d->requestedIndex != index) { - item->setParentItem(contentItem()); - d->unrequestedItems.insert(item, index); - if (d->orient == QSGListView::Vertical) { - item->setY(d->positionAt(index)); - } else { - if (d->isRightToLeft()) - item->setX(-d->positionAt(index)-item->width()); - else - item->setX(d->positionAt(index)); - } - } -} - -void QSGListView::destroyingItem(QSGItem *item) -{ - Q_D(QSGListView); - d->unrequestedItems.remove(item); -} - -void QSGListView::animStopped() -{ - Q_D(QSGListView); - d->bufferMode = QSGListViewPrivate::NoBuffer; - if (d->haveHighlightRange && d->highlightRange == QSGListView::StrictlyEnforceRange) - d->updateHighlight(); -} QSGListViewAttached *QSGListView::qmlAttachedProperties(QObject *obj) { diff --git a/src/declarative/items/qsglistview_p.h b/src/declarative/items/qsglistview_p.h index 8560225..e45e16b 100644 --- a/src/declarative/items/qsglistview_p.h +++ b/src/declarative/items/qsglistview_p.h @@ -43,7 +43,7 @@ #ifndef QSGLISTVIEW_P_H #define QSGLISTVIEW_P_H -#include "qsgflickable_p.h" +#include "qsgitemview_p.h" #include @@ -90,84 +90,33 @@ private: class QSGVisualModel; class QSGListViewAttached; class QSGListViewPrivate; -class Q_AUTOTEST_EXPORT QSGListView : public QSGFlickable +class Q_AUTOTEST_EXPORT QSGListView : public QSGItemView { Q_OBJECT Q_DECLARE_PRIVATE(QSGListView) - Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) - Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) - Q_PROPERTY(QSGItem *currentItem READ currentItem NOTIFY currentIndexChanged) - Q_PROPERTY(int count READ count NOTIFY countChanged) - - Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) - Q_PROPERTY(QSGItem *highlightItem READ highlightItem NOTIFY highlightItemChanged) - Q_PROPERTY(bool highlightFollowsCurrentItem READ highlightFollowsCurrentItem WRITE setHighlightFollowsCurrentItem NOTIFY highlightFollowsCurrentItemChanged) + // XXX deprecate these two properties (only duration should be necessary) Q_PROPERTY(qreal highlightMoveSpeed READ highlightMoveSpeed WRITE setHighlightMoveSpeed NOTIFY highlightMoveSpeedChanged) - Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) Q_PROPERTY(qreal highlightResizeSpeed READ highlightResizeSpeed WRITE setHighlightResizeSpeed NOTIFY highlightResizeSpeedChanged) - Q_PROPERTY(int highlightResizeDuration READ highlightResizeDuration WRITE setHighlightResizeDuration NOTIFY highlightResizeDurationChanged) - Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged RESET resetPreferredHighlightBegin) - Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd) - Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged) + Q_PROPERTY(int highlightResizeDuration READ highlightResizeDuration WRITE setHighlightResizeDuration NOTIFY highlightResizeDurationChanged) Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) - Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) - Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) - Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged) - Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged) + Q_PROPERTY(QSGViewSection *section READ sectionCriteria CONSTANT) Q_PROPERTY(QString currentSection READ currentSection NOTIFY currentSectionChanged) Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged) - Q_PROPERTY(QDeclarativeComponent *header READ header WRITE setHeader NOTIFY headerChanged) - Q_PROPERTY(QDeclarativeComponent *footer READ footer WRITE setFooter NOTIFY footerChanged) - - Q_ENUMS(HighlightRangeMode) Q_ENUMS(Orientation) Q_ENUMS(SnapMode) - Q_ENUMS(PositionMode) Q_CLASSINFO("DefaultProperty", "data") public: QSGListView(QSGItem *parent=0); ~QSGListView(); - QVariant model() const; - void setModel(const QVariant &); - - QDeclarativeComponent *delegate() const; - void setDelegate(QDeclarativeComponent *); - - int currentIndex() const; - void setCurrentIndex(int idx); - - QSGItem *currentItem(); - QSGItem *highlightItem(); - int count() const; - - QDeclarativeComponent *highlight() const; - void setHighlight(QDeclarativeComponent *highlight); - - bool highlightFollowsCurrentItem() const; - void setHighlightFollowsCurrentItem(bool); - - enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; - HighlightRangeMode highlightRangeMode() const; - void setHighlightRangeMode(HighlightRangeMode mode); - - qreal preferredHighlightBegin() const; - void setPreferredHighlightBegin(qreal); - void resetPreferredHighlightBegin(); - - qreal preferredHighlightEnd() const; - void setPreferredHighlightEnd(qreal); - void resetPreferredHighlightEnd(); - qreal spacing() const; void setSpacing(qreal spacing); @@ -175,115 +124,62 @@ public: Orientation orientation() const; void setOrientation(Orientation); - Qt::LayoutDirection layoutDirection() const; - void setLayoutDirection(Qt::LayoutDirection); - Qt::LayoutDirection effectiveLayoutDirection() const; - - bool isWrapEnabled() const; - void setWrapEnabled(bool); - - int cacheBuffer() const; - void setCacheBuffer(int); - QSGViewSection *sectionCriteria(); QString currentSection() const; + virtual void setHighlightFollowsCurrentItem(bool); + qreal highlightMoveSpeed() const; void setHighlightMoveSpeed(qreal); - int highlightMoveDuration() const; - void setHighlightMoveDuration(int); - qreal highlightResizeSpeed() const; void setHighlightResizeSpeed(qreal); int highlightResizeDuration() const; void setHighlightResizeDuration(int); + virtual void setHighlightMoveDuration(int); + enum SnapMode { NoSnap, SnapToItem, SnapOneItem }; SnapMode snapMode() const; void setSnapMode(SnapMode mode); - QDeclarativeComponent *footer() const; - void setFooter(QDeclarativeComponent *); - - QDeclarativeComponent *header() const; - void setHeader(QDeclarativeComponent *); - - virtual void setContentX(qreal pos); - virtual void setContentY(qreal pos); - static QSGListViewAttached *qmlAttachedProperties(QObject *); - enum PositionMode { Beginning, Center, End, Visible, Contain }; - - Q_INVOKABLE void positionViewAtIndex(int index, int mode); - Q_INVOKABLE int indexAt(qreal x, qreal y) const; - Q_INVOKABLE void positionViewAtBeginning(); - Q_INVOKABLE void positionViewAtEnd(); - public Q_SLOTS: void incrementCurrentIndex(); void decrementCurrentIndex(); Q_SIGNALS: - void countChanged(); void spacingChanged(); void orientationChanged(); - void layoutDirectionChanged(); - void effectiveLayoutDirectionChanged(); - void currentIndexChanged(); void currentSectionChanged(); void highlightMoveSpeedChanged(); - void highlightMoveDurationChanged(); void highlightResizeSpeedChanged(); void highlightResizeDurationChanged(); - void highlightChanged(); - void highlightItemChanged(); - void modelChanged(); - void delegateChanged(); - void highlightFollowsCurrentItemChanged(); - void preferredHighlightBeginChanged(); - void preferredHighlightEndChanged(); - void highlightRangeModeChanged(); - void keyNavigationWrapsChanged(); - void cacheBufferChanged(); void snapModeChanged(); - void headerChanged(); - void footerChanged(); protected: - virtual void updatePolish(); virtual void viewportMoved(); - virtual qreal minYExtent() const; - virtual qreal maxYExtent() const; - virtual qreal minXExtent() const; - virtual qreal maxXExtent() const; virtual void keyPressEvent(QKeyEvent *); virtual void geometryChanged(const QRectF &newGeometry,const QRectF &oldGeometry); - virtual void componentComplete(); -private Q_SLOTS: +protected Q_SLOTS: void updateSections(); - void refill(); - void trackedPositionChanged(); + +private Q_SLOTS: void itemsInserted(int index, int count); void itemsRemoved(int index, int count); void itemsMoved(int from, int to, int count); - void itemsChanged(int index, int count); - void modelReset(); - void destroyRemoved(); - void createdItem(int index, QSGItem *item); - void destroyingItem(QSGItem *item); - void animStopped(); }; -class QSGListViewAttached : public QObject +class QSGListViewAttached : public QSGItemViewAttached { Q_OBJECT + public: QSGListViewAttached(QObject *parent) - : QObject(parent), m_view(0), m_isCurrent(false), m_delayRemove(false) {} + : QSGItemViewAttached(parent), m_view(0) {} ~QSGListViewAttached() {} Q_PROPERTY(QSGListView *view READ view NOTIFY viewChanged) @@ -295,71 +191,11 @@ public: } } - Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) - bool isCurrentItem() const { return m_isCurrent; } - void setIsCurrentItem(bool c) { - if (m_isCurrent != c) { - m_isCurrent = c; - emit currentItemChanged(); - } - } - - Q_PROPERTY(QString previousSection READ prevSection NOTIFY prevSectionChanged) - QString prevSection() const { return m_prevSection; } - void setPrevSection(const QString §) { - if (m_prevSection != sect) { - m_prevSection = sect; - emit prevSectionChanged(); - } - } - - Q_PROPERTY(QString nextSection READ nextSection NOTIFY nextSectionChanged) - QString nextSection() const { return m_nextSection; } - void setNextSection(const QString §) { - if (m_nextSection != sect) { - m_nextSection = sect; - emit nextSectionChanged(); - } - } - - Q_PROPERTY(QString section READ section NOTIFY sectionChanged) - QString section() const { return m_section; } - void setSection(const QString §) { - if (m_section != sect) { - m_section = sect; - emit sectionChanged(); - } - } - - Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged) - bool delayRemove() const { return m_delayRemove; } - void setDelayRemove(bool delay) { - if (m_delayRemove != delay) { - m_delayRemove = delay; - emit delayRemoveChanged(); - } - } - - void emitAdd() { emit add(); } - void emitRemove() { emit remove(); } - Q_SIGNALS: - void currentItemChanged(); - void sectionChanged(); - void prevSectionChanged(); - void nextSectionChanged(); - void delayRemoveChanged(); - void add(); - void remove(); void viewChanged(); public: QDeclarativeGuard m_view; - mutable QString m_section; - QString m_prevSection; - QString m_nextSection; - bool m_isCurrent : 1; - bool m_delayRemove : 1; }; diff --git a/tests/auto/declarative/qsggridview/tst_qsggridview.cpp b/tests/auto/declarative/qsggridview/tst_qsggridview.cpp index ab202e0..5310632 100644 --- a/tests/auto/declarative/qsggridview/tst_qsggridview.cpp +++ b/tests/auto/declarative/qsggridview/tst_qsggridview.cpp @@ -1236,7 +1236,7 @@ void tst_QSGGridView::positionViewAtIndex() // Position on an item that would leave empty space if positioned at the top gridview->positionViewAtIndex(31, QSGGridView::Beginning); QTRY_COMPARE(gridview->indexAt(120, 630), 31); - QTRY_COMPARE(gridview->contentY(), 520.); + QTRY_COMPARE(gridview->contentY(), 521.); // 520 then +1 so bottom edge of last item is visible // Confirm items positioned correctly itemCount = findItems(contentItem, "wrapper").count(); @@ -1354,7 +1354,7 @@ void tst_QSGGridView::positionViewAtIndex() // positionViewAtEnd gridview->positionViewAtEnd(); - QTRY_COMPARE(gridview->contentX(), 430.); + QTRY_COMPARE(gridview->contentX(), 400.); // 8*80 - 240 (8 columns) gridview->setContentX(80); canvas->rootObject()->setProperty("showFooter", true); -- 2.7.4