From 912e7eaf9a044941f7f351d8fe2dfdec71760f73 Mon Sep 17 00:00:00 2001 From: Paolo Angelelli Date: Fri, 19 Jan 2018 18:53:24 +0100 Subject: Introduce MapObjectView This also adds addMapObject/removeMapObject to MapObjectView With this patch, MapObjectView becomes the intended way to programmatically populate a map with map objects. Objects can still be declared inside a Map{} element, but to programmatically add them it is necessary to add them to a MapObjectView that is added to the map. MapObjectView has then 3 ways to populate the map: via nested map objects, via a model, and via programmatically added map objects. All these 3 ways can be used simultaneously. Change-Id: Ic7712355fa5e5787a0b83d9547693e288cba1960 Reviewed-by: BogDan Vatra --- src/imports/locationlabs/locationlabs.cpp | 4 +- src/locationlabs/qmapobjectview.cpp | 352 ++++++++++++++++++++++++++++++ src/locationlabs/qmapobjectview_p.h | 111 ++++++++++ src/locationlabs/qmapobjectview_p_p.h | 81 +++++++ 4 files changed, 546 insertions(+), 2 deletions(-) create mode 100644 src/locationlabs/qmapobjectview.cpp create mode 100644 src/locationlabs/qmapobjectview_p.h create mode 100644 src/locationlabs/qmapobjectview_p_p.h diff --git a/src/imports/locationlabs/locationlabs.cpp b/src/imports/locationlabs/locationlabs.cpp index 0cdd7f84..4b77a2ae 100644 --- a/src/imports/locationlabs/locationlabs.cpp +++ b/src/imports/locationlabs/locationlabs.cpp @@ -35,7 +35,7 @@ ****************************************************************************/ //#include -//#include +#include #include //#include @@ -73,7 +73,7 @@ public: // Register the 5.11 types // qmlRegisterType(uri, major, minor, "Navigator"); // qmlRegisterType(uri, major, minor, "MapIconObject"); -// qmlRegisterType(uri, major, minor, "MapObjectView"); + qmlRegisterType(uri, major, minor, "MapObjectView"); qmlRegisterType(uri, major, minor, "MapRouteObject"); // Register the latest Qt version as QML type version diff --git a/src/locationlabs/qmapobjectview.cpp b/src/locationlabs/qmapobjectview.cpp new file mode 100644 index 00000000..696b2bce --- /dev/null +++ b/src/locationlabs/qmapobjectview.cpp @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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$ +** +****************************************************************************/ + +#include "qmapobjectview_p.h" +#include "qmapobjectview_p_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/* + + QGeoMapLayerPrivate + +*/ + + +QMapObjectViewPrivate::QMapObjectViewPrivate(QGeoMapObject *q) + : QGeoMapObjectPrivate(q) +{ +} + +QMapObjectViewPrivate::~QMapObjectViewPrivate() +{ + +} + +QGeoMapObject::Type QMapObjectViewPrivate::type() const +{ + return QGeoMapObject::ViewType; +} + + +/* + + QGeoMapLayerPrivateDefault + +*/ + + +QMapObjectViewPrivateDefault::QMapObjectViewPrivateDefault(const QMapObjectViewPrivate &other) : QMapObjectViewPrivate(other.q) +{ +} + +QMapObjectViewPrivateDefault::~QMapObjectViewPrivateDefault() +{ + +} + +QMapObjectViewPrivateDefault::QMapObjectViewPrivateDefault(QGeoMapObject *q) : QMapObjectViewPrivate(q) +{ + +} + +QGeoMapObjectPrivate *QMapObjectViewPrivateDefault::clone() +{ + return new QMapObjectViewPrivateDefault(*this); +} + +/* + + QMapObjectView + +*/ + + +QMapObjectView::QMapObjectView(QObject *parent) + : QGeoMapObject(QExplicitlySharedDataPointer(new QMapObjectViewPrivateDefault(this)), parent) +{ + +} + +QMapObjectView::~QMapObjectView() +{ + flushDelegateModel(); + flushUserAddedMapObjects(); +} + +QList QMapObjectView::geoMapObjectChildren() const +{ + auto kids = QGeoMapObject::geoMapObjectChildren(); + auto size = m_instantiatedMapObjects.count(); + for (int i = 0; i < size; ++i) { + auto obj = qobject_cast(m_instantiatedMapObjects[i]); + if (obj) + kids << obj; + } + for (int i = 0; i < m_userAddedMapObjects.size(); ++i) { + auto obj = m_userAddedMapObjects.at(i); + if (obj) + kids << obj; + } + return kids; +} + +void QMapObjectView::setMap(QGeoMap *map) +{ + QMapObjectViewPrivate *d = static_cast(d_ptr.data()); + if (d->m_map == map) + return; + + QGeoMapObject::setMap(map); // This is where the specialized pimpl gets created and injected + + for (int i = 0; i < m_userAddedMapObjects.size(); ++i) { + auto obj = m_userAddedMapObjects.at(i); + if (obj && obj->map() != map) + obj->setMap(map); + } + + if (!map) { + // Map was set, now it has ben re-set to NULL + flushDelegateModel(); + flushUserAddedMapObjects(); + d_ptr = new QMapObjectViewPrivateDefault(*d); + } else if (d->m_componentCompleted) { + // Map was null, now it's set AND delegateModel is already complete. + // some delegates may have been incubated but not added to the map. + for (int i = 0; i < m_pendingMapObjects.size(); ++i) { + auto obj = m_pendingMapObjects.at(i); + if (obj && obj->map() != map) + obj->setMap(map); + } + m_pendingMapObjects.clear(); + } +} + +void QMapObjectView::classBegin() +{ + QQmlContext *ctx = qmlContext(this); + m_delegateModel = new QQmlDelegateModel(ctx, this); + m_delegateModel->classBegin(); + + QQmlInstanceModel *model = m_delegateModel; + connect(model, &QQmlInstanceModel::modelUpdated, this, &QMapObjectView::modelUpdated); + connect(model, &QQmlInstanceModel::createdItem, this, &QMapObjectView::createdItem); + connect(model, &QQmlInstanceModel::destroyingItem, this, &QMapObjectView::destroyingItem); + connect(model, &QQmlInstanceModel::initItem, this, &QMapObjectView::initItem); +} + +void QMapObjectView::componentComplete() +{ + QGeoMapObject::componentComplete(); + if (m_delegate) + m_delegateModel->setDelegate(m_delegate); + if (m_model.isValid()) + m_delegateModel->setModel(m_model); + m_delegateModel->componentComplete(); +} + +QVariant QMapObjectView::model() const +{ + return m_model; +} + +QQmlComponent *QMapObjectView::delegate() const +{ + return m_delegate; +} + +void QMapObjectView::setModel(QVariant model) +{ + if (m_model == model) + return; + m_model = model; + + if (d_ptr->m_componentCompleted) + m_delegateModel->setModel(model); + + emit modelChanged(model); +} + +void QMapObjectView::setDelegate(QQmlComponent *delegate) +{ + if (m_delegate == delegate) + return; + m_delegate = delegate; + + if (d_ptr->m_componentCompleted) + m_delegateModel->setDelegate(delegate); + + emit delegateChanged(delegate); +} + +/*! + \qmlmethod void Qt.labs.location::MapObjectView::addMapObject(MapObject object) + + Adds the given \a object to the MapObjectView (for example MapIconObject, MapRouteObject), and, + indirectly, to the underlying map. If the object already is on the MapObjectView, it will not be added again. + + \sa removeMapObject +*/ +void QMapObjectView::addMapObject(QGeoMapObject *object) +{ + if (m_userAddedMapObjects.indexOf(object) < 0) + m_userAddedMapObjects.append(object); + if (map() && object->map() != map()) + object->setMap(map()); +} + +/*! + \qmlmethod void Qt.labs.location::MapObjectView::removeMapObject(MapObject object) + + Removes the given \a object from the MapObjectView (for example MapIconObject, MapRouteObject), and, + indirectly, from the underlying map. + + \sa addMapObject +*/ +void QMapObjectView::removeMapObject(QGeoMapObject *object) +{ + int idx = m_userAddedMapObjects.indexOf(object); + if ( idx >= 0) { + object->setMap(nullptr); + m_userAddedMapObjects.remove(idx); + } +} + +void QMapObjectView::destroyingItem(QObject */*object*/) +{ + +} + +void QMapObjectView::initItem(int /*index*/, QObject */*object*/) +{ + +} + +void QMapObjectView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) +{ + // 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" + flushDelegateModel(); + } else { + // Remove map objects from the back to the front to retain the mapping to what is received from the changesets + const QVector &removes = changeSet.removes(); + std::map mapRemoves; + for (int i = 0; i < removes.size(); i++) + mapRemoves.insert(std::pair(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) + removeMapObjectFromMap(idx); + } + } + + for (const QQmlChangeSet::Change &c: changeSet.inserts()) { + for (int idx = c.start(); idx < c.end(); idx++) { + m_instantiatedMapObjects.insert(idx, nullptr); + QGeoMapObject *mo = qobject_cast(m_delegateModel->object(idx, QQmlIncubator::Asynchronous)); + if (mo) // if not, a createdItem signal will be emitted. + addMapObjectToMap(mo, idx); + } + } +} + +void QMapObjectView::addMapObjectToMap(QGeoMapObject *object, int index) +{ + if (!object) + return; + + m_instantiatedMapObjects[index] = object; + if (map()) + object->setMap(map()); + else + m_pendingMapObjects << object; + + // ToDo: + // Figure out the proper way to replace "mo->setVisible(visible());". Options: + // - simply leave it to the user to set up a property binding + // - set up a property binding automatically + // - add a viewVisibility member to QGeoMapObject that gets combined at all times, + // and a connection for it. +} + +void QMapObjectView::removeMapObjectFromMap(int index) +{ + if (index >= 0 && index < m_instantiatedMapObjects.size()) { + QGeoMapObject *mo = m_instantiatedMapObjects.takeAt(index); + if (!mo) + return; + mo->setMap(nullptr); + m_delegateModel->release(mo); + } +} + +// See QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incubationMode) doc +// for explanation on when createdItem is emitted. +void QMapObjectView::createdItem(int index, QObject *object) +{ + // According to the documentation above, object() should be called again for index. + // However, this seem to result in too many references for index, which will prevent destruction with + // one single release() + // QGeoMapObject *mo = qobject_cast(m_delegateModel->object(index, QQmlIncubator::Asynchronous)); + QGeoMapObject *mo = qobject_cast(object); + if (mo) + addMapObjectToMap(mo, index); +} + + +void QMapObjectView::flushDelegateModel() +{ + // Backward as removeItemFromMap modifies m_instantiatedItems + for (int i = m_instantiatedMapObjects.size() -1; i >= 0 ; i--) + removeMapObjectFromMap(i); +} + +void QMapObjectView::flushUserAddedMapObjects() +{ + for (int i = 0; i < m_userAddedMapObjects.size(); ++i) { + auto obj = m_userAddedMapObjects.at(i); + if (obj) + obj->setMap(nullptr); // obj parent might not be this. If so, it would not be destroyed by destroying this view. + } +} + +QT_END_NAMESPACE + diff --git a/src/locationlabs/qmapobjectview_p.h b/src/locationlabs/qmapobjectview_p.h new file mode 100644 index 00000000..5477ca72 --- /dev/null +++ b/src/locationlabs/qmapobjectview_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QGEOMAPLAYER_P_H +#define QGEOMAPLAYER_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 +#include +#include + +class QQmlDelegateModel; +class QMapObjectViewPrivate; +class QQmlChangeSet; +class Q_LOCATION_PRIVATE_EXPORT QMapObjectView : public QGeoMapObject +{ + Q_OBJECT + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_INTERFACES(QQmlParserStatus) +public: + QMapObjectView(QObject *parent = nullptr); + ~QMapObjectView() override; + + // QGeoMapObject interface + QList geoMapObjectChildren() const override; + void setMap(QGeoMap *map) override; + + // QQmlParserStatus interface + void classBegin() override; + void componentComplete() override; + + QVariant model() const; + void setModel(QVariant model); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent * delegate); + +public Q_SLOTS: + // The dynamic API that matches Map.add/remove MapItem + void addMapObject(QGeoMapObject *object); + void removeMapObject(QGeoMapObject *object); + +signals: + void modelChanged(QVariant model); + void delegateChanged(QQmlComponent * delegate); + +protected Q_SLOTS: + void destroyingItem(QObject *object); + void initItem(int index, QObject *object); + void createdItem(int index, QObject *object); + void modelUpdated(const QQmlChangeSet &changeSet, bool reset); + +protected: + void addMapObjectToMap(QGeoMapObject *object, int index); + void removeMapObjectFromMap(int index); + void flushDelegateModel(); + void flushUserAddedMapObjects(); + + QVariant m_model; + QQmlComponent *m_delegate = nullptr; + QQmlDelegateModel *m_delegateModel = nullptr; + QVector> m_instantiatedMapObjects; + QVector> m_pendingMapObjects; + QVector> m_userAddedMapObjects; // A third list containing the objects dynamically added through addMapObject +}; + +#endif // QGEOMAPLAYER_P_H diff --git a/src/locationlabs/qmapobjectview_p_p.h b/src/locationlabs/qmapobjectview_p_p.h new file mode 100644 index 00000000..af6bdf8f --- /dev/null +++ b/src/locationlabs/qmapobjectview_p_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QGEOMAPLAYER_P_P_H +#define QGEOMAPLAYER_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 +#include +#include +#include + +class QQmlDelegateModel; +class QGeoMap; +class Q_LOCATION_PRIVATE_EXPORT QMapObjectViewPrivate : public QGeoMapObjectPrivate +{ +public: + QMapObjectViewPrivate(QGeoMapObject *q); + ~QMapObjectViewPrivate() override; + + virtual QGeoMapObject::Type type() const override final; +}; + +class Q_LOCATION_PRIVATE_EXPORT QMapObjectViewPrivateDefault : public QMapObjectViewPrivate +{ +public: + QMapObjectViewPrivateDefault(QGeoMapObject *q); + QMapObjectViewPrivateDefault(const QMapObjectViewPrivate &other); + ~QMapObjectViewPrivateDefault() override; + + + // QGeoMapObjectPrivate interface +public: + QGeoMapObjectPrivate *clone() override; +}; + +#endif // QGEOMAPLAYER_P_P_H -- cgit v1.2.3