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 "qquickscrollindicator_p.h"
38 #include "qquickcontrol_p_p.h"
39 
40 #include <QtQml/qqmlinfo.h>
41 #include <QtQuick/private/qquickflickable_p.h>
42 #include <QtQuick/private/qquickitemchangelistener_p.h>
43 
44 QT_BEGIN_NAMESPACE
45 
46 /*!
47     \qmltype ScrollIndicator
48     \inherits Control
49 //!     \instantiates QQuickScrollIndicator
50     \inqmlmodule QtQuick.Controls
51     \since 5.7
52     \ingroup qtquickcontrols2-indicators
53     \brief Vertical or horizontal non-interactive scroll indicator.
54 
55     \image qtquickcontrols2-scrollindicator.gif
56 
57     ScrollIndicator is a non-interactive indicator that indicates the current scroll
58     position. A scroll indicator can be either \l vertical or \l horizontal, and can
59     be attached to any \l Flickable, such as \l ListView and \l GridView.
60 
61     \code
62     Flickable {
63         // ...
64         ScrollIndicator.vertical: ScrollIndicator { }
65     }
66     \endcode
67 
68     \section1 Attaching ScrollIndicator to a Flickable
69 
70     \note When ScrollIndicator is attached \l {ScrollIndicator::vertical}{vertically}
71     or \l {ScrollIndicator::horizontal}{horizontally} to a Flickable, its geometry and
72     the following properties are automatically set and updated as appropriate:
73 
74     \list
75     \li \l orientation
76     \li \l position
77     \li \l size
78     \li \l active
79     \endlist
80 
81     An attached ScrollIndicator re-parents itself to the target Flickable. A vertically
82     attached ScrollIndicator resizes itself to the height of the Flickable, and positions
83     itself to either side of it based on the \l {Control::mirrored}{layout direction}.
84     A horizontally attached ScrollIndicator resizes itself to the width of the Flickable,
85     and positions itself to the bottom. The automatic geometry management can be disabled
86     by specifying another parent for the attached ScrollIndicator. This can be useful, for
87     example, if the ScrollIndicator should be placed outside a clipping Flickable. This is
88     demonstrated by the following example:
89 
90     \code
91     Flickable {
92         id: flickable
93         clip: true
94         // ...
95         ScrollIndicator.vertical: ScrollIndicator {
96             parent: flickable.parent
97             anchors.top: flickable.top
98             anchors.left: flickable.right
99             anchors.bottom: flickable.bottom
100         }
101     }
102     \endcode
103 
104     \section1 Binding the Active State of Horizontal and Vertical Scroll Indicators
105 
106     Horizontal and vertical scroll indicators do not share the \l active state with
107     each other by default. In order to keep both indicators visible whilst scrolling
108     to either direction, establish a two-way binding between the active states as
109     presented by the following example:
110 
111     \snippet qtquickcontrols2-scrollindicator-active.qml 1
112 
113     \section1 Non-attached Scroll Indicators
114 
115     It is possible to create an instance of ScrollIndicator without using the
116     attached property API. This is useful when the behavior of the attached
117     scoll indicator is not sufficient or a \l Flickable is not in use. In the
118     following example, horizontal and vertical scroll indicators are used to
119     indicate how far the user has scrolled over the text (using \l MouseArea
120     instead of \l Flickable):
121 
122     \snippet qtquickcontrols2-scrollindicator-non-attached.qml 1
123 
124     \image qtquickcontrols2-scrollindicator-non-attached.png
125 
126     \sa ScrollBar, {Customizing ScrollIndicator}, {Indicator Controls}
127 */
128 
129 static const QQuickItemPrivate::ChangeTypes changeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
130 static const QQuickItemPrivate::ChangeTypes horizontalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitHeight;
131 static const QQuickItemPrivate::ChangeTypes verticalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitWidth;
132 
133 class QQuickScrollIndicatorPrivate : public QQuickControlPrivate
134 {
135     Q_DECLARE_PUBLIC(QQuickScrollIndicator)
136 
137 public:
138     struct VisualArea
139     {
VisualAreaQQuickScrollIndicatorPrivate::VisualArea140         VisualArea(qreal pos, qreal sz)
141             : position(pos), size(sz) { }
142         qreal position = 0;
143         qreal size = 0;
144     };
145     VisualArea visualArea() const;
146     void visualAreaChange(const VisualArea &newVisualArea, const VisualArea &oldVisualArea);
147 
148     void resizeContent() override;
149 
150     qreal size = 0;
151     qreal minimumSize = 0;
152     qreal position = 0;
153     bool active = false;
154     Qt::Orientation orientation = Qt::Vertical;
155 };
156 
visualArea() const157 QQuickScrollIndicatorPrivate::VisualArea QQuickScrollIndicatorPrivate::visualArea() const
158 {
159     qreal visualPos = position;
160     if (minimumSize > size)
161         visualPos = position / (1.0 - size) * (1.0 - minimumSize);
162 
163     qreal visualSize = qBound<qreal>(0, qMax(size, minimumSize) + qMin<qreal>(0, visualPos), 1.0 - visualPos);
164 
165     visualPos = qBound<qreal>(0, visualPos, 1.0 - visualSize);
166 
167     return VisualArea(visualPos, visualSize);
168 }
169 
visualAreaChange(const VisualArea & newVisualArea,const VisualArea & oldVisualArea)170 void QQuickScrollIndicatorPrivate::visualAreaChange(const VisualArea &newVisualArea, const VisualArea &oldVisualArea)
171 {
172     Q_Q(QQuickScrollIndicator);
173     if (!qFuzzyCompare(newVisualArea.size, oldVisualArea.size))
174         emit q->visualSizeChanged();
175     if (!qFuzzyCompare(newVisualArea.position, oldVisualArea.position))
176         emit q->visualPositionChanged();
177 }
178 
resizeContent()179 void QQuickScrollIndicatorPrivate::resizeContent()
180 {
181     Q_Q(QQuickScrollIndicator);
182     if (!contentItem)
183         return;
184 
185     // - negative overshoot (pos < 0): clamp the pos to 0, and deduct the overshoot from the size
186     // - positive overshoot (pos + size > 1): clamp the size to 1-pos
187     const VisualArea visual = visualArea();
188 
189     if (orientation == Qt::Horizontal) {
190         contentItem->setPosition(QPointF(q->leftPadding() + visual.position * q->availableWidth(), q->topPadding()));
191         contentItem->setSize(QSizeF(q->availableWidth() * visual.size, q->availableHeight()));
192     } else {
193         contentItem->setPosition(QPointF(q->leftPadding(), q->topPadding() + visual.position * q->availableHeight()));
194         contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight() * visual.size));
195     }
196 }
197 
QQuickScrollIndicator(QQuickItem * parent)198 QQuickScrollIndicator::QQuickScrollIndicator(QQuickItem *parent)
199     : QQuickControl(*(new QQuickScrollIndicatorPrivate), parent)
200 {
201 }
202 
qmlAttachedProperties(QObject * object)203 QQuickScrollIndicatorAttached *QQuickScrollIndicator::qmlAttachedProperties(QObject *object)
204 {
205     return new QQuickScrollIndicatorAttached(object);
206 }
207 
208 /*!
209     \qmlproperty real QtQuick.Controls::ScrollIndicator::size
210 
211     This property holds the size of the indicator, scaled to \c {0.0 - 1.0}.
212 
213     \sa {Flickable::visibleArea.heightRatio}{Flickable::visibleArea}
214 
215     This property is automatically set when the scroll indicator is
216     \l {Attaching ScrollIndicator to a Flickable}{attached to a flickable}.
217 
218     \sa minimumSize, visualSize
219 */
size() const220 qreal QQuickScrollIndicator::size() const
221 {
222     Q_D(const QQuickScrollIndicator);
223     return d->size;
224 }
225 
setSize(qreal size)226 void QQuickScrollIndicator::setSize(qreal size)
227 {
228     Q_D(QQuickScrollIndicator);
229     if (qFuzzyCompare(d->size, size))
230         return;
231 
232     auto oldVisualArea = d->visualArea();
233     d->size = size;
234     if (isComponentComplete())
235         d->resizeContent();
236     emit sizeChanged();
237     d->visualAreaChange(d->visualArea(), oldVisualArea);
238 }
239 
240 /*!
241     \qmlproperty real QtQuick.Controls::ScrollIndicator::position
242 
243     This property holds the position of the indicator, scaled to \c {0.0 - 1.0}.
244 
245     This property is automatically set when the scroll indicator is
246     \l {Attaching ScrollIndicator to a Flickable}{attached to a flickable}.
247 
248     \sa {Flickable::visibleArea.yPosition}{Flickable::visibleArea}, visualPosition
249 */
position() const250 qreal QQuickScrollIndicator::position() const
251 {
252     Q_D(const QQuickScrollIndicator);
253     return d->position;
254 }
255 
setPosition(qreal position)256 void QQuickScrollIndicator::setPosition(qreal position)
257 {
258     Q_D(QQuickScrollIndicator);
259     if (qFuzzyCompare(d->position, position))
260         return;
261 
262     auto oldVisualArea = d->visualArea();
263     d->position = position;
264     if (isComponentComplete())
265         d->resizeContent();
266     emit positionChanged();
267     d->visualAreaChange(d->visualArea(), oldVisualArea);
268 }
269 
270 /*!
271     \qmlproperty bool QtQuick.Controls::ScrollIndicator::active
272 
273     This property holds whether the indicator is active, that is, when the
274     attached Flickable is \l {Flickable::moving}{moving}.
275 
276     It is possible to keep \l {Binding the Active State of Horizontal and Vertical Scroll Indicators}
277     {both horizontal and vertical indicators visible} while scrolling in either direction.
278 
279     This property is automatically set when the scroll indicator is
280     \l {Attaching ScrollIndicator to a Flickable}{attached to a flickable}.
281 */
isActive() const282 bool QQuickScrollIndicator::isActive() const
283 {
284     Q_D(const QQuickScrollIndicator);
285     return d->active;
286 }
287 
setActive(bool active)288 void QQuickScrollIndicator::setActive(bool active)
289 {
290     Q_D(QQuickScrollIndicator);
291     if (d->active == active)
292         return;
293 
294     d->active = active;
295     emit activeChanged();
296 }
297 
298 /*!
299     \qmlproperty enumeration QtQuick.Controls::ScrollIndicator::orientation
300 
301     This property holds the orientation of the indicator.
302 
303     Possible values:
304     \value Qt.Horizontal Horizontal
305     \value Qt.Vertical Vertical (default)
306 
307     This property is automatically set when the scroll indicator is
308     \l {Attaching ScrollIndicator to a Flickable}{attached to a flickable}.
309 
310     \sa horizontal, vertical
311 */
orientation() const312 Qt::Orientation QQuickScrollIndicator::orientation() const
313 {
314     Q_D(const QQuickScrollIndicator);
315     return d->orientation;
316 }
317 
setOrientation(Qt::Orientation orientation)318 void QQuickScrollIndicator::setOrientation(Qt::Orientation orientation)
319 {
320     Q_D(QQuickScrollIndicator);
321     if (d->orientation == orientation)
322         return;
323 
324     d->orientation = orientation;
325     if (isComponentComplete())
326         d->resizeContent();
327     emit orientationChanged();
328 }
329 
330 /*!
331     \since QtQuick.Controls 2.3 (Qt 5.10)
332     \qmlproperty bool QtQuick.Controls::ScrollIndicator::horizontal
333     \readonly
334 
335     This property holds whether the scroll indicator is horizontal.
336 
337     \sa orientation
338 */
isHorizontal() const339 bool QQuickScrollIndicator::isHorizontal() const
340 {
341     Q_D(const QQuickScrollIndicator);
342     return d->orientation == Qt::Horizontal;
343 }
344 
345 /*!
346     \since QtQuick.Controls 2.3 (Qt 5.10)
347     \qmlproperty bool QtQuick.Controls::ScrollIndicator::vertical
348     \readonly
349 
350     This property holds whether the scroll indicator is vertical.
351 
352     \sa orientation
353 */
isVertical() const354 bool QQuickScrollIndicator::isVertical() const
355 {
356     Q_D(const QQuickScrollIndicator);
357     return d->orientation == Qt::Vertical;
358 }
359 
360 /*!
361     \since QtQuick.Controls 2.4 (Qt 5.11)
362     \qmlproperty real QtQuick.Controls::ScrollIndicator::minimumSize
363 
364     This property holds the minimum size of the indicator, scaled to \c {0.0 - 1.0}.
365 
366     \sa size, visualSize, visualPosition
367 */
minimumSize() const368 qreal QQuickScrollIndicator::minimumSize() const
369 {
370     Q_D(const QQuickScrollIndicator);
371     return d->minimumSize;
372 }
373 
setMinimumSize(qreal minimumSize)374 void QQuickScrollIndicator::setMinimumSize(qreal minimumSize)
375 {
376     Q_D(QQuickScrollIndicator);
377     if (qFuzzyCompare(d->minimumSize, minimumSize))
378         return;
379 
380     auto oldVisualArea = d->visualArea();
381     d->minimumSize = minimumSize;
382     if (isComponentComplete())
383         d->resizeContent();
384     emit minimumSizeChanged();
385     d->visualAreaChange(d->visualArea(), oldVisualArea);
386 }
387 
388 /*!
389     \since QtQuick.Controls 2.4 (Qt 5.11)
390     \qmlproperty real QtQuick.Controls::ScrollIndicator::visualSize
391 
392     This property holds the effective visual size of the indicator,
393     which may be limited by the \l {minimumSize}{minimum size}.
394 
395     \sa size, minimumSize
396 */
visualSize() const397 qreal QQuickScrollIndicator::visualSize() const
398 {
399     Q_D(const QQuickScrollIndicator);
400     return d->visualArea().size;
401 }
402 
403 /*!
404     \since QtQuick.Controls 2.4 (Qt 5.11)
405     \qmlproperty real QtQuick.Controls::ScrollIndicator::visualPosition
406 
407     This property holds the effective visual position of the indicator,
408     which may be limited by the \l {minimumSize}{minimum size}.
409 
410     \sa position, minimumSize
411 */
visualPosition() const412 qreal QQuickScrollIndicator::visualPosition() const
413 {
414     Q_D(const QQuickScrollIndicator);
415     return d->visualArea().position;
416 }
417 
418 class QQuickScrollIndicatorAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener
419 {
420 public:
421     void activateHorizontal();
422     void activateVertical();
423 
424     void layoutHorizontal(bool move = true);
425     void layoutVertical(bool move = true);
426 
427     void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override;
428     void itemImplicitWidthChanged(QQuickItem *item) override;
429     void itemImplicitHeightChanged(QQuickItem *item) override;
430     void itemDestroyed(QQuickItem *item) override;
431 
432     QQuickFlickable *flickable = nullptr;
433     QQuickScrollIndicator *horizontal = nullptr;
434     QQuickScrollIndicator *vertical = nullptr;
435 };
436 
activateHorizontal()437 void QQuickScrollIndicatorAttachedPrivate::activateHorizontal()
438 {
439     horizontal->setActive(flickable->isMovingHorizontally());
440 }
441 
activateVertical()442 void QQuickScrollIndicatorAttachedPrivate::activateVertical()
443 {
444     vertical->setActive(flickable->isMovingVertically());
445 }
446 
layoutHorizontal(bool move)447 void QQuickScrollIndicatorAttachedPrivate::layoutHorizontal(bool move)
448 {
449     Q_ASSERT(horizontal && flickable);
450     if (horizontal->parentItem() != flickable)
451         return;
452     horizontal->setWidth(flickable->width());
453     if (move)
454         horizontal->setY(flickable->height() - horizontal->height());
455 }
456 
layoutVertical(bool move)457 void QQuickScrollIndicatorAttachedPrivate::layoutVertical(bool move)
458 {
459     Q_ASSERT(vertical && flickable);
460     if (vertical->parentItem() != flickable)
461         return;
462     vertical->setHeight(flickable->height());
463     if (move && !QQuickItemPrivate::get(vertical)->isMirrored())
464         vertical->setX(flickable->width() - vertical->width());
465 }
466 
itemGeometryChanged(QQuickItem * item,QQuickGeometryChange change,const QRectF & diff)467 void QQuickScrollIndicatorAttachedPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
468 {
469     Q_UNUSED(item);
470     Q_UNUSED(change);
471     if (horizontal && horizontal->height() > 0) {
472 #ifdef QT_QUICK_NEW_GEOMETRY_CHANGED_HANDLING // TODO: correct/rename diff to oldGeometry
473         bool move = qFuzzyIsNull(horizontal->y()) || qFuzzyCompare(horizontal->y(), diff.height() - horizontal->height());
474 #else
475         bool move = qFuzzyIsNull(horizontal->y()) || qFuzzyCompare(horizontal->y(), item->height() - diff.height() - horizontal->height());
476 #endif
477         layoutHorizontal(move);
478     }
479     if (vertical && vertical->width() > 0) {
480 #ifdef QT_QUICK_NEW_GEOMETRY_CHANGED_HANDLING // TODO: correct/rename diff to oldGeometry
481         bool move = qFuzzyIsNull(vertical->x()) || qFuzzyCompare(vertical->x(), diff.width() - vertical->width());
482 #else
483         bool move = qFuzzyIsNull(vertical->x()) || qFuzzyCompare(vertical->x(), item->width() - diff.width() - vertical->width());
484 #endif
485         layoutVertical(move);
486     }
487 }
488 
itemImplicitWidthChanged(QQuickItem * item)489 void QQuickScrollIndicatorAttachedPrivate::itemImplicitWidthChanged(QQuickItem *item)
490 {
491     if (item == vertical)
492         layoutVertical(true);
493 }
494 
itemImplicitHeightChanged(QQuickItem * item)495 void QQuickScrollIndicatorAttachedPrivate::itemImplicitHeightChanged(QQuickItem *item)
496 {
497     if (item == horizontal)
498         layoutHorizontal(true);
499 }
500 
itemDestroyed(QQuickItem * item)501 void QQuickScrollIndicatorAttachedPrivate::itemDestroyed(QQuickItem *item)
502 {
503     if (item == horizontal)
504         horizontal = nullptr;
505     if (item == vertical)
506         vertical = nullptr;
507 }
508 
QQuickScrollIndicatorAttached(QObject * parent)509 QQuickScrollIndicatorAttached::QQuickScrollIndicatorAttached(QObject *parent)
510     : QObject(*(new QQuickScrollIndicatorAttachedPrivate), parent)
511 {
512     Q_D(QQuickScrollIndicatorAttached);
513     d->flickable = qobject_cast<QQuickFlickable *>(parent);
514     if (d->flickable)
515         QQuickItemPrivate::get(d->flickable)->updateOrAddGeometryChangeListener(d, QQuickGeometryChange::Size);
516     else if (parent)
517         qmlWarning(parent) << "ScrollIndicator must be attached to a Flickable";
518 }
519 
~QQuickScrollIndicatorAttached()520 QQuickScrollIndicatorAttached::~QQuickScrollIndicatorAttached()
521 {
522     Q_D(QQuickScrollIndicatorAttached);
523     if (d->flickable) {
524         if (d->horizontal)
525             QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes);
526         if (d->vertical)
527             QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d,verticalChangeTypes);
528         // NOTE: Use removeItemChangeListener(Geometry) instead of updateOrRemoveGeometryChangeListener(Size).
529         // The latter doesn't remove the listener but only resets its types. Thus, it leaves behind a dangling
530         // pointer on destruction.
531         QQuickItemPrivate::get(d->flickable)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
532     }
533 }
534 
535 /*!
536     \qmlattachedproperty ScrollIndicator QtQuick.Controls::ScrollIndicator::horizontal
537 
538     This property attaches a horizontal scroll indicator to a \l Flickable.
539 
540     \code
541     Flickable {
542         contentWidth: 2000
543         ScrollIndicator.horizontal: ScrollIndicator { }
544     }
545     \endcode
546 
547     \sa {Attaching ScrollIndicator to a Flickable}
548 */
horizontal() const549 QQuickScrollIndicator *QQuickScrollIndicatorAttached::horizontal() const
550 {
551     Q_D(const QQuickScrollIndicatorAttached);
552     return d->horizontal;
553 }
554 
setHorizontal(QQuickScrollIndicator * horizontal)555 void QQuickScrollIndicatorAttached::setHorizontal(QQuickScrollIndicator *horizontal)
556 {
557     Q_D(QQuickScrollIndicatorAttached);
558     if (d->horizontal == horizontal)
559         return;
560 
561     if (d->horizontal && d->flickable) {
562         QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes);
563         QObjectPrivate::disconnect(d->flickable, &QQuickFlickable::movingHorizontallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateHorizontal);
564 
565         // TODO: export QQuickFlickableVisibleArea
566         QObject *area = d->flickable->property("visibleArea").value<QObject *>();
567         disconnect(area, SIGNAL(widthRatioChanged(qreal)), d->horizontal, SLOT(setSize(qreal)));
568         disconnect(area, SIGNAL(xPositionChanged(qreal)), d->horizontal, SLOT(setPosition(qreal)));
569     }
570 
571     d->horizontal = horizontal;
572 
573     if (horizontal && d->flickable) {
574         if (!horizontal->parentItem())
575             horizontal->setParentItem(d->flickable);
576         horizontal->setOrientation(Qt::Horizontal);
577 
578         QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, horizontalChangeTypes);
579         QObjectPrivate::connect(d->flickable, &QQuickFlickable::movingHorizontallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateHorizontal);
580 
581         // TODO: export QQuickFlickableVisibleArea
582         QObject *area = d->flickable->property("visibleArea").value<QObject *>();
583         connect(area, SIGNAL(widthRatioChanged(qreal)), horizontal, SLOT(setSize(qreal)));
584         connect(area, SIGNAL(xPositionChanged(qreal)), horizontal, SLOT(setPosition(qreal)));
585 
586         d->layoutHorizontal();
587         horizontal->setSize(area->property("widthRatio").toReal());
588         horizontal->setPosition(area->property("xPosition").toReal());
589     }
590     emit horizontalChanged();
591 }
592 
593 /*!
594     \qmlattachedproperty ScrollIndicator QtQuick.Controls::ScrollIndicator::vertical
595 
596     This property attaches a vertical scroll indicator to a \l Flickable.
597 
598     \code
599     Flickable {
600         contentHeight: 2000
601         ScrollIndicator.vertical: ScrollIndicator { }
602     }
603     \endcode
604 
605     \sa {Attaching ScrollIndicator to a Flickable}
606 */
vertical() const607 QQuickScrollIndicator *QQuickScrollIndicatorAttached::vertical() const
608 {
609     Q_D(const QQuickScrollIndicatorAttached);
610     return d->vertical;
611 }
612 
setVertical(QQuickScrollIndicator * vertical)613 void QQuickScrollIndicatorAttached::setVertical(QQuickScrollIndicator *vertical)
614 {
615     Q_D(QQuickScrollIndicatorAttached);
616     if (d->vertical == vertical)
617         return;
618 
619     if (d->vertical && d->flickable) {
620         QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, verticalChangeTypes);
621         QObjectPrivate::disconnect(d->flickable, &QQuickFlickable::movingVerticallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateVertical);
622 
623         // TODO: export QQuickFlickableVisibleArea
624         QObject *area = d->flickable->property("visibleArea").value<QObject *>();
625         disconnect(area, SIGNAL(heightRatioChanged(qreal)), d->vertical, SLOT(setSize(qreal)));
626         disconnect(area, SIGNAL(yPositionChanged(qreal)), d->vertical, SLOT(setPosition(qreal)));
627     }
628 
629     d->vertical = vertical;
630 
631     if (vertical && d->flickable) {
632         if (!vertical->parentItem())
633             vertical->setParentItem(d->flickable);
634         vertical->setOrientation(Qt::Vertical);
635 
636         QQuickItemPrivate::get(vertical)->addItemChangeListener(d, verticalChangeTypes);
637         QObjectPrivate::connect(d->flickable, &QQuickFlickable::movingVerticallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateVertical);
638 
639         // TODO: export QQuickFlickableVisibleArea
640         QObject *area = d->flickable->property("visibleArea").value<QObject *>();
641         connect(area, SIGNAL(heightRatioChanged(qreal)), vertical, SLOT(setSize(qreal)));
642         connect(area, SIGNAL(yPositionChanged(qreal)), vertical, SLOT(setPosition(qreal)));
643 
644         d->layoutVertical();
645         vertical->setSize(area->property("heightRatio").toReal());
646         vertical->setPosition(area->property("yPosition").toReal());
647     }
648     emit verticalChanged();
649 }
650 
651 #if QT_CONFIG(quicktemplates2_multitouch)
touchEvent(QTouchEvent * event)652 void QQuickScrollIndicator::touchEvent(QTouchEvent *event)
653 {
654     event->ignore(); // QTBUG-61785
655 }
656 #endif
657 
658 #if QT_CONFIG(accessibility)
accessibleRole() const659 QAccessible::Role QQuickScrollIndicator::accessibleRole() const
660 {
661     return QAccessible::Indicator;
662 }
663 #endif
664 
665 QT_END_NAMESPACE
666