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 "qquickswipeview_p.h"
38
39#include <QtQml/qqmlinfo.h>
40#include <QtQuickTemplates2/private/qquickcontainer_p_p.h>
41
42QT_BEGIN_NAMESPACE
43
44/*!
45 \qmltype SwipeView
46 \inherits Container
47//! \instantiates QQuickSwipeView
48 \inqmlmodule QtQuick.Controls
49 \since 5.7
50 \ingroup qtquickcontrols2-navigation
51 \ingroup qtquickcontrols2-containers
52 \ingroup qtquickcontrols2-focusscopes
53 \brief Enables the user to navigate pages by swiping sideways.
54
55 SwipeView provides a swipe-based navigation model.
56
57 \image qtquickcontrols2-swipeview.gif
58
59 SwipeView is populated with a set of pages. One page is visible at a time.
60 The user can navigate between the pages by swiping sideways. Notice that
61 SwipeView itself is entirely non-visual. It is recommended to combine it
62 with PageIndicator, to give the user a visual clue that there are multiple
63 pages.
64
65 \snippet qtquickcontrols2-swipeview-indicator.qml 1
66
67 As shown above, SwipeView is typically populated with a static set of
68 pages that are defined inline as children of the view. It is also possible
69 to \l {Container::addItem()}{add}, \l {Container::insertItem()}{insert},
70 \l {Container::moveItem()}{move}, and \l {Container::removeItem()}{remove}
71 pages dynamically at run time.
72
73 It is generally not advisable to add excessive amounts of pages to a
74 SwipeView. However, when the amount of pages grows larger, or individual
75 pages are relatively complex, it may be desirable to free up resources by
76 unloading pages that are outside the immediate reach of the user.
77 The following example presents how to use \l Loader to keep a maximum of
78 three pages simultaneously instantiated.
79
80 \code
81 SwipeView {
82 Repeater {
83 model: 6
84 Loader {
85 active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem
86 sourceComponent: Text {
87 text: index
88 Component.onCompleted: console.log("created:", index)
89 Component.onDestruction: console.log("destroyed:", index)
90 }
91 }
92 }
93 }
94 \endcode
95
96 \note SwipeView takes over the geometry management of items added to the
97 view. Using anchors on the items is not supported, and any \c width
98 or \c height assignment will be overridden by the view. Notice that
99 this only applies to the root of the item. Specifying width and height,
100 or using anchors for its children works as expected.
101
102 \sa TabBar, PageIndicator, {Customizing SwipeView}, {Navigation Controls}, {Container Controls},
103 {Focus Management in Qt Quick Controls}
104*/
105
106class QQuickSwipeViewPrivate : public QQuickContainerPrivate
107{
108 Q_DECLARE_PUBLIC(QQuickSwipeView)
109
110public:
111 void resizeItem(QQuickItem *item);
112 void resizeItems();
113
114 static QQuickSwipeViewPrivate *get(QQuickSwipeView *view);
115
116 void itemImplicitWidthChanged(QQuickItem *item) override;
117 void itemImplicitHeightChanged(QQuickItem *item) override;
118
119 qreal getContentWidth() const override;
120 qreal getContentHeight() const override;
121
122 bool interactive = true;
123 Qt::Orientation orientation = Qt::Horizontal;
124};
125
126class QQuickSwipeViewAttachedPrivate : public QObjectPrivate
127{
128 Q_DECLARE_PUBLIC(QQuickSwipeViewAttached)
129
130public:
131 static QQuickSwipeViewAttachedPrivate *get(QQuickSwipeViewAttached *attached)
132 {
133 return attached->d_func();
134 }
135
136 void update(QQuickSwipeView *newView, int newIndex);
137 void updateCurrentIndex();
138 void setCurrentIndex(int i);
139
140 QQuickSwipeView *swipeView = nullptr;
141 int index = -1;
142 int currentIndex = -1;
143};
144
145void QQuickSwipeViewPrivate::resizeItems()
146{
147 Q_Q(QQuickSwipeView);
148 const int count = q->count();
149 for (int i = 0; i < count; ++i) {
150 QQuickItem *item = itemAt(index: i);
151 if (item) {
152 QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
153 // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors
154 if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property(name: "_q_QQuickSwipeView_warned").toBool()) {
155 qmlWarning(me: item) << "SwipeView has detected conflicting anchors. Unable to layout the item.";
156 item->setProperty(name: "_q_QQuickSwipeView_warned", value: true);
157 }
158
159 if (orientation == Qt::Horizontal)
160 item->setY(0);
161 else
162 item->setX(0);
163 item->setSize(QSizeF(contentItem->width(), contentItem->height()));
164 }
165 }
166}
167
168QQuickSwipeViewPrivate *QQuickSwipeViewPrivate::get(QQuickSwipeView *view)
169{
170 return view->d_func();
171}
172
173void QQuickSwipeViewPrivate::itemImplicitWidthChanged(QQuickItem *item)
174{
175 Q_Q(QQuickSwipeView);
176 QQuickContainerPrivate::itemImplicitWidthChanged(item);
177 if (item == q->currentItem())
178 updateImplicitContentWidth();
179}
180
181void QQuickSwipeViewPrivate::itemImplicitHeightChanged(QQuickItem *item)
182{
183 Q_Q(QQuickSwipeView);
184 QQuickContainerPrivate::itemImplicitHeightChanged(item);
185 if (item == q->currentItem())
186 updateImplicitContentHeight();
187}
188
189qreal QQuickSwipeViewPrivate::getContentWidth() const
190{
191 Q_Q(const QQuickSwipeView);
192 QQuickItem *currentItem = q->currentItem();
193 return currentItem ? currentItem->implicitWidth() : 0;
194}
195
196qreal QQuickSwipeViewPrivate::getContentHeight() const
197{
198 Q_Q(const QQuickSwipeView);
199 QQuickItem *currentItem = q->currentItem();
200 return currentItem ? currentItem->implicitHeight() : 0;
201}
202
203QQuickSwipeView::QQuickSwipeView(QQuickItem *parent)
204 : QQuickContainer(*(new QQuickSwipeViewPrivate), parent)
205{
206 Q_D(QQuickSwipeView);
207 d->changeTypes |= QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
208 setFlag(flag: ItemIsFocusScope);
209 setActiveFocusOnTab(true);
210 QObjectPrivate::connect(sender: this, signal: &QQuickContainer::currentItemChanged, receiverPrivate: d, slot: &QQuickControlPrivate::updateImplicitContentSize);
211}
212
213/*!
214 \since QtQuick.Controls 2.1 (Qt 5.8)
215 \qmlproperty bool QtQuick.Controls::SwipeView::interactive
216
217 This property describes whether the user can interact with the SwipeView.
218 The user cannot swipe a view that is not interactive.
219
220 The default value is \c true.
221*/
222bool QQuickSwipeView::isInteractive() const
223{
224 Q_D(const QQuickSwipeView);
225 return d->interactive;
226}
227
228void QQuickSwipeView::setInteractive(bool interactive)
229{
230 Q_D(QQuickSwipeView);
231 if (d->interactive == interactive)
232 return;
233
234 d->interactive = interactive;
235 emit interactiveChanged();
236}
237
238/*!
239 \since QtQuick.Controls 2.2 (Qt 5.9)
240 \qmlproperty enumeration QtQuick.Controls::SwipeView::orientation
241
242 This property holds the orientation.
243
244 Possible values:
245 \value Qt.Horizontal Horizontal (default)
246 \value Qt.Vertical Vertical
247
248 \sa horizontal, vertical
249*/
250Qt::Orientation QQuickSwipeView::orientation() const
251{
252 Q_D(const QQuickSwipeView);
253 return d->orientation;
254}
255
256void QQuickSwipeView::setOrientation(Qt::Orientation orientation)
257{
258 Q_D(QQuickSwipeView);
259 if (d->orientation == orientation)
260 return;
261
262 d->orientation = orientation;
263 if (isComponentComplete())
264 d->resizeItems();
265 emit orientationChanged();
266}
267
268/*!
269 \since QtQuick.Controls 2.3 (Qt 5.10)
270 \qmlproperty bool QtQuick.Controls::SwipeView::horizontal
271 \readonly
272
273 This property holds whether the swipe view is horizontal.
274
275 \sa orientation
276*/
277bool QQuickSwipeView::isHorizontal() const
278{
279 Q_D(const QQuickSwipeView);
280 return d->orientation == Qt::Horizontal;
281}
282
283/*!
284 \since QtQuick.Controls 2.3 (Qt 5.10)
285 \qmlproperty bool QtQuick.Controls::SwipeView::vertical
286 \readonly
287
288 This property holds whether the swipe view is vertical.
289
290 \sa orientation
291*/
292bool QQuickSwipeView::isVertical() const
293{
294 Q_D(const QQuickSwipeView);
295 return d->orientation == Qt::Vertical;
296}
297
298QQuickSwipeViewAttached *QQuickSwipeView::qmlAttachedProperties(QObject *object)
299{
300 return new QQuickSwipeViewAttached(object);
301}
302
303void QQuickSwipeView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
304{
305 Q_D(QQuickSwipeView);
306 QQuickContainer::geometryChanged(newGeometry, oldGeometry);
307 d->resizeItems();
308}
309
310void QQuickSwipeView::itemAdded(int index, QQuickItem *item)
311{
312 Q_D(QQuickSwipeView);
313 if (isComponentComplete())
314 item->setSize(QSizeF(d->contentItem->width(), d->contentItem->height()));
315 QQuickSwipeViewAttached *attached = qobject_cast<QQuickSwipeViewAttached *>(object: qmlAttachedPropertiesObject<QQuickSwipeView>(obj: item));
316 if (attached)
317 QQuickSwipeViewAttachedPrivate::get(attached)->update(newView: this, newIndex: index);
318}
319
320void QQuickSwipeView::itemMoved(int index, QQuickItem *item)
321{
322 QQuickSwipeViewAttached *attached = qobject_cast<QQuickSwipeViewAttached *>(object: qmlAttachedPropertiesObject<QQuickSwipeView>(obj: item));
323 if (attached)
324 QQuickSwipeViewAttachedPrivate::get(attached)->update(newView: this, newIndex: index);
325}
326
327void QQuickSwipeView::itemRemoved(int, QQuickItem *item)
328{
329 QQuickSwipeViewAttached *attached = qobject_cast<QQuickSwipeViewAttached *>(object: qmlAttachedPropertiesObject<QQuickSwipeView>(obj: item));
330 if (attached)
331 QQuickSwipeViewAttachedPrivate::get(attached)->update(newView: nullptr, newIndex: -1);
332}
333
334#if QT_CONFIG(accessibility)
335QAccessible::Role QQuickSwipeView::accessibleRole() const
336{
337 return QAccessible::PageTabList;
338}
339#endif
340
341/*!
342 \qmlattachedproperty int QtQuick.Controls::SwipeView::index
343 \readonly
344
345 This attached property holds the index of each child item in the SwipeView.
346
347 It is attached to each child item of the SwipeView.
348*/
349
350/*!
351 \qmlattachedproperty bool QtQuick.Controls::SwipeView::isCurrentItem
352 \readonly
353
354 This attached property is \c true if this child is the current item.
355
356 It is attached to each child item of the SwipeView.
357*/
358
359/*!
360 \qmlattachedproperty bool QtQuick.Controls::SwipeView::isNextItem
361 \since QtQuick.Controls 2.1 (Qt 5.8)
362 \readonly
363
364 This attached property is \c true if this child is the next item.
365
366 It is attached to each child item of the SwipeView.
367*/
368
369/*!
370 \qmlattachedproperty bool QtQuick.Controls::SwipeView::isPreviousItem
371 \since QtQuick.Controls 2.1 (Qt 5.8)
372 \readonly
373
374 This attached property is \c true if this child is the previous item.
375
376 It is attached to each child item of the SwipeView.
377*/
378
379/*!
380 \qmlattachedproperty SwipeView QtQuick.Controls::SwipeView::view
381 \readonly
382
383 This attached property holds the view that manages this child item.
384
385 It is attached to each child item of the SwipeView.
386*/
387
388void QQuickSwipeViewAttachedPrivate::updateCurrentIndex()
389{
390 setCurrentIndex(swipeView ? swipeView->currentIndex() : -1);
391}
392
393void QQuickSwipeViewAttachedPrivate::setCurrentIndex(int i)
394{
395 if (i == currentIndex)
396 return;
397
398 Q_Q(QQuickSwipeViewAttached);
399 const bool wasCurrent = q->isCurrentItem();
400 const bool wasNext = q->isNextItem();
401 const bool wasPrevious = q->isPreviousItem();
402
403 currentIndex = i;
404 if (wasCurrent != q->isCurrentItem())
405 emit q->isCurrentItemChanged();
406 if (wasNext != q->isNextItem())
407 emit q->isNextItemChanged();
408 if (wasPrevious != q->isPreviousItem())
409 emit q->isPreviousItemChanged();
410}
411
412void QQuickSwipeViewAttachedPrivate::update(QQuickSwipeView *newView, int newIndex)
413{
414 Q_Q(QQuickSwipeViewAttached);
415 int oldIndex = index;
416 QQuickSwipeView *oldView = swipeView;
417
418 index = newIndex;
419 swipeView = newView;
420
421 if (oldView != newView) {
422 if (oldView) {
423 disconnect(sender: oldView, signal: &QQuickSwipeView::currentIndexChanged,
424 receiverPrivate: this, slot: &QQuickSwipeViewAttachedPrivate::updateCurrentIndex);
425 }
426 if (newView) {
427 connect(sender: newView, signal: &QQuickSwipeView::currentIndexChanged,
428 receiverPrivate: this, slot: &QQuickSwipeViewAttachedPrivate::updateCurrentIndex);
429 }
430 emit q->viewChanged();
431 }
432 if (oldIndex != newIndex)
433 emit q->indexChanged();
434
435 updateCurrentIndex();
436}
437
438QQuickSwipeViewAttached::QQuickSwipeViewAttached(QObject *parent)
439 : QObject(*(new QQuickSwipeViewAttachedPrivate), parent)
440{
441 if (!qobject_cast<QQuickItem *>(object: parent))
442 qmlWarning(me: parent) << "SwipeView: attached properties must be accessed from within a child item";
443}
444
445int QQuickSwipeViewAttached::index() const
446{
447 Q_D(const QQuickSwipeViewAttached);
448 return d->index;
449}
450
451bool QQuickSwipeViewAttached::isCurrentItem() const
452{
453 Q_D(const QQuickSwipeViewAttached);
454 return d->index != -1 && d->currentIndex != -1 && d->index == d->currentIndex;
455}
456
457QQuickSwipeView *QQuickSwipeViewAttached::view() const
458{
459 Q_D(const QQuickSwipeViewAttached);
460 return d->swipeView;
461}
462
463bool QQuickSwipeViewAttached::isNextItem() const
464{
465 Q_D(const QQuickSwipeViewAttached);
466 return d->index != -1 && d->currentIndex != -1 && d->index == d->currentIndex + 1;
467}
468
469bool QQuickSwipeViewAttached::isPreviousItem() const
470{
471 Q_D(const QQuickSwipeViewAttached);
472 return d->index != -1 && d->currentIndex != -1 && d->index == d->currentIndex - 1;
473}
474
475QT_END_NAMESPACE
476

source code of qtquickcontrols2/src/quicktemplates2/qquickswipeview.cpp