1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickcontainer_p.h"
5#include "qquickcontainer_p_p.h"
6
7#include <QtQuick/private/qquickflickable_p.h>
8#include <QtQuick/private/qquickitemview_p.h>
9
10QT_BEGIN_NAMESPACE
11
12/*!
13 \qmltype Container
14 \inherits Control
15//! \nativetype QQuickContainer
16 \inqmlmodule QtQuick.Controls
17 \since 5.7
18 \ingroup qtquickcontrols-containers
19 \brief Abstract base type providing functionality common to containers.
20
21 Container is the base type of container-like user interface controls that
22 allow dynamic insertion and removal of items.
23
24 \section2 Using Containers
25
26 Typically, items are statically declared as children of Container, but it
27 is also possible to \l {addItem}{add}, \l {insertItem}{insert},
28 \l {moveItem}{move} and \l {removeItem}{remove} items dynamically. The
29 items in a container can be accessed using \l itemAt() or
30 \l contentChildren.
31
32 Most containers have the concept of a "current" item. The current item is
33 specified via the \l currentIndex property, and can be accessed using the
34 read-only \l currentItem property.
35
36 The following example illustrates dynamic insertion of items to a \l TabBar,
37 which is one of the concrete implementations of Container.
38
39 \code
40 Row {
41 TabBar {
42 id: tabBar
43
44 currentIndex: 0
45 width: parent.width - addButton.width
46
47 TabButton { text: "TabButton" }
48 }
49
50 Component {
51 id: tabButton
52 TabButton { text: "TabButton" }
53 }
54
55 Button {
56 id: addButton
57 text: "+"
58 flat: true
59 onClicked: {
60 tabBar.addItem(tabButton.createObject(tabBar))
61 console.log("added:", tabBar.itemAt(tabBar.count - 1))
62 }
63 }
64 }
65 \endcode
66
67 \section2 Managing the Current Index
68
69 When using multiple containers, such as \l TabBar and \l SwipeView, together,
70 their \l currentIndex properties can be bound to each other to keep them in
71 sync. When the user interacts with either container, its current index changes
72 automatically propagate to the other container.
73
74 Notice, however, that assigning a \c currentIndex value in JavaScript removes
75 the respective binding. In order to retain the bindings, use the following
76 methods to alter the current index:
77
78 \list
79 \li \l incrementCurrentIndex()
80 \li \l decrementCurrentIndex()
81 \li \l setCurrentIndex()
82 \endlist
83
84 \code
85 TabBar {
86 id: tabBar
87 currentIndex: swipeView.currentIndex
88 }
89
90 SwipeView {
91 id: swipeView
92 currentIndex: tabBar.currentIndex
93 }
94
95 Button {
96 text: qsTr("Home")
97 onClicked: swipeView.setCurrentIndex(0)
98 enabled: swipeView.currentIndex != 0
99 }
100
101 Button {
102 text: qsTr("Previous")
103 onClicked: swipeView.decrementCurrentIndex()
104 enabled: swipeView.currentIndex > 0
105 }
106
107 Button {
108 text: qsTr("Next")
109 onClicked: swipeView.incrementCurrentIndex()
110 enabled: swipeView.currentIndex < swipeView.count - 1
111 }
112 \endcode
113
114
115 \section2 Implementing Containers
116
117 Container does not provide any default visualization. It is used to implement
118 such containers as \l SwipeView and \l TabBar. When implementing a custom
119 container, the most important part of the API is \l contentModel, which provides
120 the contained items in a way that it can be used as an object model for item
121 views and repeaters.
122
123 \code
124 Container {
125 id: container
126
127 contentItem: ListView {
128 model: container.contentModel
129 snapMode: ListView.SnapOneItem
130 orientation: ListView.Horizontal
131 }
132
133 Text {
134 text: "Page 1"
135 width: container.width
136 height: container.height
137 }
138
139 Text {
140 text: "Page 2"
141 width: container.width
142 height: container.height
143 }
144 }
145 \endcode
146
147 Notice how the sizes of the page items are set by hand. This is because the
148 example uses a plain Container, which does not make any assumptions on the
149 visual layout. It is typically not necessary to specify sizes for items in
150 concrete Container implementations, such as \l SwipeView and \l TabBar.
151
152 \sa {Container Controls}
153*/
154
155static QQuickItem *effectiveContentItem(QQuickItem *item)
156{
157 QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(object: item);
158 if (flickable)
159 return flickable->contentItem();
160 return item;
161}
162
163void QQuickContainerPrivate::init()
164{
165 Q_Q(QQuickContainer);
166 contentModel = new QQmlObjectModel(q);
167 QObject::connect(sender: contentModel, signal: &QQmlObjectModel::countChanged, context: q, slot: &QQuickContainer::countChanged);
168 QObject::connect(sender: contentModel, signal: &QQmlObjectModel::childrenChanged, context: q, slot: &QQuickContainer::contentChildrenChanged);
169 connect(sender: q, signal: &QQuickControl::implicitContentWidthChanged, receiverPrivate: this, slot: &QQuickContainerPrivate::updateContentWidth);
170 connect(sender: q, signal: &QQuickControl::implicitContentHeightChanged, receiverPrivate: this, slot: &QQuickContainerPrivate::updateContentHeight);
171 setSizePolicy(horizontalPolicy: QLayoutPolicy::Preferred, verticalPolicy: QLayoutPolicy::Preferred);
172}
173
174void QQuickContainerPrivate::cleanup()
175{
176 Q_Q(QQuickContainer);
177 // ensure correct destruction order (QTBUG-46798)
178 const int count = contentModel->count();
179 for (int i = 0; i < count; ++i) {
180 QQuickItem *item = itemAt(index: i);
181 if (item)
182 QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: changeTypes);
183 }
184
185 if (contentItem) {
186 QQuickItem *focusItem = QQuickItemPrivate::get(item: contentItem)->subFocusItem;
187 if (focusItem && window)
188 deliveryAgentPrivate()->clearFocusInScope(scope: contentItem, item: focusItem, reason: Qt::OtherFocusReason);
189
190 q->contentItemChange(newItem: nullptr, oldItem: contentItem);
191 QQuickControlPrivate::hideOldItem(item: contentItem);
192 }
193
194 QObject::disconnect(sender: contentModel, signal: &QQmlObjectModel::countChanged, receiver: q, slot: &QQuickContainer::countChanged);
195 QObject::disconnect(sender: contentModel, signal: &QQmlObjectModel::childrenChanged, receiver: q, slot: &QQuickContainer::contentChildrenChanged);
196 delete contentModel;
197 contentModel = nullptr;
198}
199
200QQuickItem *QQuickContainerPrivate::itemAt(int index) const
201{
202 return qobject_cast<QQuickItem *>(o: contentModel->get(index));
203}
204
205void QQuickContainerPrivate::insertItem(int index, QQuickItem *item)
206{
207 Q_Q(QQuickContainer);
208 if (!q->isContent(item))
209 return;
210 contentData.append(t: item);
211
212 updatingCurrent = true;
213
214 item->setParentItem(effectiveContentItem(item: q->contentItem()));
215 maybeCullItem(item);
216 QQuickItemPrivate::get(item)->addItemChangeListener(listener: this, types: changeTypes);
217 contentModel->insert(index, object: item);
218
219 q->itemAdded(index, item);
220
221 int count = contentModel->count();
222 for (int i = index + 1; i < count; ++i)
223 q->itemMoved(index: i, item: itemAt(index: i));
224
225 if (count == 1 && currentIndex == -1)
226 q->setCurrentIndex(index);
227
228 updatingCurrent = false;
229}
230
231void QQuickContainerPrivate::moveItem(int from, int to, QQuickItem *item)
232{
233 Q_Q(QQuickContainer);
234 int oldCurrent = currentIndex;
235 contentModel->move(from, to);
236
237 updatingCurrent = true;
238
239 q->itemMoved(index: to, item);
240
241 if (from < to) {
242 for (int i = from; i < to; ++i)
243 q->itemMoved(index: i, item: itemAt(index: i));
244 } else {
245 for (int i = from; i > to; --i)
246 q->itemMoved(index: i, item: itemAt(index: i));
247 }
248
249 if (from == oldCurrent)
250 q->setCurrentIndex(to);
251 else if (from < oldCurrent && to >= oldCurrent)
252 q->setCurrentIndex(oldCurrent - 1);
253 else if (from > oldCurrent && to <= oldCurrent)
254 q->setCurrentIndex(oldCurrent + 1);
255
256 updatingCurrent = false;
257}
258
259void QQuickContainerPrivate::removeItem(int index, QQuickItem *item)
260{
261 Q_Q(QQuickContainer);
262 const bool item_inDestructor = QQuickItemPrivate::get(item)->inDestructor;
263 if (!item_inDestructor && !q->isContent(item))
264 return;
265 contentData.removeOne(t: item);
266
267 updatingCurrent = true;
268
269 int count = contentModel->count();
270 bool currentChanged = false;
271 if (index == currentIndex && (index != 0 || count == 1)) {
272 q->setCurrentIndex(currentIndex - 1);
273 } else if (index < currentIndex) {
274 --currentIndex;
275 currentChanged = true;
276 }
277
278 if (!item_inDestructor) {
279 // already handled by ~QQuickItem
280 QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: changeTypes);
281 item->setParentItem(nullptr);
282 }
283 contentModel->remove(index);
284 --count;
285
286 q->itemRemoved(index, item);
287
288 for (int i = index; i < count; ++i)
289 q->itemMoved(index: i, item: itemAt(index: i));
290
291 if (currentChanged)
292 emit q->currentIndexChanged();
293
294 updatingCurrent = false;
295}
296
297void QQuickContainerPrivate::reorderItems()
298{
299 Q_Q(QQuickContainer);
300 if (!contentItem)
301 return;
302
303 // The item view eventually reparents all the items of the content model
304 // from the container. At this stage (during component complete), however, due
305 // to optimisation strategies in the item views, this doesn't happen when the
306 // visible area of the item view is less than the total content width of the items
307 // within the content model. This can cause issues while reordering. Thus, it's
308 // better to skip reordering the item within the content model once it's known
309 // that it will be reparented to the item views.
310 bool allowReorder = true;
311 if (!qobject_cast<QQuickFlickable *>(object: contentItem)) {
312 for (int index = 0; index < contentModel->count(); index++) {
313 if (const auto *item = qobject_cast<QQuickItem *>(o: contentModel->get(index))) {
314 const auto *parentItem = item->parentItem();
315 if (parentItem && !qobject_cast<QQuickItemView *>(object: parentItem->parentItem())) {
316 allowReorder = false;
317 break;
318 }
319 }
320 }
321 }
322
323 if (allowReorder) {
324 QList<QQuickItem *> siblings = effectiveContentItem(item: contentItem)->childItems();
325 int to = 0;
326 for (int i = 0; i < siblings.size(); ++i) {
327 QQuickItem* sibling = siblings.at(i);
328 if (QQuickItemPrivate::get(item: sibling)->isTransparentForPositioner())
329 continue;
330 const int index = contentModel->indexOf(object: sibling, objectContext: nullptr);
331 if (index >= 0)
332 q->moveItem(from: index, to: to++);
333 }
334 }
335}
336
337void QQuickContainerPrivate::maybeCullItem(QQuickItem *item)
338{
339 if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
340 return;
341
342 // Items like Repeater don't control the visibility of the items they create,
343 // so we can't expected them to uncull items added dynamically. As mentioned
344 // below, Repeater _does_ uncull items added to it, but unlike e.g. ListView,
345 // it shouldn't care if its size becomes zero and so it shouldn't manage
346 // the culled state of items in the same way.
347 if (!qobject_cast<QQuickItemView *>(object: contentItem))
348 return;
349
350 // Only cull items if the contentItem has a zero size; otherwise let the
351 // contentItem manage it.
352 const bool hasZeroSize = qFuzzyIsNull(d: width) && qFuzzyIsNull(d: height);
353 if (!hasZeroSize)
354 return;
355
356 QQuickItemPrivate::get(item)->setCulled(true);
357}
358
359void QQuickContainerPrivate::maybeCullItems()
360{
361 if (!contentItem)
362 return;
363
364 const QList<QQuickItem *> childItems = effectiveContentItem(item: contentItem)->childItems();
365 for (auto &childItem : childItems)
366 maybeCullItem(item: childItem);
367}
368
369void QQuickContainerPrivate::_q_currentIndexChanged()
370{
371 Q_Q(QQuickContainer);
372 if (!updatingCurrent)
373 q->setCurrentIndex(contentItem ? contentItem->property(name: "currentIndex").toInt() : -1);
374}
375
376void QQuickContainerPrivate::itemChildAdded(QQuickItem *, QQuickItem *child)
377{
378 // add dynamically reparented items (eg. by a Repeater)
379 if (!QQuickItemPrivate::get(item: child)->isTransparentForPositioner() && !contentData.contains(t: child))
380 insertItem(index: contentModel->count(), item: child);
381}
382
383void QQuickContainerPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
384{
385 // remove dynamically unparented items (eg. by a Repeater)
386 if (!parent)
387 removeItem(index: contentModel->indexOf(object: item, objectContext: nullptr), item);
388}
389
390void QQuickContainerPrivate::itemSiblingOrderChanged(QQuickItem *)
391{
392 if (!componentComplete)
393 return;
394
395 // reorder the restacked items (eg. by a Repeater)
396 reorderItems();
397}
398
399void QQuickContainerPrivate::itemDestroyed(QQuickItem *item)
400{
401 int index = contentModel->indexOf(object: item, objectContext: nullptr);
402 if (index != -1)
403 removeItem(index, item);
404 else
405 QQuickControlPrivate::itemDestroyed(item);
406}
407
408void QQuickContainerPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
409{
410 QQuickContainer *q = static_cast<QQuickContainer *>(prop->object);
411 QQuickContainerPrivate *p = QQuickContainerPrivate::get(container: q);
412 QQuickItem *item = qobject_cast<QQuickItem *>(o: obj);
413 if (item) {
414 if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
415 item->setParentItem(effectiveContentItem(item: q->contentItem()));
416 else if (p->contentModel->indexOf(object: item, objectContext: nullptr) == -1)
417 q->addItem(item);
418 } else {
419 p->contentData.append(t: obj);
420 }
421}
422
423qsizetype QQuickContainerPrivate::contentData_count(QQmlListProperty<QObject> *prop)
424{
425 QQuickContainer *q = static_cast<QQuickContainer *>(prop->object);
426 return QQuickContainerPrivate::get(container: q)->contentData.size();
427}
428
429QObject *QQuickContainerPrivate::contentData_at(QQmlListProperty<QObject> *prop, qsizetype index)
430{
431 QQuickContainer *q = static_cast<QQuickContainer *>(prop->object);
432 return QQuickContainerPrivate::get(container: q)->contentData.value(i: index);
433}
434
435void QQuickContainerPrivate::contentData_clear(QQmlListProperty<QObject> *prop)
436{
437 QQuickContainer *q = static_cast<QQuickContainer *>(prop->object);
438 return QQuickContainerPrivate::get(container: q)->contentData.clear();
439}
440
441void QQuickContainerPrivate::contentChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *item)
442{
443 QQuickContainer *q = static_cast<QQuickContainer *>(prop->object);
444 q->addItem(item);
445}
446
447qsizetype QQuickContainerPrivate::contentChildren_count(QQmlListProperty<QQuickItem> *prop)
448{
449 QQuickContainer *q = static_cast<QQuickContainer *>(prop->object);
450 return QQuickContainerPrivate::get(container: q)->contentModel->count();
451}
452
453QQuickItem *QQuickContainerPrivate::contentChildren_at(QQmlListProperty<QQuickItem> *prop, qsizetype index)
454{
455 QQuickContainer *q = static_cast<QQuickContainer *>(prop->object);
456 return q->itemAt(index);
457}
458
459void QQuickContainerPrivate::contentChildren_clear(QQmlListProperty<QQuickItem> *prop)
460{
461 QQuickContainer *q = static_cast<QQuickContainer *>(prop->object);
462 return QQuickContainerPrivate::get(container: q)->contentModel->clear();
463}
464
465void QQuickContainerPrivate::updateContentWidth()
466{
467 Q_Q(QQuickContainer);
468 if (hasContentWidth || qFuzzyCompare(p1: contentWidth, p2: implicitContentWidth) || !contentModel)
469 return;
470
471 contentWidth = implicitContentWidth;
472 emit q->contentWidthChanged();
473}
474
475void QQuickContainerPrivate::updateContentHeight()
476{
477 Q_Q(QQuickContainer);
478 if (hasContentHeight || qFuzzyCompare(p1: contentHeight, p2: implicitContentHeight) || !contentModel)
479 return;
480
481 contentHeight = implicitContentHeight;
482 emit q->contentHeightChanged();
483}
484
485QQuickContainer::QQuickContainer(QQuickItem *parent)
486 : QQuickControl(*(new QQuickContainerPrivate), parent)
487{
488 Q_D(QQuickContainer);
489 d->init();
490}
491
492QQuickContainer::QQuickContainer(QQuickContainerPrivate &dd, QQuickItem *parent)
493 : QQuickControl(dd, parent)
494{
495 Q_D(QQuickContainer);
496 d->init();
497}
498
499QQuickContainer::~QQuickContainer()
500{
501 Q_D(QQuickContainer);
502 d->cleanup();
503}
504
505/*!
506 \qmlproperty int QtQuick.Controls::Container::count
507 \readonly
508
509 This property holds the number of items.
510*/
511int QQuickContainer::count() const
512{
513 Q_D(const QQuickContainer);
514 return d->contentModel->count();
515}
516
517/*!
518 \qmlmethod Item QtQuick.Controls::Container::itemAt(int index)
519
520 Returns the item at \a index, or \c null if it does not exist.
521*/
522QQuickItem *QQuickContainer::itemAt(int index) const
523{
524 Q_D(const QQuickContainer);
525 return d->itemAt(index);
526}
527
528/*!
529 \qmlmethod void QtQuick.Controls::Container::addItem(Item item)
530
531 Adds an \a item.
532*/
533void QQuickContainer::addItem(QQuickItem *item)
534{
535 Q_D(QQuickContainer);
536 insertItem(index: d->contentModel->count(), item);
537}
538
539/*!
540 \qmlmethod void QtQuick.Controls::Container::insertItem(int index, Item item)
541
542 Inserts an \a item at \a index.
543*/
544void QQuickContainer::insertItem(int index, QQuickItem *item)
545{
546 Q_D(QQuickContainer);
547 if (!item)
548 return;
549 const int count = d->contentModel->count();
550 if (index < 0 || index > count)
551 index = count;
552
553 int oldIndex = d->contentModel->indexOf(object: item, objectContext: nullptr);
554 if (oldIndex != -1) {
555 if (oldIndex < index)
556 --index;
557 if (oldIndex != index)
558 d->moveItem(from: oldIndex, to: index, item);
559 } else {
560 d->insertItem(index, item);
561 }
562}
563
564/*!
565 \qmlmethod void QtQuick.Controls::Container::moveItem(int from, int to)
566
567 Moves an item \a from one index \a to another.
568*/
569void QQuickContainer::moveItem(int from, int to)
570{
571 Q_D(QQuickContainer);
572 const int count = d->contentModel->count();
573 if (from < 0 || from > count - 1)
574 return;
575 if (to < 0 || to > count - 1)
576 to = count - 1;
577
578 if (from != to)
579 d->moveItem(from, to, item: d->itemAt(index: from));
580}
581
582/*!
583 \since QtQuick.Controls 2.3 (Qt 5.10)
584 \qmlmethod void QtQuick.Controls::Container::removeItem(Item item)
585
586 Removes and destroys the specified \a item.
587*/
588void QQuickContainer::removeItem(QQuickItem *item)
589{
590 Q_D(QQuickContainer);
591 if (!item)
592 return;
593
594 const int index = d->contentModel->indexOf(object: item, objectContext: nullptr);
595 if (index == -1)
596 return;
597
598 d->removeItem(index, item);
599 item->deleteLater();
600}
601
602/*!
603 \since QtQuick.Controls 2.3 (Qt 5.10)
604 \qmlmethod Item QtQuick.Controls::Container::takeItem(int index)
605
606 Removes and returns the item at \a index.
607
608 \note The ownership of the item is transferred to the caller.
609*/
610QQuickItem *QQuickContainer::takeItem(int index)
611{
612 Q_D(QQuickContainer);
613 const int count = d->contentModel->count();
614 if (index < 0 || index >= count)
615 return nullptr;
616
617 QQuickItem *item = itemAt(index);
618 if (item)
619 d->removeItem(index, item);
620 return item;
621}
622
623/*!
624 \qmlproperty model QtQuick.Controls::Container::contentModel
625 \readonly
626
627 This property holds the content model of items.
628
629 The content model is provided for visualization purposes. It can be assigned
630 as a model to a content item that presents the contents of the container.
631
632 \code
633 Container {
634 id: container
635 contentItem: ListView {
636 model: container.contentModel
637 }
638 }
639 \endcode
640
641 \sa contentData, contentChildren
642*/
643QVariant QQuickContainer::contentModel() const
644{
645 Q_D(const QQuickContainer);
646 return QVariant::fromValue(value: d->contentModel);
647}
648
649/*!
650 \qmlproperty list<QtObject> QtQuick.Controls::Container::contentData
651 \qmldefault
652
653 This property holds the list of content data.
654
655 The list contains all objects that have been declared in QML as children
656 of the container, and also items that have been dynamically added or
657 inserted using the \l addItem() and \l insertItem() methods, respectively.
658
659 \note Unlike \c contentChildren, \c contentData does include non-visual QML
660 objects. It is not re-ordered when items are inserted or moved.
661
662 \sa Item::data, contentChildren
663*/
664QQmlListProperty<QObject> QQuickContainer::contentData()
665{
666 Q_D(QQuickContainer);
667 if (!d->contentItem)
668 d->executeContentItem();
669 return QQmlListProperty<QObject>(this, nullptr,
670 QQuickContainerPrivate::contentData_append,
671 QQuickContainerPrivate::contentData_count,
672 QQuickContainerPrivate::contentData_at,
673 QQuickContainerPrivate::contentData_clear);
674}
675
676/*!
677 \qmlproperty list<Item> QtQuick.Controls::Container::contentChildren
678
679 This property holds the list of content children.
680
681 The list contains all items that have been declared in QML as children
682 of the container, and also items that have been dynamically added or
683 inserted using the \l addItem() and \l insertItem() methods, respectively.
684
685 \note Unlike \c contentData, \c contentChildren does not include non-visual
686 QML objects. It is re-ordered when items are inserted or moved.
687
688 \sa Item::children, contentData
689*/
690QQmlListProperty<QQuickItem> QQuickContainer::contentChildren()
691{
692 return QQmlListProperty<QQuickItem>(this, nullptr,
693 QQuickContainerPrivate::contentChildren_append,
694 QQuickContainerPrivate::contentChildren_count,
695 QQuickContainerPrivate::contentChildren_at,
696 QQuickContainerPrivate::contentChildren_clear);
697}
698
699/*!
700 \qmlproperty int QtQuick.Controls::Container::currentIndex
701
702 This property holds the index of the current item.
703
704 \sa currentItem, {Managing the Current Index}
705*/
706int QQuickContainer::currentIndex() const
707{
708 Q_D(const QQuickContainer);
709 return d->currentIndex;
710}
711
712/*!
713 \qmlmethod void QtQuick.Controls::Container::setCurrentIndex(int index)
714
715 Sets the current \a index of the container.
716
717 This method can be called to set a specific current index without breaking
718 existing \c currentIndex bindings.
719
720 \sa currentIndex, {Managing the Current Index}
721*/
722void QQuickContainer::setCurrentIndex(int index)
723{
724 Q_D(QQuickContainer);
725 if (d->currentIndex == index)
726 return;
727
728 d->currentIndex = index;
729 emit currentIndexChanged();
730 emit currentItemChanged();
731}
732
733/*!
734 \qmlmethod void QtQuick.Controls::Container::incrementCurrentIndex()
735 \since QtQuick.Controls 2.1 (Qt 5.8)
736
737 Increments the current index of the container.
738
739 This method can be called to alter the current index without breaking
740 existing \c currentIndex bindings.
741
742 \sa currentIndex, {Managing the Current Index}
743*/
744void QQuickContainer::incrementCurrentIndex()
745{
746 Q_D(QQuickContainer);
747 if (d->currentIndex < count() - 1)
748 setCurrentIndex(d->currentIndex + 1);
749}
750
751/*!
752 \qmlmethod void QtQuick.Controls::Container::decrementCurrentIndex()
753 \since QtQuick.Controls 2.1 (Qt 5.8)
754
755 Decrements the current index of the container.
756
757 This method can be called to alter the current index without breaking
758 existing \c currentIndex bindings.
759
760 \sa currentIndex, {Managing the Current Index}
761*/
762void QQuickContainer::decrementCurrentIndex()
763{
764 Q_D(QQuickContainer);
765 if (d->currentIndex > 0)
766 setCurrentIndex(d->currentIndex - 1);
767}
768
769/*!
770 \qmlproperty Item QtQuick.Controls::Container::currentItem
771 \readonly
772
773 This property holds the current item.
774
775 \sa currentIndex
776*/
777QQuickItem *QQuickContainer::currentItem() const
778{
779 Q_D(const QQuickContainer);
780 return itemAt(index: d->currentIndex);
781}
782
783/*!
784 \since QtQuick.Controls 2.5 (Qt 5.12)
785 \qmlproperty real QtQuick.Controls::Container::contentWidth
786
787 This property holds the content width. It is used for calculating the total
788 implicit width of the container.
789
790 Unless explicitly overridden, the content width is automatically calculated
791 based on the implicit width of the items in the container.
792
793 \sa contentHeight
794*/
795qreal QQuickContainer::contentWidth() const
796{
797 Q_D(const QQuickContainer);
798 return d->contentWidth;
799}
800
801void QQuickContainer::setContentWidth(qreal width)
802{
803 Q_D(QQuickContainer);
804 d->hasContentWidth = true;
805 if (qFuzzyCompare(p1: d->contentWidth, p2: width))
806 return;
807
808 d->contentWidth = width;
809 d->resizeContent();
810 d->updateImplicitContentWidth();
811 emit contentWidthChanged();
812}
813
814void QQuickContainer::resetContentWidth()
815{
816 Q_D(QQuickContainer);
817 if (!d->hasContentWidth)
818 return;
819
820 d->hasContentWidth = false;
821 d->updateImplicitContentWidth();
822}
823
824/*!
825 \since QtQuick.Controls 2.5 (Qt 5.12)
826 \qmlproperty real QtQuick.Controls::Container::contentHeight
827
828 This property holds the content height. It is used for calculating the total
829 implicit height of the container.
830
831 Unless explicitly overridden, the content height is automatically calculated
832 based on the implicit height of the items in the container.
833
834 \sa contentWidth
835*/
836qreal QQuickContainer::contentHeight() const
837{
838 Q_D(const QQuickContainer);
839 return d->contentHeight;
840}
841
842void QQuickContainer::setContentHeight(qreal height)
843{
844 Q_D(QQuickContainer);
845 d->hasContentHeight = true;
846 if (qFuzzyCompare(p1: d->contentHeight, p2: height))
847 return;
848
849 d->contentHeight = height;
850 d->resizeContent();
851 d->updateImplicitContentHeight();
852 emit contentHeightChanged();
853}
854
855void QQuickContainer::resetContentHeight()
856{
857 Q_D(QQuickContainer);
858 if (!d->hasContentHeight)
859 return;
860
861 d->hasContentHeight = false;
862 d->updateImplicitContentHeight();
863}
864
865qreal QQuickContainerPrivate::getContentWidth() const
866{
867 if (hasContentWidth)
868 return contentWidth;
869
870 return QQuickControlPrivate::getContentWidth();
871}
872
873qreal QQuickContainerPrivate::getContentHeight() const
874{
875 if (hasContentHeight)
876 return contentHeight;
877
878 return QQuickControlPrivate::getContentHeight();
879}
880
881void QQuickContainer::componentComplete()
882{
883 Q_D(QQuickContainer);
884 QQuickControl::componentComplete();
885 d->reorderItems();
886 d->maybeCullItems();
887}
888
889void QQuickContainer::itemChange(ItemChange change, const ItemChangeData &data)
890{
891 Q_D(QQuickContainer);
892 QQuickControl::itemChange(change, value: data);
893 if (change == QQuickItem::ItemChildAddedChange && isComponentComplete() && data.item != d->background && data.item != d->contentItem) {
894 if (!QQuickItemPrivate::get(item: data.item)->isTransparentForPositioner() && d->contentModel->indexOf(object: data.item, objectContext: nullptr) == -1)
895 addItem(item: data.item);
896 }
897}
898
899void QQuickContainer::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
900{
901 Q_D(QQuickContainer);
902 QQuickControl::contentItemChange(newItem, oldItem);
903
904 static const int slotIndex = metaObject()->indexOfSlot(slot: "_q_currentIndexChanged()");
905
906 if (oldItem) {
907 QQuickItemPrivate::get(item: oldItem)->removeItemChangeListener(d, types: QQuickItemPrivate::Children | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight);
908 QQuickItem *oldContentItem = effectiveContentItem(item: oldItem);
909 if (oldContentItem != oldItem)
910 QQuickItemPrivate::get(item: oldContentItem)->removeItemChangeListener(d, types: QQuickItemPrivate::Children);
911
912 int signalIndex = oldItem->metaObject()->indexOfSignal(signal: "currentIndexChanged()");
913 if (signalIndex != -1)
914 QMetaObject::disconnect(sender: oldItem, signal_index: signalIndex, receiver: this, method_index: slotIndex);
915 }
916
917 if (newItem) {
918 QQuickItemPrivate::get(item: newItem)->addItemChangeListener(listener: d, types: QQuickItemPrivate::Children | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight);
919 QQuickItem *newContentItem = effectiveContentItem(item: newItem);
920 if (newContentItem != newItem)
921 QQuickItemPrivate::get(item: newContentItem)->addItemChangeListener(listener: d, types: QQuickItemPrivate::Children);
922
923 int signalIndex = newItem->metaObject()->indexOfSignal(signal: "currentIndexChanged()");
924 if (signalIndex != -1)
925 QMetaObject::connect(sender: newItem, signal_index: signalIndex, receiver: this, method_index: slotIndex);
926 }
927}
928
929bool QQuickContainer::isContent(QQuickItem *item) const
930{
931 // If the item has a QML context associated to it (it was created in QML),
932 // we add it to the content model. Otherwise, it's probably the default
933 // highlight item that is always created by the item views, which we need
934 // to exclude.
935 //
936 // TODO: Find a better way to identify/exclude the highlight item...
937 return qmlContext(item);
938}
939
940void QQuickContainer::itemAdded(int index, QQuickItem *item)
941{
942 Q_UNUSED(index);
943 Q_UNUSED(item);
944}
945
946void QQuickContainer::itemMoved(int index, QQuickItem *item)
947{
948 Q_UNUSED(index);
949 Q_UNUSED(item);
950}
951
952void QQuickContainer::itemRemoved(int index, QQuickItem *item)
953{
954 Q_UNUSED(index);
955 Q_UNUSED(item);
956}
957
958QT_END_NAMESPACE
959
960#include "moc_qquickcontainer_p.cpp"
961

source code of qtdeclarative/src/quicktemplates/qquickcontainer.cpp