aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates/qquickscrollbar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quicktemplates/qquickscrollbar.cpp')
-rw-r--r--src/quicktemplates/qquickscrollbar.cpp1295
1 files changed, 1295 insertions, 0 deletions
diff --git a/src/quicktemplates/qquickscrollbar.cpp b/src/quicktemplates/qquickscrollbar.cpp
new file mode 100644
index 0000000000..75de0363e9
--- /dev/null
+++ b/src/quicktemplates/qquickscrollbar.cpp
@@ -0,0 +1,1295 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickscrollbar_p.h"
+#include "qquickscrollbar_p_p.h"
+#include "qquickscrollview_p.h"
+
+#include <QtQml/qqmlinfo.h>
+#include <QtQuick/private/qquickflickable_p.h>
+#if QT_CONFIG(accessibility)
+#include <QtQuick/private/qquickaccessibleattached_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype ScrollBar
+ \inherits Control
+//! \instantiates QQuickScrollBar
+ \inqmlmodule QtQuick.Controls
+ \since 5.7
+ \ingroup qtquickcontrols-indicators
+ \brief Vertical or horizontal interactive scroll bar.
+
+ \image qtquickcontrols-scrollbar.gif
+
+ ScrollBar is an interactive bar that can be used to scroll to a specific
+ position. A scroll bar can be either \l vertical or \l horizontal, and can
+ be attached to any \l Flickable, such as \l ListView and \l GridView.
+ It can also be used with \l ScrollView.
+
+ \code
+ Flickable {
+ // ...
+ ScrollBar.vertical: ScrollBar { }
+ }
+ \endcode
+
+ \section1 Attaching ScrollBar to a Flickable
+
+ When ScrollBar is attached \l {ScrollBar::vertical}{vertically} or
+ \l {ScrollBar::horizontal}{horizontally} to a Flickable, its geometry and
+ the following properties are automatically set and updated as appropriate:
+
+ \list
+ \li \l orientation
+ \li \l position
+ \li \l {ScrollBar::} {size}
+ \li \l active
+ \endlist
+
+ An attached ScrollBar re-parents itself to the target Flickable. A vertically
+ attached ScrollBar resizes itself to the height of the Flickable, and positions
+ itself to either side of it based on the \l {Control::mirrored}{layout direction}.
+ A horizontally attached ScrollBar resizes itself to the width of the Flickable,
+ and positions itself to the bottom. The automatic geometry management can be disabled
+ by specifying another parent for the attached ScrollBar. This can be useful, for
+ example, if the ScrollBar should be placed outside a clipping Flickable. This is
+ demonstrated by the following example:
+
+ \code
+ Flickable {
+ id: flickable
+ clip: true
+ // ...
+ ScrollBar.vertical: ScrollBar {
+ parent: flickable.parent
+ anchors.top: flickable.top
+ anchors.left: flickable.right
+ anchors.bottom: flickable.bottom
+ }
+ }
+ \endcode
+
+ Notice that ScrollBar does not filter key events of the Flickable it is
+ attached to. The following example illustrates how to implement scrolling
+ with up and down keys:
+
+ \code
+ Flickable {
+ focus: true
+
+ Keys.onUpPressed: scrollBar.decrease()
+ Keys.onDownPressed: scrollBar.increase()
+
+ ScrollBar.vertical: ScrollBar { id: scrollBar }
+ }
+ \endcode
+
+ \section1 Binding the Active State of Horizontal and Vertical Scroll Bars
+
+ Horizontal and vertical scroll bars do not share the \l active state with
+ each other by default. In order to keep both bars visible whilst scrolling
+ to either direction, establish a two-way binding between the active states
+ as presented by the following example:
+
+ \snippet qtquickcontrols-scrollbar-active.qml 1
+
+ \section1 Non-attached Scroll Bars
+
+ It is possible to create an instance of ScrollBar without using the
+ attached property API. This is useful when the behavior of the attached
+ scroll bar is not sufficient or a \l Flickable is not in use. In the
+ following example, horizontal and vertical scroll bars are used to
+ scroll over the text without using \l Flickable:
+
+ \snippet qtquickcontrols-scrollbar-non-attached.qml 1
+
+ \image qtquickcontrols-scrollbar-non-attached.png
+
+ When using a non-attached ScrollBar, the following must be done manually:
+
+ \list
+ \li Layout the scroll bar (with the \l {Item::}{x} and \l {Item::}{y} or
+ \l {Item::}{anchors} property, for example).
+ \li Set the \l size and \l position properties to determine the size and position
+ of the scroll bar in relation to the scrolled item.
+ \li Set the \l active property to determine when the scroll bar will be
+ visible.
+ \endlist
+
+ \sa ScrollIndicator, ScrollView, {Customizing ScrollBar}, {Indicator Controls}
+*/
+
+static const QQuickItemPrivate::ChangeTypes QsbChangeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
+static const QQuickItemPrivate::ChangeTypes QsbHorizontalChangeTypes = QsbChangeTypes | QQuickItemPrivate::ImplicitHeight;
+static const QQuickItemPrivate::ChangeTypes QsbVerticalChangeTypes = QsbChangeTypes | QQuickItemPrivate::ImplicitWidth;
+
+QQuickScrollBarPrivate::VisualArea QQuickScrollBarPrivate::visualArea() const
+{
+ qreal visualPos = position;
+
+ if (minimumSize > size && size != 1.0)
+ visualPos = position / (1.0 - size) * (1.0 - minimumSize);
+
+ qreal maximumSize = qMax<qreal>(0.0, 1.0 - visualPos);
+ qreal visualSize = qMax<qreal>(minimumSize,
+ qMin<qreal>(qMax(size, minimumSize) + qMin<qreal>(0, visualPos),
+ maximumSize));
+
+ visualPos = qMax<qreal>(0,qMin<qreal>(visualPos,qMax<qreal>(0, 1.0 - visualSize)));
+
+ return VisualArea(visualPos, visualSize);
+}
+
+qreal QQuickScrollBarPrivate::logicalPosition(qreal position) const
+{
+ if (minimumSize > size && minimumSize != 1.0)
+ return position * (1.0 - size) / (1.0 - minimumSize);
+ return position;
+}
+
+qreal QQuickScrollBarPrivate::snapPosition(qreal position) const
+{
+ const qreal effectiveStep = stepSize * (1.0 - size);
+ if (qFuzzyIsNull(effectiveStep))
+ return position;
+
+ return qRound(position / effectiveStep) * effectiveStep;
+}
+
+qreal QQuickScrollBarPrivate::positionAt(const QPointF &point) const
+{
+ Q_Q(const QQuickScrollBar);
+ if (orientation == Qt::Horizontal)
+ return logicalPosition(point.x() - q->leftPadding()) / q->availableWidth();
+ else
+ return logicalPosition(point.y() - q->topPadding()) / q->availableHeight();
+}
+
+void QQuickScrollBarPrivate::setInteractive(bool enabled)
+{
+ Q_Q(QQuickScrollBar);
+ if (interactive == enabled)
+ return;
+
+ interactive = enabled;
+ if (interactive) {
+ q->setAcceptedMouseButtons(Qt::LeftButton);
+#if QT_CONFIG(quicktemplates2_multitouch)
+ q->setAcceptTouchEvents(true);
+#endif
+#if QT_CONFIG(cursor)
+ q->setCursor(Qt::ArrowCursor);
+#endif
+ } else {
+ q->setAcceptedMouseButtons(Qt::NoButton);
+#if QT_CONFIG(quicktemplates2_multitouch)
+ q->setAcceptTouchEvents(false);
+#endif
+#if QT_CONFIG(cursor)
+ q->unsetCursor();
+#endif
+ q->ungrabMouse();
+ }
+ emit q->interactiveChanged();
+}
+
+void QQuickScrollBarPrivate::updateActive()
+{
+ Q_Q(QQuickScrollBar);
+#if QT_CONFIG(quicktemplates2_hover)
+ bool hover = hovered;
+#else
+ bool hover = false;
+#endif
+ q->setActive(moving || (interactive && (pressed || hover)));
+}
+
+void QQuickScrollBarPrivate::resizeContent()
+{
+ Q_Q(QQuickScrollBar);
+ if (!contentItem)
+ return;
+
+ // - negative overshoot (pos < 0): clamp the pos to 0, and deduct the overshoot from the size
+ // - positive overshoot (pos + size > 1): clamp the size to 1-pos
+ const VisualArea visual = visualArea();
+
+ if (orientation == Qt::Horizontal) {
+ contentItem->setPosition(QPointF(q->leftPadding() + visual.position * q->availableWidth(), q->topPadding()));
+ contentItem->setSize(QSizeF(q->availableWidth() * visual.size, q->availableHeight()));
+ } else {
+ contentItem->setPosition(QPointF(q->leftPadding(), q->topPadding() + visual.position * q->availableHeight()));
+ contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight() * visual.size));
+ }
+}
+
+void QQuickScrollBarPrivate::itemImplicitWidthChanged(QQuickItem *item)
+{
+ Q_Q(QQuickScrollBar);
+ QQuickControlPrivate::itemImplicitWidthChanged(item);
+ QQuickIndicatorButton *indicatorButton = q->decreaseVisual();
+ if (!indicatorButton || item != indicatorButton->indicator()) {
+ indicatorButton = q->increaseVisual();
+ if (!indicatorButton || item != indicatorButton->indicator())
+ return;
+ }
+ if (indicatorButton)
+ emit indicatorButton->implicitIndicatorWidthChanged();
+}
+
+void QQuickScrollBarPrivate::itemImplicitHeightChanged(QQuickItem *item)
+{
+ Q_Q(QQuickScrollBar);
+ QQuickControlPrivate::itemImplicitHeightChanged(item);
+ QQuickIndicatorButton *indicatorButton = q->decreaseVisual();
+ if (!indicatorButton || item != indicatorButton->indicator()) {
+ indicatorButton = q->increaseVisual();
+ if (!indicatorButton || item != indicatorButton->indicator())
+ return;
+ }
+ if (indicatorButton)
+ emit indicatorButton->implicitIndicatorHeightChanged();
+}
+
+bool QQuickScrollBarPrivate::handlePress(const QPointF &point, ulong timestamp)
+{
+ Q_Q(QQuickScrollBar);
+ QQuickControlPrivate::handlePress(point, timestamp);
+ if (QQuickIndicatorButton *indicatorButton = q->decreaseVisual()) {
+ QQuickItem *decreaseArrow = indicatorButton->indicator();
+ if (decreaseArrow && decreaseArrow->contains(q->mapToItem(decreaseArrow, point + QPointF(0.5, 0.5)))) {
+ indicatorButton->setPressed(true);
+ q->decrease();
+ return true;
+ }
+ }
+
+ if (QQuickIndicatorButton *increaseObject = q->increaseVisual()) {
+ QQuickItem *increaseArrow = increaseObject->indicator();
+ if (increaseArrow && increaseArrow->contains(q->mapToItem(increaseArrow, point + QPointF(0.5, 0.5)))) {
+ increaseObject->setPressed(true);
+ q->increase();
+ return true;
+ }
+ }
+
+ offset = positionAt(point) - position;
+ qreal sz = qMax(size, logicalPosition(minimumSize));
+ if (offset < 0 || offset > sz)
+ offset = sz / 2;
+ q->setPressed(true);
+ return true;
+}
+
+bool QQuickScrollBarPrivate::handleMove(const QPointF &point, ulong timestamp)
+{
+ Q_Q(QQuickScrollBar);
+ QQuickControlPrivate::handleMove(point, timestamp);
+
+ /*
+ * handleMove() will be called as soon as you hold the mouse button down *anywhere* on the
+ * ScrollBar, including the increase/decrease button indicator areas. So without the following
+ * early return, it would move the scrollbar handle to one of its extremeties. That would
+ * ruin the behavior we would like when clicking e.g. the "increase button": To step the
+ * scrollbar gently.
+ */
+ if (!pressed)
+ return true;
+
+ qreal pos = qMax<qreal>(0.0, qMin<qreal>(positionAt(point) - offset, 1.0 - size));
+ if (snapMode == QQuickScrollBar::SnapAlways)
+ pos = snapPosition(pos);
+ q->setPosition(pos);
+ return true;
+}
+
+bool QQuickScrollBarPrivate::handleRelease(const QPointF &point, ulong timestamp)
+{
+ Q_Q(QQuickScrollBar);
+ QQuickControlPrivate::handleRelease(point, timestamp);
+
+ if (orientation == Qt::Vertical) {
+ if (point.y() < q->topPadding() || point.y() >= (q->height() - q->bottomPadding()))
+ return true;
+ } else /* orientation == Qt::Horizontal */{
+ if (point.x() < q->leftPadding() || point.x() >= (q->width() - q->rightPadding()))
+ return true;
+ }
+
+ qreal pos = qMax<qreal>(0.0, qMin<qreal>(positionAt(point) - offset, 1.0 - size));
+ if (snapMode != QQuickScrollBar::NoSnap)
+ pos = snapPosition(pos);
+ q->setPosition(pos);
+ offset = 0.0;
+ q->setPressed(false);
+ return true;
+}
+
+void QQuickScrollBarPrivate::handleUngrab()
+{
+ Q_Q(QQuickScrollBar);
+ QQuickControlPrivate::handleUngrab();
+ offset = 0.0;
+ q->setPressed(false);
+}
+
+void QQuickScrollBarPrivate::visualAreaChange(const VisualArea &newVisualArea, const VisualArea &oldVisualArea)
+{
+ Q_Q(QQuickScrollBar);
+ if (!qFuzzyCompare(newVisualArea.size, oldVisualArea.size))
+ emit q->visualSizeChanged();
+ if (!qFuzzyCompare(newVisualArea.position, oldVisualArea.position))
+ emit q->visualPositionChanged();
+}
+
+void QQuickScrollBarPrivate::updateHover(const QPointF &pos, std::optional<bool> newHoverState)
+{
+ Q_Q(QQuickScrollBar);
+ auto updateHoverOnButton = [&](QQuickIndicatorButton *sbButton) {
+ if (sbButton) {
+ bool hovered = newHoverState.value_or(false);
+ if (!newHoverState.has_value()) {
+ if (QQuickItem *indicator = sbButton->indicator())
+ hovered = indicator->contains(q->mapToItem(indicator, pos));
+ }
+ sbButton->setHovered(hovered);
+ }
+ };
+ updateHoverOnButton(q->decreaseVisual());
+ updateHoverOnButton(q->increaseVisual());
+}
+
+QQuickScrollBar::QQuickScrollBar(QQuickItem *parent)
+ : QQuickControl(*(new QQuickScrollBarPrivate), parent)
+{
+ Q_D(QQuickScrollBar);
+ d->decreaseVisual = new QQuickIndicatorButton(this);
+ d->increaseVisual = new QQuickIndicatorButton(this);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+ setKeepMouseGrab(true);
+ setAcceptedMouseButtons(Qt::LeftButton);
+#if QT_CONFIG(quicktemplates2_multitouch)
+ setAcceptTouchEvents(true);
+#endif
+#if QT_CONFIG(cursor)
+ setCursor(Qt::ArrowCursor);
+#endif
+}
+
+QQuickScrollBarAttached *QQuickScrollBar::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickScrollBarAttached(object);
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::ScrollBar::size
+
+ This property holds the size of the scroll bar, scaled to \c {0.0 - 1.0}.
+
+ \sa {Flickable::visibleArea.heightRatio}{Flickable::visibleArea}
+
+ This property is automatically set when the scroll bar is
+ \l {Attaching ScrollBar to a Flickable}{attached to a flickable}.
+
+ \sa minimumSize, visualSize
+*/
+qreal QQuickScrollBar::size() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->size;
+}
+
+void QQuickScrollBar::setSize(qreal size)
+{
+ Q_D(QQuickScrollBar);
+ if (!qt_is_finite(size))
+ return;
+ size = qBound(0.0, size, 1.0);
+ if (qFuzzyCompare(d->size, size))
+ return;
+
+ const auto oldVisualArea = d->visualArea();
+ d->size = size;
+ if (d->size + d->position > 1.0) {
+ d->setPosition(1.0 - d->size, false);
+ }
+
+ if (isComponentComplete())
+ d->resizeContent();
+ emit sizeChanged();
+ d->visualAreaChange(d->visualArea(), oldVisualArea);
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::ScrollBar::position
+
+ This property holds the position of the scroll bar, scaled to \c {0.0 - 1.0}.
+
+ The largest valid scrollbar position is \c {(1.0 - size)}. This gives
+ correct behavior for the most used case where moving the scrollbar
+ to the end will put the end of the document at the lower end of the
+ visible area of the connected Flickable.
+
+ \sa {Flickable::visibleArea.yPosition}{Flickable::visibleArea}
+
+ This property is automatically set when the scroll bar is
+ \l {Attaching ScrollBar to a Flickable}{attached to a flickable}.
+
+ \sa visualPosition
+*/
+qreal QQuickScrollBar::position() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->position;
+}
+
+void QQuickScrollBar::setPosition(qreal position)
+{
+ Q_D(QQuickScrollBar);
+ d->setPosition(position);
+}
+
+void QQuickScrollBarPrivate::setPosition(qreal newPosition, bool notifyVisualChange)
+{
+ Q_Q(QQuickScrollBar);
+ if (!qt_is_finite(newPosition) || qFuzzyCompare(position, newPosition))
+ return;
+
+ auto oldVisualArea = visualArea();
+ position = newPosition;
+ if (q->isComponentComplete())
+ resizeContent();
+ emit q->positionChanged();
+ if (notifyVisualChange)
+ visualAreaChange(visualArea(), oldVisualArea);
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::ScrollBar::stepSize
+
+ This property holds the step size. The default value is \c 0.0.
+
+ \sa snapMode, increase(), decrease()
+*/
+qreal QQuickScrollBar::stepSize() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->stepSize;
+}
+
+void QQuickScrollBar::setStepSize(qreal step)
+{
+ Q_D(QQuickScrollBar);
+ if (!qt_is_finite(step) || qFuzzyCompare(d->stepSize, step))
+ return;
+
+ d->stepSize = step;
+ emit stepSizeChanged();
+}
+
+/*!
+ \qmlproperty bool QtQuick.Controls::ScrollBar::active
+
+ This property holds whether the scroll bar is active, i.e. when it's \l pressed
+ or the attached Flickable is \l {Flickable::moving}{moving}.
+
+ It is possible to keep \l {Binding the Active State of Horizontal and Vertical Scroll Bars}
+ {both horizontal and vertical bars visible} while scrolling in either direction.
+
+ This property is automatically set when the scroll bar is
+ \l {Attaching ScrollBar to a Flickable}{attached to a flickable}.
+*/
+bool QQuickScrollBar::isActive() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->active;
+}
+
+void QQuickScrollBar::setActive(bool active)
+{
+ Q_D(QQuickScrollBar);
+ if (d->active == active)
+ return;
+
+ d->active = active;
+ emit activeChanged();
+}
+
+/*!
+ \qmlproperty bool QtQuick.Controls::ScrollBar::pressed
+
+ This property holds whether the scroll bar is pressed.
+*/
+bool QQuickScrollBar::isPressed() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->pressed;
+}
+
+void QQuickScrollBar::setPressed(bool pressed)
+{
+ Q_D(QQuickScrollBar);
+ if (!pressed) {
+ if (QQuickIndicatorButton *button = decreaseVisual())
+ button->setPressed(false);
+ if (QQuickIndicatorButton *button = increaseVisual())
+ button->setPressed(false);
+ }
+ if (d->pressed == pressed)
+ return;
+
+ d->pressed = pressed;
+ setAccessibleProperty("pressed", pressed);
+ d->updateActive();
+ emit pressedChanged();
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Controls::ScrollBar::orientation
+
+ This property holds the orientation of the scroll bar.
+
+ Possible values:
+ \value Qt.Horizontal Horizontal
+ \value Qt.Vertical Vertical (default)
+
+ This property is automatically set when the scroll bar is
+ \l {Attaching ScrollBar to a Flickable}{attached to a flickable}.
+
+ \sa horizontal, vertical
+*/
+Qt::Orientation QQuickScrollBar::orientation() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->orientation;
+}
+
+void QQuickScrollBar::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QQuickScrollBar);
+ if (d->orientation == orientation)
+ return;
+
+ if (orientation == Qt::Horizontal)
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+ else
+ d->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Preferred);
+
+ d->orientation = orientation;
+ if (isComponentComplete())
+ d->resizeContent();
+ emit orientationChanged();
+}
+
+/*!
+ \since QtQuick.Controls 2.2 (Qt 5.9)
+ \qmlproperty enumeration QtQuick.Controls::ScrollBar::snapMode
+
+ This property holds the snap mode.
+
+ Possible values:
+ \value ScrollBar.NoSnap The scrollbar does not snap (default).
+ \value ScrollBar.SnapAlways The scrollbar snaps while dragged.
+ \value ScrollBar.SnapOnRelease The scrollbar does not snap while being dragged, but only after released.
+
+ In the following table, the various modes are illustrated with animations.
+ The movement and the \l stepSize (\c 0.25) are identical in each animation.
+
+ \table
+ \header
+ \row \li \b Value \li \b Example
+ \row \li \c ScrollBar.NoSnap \li \image qtquickcontrols-scrollbar-nosnap.gif
+ \row \li \c ScrollBar.SnapAlways \li \image qtquickcontrols-scrollbar-snapalways.gif
+ \row \li \c ScrollBar.SnapOnRelease \li \image qtquickcontrols-scrollbar-snaponrelease.gif
+ \endtable
+
+ \sa stepSize
+*/
+QQuickScrollBar::SnapMode QQuickScrollBar::snapMode() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->snapMode;
+}
+
+void QQuickScrollBar::setSnapMode(SnapMode mode)
+{
+ Q_D(QQuickScrollBar);
+ if (d->snapMode == mode)
+ return;
+
+ d->snapMode = mode;
+ emit snapModeChanged();
+}
+
+/*!
+ \since QtQuick.Controls 2.2 (Qt 5.9)
+ \qmlproperty bool QtQuick.Controls::ScrollBar::interactive
+
+ This property holds whether the scroll bar is interactive. The default value is \c true.
+
+ A non-interactive scroll bar is visually and behaviorally similar to \l ScrollIndicator.
+ This property is useful for switching between typical mouse- and touch-orientated UIs
+ with interactive and non-interactive scroll bars, respectively.
+*/
+bool QQuickScrollBar::isInteractive() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->interactive;
+}
+
+void QQuickScrollBar::setInteractive(bool interactive)
+{
+ Q_D(QQuickScrollBar);
+ d->explicitInteractive = true;
+ d->setInteractive(interactive);
+}
+
+void QQuickScrollBar::resetInteractive()
+{
+ Q_D(QQuickScrollBar);
+ d->explicitInteractive = false;
+ d->setInteractive(true);
+}
+
+/*!
+ \since QtQuick.Controls 2.2 (Qt 5.9)
+ \qmlproperty enumeration QtQuick.Controls::ScrollBar::policy
+
+ This property holds the policy of the scroll bar. The default policy is \c ScrollBar.AsNeeded.
+
+ Possible values:
+ \value ScrollBar.AsNeeded The scroll bar is only shown when the content is too large to fit.
+ \value ScrollBar.AlwaysOff The scroll bar is never shown.
+ \value ScrollBar.AlwaysOn The scroll bar is always shown.
+
+ The following example keeps the vertical scroll bar always visible:
+
+ \snippet qtquickcontrols-scrollbar-policy-alwayson.qml 1
+
+ Styles may use this property in combination with the \l active property
+ in order to implement transient scroll bars. Transient scroll bars are
+ hidden shortly after the last interaction event (hover or press). This
+ is typically done by animating the opacity of the scroll bar. To override
+ this behavior, set the policy to \c ScrollBar.AlwaysOn or
+ \c ScrollBar.AlwaysOff, depending on the size of the content compared to
+ its view. For example, for a vertical \l ListView:
+
+ \snippet qtquickcontrols-scrollbar-policy-alwayson-when-needed.qml 1
+*/
+QQuickScrollBar::Policy QQuickScrollBar::policy() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->policy;
+}
+
+void QQuickScrollBar::setPolicy(Policy policy)
+{
+ Q_D(QQuickScrollBar);
+ if (d->policy == policy)
+ return;
+
+ d->policy = policy;
+ emit policyChanged();
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlproperty bool QtQuick.Controls::ScrollBar::horizontal
+ \readonly
+
+ This property holds whether the scroll bar is horizontal.
+
+ \sa orientation
+*/
+bool QQuickScrollBar::isHorizontal() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->orientation == Qt::Horizontal;
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlproperty bool QtQuick.Controls::ScrollBar::vertical
+ \readonly
+
+ This property holds whether the scroll bar is vertical.
+
+ \sa orientation
+*/
+bool QQuickScrollBar::isVertical() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->orientation == Qt::Vertical;
+}
+
+/*!
+ \since QtQuick.Controls 2.4 (Qt 5.11)
+ \qmlproperty real QtQuick.Controls::ScrollBar::minimumSize
+
+ This property holds the minimum size of the scroll bar, scaled to \c {0.0 - 1.0}.
+
+ \sa size, visualSize, visualPosition
+*/
+qreal QQuickScrollBar::minimumSize() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->minimumSize;
+}
+
+void QQuickScrollBar::setMinimumSize(qreal minimumSize)
+{
+ Q_D(QQuickScrollBar);
+ if (!qt_is_finite(minimumSize) || qFuzzyCompare(d->minimumSize, minimumSize))
+ return;
+
+ auto oldVisualArea = d->visualArea();
+ d->minimumSize = qBound(0.0, minimumSize, 1.0);
+ if (isComponentComplete())
+ d->resizeContent();
+ emit minimumSizeChanged();
+ d->visualAreaChange(d->visualArea(), oldVisualArea);
+}
+
+/*!
+ \since QtQuick.Controls 2.4 (Qt 5.11)
+ \qmlproperty real QtQuick.Controls::ScrollBar::visualSize
+ \readonly
+
+ This property holds the effective visual size of the scroll bar,
+ which may be limited by the \l {minimumSize}{minimum size}.
+
+ \sa size, minimumSize
+*/
+qreal QQuickScrollBar::visualSize() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->visualArea().size;
+}
+
+/*!
+ \since QtQuick.Controls 2.4 (Qt 5.11)
+ \qmlproperty real QtQuick.Controls::ScrollBar::visualPosition
+ \readonly
+
+ This property holds the effective visual position of the scroll bar,
+ which may be limited by the \l {minimumSize}{minimum size}.
+
+ \sa position, minimumSize
+*/
+qreal QQuickScrollBar::visualPosition() const
+{
+ Q_D(const QQuickScrollBar);
+ return d->visualArea().position;
+}
+
+QQuickIndicatorButton *QQuickScrollBar::decreaseVisual()
+{
+ Q_D(QQuickScrollBar);
+ return d->decreaseVisual;
+}
+
+QQuickIndicatorButton *QQuickScrollBar::increaseVisual()
+{
+ Q_D(QQuickScrollBar);
+ return d->increaseVisual;
+}
+
+/*!
+ \qmlmethod void QtQuick.Controls::ScrollBar::increase()
+
+ Increases the position by \l stepSize or \c 0.1 if stepSize is \c 0.0.
+
+ \sa stepSize
+*/
+void QQuickScrollBar::increase()
+{
+ Q_D(QQuickScrollBar);
+ qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize;
+ bool wasActive = d->active;
+ setActive(true);
+ setPosition(qMin<qreal>(1.0 - d->size, d->position + step));
+ setActive(wasActive);
+}
+
+/*!
+ \qmlmethod void QtQuick.Controls::ScrollBar::decrease()
+
+ Decreases the position by \l stepSize or \c 0.1 if stepSize is \c 0.0.
+
+ \sa stepSize
+*/
+void QQuickScrollBar::decrease()
+{
+ Q_D(QQuickScrollBar);
+ qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize;
+ bool wasActive = d->active;
+ setActive(true);
+ setPosition(qMax<qreal>(0.0, d->position - step));
+ setActive(wasActive);
+}
+
+void QQuickScrollBar::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QQuickScrollBar);
+ QQuickControl::mousePressEvent(event);
+ d->handleMove(event->position(), event->timestamp());
+}
+
+#if QT_CONFIG(quicktemplates2_hover)
+void QQuickScrollBar::hoverChange()
+{
+ Q_D(QQuickScrollBar);
+ d->updateActive();
+}
+
+void QQuickScrollBar::hoverEnterEvent(QHoverEvent *event)
+{
+ Q_D(QQuickScrollBar);
+ QQuickControl::hoverEnterEvent(event);
+ d->updateHover(event->position());
+ event->ignore();
+}
+
+void QQuickScrollBar::hoverMoveEvent(QHoverEvent *event)
+{
+ Q_D(QQuickScrollBar);
+ QQuickControl::hoverMoveEvent(event);
+ d->updateHover(event->position());
+ event->ignore();
+}
+
+void QQuickScrollBar::hoverLeaveEvent(QHoverEvent *event)
+{
+ Q_D(QQuickScrollBar);
+ QQuickControl::hoverLeaveEvent(event);
+
+ d->updateHover(QPoint(), false); //position is not needed when we force it to unhover
+ event->ignore();
+}
+#endif
+
+void QQuickScrollBar::classBegin()
+{
+ Q_D(QQuickScrollBar);
+ QQuickControl::classBegin();
+
+ QQmlContext *context = qmlContext(this);
+ if (context) {
+ QQmlEngine::setContextForObject(d->decreaseVisual, context);
+ QQmlEngine::setContextForObject(d->increaseVisual, context);
+ }
+}
+
+void QQuickScrollBar::componentComplete()
+{
+ Q_D(QQuickScrollBar);
+ QQuickIndicatorButtonPrivate::get(d->decreaseVisual)->executeIndicator(true);
+ QQuickIndicatorButtonPrivate::get(d->increaseVisual)->executeIndicator(true);
+
+ QQuickControl::componentComplete();
+}
+
+#if QT_CONFIG(accessibility)
+void QQuickScrollBar::accessibilityActiveChanged(bool active)
+{
+ QQuickControl::accessibilityActiveChanged(active);
+
+ Q_D(QQuickScrollBar);
+ if (active) {
+ setAccessibleProperty("pressed", d->pressed);
+
+ if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(this)) {
+ connect(accessibleAttached, &QQuickAccessibleAttached::increaseAction, this, &QQuickScrollBar::increase);
+ connect(accessibleAttached, &QQuickAccessibleAttached::decreaseAction, this, &QQuickScrollBar::decrease);
+ }
+ } else {
+ if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(this)) {
+ disconnect(accessibleAttached, &QQuickAccessibleAttached::increaseAction, this, &QQuickScrollBar::increase);
+ disconnect(accessibleAttached, &QQuickAccessibleAttached::decreaseAction, this, &QQuickScrollBar::decrease);
+ }
+ }
+}
+
+QAccessible::Role QQuickScrollBar::accessibleRole() const
+{
+ return QAccessible::ScrollBar;
+}
+#endif
+
+void QQuickScrollBarAttachedPrivate::setFlickable(QQuickFlickable *item)
+{
+ if (flickable) {
+ // NOTE: Use removeItemChangeListener(Geometry) instead of updateOrRemoveGeometryChangeListener(Size).
+ // The latter doesn't remove the listener but only resets its types. Thus, it leaves behind a dangling
+ // pointer on destruction.
+ QQuickItemPrivate::get(flickable)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
+ QQuickItemPrivate::get(flickable)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed);
+ if (horizontal)
+ cleanupHorizontal();
+ if (vertical)
+ cleanupVertical();
+ }
+
+ flickable = item;
+
+ if (item) {
+ // Don't know how to combine these calls into one, and as long as they're separate calls,
+ // the remove* calls above need to be separate too, otherwise they will have no effect.
+ QQuickItemPrivate::get(item)->updateOrAddGeometryChangeListener(this, QQuickGeometryChange::Size);
+ QQuickItemPrivate::get(item)->updateOrAddItemChangeListener(this, QQuickItemPrivate::Destroyed);
+ if (horizontal)
+ initHorizontal();
+ if (vertical)
+ initVertical();
+ }
+}
+
+void QQuickScrollBarAttachedPrivate::initHorizontal()
+{
+ Q_ASSERT(flickable && horizontal);
+
+ connect(flickable, &QQuickFlickable::movingHorizontallyChanged, this, &QQuickScrollBarAttachedPrivate::activateHorizontal);
+
+ // TODO: export QQuickFlickableVisibleArea
+ QObject *area = flickable->property("visibleArea").value<QObject *>();
+ QObject::connect(area, SIGNAL(widthRatioChanged(qreal)), horizontal, SLOT(setSize(qreal)));
+ QObject::connect(area, SIGNAL(xPositionChanged(qreal)), horizontal, SLOT(setPosition(qreal)));
+
+ // ensure that the ScrollBar is stacked above the Flickable in a ScrollView
+ QQuickItem *parent = horizontal->parentItem();
+ if (parent && parent == flickable->parentItem())
+ horizontal->stackAfter(flickable);
+
+ // If a scroll bar was previously hidden (due to e.g. setting a new contentItem
+ // on a ScrollView), we need to make sure that we un-hide it.
+ if (auto control = qobject_cast<QQuickControl*>(q_func()->parent())) {
+ const auto visibility = horizontal->policy() != QQuickScrollBar::AlwaysOff
+ ? QQuickControlPrivate::UnhideVisibility::Show : QQuickControlPrivate::UnhideVisibility::Hide;
+ QQuickControlPrivate::unhideOldItem(control, horizontal, visibility);
+ }
+
+ layoutHorizontal();
+ horizontal->setSize(area->property("widthRatio").toReal());
+ horizontal->setPosition(area->property("xPosition").toReal());
+}
+
+void QQuickScrollBarAttachedPrivate::initVertical()
+{
+ Q_ASSERT(flickable && vertical);
+
+ connect(flickable, &QQuickFlickable::movingVerticallyChanged, this, &QQuickScrollBarAttachedPrivate::activateVertical);
+
+ // TODO: export QQuickFlickableVisibleArea
+ QObject *area = flickable->property("visibleArea").value<QObject *>();
+ QObject::connect(area, SIGNAL(heightRatioChanged(qreal)), vertical, SLOT(setSize(qreal)));
+ QObject::connect(area, SIGNAL(yPositionChanged(qreal)), vertical, SLOT(setPosition(qreal)));
+
+ // ensure that the ScrollBar is stacked above the Flickable in a ScrollView
+ QQuickItem *parent = vertical->parentItem();
+ if (parent && parent == flickable->parentItem())
+ vertical->stackAfter(flickable);
+
+ if (auto control = qobject_cast<QQuickControl*>(q_func()->parent())) {
+ const auto visibility = vertical->policy() != QQuickScrollBar::AlwaysOff
+ ? QQuickControlPrivate::UnhideVisibility::Show : QQuickControlPrivate::UnhideVisibility::Hide;
+ QQuickControlPrivate::unhideOldItem(control, vertical, visibility);
+ }
+
+ layoutVertical();
+ vertical->setSize(area->property("heightRatio").toReal());
+ vertical->setPosition(area->property("yPosition").toReal());
+}
+
+void QQuickScrollBarAttachedPrivate::cleanupHorizontal()
+{
+ Q_ASSERT(flickable && horizontal);
+
+ QQuickControlPrivate::hideOldItem(horizontal);
+ // ScrollBar.qml has a binding to visible and ScrollView.qml has a binding to parent.
+ // If we just set visible to false and parent to null, these bindings will overwrite
+ // them upon component completion as part of the binding evaluation.
+ // That's why we remove the binding completely.
+ const QQmlProperty visibleProperty(horizontal, QStringLiteral("visible"));
+ const QQmlProperty parentProperty(horizontal, QStringLiteral("parent"));
+ QQmlPropertyPrivate::removeBinding(visibleProperty);
+ QQmlPropertyPrivate::removeBinding(parentProperty);
+
+ disconnect(flickable, &QQuickFlickable::movingHorizontallyChanged, this, &QQuickScrollBarAttachedPrivate::activateHorizontal);
+
+ // TODO: export QQuickFlickableVisibleArea
+ QObject *area = flickable->property("visibleArea").value<QObject *>();
+ QObject::disconnect(area, SIGNAL(widthRatioChanged(qreal)), horizontal, SLOT(setSize(qreal)));
+ QObject::disconnect(area, SIGNAL(xPositionChanged(qreal)), horizontal, SLOT(setPosition(qreal)));
+}
+
+void QQuickScrollBarAttachedPrivate::cleanupVertical()
+{
+ Q_ASSERT(flickable && vertical);
+
+ QQuickControlPrivate::hideOldItem(vertical);
+ const QQmlProperty visibleProperty(vertical, QStringLiteral("visible"));
+ const QQmlProperty parentProperty(vertical, QStringLiteral("parent"));
+ QQmlPropertyPrivate::removeBinding(visibleProperty);
+ QQmlPropertyPrivate::removeBinding(parentProperty);
+
+ disconnect(flickable, &QQuickFlickable::movingVerticallyChanged, this, &QQuickScrollBarAttachedPrivate::activateVertical);
+
+ // TODO: export QQuickFlickableVisibleArea
+ QObject *area = flickable->property("visibleArea").value<QObject *>();
+ QObject::disconnect(area, SIGNAL(heightRatioChanged(qreal)), vertical, SLOT(setSize(qreal)));
+ QObject::disconnect(area, SIGNAL(yPositionChanged(qreal)), vertical, SLOT(setPosition(qreal)));
+}
+
+void QQuickScrollBarAttachedPrivate::activateHorizontal()
+{
+ QQuickScrollBarPrivate *p = QQuickScrollBarPrivate::get(horizontal);
+ p->moving = flickable->isMovingHorizontally();
+ p->updateActive();
+}
+
+void QQuickScrollBarAttachedPrivate::activateVertical()
+{
+ QQuickScrollBarPrivate *p = QQuickScrollBarPrivate::get(vertical);
+ p->moving = flickable->isMovingVertically();
+ p->updateActive();
+}
+
+// TODO: QQuickFlickable::maxXYExtent()
+class QQuickFriendlyFlickable : public QQuickFlickable
+{
+ friend class QQuickScrollBarAttachedPrivate;
+};
+
+void QQuickScrollBarAttachedPrivate::scrollHorizontal()
+{
+ if (!flickable)
+ return;
+
+ QQuickFriendlyFlickable *f = reinterpret_cast<QQuickFriendlyFlickable *>(flickable);
+
+ const qreal viewwidth = f->width();
+ const qreal maxxextent = -f->maxXExtent() + f->minXExtent();
+ const qreal cx = horizontal->position() * (maxxextent + viewwidth) - f->minXExtent();
+
+ if (!qIsNaN(cx) && !qFuzzyCompare(cx, flickable->contentX()))
+ flickable->setContentX(cx);
+}
+
+void QQuickScrollBarAttachedPrivate::scrollVertical()
+{
+ if (!flickable)
+ return;
+
+ QQuickFriendlyFlickable *f = reinterpret_cast<QQuickFriendlyFlickable *>(flickable);
+
+ const qreal viewheight = f->height();
+ const qreal maxyextent = -f->maxYExtent() + f->minYExtent();
+ const qreal cy = vertical->position() * (maxyextent + viewheight) - f->minYExtent();
+
+ if (!qIsNaN(cy) && !qFuzzyCompare(cy, flickable->contentY()))
+ flickable->setContentY(cy);
+}
+
+void QQuickScrollBarAttachedPrivate::mirrorVertical()
+{
+ layoutVertical(true);
+}
+
+void QQuickScrollBarAttachedPrivate::layoutHorizontal(bool move)
+{
+ Q_ASSERT(horizontal && flickable);
+ if (horizontal->parentItem() != flickable)
+ return;
+ horizontal->setWidth(flickable->width());
+ if (move)
+ horizontal->setY(flickable->height() - horizontal->height());
+}
+
+void QQuickScrollBarAttachedPrivate::layoutVertical(bool move)
+{
+ Q_ASSERT(vertical && flickable);
+ if (vertical->parentItem() != flickable)
+ return;
+ vertical->setHeight(flickable->height());
+ if (move)
+ vertical->setX(vertical->isMirrored() ? 0 : flickable->width() - vertical->width());
+}
+
+void QQuickScrollBarAttachedPrivate::itemGeometryChanged(QQuickItem *item, const QQuickGeometryChange change, const QRectF &diff)
+{
+ Q_UNUSED(item);
+ Q_UNUSED(change);
+ if (horizontal && horizontal->height() > 0) {
+#ifdef QT_QUICK_NEW_GEOMETRY_CHANGED_HANDLING // TODO: correct/rename diff to oldGeometry
+ bool move = qFuzzyIsNull(horizontal->y()) || qFuzzyCompare(horizontal->y(), diff.height() - horizontal->height());
+#else
+ bool move = qFuzzyIsNull(horizontal->y()) || qFuzzyCompare(horizontal->y(), item->height() - diff.height() - horizontal->height());
+#endif
+ if (flickable)
+ layoutHorizontal(move);
+ }
+ if (vertical && vertical->width() > 0) {
+#ifdef QT_QUICK_NEW_GEOMETRY_CHANGED_HANDLING // TODO: correct/rename diff to oldGeometry
+ bool move = qFuzzyIsNull(vertical->x()) || qFuzzyCompare(vertical->x(), diff.width() - vertical->width());
+#else
+ bool move = qFuzzyIsNull(vertical->x()) || qFuzzyCompare(vertical->x(), item->width() - diff.width() - vertical->width());
+#endif
+ if (flickable)
+ layoutVertical(move);
+ }
+}
+
+void QQuickScrollBarAttachedPrivate::itemImplicitWidthChanged(QQuickItem *item)
+{
+ if (item == vertical && flickable)
+ layoutVertical(true);
+}
+
+void QQuickScrollBarAttachedPrivate::itemImplicitHeightChanged(QQuickItem *item)
+{
+ if (item == horizontal && flickable)
+ layoutHorizontal(true);
+}
+
+void QQuickScrollBarAttachedPrivate::itemDestroyed(QQuickItem *item)
+{
+ if (item == flickable)
+ flickable = nullptr;
+ if (item == horizontal)
+ horizontal = nullptr;
+ if (item == vertical)
+ vertical = nullptr;
+}
+
+QQuickScrollBarAttached::QQuickScrollBarAttached(QObject *parent)
+ : QObject(*(new QQuickScrollBarAttachedPrivate), parent)
+{
+ Q_D(QQuickScrollBarAttached);
+ d->setFlickable(qobject_cast<QQuickFlickable *>(parent));
+
+ if (parent && !d->flickable && !qobject_cast<QQuickScrollView *>(parent))
+ qmlWarning(parent) << "ScrollBar must be attached to a Flickable or ScrollView";
+}
+
+QQuickScrollBarAttached::~QQuickScrollBarAttached()
+{
+ Q_D(QQuickScrollBarAttached);
+ if (d->horizontal) {
+ QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QsbHorizontalChangeTypes);
+ d->horizontal = nullptr;
+ }
+ if (d->vertical) {
+ QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QsbVerticalChangeTypes);
+ d->vertical = nullptr;
+ }
+ d->setFlickable(nullptr);
+}
+
+/*!
+ \qmlattachedproperty ScrollBar QtQuick.Controls::ScrollBar::horizontal
+
+ This property attaches a horizontal scroll bar to a \l Flickable.
+
+ \code
+ Flickable {
+ contentWidth: 2000
+ ScrollBar.horizontal: ScrollBar { }
+ }
+ \endcode
+
+ \sa {Attaching ScrollBar to a Flickable}
+*/
+QQuickScrollBar *QQuickScrollBarAttached::horizontal() const
+{
+ Q_D(const QQuickScrollBarAttached);
+ return d->horizontal;
+}
+
+void QQuickScrollBarAttached::setHorizontal(QQuickScrollBar *horizontal)
+{
+ Q_D(QQuickScrollBarAttached);
+ if (d->horizontal == horizontal)
+ return;
+
+ if (d->horizontal) {
+ QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QsbHorizontalChangeTypes);
+ QObjectPrivate::disconnect(d->horizontal, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollHorizontal);
+
+ if (d->flickable)
+ d->cleanupHorizontal();
+ }
+
+ d->horizontal = horizontal;
+
+ if (horizontal) {
+ if (!horizontal->parentItem())
+ horizontal->setParentItem(qobject_cast<QQuickItem *>(parent()));
+ horizontal->setOrientation(Qt::Horizontal);
+
+ QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, QsbHorizontalChangeTypes);
+ QObjectPrivate::connect(horizontal, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollHorizontal);
+
+ if (d->flickable)
+ d->initHorizontal();
+ }
+ emit horizontalChanged();
+}
+
+/*!
+ \qmlattachedproperty ScrollBar QtQuick.Controls::ScrollBar::vertical
+
+ This property attaches a vertical scroll bar to a \l Flickable.
+
+ \code
+ Flickable {
+ contentHeight: 2000
+ ScrollBar.vertical: ScrollBar { }
+ }
+ \endcode
+
+ \sa {Attaching ScrollBar to a Flickable}
+*/
+QQuickScrollBar *QQuickScrollBarAttached::vertical() const
+{
+ Q_D(const QQuickScrollBarAttached);
+ return d->vertical;
+}
+
+void QQuickScrollBarAttached::setVertical(QQuickScrollBar *vertical)
+{
+ Q_D(QQuickScrollBarAttached);
+ if (d->vertical == vertical)
+ return;
+
+ if (d->vertical) {
+ QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QsbVerticalChangeTypes);
+ QObjectPrivate::disconnect(d->vertical, &QQuickScrollBar::mirroredChanged, d, &QQuickScrollBarAttachedPrivate::mirrorVertical);
+ QObjectPrivate::disconnect(d->vertical, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollVertical);
+
+ if (d->flickable)
+ d->cleanupVertical();
+ }
+
+ d->vertical = vertical;
+
+ if (vertical) {
+ if (!vertical->parentItem())
+ vertical->setParentItem(qobject_cast<QQuickItem *>(parent()));
+ vertical->setOrientation(Qt::Vertical);
+
+ QQuickItemPrivate::get(vertical)->addItemChangeListener(d, QsbVerticalChangeTypes);
+ QObjectPrivate::connect(vertical, &QQuickScrollBar::mirroredChanged, d, &QQuickScrollBarAttachedPrivate::mirrorVertical);
+ QObjectPrivate::connect(vertical, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollVertical);
+
+ if (d->flickable)
+ d->initVertical();
+ }
+ emit verticalChanged();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickscrollbar_p.cpp"