summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli@qt.io>2017-07-21 18:55:37 +0200
committerPaolo Angelelli <paolo.angelelli@qt.io>2018-04-17 13:36:34 +0000
commit572957bb1326190672ea138ccf475c0cf1893192 (patch)
tree7679c6062673a25af15a7b2cead6325316e87676
parent9277ad55ad1a76492a3985460569d0a6fd15b08a (diff)
downloadqtlocation-572957bb1326190672ea138ccf475c0cf1893192.tar.gz
Fix MapItemView not setting context data upon item deletion
The current implementation does not properly re-set context data upon model item deletion/change. This patch uses a QQmlDelegateModel that does all the management internally. To accommodate for items disappearing/reappearing all at once, potentially introducing flickering, a fixed opacity transition 300msec long has been introduced as exit transition. In later Qt releases, new API can be added to let the user change it, as well as also adding an enter transition and possibly more. Task-number: QTBUG-62086 Task-number: QTBUG-65833 Change-Id: I59a8147a12f035d5c7f86c2546e9144b2e1a7b3c Reviewed-by: J-P Nurmi <jpnurmi@qt.io> Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
-rw-r--r--src/location/declarativemaps/declarativemaps.pri3
-rw-r--r--src/location/declarativemaps/mapitemviewdelegateincubator.cpp54
-rw-r--r--src/location/declarativemaps/qdeclarativegeomap.cpp2
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitembase.cpp77
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitembase_p.h39
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitemview.cpp453
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitemview_p.h55
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitemview_p_p.h87
-rw-r--r--tests/auto/declarative_ui/tst_map_itemview.qml12
9 files changed, 280 insertions, 502 deletions
diff --git a/src/location/declarativemaps/declarativemaps.pri b/src/location/declarativemaps/declarativemaps.pri
index 335dc55a..3cd7a529 100644
--- a/src/location/declarativemaps/declarativemaps.pri
+++ b/src/location/declarativemaps/declarativemaps.pri
@@ -5,7 +5,6 @@ INCLUDEPATH += declarativemaps
PRIVATE_HEADERS += \
declarativemaps/error_messages_p.h \
declarativemaps/qdeclarativegeomapitemview_p.h \
- declarativemaps/qdeclarativegeomapitemview_p_p.h \
declarativemaps/qdeclarativegeoserviceprovider_p.h \
declarativemaps/qdeclarativegeocodemodel_p.h \
declarativemaps/qdeclarativegeoroutemodel_p.h \
@@ -28,7 +27,6 @@ PRIVATE_HEADERS += \
declarativemaps/qquickgeomapgesturearea_p.h \
declarativemaps/qdeclarativegeomapitemgroup_p.h \
declarativemaps/qparameterizableobject_p.h \
- declarativemaps/mapitemviewdelegateincubator_p.h \
declarativemaps/qgeomapobject_p.h \
declarativemaps/qgeomapobject_p_p.h \
../imports/positioning/qquickgeocoordinateanimation_p.h
@@ -58,7 +56,6 @@ SOURCES += \
declarativemaps/qquickgeomapgesturearea.cpp \
declarativemaps/qparameterizableobject.cpp \
declarativemaps/qdeclarativegeomapitemgroup.cpp \
- declarativemaps/mapitemviewdelegateincubator.cpp \
declarativemaps/qgeomapobject.cpp \
../imports/positioning/qquickgeocoordinateanimation.cpp
diff --git a/src/location/declarativemaps/mapitemviewdelegateincubator.cpp b/src/location/declarativemaps/mapitemviewdelegateincubator.cpp
deleted file mode 100644
index c8500e4b..00000000
--- a/src/location/declarativemaps/mapitemviewdelegateincubator.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Copyright (C) 2015 Jolla Ltd, author: Aaron McCarthy <aaron.mccarthy@jollamobile.com>
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtLocation module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPLv3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "mapitemviewdelegateincubator_p.h"
-#include "qdeclarativegeomapitemview_p.h"
-#include "qdeclarativegeomapitemview_p_p.h"
-
-QT_BEGIN_NAMESPACE
-
-MapItemViewDelegateIncubator::MapItemViewDelegateIncubator(QDeclarativeGeoMapItemView *view, QDeclarativeGeoMapItemViewItemData *itemData, bool batched)
-: m_view(view), m_itemData(itemData), m_batched(batched)
-{
-}
-
-void MapItemViewDelegateIncubator::statusChanged(QQmlIncubator::Status status)
-{
- m_view->incubatorStatusChanged(this, status, m_batched);
-}
-
-QT_END_NAMESPACE
diff --git a/src/location/declarativemaps/qdeclarativegeomap.cpp b/src/location/declarativemaps/qdeclarativegeomap.cpp
index f8218604..09f9d01c 100644
--- a/src/location/declarativemaps/qdeclarativegeomap.cpp
+++ b/src/location/declarativemaps/qdeclarativegeomap.cpp
@@ -503,9 +503,7 @@ void QDeclarativeGeoMap::populateParameters()
*/
void QDeclarativeGeoMap::setupMapView(QDeclarativeGeoMapItemView *view)
{
- Q_UNUSED(view)
view->setMap(this);
- view->repopulate();
}
/*!
diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp
index 7b3950db..faa06fef 100644
--- a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp
+++ b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp
@@ -283,6 +283,44 @@ qreal QDeclarativeGeoMapItemBase::mapItemOpacity() const
return opacity();
}
+bool QDeclarativeGeoMapItemBase::prepareEnterTransition()
+{
+ if (m_transitionManager->m_transitionState == QDeclarativeGeoMapItemTransitionManager::EnterTransition
+ && m_transitionManager->isRunning())
+ return false;
+
+ if (m_transitionManager->m_transitionState != QDeclarativeGeoMapItemTransitionManager::EnterTransition) {
+ setVisible(true);
+ m_transitionManager->m_transitionState = QDeclarativeGeoMapItemTransitionManager::EnterTransition;
+ }
+ return true;
+}
+
+bool QDeclarativeGeoMapItemBase::prepareExitTransition()
+{
+ if (m_transitionManager->m_transitionState == QDeclarativeGeoMapItemTransitionManager::ExitTransition
+ && m_transitionManager->isRunning())
+ return false;
+
+ if (m_transitionManager->m_transitionState != QDeclarativeGeoMapItemTransitionManager::ExitTransition) {
+ m_transitionManager->m_transitionState = QDeclarativeGeoMapItemTransitionManager::ExitTransition;
+ }
+ return true;
+}
+
+void QDeclarativeGeoMapItemBase::finalizeEnterTransition()
+{
+ m_transitionManager->m_transitionState = QDeclarativeGeoMapItemTransitionManager::NoTransition;
+ emit enterTransitionFinished();
+}
+
+void QDeclarativeGeoMapItemBase::finalizeExitTransition()
+{
+ setVisible(false);
+ m_transitionManager->m_transitionState = QDeclarativeGeoMapItemTransitionManager::NoTransition;
+ emit exitTransitionFinished();
+}
+
bool QDeclarativeGeoMapItemBase::isPolishScheduled() const
{
return QQuickItemPrivate::get(this)->polishScheduled;
@@ -294,4 +332,43 @@ void QDeclarativeGeoMapItemBase::polishAndUpdate()
update();
}
+QDeclarativeGeoMapItemTransitionManager::QDeclarativeGeoMapItemTransitionManager(QDeclarativeGeoMapItemBase *mapItem)
+ : QQuickTransitionManager(), m_mapItem(mapItem)
+{
+}
+
+void QDeclarativeGeoMapItemTransitionManager::transitionEnter()
+{
+ if (m_transitionState == ExitTransition)
+ cancel();
+
+ if (!m_mapItem->prepareEnterTransition())
+ return;
+
+ if (m_view && m_view->m_enter)
+ transition(enterActions, m_view->m_enter, m_mapItem);
+ else
+ finished();
+}
+
+void QDeclarativeGeoMapItemTransitionManager::transitionExit()
+{
+ if (!m_mapItem->prepareExitTransition())
+ return;
+
+ if (m_view && m_view->m_exit)
+ transition(exitActions, m_view->m_exit, m_mapItem);
+ else
+ finished();
+}
+
+void QDeclarativeGeoMapItemTransitionManager::finished()
+{
+ if (m_transitionState == EnterTransition)
+ m_mapItem->finalizeEnterTransition();
+ else if (m_transitionState == ExitTransition)
+ m_mapItem->finalizeExitTransition();
+}
+
+
QT_END_NAMESPACE
diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase_p.h b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h
index edb97c10..162c6d71 100644
--- a/src/location/declarativemaps/qdeclarativegeomapitembase_p.h
+++ b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h
@@ -56,9 +56,12 @@
#include <QtLocation/private/qdeclarativegeomap_p.h>
#include <QtLocation/private/qlocationglobal_p.h>
#include <QtLocation/private/qgeomap_p.h>
+#include <QtQuick/private/qquicktransitionmanager_p_p.h>
+#include <QScopedPointer>
QT_BEGIN_NAMESPACE
-
+class QDeclarativeGeoMapItemTransitionManager;
+class QDeclarativeGeoMapItemBase;
class Q_LOCATION_PRIVATE_EXPORT QGeoMapViewportChangeEvent
{
public:
@@ -77,6 +80,29 @@ public:
bool rollChanged;
};
+class QDeclarativeGeoMapItemTransitionManager : public QQuickTransitionManager
+{
+public:
+ enum TransitionState {
+ NoTransition, EnterTransition, ExitTransition
+ };
+
+ QDeclarativeGeoMapItemTransitionManager(QDeclarativeGeoMapItemBase *mapItem);
+
+ void transitionEnter();
+ void transitionExit();
+
+protected:
+ void finished() override;
+
+public:
+ QDeclarativeGeoMapItemBase *m_mapItem;
+ QDeclarativeGeoMapItemView *m_view = nullptr;
+ QList<QQuickStateAction> enterActions;
+ QList<QQuickStateAction> exitActions;
+ TransitionState m_transitionState = NoTransition;
+};
+
class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemBase : public QQuickItem
{
Q_OBJECT
@@ -99,8 +125,15 @@ public:
virtual QGeoMap::ItemType itemType() const = 0;
qreal mapItemOpacity() const;
+ virtual bool prepareEnterTransition();
+ virtual bool prepareExitTransition();
+ virtual void finalizeEnterTransition();
+ virtual void finalizeExitTransition();
+
Q_SIGNALS:
void mapItemOpacityChanged();
+ void enterTransitionFinished();
+ void exitTransitionFinished();
protected Q_SLOTS:
virtual void afterChildrenChanged();
@@ -124,7 +157,11 @@ private:
QDeclarativeGeoMapItemGroup *parentGroup_;
+ QScopedPointer<QDeclarativeGeoMapItemTransitionManager> m_transitionManager;
+
friend class QDeclarativeGeoMap;
+ friend class QDeclarativeGeoMapItemView;
+ friend class QDeclarativeGeoMapItemTransitionManager;
};
QT_END_NAMESPACE
diff --git a/src/location/declarativemaps/qdeclarativegeomapitemview.cpp b/src/location/declarativemaps/qdeclarativegeomapitemview.cpp
index ce7b4946..b49e675c 100644
--- a/src/location/declarativemaps/qdeclarativegeomapitemview.cpp
+++ b/src/location/declarativemaps/qdeclarativegeomapitemview.cpp
@@ -37,15 +37,16 @@
****************************************************************************/
#include "qdeclarativegeomapitemview_p.h"
-#include "qdeclarativegeomapitemview_p_p.h"
#include "qdeclarativegeomap_p.h"
#include "qdeclarativegeomapitembase_p.h"
#include "mapitemviewdelegateincubator_p.h"
#include <QtCore/QAbstractItemModel>
#include <QtQml/QQmlContext>
-#include <QtQml/QQmlIncubator>
+#include <QtQml/private/qqmldelegatemodel_p.h>
#include <QtQml/private/qqmlopenmetaobject_p.h>
+#include <QtQuick/private/qquickanimation_p.h>
+#include <QtQml/QQmlListProperty>
QT_BEGIN_NAMESPACE
@@ -76,16 +77,20 @@ QT_BEGIN_NAMESPACE
QDeclarativeGeoMapItemView::QDeclarativeGeoMapItemView(QQuickItem *parent)
: QObject(parent), m_componentCompleted(false), m_delegate(0),
- m_itemModel(0), m_map(0), m_fitViewport(false), m_metaObjectType(0),
- m_readyIncubators(0), m_repopulating(false)
+ m_map(0), m_fitViewport(false), m_delegateModel(0)
{
+ m_exit = new QQuickTransition(this);
+ QQmlListProperty<QQuickAbstractAnimation> anims = m_exit->animations();
+ QQuickNumberAnimation *ani = new QQuickNumberAnimation(m_exit);
+ ani->setProperty(QStringLiteral("opacity"));
+ ani->setTo(0.0);
+ ani->setDuration(300.0);
+ anims.append(&anims, ani);
}
QDeclarativeGeoMapItemView::~QDeclarativeGeoMapItemView()
{
removeInstantiatedItems();
- if (m_metaObjectType)
- m_metaObjectType->release();
}
/*!
@@ -94,239 +99,103 @@ QDeclarativeGeoMapItemView::~QDeclarativeGeoMapItemView()
void QDeclarativeGeoMapItemView::componentComplete()
{
m_componentCompleted = true;
-}
+ if (!m_itemModel.isNull())
+ m_delegateModel->setModel(m_itemModel);
-void QDeclarativeGeoMapItemView::incubatorStatusChanged(MapItemViewDelegateIncubator *incubator,
- QQmlIncubator::Status status,
- bool batched)
-{
- if (status == QQmlIncubator::Loading)
- return;
+ if (m_delegate)
+ m_delegateModel->setDelegate(m_delegate);
- QDeclarativeGeoMapItemViewItemData *itemData = incubator->m_itemData;
- if (!itemData) {
- // Should never get here
- qWarning() << "MapItemViewDelegateIncubator incubating invalid itemData";
- return;
- }
-
- switch (status) {
- case QQmlIncubator::Ready:
- {
- QDeclarativeGeoMapItemBase *item = qobject_cast<QDeclarativeGeoMapItemBase *>(incubator->object());
- if (!item)
- break;
- itemData->item = item;
- if (!itemData->item) {
- qWarning() << "QDeclarativeGeoMapItemView map item delegate is of unsupported type.";
- delete incubator->object();
- } else {
- if (!batched) {
- m_map->addMapItem(itemData->item);
- fitViewport();
- } else {
- ++m_readyIncubators; // QSemaphore not needed as multiple threads not involved
-
- if (m_readyIncubators == m_itemDataBatched.size()) {
-
- // Clearing stuff older than the reset
- foreach (QDeclarativeGeoMapItemViewItemData *i, m_itemData)
- removeItemData(i);
- m_itemData.clear();
-
- // Adding everthing created after reset was issued
- foreach (QDeclarativeGeoMapItemViewItemData *i, m_itemDataBatched) {
- m_map->addMapItem(i->item);
- }
- m_itemData = m_itemDataBatched;
- m_itemDataBatched.clear();
-
- m_readyIncubators = 0;
- m_repopulating = false;
-
- fitViewport();
- }
- }
- }
- delete itemData->incubator;
- itemData->incubator = 0;
- break;
- }
- case QQmlIncubator::Null:
- // Should never get here
- delete itemData->incubator;
- itemData->incubator = 0;
- break;
- case QQmlIncubator::Error:
- qWarning() << "QDeclarativeGeoMapItemView map item creation failed.";
- delete itemData->incubator;
- itemData->incubator = 0;
- break;
- default:
- ;
- }
+ m_delegateModel->componentComplete();
}
-/*!
- \qmlproperty model QtLocation::MapItemView::model
-
- This property holds the model that provides data used for creating the map items defined by the
- delegate. Only QAbstractItemModel based models are supported.
-*/
-QVariant QDeclarativeGeoMapItemView::model() const
+void QDeclarativeGeoMapItemView::classBegin()
{
- return QVariant::fromValue(m_itemModel);
+ QQmlContext *ctx = qmlContext(this);
+ m_delegateModel = new QQmlDelegateModel(ctx, this);
+ m_delegateModel->classBegin();
+
+ connect(m_delegateModel, &QQmlInstanceModel::modelUpdated, this, &QDeclarativeGeoMapItemView::modelUpdated);
+ connect(m_delegateModel, &QQmlInstanceModel::createdItem, this, &QDeclarativeGeoMapItemView::createdItem);
+ connect(m_delegateModel, &QQmlInstanceModel::destroyingItem, this, &QDeclarativeGeoMapItemView::destroyingItem);
+ connect(m_delegateModel, &QQmlInstanceModel::initItem, this, &QDeclarativeGeoMapItemView::initItem);
}
-void QDeclarativeGeoMapItemView::setModel(const QVariant &model)
+void QDeclarativeGeoMapItemView::destroyingItem(QObject */*object*/)
{
- QAbstractItemModel *itemModel = model.value<QAbstractItemModel *>();
- if (itemModel == m_itemModel)
- return;
-
- if (m_itemModel) {
- disconnect(m_itemModel, SIGNAL(modelReset()), this, SLOT(itemModelReset()));
- disconnect(m_itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- this, SLOT(itemModelRowsRemoved(QModelIndex,int,int)));
- disconnect(m_itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
- this, SLOT(itemModelRowsInserted(QModelIndex,int,int)));
- disconnect(m_itemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- this, SLOT(itemModelRowsMoved(QModelIndex,int,int,QModelIndex,int)));
- disconnect(m_itemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
- this, SLOT(itemModelDataChanged(QModelIndex,QModelIndex,QVector<int>)));
-
- removeInstantiatedItems(); // this also terminates ongong repopulations.
- m_metaObjectType->release();
- m_metaObjectType = 0;
-
- m_itemModel = 0;
- }
-
- if (itemModel) {
- m_itemModel = itemModel;
- connect(m_itemModel, SIGNAL(modelReset()), this, SLOT(itemModelReset()));
- connect(m_itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- this, SLOT(itemModelRowsRemoved(QModelIndex,int,int)));
- connect(m_itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
- this, SLOT(itemModelRowsInserted(QModelIndex,int,int)));
- connect(m_itemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- this, SLOT(itemModelRowsMoved(QModelIndex,int,int,QModelIndex,int)));
- connect(m_itemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
- this, SLOT(itemModelDataChanged(QModelIndex,QModelIndex,QVector<int>)));
-
- m_metaObjectType = new QQmlOpenMetaObjectType(&QObject::staticMetaObject, 0);
- foreach (const QByteArray &name, m_itemModel->roleNames())
- m_metaObjectType->createProperty(name);
-
- instantiateAllItems();
- }
- emit modelChanged();
}
-/*!
- \internal
-*/
-void QDeclarativeGeoMapItemView::itemModelReset()
+void QDeclarativeGeoMapItemView::initItem(int /*index*/, QObject */*object*/)
{
- repopulate();
+
}
-/*!
- \internal
-*/
-void QDeclarativeGeoMapItemView::itemModelRowsInserted(const QModelIndex &index, int start, int end)
+void QDeclarativeGeoMapItemView::createdItem(int index, QObject */*object*/)
{
- Q_UNUSED(index)
-
- if (!m_componentCompleted || !m_map || !m_delegate || !m_itemModel)
+ if (!m_map)
return;
-
- for (int i = start; i <= end; ++i) {
- const QModelIndex insertedIndex = m_itemModel->index(i, 0, index);
- // If ran inside a qquickwidget which forces incubators to be synchronous, this call won't happen
- // with m_repopulating == true while incubators from a model reset are still incubating.
- // Note that having the model in a different thread is not supported in general.
- createItemForIndex(insertedIndex, m_repopulating);
- }
-
- fitViewport();
+ // createdItem is emitted on asynchronous creation. In which case, object has to be invoked again.
+ // See QQmlDelegateModel::object for further info.
+ QDeclarativeGeoMapItemBase *item =
+ qobject_cast<QDeclarativeGeoMapItemBase *>(m_delegateModel->object(index, QQmlIncubator::Asynchronous));
+ if (item)
+ addItemToMap(item, index);
}
-/*!
- \internal
-*/
-void QDeclarativeGeoMapItemView::itemModelRowsRemoved(const QModelIndex &index, int start, int end)
+void QDeclarativeGeoMapItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
{
- Q_UNUSED(index)
-
- if (!m_componentCompleted || !m_map || !m_delegate || !m_itemModel)
- return;
+ // move changes are expressed as one remove + one insert, with the same moveId.
+ // For simplicity, they will be treated as remove + insert.
+ // Changes will be also ignored, as they represent only data changes, not layout changes
+ if (reset) { // Assuming this means "remove everything already instantiated"
+ removeInstantiatedItems();
+ } else {
+ // Remove items from the back to the front to retain the mapping to what is received from the changesets
+ const QVector<QQmlChangeSet::Change> &removes = changeSet.removes();
+ std::map<int, int> mapRemoves;
+ for (int i = 0; i < removes.size(); i++)
+ mapRemoves.insert(std::pair<int, int>(removes.at(i).start(), i));
+
+ for (auto rit = mapRemoves.rbegin(); rit != mapRemoves.rend(); ++rit) {
+ const QQmlChangeSet::Change &c = removes.at(rit->second);
+ for (int idx = c.end() - 1; idx >= c.start(); --idx)
+ removeItemFromMap(idx);
+ }
+ }
- for (int i = end; i >= start; --i) {
- if (m_repopulating) {
- QDeclarativeGeoMapItemViewItemData *itemData = m_itemDataBatched.takeAt(i);
- if (!itemData)
- continue;
- if (itemData->incubator) {
- if (itemData->incubator->isReady()) {
- --m_readyIncubators;
- delete itemData->incubator->object();
- }
- itemData->incubator->clear();
- }
- delete itemData;
- } else {
- QDeclarativeGeoMapItemViewItemData *itemData = m_itemData.takeAt(i);
- removeItemData(itemData);
+ for (const QQmlChangeSet::Change &c: changeSet.inserts()) {
+ for (int idx = c.start(); idx < c.end(); idx++) {
+ QDeclarativeGeoMapItemBase *item =
+ qobject_cast<QDeclarativeGeoMapItemBase *>(m_delegateModel->object(idx, QQmlIncubator::Asynchronous));
+ if (item) // if not, a createdItem signal will be emitted.
+ addItemToMap(item, idx);
}
}
fitViewport();
}
-void QDeclarativeGeoMapItemView::itemModelRowsMoved(const QModelIndex &parent, int start, int end,
- const QModelIndex &destination, int row)
-{
- Q_UNUSED(parent)
- Q_UNUSED(start)
- Q_UNUSED(end)
- Q_UNUSED(destination)
- Q_UNUSED(row)
+/*!
+ \qmlproperty model QtLocation::MapItemView::model
- qWarning() << "QDeclarativeGeoMapItemView does not support models that move rows.";
+ This property holds the model that provides data used for creating the map items defined by the
+ delegate. Only QAbstractItemModel based models are supported.
+*/
+QVariant QDeclarativeGeoMapItemView::model() const
+{
+ return m_itemModel;
}
-void QDeclarativeGeoMapItemView::itemModelDataChanged(const QModelIndex &topLeft,
- const QModelIndex &bottomRight,
- const QVector<int> &roles)
+void QDeclarativeGeoMapItemView::setModel(const QVariant &model)
{
- Q_UNUSED(roles)
-
- if (!m_itemData.count() || (m_repopulating && !m_itemDataBatched.count()) )
+ if (model == m_itemModel)
return;
- for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
- const QModelIndex index = m_itemModel->index(i, 0);
- QDeclarativeGeoMapItemViewItemData *itemData;
- if (m_repopulating)
- itemData= m_itemDataBatched.at(i);
- else
- itemData= m_itemData.at(i);
-
- QHashIterator<int, QByteArray> iterator(m_itemModel->roleNames());
- while (iterator.hasNext()) {
- iterator.next();
-
- QVariant modelData = m_itemModel->data(index, iterator.key());
- if (!modelData.isValid())
- continue;
-
- itemData->context->setContextProperty(QString::fromLatin1(iterator.value().constData()),
- modelData);
- itemData->modelDataMeta->setValue(iterator.value(), modelData);
- }
- }
+ m_itemModel = model;
+ if (m_componentCompleted)
+ m_delegateModel->setModel(m_itemModel);
+
+ emit modelChanged();
}
/*!
@@ -347,8 +216,9 @@ void QDeclarativeGeoMapItemView::setDelegate(QQmlComponent *delegate)
return;
m_delegate = delegate;
+ if (m_componentCompleted)
+ m_delegateModel->setDelegate(m_delegate);
- repopulate();
emit delegateChanged();
}
@@ -365,11 +235,12 @@ bool QDeclarativeGeoMapItemView::autoFitViewport() const
return m_fitViewport;
}
-void QDeclarativeGeoMapItemView::setAutoFitViewport(const bool &fitViewport)
+void QDeclarativeGeoMapItemView::setAutoFitViewport(const bool &fit)
{
- if (fitViewport == m_fitViewport)
+ if (fit == m_fitViewport)
return;
- m_fitViewport = fitViewport;
+ m_fitViewport = fit;
+ fitViewport();
emit autoFitViewportChanged();
}
@@ -378,7 +249,8 @@ void QDeclarativeGeoMapItemView::setAutoFitViewport(const bool &fitViewport)
*/
void QDeclarativeGeoMapItemView::fitViewport()
{
- if (!m_map || !m_map->mapReady() || !m_fitViewport || m_repopulating)
+
+ if (!m_map || !m_map->mapReady() || !m_fitViewport)
return;
if (m_map->mapItems().size() > 0)
@@ -393,6 +265,7 @@ void QDeclarativeGeoMapItemView::setMap(QDeclarativeGeoMap *map)
if (!map || m_map) // changing map on the fly not supported
return;
m_map = map;
+ instantiateAllItems();
}
/*!
@@ -403,10 +276,9 @@ void QDeclarativeGeoMapItemView::removeInstantiatedItems()
if (!m_map)
return;
- terminateOngoingRepopulation();
- foreach (QDeclarativeGeoMapItemViewItemData *itemData, m_itemData)
- removeItemData(itemData);
- m_itemData.clear();
+ // Backward as removeItemFromMap modifies m_instantiatedItems
+ for (int i = m_instantiatedItems.size() -1; i >= 0 ; i--)
+ removeItemFromMap(i);
}
/*!
@@ -416,127 +288,70 @@ void QDeclarativeGeoMapItemView::removeInstantiatedItems()
*/
void QDeclarativeGeoMapItemView::instantiateAllItems()
{
- if (!m_componentCompleted || !m_map || !m_delegate || !m_itemModel)
+ // The assumption is that if m_instantiatedItems isn't empty, instantiated items have been already added
+ if (!m_componentCompleted || !m_map || !m_delegate || m_itemModel.isNull() || !m_instantiatedItems.isEmpty())
return;
- Q_ASSERT(!m_itemDataBatched.size());
- m_repopulating = true;
-
- // QQuickWidget forces incubators to synchronous mode. Thus itemDataChanged gets called during the for loop below.
- m_itemDataBatched.resize(m_itemModel->rowCount());
- for (int i = 0; i < m_itemModel->rowCount(); ++i) {
- const QModelIndex index = m_itemModel->index(i, 0);
- createItemForIndex(index, true);
+
+ // If here, m_delegateModel may contain data, but QQmlInstanceModel::object for each row hasn't been called yet.
+ for (int i = 0; i < m_delegateModel->count(); i++) {
+ QDeclarativeGeoMapItemBase *item =
+ qobject_cast<QDeclarativeGeoMapItemBase *>(m_delegateModel->object(i, QQmlIncubator::Asynchronous));
+ if (item) // else createdItem will be emitted.
+ addItemToMap(item, i);
}
fitViewport();
}
-/*
- * used internally
-*/
-void QDeclarativeGeoMapItemView::removeItemData(QDeclarativeGeoMapItemViewItemData *itemData)
+void QDeclarativeGeoMapItemView::removeItemFromMap(int index)
{
- if (!itemData)
- return;
- if (itemData->incubator) {
- if (itemData->incubator->isReady()) {
- if (itemData->incubator->object() == itemData->item) {
- m_map->removeMapItem(itemData->item); // removeMapItem checks whether the item is in the map, so it's safe to call.
- itemData->item = 0;
+ if (index >= 0 && index < m_instantiatedItems.size()) {
+ QDeclarativeGeoMapItemBase *item = m_instantiatedItems.takeAt(index);
+ if (m_exit && m_map) {
+ if (!item->m_transitionManager) {
+ QScopedPointer<QDeclarativeGeoMapItemTransitionManager>manager(new QDeclarativeGeoMapItemTransitionManager(item));
+ item->m_transitionManager.swap(manager);
+ item->m_transitionManager->m_view = this;
}
- delete itemData->incubator->object();
+ connect(item, &QDeclarativeGeoMapItemBase::exitTransitionFinished,
+ this, &QDeclarativeGeoMapItemView::exitTransitionFinished);
+ item->m_transitionManager->transitionExit();
+ } else {
+ if (m_map)
+ m_map->removeMapItem(item);
+ m_delegateModel->release(item);
}
- itemData->incubator->clear(); // stops ongoing incubation
}
- if (itemData->item)
- m_map->removeMapItem(itemData->item);
- delete itemData; // destroys the ->item too.
}
-void QDeclarativeGeoMapItemView::terminateOngoingRepopulation()
-{
- if (m_repopulating) {
- // Terminate the previous resetting task. Not all incubators finished, but
- // QQmlIncubatorController operates in the same thread, so it is safe
- // to check, here, whether incubators are ready or not, without having
- // to race with them.
-
- foreach (QDeclarativeGeoMapItemViewItemData *itemData, m_itemDataBatched)
- removeItemData(itemData);
-
- m_itemDataBatched.clear();
- m_readyIncubators = 0;
- m_repopulating = false;
- }
-}
-
-/*!
- \internal
- Removes and repopulates all items.
-*/
-void QDeclarativeGeoMapItemView::repopulate()
+void QDeclarativeGeoMapItemView::exitTransitionFinished()
{
- if (!m_itemModel || !m_itemModel->rowCount()) {
- removeInstantiatedItems();
- } else {
- terminateOngoingRepopulation();
- instantiateAllItems(); // removal of instantiated item done at incubation completion
- }
+ QDeclarativeGeoMapItemBase *item = static_cast<QDeclarativeGeoMapItemBase *>(sender());
+ disconnect(item, 0, this, 0);
+ if (m_map)
+ m_map->removeMapItem(item);
+ m_delegateModel->release(item);
}
-/*!
- \internal
-
- Note: this call is async. that is returns to the event loop before returning to the caller.
- May also trigger incubatorStatusChanged() before returning to the caller if the incubator is fast enough.
-*/
-void QDeclarativeGeoMapItemView::createItemForIndex(const QModelIndex &index, bool batched)
+void QDeclarativeGeoMapItemView::addItemToMap(QDeclarativeGeoMapItemBase *item, int index)
{
- // Expected to be already tested by caller.
- Q_ASSERT(m_delegate);
- Q_ASSERT(m_itemModel);
-
- QDeclarativeGeoMapItemViewItemData *itemData = new QDeclarativeGeoMapItemViewItemData;
-
- itemData->modelData = new QObject;
- itemData->modelDataMeta = new QQmlOpenMetaObject(itemData->modelData, m_metaObjectType, false);
- itemData->context = new QQmlContext(qmlContext(this));
-
- QHashIterator<int, QByteArray> iterator(m_itemModel->roleNames());
- while (iterator.hasNext()) {
- iterator.next();
-
- QVariant modelData = m_itemModel->data(index, iterator.key());
- if (!modelData.isValid())
- continue;
-
- itemData->context->setContextProperty(QString::fromLatin1(iterator.value().constData()),
- modelData);
+ if (!item || (m_map && item->quickMap() == m_map))
+ return;
- itemData->modelDataMeta->setValue(iterator.value(), modelData);
+ if (m_map) {
+ m_instantiatedItems.insert(index, item);
+ m_map->addMapItem(item);
+ if (m_enter) {
+ if (!item->m_transitionManager) {
+ QScopedPointer<QDeclarativeGeoMapItemTransitionManager>manager(new QDeclarativeGeoMapItemTransitionManager(item));
+ item->m_transitionManager.swap(manager);
+ }
+ item->m_transitionManager->m_view = this;
+ item->m_transitionManager->transitionEnter();
+ }
}
-
- itemData->context->setContextProperty(QLatin1String("model"), itemData->modelData);
- itemData->context->setContextProperty(QLatin1String("index"), index.row());
-
- if (batched || m_repopulating) {
- if (index.row() < m_itemDataBatched.size())
- m_itemDataBatched.replace(index.row(), itemData);
- else
- m_itemDataBatched.insert(index.row(), itemData);
- } else
- m_itemData.insert(index.row(), itemData);
- itemData->incubator = new MapItemViewDelegateIncubator(this, itemData, batched || m_repopulating);
-
- m_delegate->create(*itemData->incubator, itemData->context);
-}
-
-QDeclarativeGeoMapItemViewItemData::~QDeclarativeGeoMapItemViewItemData()
-{
- delete incubator;
- delete item;
- delete context;
- delete modelData;
}
QT_END_NAMESPACE
+
+
diff --git a/src/location/declarativemaps/qdeclarativegeomapitemview_p.h b/src/location/declarativemaps/qdeclarativegeomapitemview_p.h
index e1132025..a3bf8596 100644
--- a/src/location/declarativemaps/qdeclarativegeomapitemview_p.h
+++ b/src/location/declarativemaps/qdeclarativegeomapitemview_p.h
@@ -51,11 +51,13 @@
//
#include <QtLocation/private/qlocationglobal_p.h>
-
+#include <map>
#include <QtCore/QModelIndex>
#include <QtQml/QQmlParserStatus>
#include <QtQml/QQmlIncubator>
#include <QtQml/qqml.h>
+#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQuick/private/qquicktransition_p.h>
QT_BEGIN_NAMESPACE
@@ -68,6 +70,7 @@ class QQmlOpenMetaObject;
class QQmlOpenMetaObjectType;
class MapItemViewDelegateIncubator;
class QDeclarativeGeoMapItemViewItemData;
+class QDeclarativeGeoMapItemView;
class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemView : public QObject, public QQmlParserStatus
{
@@ -78,6 +81,8 @@ class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemView : public QObject, pub
Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
Q_PROPERTY(bool autoFitViewport READ autoFitViewport WRITE setAutoFitViewport NOTIFY autoFitViewportChanged)
+// Q_PROPERTY(QQuickTransition *enter MEMBER m_enter)
+// Q_PROPERTY(QQuickTransition *exit MEMBER m_exit)
public:
explicit QDeclarativeGeoMapItemView(QQuickItem *parent = 0);
@@ -90,60 +95,46 @@ public:
void setDelegate(QQmlComponent *);
bool autoFitViewport() const;
- void setAutoFitViewport(const bool &);
+ void setAutoFitViewport(const bool &fit);
void setMap(QDeclarativeGeoMap *);
- void repopulate();
void removeInstantiatedItems();
void instantiateAllItems();
- qreal zValue();
- void setZValue(qreal zValue);
-
// From QQmlParserStatus
- virtual void componentComplete();
- void classBegin() {}
+ void componentComplete() override;
+ void classBegin() override;
Q_SIGNALS:
void modelChanged();
void delegateChanged();
void autoFitViewportChanged();
-protected:
- void incubatorStatusChanged(MapItemViewDelegateIncubator *incubator,
- QQmlIncubator::Status status,
- bool batched);
-
private Q_SLOTS:
- void itemModelReset();
- void itemModelRowsInserted(const QModelIndex &index, int start, int end);
- void itemModelRowsRemoved(const QModelIndex &index, int start, int end);
- void itemModelRowsMoved(const QModelIndex &parent, int start, int end,
- const QModelIndex &destination, int row);
- void itemModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
- const QVector<int> &roles);
+ void destroyingItem(QObject *object);
+ void initItem(int index, QObject *object);
+ void createdItem(int index, QObject *object);
+ void modelUpdated(const QQmlChangeSet &changeSet, bool reset);
+ void exitTransitionFinished();
private:
- void createItemForIndex(const QModelIndex &index, bool batched = false);
void fitViewport();
- void terminateOngoingRepopulation();
- void removeItemData(QDeclarativeGeoMapItemViewItemData *itemData);
+ void removeItemFromMap(int index);
+ void addItemToMap(QDeclarativeGeoMapItemBase *item, int index);
bool m_componentCompleted;
QQmlComponent *m_delegate;
- QAbstractItemModel *m_itemModel;
+ QVariant m_itemModel;
QDeclarativeGeoMap *m_map;
- QVector<QDeclarativeGeoMapItemViewItemData *> m_itemData;
- QVector<QDeclarativeGeoMapItemViewItemData *> m_itemDataBatched;
+ QList<QDeclarativeGeoMapItemBase *> m_instantiatedItems;
bool m_fitViewport;
-
- QQmlOpenMetaObjectType *m_metaObjectType;
- int m_readyIncubators;
- bool m_repopulating;
+ QQmlDelegateModel *m_delegateModel;
+ QQuickTransition *m_enter = nullptr;
+ QQuickTransition *m_exit = nullptr;
friend class QDeclarativeGeoMap;
- friend class QDeclarativeGeoMapItemViewItemData;
- friend class MapItemViewDelegateIncubator;
+ friend class QDeclarativeGeoMapItemBase;
+ friend class QDeclarativeGeoMapItemTransitionManager;
};
QT_END_NAMESPACE
diff --git a/src/location/declarativemaps/qdeclarativegeomapitemview_p_p.h b/src/location/declarativemaps/qdeclarativegeomapitemview_p_p.h
deleted file mode 100644
index 3ad3ceb4..00000000
--- a/src/location/declarativemaps/qdeclarativegeomapitemview_p_p.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtLocation module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPLv3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QDECLARATIVEGEOMAPITEMVIEW_P_P_H
-#define QDECLARATIVEGEOMAPITEMVIEW_P_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/QModelIndex>
-#include <QtQml/QQmlParserStatus>
-#include <QtQml/QQmlIncubator>
-#include <QtQml/qqml.h>
-#include <QtQml/private/qqmlopenmetaobject_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class MapItemViewDelegateIncubator;
-class QDeclarativeGeoMapItemView;
-class QDeclarativeGeoMapItemBase;
-
-class QDeclarativeGeoMapItemViewItemData
-{
-public:
- QDeclarativeGeoMapItemViewItemData()
- : incubator(0), item(0), context(0), modelData(0), modelDataMeta(0)
- {
- }
-
- ~QDeclarativeGeoMapItemViewItemData();
-
- MapItemViewDelegateIncubator *incubator;
- QDeclarativeGeoMapItemBase *item;
- QQmlContext *context;
- QObject *modelData;
- QQmlOpenMetaObject *modelDataMeta;
-
- friend class MapItemViewDelegateIncubator;
- friend class QDeclarativeGeoMapItemView;
-};
-
-Q_DECLARE_TYPEINFO(QDeclarativeGeoMapItemViewItemData, Q_MOVABLE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif // QDECLARATIVEGEOMAPITEMVIEW_P_P_H
diff --git a/tests/auto/declarative_ui/tst_map_itemview.qml b/tests/auto/declarative_ui/tst_map_itemview.qml
index ff24e7af..097212ca 100644
--- a/tests/auto/declarative_ui/tst_map_itemview.qml
+++ b/tests/auto/declarative_ui/tst_map_itemview.qml
@@ -447,7 +447,8 @@ Item {
}
testingListModel.remove(0)
- compare(mapForTestingListModel.mapItems.length, 2)
+ // default exit animation kicks in here, of length 300msec
+ tryCompare(mapForTestingListModel, "mapItemsLength", 2)
for (var i = 0; i < 2; ++i) {
itemCoord = mapForTestingListModel.mapItems[i].center
@@ -460,7 +461,8 @@ Item {
compare(mapForTestingListModel.mapItems[2].center, QtPositioning.coordinate(1, 1))
testingListModel.clear()
- compare(mapForTestingListModel.mapItems.length, 0)
+ // default exit animation kicks in here, of length 300msec
+ tryCompare(mapForTestingListModel, "mapItemsLength", 0)
// Repopulating the model with initial data
testingListModel.append({ "lat": 11, "lon": 31 })
@@ -511,7 +513,8 @@ Item {
routeModel.update();
tryCompare(mapForTestingRouteModel, "mapItemsLength", 3)
routeModel.reset();
- compare(mapForTestingRouteModel.mapItems.length, 0)
+ // default exit animation kicks in here, of length 300msec
+ tryCompare(mapForTestingRouteModel, "mapItemsLength", 0)
routeModel.reset(); // clear empty model
routeQuery.numberAlternativeRoutes = 3
routeModel.update();
@@ -532,7 +535,8 @@ Item {
compare(mapForTestingRouteModel.mapItems.length, 4)
compare(mapForTestingRouteModel.mapItems[3], externalCircle2)
routeModel.reset();
- compare(mapForTestingRouteModel.mapItems.length, 1)
+ // default exit animation kicks in here, of length 300msec
+ tryCompare(mapForTestingRouteModel, "mapItemsLength", 1)
mapForTestingRouteModel.clearMapItems()
compare(mapForTestingRouteModel.mapItems.length, 0)