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