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