Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdeclarativegeomapitemview.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 Jolla Ltd.
2// Copyright (C) 2022 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
8
9#include <QtCore/QAbstractItemModel>
10#include <QtQml/QQmlContext>
11#include <QtQuick/private/qquickanimation_p.h>
12#include <QtQml/QQmlListProperty>
13
15
16/*!
17 \qmltype MapItemView
18 \nativetype QDeclarativeGeoMapItemView
19 \inqmlmodule QtLocation
20 \ingroup qml-QtLocation5-maps
21 \since QtLocation 5.5
22 \inherits QtObject
23
24 \brief The MapItemView is used to populate Map from a model.
25
26 The MapItemView is used to populate Map with MapItems from a model.
27 The MapItemView type only makes sense when contained in a Map,
28 meaning that it has no standalone presentation.
29
30 \section2 Example Usage
31
32 This example demonstrates how to use the MapViewItem object to display
33 a \l{route}{route} on a \l{Map}{map}:
34
35 \snippet declarative/maps.qml QtQuick import
36 \snippet declarative/maps.qml QtLocation import
37 \codeline
38 \snippet declarative/maps.qml MapRoute
39*/
40
41/*!
42 \qmlproperty Transition QtLocation::MapItemView::add
43
44 This property holds the transition that is applied to the map items created by the view
45 when they are instantiated and added to the map.
46
47 \since QtLocation 5.12
48*/
49
50/*!
51 \qmlproperty Transition QtLocation::MapItemView::remove
52
53 This property holds the transition that is applied to the map items created by the view
54 when they are removed.
55
56 \since QtLocation 5.12
57*/
58
59QDeclarativeGeoMapItemView::QDeclarativeGeoMapItemView(QQuickItem *parent)
60 : QDeclarativeGeoMapItemGroup(parent)
61{
62 m_exit = new QQuickTransition(this);
63 QQmlListProperty<QQuickAbstractAnimation> anims = m_exit->animations();
64 QQuickNumberAnimation *ani = new QQuickNumberAnimation(m_exit);
65 ani->setProperty(QStringLiteral("opacity"));
66 ani->setTo(0.0);
67 ani->setDuration(300.0);
68 anims.append(&anims, ani);
69}
70
71QDeclarativeGeoMapItemView::~QDeclarativeGeoMapItemView()
72{
73 // No need to remove instantiated items: if the MIV has instantiated items because it has been added
74 // to a Map (or is child of a Map), the Map destructor takes care of removing it and the instantiated items.
75}
76
77/*!
78 \internal
79*/
80void QDeclarativeGeoMapItemView::componentComplete()
81{
82 QDeclarativeGeoMapItemGroup::componentComplete();
83 m_componentCompleted = true;
84 if (!m_itemModel.isNull())
85 m_delegateModel->setModel(std::exchange(m_itemModel, QVariant()));
86
87 if (m_delegate)
88 m_delegateModel->setDelegate(m_delegate);
89
90 m_delegateModel->setDelegateModelAccess(m_delegateModelAccess);
91 m_delegateModel->componentComplete();
92
93 // Only connect it here because we don't want to see the modelChanged() for the initial setting
94 // of the model.
95 QObject::connect(m_delegateModel, &QQmlDelegateModel::modelChanged,
96 this, &QDeclarativeGeoMapItemView::modelChanged);
97}
98
99void QDeclarativeGeoMapItemView::classBegin()
100{
101 QDeclarativeGeoMapItemGroup::classBegin();
102 QQmlContext *ctx = qmlContext(this);
103 m_delegateModel = new QQmlDelegateModel(ctx, this);
104 m_delegateModel->classBegin();
105
106 connect(m_delegateModel, &QQmlInstanceModel::modelUpdated, this, &QDeclarativeGeoMapItemView::modelUpdated);
107 connect(m_delegateModel, &QQmlInstanceModel::createdItem, this, &QDeclarativeGeoMapItemView::createdItem);
108// connect(m_delegateModel, &QQmlInstanceModel::destroyingItem, this, &QDeclarativeGeoMapItemView::destroyingItem);
109// connect(m_delegateModel, &QQmlInstanceModel::initItem, this, &QDeclarativeGeoMapItemView::initItem);
110}
111
112void QDeclarativeGeoMapItemView::destroyingItem(QObject * /*object*/)
113{
114
115}
116
117void QDeclarativeGeoMapItemView::initItem(int /*index*/, QObject * /*object*/)
118{
119
120}
121
122void QDeclarativeGeoMapItemView::createdItem(int index, QObject * /*object*/)
123{
124 if (!m_map)
125 return;
126 // createdItem is emitted on asynchronous creation. In which case, object has to be invoked again.
127 // See QQmlDelegateModel::object for further info.
128
129 // DelegateModel apparently triggers this method in any case, that is:
130 // 1. Synchronous incubation, delegate instantiated on the first object() call (during the object() call!)
131 // 2. Async incubation, delegate not instantiated on the first object() call
132 // 3. Async incubation, delegate present in the cache, and returned on the first object() call.
133 // createdItem also called during the object() call.
134 if (m_creatingObject) {
135 // Falling into case 1. or 3. Returning early to prevent double referencing the delegate instance.
136 return;
137 }
138
139 QQuickItem *item = qobject_cast<QQuickItem *>(m_delegateModel->object(index, m_incubationMode));
140 if (item)
141 addDelegateToMap(item, index, true);
142 else
143 qWarning() << "QQmlDelegateModel:: object called in createdItem for " << index << " produced a null item";
144}
145
146void QDeclarativeGeoMapItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
147{
148 if (!m_map) // everything will be done in instantiateAllItems. Removal is done by declarativegeomap.
149 return;
150
151 // move changes are expressed as one remove + one insert, with the same moveId.
152 // For simplicity, they will be treated as remove + insert.
153 // Changes will be also ignored, as they represent only data changes, not layout changes
154 if (reset) { // Assuming this means "remove everything already instantiated"
155 removeInstantiatedItems();
156 } else {
157 // Remove items from the back to the front to retain the mapping to what is received from the changesets
158 const QList<QQmlChangeSet::Change> &removes = changeSet.removes();
159 std::map<int, int> mapRemoves;
160 for (qsizetype i = 0; i < removes.size(); i++)
161 mapRemoves.insert(std::pair<int, int>(removes.at(i).start(), i));
162
163 for (auto rit = mapRemoves.rbegin(); rit != mapRemoves.rend(); ++rit) {
164 const QQmlChangeSet::Change &c = removes.at(rit->second);
165 for (auto idx = c.end() - 1; idx >= c.start(); --idx)
166 removeDelegateFromMap(idx);
167 }
168 }
169
170 QScopedValueRollback createBlocker(m_creatingObject, true);
171 for (const QQmlChangeSet::Change &c: changeSet.inserts()) {
172 for (auto idx = c.start(); idx < c.end(); idx++) {
173 QObject *delegateInstance = m_delegateModel->object(idx, m_incubationMode);
174 addDelegateToMap(qobject_cast<QQuickItem *>(delegateInstance), idx);
175 }
176 }
177
178 fitViewport();
179}
180
181/*!
182 \qmlproperty model QtLocation::MapItemView::model
183
184 This property holds the model that provides data used for creating the map items defined by the
185 delegate. Only QAbstractItemModel based models are supported.
186*/
187QVariant QDeclarativeGeoMapItemView::model() const
188{
189 return m_componentCompleted
190 ? m_delegateModel->model()
191 : m_itemModel;
192}
193
194void QDeclarativeGeoMapItemView::setModel(const QVariant &model)
195{
196 if (m_componentCompleted) {
197 if (m_delegateModel->model() == model)
198 return;
199
200 // Make sure to clear all stale items from the map and the model
201 m_delegateModel->drainReusableItemsPool(0);
202 removeInstantiatedItems(false);
203
204 m_delegateModel->setModel(model);
205 return;
206 }
207
208 if (model == m_itemModel)
209 return;
210
211 m_itemModel = model;
212 emit modelChanged();
213}
214
215/*!
216 \qmlproperty Component QtLocation::MapItemView::delegate
217
218 This property holds the delegate which defines how each item in the
219 model should be displayed. The Component must contain exactly one
220 MapItem -derived object as the root object.
221*/
222QQmlComponent *QDeclarativeGeoMapItemView::delegate() const
223{
224 return m_delegate;
225}
226
227void QDeclarativeGeoMapItemView::setDelegate(QQmlComponent *delegate)
228{
229 if (m_delegate == delegate)
230 return;
231
232 m_delegate = delegate;
233 if (m_componentCompleted)
234 m_delegateModel->setDelegate(m_delegate);
235
236 emit delegateChanged();
237}
238
239/*!
240 \qmlproperty bool QtLocation::MapItemView::autoFitViewport
241
242 This property controls whether to automatically pan and zoom the viewport
243 to display all map items when items are added or removed.
244
245 Defaults to false.
246*/
247bool QDeclarativeGeoMapItemView::autoFitViewport() const
248{
249 return m_fitViewport;
250}
251
252void QDeclarativeGeoMapItemView::setAutoFitViewport(const bool &fit)
253{
254 if (fit == m_fitViewport)
255 return;
256 m_fitViewport = fit;
257 fitViewport();
258 emit autoFitViewportChanged();
259}
260
261/*!
262 \internal
263*/
264void QDeclarativeGeoMapItemView::fitViewport()
265{
266
267 if (!m_map || !m_map->mapReady() || !m_fitViewport)
268 return;
269
270 if (m_map->mapItems().size() > 0)
271 m_map->fitViewportToMapItems();
272}
273
274/*!
275 \internal
276*/
277void QDeclarativeGeoMapItemView::setMap(QDeclarativeGeoMap *map)
278{
279 if (!map || m_map) // changing map on the fly not supported
280 return;
281 m_map = map;
282 instantiateAllItems();
283}
284
285/*!
286 \internal
287*/
288void QDeclarativeGeoMapItemView::removeInstantiatedItems(bool transition)
289{
290 if (!m_map)
291 return;
292
293 // with transition = false removeInstantiatedItems aborts ongoing exit transitions //QTBUG-69195
294 // Backward as removeItemFromMap modifies m_instantiatedItems
295 for (qsizetype i = m_instantiatedItems.size() -1; i >= 0 ; i--)
296 removeDelegateFromMap(i, transition);
297}
298
299/*!
300 \internal
301
302 Instantiates all items.
303*/
304void QDeclarativeGeoMapItemView::instantiateAllItems()
305{
306 // The assumption is that if m_instantiatedItems isn't empty, instantiated items have been already added
307 if (!m_componentCompleted
308 || !m_map
309 || !m_delegate
310 || m_delegateModel->model().isNull()
311 || !m_instantiatedItems.isEmpty()) {
312 return;
313 }
314
315 // If here, m_delegateModel may contain data, but QQmlInstanceModel::object for each row hasn't been called yet.
316 QScopedValueRollback createBlocker(m_creatingObject, true);
317 for (qsizetype i = 0; i < m_delegateModel->count(); i++) {
318 QObject *delegateInstance = m_delegateModel->object(i, m_incubationMode);
319 addDelegateToMap(qobject_cast<QQuickItem *>(delegateInstance), i);
320 }
321
322 fitViewport();
323}
324
325void QDeclarativeGeoMapItemView::setIncubateDelegates(bool useIncubators)
326{
327 const QQmlIncubator::IncubationMode incubationMode =
328 (useIncubators) ? QQmlIncubator::Asynchronous : QQmlIncubator::Synchronous;
329 if (m_incubationMode == incubationMode)
330 return;
331 m_incubationMode = incubationMode;
332 emit incubateDelegatesChanged();
333}
334
335bool QDeclarativeGeoMapItemView::incubateDelegates() const
336{
337 return m_incubationMode == QQmlIncubator::Asynchronous;
338}
339
340/*!
341 \qmlproperty enumeration QtLocation::MapItemView::delegateModelAccess
342 \since 6.10
343
344 This property determines how delegates can access the model.
345
346 \value DelegateModel.ReadOnly
347 Prohibit delegates from writing the model via either context properties,
348 the \c model object, or required properties.
349
350 \value DelegateModel.ReadWrite
351 Allow delegates to write the model via either context properties,
352 the \c model object, or required properties.
353
354 \value DelegateModel.Qt5ReadWrite
355 Allow delegates to write the model via the \c model object and context
356 properties but \e not via required properties.
357
358 The default is \c DelegateModel.Qt5ReadWrite.
359
360 \sa {Models and Views in Qt Quick#Changing Model Data}
361*/
362QQmlDelegateModel::DelegateModelAccess QDeclarativeGeoMapItemView::delegateModelAccess() const
363{
364 return m_delegateModelAccess;
365}
366
367void QDeclarativeGeoMapItemView::setDelegateModelAccess(
368 QQmlDelegateModel::DelegateModelAccess delegateModelAccess)
369{
370 if (m_delegateModelAccess == delegateModelAccess)
371 return;
372
373 m_delegateModelAccess = delegateModelAccess;
374 if (m_componentCompleted)
375 m_delegateModel->setDelegateModelAccess(m_delegateModelAccess);
376
377 emit delegateModelAccessChanged();
378}
379
380QList<QQuickItem *> QDeclarativeGeoMapItemView::mapItems()
381{
382 return m_instantiatedItems;
383}
384
385QQmlInstanceModel::ReleaseFlags QDeclarativeGeoMapItemView::disposeDelegate(QQuickItem *item)
386{
387 disconnect(item, 0, this, 0);
388 removeDelegateFromMap(item);
389 item->setParentItem(nullptr); // Needed because
390 item->setParent(nullptr); // m_delegateModel->release(item) does not destroy the item most of the times!!
391 QQmlInstanceModel::ReleaseFlags releaseStatus = m_delegateModel->release(item);
392 return releaseStatus;
393}
394
395void QDeclarativeGeoMapItemView::removeDelegateFromMap(int index, bool transition)
396{
397 if (index >= 0 && index < m_instantiatedItems.size()) {
398 QQuickItem *item = m_instantiatedItems.takeAt(index);
399 if (!item) { // not yet incubated
400 // Don't cancel incubation explicitly when model rows are removed, as DelegateModel
401 // apparently takes care of incubating elements when the model remove those indices.
402 // Cancel them explicitly only when a MIV is removed from a map.
403 if (!transition)
404 m_delegateModel->cancel(index);
405 return;
406 }
407 // item can be either a QDeclarativeGeoMapItemBase or a QDeclarativeGeoMapItemGroup (subclass)
408 if (m_exit && m_map && transition) {
409 transitionItemOut(item);
410 } else {
411 if (m_exit && m_map && !transition) {
412 // check if the exit transition is still running, if so stop it.
413 // This can happen when explicitly calling Map.removeMapItemView, soon after adding it.
414 terminateExitTransition(item);
415 }
416 QQmlInstanceModel::ReleaseFlags releaseStatus = disposeDelegate(item);
417#ifdef QT_DEBUG
418 if (releaseStatus == QQmlInstanceModel::Referenced)
419 qWarning() << "item "<< index << "(" << item << ") still referenced";
420#else
421 Q_UNUSED(releaseStatus);
422#endif
423 }
424 }
425}
426
427void QDeclarativeGeoMapItemView::removeDelegateFromMap(QQuickItem *o)
428{
429 if (!m_map)
430 return;
431
432 QDeclarativeGeoMapItemBase *item = qobject_cast<QDeclarativeGeoMapItemBase *>(o);
433 if (item) {
434 m_map->removeMapItem(item);
435 return;
436 }
437 QDeclarativeGeoMapItemView *view = qobject_cast<QDeclarativeGeoMapItemView *>(o);
438 if (view) {
439 m_map->removeMapItemView(view);
440 return;
441 }
442 QDeclarativeGeoMapItemGroup *group = qobject_cast<QDeclarativeGeoMapItemGroup *>(o);
443 if (group) {
444 m_map->removeMapItemGroup(group);
445 return;
446 }
447}
448
449void QDeclarativeGeoMapItemView::transitionItemOut(QQuickItem *o)
450{
451 QDeclarativeGeoMapItemGroup *group = qobject_cast<QDeclarativeGeoMapItemGroup *>(o);
452 if (group) {
453 if (!group->m_transitionManager) {
454 std::unique_ptr<QDeclarativeGeoMapItemTransitionManager>manager(new QDeclarativeGeoMapItemTransitionManager(group));
455 group->m_transitionManager.swap(manager);
456 group->m_transitionManager->m_view = this;
457 }
458 connect(group, &QDeclarativeGeoMapItemGroup::removeTransitionFinished,
459 this, &QDeclarativeGeoMapItemView::exitTransitionFinished);
460
461 group->m_transitionManager->transitionExit();
462 return;
463 }
464 QDeclarativeGeoMapItemBase *item = qobject_cast<QDeclarativeGeoMapItemBase *>(o);
465 if (item) {
466 if (!item->m_transitionManager) {
467 std::unique_ptr<QDeclarativeGeoMapItemTransitionManager> manager(new QDeclarativeGeoMapItemTransitionManager(item));
468 item->m_transitionManager.swap(manager);
469 item->m_transitionManager->m_view = this;
470 }
471 connect(item, &QDeclarativeGeoMapItemBase::removeTransitionFinished,
472 this, &QDeclarativeGeoMapItemView::exitTransitionFinished);
473
474 item->m_transitionManager->transitionExit();
475 return;
476 }
477}
478
479void QDeclarativeGeoMapItemView::terminateExitTransition(QQuickItem *o)
480{
481 QDeclarativeGeoMapItemGroup *group = qobject_cast<QDeclarativeGeoMapItemGroup *>(o);
482 if (group && group->m_transitionManager) {
483 group->m_transitionManager->cancel();
484 return;
485 }
486 QDeclarativeGeoMapItemBase *item = qobject_cast<QDeclarativeGeoMapItemBase *>(o);
487 if (item && item->m_transitionManager) {
488 item->m_transitionManager->cancel();
489 return;
490 }
491}
492
493void QDeclarativeGeoMapItemView::exitTransitionFinished()
494{
495 QQuickItem *item = qobject_cast<QQuickItem *>(sender());
496 if (!item)
497 return;
498 QQmlInstanceModel::ReleaseFlags releaseStatus = disposeDelegate(item);
499#ifdef QT_DEBUG
500 if (releaseStatus == QQmlInstanceModel::Referenced)
501 qWarning() << "item "<<item<<" still referenced";
502#else
503 Q_UNUSED(releaseStatus);
504#endif
505}
506
507void QDeclarativeGeoMapItemView::addItemToMap(QDeclarativeGeoMapItemBase *item, int index, bool createdItem)
508{
509
510 if (m_map && item->quickMap() == m_map) // test for *item done in the caller
511 return;
512
513 if (m_map) {
514 insertInstantiatedItem(index, item, createdItem);
515 item->setParentItem(this);
516 m_map->addMapItem(item);
517 if (m_enter) {
518 if (!item->m_transitionManager) {
519 std::unique_ptr<QDeclarativeGeoMapItemTransitionManager>manager(new QDeclarativeGeoMapItemTransitionManager(item));
520 item->m_transitionManager.swap(manager);
521 }
522 item->m_transitionManager->m_view = this;
523 item->m_transitionManager->transitionEnter();
524 }
525 }
526}
527
528void QDeclarativeGeoMapItemView::insertInstantiatedItem(int index, QQuickItem *o, bool createdItem)
529{
530 if (createdItem)
531 m_instantiatedItems.replace(index, o);
532 else
533 m_instantiatedItems.insert(index, o);
534}
535
536void QDeclarativeGeoMapItemView::addItemViewToMap(QDeclarativeGeoMapItemView *item, int index, bool createdItem)
537{
538 if (m_map && item->quickMap() == m_map) // test for *item done in the caller
539 return;
540
541 if (m_map) {
542 insertInstantiatedItem(index, item, createdItem);
543 item->setParentItem(this);
544 m_map->addMapItemView(item);
545 if (m_enter) {
546 if (!item->m_transitionManager) {
547 std::unique_ptr<QDeclarativeGeoMapItemTransitionManager> manager(new QDeclarativeGeoMapItemTransitionManager(item));
548 item->m_transitionManager.swap(manager);
549 }
550 item->m_transitionManager->m_view = this;
551 item->m_transitionManager->transitionEnter();
552 }
553 }
554}
555
556void QDeclarativeGeoMapItemView::addItemGroupToMap(QDeclarativeGeoMapItemGroup *item, int index, bool createdItem)
557{
558 if (m_map && item->quickMap() == m_map) // test for *item done in the caller
559 return;
560
561 if (m_map) {
562 insertInstantiatedItem(index, item, createdItem);
563 item->setParentItem(this);
564 m_map->addMapItemGroup(item);
565 if (m_enter) {
566 if (!item->m_transitionManager) {
567 std::unique_ptr<QDeclarativeGeoMapItemTransitionManager>manager(new QDeclarativeGeoMapItemTransitionManager(item));
568 item->m_transitionManager.swap(manager);
569 }
570 item->m_transitionManager->m_view = this;
571 item->m_transitionManager->transitionEnter();
572 }
573 }
574}
575
576void QDeclarativeGeoMapItemView::addDelegateToMap(QQuickItem *object, int index, bool createdItem)
577{
578 if (!object) {
579 if (!createdItem)
580 m_instantiatedItems.insert(index, nullptr); // insert placeholder
581 return;
582 }
583 QDeclarativeGeoMapItemBase *item = qobject_cast<QDeclarativeGeoMapItemBase *>(object);
584 if (item) { // else createdItem will be emitted.
585 addItemToMap(item, index, createdItem);
586 return;
587 }
588 QDeclarativeGeoMapItemView *view = qobject_cast<QDeclarativeGeoMapItemView *>(object);
589 if (view) {
590 addItemViewToMap(view, index, createdItem);
591 return;
592 }
593 QDeclarativeGeoMapItemGroup *group = qobject_cast<QDeclarativeGeoMapItemGroup *>(object);
594 if (group) {
595 addItemGroupToMap(group, index, createdItem);
596 return;
597 }
598 qWarning() << "addDelegateToMap called with a "<< object->metaObject()->className();
599}
600
601QT_END_NAMESPACE