| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: http://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL3$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see http://www.qt.io/terms-conditions. For further |
| ** information use the contact form at http://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or later as published by the Free |
| ** Software Foundation and appearing in the file LICENSE.GPL included in |
| ** the packaging of this file. Please review the following information to |
| ** ensure the GNU General Public License version 2.0 requirements will be |
| ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qquickswipeview_p.h" |
| |
| #include <QtQml/qqmlinfo.h> |
| #include <QtQuickTemplates2/private/qquickcontainer_p_p.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| /*! |
| \qmltype SwipeView |
| \inherits Container |
| //! \instantiates QQuickSwipeView |
| \inqmlmodule QtQuick.Controls |
| \since 5.7 |
| \ingroup qtquickcontrols2-navigation |
| \ingroup qtquickcontrols2-containers |
| \ingroup qtquickcontrols2-focusscopes |
| \brief Enables the user to navigate pages by swiping sideways. |
| |
| SwipeView provides a swipe-based navigation model. |
| |
| \image qtquickcontrols2-swipeview.gif |
| |
| SwipeView is populated with a set of pages. One page is visible at a time. |
| The user can navigate between the pages by swiping sideways. Notice that |
| SwipeView itself is entirely non-visual. It is recommended to combine it |
| with PageIndicator, to give the user a visual clue that there are multiple |
| pages. |
| |
| \snippet qtquickcontrols2-swipeview-indicator.qml 1 |
| |
| As shown above, SwipeView is typically populated with a static set of |
| pages that are defined inline as children of the view. It is also possible |
| to \l {Container::addItem()}{add}, \l {Container::insertItem()}{insert}, |
| \l {Container::moveItem()}{move}, and \l {Container::removeItem()}{remove} |
| pages dynamically at run time. |
| |
| It is generally not advisable to add excessive amounts of pages to a |
| SwipeView. However, when the amount of pages grows larger, or individual |
| pages are relatively complex, it may be desirable to free up resources by |
| unloading pages that are outside the immediate reach of the user. |
| The following example presents how to use \l Loader to keep a maximum of |
| three pages simultaneously instantiated. |
| |
| \code |
| SwipeView { |
| Repeater { |
| model: 6 |
| Loader { |
| active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem |
| sourceComponent: Text { |
| text: index |
| Component.onCompleted: console.log("created:", index) |
| Component.onDestruction: console.log("destroyed:", index) |
| } |
| } |
| } |
| } |
| \endcode |
| |
| \note SwipeView takes over the geometry management of items added to the |
| view. Using anchors on the items is not supported, and any \c width |
| or \c height assignment will be overridden by the view. Notice that |
| this only applies to the root of the item. Specifying width and height, |
| or using anchors for its children works as expected. |
| |
| \sa TabBar, PageIndicator, {Customizing SwipeView}, {Navigation Controls}, {Container Controls}, |
| {Focus Management in Qt Quick Controls} |
| */ |
| |
| class QQuickSwipeViewPrivate : public QQuickContainerPrivate |
| { |
| Q_DECLARE_PUBLIC(QQuickSwipeView) |
| |
| public: |
| void resizeItem(QQuickItem *item); |
| void resizeItems(); |
| |
| static QQuickSwipeViewPrivate *get(QQuickSwipeView *view); |
| |
| void itemImplicitWidthChanged(QQuickItem *item) override; |
| void itemImplicitHeightChanged(QQuickItem *item) override; |
| |
| qreal getContentWidth() const override; |
| qreal getContentHeight() const override; |
| |
| bool interactive = true; |
| Qt::Orientation orientation = Qt::Horizontal; |
| }; |
| |
| class QQuickSwipeViewAttachedPrivate : public QObjectPrivate |
| { |
| Q_DECLARE_PUBLIC(QQuickSwipeViewAttached) |
| |
| public: |
| static QQuickSwipeViewAttachedPrivate *get(QQuickSwipeViewAttached *attached) |
| { |
| return attached->d_func(); |
| } |
| |
| void update(QQuickSwipeView *newView, int newIndex); |
| void updateCurrentIndex(); |
| void setCurrentIndex(int i); |
| |
| QQuickSwipeView *swipeView = nullptr; |
| int index = -1; |
| int currentIndex = -1; |
| }; |
| |
| void QQuickSwipeViewPrivate::resizeItems() |
| { |
| Q_Q(QQuickSwipeView); |
| const int count = q->count(); |
| for (int i = 0; i < count; ++i) { |
| QQuickItem *item = itemAt(i); |
| if (item) { |
| QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors; |
| // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors |
| if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) { |
| qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item."; |
| item->setProperty("_q_QQuickSwipeView_warned", true); |
| } |
| |
| if (orientation == Qt::Horizontal) |
| item->setY(0); |
| else |
| item->setX(0); |
| item->setSize(QSizeF(contentItem->width(), contentItem->height())); |
| } |
| } |
| } |
| |
| QQuickSwipeViewPrivate *QQuickSwipeViewPrivate::get(QQuickSwipeView *view) |
| { |
| return view->d_func(); |
| } |
| |
| void QQuickSwipeViewPrivate::itemImplicitWidthChanged(QQuickItem *item) |
| { |
| Q_Q(QQuickSwipeView); |
| QQuickContainerPrivate::itemImplicitWidthChanged(item); |
| if (item == q->currentItem()) |
| updateImplicitContentWidth(); |
| } |
| |
| void QQuickSwipeViewPrivate::itemImplicitHeightChanged(QQuickItem *item) |
| { |
| Q_Q(QQuickSwipeView); |
| QQuickContainerPrivate::itemImplicitHeightChanged(item); |
| if (item == q->currentItem()) |
| updateImplicitContentHeight(); |
| } |
| |
| qreal QQuickSwipeViewPrivate::getContentWidth() const |
| { |
| Q_Q(const QQuickSwipeView); |
| QQuickItem *currentItem = q->currentItem(); |
| return currentItem ? currentItem->implicitWidth() : 0; |
| } |
| |
| qreal QQuickSwipeViewPrivate::getContentHeight() const |
| { |
| Q_Q(const QQuickSwipeView); |
| QQuickItem *currentItem = q->currentItem(); |
| return currentItem ? currentItem->implicitHeight() : 0; |
| } |
| |
| QQuickSwipeView::QQuickSwipeView(QQuickItem *parent) |
| : QQuickContainer(*(new QQuickSwipeViewPrivate), parent) |
| { |
| Q_D(QQuickSwipeView); |
| d->changeTypes |= QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight; |
| setFlag(ItemIsFocusScope); |
| setActiveFocusOnTab(true); |
| QObjectPrivate::connect(this, &QQuickContainer::currentItemChanged, d, &QQuickControlPrivate::updateImplicitContentSize); |
| } |
| |
| /*! |
| \since QtQuick.Controls 2.1 (Qt 5.8) |
| \qmlproperty bool QtQuick.Controls::SwipeView::interactive |
| |
| This property describes whether the user can interact with the SwipeView. |
| The user cannot swipe a view that is not interactive. |
| |
| The default value is \c true. |
| */ |
| bool QQuickSwipeView::isInteractive() const |
| { |
| Q_D(const QQuickSwipeView); |
| return d->interactive; |
| } |
| |
| void QQuickSwipeView::setInteractive(bool interactive) |
| { |
| Q_D(QQuickSwipeView); |
| if (d->interactive == interactive) |
| return; |
| |
| d->interactive = interactive; |
| emit interactiveChanged(); |
| } |
| |
| /*! |
| \since QtQuick.Controls 2.2 (Qt 5.9) |
| \qmlproperty enumeration QtQuick.Controls::SwipeView::orientation |
| |
| This property holds the orientation. |
| |
| Possible values: |
| \value Qt.Horizontal Horizontal (default) |
| \value Qt.Vertical Vertical |
| |
| \sa horizontal, vertical |
| */ |
| Qt::Orientation QQuickSwipeView::orientation() const |
| { |
| Q_D(const QQuickSwipeView); |
| return d->orientation; |
| } |
| |
| void QQuickSwipeView::setOrientation(Qt::Orientation orientation) |
| { |
| Q_D(QQuickSwipeView); |
| if (d->orientation == orientation) |
| return; |
| |
| d->orientation = orientation; |
| if (isComponentComplete()) |
| d->resizeItems(); |
| emit orientationChanged(); |
| } |
| |
| /*! |
| \since QtQuick.Controls 2.3 (Qt 5.10) |
| \qmlproperty bool QtQuick.Controls::SwipeView::horizontal |
| \readonly |
| |
| This property holds whether the swipe view is horizontal. |
| |
| \sa orientation |
| */ |
| bool QQuickSwipeView::isHorizontal() const |
| { |
| Q_D(const QQuickSwipeView); |
| return d->orientation == Qt::Horizontal; |
| } |
| |
| /*! |
| \since QtQuick.Controls 2.3 (Qt 5.10) |
| \qmlproperty bool QtQuick.Controls::SwipeView::vertical |
| \readonly |
| |
| This property holds whether the swipe view is vertical. |
| |
| \sa orientation |
| */ |
| bool QQuickSwipeView::isVertical() const |
| { |
| Q_D(const QQuickSwipeView); |
| return d->orientation == Qt::Vertical; |
| } |
| |
| QQuickSwipeViewAttached *QQuickSwipeView::qmlAttachedProperties(QObject *object) |
| { |
| return new QQuickSwipeViewAttached(object); |
| } |
| |
| void QQuickSwipeView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
| { |
| Q_D(QQuickSwipeView); |
| QQuickContainer::geometryChanged(newGeometry, oldGeometry); |
| d->resizeItems(); |
| } |
| |
| void QQuickSwipeView::itemAdded(int index, QQuickItem *item) |
| { |
| Q_D(QQuickSwipeView); |
| QQuickItemPrivate::get(item)->setCulled(true); // QTBUG-51078, QTBUG-51669 |
| if (isComponentComplete()) |
| item->setSize(QSizeF(d->contentItem->width(), d->contentItem->height())); |
| QQuickSwipeViewAttached *attached = qobject_cast<QQuickSwipeViewAttached *>(qmlAttachedPropertiesObject<QQuickSwipeView>(item)); |
| if (attached) |
| QQuickSwipeViewAttachedPrivate::get(attached)->update(this, index); |
| } |
| |
| void QQuickSwipeView::itemMoved(int index, QQuickItem *item) |
| { |
| QQuickSwipeViewAttached *attached = qobject_cast<QQuickSwipeViewAttached *>(qmlAttachedPropertiesObject<QQuickSwipeView>(item)); |
| if (attached) |
| QQuickSwipeViewAttachedPrivate::get(attached)->update(this, index); |
| } |
| |
| void QQuickSwipeView::itemRemoved(int, QQuickItem *item) |
| { |
| QQuickSwipeViewAttached *attached = qobject_cast<QQuickSwipeViewAttached *>(qmlAttachedPropertiesObject<QQuickSwipeView>(item)); |
| if (attached) |
| QQuickSwipeViewAttachedPrivate::get(attached)->update(nullptr, -1); |
| } |
| |
| #if QT_CONFIG(accessibility) |
| QAccessible::Role QQuickSwipeView::accessibleRole() const |
| { |
| return QAccessible::PageTabList; |
| } |
| #endif |
| |
| /*! |
| \qmlattachedproperty int QtQuick.Controls::SwipeView::index |
| \readonly |
| |
| This attached property holds the index of each child item in the SwipeView. |
| |
| It is attached to each child item of the SwipeView. |
| */ |
| |
| /*! |
| \qmlattachedproperty bool QtQuick.Controls::SwipeView::isCurrentItem |
| \readonly |
| |
| This attached property is \c true if this child is the current item. |
| |
| It is attached to each child item of the SwipeView. |
| */ |
| |
| /*! |
| \qmlattachedproperty bool QtQuick.Controls::SwipeView::isNextItem |
| \since QtQuick.Controls 2.1 (Qt 5.8) |
| \readonly |
| |
| This attached property is \c true if this child is the next item. |
| |
| It is attached to each child item of the SwipeView. |
| */ |
| |
| /*! |
| \qmlattachedproperty bool QtQuick.Controls::SwipeView::isPreviousItem |
| \since QtQuick.Controls 2.1 (Qt 5.8) |
| \readonly |
| |
| This attached property is \c true if this child is the previous item. |
| |
| It is attached to each child item of the SwipeView. |
| */ |
| |
| /*! |
| \qmlattachedproperty SwipeView QtQuick.Controls::SwipeView::view |
| \readonly |
| |
| This attached property holds the view that manages this child item. |
| |
| It is attached to each child item of the SwipeView. |
| */ |
| |
| void QQuickSwipeViewAttachedPrivate::updateCurrentIndex() |
| { |
| setCurrentIndex(swipeView ? swipeView->currentIndex() : -1); |
| } |
| |
| void QQuickSwipeViewAttachedPrivate::setCurrentIndex(int i) |
| { |
| if (i == currentIndex) |
| return; |
| |
| Q_Q(QQuickSwipeViewAttached); |
| const bool wasCurrent = q->isCurrentItem(); |
| const bool wasNext = q->isNextItem(); |
| const bool wasPrevious = q->isPreviousItem(); |
| |
| currentIndex = i; |
| if (wasCurrent != q->isCurrentItem()) |
| emit q->isCurrentItemChanged(); |
| if (wasNext != q->isNextItem()) |
| emit q->isNextItemChanged(); |
| if (wasPrevious != q->isPreviousItem()) |
| emit q->isPreviousItemChanged(); |
| } |
| |
| void QQuickSwipeViewAttachedPrivate::update(QQuickSwipeView *newView, int newIndex) |
| { |
| Q_Q(QQuickSwipeViewAttached); |
| int oldIndex = index; |
| QQuickSwipeView *oldView = swipeView; |
| |
| index = newIndex; |
| swipeView = newView; |
| |
| if (oldView != newView) { |
| if (oldView) { |
| disconnect(oldView, &QQuickSwipeView::currentIndexChanged, |
| this, &QQuickSwipeViewAttachedPrivate::updateCurrentIndex); |
| } |
| if (newView) { |
| connect(newView, &QQuickSwipeView::currentIndexChanged, |
| this, &QQuickSwipeViewAttachedPrivate::updateCurrentIndex); |
| } |
| emit q->viewChanged(); |
| } |
| if (oldIndex != newIndex) |
| emit q->indexChanged(); |
| |
| updateCurrentIndex(); |
| } |
| |
| QQuickSwipeViewAttached::QQuickSwipeViewAttached(QObject *parent) |
| : QObject(*(new QQuickSwipeViewAttachedPrivate), parent) |
| { |
| if (!qobject_cast<QQuickItem *>(parent)) |
| qmlWarning(parent) << "SwipeView: attached properties must be accessed from within a child item"; |
| } |
| |
| int QQuickSwipeViewAttached::index() const |
| { |
| Q_D(const QQuickSwipeViewAttached); |
| return d->index; |
| } |
| |
| bool QQuickSwipeViewAttached::isCurrentItem() const |
| { |
| Q_D(const QQuickSwipeViewAttached); |
| return d->index != -1 && d->currentIndex != -1 && d->index == d->currentIndex; |
| } |
| |
| QQuickSwipeView *QQuickSwipeViewAttached::view() const |
| { |
| Q_D(const QQuickSwipeViewAttached); |
| return d->swipeView; |
| } |
| |
| bool QQuickSwipeViewAttached::isNextItem() const |
| { |
| Q_D(const QQuickSwipeViewAttached); |
| return d->index != -1 && d->currentIndex != -1 && d->index == d->currentIndex + 1; |
| } |
| |
| bool QQuickSwipeViewAttached::isPreviousItem() const |
| { |
| Q_D(const QQuickSwipeViewAttached); |
| return d->index != -1 && d->currentIndex != -1 && d->index == d->currentIndex - 1; |
| } |
| |
| QT_END_NAMESPACE |