| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: http://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL3$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see http://www.qt.io/terms-conditions. For further |
| ** information use the contact form at http://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or later as published by the Free |
| ** Software Foundation and appearing in the file LICENSE.GPL included in |
| ** the packaging of this file. Please review the following information to |
| ** ensure the GNU General Public License version 2.0 requirements will be |
| ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "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 qtquickcontrols2-indicators |
| \brief Vertical or horizontal interactive scroll bar. |
| |
| \image qtquickcontrols2-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. |
| |
| \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 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 qtquickcontrols2-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 qtquickcontrols2-scrollbar-non-attached.qml 1 |
| |
| \image qtquickcontrols2-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 [QtQuick]{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, {Customizing ScrollBar}, {Indicator Controls} |
| */ |
| |
| static const QQuickItemPrivate::ChangeTypes changeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed; |
| static const QQuickItemPrivate::ChangeTypes horizontalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitHeight; |
| static const QQuickItemPrivate::ChangeTypes verticalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitWidth; |
| |
| QQuickScrollBarPrivate::VisualArea QQuickScrollBarPrivate::visualArea() const |
| { |
| qreal visualPos = position; |
| if (minimumSize > size) |
| visualPos = position / (1.0 - size) * (1.0 - minimumSize); |
| |
| qreal visualSize = qBound<qreal>(0, qMax(size, minimumSize) + qMin<qreal>(0, visualPos), 1.0 - visualPos); |
| |
| visualPos = qBound<qreal>(0, visualPos, 1.0 - visualSize); |
| |
| return VisualArea(visualPos, visualSize); |
| } |
| |
| qreal QQuickScrollBarPrivate::logicalPosition(qreal position) const |
| { |
| if (minimumSize > size) |
| 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(cursor) |
| q->setCursor(Qt::ArrowCursor); |
| #endif |
| } else { |
| q->setAcceptedMouseButtons(Qt::NoButton); |
| #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::handlePress(const QPointF &point) |
| { |
| Q_Q(QQuickScrollBar); |
| QQuickControlPrivate::handlePress(point); |
| offset = positionAt(point) - position; |
| qreal sz = qMax(size, logicalPosition(minimumSize)); |
| if (offset < 0 || offset > sz) |
| offset = sz / 2; |
| q->setPressed(true); |
| } |
| |
| void QQuickScrollBarPrivate::handleMove(const QPointF &point) |
| { |
| Q_Q(QQuickScrollBar); |
| QQuickControlPrivate::handleMove(point); |
| qreal pos = qBound<qreal>(0.0, positionAt(point) - offset, 1.0 - size); |
| if (snapMode == QQuickScrollBar::SnapAlways) |
| pos = snapPosition(pos); |
| q->setPosition(pos); |
| } |
| |
| void QQuickScrollBarPrivate::handleRelease(const QPointF &point) |
| { |
| Q_Q(QQuickScrollBar); |
| QQuickControlPrivate::handleRelease(point); |
| qreal pos = qBound<qreal>(0.0, positionAt(point) - offset, 1.0 - size); |
| if (snapMode != QQuickScrollBar::NoSnap) |
| pos = snapPosition(pos); |
| q->setPosition(pos); |
| offset = 0.0; |
| q->setPressed(false); |
| } |
| |
| 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(); |
| } |
| |
| QQuickScrollBar::QQuickScrollBar(QQuickItem *parent) |
| : QQuickControl(*(new QQuickScrollBarPrivate), parent) |
| { |
| setKeepMouseGrab(true); |
| setAcceptedMouseButtons(Qt::LeftButton); |
| #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 (qFuzzyCompare(d->size, size)) |
| return; |
| |
| auto oldVisualArea = d->visualArea(); |
| d->size = size; |
| 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}. |
| |
| \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); |
| if (qFuzzyCompare(d->position, position)) |
| return; |
| |
| auto oldVisualArea = d->visualArea(); |
| d->position = position; |
| if (isComponentComplete()) |
| d->resizeContent(); |
| emit positionChanged(); |
| d->visualAreaChange(d->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 (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 (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; |
| |
| 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 qtquickcontrols2-scrollbar-nosnap.gif |
| \row \li \c ScrollBar.SnapAlways \li \image qtquickcontrols2-scrollbar-snapalways.gif |
| \row \li \c ScrollBar.SnapOnRelease \li \image qtquickcontrols2-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 qtquickcontrols2-scrollbar-policy.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 (qFuzzyCompare(d->minimumSize, minimumSize)) |
| return; |
| |
| auto oldVisualArea = d->visualArea(); |
| d->minimumSize = minimumSize; |
| 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 |
| |
| 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 |
| |
| 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; |
| } |
| |
| /*! |
| \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->localPos()); |
| } |
| |
| #if QT_CONFIG(quicktemplates2_hover) |
| void QQuickScrollBar::hoverChange() |
| { |
| Q_D(QQuickScrollBar); |
| d->updateActive(); |
| } |
| #endif |
| |
| #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); |
| if (horizontal) |
| cleanupHorizontal(); |
| if (vertical) |
| cleanupVertical(); |
| } |
| |
| flickable = item; |
| |
| if (item) { |
| QQuickItemPrivate::get(item)->updateOrAddGeometryChangeListener(this, QQuickGeometryChange::Size); |
| 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); |
| |
| 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); |
| |
| layoutVertical(); |
| vertical->setSize(area->property("heightRatio").toReal()); |
| vertical->setPosition(area->property("yPosition").toReal()); |
| } |
| |
| void QQuickScrollBarAttachedPrivate::cleanupHorizontal() |
| { |
| Q_ASSERT(flickable && horizontal); |
| |
| 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); |
| |
| 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() |
| { |
| 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() |
| { |
| 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 == 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, horizontalChangeTypes); |
| d->horizontal = nullptr; |
| } |
| if (d->vertical) { |
| QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, verticalChangeTypes); |
| 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, horizontalChangeTypes); |
| 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, horizontalChangeTypes); |
| 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, verticalChangeTypes); |
| 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, verticalChangeTypes); |
| 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 |