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 | |
10 | QT_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 | |
155 | static 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 | |
163 | void 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 | |
174 | void 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 | QQuickWindowPrivate::get(c: window)->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 | |
200 | QQuickItem *QQuickContainerPrivate::itemAt(int index) const |
201 | { |
202 | return qobject_cast<QQuickItem *>(o: contentModel->get(index)); |
203 | } |
204 | |
205 | void 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 | |
231 | void 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 | |
259 | void 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 | |
297 | void QQuickContainerPrivate::reorderItems() |
298 | { |
299 | Q_Q(QQuickContainer); |
300 | if (!contentItem) |
301 | return; |
302 | |
303 | QList<QQuickItem *> siblings = effectiveContentItem(item: contentItem)->childItems(); |
304 | |
305 | int to = 0; |
306 | for (int i = 0; i < siblings.size(); ++i) { |
307 | QQuickItem* sibling = siblings.at(i); |
308 | if (QQuickItemPrivate::get(item: sibling)->isTransparentForPositioner()) |
309 | continue; |
310 | int index = contentModel->indexOf(object: sibling, objectContext: nullptr); |
311 | q->moveItem(from: index, to: to++); |
312 | } |
313 | } |
314 | |
315 | void QQuickContainerPrivate::maybeCullItem(QQuickItem *item) |
316 | { |
317 | if (QQuickItemPrivate::get(item)->isTransparentForPositioner()) |
318 | return; |
319 | |
320 | // Items like Repeater don't control the visibility of the items they create, |
321 | // so we can't expected them to uncull items added dynamically. As mentioned |
322 | // below, Repeater _does_ uncull items added to it, but unlike e.g. ListView, |
323 | // it shouldn't care if its size becomes zero and so it shouldn't manage |
324 | // the culled state of items in the same way. |
325 | if (!qobject_cast<QQuickItemView *>(object: contentItem)) |
326 | return; |
327 | |
328 | // Only cull items if the contentItem has a zero size; otherwise let the |
329 | // contentItem manage it. |
330 | const bool hasZeroSize = qFuzzyIsNull(d: width) && qFuzzyIsNull(d: height); |
331 | if (!hasZeroSize) |
332 | return; |
333 | |
334 | QQuickItemPrivate::get(item)->setCulled(true); |
335 | } |
336 | |
337 | void QQuickContainerPrivate::maybeCullItems() |
338 | { |
339 | if (!contentItem) |
340 | return; |
341 | |
342 | const QList<QQuickItem *> childItems = effectiveContentItem(item: contentItem)->childItems(); |
343 | for (auto &childItem : childItems) |
344 | maybeCullItem(item: childItem); |
345 | } |
346 | |
347 | void QQuickContainerPrivate::_q_currentIndexChanged() |
348 | { |
349 | Q_Q(QQuickContainer); |
350 | if (!updatingCurrent) |
351 | q->setCurrentIndex(contentItem ? contentItem->property(name: "currentIndex").toInt() : -1); |
352 | } |
353 | |
354 | void QQuickContainerPrivate::itemChildAdded(QQuickItem *, QQuickItem *child) |
355 | { |
356 | // add dynamically reparented items (eg. by a Repeater) |
357 | if (!QQuickItemPrivate::get(item: child)->isTransparentForPositioner() && !contentData.contains(t: child)) |
358 | insertItem(index: contentModel->count(), item: child); |
359 | } |
360 | |
361 | void QQuickContainerPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent) |
362 | { |
363 | // remove dynamically unparented items (eg. by a Repeater) |
364 | if (!parent) |
365 | removeItem(index: contentModel->indexOf(object: item, objectContext: nullptr), item); |
366 | } |
367 | |
368 | void QQuickContainerPrivate::itemSiblingOrderChanged(QQuickItem *) |
369 | { |
370 | if (!componentComplete) |
371 | return; |
372 | |
373 | // reorder the restacked items (eg. by a Repeater) |
374 | reorderItems(); |
375 | } |
376 | |
377 | void QQuickContainerPrivate::itemDestroyed(QQuickItem *item) |
378 | { |
379 | int index = contentModel->indexOf(object: item, objectContext: nullptr); |
380 | if (index != -1) |
381 | removeItem(index, item); |
382 | else |
383 | QQuickControlPrivate::itemDestroyed(item); |
384 | } |
385 | |
386 | void QQuickContainerPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj) |
387 | { |
388 | QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); |
389 | QQuickContainerPrivate *p = QQuickContainerPrivate::get(container: q); |
390 | QQuickItem *item = qobject_cast<QQuickItem *>(o: obj); |
391 | if (item) { |
392 | if (QQuickItemPrivate::get(item)->isTransparentForPositioner()) |
393 | item->setParentItem(effectiveContentItem(item: q->contentItem())); |
394 | else if (p->contentModel->indexOf(object: item, objectContext: nullptr) == -1) |
395 | q->addItem(item); |
396 | } else { |
397 | p->contentData.append(t: obj); |
398 | } |
399 | } |
400 | |
401 | qsizetype QQuickContainerPrivate::contentData_count(QQmlListProperty<QObject> *prop) |
402 | { |
403 | QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); |
404 | return QQuickContainerPrivate::get(container: q)->contentData.size(); |
405 | } |
406 | |
407 | QObject *QQuickContainerPrivate::contentData_at(QQmlListProperty<QObject> *prop, qsizetype index) |
408 | { |
409 | QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); |
410 | return QQuickContainerPrivate::get(container: q)->contentData.value(i: index); |
411 | } |
412 | |
413 | void QQuickContainerPrivate::contentData_clear(QQmlListProperty<QObject> *prop) |
414 | { |
415 | QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); |
416 | return QQuickContainerPrivate::get(container: q)->contentData.clear(); |
417 | } |
418 | |
419 | void QQuickContainerPrivate::contentChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *item) |
420 | { |
421 | QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); |
422 | q->addItem(item); |
423 | } |
424 | |
425 | qsizetype QQuickContainerPrivate::contentChildren_count(QQmlListProperty<QQuickItem> *prop) |
426 | { |
427 | QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); |
428 | return QQuickContainerPrivate::get(container: q)->contentModel->count(); |
429 | } |
430 | |
431 | QQuickItem *QQuickContainerPrivate::contentChildren_at(QQmlListProperty<QQuickItem> *prop, qsizetype index) |
432 | { |
433 | QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); |
434 | return q->itemAt(index); |
435 | } |
436 | |
437 | void QQuickContainerPrivate::contentChildren_clear(QQmlListProperty<QQuickItem> *prop) |
438 | { |
439 | QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); |
440 | return QQuickContainerPrivate::get(container: q)->contentModel->clear(); |
441 | } |
442 | |
443 | void QQuickContainerPrivate::updateContentWidth() |
444 | { |
445 | Q_Q(QQuickContainer); |
446 | if (hasContentWidth || qFuzzyCompare(p1: contentWidth, p2: implicitContentWidth) || !contentModel) |
447 | return; |
448 | |
449 | contentWidth = implicitContentWidth; |
450 | emit q->contentWidthChanged(); |
451 | } |
452 | |
453 | void QQuickContainerPrivate::updateContentHeight() |
454 | { |
455 | Q_Q(QQuickContainer); |
456 | if (hasContentHeight || qFuzzyCompare(p1: contentHeight, p2: implicitContentHeight) || !contentModel) |
457 | return; |
458 | |
459 | contentHeight = implicitContentHeight; |
460 | emit q->contentHeightChanged(); |
461 | } |
462 | |
463 | QQuickContainer::QQuickContainer(QQuickItem *parent) |
464 | : QQuickControl(*(new QQuickContainerPrivate), parent) |
465 | { |
466 | Q_D(QQuickContainer); |
467 | d->init(); |
468 | } |
469 | |
470 | QQuickContainer::QQuickContainer(QQuickContainerPrivate &dd, QQuickItem *parent) |
471 | : QQuickControl(dd, parent) |
472 | { |
473 | Q_D(QQuickContainer); |
474 | d->init(); |
475 | } |
476 | |
477 | QQuickContainer::~QQuickContainer() |
478 | { |
479 | Q_D(QQuickContainer); |
480 | d->cleanup(); |
481 | } |
482 | |
483 | /*! |
484 | \qmlproperty int QtQuick.Controls::Container::count |
485 | \readonly |
486 | |
487 | This property holds the number of items. |
488 | */ |
489 | int QQuickContainer::count() const |
490 | { |
491 | Q_D(const QQuickContainer); |
492 | return d->contentModel->count(); |
493 | } |
494 | |
495 | /*! |
496 | \qmlmethod Item QtQuick.Controls::Container::itemAt(int index) |
497 | |
498 | Returns the item at \a index, or \c null if it does not exist. |
499 | */ |
500 | QQuickItem *QQuickContainer::itemAt(int index) const |
501 | { |
502 | Q_D(const QQuickContainer); |
503 | return d->itemAt(index); |
504 | } |
505 | |
506 | /*! |
507 | \qmlmethod void QtQuick.Controls::Container::addItem(Item item) |
508 | |
509 | Adds an \a item. |
510 | */ |
511 | void QQuickContainer::addItem(QQuickItem *item) |
512 | { |
513 | Q_D(QQuickContainer); |
514 | insertItem(index: d->contentModel->count(), item); |
515 | } |
516 | |
517 | /*! |
518 | \qmlmethod void QtQuick.Controls::Container::insertItem(int index, Item item) |
519 | |
520 | Inserts an \a item at \a index. |
521 | */ |
522 | void QQuickContainer::insertItem(int index, QQuickItem *item) |
523 | { |
524 | Q_D(QQuickContainer); |
525 | if (!item) |
526 | return; |
527 | const int count = d->contentModel->count(); |
528 | if (index < 0 || index > count) |
529 | index = count; |
530 | |
531 | int oldIndex = d->contentModel->indexOf(object: item, objectContext: nullptr); |
532 | if (oldIndex != -1) { |
533 | if (oldIndex < index) |
534 | --index; |
535 | if (oldIndex != index) |
536 | d->moveItem(from: oldIndex, to: index, item); |
537 | } else { |
538 | d->insertItem(index, item); |
539 | } |
540 | } |
541 | |
542 | /*! |
543 | \qmlmethod void QtQuick.Controls::Container::moveItem(int from, int to) |
544 | |
545 | Moves an item \a from one index \a to another. |
546 | */ |
547 | void QQuickContainer::moveItem(int from, int to) |
548 | { |
549 | Q_D(QQuickContainer); |
550 | const int count = d->contentModel->count(); |
551 | if (from < 0 || from > count - 1) |
552 | return; |
553 | if (to < 0 || to > count - 1) |
554 | to = count - 1; |
555 | |
556 | if (from != to) |
557 | d->moveItem(from, to, item: d->itemAt(index: from)); |
558 | } |
559 | |
560 | /*! |
561 | \since QtQuick.Controls 2.3 (Qt 5.10) |
562 | \qmlmethod void QtQuick.Controls::Container::removeItem(Item item) |
563 | |
564 | Removes and destroys the specified \a item. |
565 | */ |
566 | void QQuickContainer::removeItem(QQuickItem *item) |
567 | { |
568 | Q_D(QQuickContainer); |
569 | if (!item) |
570 | return; |
571 | |
572 | const int index = d->contentModel->indexOf(object: item, objectContext: nullptr); |
573 | if (index == -1) |
574 | return; |
575 | |
576 | d->removeItem(index, item); |
577 | item->deleteLater(); |
578 | } |
579 | |
580 | /*! |
581 | \since QtQuick.Controls 2.3 (Qt 5.10) |
582 | \qmlmethod Item QtQuick.Controls::Container::takeItem(int index) |
583 | |
584 | Removes and returns the item at \a index. |
585 | |
586 | \note The ownership of the item is transferred to the caller. |
587 | */ |
588 | QQuickItem *QQuickContainer::takeItem(int index) |
589 | { |
590 | Q_D(QQuickContainer); |
591 | const int count = d->contentModel->count(); |
592 | if (index < 0 || index >= count) |
593 | return nullptr; |
594 | |
595 | QQuickItem *item = itemAt(index); |
596 | if (item) |
597 | d->removeItem(index, item); |
598 | return item; |
599 | } |
600 | |
601 | /*! |
602 | \qmlproperty model QtQuick.Controls::Container::contentModel |
603 | \readonly |
604 | |
605 | This property holds the content model of items. |
606 | |
607 | The content model is provided for visualization purposes. It can be assigned |
608 | as a model to a content item that presents the contents of the container. |
609 | |
610 | \code |
611 | Container { |
612 | id: container |
613 | contentItem: ListView { |
614 | model: container.contentModel |
615 | } |
616 | } |
617 | \endcode |
618 | |
619 | \sa contentData, contentChildren |
620 | */ |
621 | QVariant QQuickContainer::contentModel() const |
622 | { |
623 | Q_D(const QQuickContainer); |
624 | return QVariant::fromValue(value: d->contentModel); |
625 | } |
626 | |
627 | /*! |
628 | \qmlproperty list<QtObject> QtQuick.Controls::Container::contentData |
629 | \qmldefault |
630 | |
631 | This property holds the list of content data. |
632 | |
633 | The list contains all objects that have been declared in QML as children |
634 | of the container, and also items that have been dynamically added or |
635 | inserted using the \l addItem() and \l insertItem() methods, respectively. |
636 | |
637 | \note Unlike \c contentChildren, \c contentData does include non-visual QML |
638 | objects. It is not re-ordered when items are inserted or moved. |
639 | |
640 | \sa Item::data, contentChildren |
641 | */ |
642 | QQmlListProperty<QObject> QQuickContainer::contentData() |
643 | { |
644 | Q_D(QQuickContainer); |
645 | if (!d->contentItem) |
646 | d->executeContentItem(); |
647 | return QQmlListProperty<QObject>(this, nullptr, |
648 | QQuickContainerPrivate::contentData_append, |
649 | QQuickContainerPrivate::contentData_count, |
650 | QQuickContainerPrivate::contentData_at, |
651 | QQuickContainerPrivate::contentData_clear); |
652 | } |
653 | |
654 | /*! |
655 | \qmlproperty list<Item> QtQuick.Controls::Container::contentChildren |
656 | |
657 | This property holds the list of content children. |
658 | |
659 | The list contains all items that have been declared in QML as children |
660 | of the container, and also items that have been dynamically added or |
661 | inserted using the \l addItem() and \l insertItem() methods, respectively. |
662 | |
663 | \note Unlike \c contentData, \c contentChildren does not include non-visual |
664 | QML objects. It is re-ordered when items are inserted or moved. |
665 | |
666 | \sa Item::children, contentData |
667 | */ |
668 | QQmlListProperty<QQuickItem> QQuickContainer::contentChildren() |
669 | { |
670 | return QQmlListProperty<QQuickItem>(this, nullptr, |
671 | QQuickContainerPrivate::contentChildren_append, |
672 | QQuickContainerPrivate::contentChildren_count, |
673 | QQuickContainerPrivate::contentChildren_at, |
674 | QQuickContainerPrivate::contentChildren_clear); |
675 | } |
676 | |
677 | /*! |
678 | \qmlproperty int QtQuick.Controls::Container::currentIndex |
679 | |
680 | This property holds the index of the current item. |
681 | |
682 | \sa currentItem, {Managing the Current Index} |
683 | */ |
684 | int QQuickContainer::currentIndex() const |
685 | { |
686 | Q_D(const QQuickContainer); |
687 | return d->currentIndex; |
688 | } |
689 | |
690 | /*! |
691 | \qmlmethod void QtQuick.Controls::Container::setCurrentIndex(int index) |
692 | |
693 | Sets the current \a index of the container. |
694 | |
695 | This method can be called to set a specific current index without breaking |
696 | existing \c currentIndex bindings. |
697 | |
698 | \sa currentIndex, {Managing the Current Index} |
699 | */ |
700 | void QQuickContainer::setCurrentIndex(int index) |
701 | { |
702 | Q_D(QQuickContainer); |
703 | if (d->currentIndex == index) |
704 | return; |
705 | |
706 | d->currentIndex = index; |
707 | emit currentIndexChanged(); |
708 | emit currentItemChanged(); |
709 | } |
710 | |
711 | /*! |
712 | \qmlmethod void QtQuick.Controls::Container::incrementCurrentIndex() |
713 | \since QtQuick.Controls 2.1 (Qt 5.8) |
714 | |
715 | Increments the current index of the container. |
716 | |
717 | This method can be called to alter the current index without breaking |
718 | existing \c currentIndex bindings. |
719 | |
720 | \sa currentIndex, {Managing the Current Index} |
721 | */ |
722 | void QQuickContainer::incrementCurrentIndex() |
723 | { |
724 | Q_D(QQuickContainer); |
725 | if (d->currentIndex < count() - 1) |
726 | setCurrentIndex(d->currentIndex + 1); |
727 | } |
728 | |
729 | /*! |
730 | \qmlmethod void QtQuick.Controls::Container::decrementCurrentIndex() |
731 | \since QtQuick.Controls 2.1 (Qt 5.8) |
732 | |
733 | Decrements the current index of the container. |
734 | |
735 | This method can be called to alter the current index without breaking |
736 | existing \c currentIndex bindings. |
737 | |
738 | \sa currentIndex, {Managing the Current Index} |
739 | */ |
740 | void QQuickContainer::decrementCurrentIndex() |
741 | { |
742 | Q_D(QQuickContainer); |
743 | if (d->currentIndex > 0) |
744 | setCurrentIndex(d->currentIndex - 1); |
745 | } |
746 | |
747 | /*! |
748 | \qmlproperty Item QtQuick.Controls::Container::currentItem |
749 | \readonly |
750 | |
751 | This property holds the current item. |
752 | |
753 | \sa currentIndex |
754 | */ |
755 | QQuickItem *QQuickContainer::currentItem() const |
756 | { |
757 | Q_D(const QQuickContainer); |
758 | return itemAt(index: d->currentIndex); |
759 | } |
760 | |
761 | /*! |
762 | \since QtQuick.Controls 2.5 (Qt 5.12) |
763 | \qmlproperty real QtQuick.Controls::Container::contentWidth |
764 | |
765 | This property holds the content width. It is used for calculating the total |
766 | implicit width of the container. |
767 | |
768 | Unless explicitly overridden, the content width is automatically calculated |
769 | based on the implicit width of the items in the container. |
770 | |
771 | \sa contentHeight |
772 | */ |
773 | qreal QQuickContainer::contentWidth() const |
774 | { |
775 | Q_D(const QQuickContainer); |
776 | return d->contentWidth; |
777 | } |
778 | |
779 | void QQuickContainer::setContentWidth(qreal width) |
780 | { |
781 | Q_D(QQuickContainer); |
782 | d->hasContentWidth = true; |
783 | if (qFuzzyCompare(p1: d->contentWidth, p2: width)) |
784 | return; |
785 | |
786 | d->contentWidth = width; |
787 | d->resizeContent(); |
788 | emit contentWidthChanged(); |
789 | } |
790 | |
791 | void QQuickContainer::resetContentWidth() |
792 | { |
793 | Q_D(QQuickContainer); |
794 | if (!d->hasContentWidth) |
795 | return; |
796 | |
797 | d->hasContentWidth = false; |
798 | d->updateContentWidth(); |
799 | } |
800 | |
801 | /*! |
802 | \since QtQuick.Controls 2.5 (Qt 5.12) |
803 | \qmlproperty real QtQuick.Controls::Container::contentHeight |
804 | |
805 | This property holds the content height. It is used for calculating the total |
806 | implicit height of the container. |
807 | |
808 | Unless explicitly overridden, the content height is automatically calculated |
809 | based on the implicit height of the items in the container. |
810 | |
811 | \sa contentWidth |
812 | */ |
813 | qreal QQuickContainer::contentHeight() const |
814 | { |
815 | Q_D(const QQuickContainer); |
816 | return d->contentHeight; |
817 | } |
818 | |
819 | void QQuickContainer::setContentHeight(qreal height) |
820 | { |
821 | Q_D(QQuickContainer); |
822 | d->hasContentHeight = true; |
823 | if (qFuzzyCompare(p1: d->contentHeight, p2: height)) |
824 | return; |
825 | |
826 | d->contentHeight = height; |
827 | d->resizeContent(); |
828 | emit contentHeightChanged(); |
829 | } |
830 | |
831 | void QQuickContainer::resetContentHeight() |
832 | { |
833 | Q_D(QQuickContainer); |
834 | if (!d->hasContentHeight) |
835 | return; |
836 | |
837 | d->hasContentHeight = false; |
838 | d->updateContentHeight(); |
839 | } |
840 | |
841 | void QQuickContainer::componentComplete() |
842 | { |
843 | Q_D(QQuickContainer); |
844 | QQuickControl::componentComplete(); |
845 | d->reorderItems(); |
846 | d->maybeCullItems(); |
847 | } |
848 | |
849 | void QQuickContainer::itemChange(ItemChange change, const ItemChangeData &data) |
850 | { |
851 | Q_D(QQuickContainer); |
852 | QQuickControl::itemChange(change, value: data); |
853 | if (change == QQuickItem::ItemChildAddedChange && isComponentComplete() && data.item != d->background && data.item != d->contentItem) { |
854 | if (!QQuickItemPrivate::get(item: data.item)->isTransparentForPositioner() && d->contentModel->indexOf(object: data.item, objectContext: nullptr) == -1) |
855 | addItem(item: data.item); |
856 | } |
857 | } |
858 | |
859 | void QQuickContainer::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) |
860 | { |
861 | Q_D(QQuickContainer); |
862 | QQuickControl::contentItemChange(newItem, oldItem); |
863 | |
864 | static const int slotIndex = metaObject()->indexOfSlot(slot: "_q_currentIndexChanged()"); |
865 | |
866 | if (oldItem) { |
867 | QQuickItemPrivate::get(item: oldItem)->removeItemChangeListener(d, types: QQuickItemPrivate::Children | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); |
868 | QQuickItem *oldContentItem = effectiveContentItem(item: oldItem); |
869 | if (oldContentItem != oldItem) |
870 | QQuickItemPrivate::get(item: oldContentItem)->removeItemChangeListener(d, types: QQuickItemPrivate::Children); |
871 | |
872 | int signalIndex = oldItem->metaObject()->indexOfSignal(signal: "currentIndexChanged()"); |
873 | if (signalIndex != -1) |
874 | QMetaObject::disconnect(sender: oldItem, signal_index: signalIndex, receiver: this, method_index: slotIndex); |
875 | } |
876 | |
877 | if (newItem) { |
878 | QQuickItemPrivate::get(item: newItem)->addItemChangeListener(listener: d, types: QQuickItemPrivate::Children | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); |
879 | QQuickItem *newContentItem = effectiveContentItem(item: newItem); |
880 | if (newContentItem != newItem) |
881 | QQuickItemPrivate::get(item: newContentItem)->addItemChangeListener(listener: d, types: QQuickItemPrivate::Children); |
882 | |
883 | int signalIndex = newItem->metaObject()->indexOfSignal(signal: "currentIndexChanged()"); |
884 | if (signalIndex != -1) |
885 | QMetaObject::connect(sender: newItem, signal_index: signalIndex, receiver: this, method_index: slotIndex); |
886 | } |
887 | } |
888 | |
889 | bool QQuickContainer::isContent(QQuickItem *item) const |
890 | { |
891 | // If the item has a QML context associated to it (it was created in QML), |
892 | // we add it to the content model. Otherwise, it's probably the default |
893 | // highlight item that is always created by the item views, which we need |
894 | // to exclude. |
895 | // |
896 | // TODO: Find a better way to identify/exclude the highlight item... |
897 | return qmlContext(item); |
898 | } |
899 | |
900 | void QQuickContainer::itemAdded(int index, QQuickItem *item) |
901 | { |
902 | Q_UNUSED(index); |
903 | Q_UNUSED(item); |
904 | } |
905 | |
906 | void QQuickContainer::itemMoved(int index, QQuickItem *item) |
907 | { |
908 | Q_UNUSED(index); |
909 | Q_UNUSED(item); |
910 | } |
911 | |
912 | void QQuickContainer::itemRemoved(int index, QQuickItem *item) |
913 | { |
914 | Q_UNUSED(index); |
915 | Q_UNUSED(item); |
916 | } |
917 | |
918 | QT_END_NAMESPACE |
919 | |
920 | #include "moc_qquickcontainer_p.cpp" |
921 |
Definitions
- effectiveContentItem
- init
- cleanup
- itemAt
- insertItem
- moveItem
- removeItem
- reorderItems
- maybeCullItem
- maybeCullItems
- _q_currentIndexChanged
- itemChildAdded
- itemParentChanged
- itemSiblingOrderChanged
- itemDestroyed
- contentData_append
- contentData_count
- contentData_at
- contentData_clear
- contentChildren_append
- contentChildren_count
- contentChildren_at
- contentChildren_clear
- updateContentWidth
- updateContentHeight
- QQuickContainer
- QQuickContainer
- ~QQuickContainer
- count
- itemAt
- addItem
- insertItem
- moveItem
- removeItem
- takeItem
- contentModel
- contentData
- contentChildren
- currentIndex
- setCurrentIndex
- incrementCurrentIndex
- decrementCurrentIndex
- currentItem
- contentWidth
- setContentWidth
- resetContentWidth
- contentHeight
- setContentHeight
- resetContentHeight
- componentComplete
- itemChange
- contentItemChange
- isContent
- itemAdded
- itemMoved
Start learning QML with our Intro Training
Find out more