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