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 "qquickpageindicator_p.h"
38#include "qquickcontrol_p_p.h"
39
40#include <QtCore/qmath.h>
41#include <QtQuick/private/qquickitem_p.h>
42
43QT_BEGIN_NAMESPACE
44
45/*!
46 \qmltype PageIndicator
47 \inherits Control
48//! \instantiates QQuickPageIndicator
49 \inqmlmodule QtQuick.Controls
50 \since 5.7
51 \ingroup qtquickcontrols2-indicators
52 \brief Indicates the currently active page.
53
54 PageIndicator is used to indicate the currently active page
55 in a container of multiple pages. PageIndicator consists of
56 delegate items that present pages.
57
58 \image qtquickcontrols2-pageindicator.png
59
60 \code
61 Column {
62 StackLayout {
63 id: stackLayout
64
65 Page {
66 // ...
67 }
68 Page {
69 // ...
70 }
71 Page {
72 // ...
73 }
74 }
75
76 PageIndicator {
77 currentIndex: stackLayout.currentIndex
78 count: stackLayout.count
79 }
80 }
81 \endcode
82
83 \sa SwipeView, {Customizing PageIndicator}, {Indicator Controls}
84*/
85
86class QQuickPageIndicatorPrivate : public QQuickControlPrivate
87{
88 Q_DECLARE_PUBLIC(QQuickPageIndicator)
89
90public:
91 void handlePress(const QPointF &point) override;
92 void handleMove(const QPointF &point) override;
93 void handleRelease(const QPointF &point) override;
94 void handleUngrab() override;
95
96 QQuickItem *itemAt(const QPointF &pos) const;
97 void updatePressed(bool pressed, const QPointF &pos = QPointF());
98 void setContextProperty(QQuickItem *item, const QString &name, const QVariant &value);
99
100 void itemChildAdded(QQuickItem *, QQuickItem *child) override;
101
102 int count = 0;
103 int currentIndex = 0;
104 bool interactive = false;
105 QQmlComponent *delegate = nullptr;
106 QQuickItem *pressedItem = nullptr;
107};
108
109void QQuickPageIndicatorPrivate::handlePress(const QPointF &point)
110{
111 QQuickControlPrivate::handlePress(point);
112 if (interactive)
113 updatePressed(pressed: true, pos: point);
114}
115
116void QQuickPageIndicatorPrivate::handleMove(const QPointF &point)
117{
118 QQuickControlPrivate::handleMove(point);
119 if (interactive)
120 updatePressed(pressed: true, pos: point);
121}
122
123void QQuickPageIndicatorPrivate::handleRelease(const QPointF &point)
124{
125 Q_Q(QQuickPageIndicator);
126 QQuickControlPrivate::handleRelease(point);
127 if (interactive) {
128 if (pressedItem && contentItem)
129 q->setCurrentIndex(contentItem->childItems().indexOf(t: pressedItem));
130 updatePressed(pressed: false);
131 }
132}
133
134void QQuickPageIndicatorPrivate::handleUngrab()
135{
136 QQuickControlPrivate::handleUngrab();
137 if (interactive)
138 updatePressed(pressed: false);
139}
140
141QQuickItem *QQuickPageIndicatorPrivate::itemAt(const QPointF &pos) const
142{
143 Q_Q(const QQuickPageIndicator);
144 if (!contentItem || !q->contains(point: pos))
145 return nullptr;
146
147 QPointF contentPos = q->mapToItem(item: contentItem, point: pos);
148 QQuickItem *item = contentItem->childAt(x: contentPos.x(), y: contentPos.y());
149 while (item && item->parentItem() != contentItem)
150 item = item->parentItem();
151 if (item && !QQuickItemPrivate::get(item)->isTransparentForPositioner())
152 return item;
153
154 // find the nearest
155 qreal distance = qInf();
156 QQuickItem *nearest = nullptr;
157 const auto childItems = contentItem->childItems();
158 for (QQuickItem *child : childItems) {
159 if (QQuickItemPrivate::get(item: child)->isTransparentForPositioner())
160 continue;
161
162 QPointF center = child->boundingRect().center();
163 QPointF pt = contentItem->mapToItem(item: child, point: contentPos);
164
165 qreal len = QLineF(center, pt).length();
166 if (len < distance) {
167 distance = len;
168 nearest = child;
169 }
170 }
171 return nearest;
172}
173
174void QQuickPageIndicatorPrivate::updatePressed(bool pressed, const QPointF &pos)
175{
176 QQuickItem *prevItem = pressedItem;
177 pressedItem = pressed ? itemAt(pos) : nullptr;
178 if (prevItem != pressedItem) {
179 setContextProperty(item: prevItem, QStringLiteral("pressed"), value: false);
180 setContextProperty(item: pressedItem, QStringLiteral("pressed"), value: pressed);
181 }
182}
183
184void QQuickPageIndicatorPrivate::setContextProperty(QQuickItem *item, const QString &name, const QVariant &value)
185{
186 QQmlContext *context = qmlContext(item);
187 if (context && context->isValid()) {
188 context = context->parentContext();
189 if (context && context->isValid())
190 context->setContextProperty(name, value);
191 }
192}
193
194void QQuickPageIndicatorPrivate::itemChildAdded(QQuickItem *, QQuickItem *child)
195{
196 if (!QQuickItemPrivate::get(item: child)->isTransparentForPositioner())
197 setContextProperty(item: child, QStringLiteral("pressed"), value: false);
198}
199
200QQuickPageIndicator::QQuickPageIndicator(QQuickItem *parent)
201 : QQuickControl(*(new QQuickPageIndicatorPrivate), parent)
202{
203}
204
205/*!
206 \qmlproperty int QtQuick.Controls::PageIndicator::count
207
208 This property holds the number of pages.
209*/
210int QQuickPageIndicator::count() const
211{
212 Q_D(const QQuickPageIndicator);
213 return d->count;
214}
215
216void QQuickPageIndicator::setCount(int count)
217{
218 Q_D(QQuickPageIndicator);
219 if (d->count == count)
220 return;
221
222 d->count = count;
223 emit countChanged();
224}
225
226/*!
227 \qmlproperty int QtQuick.Controls::PageIndicator::currentIndex
228
229 This property holds the index of the current page.
230*/
231int QQuickPageIndicator::currentIndex() const
232{
233 Q_D(const QQuickPageIndicator);
234 return d->currentIndex;
235}
236
237void QQuickPageIndicator::setCurrentIndex(int index)
238{
239 Q_D(QQuickPageIndicator);
240 if (d->currentIndex == index)
241 return;
242
243 d->currentIndex = index;
244 emit currentIndexChanged();
245}
246
247/*!
248 \qmlproperty bool QtQuick.Controls::PageIndicator::interactive
249
250 This property holds whether the control is interactive. An interactive page indicator
251 reacts to presses and automatically changes the \l {currentIndex}{current index}
252 appropriately.
253
254 \snippet qtquickcontrols2-pageindicator-interactive.qml 1
255
256 \note Page indicators are typically quite small (in order to avoid
257 distracting the user from the actual content of the user interface). They
258 can be hard to click, and might not be easily recognized as interactive by
259 the user. For these reasons, they are best used to complement primary
260 methods of navigation (such as \l SwipeView), not replace them.
261
262 The default value is \c false.
263*/
264bool QQuickPageIndicator::isInteractive() const
265{
266 Q_D(const QQuickPageIndicator);
267 return d->interactive;
268}
269
270void QQuickPageIndicator::setInteractive(bool interactive)
271{
272 Q_D(QQuickPageIndicator);
273 if (d->interactive == interactive)
274 return;
275
276 d->interactive = interactive;
277 if (interactive) {
278 setAcceptedMouseButtons(Qt::LeftButton);
279#if QT_CONFIG(quicktemplates2_multitouch)
280 setAcceptTouchEvents(true);
281#endif
282#if QT_CONFIG(cursor)
283 setCursor(Qt::ArrowCursor);
284#endif
285 } else {
286 setAcceptedMouseButtons(Qt::NoButton);
287#if QT_CONFIG(quicktemplates2_multitouch)
288 setAcceptTouchEvents(true);
289#endif
290#if QT_CONFIG(cursor)
291 unsetCursor();
292#endif
293 }
294 emit interactiveChanged();
295}
296
297/*!
298 \qmlproperty Component QtQuick.Controls::PageIndicator::delegate
299
300 This property holds a delegate that presents a page.
301
302 The following properties are available in the context of each delegate:
303 \table
304 \row \li \b index : int \li The index of the item
305 \row \li \b pressed : bool \li Whether the item is pressed
306 \endtable
307*/
308QQmlComponent *QQuickPageIndicator::delegate() const
309{
310 Q_D(const QQuickPageIndicator);
311 return d->delegate;
312}
313
314void QQuickPageIndicator::setDelegate(QQmlComponent *delegate)
315{
316 Q_D(QQuickPageIndicator);
317 if (d->delegate == delegate)
318 return;
319
320 d->delegate = delegate;
321 emit delegateChanged();
322}
323
324void QQuickPageIndicator::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
325{
326 Q_D(QQuickPageIndicator);
327 QQuickControl::contentItemChange(newItem, oldItem);
328 if (oldItem)
329 QQuickItemPrivate::get(item: oldItem)->removeItemChangeListener(d, types: QQuickItemPrivate::Children);
330 if (newItem)
331 QQuickItemPrivate::get(item: newItem)->addItemChangeListener(listener: d, types: QQuickItemPrivate::Children);
332}
333
334#if QT_CONFIG(quicktemplates2_multitouch)
335void QQuickPageIndicator::touchEvent(QTouchEvent *event)
336{
337 Q_D(QQuickPageIndicator);
338 if (d->interactive)
339 QQuickControl::touchEvent(event);
340 else
341 event->ignore(); // QTBUG-61785
342}
343#endif
344
345#if QT_CONFIG(accessibility)
346QAccessible::Role QQuickPageIndicator::accessibleRole() const
347{
348 return QAccessible::Indicator;
349}
350#endif
351
352QT_END_NAMESPACE
353

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